updated README
[k8sterm.git] / src / iniparse.c
blob1c888f0d469f83e70e27a6271932c969fbeb68b3
1 // parse the argument list
2 // return error message or NULL
3 // format:
4 // '*': skip
5 // 's': string (char *)
6 // 'i': integer (int *)
7 // 'b': boolean (int *)
8 // '|': optional arguments follows
9 // '.': stop parsing, ignore rest
10 // 'R': stop parsing, set rest ptr (char *)
11 // string modifiers (also for 'R'):
12 // '!' -- don't allow empty strings
13 // '-' -- trim spaces
14 // int modifiers:
15 // {lo,hi}
16 // {,hi}
17 // {lo}
18 // WARNING! `line` will be modified!
19 // WARNING! string pointers will point INSIDE `line`, so don't discard it!
20 // UGLY! REWRITE!
21 const char *iniParseArguments (char *line, const char *fmt, ...) {
22 va_list ap;
23 int inOptional = 0;
25 if (line == NULL) return "alas";
26 trimstr(line);
27 va_start(ap, fmt);
28 while (*fmt) {
29 char spec = *fmt++, *args;
31 if (spec == '|') { inOptional = 1; continue; }
32 if (spec == '.') { va_end(ap); return NULL; }
34 while (*line && isspace(*line)) ++line;
35 if (*line == '#') *line = 0;
37 if (spec == 'R') {
38 char **p = va_arg(ap, char **);
39 int noempty = 0;
41 while (*fmt) {
42 if (*fmt == '!') { ++fmt; noempty = 1; }
43 else if (*fmt == '-') { ++fmt; trimstr(line); }
44 else break;
46 va_end(ap);
47 if (noempty && !line[0]) return "invalid empty arg";
48 if (p != NULL) *p = line;
49 return NULL;
52 if (!line[0]) {
53 // end of line, stop right here
54 va_end(ap);
55 if (!inOptional) return "out of args";
56 return NULL;
59 args = line;
61 char *dest = args, qch = '#';
62 int n;
64 if (line[0] == '"' || line[0] == '\'') qch = *line++;
66 while (*line && *line != qch) {
67 if (qch == '#' && isspace(*line)) break;
69 if (*line == '\\') {
70 if (!line[1]) { va_end(ap); return "invalid escape"; }
71 switch (*(++line)) {
72 case 'n': *dest++ = '\n'; ++line; break;
73 case 'r': *dest++ = '\r'; ++line; break;
74 case 't': *dest++ = '\t'; ++line; break;
75 case 'a': *dest++ = '\a'; ++line; break;
76 case 'e': *dest++ = '\x1b'; ++line; break; // esc
77 case 's': *dest++ = ' '; ++line; break;
78 case 'x': // hex
79 ++line;
80 if (!isxdigit(*line)) { va_end(ap); return "invalid hex escape"; }
81 n = toupper(*line)-'0'; if (n > 9) n -= 7;
82 ++line;
83 if (isxdigit(*line)) {
84 int b = toupper(*line)-'0'; if (b > 9) b -= 7;
86 n = (n*16)+b;
87 ++line;
89 *dest++ = n;
90 break;
91 case '0': // octal
92 n = 0;
93 for (int f = 0; f < 4; ++f) {
94 if (*line < '0' || *line > '7') break;
95 n = (n*8)+(line[0]-'0');
96 if (n > 255) { va_end(ap); return "invalid oct escape"; }
97 ++line;
99 if (n == 0) { va_end(ap); return "invalid oct escape"; }
100 *dest++ = n;
101 break;
102 case '1'...'9': // decimal
103 n = 0;
104 for (int f = 0; f < 3; ++f) {
105 if (*line < '0' || *line > '9') break;
106 n = (n*8)+(line[0]-'0');
107 if (n > 255) { va_end(ap); return "invalid dec escape"; }
108 ++line;
110 if (n == 0) { va_end(ap); return "invalid oct escape"; }
111 *dest++ = n;
112 break;
113 default:
114 *dest++ = *line++;
115 break;
117 } else {
118 *dest++ = *line++;
121 if (qch != '#') {
122 if (*line != qch) return "unfinished string";
123 if (*line) ++line;
124 } else if (*line && *line != '#') ++line;
125 *dest = 0;
127 // now process and convert argument
128 switch (spec) {
129 case '*': /* skip */
130 break;
131 case 's': { /* string */
132 int noempty = 0, trim = 0;
133 char **p;
135 for (;;) {
136 if (*fmt == '!') { noempty = 1; ++fmt; }
137 else if (*fmt == '-') { trim = 1; ++fmt; }
138 else break;
141 if (trim) trimstr(args);
143 if (noempty && !args[0]) { va_end(ap); return "invalid empty string"; }
144 p = va_arg(ap, char **);
145 if (p != NULL) *p = args;
146 } break;
147 case 'i': /* int */
148 if (!args[0]) {
149 va_end(ap);
150 return "invalid integer";
151 } else {
152 int *p = va_arg(ap, int *);
153 long int n;
154 char *eptr;
156 trimstr(args);
157 n = strtol(args, &eptr, 0);
158 if (*eptr) { va_end(ap); return "invalid integer"; }
160 if (*fmt == '{') {
161 // check min/max
162 int minmax[2], haveminmax[2];
164 haveminmax[0] = haveminmax[1] = 0;
165 minmax[0] = minmax[1] = 0;
166 ++fmt;
167 for (int f = 0; f < 2; ++f) {
168 if (isdigit(*fmt) || *fmt == '-' || *fmt == '+') {
169 int neg = 0;
170 haveminmax[f] = 1;
171 if (*fmt == '-') neg = 1;
172 if (!isdigit(*fmt)) {
173 ++fmt;
174 if (!isdigit(*fmt)) { va_end(ap); return "invalid integer bounds"; }
176 while (isdigit(*fmt)) {
177 minmax[f] = minmax[f]*10+(fmt[0]-'0');
178 ++fmt;
180 if (neg) minmax[f] = -minmax[f];
181 //fprintf(stderr, "got: %d\n", minmax[f]);
183 if (*fmt == ',') {
184 if (f == 1) { va_end(ap); return "invalid integer bounds: extra comma"; }
185 // do nothing, we are happy
186 ++fmt;
187 } else if (*fmt == '}') {
188 // ok, done
189 break;
190 } else { va_end(ap); return "invalid integer bounds"; }
192 if (*fmt != '}') { va_end(ap); return "invalid integer bounds"; }
193 ++fmt;
195 //fprintf(stderr, "b: (%d,%d) (%d,%d)\n", haveminmax[0], minmax[0], haveminmax[1], minmax[1]);
196 if ((haveminmax[0] && n < minmax[0]) || (haveminmax[1] && n > minmax[1])) { va_end(ap); return "integer out of bounds"; }
199 if (p) *p = n;
201 break;
202 case 'b': { /* bool */
203 int *p = va_arg(ap, int *);
205 trimstr(args);
206 if (!args[0]) { va_end(ap); return "invalid boolean"; }
207 if (strcasecmp(args, "true") == 0 || strcasecmp(args, "on") == 0 ||
208 strcasecmp(args, "tan") == 0 || strcasecmp(args, "1") == 0 ||
209 strcasecmp(args, "yes") == 0) {
210 if (p) *p = 1;
211 } else if (strcasecmp(args, "false") == 0 || strcasecmp(args, "off") == 0 ||
212 strcasecmp(args, "ona") == 0 || strcasecmp(args, "0") == 0 ||
213 strcasecmp(args, "no") == 0) {
214 if (p) *p = 0;
215 } else {
216 va_end(ap);
217 return "invalid boolean";
219 } break;
220 default:
221 va_end(ap);
222 return "invalid format specifier";
225 va_end(ap);
226 while (*line && isspace(*line)) ++line;
227 if (!line[0] || line[0] == '#') return NULL;
228 return "extra args";