]> git.sesse.net Git - cubemap/blob - parse.cpp
Move stream creation out of main().
[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 string fetch_config_string(const vector<ConfigLine> &config, const string &keyword,
117                            ParameterType parameter_type, const string &default_value)
118 {
119         assert(parameter_type == PARAMATER_MANDATORY || parameter_type == PARAMETER_OPTIONAL);
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                 return config[i].arguments[0];
130         }
131         if (parameter_type == PARAMATER_MANDATORY) {
132                 fprintf(stderr, "ERROR: Missing '%s' statement in config file.\n",
133                         keyword.c_str());
134                 exit(1);
135         } else {
136                 return default_value;
137         }
138 }
139
140 int fetch_config_int(const std::vector<ConfigLine> &config, const std::string &keyword,
141                      int min_limit, int max_limit,
142                      ParameterType parameter_type, int default_value)
143 {
144         assert(parameter_type == PARAMATER_MANDATORY || parameter_type == PARAMETER_OPTIONAL);
145         bool value_found = false;
146         int value = -1;
147         for (unsigned i = 0; i < config.size(); ++i) {
148                 if (config[i].keyword != keyword) {
149                         continue;
150                 }
151                 if (config[i].parameters.size() > 0 ||
152                     config[i].arguments.size() != 1) {
153                         fprintf(stderr, "ERROR: '%s' takes one argument and no parameters\n", keyword.c_str());
154                         exit(1);
155                 }
156                 value_found = true;
157                 value = atoi(config[i].arguments[0].c_str());  // TODO: verify int validity.
158         }
159         if (!value_found) {
160                 if (parameter_type == PARAMETER_OPTIONAL) {
161                         return default_value;
162                 }
163                 fprintf(stderr, "ERROR: Missing '%s' statement in config file.\n",
164                         keyword.c_str());
165                 exit(1);
166         }
167         if (value < min_limit || value > max_limit) {
168                 fprintf(stderr, "ERROR: '%s' is set to %d, must be in [%d,%d]\n",
169                         keyword.c_str(), value, min_limit, max_limit);
170                 exit(1);
171         }
172         return value;
173 }
174
175 #define MAX_REQUEST_SIZE 16384  /* 16 kB. */
176
177 RequestParseStatus wait_for_double_newline(string *existing_data, const char *new_data, size_t new_data_size)
178 {
179         // Guard against overlong requests gobbling up all of our space.
180         if (existing_data->size() + new_data_size > MAX_REQUEST_SIZE) {
181                 return RP_OUT_OF_SPACE;
182         }       
183
184         // See if we have \r\n\r\n anywhere in the request. We start three bytes
185         // before what we just appended, in case we just got the final character.
186         size_t existing_data_bytes = existing_data->size();
187         existing_data->append(string(new_data, new_data + new_data_size));
188         
189         const size_t start_at = (existing_data_bytes >= 3 ? existing_data_bytes - 3 : 0);
190         const char *ptr = reinterpret_cast<char *>(
191                 memmem(existing_data->data() + start_at, existing_data->size() - start_at,
192                        "\r\n\r\n", 4));
193         if (ptr == NULL) {
194                 return RP_NOT_FINISHED_YET;
195         }
196         if (ptr != existing_data->data() + existing_data->size() - 4) {
197                 return RP_EXTRA_DATA;
198         }
199         return RP_FINISHED;
200 }