Mercurial > mplayer.hg
annotate subopt-helper.c @ 15231:e183a591efee
More fixes by The Wanderer
author | rtognimp |
---|---|
date | Wed, 20 Apr 2005 22:07:08 +0000 |
parents | 2ef20aa3623b |
children | e678e306068e |
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 ); | |
35 | |
36 /** | |
37 * \brief Try to parse all options in str and fail if it was not possible. | |
38 * | |
39 * \param str Pointer to the zero terminated string to be parsed. | |
40 * \param opts Pointer to a options array. The array must be terminated | |
41 * with an element having set name to NULL in its opt_t structure. | |
42 * | |
43 * \return The return value is zero if the string could be parsed | |
44 * else a non-zero value is returned. | |
45 * | |
46 */ | |
47 int subopt_parse( char const * const str, opt_t * opts ) | |
48 { | |
49 int parse_err = 0, idx; | |
50 unsigned int parse_pos = 0; | |
51 | |
52 /* Initialize set member to false. * | |
53 * It is set to true if it was found in str */ | |
54 for ( idx=0; opts[idx].name; ++idx ) | |
55 { | |
56 opts[idx].set = 0; | |
57 } | |
58 | |
59 if ( str ) | |
60 { | |
61 while ( str[parse_pos] && !parse_err ) | |
62 { | |
63 int next = 0; | |
64 | |
65 idx = 0; // reset index for the below loop | |
66 while ( opts[idx].name ) | |
67 { | |
68 int opt_len; | |
69 int substr_len; | |
70 | |
71 // get length of the option we test against */ | |
72 opt_len = strlen( opts[idx].name ); | |
73 | |
74 // get length of the current substring of str */ | |
75 { | |
76 char * delim, * arg_delim; | |
77 | |
78 /* search nearest delimiter ( option or argument delimiter ) */ | |
79 delim = strchr( &str[parse_pos], ':' ); | |
80 arg_delim = strchr( &str[parse_pos], '=' ); | |
81 | |
82 if ( ( delim && arg_delim && delim > arg_delim ) || | |
83 delim == NULL ) | |
84 { | |
85 delim = strchr( &str[parse_pos], '=' ); | |
86 } | |
87 | |
88 substr_len = delim ? // is a delim present | |
89 delim - &str[parse_pos] : // yes | |
90 strlen( &str[parse_pos] ); // no, end of string | |
91 } | |
92 | |
93 //printf( "substr_len=%d, opt_len=%d\n", substr_len, opt_len ); | |
94 | |
95 /* Check if the length of the current option matches the * | |
96 * length of the option we want to test again. */ | |
97 if ( substr_len == opt_len ) | |
98 { | |
99 /* check if option was activated/deactivated */ | |
100 if( strncmp( &str[parse_pos], opts[idx].name, opt_len ) == 0 ) | |
101 { | |
102 /* option was found */ | |
103 opts[idx].set = 1; next = 1; | |
104 | |
105 assert( opts[idx].valp && "Need a pointer to store the arg!" ); | |
106 | |
107 /* type specific code */ | |
108 if ( opts[idx].type == OPT_ARG_BOOL ) | |
109 { | |
110 /* Handle OPT_ARG_BOOL seperately so * | |
111 * the others can share code. */ | |
112 | |
113 /* set option to true */ | |
114 *((int *)(opts[idx].valp)) = 1; | |
115 | |
116 /* increment position */ | |
117 parse_pos += opt_len; | |
118 } | |
119 else | |
120 { | |
121 /* Type is not OPT_ARG_BOOL, means we have to parse * | |
122 * for the arg delimiter character and eventually * | |
123 * call a test function. */ | |
124 char const * last; | |
125 | |
126 /* increment position to check for arg */ | |
127 parse_pos += opt_len; | |
128 | |
129 if ( str[parse_pos] != '=' ) | |
130 { | |
131 parse_err = 1; break; | |
132 } | |
133 | |
134 /* '=' char was there, so let's move after it */ | |
135 ++parse_pos; | |
136 | |
137 switch ( opts[idx].type ) | |
138 { | |
139 case OPT_ARG_INT: | |
140 last = parse_int( &str[parse_pos], | |
141 (int *)opts[idx].valp ); | |
142 | |
143 break; | |
144 case OPT_ARG_STR: | |
145 last = parse_str( &str[parse_pos], | |
146 (strarg_t *)opts[idx].valp ); | |
147 break; | |
14538
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
148 case OPT_ARG_MSTRZ: |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
149 { |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
150 char **valp = opts[idx].valp; |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
151 strarg_t tmp; |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
152 tmp.str = NULL; |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
153 tmp.len = 0; |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
154 last = parse_str( &str[parse_pos], &tmp ); |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
155 if (*valp) |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
156 free(*valp); |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
157 *valp = NULL; |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
158 if (tmp.str && tmp.len > 0) { |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
159 *valp = malloc(tmp.len + 1); |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
160 memcpy(*valp, tmp.str, tmp.len); |
14572 | 161 (*valp)[tmp.len] = 0; |
14538
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
162 } |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
163 break; |
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
164 } |
14281 | 165 default: |
166 assert( 0 && "Arg type of suboption doesn't exist!" ); | |
167 last = NULL; // break parsing! | |
168 } | |
169 | |
170 /* was the conversion succesful? */ | |
171 if ( !last ) | |
172 { | |
173 parse_err = 1; break; | |
174 } | |
175 | |
176 /* make test if supplied */ | |
177 if ( opts[idx].test && !opts[idx].test( opts[idx].valp ) ) | |
178 { | |
179 parse_err = 1; break; | |
180 } | |
181 | |
182 /* we succeded, set position */ | |
183 parse_pos = last - str; | |
184 } | |
185 } | |
186 } | |
187 else if ( substr_len == opt_len+2 ) | |
188 { | |
189 if ( opts[idx].type == OPT_ARG_BOOL && // check for no<opt> | |
190 strncmp( &str[parse_pos], "no", 2 ) == 0 && | |
191 strncmp( &str[parse_pos+2], opts[idx].name, opt_len ) == 0 ) | |
192 { | |
193 /* option was found but negated */ | |
194 opts[idx].set = 1; next = 1; | |
195 | |
196 /* set arg to false */ | |
197 *((int *)(opts[idx].valp)) = 0; | |
198 | |
199 /* increment position */ | |
200 parse_pos += opt_len+2; | |
201 } | |
202 } | |
203 | |
204 ++idx; // test against next option | |
205 | |
206 /* break out of the loop, if this subopt is processed */ | |
207 if ( next ) { break; } | |
208 } | |
209 | |
210 /* if we had a valid suboption the current pos should * | |
211 * equal the delimiter char, which should be ':' for * | |
212 * suboptions. */ | |
213 if ( !parse_err && str[parse_pos] == ':' ) { ++parse_pos; } | |
214 else if ( str[parse_pos] ) { parse_err = 1; } | |
215 } | |
216 } | |
217 | |
218 /* if an error was encountered */ | |
219 if (parse_err) | |
220 { | |
221 unsigned int i; | |
222 mp_msg( MSGT_VO, MSGL_FATAL, "Could not parse arguments at the position indicated below:\n%s\n", str ); | |
223 for ( i = 0; i < parse_pos; ++i ) | |
224 { | |
225 mp_msg(MSGT_VO, MSGL_FATAL, " "); | |
226 } | |
227 mp_msg(MSGT_VO, MSGL_FATAL, "^\n"); | |
228 | |
229 return -1; | |
230 } | |
231 | |
232 /* we could parse everything */ | |
233 return 0; | |
234 } | |
235 | |
236 static char const * parse_int( char const * const str, int * const valp ) | |
237 { | |
238 char * endp; | |
239 | |
240 assert( str && "parse_int(): str == NULL" ); | |
241 | |
242 *valp = (int)strtol( str, &endp, 0 ); | |
243 | |
244 /* nothing was converted */ | |
245 if ( str == endp ) { return NULL; } | |
246 | |
247 return endp; | |
248 } | |
249 | |
250 static char const * parse_str( char const * const str, strarg_t * const valp ) | |
251 { | |
252 char const * match = strchr( str, ':' ); | |
253 | |
254 if ( !match ) | |
14294
90bcd37dba7f
fix string argument parsing (e.g. one char strings were not accepted)
reimar
parents:
14281
diff
changeset
|
255 match = &str[strlen(str)]; |
90bcd37dba7f
fix string argument parsing (e.g. one char strings were not accepted)
reimar
parents:
14281
diff
changeset
|
256 |
90bcd37dba7f
fix string argument parsing (e.g. one char strings were not accepted)
reimar
parents:
14281
diff
changeset
|
257 // empty string or too long |
14538
00c3c4111017
New suboption type: malloc'ed, zero terminated string
reimar
parents:
14294
diff
changeset
|
258 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
|
259 return NULL; |
14281 | 260 |
261 valp->len = match - str; | |
262 valp->str = str; | |
263 | |
264 return match; | |
265 } | |
14736 | 266 |
267 | |
268 /*** common test functions ***/ | |
269 | |
270 /** \brief Test if i is not negative */ | |
271 int int_non_neg( int * i ) | |
272 { | |
273 if ( *i < 0 ) { return 0; } | |
274 | |
275 return 1; | |
276 } | |
277 /** \brief Test if i is positive. */ | |
278 int int_pos( int * i ) | |
279 { | |
280 if ( *i > 0 ) { return 1; } | |
281 | |
282 return 0; | |
283 } |