completely finished the documentation of the EBUF module (!)
[neuro.git] / src / misc / arg.c
blobe3febdb3442b1a10425beba8298663c1da13af14
2 /*
3 * libneuro, a light weight abstraction of high or lower libraries
4 * and toolkit for applications.
5 * Copyright (C) 2005-2006 Nicholas Niro, Robert Lemay
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include <string.h>
24 #include <other.h>
26 #include <ebuf.h>
27 #include <debug.h>
29 typedef struct DATA
31 char *data;
32 }DATA;
34 /* the struct type that contains the list of options used
35 * by this application.
36 * TODO find a better name
38 typedef struct LOPTIONS
40 char *string;
41 int options;
42 void (*action)(char *data);
43 u32 present;
44 char *data;
45 EBUF *datas;
46 }LOPTIONS;
48 /* struct buffer to keep the input arguments */
49 typedef struct BUFINPUT
51 char *string;
52 u8 type; /* 0 is unknown, 1 is option, 2 is data and 3 is a nest of options */
53 u8 used; /* if this is 0 it will do an error, with a nest of options, at least
54 one option needs to be invalid to make an error. */
55 }BUFINPUT;
57 /* input types */
58 enum
60 TYPE_UNKNOWN, /* 0 */
61 TYPE_OPTION,
62 TYPE_DATA,
63 TYPE_NEST /* 3 */
66 static EBUF *bufinput; /* buffer of argv */
67 static EBUF *loptions; /* Buffer of the list of options */
69 static void
70 clean_loptions(void *src)
72 LOPTIONS *buf;
74 buf = (LOPTIONS*)src;
76 if (!Neuro_EBufIsEmpty(buf->datas))
77 Neuro_CleanEBuf(&buf->datas);
79 if (buf->string)
81 /* printf("cleaning %s\n", buf->string); */
82 free(buf->string);
86 /* finds a data option and diminish its presence by 1 */
88 static void
89 Find_DATA_And_Revoke()
91 LOPTIONS *option;
92 u32 ototal;
94 ototal = Neuro_GiveEBufCount(loptions) + 1;
96 while (ototal-- > 0)
98 option = Neuro_GiveEBuf(loptions, ototal);
100 if (option->string == NULL)
102 option->present--;
108 static void
109 Push_Data(LOPTIONS *option, char *string)
111 DATA *dta;
113 if (!option->datas)
115 Neuro_CreateEBuf(&option->datas);
118 Neuro_AllocEBuf(option->datas, sizeof(DATA*), sizeof(DATA));
120 dta = Neuro_GiveCurEBuf(option->datas);
122 dta->data = string;
126 static int
127 Handle_Option_Argument(LOPTIONS *option, u32 i, char *input_string)
129 BUFINPUT *input;
130 char *temp;
131 int _err = 0;
133 /* completely useless because the caller of this function filters the strings
134 * and only matches those in loptions (meaning none can in theory have a '='
135 * character in them)
137 if ((temp = memchr(input_string, '=', strlen(input_string))))
139 if (temp[1] == '\0')
140 _err = 1;
141 else
143 /* option->data = &temp[1]; */
144 Push_Data(option, &temp[1]);
148 else
150 input = Neuro_GiveEBuf(bufinput, i + 1);
151 if (input)
153 if (input->type == TYPE_DATA)
155 /* option->data = input->string; */
156 Push_Data(option, input->string);
157 input->used = 1;
158 /* Find_DATA_And_Revoke(); */
160 else
161 _err = 1;
163 else
164 _err = 1;
166 input = Neuro_GiveEBuf(bufinput, i);
169 if (_err == 1)
171 Debug_Val(0, "Missing a DATA argument to the option %s\n", option->string);
173 /*else
175 Debug_Val(2, " includes data \"%s\" ", option->data);
178 return _err;
181 static int
182 Handle_Options_nest(BUFINPUT *input, LOPTIONS *option, char *input_string,
183 char *sep_string, u32 current)
185 int _err = 0;
188 Debug_Val(4, " N %s -%c", sep_string, input_string[0]);
190 /* we drop any checks with long options (--) */
191 if (strlen(sep_string) > 2)
192 return 0;
194 if (input_string[0] == sep_string[1])
196 option->present++;
198 Debug_Val(4, " <- found %c ", input->used);
200 if (option->options & OPTION_NESTED)
202 input->used++;
204 _err = 3;
206 if (option->options & OPTION_ARGUMENT)
207 _err = Handle_Option_Argument(option, current, input->string);
209 else
211 _err = 1;
212 Debug_Val(0, "Invalid option in a nest of options %d \n", input->used);
217 return _err;
220 /* TODO find a better name eheh */
221 static int
222 Handle_Options(BUFINPUT *input, LOPTIONS *option, char *sep_string, u32 current)
224 char *sep; /* will contain the - or -- prefix plus the option */
225 int len; /* sep_string len */
226 int _err = 0;
228 len = strlen(sep_string);
230 sep = calloc(1, len + 4);
232 if (len > 1 /* && sep_string[0] != '-'*/)
234 /* could be either a long option (ie email) or a
235 * short option ( ie -e ) but with extra options in
236 * it ( ie -eau ). An error would be that the
237 * extra option contains invalid nested options, a
238 * '=' character when a small option.
240 sep[0] = '-';
241 sep[1] = '-';
242 strncpy(&sep[2], sep_string, len);
244 else
247 sep[0] = '-';
248 sep[1] = sep_string[0];
250 if (len == 1)
252 sep[2] = '\0';
253 _err = Handle_Options(input, option, sep, current);
255 else
257 sep[2] = sep_string[1];
258 sep[3] = '\0';
263 Debug_Val(4, "%s %s", sep, input->string);
266 tmp = strchr(input->string, '=');
268 if (tmp)
270 tmp = '\0';
271 printf(" <- data included ");
275 /* check if we have an element in bufinput that is a nest of options
276 * ie -au
278 if (input->type == TYPE_NEST)
280 int i = 1;
281 int ilen = strlen(input->string);
283 Debug_Val(4, " <- nested options \n");
285 /* input->used = 0; */
287 while (i < ilen)
290 _err = Handle_Options_nest(input, option, &input->string[i], sep, current);
291 if (i + 1 < ilen)
292 Debug_Val(4, "\n");
293 i++;
297 else
299 if (!strncmp(input->string, sep, strlen(sep)))
301 option->present++;
303 Debug_Val(4, " <- found ");
305 input->used = 1;
307 _err = 3;
309 if (option->options & OPTION_ARGUMENT)
310 _err = Handle_Option_Argument(option, current, input->string);
312 /*else
314 if (input->used != 1)
315 input->used = 0;
320 Debug_Val(4, "\n");
321 free(sep);
323 return _err;
327 Neuro_ArgInit(int argc, char **argv)
329 int i = 0;
330 BUFINPUT *buf;
332 if (loptions)
333 return 1;
334 if (argc == 0)
335 return 1;
336 if (argv == NULL)
337 return 1;
339 Neuro_CreateEBuf(&bufinput);
340 Neuro_CreateEBuf(&loptions);
342 if (!bufinput || !loptions)
343 return 1;
345 Neuro_SetcallbEBuf(loptions, clean_loptions);
347 /* since 0 is the name of the executed command */
348 i = 1;
349 while (i < argc)
351 Neuro_AllocEBuf(bufinput, sizeof(BUFINPUT*), sizeof(BUFINPUT));
352 buf = Neuro_GiveCurEBuf(bufinput);
354 buf->string = argv[i];
355 i++;
358 #if init_test
359 /* a test to see if everything works */
360 i = Neuro_GiveEBufCount(bufinput) + 1;
361 while (i-- > 0)
363 buf = Neuro_GiveEBuf(bufinput, i);
364 Debug_Val(10, "arg output %s\n", buf->string);
366 #endif /* init_test */
368 return 0;
371 void
372 Neuro_ArgOption(char *string, int options, void (*action)(char *data))
374 LOPTIONS *buf;
376 if (!bufinput)
377 return;
378 if (!loptions)
379 return;
381 Neuro_AllocEBuf(loptions, sizeof(LOPTIONS*), sizeof(LOPTIONS));
382 buf = Neuro_GiveCurEBuf(loptions);
384 buf->string = NULL;
385 if (string)
387 buf->string = calloc(1, strlen(string) + 1);
388 strncpy(buf->string, string, strlen(string));
391 buf->options = options;
393 if (action)
394 buf->action = action;
398 Neuro_ArgProcess()
400 /* interesting notes :
401 * - options can contain more than one option in it
402 * options only with the OPTION_NESTED flag can go in it.
403 * - options with the flag OPTION_ARGUMENT can't contain a
404 * = character in it, the next input argument is read to see
405 * if it is a DATA type, if it is not, it will do an error.
407 * -- options can only contain a single option but the string
408 * of the option can be of any lenght (unlike - ). In presence
409 * of OPTION_ARGUMENT, the algorithm doesn't loop for the next
410 * input argument like for -, it will look for the -- option and
411 * try to find a '=' character which should follow a string that
412 * is the argument or else it will do an error.
415 BUFINPUT *input;
416 u32 itotal;
417 LOPTIONS *option;
418 u32 ototal;
419 u32 i = 0, i2 = 0, i3 = 0;
420 SepChr_Data *sep;
421 EBUF *sepchr;
422 int _err = 0;
424 ototal = Neuro_GiveEBufCount(loptions) + 1;
426 /* in case there is no arguments inputed
427 * we search the loptions to see if an
428 * option contains the OPTION_VOID tag
429 * so we can run it. Normally, only one
430 * option should have this tag so we
431 * will only run the first we find.
433 if (Neuro_EBufIsEmpty(bufinput))
435 while (i < ototal)
437 option = Neuro_GiveEBuf(loptions, i);
439 if (option->options & OPTION_VOID)
441 if (option->action)
442 (option->action)(NULL);
443 return 1;
445 i++;
447 return 0;
451 itotal = Neuro_GiveEBufCount(bufinput) + 1;
453 /* printf("TOTAL %d\n", itotal); */
454 Debug_Val(3, "TOTAL %d\n", itotal);
457 i = 0;
458 /* flag the input arguments with their types */
459 while (i < itotal)
461 input = Neuro_GiveEBuf(bufinput, i);
463 Debug_Val(6, "input %s", input->string);
465 if (input->string[0] == '-')
467 if (input->string[1] != '-' && strlen(input->string) > 2)
469 if (strchr(input->string, '='))
471 _err = 2;
472 Debug_Val(0, "Invalid use of the option character \'-\', it may only be used alone or with other options, \n");
473 Debug_Val(0, "to pass an argument to it, please use either the long version of the option (ie --foo=bar)\n");
474 Debug_Val(0, "or have a space between the option and the argument (ie -f bar).\n");
476 else
478 input->type = TYPE_NEST;
480 Debug_Val(6, " nest\n");
483 else
485 input->type = TYPE_OPTION;
487 Debug_Val(6, " option\n");
490 else
492 input->type = TYPE_DATA;
494 Debug_Val(6, " data\n");
496 i++;
499 i = 0;
500 /* loop the options & arguments and process(change flags only except
501 * some exceptions like arguments to options which it will handle) the
502 * normal, <done>
503 * argument, <done>
504 * nested(-au), <done>
505 * multi <done>
506 * and required <done>
507 * type of options.
509 while (i < itotal && !_err)
511 input = Neuro_GiveEBuf(bufinput, i);
512 i2 = 0;
513 while (i2 < ototal && !_err)
515 option = Neuro_GiveEBuf(loptions, i2);
517 if (option->string)
519 sepchr = Neuro_SepChr2(',', option->string);
521 i3 = Neuro_GiveEBufCount(sepchr) + 1;
522 while (i3-- > 0 && !_err)
524 sep = Neuro_GiveEBuf(sepchr, i3);
526 /* TODO find a better name eheh */
527 _err = Handle_Options(input, option, sep->string, i);
529 if (_err == 1)
531 /* an error occured */
532 _err = 2;
534 else
536 /* 3 means the current option
537 * and input concords
539 if (_err == 3)
541 if (option->options & OPTION_QUIT)
542 _err = 1;
543 else
544 _err = 0;
548 Neuro_CleanEBuf(&sepchr);
550 else
553 Debug_Val(4, "DATA %s", input->string);
555 if (input->type == TYPE_DATA && input->used == 0)
557 input->used = 1;
558 option->present++;
559 /* option->data = input->string; */
560 Push_Data(option, input->string);
562 Debug_Val(4, " <- found ");
564 Debug_Val(4, "\n");
567 i2++;
569 i++;
572 if (!_err)
574 i = 0;
575 /* loop the options to see if all the required
576 * options are present and also check if options
577 * don't have more than one iteration of themselves
578 * unless they have the multi option.
580 while (i < ototal)
582 option = Neuro_GiveEBuf(loptions, i);
584 if (option->options & OPTION_REQUIRED && option->present == 0)
586 if (option->string)
587 Debug_Val(0, "Required option %s is not present.\n", option->string);
588 else
589 Debug_Val(0, "Required option DATA is not present.\n");
591 _err = 2;
594 if (!(option->options & OPTION_MULTI) && option->present > 1)
596 if (option->string)
597 Debug_Val(0, "Invalid use of more than one iteration of the option %s.\n", option->string);
598 else
599 Debug_Val(0, "Invalid use of more than one iteration of the option DATA.\n");
601 _err = 2;
604 i++;
608 if (!_err)
610 i = 0;
611 /* loop the bufinput elements and check to see if theres
612 * invalid arguments and print the according errors.
614 while (i < itotal)
616 input = Neuro_GiveEBuf(bufinput, i);
618 if (input->used == 0)
620 Debug_Val(0, "Invalid option used %s\n", input->string);
621 _err = 2;
623 else
625 if (input->type == TYPE_NEST)
627 if (input->used < strlen(input->string) - 1)
629 Debug_Val(0, "One or more options in the option nest \"%s\" is invalid\n", input->string);
630 _err = 2;
635 i++;
639 if (_err <= 1)
641 i = 0;
642 /* loop the options and call the callbacks if theres no errors */
643 while (i < ototal)
645 option = Neuro_GiveEBuf(loptions, i);
647 if (option->present)
649 if (option->action)
651 /*if (option->data)
652 (option->action)(option->data);
654 if (!Neuro_EBufIsEmpty(option->datas))
656 DATA *dta;
657 int dtotal = 0;
659 dtotal = Neuro_GiveEBufCount(option->datas);
660 dtotal++;
661 while (dtotal-- > 0)
663 dta = Neuro_GiveEBuf(option->datas, dtotal);
664 (option->action)(dta->data);
667 else
668 (option->action)(NULL);
670 if (option->options & OPTION_QUIT)
672 _err = 1;
673 break;
677 i++;
681 return _err;
684 void
685 Neuro_ArgClean()
687 Neuro_CleanEBuf(&bufinput);
688 Neuro_CleanEBuf(&loptions);