42 |
44 |
43 |
45 |
44 /* */ |
46 /* */ |
45 |
47 |
46 |
48 |
47 static int read_statement(const ConfOpt **opt_ret, Token *tokens, |
49 static int read_statement(Tokenizer *tokz, Token *tokens, int *ntok_ret) |
48 int *ntok_ret, Tokenizer *tokz, |
|
49 const ConfOpt *options) |
|
50 { |
50 { |
51 int ntokens=0; |
51 int ntokens=0; |
52 Token *tok=NULL; |
52 Token *tok=NULL; |
53 const ConfOpt *opt=NULL; |
53 int had_comma=0; /* 0 - no, 1 - yes, 2 - not had, not expected */ |
54 int had_comma=0; |
54 int retval=0; |
55 int retval=P_OK; |
|
56 int e=0; |
55 int e=0; |
57 int e_line=0; |
|
58 |
56 |
59 while(1){ |
57 while(1){ |
|
58 tok=&tokens[ntokens]; |
|
59 |
|
60 if(!tokz_get_token(tokz, tok)){ |
|
61 e=1; |
|
62 continue; |
|
63 } |
|
64 |
60 if(ntokens==MAX_TOKENS-1){ |
65 if(ntokens==MAX_TOKENS-1){ |
61 e=E_TOKZ_TOKEN_LIMIT; |
66 e=E_TOKZ_TOKEN_LIMIT; |
62 goto ret_err; |
67 tokz_warn_error(tokz, tok->line, e); |
63 } |
68 if(!(tokz->flags&TOKZ_ERROR_TOLERANT)) |
64 |
69 break; |
65 tok=&tokens[ntokens]; |
70 }else{ |
66 |
71 ntokens++; |
67 if(!tokz_get_token(tokz, tok)) |
72 } |
68 goto ret_err; |
|
69 |
|
70 ntokens++; |
|
71 |
73 |
72 if(!TOK_IS_OP(tok)){ |
74 if(!TOK_IS_OP(tok)){ |
73 if(ntokens==1 && !had_comma){ |
75 if(ntokens==1 && !had_comma){ |
74 if(!TOK_IS_IDENT(tok)){ |
76 /*if(!TOK_IS_IDENT(tok)){ |
75 e=E_TOKZ_IDENTIFIER_EXPECTED; |
77 e=E_TOKZ_IDENTIFIER_EXPECTED; |
76 goto ret_err; |
78 goto handle_error; |
77 } |
79 }*/ |
78 |
80 |
79 /* find the option */ |
|
80 for(opt=options; opt->optname; opt++){ |
|
81 if(strcmp(opt->optname, TOK_IDENT_VAL(tok))==0) |
|
82 break; |
|
83 } |
|
84 |
|
85 if(opt->optname==NULL){ |
|
86 /* common opt? include, etc. */ |
|
87 for(opt=common_opts; opt->optname; opt++){ |
|
88 if(strcmp(opt->optname, TOK_IDENT_VAL(tok))==0) |
|
89 break; |
|
90 } |
|
91 if(opt->optname==NULL){ |
|
92 e=E_TOKZ_UNKNOWN_OPTION; |
|
93 e_line=tok->line; |
|
94 retval=P_ERROR; |
|
95 } |
|
96 } |
|
97 |
|
98 had_comma=2; |
81 had_comma=2; |
99 }else{ |
82 }else{ |
100 if(!had_comma){ |
83 if(had_comma==0) |
101 e=E_TOKZ_SYNTAX; |
84 goto syntax; |
102 goto ret_err; |
|
103 } |
|
104 |
85 |
105 had_comma=0; |
86 had_comma=0; |
106 } |
87 } |
107 continue; |
88 continue; |
108 } |
89 } |
109 |
90 |
110 /* It is an operator */ |
91 /* It is an operator */ |
|
92 ntokens--; |
111 |
93 |
112 switch(TOK_OP_VAL(tok)){ |
94 switch(TOK_OP_VAL(tok)){ |
113 case OP_SCOLON: |
95 case OP_SCOLON: |
114 if(opt){ |
96 retval=(ntokens==0 ? P_NONE : P_STMT_NS); |
115 if(had_comma || opt->opts){ |
|
116 e=E_TOKZ_SYNTAX; |
|
117 goto ret_err; |
|
118 } |
|
119 goto ret_success; |
|
120 } |
|
121 break; |
97 break; |
122 |
98 |
123 case OP_NEXTLINE: |
99 case OP_NEXTLINE: |
124 if(had_comma==1){ |
100 retval=(ntokens==0 ? P_NONE : P_STMT); |
125 e=E_TOKZ_SYNTAX; |
101 break; |
126 e_line=tok->line-1; |
102 |
127 goto ret_err2; |
103 case OP_L_BRC: |
128 } |
104 retval=(ntokens==0 ? P_BEG_SECT : P_STMT_SECT); |
129 |
105 break; |
130 if(opt && !opt->opts) |
106 |
131 goto ret_success; |
107 case OP_R_BRC: |
132 break; |
108 if(ntokens==0){ |
133 |
109 retval=P_END_SECT; |
|
110 }else{ |
|
111 tokz_unget_token(tokz, tok); |
|
112 retval=P_STMT_NS; |
|
113 } |
|
114 break; |
|
115 |
134 case OP_EOF: |
116 case OP_EOF: |
|
117 retval=(ntokens==0 ? P_EOF : P_STMT_NS); |
|
118 |
135 if(had_comma==1){ |
119 if(had_comma==1){ |
136 e=E_TOKZ_UNEXPECTED_EOF; |
120 e=E_TOKZ_UNEXPECTED_EOF; |
137 goto ret_err; |
121 goto handle_error; |
138 } |
122 } |
139 |
123 |
140 if(opt && opt->opts){ |
124 goto end; |
141 e=E_TOKZ_UNEXPECTED_EOF; |
|
142 goto ret_err; |
|
143 } |
|
144 |
|
145 retval=P_EOF; |
|
146 goto ret_success; |
|
147 |
|
148 case OP_R_BRC: |
|
149 if(had_comma==1){ |
|
150 e=E_TOKZ_SYNTAX; |
|
151 goto ret_err; |
|
152 } |
|
153 |
|
154 if(opt && opt->opts){ |
|
155 e=E_TOKZ_SYNTAX; |
|
156 goto ret_err; |
|
157 } |
|
158 |
|
159 retval=P_END_SECT; |
|
160 goto ret_success; |
|
161 |
|
162 case OP_L_BRC: |
|
163 if(had_comma==1 || !opt || !opt->opts){ |
|
164 e=E_TOKZ_SYNTAX; |
|
165 goto ret_err; |
|
166 } |
|
167 |
|
168 retval=P_BEG_SECT; |
|
169 goto ret_success; |
|
170 |
125 |
171 case OP_COMMA: |
126 case OP_COMMA: |
172 if(had_comma){ |
127 if(had_comma!=0) |
173 e=E_TOKZ_SYNTAX; |
128 goto syntax; |
174 goto ret_err; |
129 |
175 } |
|
176 had_comma=1; |
130 had_comma=1; |
177 break; |
131 continue; |
178 |
132 |
179 default: |
133 default: |
180 e=E_TOKZ_SYNTAX; |
134 goto syntax; |
181 goto ret_err; |
135 } |
182 } |
136 |
183 |
137 if(had_comma!=1) |
184 ntokens--; |
138 break; |
185 } |
139 |
186 |
140 syntax: |
187 ret_err: |
141 e=E_TOKZ_SYNTAX; |
188 e_line=tok->line; |
142 handle_error: |
189 ret_err2: |
143 tokz_warn_error(tokz, tok->line, e); |
190 retval=P_ERROR; |
144 |
191 |
145 if(!(tokz->flags&TOKZ_ERROR_TOLERANT) || retval!=0) |
192 ret_success: |
146 break; |
193 if(retval==P_ERROR && e!=0) |
147 } |
194 tokz_warn_error(tokz, e_line, e); |
148 |
195 |
149 end: |
196 *opt_ret=opt; |
150 if(e!=0) |
|
151 retval=-retval; |
|
152 |
197 *ntok_ret=ntokens; |
153 *ntok_ret=ntokens; |
198 |
154 |
199 return retval; |
155 return retval; |
200 } |
156 } |
201 |
157 |
202 |
158 |
|
159 static bool find_beg_sect(Tokenizer *tokz) |
|
160 { |
|
161 Token tok; |
|
162 |
|
163 while(tokz_get_token(tokz, &tok)){ |
|
164 if(TOK_IS_OP(&tok)){ |
|
165 if(TOK_OP_VAL(&tok)==OP_NEXTLINE) |
|
166 continue; |
|
167 |
|
168 if(TOK_OP_VAL(&tok)==OP_SCOLON) |
|
169 return FALSE; |
|
170 |
|
171 if(TOK_OP_VAL(&tok)==OP_L_BRC) |
|
172 return TRUE; |
|
173 } |
|
174 |
|
175 tokz_unget_token(tokz, &tok); |
|
176 break; |
|
177 } |
|
178 return FALSE; |
|
179 } |
|
180 |
|
181 |
203 /* */ |
182 /* */ |
204 |
183 |
205 |
184 |
206 static bool call_end_sect(Tokenizer *tokz, const ConfOpt *options) |
185 static const ConfOpt* lookup_option(const ConfOpt *opts, const char *name) |
|
186 { |
|
187 while(opts->optname!=NULL){ |
|
188 if(strcmp(opts->optname, name)==0) |
|
189 return opts; |
|
190 opts++; |
|
191 } |
|
192 return NULL; |
|
193 } |
|
194 |
|
195 |
|
196 static bool call_end_sect(Tokenizer *tokz, const ConfOpt *opts) |
207 { |
197 { |
208 bool retval=TRUE; |
198 opts=lookup_option(opts, "#end"); |
209 |
199 if(opts!=NULL) |
210 while(options->optname){ |
200 return opts->fn(tokz, 0, NULL); |
211 if(strcmp(options->optname, "#end")==0){ |
201 |
212 retval=options->fn(tokz, 0, NULL); |
202 return TRUE; |
213 break; |
203 } |
214 } |
204 |
215 options++; |
205 |
216 } |
206 static bool call_cancel_sect(Tokenizer *tokz, const ConfOpt *opts) |
217 |
207 { |
218 return retval; |
208 opts=lookup_option(opts, "#cancel"); |
219 } |
209 if(opts!=NULL) |
220 |
210 return opts->fn(tokz, 0, NULL); |
221 |
211 |
222 static void call_cancel_sect(Tokenizer *tokz, const ConfOpt *options) |
212 return TRUE; |
223 { |
|
224 while(options->optname){ |
|
225 if(strcmp(options->optname, "#cancel")==0){ |
|
226 options->fn(tokz, 0, NULL); |
|
227 break; |
|
228 } |
|
229 options++; |
|
230 } |
|
231 } |
213 } |
232 |
214 |
233 |
215 |
234 /* */ |
216 /* */ |
235 |
217 |
236 |
218 |
237 /* Does the parsing work |
|
238 */ |
|
239 bool parse_config_tokz(Tokenizer *tokz, const ConfOpt *options) |
219 bool parse_config_tokz(Tokenizer *tokz, const ConfOpt *options) |
240 { |
220 { |
241 Token tokens[MAX_TOKENS]; |
221 Token tokens[MAX_TOKENS]; |
242 bool alloced_optstack=FALSE; |
222 bool alloced_optstack=FALSE; |
243 int i, t, ntokens; |
223 int i, t, ntokens=0; |
244 int init_nest_lvl; |
224 int init_nest_lvl; |
245 bool had_error=FALSE; |
225 bool had_error; |
|
226 int errornest=0; |
246 |
227 |
247 /* Allocate tokz->optstack if it does not yet exist (if it does, |
228 /* Allocate tokz->optstack if it does not yet exist (if it does, |
248 * we have been called from an option handler) |
229 * we have been called from an option handler) |
249 */ |
230 */ |
250 if(!tokz->optstack){ |
231 if(!tokz->optstack){ |
261 init_nest_lvl=tokz->nest_lvl; |
242 init_nest_lvl=tokz->nest_lvl; |
262 } |
243 } |
263 |
244 |
264 tokz->optstack[init_nest_lvl]=options; |
245 tokz->optstack[init_nest_lvl]=options; |
265 |
246 |
266 for(i=0;i<MAX_TOKENS;i++) |
247 for(i=0; i<MAX_TOKENS; i++) |
267 tok_init(&tokens[i]); |
248 tok_init(&tokens[i]); |
268 |
249 |
269 |
250 |
270 /* The loop |
251 /* The loop |
271 */ |
252 */ |
272 while(1){ |
253 while(1){ |
273 t=read_statement(&options, tokens, &ntokens, |
254 had_error=FALSE; |
274 tokz, tokz->optstack[tokz->nest_lvl]); |
255 |
275 |
|
276 had_error=(t==P_ERROR); |
|
277 |
|
278 /* Check that arguments are ok */ |
|
279 if(!had_error && options) |
|
280 had_error=!check_args(tokz, tokens, ntokens, options->argfmt); |
|
281 |
|
282 if(tokz->flags&TOKZ_PARSER_INDENT_MODE) |
|
283 verbose_indent(tokz->nest_lvl); |
|
284 |
|
285 /* New section? */ |
|
286 if(t==P_BEG_SECT){ |
|
287 if(tokz->nest_lvl==MAX_NEST-1){ |
|
288 tokz_warn_error(tokz, tokz->line, E_TOKZ_MAX_NEST); |
|
289 had_error=TRUE; |
|
290 while(ntokens--) |
|
291 tok_free(&tokens[ntokens]); |
|
292 break; |
|
293 }else{ |
|
294 tokz->optstack[++tokz->nest_lvl]=options->opts; |
|
295 } |
|
296 } |
|
297 |
|
298 /* call the handler */ |
|
299 if(!had_error && options && options->fn) |
|
300 had_error=!options->fn(tokz, ntokens-1, tokens); |
|
301 |
|
302 /* free the tokens */ |
256 /* free the tokens */ |
303 while(ntokens--) |
257 while(ntokens--) |
304 tok_free(&tokens[ntokens]); |
258 tok_free(&tokens[ntokens]); |
305 |
259 |
306 switch(t){ |
260 /* read the tokens */ |
307 case P_BEG_SECT: |
261 t=read_statement(tokz, tokens, &ntokens); |
308 if(!had_error) |
262 |
309 continue; |
263 if((had_error=t<0)) |
310 /* #cancel handler should not be called when |
264 t=-t; |
311 * error occured in section begin handler */ |
265 |
312 tokz->nest_lvl--; |
266 switch(t){ |
313 break; |
267 case P_STMT: |
314 |
268 case P_STMT_NS: |
|
269 case P_STMT_SECT: |
|
270 |
|
271 if(errornest) |
|
272 had_error=TRUE; |
|
273 else if(tokz->flags&TOKZ_PARSER_INDENT_MODE) |
|
274 verbose_indent(tokz->nest_lvl); |
|
275 |
|
276 if(!TOK_IS_IDENT(tokens+0)){ |
|
277 had_error=TRUE; |
|
278 tokz_warn_error(tokz, tokens->line, |
|
279 E_TOKZ_IDENTIFIER_EXPECTED); |
|
280 } |
|
281 |
|
282 if(had_error) |
|
283 break; |
|
284 |
|
285 if(t==P_STMT){ |
|
286 if(find_beg_sect(tokz)) |
|
287 t=P_STMT_SECT; |
|
288 } |
|
289 |
|
290 options=lookup_option(tokz->optstack[tokz->nest_lvl], |
|
291 TOK_IDENT_VAL(tokens+0)); |
|
292 if(options==NULL) |
|
293 options=lookup_option(common_opts, TOK_IDENT_VAL(tokens+0)); |
|
294 |
|
295 if(options==NULL){ |
|
296 had_error=TRUE; |
|
297 tokz_warn_error(tokz, tokens->line, E_TOKZ_UNKNOWN_OPTION); |
|
298 }else{ |
|
299 had_error=!check_args(tokz, tokens, ntokens, options->argfmt); |
|
300 } |
|
301 |
|
302 if(had_error) |
|
303 break; |
|
304 |
|
305 if(options->opts!=NULL){ |
|
306 if(t!=P_STMT_SECT){ |
|
307 had_error=TRUE; |
|
308 tokz_warn_error(tokz, tokz->line, E_TOKZ_LBRACE_EXPECTED); |
|
309 }else if(tokz->nest_lvl==MAX_NEST-1){ |
|
310 tokz_warn_error(tokz, tokz->line, E_TOKZ_MAX_NEST); |
|
311 had_error=TRUE; |
|
312 }else{ |
|
313 tokz->optstack[++tokz->nest_lvl]=options->opts; |
|
314 } |
|
315 }else if(t==P_STMT_SECT){ |
|
316 had_error=TRUE; |
|
317 tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX); |
|
318 } |
|
319 |
|
320 if(!had_error && options->fn!=NULL){ |
|
321 had_error=!options->fn(tokz, ntokens, tokens); |
|
322 if(t==P_STMT_SECT && had_error) |
|
323 tokz->nest_lvl--; |
|
324 } |
|
325 break; |
|
326 |
315 case P_EOF: |
327 case P_EOF: |
316 if(tokz_popf(tokz)){ |
328 if(tokz_popf(tokz)){ |
317 if(!had_error) |
329 break; |
318 continue; |
330 }else if(tokz->nest_lvl>0 || errornest>0){ |
319 }else if(tokz->nest_lvl>0){ |
|
320 tokz_warn_error(tokz, 0, E_TOKZ_UNEXPECTED_EOF); |
331 tokz_warn_error(tokz, 0, E_TOKZ_UNEXPECTED_EOF); |
321 had_error=TRUE; |
332 had_error=TRUE; |
322 }else if(!had_error){ |
333 } |
323 had_error=!call_end_sect(tokz, tokz->optstack[0]); |
334 goto eof; |
324 } |
335 |
325 break; |
336 case P_BEG_SECT: |
326 |
337 had_error=TRUE; |
|
338 errornest++; |
|
339 tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX); |
|
340 break; |
|
341 |
327 case P_END_SECT: |
342 case P_END_SECT: |
|
343 if(errornest!=0){ |
|
344 errornest--; |
|
345 break; |
|
346 } |
|
347 |
328 if(tokz->nest_lvl==0){ |
348 if(tokz->nest_lvl==0){ |
329 tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX); |
349 tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX); |
330 had_error=TRUE; |
350 had_error=TRUE; |
331 break; |
351 break; |
332 } |
352 } |
335 had_error=!call_end_sect(tokz, tokz->optstack[tokz->nest_lvl]); |
355 had_error=!call_end_sect(tokz, tokz->optstack[tokz->nest_lvl]); |
336 |
356 |
337 tokz->nest_lvl--; |
357 tokz->nest_lvl--; |
338 |
358 |
339 if(tokz->nest_lvl<init_nest_lvl) |
359 if(tokz->nest_lvl<init_nest_lvl) |
340 break; |
360 goto eof; |
341 |
361 } |
342 /* fall thru */ |
362 |
343 |
363 if(!had_error) |
344 default: |
364 continue; |
345 if(!had_error) |
365 |
346 continue; |
366 if(t==P_STMT_SECT) |
347 } |
367 errornest++; |
348 break; |
368 |
349 } |
369 if(!(tokz->flags&TOKZ_ERROR_TOLERANT)) |
350 |
370 break; |
351 /* On error, call all the #cancel-handlers */ |
371 } |
352 while(had_error && tokz->nest_lvl>=init_nest_lvl){ |
372 |
353 call_cancel_sect(tokz, tokz->optstack[tokz->nest_lvl]); |
373 eof: |
|
374 /* free the tokens */ |
|
375 while(ntokens--) |
|
376 tok_free(&tokens[ntokens]); |
|
377 |
|
378 while(tokz->nest_lvl>=init_nest_lvl){ |
|
379 if(tokz->flags&TOKZ_ERROR_TOLERANT || !had_error) |
|
380 call_end_sect(tokz, tokz->optstack[tokz->nest_lvl]); |
|
381 else |
|
382 call_cancel_sect(tokz, tokz->optstack[tokz->nest_lvl]); |
354 tokz->nest_lvl--; |
383 tokz->nest_lvl--; |
355 } |
384 } |
356 |
385 |
357 /* Free optstack if it was alloced by this call */ |
386 /* Free optstack if it was alloced by this call */ |
358 if(alloced_optstack){ |
387 if(alloced_optstack){ |
410 /* |
443 /* |
411 * Argument validity checking stuff |
444 * Argument validity checking stuff |
412 */ |
445 */ |
413 |
446 |
414 |
447 |
415 static bool arg_match(Token *tok, char c) |
448 static int arg_match(Token *tok, char c) |
416 { |
449 { |
417 static const char chs[]={0, 'l', 'd', 'c', 's', 'i', 0, 0}; |
450 static const char chs[]={0, 'l', 'd', 'c', 's', 'i', 0, 0}; |
418 char c2; |
451 char c2; |
419 |
452 |
420 if(c=='.' || c=='*') |
453 if(c=='.' || c=='*') |
421 return TRUE; |
454 return 0; |
422 |
455 |
423 c2=chs[tok->type]; |
456 c2=chs[tok->type]; |
424 |
457 |
425 if(c2==c) |
458 if(c2==c) |
426 return TRUE; |
459 return 0; |
427 |
460 |
428 if(c2=='c' && c=='l'){ |
461 if(c2=='c' && c=='l'){ |
429 TOK_SET_LONG(tok, TOK_CHAR_VAL(tok)); |
462 TOK_SET_LONG(tok, TOK_CHAR_VAL(tok)); |
430 return TRUE; |
463 return 0; |
431 } |
464 } |
432 |
465 |
433 if(c2=='l' && c=='c'){ |
466 if(c2=='l' && c=='c'){ |
434 TOK_SET_CHAR(tok, TOK_LONG_VAL(tok)); |
467 TOK_SET_CHAR(tok, TOK_LONG_VAL(tok)); |
435 return TRUE; |
468 return 0; |
436 } |
469 } |
437 |
470 |
438 if(c2=='l' && c=='d'){ |
471 if(c2=='l' && c=='d'){ |
439 TOK_SET_DOUBLE(tok, TOK_LONG_VAL(tok)); |
472 TOK_SET_DOUBLE(tok, TOK_LONG_VAL(tok)); |
440 return TRUE; |
473 return 0; |
441 } |
474 } |
442 |
475 |
443 return FALSE; |
476 return E_TOKZ_INVALID_ARGUMENT; |
444 } |
477 } |
445 |
478 |
446 |
479 |
447 static bool check_argument(const char **pret, Token *tok, const char *p) |
480 static int check_argument(const char **pret, Token *tok, const char *p) |
448 { |
481 { |
449 int mode=0; |
482 int mode; |
450 |
483 int e=E_TOKZ_TOO_MANY_ARGS; |
|
484 |
|
485 again: |
|
486 mode=0; |
|
487 |
451 if(*p=='*'){ |
488 if(*p=='*'){ |
452 *pret=p; |
489 *pret=p; |
453 return TRUE; |
490 return 0; |
454 }else if(*p=='?'){ |
491 }else if(*p=='?'){ |
455 mode=1; |
492 mode=1; |
456 p++; |
493 p++; |
457 }else if(*p==':'){ |
494 }else if(*p==':'){ |
458 mode=2; |
495 mode=2; |
514 |
553 |
515 static bool check_args(const Tokenizer *tokz, Token *tokens, int ntokens, |
554 static bool check_args(const Tokenizer *tokz, Token *tokens, int ntokens, |
516 const char *fmt) |
555 const char *fmt) |
517 { |
556 { |
518 int i; |
557 int i; |
519 |
558 int e; |
520 if(fmt==NULL) |
559 |
521 return ntokens==2; |
560 if(fmt==NULL){ |
522 |
561 if(ntokens!=1) |
523 for(i=1; i<ntokens-1; i++){ |
562 tokz_warn_error(tokz, tokens[0].line, E_TOKZ_TOO_MANY_ARGS); |
524 if(!check_argument(&fmt, &tokens[i], fmt)){ |
563 return ntokens==1; |
525 tokz_warn_error(tokz, tokens[i].line, |
564 } |
526 *fmt!='\0' ? E_TOKZ_INVALID_ARGUMENT |
565 |
527 : E_TOKZ_TOO_MANY_ARGS); |
566 for(i=1; i<ntokens; i++){ |
|
567 e=check_argument(&fmt, &tokens[i], fmt); |
|
568 if(e!=0){ |
|
569 tokz_warn_error(tokz, tokens[i].line, e); |
528 return FALSE; |
570 return FALSE; |
529 } |
571 } |
530 } |
572 } |
531 |
573 |
532 if(!args_at_end(fmt)){ |
574 if(!args_at_end(fmt)){ |