Fix a few issues in the to_process() handling.
[cubemap] / parse.cpp
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <ctype.h>
5 #include <assert.h>
6 #include <vector>
7 #include <string>
8
9 #include "parse.h"
10
11 using namespace std;
12
13 vector<string> split_tokens(const string &line)
14 {
15         vector<string> ret;
16         string current_token;
17
18         for (size_t i = 0; i < line.size(); ++i) {
19                 if (isspace(line[i])) {
20                         if (!current_token.empty()) {
21                                 ret.push_back(current_token);
22                         }
23                         current_token.clear();
24                 } else {
25                         current_token.push_back(line[i]);
26                 }
27         }
28         if (!current_token.empty()) {
29                 ret.push_back(current_token);
30         }
31         return ret;
32 }
33
34 vector<string> split_lines(const string &str)
35 {
36         vector<string> ret;
37         string current_line;
38
39         for (size_t i = 0; i < str.size(); ++i) {
40                 // Skip \r if followed by an \n.
41                 if (str[i] == '\r' && i < str.size() - 1 && str[i + 1] == '\n') {
42                         continue;
43                 }
44
45                 // End of the current line?
46                 if (str[i] == '\n') {
47                         if (!current_line.empty()) {
48                                 ret.push_back(current_line);
49                         }
50                         current_line.clear();
51                 } else {
52                         current_line.push_back(str[i]);
53                 }
54         }
55         if (!current_line.empty()) {
56                 ret.push_back(current_line);
57         }
58         return ret;
59 }
60
61 vector<ConfigLine> parse_config(const string &filename)
62 {
63         vector<ConfigLine> ret;
64
65         FILE *fp = fopen(filename.c_str(), "r");
66         if (fp == NULL) {
67                 perror(filename.c_str());
68                 exit(1);
69         }
70
71         char buf[4096];
72         while (!feof(fp)) {
73                 if (fgets(buf, sizeof(buf), fp) == NULL) {
74                         break;
75                 }
76
77                 // Chop off the string at the first #, \r or \n.
78                 buf[strcspn(buf, "#\r\n")] = 0;
79
80                 // Remove all whitespace from the end of the string.
81                 size_t len = strlen(buf);
82                 while (len > 0 && isspace(buf[len - 1])) {
83                         buf[--len] = 0;
84                 }
85
86                 // If the line is now all blank, ignore it.
87                 if (len == 0) {
88                         continue;
89                 }
90
91                 vector<string> tokens = split_tokens(buf);
92                 assert(!tokens.empty());
93                 
94                 ConfigLine line;
95                 line.keyword = tokens[0];
96
97                 for (size_t i = 1; i < tokens.size(); ++i) {
98                         // foo=bar is a parameter; anything else is an argument.
99                         size_t equals_pos = tokens[i].find_first_of('=');
100                         if (equals_pos == string::npos) {
101                                 line.arguments.push_back(tokens[i]);
102                         } else {
103                                 string key = tokens[i].substr(0, equals_pos);
104                                 string value = tokens[i].substr(equals_pos + 1, string::npos);
105                                 line.parameters.insert(make_pair(key, value));
106                         }
107                 }
108
109                 ret.push_back(line);
110         }
111
112         fclose(fp);
113         return ret;
114 }
115
116 int fetch_config_int(const vector<ConfigLine> &config, const string &keyword, int min_limit, int max_limit)
117 {
118         bool value_found = false;
119         int value = -1;
120         for (unsigned i = 0; i < config.size(); ++i) {
121                 if (config[i].keyword != keyword) {
122                         continue;
123                 }
124                 if (config[i].parameters.size() > 0 ||
125                     config[i].arguments.size() != 1) {
126                         fprintf(stderr, "ERROR: '%s' takes one argument and no parameters\n", keyword.c_str());
127                         exit(1);
128                 }
129                 value_found = true;
130                 value = atoi(config[i].arguments[0].c_str());  // TODO: verify int validity.
131         }
132         if (!value_found) {
133                 fprintf(stderr, "ERROR: Missing '%s' statement in config file.\n",
134                         keyword.c_str());
135                 exit(1);
136         }
137         if (value < min_limit || value > max_limit) {
138                 fprintf(stderr, "ERROR: '%s' is set to %d, must be in [%d,%d]\n",
139                         keyword.c_str(), value, min_limit, max_limit);
140                 exit(1);
141         }
142         return value;
143 }
144
145 #define MAX_REQUEST_SIZE 16384  /* 16 kB. */
146
147 RequestParseStatus wait_for_double_newline(string *existing_data, const char *new_data, size_t new_data_size)
148 {
149         // Guard against overlong requests gobbling up all of our space.
150         if (existing_data->size() + new_data_size > MAX_REQUEST_SIZE) {
151                 return RP_OUT_OF_SPACE;
152         }       
153
154         // See if we have \r\n\r\n anywhere in the request. We start three bytes
155         // before what we just appended, in case we just got the final character.
156         size_t existing_data_bytes = existing_data->size();
157         existing_data->append(string(new_data, new_data + new_data_size));
158         
159         const size_t start_at = (existing_data_bytes >= 3 ? existing_data_bytes - 3 : 0);
160         const char *ptr = reinterpret_cast<char *>(
161                 memmem(existing_data->data() + start_at, existing_data->size() - start_at,
162                        "\r\n\r\n", 4));
163         if (ptr == NULL) {
164                 return RP_NOT_FINISHED_YET;
165         }
166         if (ptr != existing_data->data() + existing_data->size() - 4) {
167                 return RP_EXTRA_DATA;
168         }
169         return RP_FINISHED;
170 }