Mercurial > mplayer.hg
annotate subopt-helper.c @ 28220:e8a6bde9ef9e
cosmetics: Move clean targets to the bottom.
author | diego |
---|---|
date | Mon, 05 Jan 2009 00:01:55 +0000 |
parents | bc9e95184521 |
children | 2b021e3e1000 |
rev | line source |
---|---|
14281 | 1 /** |
2 * \file subopt-helper.c | |
3 * | |
4 * \brief Compensates the suboption parsing code duplication a bit. | |
5 * | |
6 * The routines defined below are there to help you with the | |
7 * suboption parsing. Meaning extracting the options and their | |
8 * values for you and also outputting generic help message if | |
9 * a parse error is encountered. | |
10 * | |
11 * Most stuff happens in the subopt_parse function: if you call it | |
12 * it parses for the passed opts in the passed string. It calls some | |
13 * extra functions for explicit argument parsing ( where the option | |
14 * itself isn't the argument but a value given after the argument | |
15 * delimiter ('='). It also calls your test function if you supplied | |
16 * one. | |
17 * | |
18 */ | |
19 | |
20 #include "subopt-helper.h" | |
21 #include "mp_msg.h" | |
22 | |
23 #include <stdlib.h> | |
24 #include <string.h> | |
14538
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
25 #include <limits.h> |
14281 | 26 #include <assert.h> |
27 | |
28 #ifndef MPDEBUG | |
29 #define NDEBUG | |
30 #endif | |
31 | |
32 /* prototypes for argument parsing */ | |
33 static char const * parse_int( char const * const str, int * const valp ); | |
34 static char const * parse_str( char const * const str, strarg_t * const valp ); | |
16720 | 35 static char const * parse_float( char const * const str, float * const valp ); |
14281 | 36 |
37 /** | |
38 * \brief Try to parse all options in str and fail if it was not possible. | |
39 * | |
40 * \param str Pointer to the zero terminated string to be parsed. | |
41 * \param opts Pointer to a options array. The array must be terminated | |
42 * with an element having set name to NULL in its opt_t structure. | |
43 * | |
44 * \return The return value is zero if the string could be parsed | |
45 * else a non-zero value is returned. | |
46 * | |
47 */ | |
48 int subopt_parse( char const * const str, opt_t * opts ) | |
49 { | |
50 int parse_err = 0, idx; | |
51 unsigned int parse_pos = 0; | |
52 | |
53 /* Initialize set member to false. * | |
54 * It is set to true if it was found in str */ | |
55 for ( idx=0; opts[idx].name; ++idx ) | |
56 { | |
57 opts[idx].set = 0; | |
58 } | |
59 | |
60 if ( str ) | |
61 { | |
62 while ( str[parse_pos] && !parse_err ) | |
63 { | |
64 int next = 0; | |
65 | |
66 idx = 0; // reset index for the below loop | |
67 while ( opts[idx].name ) | |
68 { | |
69 int opt_len; | |
70 int substr_len; | |
71 | |
72 // get length of the option we test against */ | |
73 opt_len = strlen( opts[idx].name ); | |
74 | |
75 // get length of the current substring of str */ | |
76 { | |
77 char * delim, * arg_delim; | |
78 | |
79 /* search nearest delimiter ( option or argument delimiter ) */ | |
80 delim = strchr( &str[parse_pos], ':' ); | |
81 arg_delim = strchr( &str[parse_pos], '=' ); | |
82 | |
83 if ( ( delim && arg_delim && delim > arg_delim ) || | |
84 delim == NULL ) | |
85 { | |
86 delim = strchr( &str[parse_pos], '=' ); | |
87 } | |
88 | |
89 substr_len = delim ? // is a delim present | |
90 delim - &str[parse_pos] : // yes | |
91 strlen( &str[parse_pos] ); // no, end of string | |
92 } | |
93 | |
94 //printf( "substr_len=%d, opt_len=%d\n", substr_len, opt_len ); | |
95 | |
96 /* Check if the length of the current option matches the * | |
97 * length of the option we want to test again. */ | |
98 if ( substr_len == opt_len ) | |
99 { | |
100 /* check if option was activated/deactivated */ | |
101 if( strncmp( &str[parse_pos], opts[idx].name, opt_len ) == 0 ) | |
102 { | |
103 /* option was found */ | |
104 opts[idx].set = 1; next = 1; | |
105 | |
106 assert( opts[idx].valp && "Need a pointer to store the arg!" ); | |
107 | |
108 /* type specific code */ | |
109 if ( opts[idx].type == OPT_ARG_BOOL ) | |
110 { | |
22283
bc9e95184521
cosmetics: Fix some common typos, sepErate --> sepArate, deciSSion --> deciSion.
diego
parents:
19104
diff
changeset
|
111 /* Handle OPT_ARG_BOOL separately so * |
14281 | 112 * the others can share code. */ |
113 | |
114 /* set option to true */ | |
115 *((int *)(opts[idx].valp)) = 1; | |
116 | |
117 /* increment position */ | |
118 parse_pos += opt_len; | |
119 } | |
120 else | |
121 { | |
122 /* Type is not OPT_ARG_BOOL, means we have to parse * | |
123 * for the arg delimiter character and eventually * | |
124 * call a test function. */ | |
125 char const * last; | |
126 | |
127 /* increment position to check for arg */ | |
128 parse_pos += opt_len; | |
129 | |
130 if ( str[parse_pos] != '=' ) | |
131 { | |
132 parse_err = 1; break; | |
133 } | |
134 | |
135 /* '=' char was there, so let's move after it */ | |
136 ++parse_pos; | |
137 | |
138 switch ( opts[idx].type ) | |
139 { | |
140 case OPT_ARG_INT: | |
141 last = parse_int( &str[parse_pos], | |
142 (int *)opts[idx].valp ); | |
143 | |
144 break; | |
145 case OPT_ARG_STR: | |
146 last = parse_str( &str[parse_pos], | |
147 (strarg_t *)opts[idx].valp ); | |
148 break; | |
14538
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
149 case OPT_ARG_MSTRZ: |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
150 { |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
151 char **valp = opts[idx].valp; |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
152 strarg_t tmp; |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
153 tmp.str = NULL; |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
154 tmp.len = 0; |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
155 last = parse_str( &str[parse_pos], &tmp ); |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
156 if (*valp) |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
157 free(*valp); |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
158 *valp = NULL; |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
159 if (tmp.str && tmp.len > 0) { |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
160 *valp = malloc(tmp.len + 1); |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
161 memcpy(*valp, tmp.str, tmp.len); |
14572 | 162 (*valp)[tmp.len] = 0; |
14538
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
163 } |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
164 break; |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
165 } |
16720 | 166 case OPT_ARG_FLOAT: |
167 last = parse_float( &str[parse_pos], | |
168 (float *)opts[idx].valp ); | |
169 break; | |
14281 | 170 default: |
171 assert( 0 && "Arg type of suboption doesn't exist!" ); | |
172 last = NULL; // break parsing! | |
173 } | |
174 | |
175 /* was the conversion succesful? */ | |
176 if ( !last ) | |
177 { | |
178 parse_err = 1; break; | |
179 } | |
180 | |
181 /* make test if supplied */ | |
182 if ( opts[idx].test && !opts[idx].test( opts[idx].valp ) ) | |
183 { | |
184 parse_err = 1; break; | |
185 } | |
186 | |
187 /* we succeded, set position */ | |
188 parse_pos = last - str; | |
189 } | |
190 } | |
191 } | |
192 else if ( substr_len == opt_len+2 ) | |
193 { | |
194 if ( opts[idx].type == OPT_ARG_BOOL && // check for no<opt> | |
195 strncmp( &str[parse_pos], "no", 2 ) == 0 && | |
196 strncmp( &str[parse_pos+2], opts[idx].name, opt_len ) == 0 ) | |
197 { | |
198 /* option was found but negated */ | |
199 opts[idx].set = 1; next = 1; | |
200 | |
201 /* set arg to false */ | |
202 *((int *)(opts[idx].valp)) = 0; | |
203 | |
204 /* increment position */ | |
205 parse_pos += opt_len+2; | |
206 } | |
207 } | |
208 | |
209 ++idx; // test against next option | |
210 | |
211 /* break out of the loop, if this subopt is processed */ | |
212 if ( next ) { break; } | |
213 } | |
214 | |
215 /* if we had a valid suboption the current pos should * | |
216 * equal the delimiter char, which should be ':' for * | |
217 * suboptions. */ | |
218 if ( !parse_err && str[parse_pos] == ':' ) { ++parse_pos; } | |
219 else if ( str[parse_pos] ) { parse_err = 1; } | |
220 } | |
221 } | |
222 | |
223 /* if an error was encountered */ | |
224 if (parse_err) | |
225 { | |
226 unsigned int i; | |
227 mp_msg( MSGT_VO, MSGL_FATAL, "Could not parse arguments at the position indicated below:\n%s\n", str ); | |
228 for ( i = 0; i < parse_pos; ++i ) | |
229 { | |
230 mp_msg(MSGT_VO, MSGL_FATAL, " "); | |
231 } | |
232 mp_msg(MSGT_VO, MSGL_FATAL, "^\n"); | |
233 | |
234 return -1; | |
235 } | |
236 | |
237 /* we could parse everything */ | |
238 return 0; | |
239 } | |
240 | |
241 static char const * parse_int( char const * const str, int * const valp ) | |
242 { | |
243 char * endp; | |
244 | |
245 assert( str && "parse_int(): str == NULL" ); | |
246 | |
247 *valp = (int)strtol( str, &endp, 0 ); | |
248 | |
249 /* nothing was converted */ | |
250 if ( str == endp ) { return NULL; } | |
251 | |
252 return endp; | |
253 } | |
254 | |
16720 | 255 static char const * parse_float( char const * const str, float * const valp ) |
256 { | |
257 char * endp; | |
258 | |
259 assert( str && "parse_float(): str == NULL" ); | |
260 | |
16725
2a7220c457a7
1l, strtof is only C99, strtod is ANSI C, so use that instead.
reimar
parents:
16720
diff
changeset
|
261 *valp = strtod( str, &endp ); |
16720 | 262 |
263 /* nothing was converted */ | |
264 if ( str == endp ) { return NULL; } | |
265 | |
266 return endp; | |
267 } | |
268 | |
15733
e678e306068e
support lenght-quoting of strings in subopt parser.
reimar
parents:
14736
diff
changeset
|
269 #define QUOTE_CHAR '%' |
e678e306068e
support lenght-quoting of strings in subopt parser.
reimar
parents:
14736
diff
changeset
|
270 static char const * parse_str( char const * str, strarg_t * const valp ) |
14281 | 271 { |
272 char const * match = strchr( str, ':' ); | |
273 | |
15733
e678e306068e
support lenght-quoting of strings in subopt parser.
reimar
parents:
14736
diff
changeset
|
274 if (str[0] == QUOTE_CHAR) { |
e678e306068e
support lenght-quoting of strings in subopt parser.
reimar
parents:
14736
diff
changeset
|
275 int len = 0; |
e678e306068e
support lenght-quoting of strings in subopt parser.
reimar
parents:
14736
diff
changeset
|
276 str = &str[1]; |
e678e306068e
support lenght-quoting of strings in subopt parser.
reimar
parents:
14736
diff
changeset
|
277 len = (int)strtol(str, (char **)&str, 0); |
e678e306068e
support lenght-quoting of strings in subopt parser.
reimar
parents:
14736
diff
changeset
|
278 if (!str || str[0] != QUOTE_CHAR || (len > strlen(str) - 1)) |
e678e306068e
support lenght-quoting of strings in subopt parser.
reimar
parents:
14736
diff
changeset
|
279 return NULL; |
e678e306068e
support lenght-quoting of strings in subopt parser.
reimar
parents:
14736
diff
changeset
|
280 str = &str[1]; |
e678e306068e
support lenght-quoting of strings in subopt parser.
reimar
parents:
14736
diff
changeset
|
281 match = &str[len]; |
e678e306068e
support lenght-quoting of strings in subopt parser.
reimar
parents:
14736
diff
changeset
|
282 } |
e678e306068e
support lenght-quoting of strings in subopt parser.
reimar
parents:
14736
diff
changeset
|
283 else |
16609 | 284 if (str[0] == '"') { |
285 str = &str[1]; | |
286 match = strchr(str, '"'); | |
287 if (!match) | |
288 return NULL; | |
289 valp->len = match - str; | |
290 valp->str = str; | |
291 return &match[1]; | |
292 } | |
14281 | 293 if ( !match ) |
14294
90bcd37dba7f
fix string argument parsing (e.g. one char strings were not accepted)
reimar
parents:
14281
diff
changeset
|
294 match = &str[strlen(str)]; |
90bcd37dba7f
fix string argument parsing (e.g. one char strings were not accepted)
reimar
parents:
14281
diff
changeset
|
295 |
90bcd37dba7f
fix string argument parsing (e.g. one char strings were not accepted)
reimar
parents:
14281
diff
changeset
|
296 // empty string or too long |
14538
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
297 if ((match == str) || (match - str > INT_MAX)) |
14294
90bcd37dba7f
fix string argument parsing (e.g. one char strings were not accepted)
reimar
parents:
14281
diff
changeset
|
298 return NULL; |
14281 | 299 |
300 valp->len = match - str; | |
301 valp->str = str; | |
302 | |
303 return match; | |
304 } | |
14736 | 305 |
306 | |
307 /*** common test functions ***/ | |
308 | |
309 /** \brief Test if i is not negative */ | |
310 int int_non_neg( int * i ) | |
311 { | |
312 if ( *i < 0 ) { return 0; } | |
313 | |
314 return 1; | |
315 } | |
316 /** \brief Test if i is positive. */ | |
317 int int_pos( int * i ) | |
318 { | |
319 if ( *i > 0 ) { return 1; } | |
320 | |
321 return 0; | |
322 } | |
15734
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
323 |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
324 /*** little helpers */ |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
325 |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
326 /** \brief compare the stings just as strcmp does */ |
19104
2ec2301183cd
marks several read-only string parameters which aren't modified inside the called function as const. Patch by Stefan Huehner, stefan AT huehner-org
reynaldo
parents:
16725
diff
changeset
|
327 int strargcmp(strarg_t *arg, const char *str) { |
15734
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
328 int res = strncmp(arg->str, str, arg->len); |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
329 if (!res && arg->len != strlen(str)) |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
330 res = arg->len - strlen(str); |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
331 return res; |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
332 } |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
333 |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
334 /** \brief compare the stings just as strcasecmp does */ |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
335 int strargcasecmp(strarg_t *arg, char *str) { |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
336 int res = strncasecmp(arg->str, str, arg->len); |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
337 if (!res && arg->len != strlen(str)) |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
338 res = arg->len - strlen(str); |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
339 return res; |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
340 } |
7e4fa8fc255c
helper functions for comparing strarg_t "strings".
reimar
parents:
15733
diff
changeset
|
341 |