1 /* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* ====================================================================
3 * Copyright (c) 1999-2004 Carnegie Mellon University. All rights
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
18 * This work was supported in part by funding from the Defense Advanced
19 * Research Projects Agency and the National Science Foundation of the
20 * United States of America, and the CMU Sphinx Speech Consortium.
22 * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND
23 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
26 * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * ====================================================================
38 * cmd_ln.c -- Command line argument parsing.
40 * **********************************************
41 * CMU ARPA Speech Project
43 * Copyright (c) 1999 Carnegie Mellon University.
44 * ALL RIGHTS RESERVED.
45 * **********************************************
49 * 10-Sep-1998 M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
50 * Changed strcasecmp() call in cmp_name() to strcmp_nocase() call.
52 * 15-Jul-1997 M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
53 * Added required arguments handling.
55 * 07-Dec-96 M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
56 * Created, based on Eric's implementation. Basically, combined several
57 * functions into one, eliminated validation, and simplified the interface.
67 #pragma warning (disable: 4996 4018)
78 #include "sphinxbase/cmd_ln.h"
79 #include "sphinxbase/err.h"
80 #include "sphinxbase/ckd_alloc.h"
81 #include "sphinxbase/hash_table.h"
82 #include "sphinxbase/case.h"
83 #include "sphinxbase/strfuncs.h"
85 typedef struct cmd_ln_val_s {
97 /** Global command-line, for non-reentrant API. */
98 cmd_ln_t *global_cmdln;
99 static void arg_dump_r(cmd_ln_t *cmdln, FILE * fp, arg_t const *defn, int32 doc);
100 static cmd_ln_t * parse_options(cmd_ln_t *cmdln, const arg_t *defn, int32 argc, char* argv[], int32 strict);
103 * Find max length of name and default fields in the given defn array.
104 * Return #items in defn array.
107 arg_strlen(const arg_t * defn, int32 * namelen, int32 * deflen)
111 *namelen = *deflen = 0;
112 for (i = 0; defn[i].name; i++) {
113 l = strlen(defn[i].name);
118 l = strlen(defn[i].deflt);
120 l = strlen("(null)");
121 /* E_INFO("string default, %s , name %s, length %d\n",defn[i].deflt,defn[i].name,l); */
131 cmp_name(const void *a, const void *b)
133 return (strcmp_nocase
134 ((* (arg_t**) a)->name,
135 (* (arg_t**) b)->name));
138 static const arg_t **
139 arg_sort(const arg_t * defn, int32 n)
144 pos = (const arg_t **) ckd_calloc(n, sizeof(arg_t *));
145 for (i = 0; i < n; ++i)
147 qsort(pos, n, sizeof(arg_t *), cmp_name);
153 strnappend(char **dest, size_t *dest_allocation,
154 const char *source, size_t n)
156 size_t source_len, required_allocation;
158 if (dest == NULL || dest_allocation == NULL)
160 if (*dest == NULL && *dest_allocation != 0)
163 return *dest_allocation;
165 source_len = strlen(source);
166 if (n && n < source_len)
169 required_allocation = (*dest ? strlen(*dest) : 0) + source_len + 1;
170 if (*dest_allocation < required_allocation) {
171 if (*dest_allocation == 0) {
172 *dest = ckd_calloc(required_allocation * 2, 1);
174 *dest = ckd_realloc(*dest, required_allocation * 2);
176 *dest_allocation = required_allocation * 2;
179 strncat(*dest, source, source_len);
181 return *dest_allocation;
185 strappend(char **dest, size_t *dest_allocation,
188 return strnappend(dest, dest_allocation, source, 0);
192 arg_resolve_env(const char *str)
194 char *resolved_str = NULL;
198 const char *i = str, *j;
200 /* calculate required resolved_str size */
205 strnappend(&resolved_str, &alloced, i, j - i);
208 j = strchr(i + 2, ')');
210 if (j - (i + 2) < 100) {
211 strncpy(env_name, i + 2, j - (i + 2));
212 env_name[j - (i + 2)] = '\0';
213 #if !defined(_WIN32_WCE)
214 env_val = getenv(env_name);
216 strappend(&resolved_str, &alloced, env_val);
223 /* unclosed, copy and skip */
225 strnappend(&resolved_str, &alloced, i, j - i);
229 strappend(&resolved_str, &alloced, i);
237 arg_dump_r(cmd_ln_t *cmdln, FILE * fp, const arg_t * defn, int32 doc)
241 int32 namelen, deflen;
245 /* No definitions, do nothing. */
251 /* Find max lengths of name and default value fields, and #entries in defn */
252 n = arg_strlen(defn, &namelen, &deflen);
253 /* E_INFO("String length %d. Name length %d, Default Length %d\n",n, namelen, deflen); */
254 namelen = namelen & 0xfffffff8; /* Previous tab position */
255 deflen = deflen & 0xfffffff8; /* Previous tab position */
257 fprintf(fp, "[NAME]");
258 for (l = strlen("[NAME]"); l < namelen; l += 8)
260 fprintf(fp, "\t[DEFLT]");
261 for (l = strlen("[DEFLT]"); l < deflen; l += 8)
265 fprintf(fp, "\t[DESCR]\n");
268 fprintf(fp, "\t[VALUE]\n");
271 /* Print current configuration, sorted by name */
272 pos = arg_sort(defn, n);
273 for (i = 0; i < n; i++) {
274 fprintf(fp, "%s", pos[i]->name);
275 for (l = strlen(pos[i]->name); l < namelen; l += 8)
280 fprintf(fp, "%s", pos[i]->deflt);
281 l = strlen(pos[i]->deflt);
285 for (; l < deflen; l += 8)
291 fprintf(fp, "%s", pos[i]->doc);
294 vp = cmd_ln_access_r(cmdln, pos[i]->name);
296 switch (pos[i]->type) {
299 fprintf(fp, "%ld", vp->i);
302 case REQARG_FLOATING:
303 fprintf(fp, "%e", vp->fl);
308 fprintf(fp, "%s", (char *)vp->ptr);
310 case ARG_STRING_LIST:
311 array = (char const**)vp->ptr;
313 for (l = 0; array[l] != 0; l++) {
314 fprintf(fp, "%s,", array[l]);
319 fprintf(fp, "%s", vp->i ? "yes" : "no");
322 E_ERROR("Unknown argument type: %d\n", pos[i]->type);
336 parse_string_list(const char *str)
349 /* Should end with NULL */
350 result = (char **) ckd_calloc(count + 1, sizeof(char *));
352 for (i = 0; i < count; i++) {
353 for (j = 0; p[j] != ',' && p[j] != 0; j++);
354 result[i] = ckd_calloc(j + 1, sizeof(char));
355 strncpy( result[i], p, j);
361 static cmd_ln_val_t *
362 cmd_ln_val_init(int t, const char *str)
369 /* For lack of a better default value. */
370 memset(&val, 0, sizeof(val));
374 e_str = arg_resolve_env(str);
379 if (sscanf(e_str, "%ld", &val.i) != 1)
383 case REQARG_FLOATING:
384 if (e_str == NULL || e_str[0] == 0)
386 val.fl = atof_c(e_str);
390 if ((e_str[0] == 'y') || (e_str[0] == 't') ||
391 (e_str[0] == 'Y') || (e_str[0] == 'T') || (e_str[0] == '1')) {
394 else if ((e_str[0] == 'n') || (e_str[0] == 'f') ||
395 (e_str[0] == 'N') || (e_str[0] == 'F') |
400 E_ERROR("Unparsed boolean value '%s'\n", str);
406 val.ptr = ckd_salloc(e_str);
408 case ARG_STRING_LIST:
409 val.ptr = parse_string_list(e_str);
412 E_ERROR("Unknown argument type: %d\n", t);
421 v = ckd_calloc(1, sizeof(*v));
422 memcpy(v, &val, sizeof(val));
429 * Handles option parsing for cmd_ln_parse_file_r() and cmd_ln_init()
430 * also takes care of storing argv.
431 * DO NOT call it from cmd_ln_parse_r()
434 parse_options(cmd_ln_t *cmdln, const arg_t *defn, int32 argc, char* argv[], int32 strict)
438 new_cmdln = cmd_ln_parse_r(cmdln, defn, argc, argv, strict);
439 /* If this failed then clean up and return NULL. */
440 if (new_cmdln == NULL) {
442 for (i = 0; i < argc; ++i)
448 /* Otherwise, we need to add the contents of f_argv to the new object. */
449 if (new_cmdln == cmdln) {
450 /* If we are adding to a previously passed-in cmdln, then
451 * store our allocated strings in its f_argv. */
452 new_cmdln->f_argv = ckd_realloc(new_cmdln->f_argv,
453 (new_cmdln->f_argc + argc)
454 * sizeof(*new_cmdln->f_argv));
455 memcpy(new_cmdln->f_argv + new_cmdln->f_argc, argv,
456 argc * sizeof(*argv));
458 new_cmdln->f_argc += argc;
461 /* Otherwise, store f_argc and f_argv. */
462 new_cmdln->f_argc = argc;
463 new_cmdln->f_argv = argv;
470 cmd_ln_val_free(cmd_ln_val_t *val)
473 if (val->type & ARG_STRING_LIST) {
474 char const** array = (char const **)val->val.ptr;
476 for (i = 0; array[i] != NULL; i++) {
477 ckd_free(val->val.ptr);
482 if (val->type & ARG_STRING)
483 ckd_free(val->val.ptr);
494 cmd_ln_appl_enter(int argc, char *argv[],
495 const char *default_argfn,
498 /* Look for default or specified arguments file */
503 if ((argc == 2) && (strcmp(argv[1], "help") == 0)) {
504 cmd_ln_print_help(stderr, defn);
508 if ((argc == 2) && (argv[1][0] != '-'))
510 else if (argc == 1) {
512 E_INFO("Looking for default argument file: %s\n", default_argfn);
514 if ((fp = fopen(default_argfn, "r")) == NULL) {
515 E_INFO("Can't find default argument file %s.\n",
527 /* Build command line argument list from file */
528 E_INFO("Parsing command lines from file %s\n", str);
529 if (cmd_ln_parse_file(defn, str, TRUE)) {
530 E_INFOCONT("Usage:\n");
531 E_INFOCONT("\t%s argument-list, or\n", argv[0]);
532 E_INFOCONT("\t%s [argument-file] (default file: . %s)\n\n",
533 argv[0], default_argfn);
534 cmd_ln_print_help(stderr, defn);
539 cmd_ln_parse(defn, argc, argv, TRUE);
551 cmd_ln_parse_r(cmd_ln_t *inout_cmdln, const arg_t * defn, int32 argc, char *argv[], int strict)
553 int32 i, j, n, argstart;
554 hash_table_t *defidx = NULL;
557 /* Construct command-line object */
558 if (inout_cmdln == NULL) {
559 cmdln = ckd_calloc(1, sizeof(*cmdln));
565 /* Build a hash table for argument definitions */
566 defidx = hash_table_new(50, 0);
568 for (n = 0; defn[n].name; n++) {
571 v = hash_table_enter(defidx, defn[n].name, (void *)&defn[n]);
572 if (strict && (v != &defn[n])) {
573 E_ERROR("Duplicate argument name in definition: %s\n", defn[n].name);
579 /* No definitions. */
583 /* Allocate memory for argument values */
584 if (cmdln->ht == NULL)
585 cmdln->ht = hash_table_new(n, 0 /* argument names are case-sensitive */ );
588 /* skip argv[0] if it doesn't start with dash */
590 if (argc > 0 && argv[0][0] != '-') {
594 /* Parse command line arguments (name-value pairs) */
595 for (j = argstart; j < argc; j += 2) {
600 if (hash_table_lookup(defidx, argv[j], &v) < 0) {
602 E_ERROR("Unknown argument name '%s'\n", argv[j]);
605 else if (defn == NULL)
612 /* Enter argument value */
614 cmd_ln_print_help_r(cmdln, stderr, defn);
615 E_ERROR("Argument value for '%s' missing\n", argv[j]);
620 val = cmd_ln_val_init(ARG_STRING, argv[j + 1]);
622 if ((val = cmd_ln_val_init(argdef->type, argv[j + 1])) == NULL) {
623 cmd_ln_print_help_r(cmdln, stderr, defn);
624 E_ERROR("Bad argument value for %s: %s\n", argv[j],
630 if ((v = hash_table_enter(cmdln->ht, argv[j], (void *)val)) != (void *)val) {
632 cmd_ln_val_free(val);
633 E_ERROR("Duplicate argument name in arguments: %s\n",
638 v = hash_table_replace(cmdln->ht, argv[j], (void *)val);
639 cmd_ln_val_free((cmd_ln_val_t *)v);
644 /* Fill in default values, if any, for unspecified arguments */
645 for (i = 0; i < n; i++) {
649 if (hash_table_lookup(cmdln->ht, defn[i].name, &v) < 0) {
650 if ((val = cmd_ln_val_init(defn[i].type, defn[i].deflt)) == NULL) {
652 ("Bad default argument value for %s: %s\n",
653 defn[i].name, defn[i].deflt);
656 hash_table_enter(cmdln->ht, defn[i].name, (void *)val);
660 /* Check for required arguments; exit if any missing */
662 for (i = 0; i < n; i++) {
663 if (defn[i].type & ARG_REQUIRED) {
665 if (hash_table_lookup(cmdln->ht, defn[i].name, &v) != 0)
666 E_ERROR("Missing required argument %s\n", defn[i].name);
670 cmd_ln_print_help_r(cmdln, stderr, defn);
674 if (strict && argc == 1) {
675 E_ERROR("No arguments given, available options are:\n");
676 cmd_ln_print_help_r(cmdln, stderr, defn);
678 hash_table_free(defidx);
679 if (inout_cmdln == NULL)
680 cmd_ln_free_r(cmdln);
685 /* Set up logging. We need to do this earlier because we want to dump
686 * the information to the configured log, not to the stderr. */
687 if (cmd_ln_exists_r(cmdln, "-logfn") && cmd_ln_str_r(cmdln, "-logfn"))
688 err_set_logfile(cmd_ln_str_r(cmdln, "-logfn"));
690 /* Echo command line */
691 E_INFO("Parsing command line:\n");
692 for (i = 0; i < argc; i++) {
693 if (argv[i][0] == '-')
694 E_INFOCONT("\\\n\t");
695 E_INFOCONT("%s ", argv[i]);
700 /* Print configuration */
701 E_INFOCONT("Current configuration:\n");
702 arg_dump_r(cmdln, err_get_logfp(), defn, 0);
705 hash_table_free(defidx);
710 hash_table_free(defidx);
711 if (inout_cmdln == NULL)
712 cmd_ln_free_r(cmdln);
713 E_ERROR("Failed to parse arguments list\n");
718 cmd_ln_init(cmd_ln_t *inout_cmdln, const arg_t *defn, int32 strict, ...)
721 const char *arg, *val;
725 va_start(args, strict);
727 while ((arg = va_arg(args, const char *))) {
729 val = va_arg(args, const char*);
731 E_ERROR("Number of arguments must be even!\n");
738 /* Now allocate f_argv */
739 f_argv = ckd_calloc(f_argc, sizeof(*f_argv));
740 va_start(args, strict);
742 while ((arg = va_arg(args, const char *))) {
743 f_argv[f_argc] = ckd_salloc(arg);
745 val = va_arg(args, const char*);
746 f_argv[f_argc] = ckd_salloc(val);
751 return parse_options(inout_cmdln, defn, f_argc, f_argv, strict);
755 cmd_ln_parse(const arg_t * defn, int32 argc, char *argv[], int strict)
759 cmdln = cmd_ln_parse_r(global_cmdln, defn, argc, argv, strict);
761 /* Old, bogus behaviour... */
762 E_ERROR("Failed to parse arguments list, forced exit\n");
765 /* Initialize global_cmdln if not present. */
766 if (global_cmdln == NULL) {
767 global_cmdln = cmdln;
773 cmd_ln_parse_file_r(cmd_ln_t *inout_cmdln, const arg_t * defn, const char *filename, int32 strict)
779 int arg_max_length = 512;
784 const char separator[] = " \t\r\n";
786 if ((file = fopen(filename, "r")) == NULL) {
787 E_ERROR("Cannot open configuration file %s for reading\n",
793 /* Skip to the next interesting character */
794 for (; ch != EOF && strchr(separator, ch); ch = fgetc(file)) ;
802 * Initialize default argv, argc, and argv_size.
806 f_argv = ckd_calloc(argv_size, sizeof(char *));
807 /* Silently make room for \0 */
808 str = ckd_calloc(arg_max_length + 1, sizeof(char));
812 /* Handle arguments that are commented out */
813 if (len == 0 && argc % 2 == 0) {
815 /* Skip everything until newline */
816 for (ch = fgetc(file); ch != EOF && ch != '\n'; ch = fgetc(file)) ;
817 /* Skip to the next interesting character */
818 for (ch = fgetc(file); ch != EOF && strchr(separator, ch); ch = fgetc(file)) ;
821 /* Check if we are at the last line (without anything interesting in it) */
826 /* Handle quoted arguments */
827 if (ch == '"' || ch == '\'') {
828 if (quoting == ch) /* End a quoted section with the same type */
831 E_ERROR("Nesting quotations is not supported!\n");
836 quoting = ch; /* Start a quoted section */
838 else if (ch == EOF || (!quoting && strchr(separator, ch))) {
839 /* Reallocate argv so it is big enough to contain all the arguments */
840 if (argc >= argv_size) {
843 ckd_realloc(f_argv, argv_size * 2 * sizeof(char *)))) {
850 /* Add the string to the list of arguments */
851 f_argv[argc] = ckd_salloc(str);
857 E_WARN("Unclosed quotation, having EOF close it...\n");
859 /* Skip to the next interesting character */
860 for (; ch != EOF && strchr(separator, ch); ch = fgetc(file)) ;
865 /* We already have the next character */
869 if (len >= arg_max_length) {
870 /* Make room for more chars (including the \0 !) */
872 if ((tmp_str = ckd_realloc(str, (1 + arg_max_length * 2) * sizeof(char))) == NULL) {
879 /* Add the char to the argument string */
881 /* Always null terminate */
893 for (ch = 0; ch < argc; ++ch)
894 ckd_free(f_argv[ch]);
899 return parse_options(inout_cmdln, defn, argc, f_argv, strict);
903 cmd_ln_parse_file(const arg_t * defn, const char *filename, int32 strict)
907 cmdln = cmd_ln_parse_file_r(global_cmdln, defn, filename, strict);
911 /* Initialize global_cmdln if not present. */
912 if (global_cmdln == NULL) {
913 global_cmdln = cmdln;
919 cmd_ln_print_help_r(cmd_ln_t *cmdln, FILE * fp, arg_t const* defn)
923 fprintf(fp, "Arguments list definition:\n");
924 arg_dump_r(cmdln, fp, defn, 1);
929 cmd_ln_exists_r(cmd_ln_t *cmdln, const char *name)
934 return (hash_table_lookup(cmdln->ht, name, &val) == 0);
938 cmd_ln_access_r(cmd_ln_t *cmdln, const char *name)
941 if (hash_table_lookup(cmdln->ht, name, &val) < 0) {
942 E_ERROR("Unknown argument: %s\n", name);
945 return (anytype_t *)val;
949 cmd_ln_str_r(cmd_ln_t *cmdln, char const *name)
952 val = cmd_ln_access_r(cmdln, name);
955 return (char const *)val->ptr;
959 cmd_ln_str_list_r(cmd_ln_t *cmdln, char const *name)
962 val = cmd_ln_access_r(cmdln, name);
965 return (char const **)val->ptr;
969 cmd_ln_int_r(cmd_ln_t *cmdln, char const *name)
972 val = cmd_ln_access_r(cmdln, name);
979 cmd_ln_float_r(cmd_ln_t *cmdln, char const *name)
982 val = cmd_ln_access_r(cmdln, name);
989 cmd_ln_set_str_r(cmd_ln_t *cmdln, char const *name, char const *str)
992 val = cmd_ln_access_r(cmdln, name);
994 E_ERROR("Unknown argument: %s\n", name);
1001 val->ptr = ckd_salloc(str);
1005 cmd_ln_set_int_r(cmd_ln_t *cmdln, char const *name, long iv)
1008 val = cmd_ln_access_r(cmdln, name);
1010 E_ERROR("Unknown argument: %s\n", name);
1017 cmd_ln_set_float_r(cmd_ln_t *cmdln, char const *name, double fv)
1020 val = cmd_ln_access_r(cmdln, name);
1022 E_ERROR("Unknown argument: %s\n", name);
1029 cmd_ln_retain(cmd_ln_t *cmdln)
1036 cmd_ln_free_r(cmd_ln_t *cmdln)
1040 if (--cmdln->refcount > 0)
1041 return cmdln->refcount;
1048 entries = hash_table_tolist(cmdln->ht, &n);
1049 for (gn = entries; gn; gn = gnode_next(gn)) {
1050 hash_entry_t *e = gnode_ptr(gn);
1051 cmd_ln_val_free((cmd_ln_val_t *)e->val);
1053 glist_free(entries);
1054 hash_table_free(cmdln->ht);
1058 if (cmdln->f_argv) {
1060 for (i = 0; i < cmdln->f_argc; ++i) {
1061 ckd_free(cmdln->f_argv[i]);
1063 ckd_free(cmdln->f_argv);
1064 cmdln->f_argv = NULL;
1074 cmd_ln_free_r(global_cmdln);
1075 global_cmdln = NULL;