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