SphinxBase 0.6
src/libsphinxbase/util/cmd_ln.c
00001 /* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
00002 /* ====================================================================
00003  * Copyright (c) 1999-2004 Carnegie Mellon University.  All rights
00004  * reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * 1. Redistributions of source code must retain the above copyright
00011  *    notice, this list of conditions and the following disclaimer. 
00012  *
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in
00015  *    the documentation and/or other materials provided with the
00016  *    distribution.
00017  *
00018  * This work was supported in part by funding from the Defense Advanced 
00019  * Research Projects Agency and the National Science Foundation of the 
00020  * United States of America, and the CMU Sphinx Speech Consortium.
00021  *
00022  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND 
00023  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
00024  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00025  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
00026  * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00027  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
00028  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
00029  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
00030  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
00031  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
00032  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033  *
00034  * ====================================================================
00035  *
00036  */
00037 /*
00038  * cmd_ln.c -- Command line argument parsing.
00039  *
00040  * **********************************************
00041  * CMU ARPA Speech Project
00042  *
00043  * Copyright (c) 1999 Carnegie Mellon University.
00044  * ALL RIGHTS RESERVED.
00045  * **********************************************
00046  * 
00047  * HISTORY
00048  * 
00049  * 10-Sep-1998  M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
00050  *              Changed strcasecmp() call in cmp_name() to strcmp_nocase() call.
00051  * 
00052  * 15-Jul-1997  M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
00053  *              Added required arguments handling.
00054  * 
00055  * 07-Dec-96    M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
00056  *              Created, based on Eric's implementation.  Basically, combined several
00057  *              functions into one, eliminated validation, and simplified the interface.
00058  */
00059 
00060 
00061 #include <stdio.h>
00062 #include <stdlib.h>
00063 #include <string.h>
00064 #include <assert.h>
00065 
00066 #ifdef _MSC_VER
00067 #pragma warning (disable: 4996 4018)
00068 #endif
00069 
00070 #ifdef HAVE_CONFIG_H
00071 #include <config.h>
00072 #endif
00073 
00074 #ifdef HAVE_UNISTD_H
00075 #include <unistd.h>
00076 #endif
00077 
00078 #include "sphinxbase/cmd_ln.h"
00079 #include "sphinxbase/err.h"
00080 #include "sphinxbase/ckd_alloc.h"
00081 #include "sphinxbase/hash_table.h"
00082 #include "sphinxbase/case.h"
00083 #include "sphinxbase/strfuncs.h"
00084 
00085 typedef struct cmd_ln_val_s {
00086     anytype_t val;
00087     int type;
00088 } cmd_ln_val_t;
00089 
00090 struct cmd_ln_s {
00091     int refcount;
00092     hash_table_t *ht;
00093     char **f_argv;
00094     uint32 f_argc;
00095 };
00096 
00098 cmd_ln_t *global_cmdln;
00099 static void arg_dump_r(cmd_ln_t *cmdln, FILE * fp, arg_t const *defn, int32 doc);
00100 static cmd_ln_t * parse_options(cmd_ln_t *cmdln, const arg_t *defn, int32 argc, char* argv[], int32 strict);
00101 
00102 /*
00103  * Find max length of name and default fields in the given defn array.
00104  * Return #items in defn array.
00105  */
00106 static int32
00107 arg_strlen(const arg_t * defn, int32 * namelen, int32 * deflen)
00108 {
00109     int32 i, l;
00110 
00111     *namelen = *deflen = 0;
00112     for (i = 0; defn[i].name; i++) {
00113         l = strlen(defn[i].name);
00114         if (*namelen < l)
00115             *namelen = l;
00116 
00117         if (defn[i].deflt)
00118             l = strlen(defn[i].deflt);
00119         else
00120             l = strlen("(null)");
00121         /*      E_INFO("string default, %s , name %s, length %d\n",defn[i].deflt,defn[i].name,l); */
00122         if (*deflen < l)
00123             *deflen = l;
00124     }
00125 
00126     return i;
00127 }
00128 
00129 
00130 static int32
00131 cmp_name(const void *a, const void *b)
00132 {
00133     return (strcmp_nocase
00134             ((* (arg_t**) a)->name,
00135              (* (arg_t**) b)->name));
00136 }
00137 
00138 static const arg_t **
00139 arg_sort(const arg_t * defn, int32 n)
00140 {
00141     const arg_t ** pos;
00142     int32 i;
00143 
00144     pos = (const arg_t **) ckd_calloc(n, sizeof(arg_t *));
00145     for (i = 0; i < n; ++i)
00146         pos[i] = &defn[i];
00147     qsort(pos, n, sizeof(arg_t *), cmp_name);
00148 
00149     return pos;
00150 }
00151 
00152 static size_t
00153 strnappend(char **dest, size_t *dest_allocation, 
00154        const char *source, size_t n)
00155 {
00156     size_t source_len, required_allocation;
00157 
00158     if (dest == NULL || dest_allocation == NULL)
00159         return -1;
00160     if (*dest == NULL && *dest_allocation != 0)
00161         return -1;
00162     if (source == NULL)
00163         return *dest_allocation;
00164 
00165     source_len = strlen(source);
00166     if (n && n < source_len)
00167         source_len = n;
00168 
00169     required_allocation = (*dest ? strlen(*dest) : 0) + source_len + 1;
00170     if (*dest_allocation < required_allocation) {
00171         if (*dest_allocation == 0) {
00172             *dest = ckd_calloc(required_allocation * 2, 1);
00173         } else {
00174             *dest = ckd_realloc(*dest, required_allocation * 2);
00175         }
00176         *dest_allocation = required_allocation * 2;
00177     } 
00178 
00179     strncat(*dest, source, source_len);
00180 
00181     return *dest_allocation;
00182 }
00183 
00184 static size_t
00185 strappend(char **dest, size_t *dest_allocation, 
00186        const char *source)
00187 {
00188     return strnappend(dest, dest_allocation, source, 0);
00189 }
00190 
00191 static char*
00192 arg_resolve_env(const char *str)
00193 {
00194     char *resolved_str = NULL;
00195     char env_name[100];
00196     const char *env_val;
00197     size_t alloced = 0;
00198     const char *i = str, *j;
00199 
00200     /* calculate required resolved_str size */
00201     do {
00202         j = strstr(i, "$(");
00203         if (j != NULL) {
00204             if (j != i) {
00205                 strnappend(&resolved_str, &alloced, i, j - i);
00206                 i = j;
00207             }
00208             j = strchr(i + 2, ')');
00209             if (j != NULL) {
00210                 if (j - (i + 2) < 100) {
00211                     strncpy(env_name, i + 2, j - (i + 2));
00212                     env_name[j - (i + 2)] = '\0';
00213                     #if !defined(_WIN32_WCE)
00214                     env_val = getenv(env_name);
00215                     if (env_val)
00216                         strappend(&resolved_str, &alloced, env_val);
00217                     #else
00218                     env_val = 0;
00219                     #endif
00220                 }
00221                 i = j + 1;
00222             } else {
00223                 /* unclosed, copy and skip */
00224                 j = i + 2;
00225                 strnappend(&resolved_str, &alloced, i, j - i);
00226                 i = j;
00227             }
00228         } else {
00229             strappend(&resolved_str, &alloced, i);
00230         }
00231     } while(j != NULL);
00232 
00233     return resolved_str;
00234 }
00235 
00236 static void
00237 arg_dump_r(cmd_ln_t *cmdln, FILE * fp, const arg_t * defn, int32 doc)
00238 {
00239     const arg_t **pos;
00240     int32 i, l, n;
00241     int32 namelen, deflen;
00242     anytype_t *vp;
00243     char const **array;
00244 
00245     /* No definitions, do nothing. */
00246     if (defn == NULL)
00247         return;
00248     if (fp == NULL)
00249         return;
00250 
00251     /* Find max lengths of name and default value fields, and #entries in defn */
00252     n = arg_strlen(defn, &namelen, &deflen);
00253     /*    E_INFO("String length %d. Name length %d, Default Length %d\n",n, namelen, deflen); */
00254     namelen = namelen & 0xfffffff8;     /* Previous tab position */
00255     deflen = deflen & 0xfffffff8;       /* Previous tab position */
00256 
00257     fprintf(fp, "[NAME]");
00258     for (l = strlen("[NAME]"); l < namelen; l += 8)
00259         fprintf(fp, "\t");
00260     fprintf(fp, "\t[DEFLT]");
00261     for (l = strlen("[DEFLT]"); l < deflen; l += 8)
00262         fprintf(fp, "\t");
00263 
00264     if (doc) {
00265         fprintf(fp, "\t[DESCR]\n");
00266     }
00267     else {
00268         fprintf(fp, "\t[VALUE]\n");
00269     }
00270 
00271     /* Print current configuration, sorted by name */
00272     pos = arg_sort(defn, n);
00273     for (i = 0; i < n; i++) {
00274         fprintf(fp, "%s", pos[i]->name);
00275         for (l = strlen(pos[i]->name); l < namelen; l += 8)
00276             fprintf(fp, "\t");
00277 
00278         fprintf(fp, "\t");
00279         if (pos[i]->deflt) {
00280             fprintf(fp, "%s", pos[i]->deflt);
00281             l = strlen(pos[i]->deflt);
00282         }
00283         else
00284             l = 0;
00285         for (; l < deflen; l += 8)
00286             fprintf(fp, "\t");
00287 
00288         fprintf(fp, "\t");
00289         if (doc) {
00290             if (pos[i]->doc)
00291                 fprintf(fp, "%s", pos[i]->doc);
00292         }
00293         else {
00294             vp = cmd_ln_access_r(cmdln, pos[i]->name);
00295             if (vp) {
00296                 switch (pos[i]->type) {
00297                 case ARG_INTEGER:
00298                 case REQARG_INTEGER:
00299                     fprintf(fp, "%ld", vp->i);
00300                     break;
00301                 case ARG_FLOATING:
00302                 case REQARG_FLOATING:
00303                     fprintf(fp, "%e", vp->fl);
00304                     break;
00305                 case ARG_STRING:
00306                 case REQARG_STRING:
00307                     if (vp->ptr)
00308                         fprintf(fp, "%s", (char *)vp->ptr);
00309                     break;
00310                 case ARG_STRING_LIST:
00311                     array = (char const**)vp->ptr;
00312                     if (array)
00313                         for (l = 0; array[l] != 0; l++) {
00314                             fprintf(fp, "%s,", array[l]);
00315                         }
00316                     break;
00317                 case ARG_BOOLEAN:
00318                 case REQARG_BOOLEAN:
00319                     fprintf(fp, "%s", vp->i ? "yes" : "no");
00320                     break;
00321                 default:
00322                     E_ERROR("Unknown argument type: %d\n", pos[i]->type);
00323                 }
00324             }
00325         }
00326 
00327         fprintf(fp, "\n");
00328     }
00329     ckd_free(pos);
00330 
00331     fprintf(fp, "\n");
00332     fflush(fp);
00333 }
00334 
00335 static char **
00336 parse_string_list(const char *str)
00337 {
00338     int count, i, j;
00339     const char *p;
00340     char ** result;
00341 
00342     p = str;
00343     count = 1;
00344     while (*p) {
00345         if (*p == ',')
00346             count++;
00347         p++;
00348     }
00349     /* Should end with NULL */
00350     result = (char **) ckd_calloc(count + 1, sizeof(char *));
00351     p = str;
00352     for (i = 0; i < count; i++) {
00353         for (j = 0; p[j] != ',' && p[j] != 0; j++);
00354         result[i] = ckd_calloc(j + 1, sizeof(char));
00355         strncpy( result[i], p, j);
00356         p = p + j + 1;
00357     }
00358     return result;
00359 }
00360 
00361 static cmd_ln_val_t *
00362 cmd_ln_val_init(int t, const char *str)
00363 {
00364     cmd_ln_val_t *v;
00365     anytype_t val;
00366     char *e_str;
00367 
00368     if (!str) {
00369         /* For lack of a better default value. */
00370         memset(&val, 0, sizeof(val));
00371     }
00372     else {
00373         int valid = 1;
00374         e_str = arg_resolve_env(str);
00375 
00376         switch (t) {
00377         case ARG_INTEGER:
00378         case REQARG_INTEGER:
00379             if (sscanf(e_str, "%ld", &val.i) != 1)
00380                 valid = 0;
00381             break;
00382         case ARG_FLOATING:
00383         case REQARG_FLOATING:
00384             if (e_str == NULL || e_str[0] == 0)
00385                 valid = 0;
00386             val.fl = atof_c(e_str);
00387             break;
00388         case ARG_BOOLEAN:
00389         case REQARG_BOOLEAN:
00390             if ((e_str[0] == 'y') || (e_str[0] == 't') ||
00391                 (e_str[0] == 'Y') || (e_str[0] == 'T') || (e_str[0] == '1')) {
00392                 val.i = TRUE;
00393             }
00394             else if ((e_str[0] == 'n') || (e_str[0] == 'f') ||
00395                      (e_str[0] == 'N') || (e_str[0] == 'F') |
00396                      (e_str[0] == '0')) {
00397                 val.i = FALSE;
00398             }
00399             else {
00400                 E_ERROR("Unparsed boolean value '%s'\n", str);
00401                 valid = 0;
00402             }
00403             break;
00404         case ARG_STRING:
00405         case REQARG_STRING:
00406             val.ptr = ckd_salloc(e_str);
00407             break;
00408         case ARG_STRING_LIST:
00409             val.ptr = parse_string_list(e_str);
00410             break;
00411         default:
00412             E_ERROR("Unknown argument type: %d\n", t);
00413             valid = 0;
00414         }
00415 
00416         ckd_free(e_str);
00417         if (valid == 0)
00418             return NULL;
00419     }
00420 
00421     v = ckd_calloc(1, sizeof(*v));
00422     memcpy(v, &val, sizeof(val));
00423     v->type = t;
00424 
00425     return v;
00426 }
00427 
00428 /*
00429  * Handles option parsing for cmd_ln_parse_file_r() and cmd_ln_init()
00430  * also takes care of storing argv.
00431  * DO NOT call it from cmd_ln_parse_r()
00432  */
00433 static cmd_ln_t *
00434 parse_options(cmd_ln_t *cmdln, const arg_t *defn, int32 argc, char* argv[], int32 strict)
00435 {
00436     cmd_ln_t *new_cmdln;
00437 
00438     new_cmdln = cmd_ln_parse_r(cmdln, defn, argc, argv, strict);
00439     /* If this failed then clean up and return NULL. */
00440     if (new_cmdln == NULL) {
00441         int32 i;
00442         for (i = 0; i < argc; ++i)
00443             ckd_free(argv[i]);
00444         ckd_free(argv);
00445         return NULL;
00446     }
00447 
00448     /* Otherwise, we need to add the contents of f_argv to the new object. */
00449     if (new_cmdln == cmdln) {
00450         /* If we are adding to a previously passed-in cmdln, then
00451          * store our allocated strings in its f_argv. */
00452         new_cmdln->f_argv = ckd_realloc(new_cmdln->f_argv,
00453                                         (new_cmdln->f_argc + argc)
00454                                         * sizeof(*new_cmdln->f_argv));
00455         memcpy(new_cmdln->f_argv + new_cmdln->f_argc, argv,
00456                argc * sizeof(*argv));
00457         ckd_free(argv);
00458         new_cmdln->f_argc += argc;
00459     }
00460     else {
00461         /* Otherwise, store f_argc and f_argv. */
00462         new_cmdln->f_argc = argc;
00463         new_cmdln->f_argv = argv;
00464     }
00465 
00466     return new_cmdln;
00467 }
00468 
00469 void
00470 cmd_ln_val_free(cmd_ln_val_t *val)
00471 {
00472     int i;
00473     if (val->type & ARG_STRING_LIST) {
00474         char const** array = (char const **)val->val.ptr;
00475         if (array) {
00476             for (i = 0; array[i] != NULL; i++) {
00477                 ckd_free(val->val.ptr);
00478             }
00479             ckd_free(array);
00480         }
00481     }
00482     if (val->type & ARG_STRING)
00483         ckd_free(val->val.ptr);
00484     ckd_free(val);
00485 }
00486 
00487 cmd_ln_t *
00488 cmd_ln_get(void)
00489 {
00490     return global_cmdln;
00491 }
00492 
00493 void
00494 cmd_ln_appl_enter(int argc, char *argv[],
00495                   const char *default_argfn,
00496                   const arg_t * defn)
00497 {
00498     /* Look for default or specified arguments file */
00499     const char *str;
00500 
00501     str = NULL;
00502 
00503     if ((argc == 2) && (strcmp(argv[1], "help") == 0)) {
00504         cmd_ln_print_help(stderr, defn);
00505         exit(1);
00506     }
00507 
00508     if ((argc == 2) && (argv[1][0] != '-'))
00509         str = argv[1];
00510     else if (argc == 1) {
00511         FILE *fp;
00512         E_INFO("Looking for default argument file: %s\n", default_argfn);
00513 
00514         if ((fp = fopen(default_argfn, "r")) == NULL) {
00515             E_INFO("Can't find default argument file %s.\n",
00516                    default_argfn);
00517         }
00518         else {
00519             str = default_argfn;
00520         }
00521         if (fp != NULL)
00522             fclose(fp);
00523     }
00524 
00525 
00526     if (str) {
00527         /* Build command line argument list from file */
00528         E_INFO("Parsing command lines from file %s\n", str);
00529         if (cmd_ln_parse_file(defn, str, TRUE)) {
00530             E_INFOCONT("Usage:\n");
00531             E_INFOCONT("\t%s argument-list, or\n", argv[0]);
00532             E_INFOCONT("\t%s [argument-file] (default file: . %s)\n\n",
00533                     argv[0], default_argfn);
00534             cmd_ln_print_help(stderr, defn);
00535             exit(1);
00536         }
00537     }
00538     else {
00539         cmd_ln_parse(defn, argc, argv, TRUE);
00540     }
00541 }
00542 
00543 void
00544 cmd_ln_appl_exit()
00545 {
00546     cmd_ln_free();
00547 }
00548 
00549 
00550 cmd_ln_t *
00551 cmd_ln_parse_r(cmd_ln_t *inout_cmdln, const arg_t * defn, int32 argc, char *argv[], int strict)
00552 {
00553     int32 i, j, n, argstart;
00554     hash_table_t *defidx = NULL;
00555     cmd_ln_t *cmdln;
00556 
00557     /* Echo command line */
00558 #ifndef _WIN32_WCE
00559     E_INFO("Parsing command line:\n");
00560     for (i = 0; i < argc; i++) {
00561         if (argv[i][0] == '-')
00562             E_INFOCONT("\\\n\t");
00563         E_INFOCONT("%s ", argv[i]);
00564     }
00565     E_INFOCONT("\n\n");
00566     fflush(stderr);
00567 #endif
00568 
00569     /* Construct command-line object */
00570     if (inout_cmdln == NULL) {
00571         cmdln = ckd_calloc(1, sizeof(*cmdln));
00572         cmdln->refcount = 1;
00573     }
00574     else
00575         cmdln = inout_cmdln;
00576 
00577     /* Build a hash table for argument definitions */
00578     defidx = hash_table_new(50, 0);
00579     if (defn) {
00580         for (n = 0; defn[n].name; n++) {
00581             void *v;
00582 
00583             v = hash_table_enter(defidx, defn[n].name, (void *)&defn[n]);
00584             if (strict && (v != &defn[n])) {
00585                 E_ERROR("Duplicate argument name in definition: %s\n", defn[n].name);
00586                 goto error;
00587             }
00588         }
00589     }
00590     else {
00591         /* No definitions. */
00592         n = 0;
00593     }
00594 
00595     /* Allocate memory for argument values */
00596     if (cmdln->ht == NULL)
00597         cmdln->ht = hash_table_new(n, 0 /* argument names are case-sensitive */ );
00598 
00599 
00600     /* skip argv[0] if it doesn't start with dash */
00601     argstart = 0;
00602     if (argc > 0 && argv[0][0] != '-') {
00603         argstart = 1;
00604     } 
00605 
00606     /* Parse command line arguments (name-value pairs) */
00607     for (j = argstart; j < argc; j += 2) {
00608         arg_t *argdef;
00609         cmd_ln_val_t *val;
00610         void *v;
00611 
00612         if (hash_table_lookup(defidx, argv[j], &v) < 0) {
00613             if (strict) {
00614                 E_ERROR("Unknown argument name '%s'\n", argv[j]);
00615                 goto error;
00616             }
00617             else if (defn == NULL)
00618                 v = NULL;
00619             else
00620                 continue;
00621         }
00622         argdef = v;
00623 
00624         /* Enter argument value */      
00625         if (j + 1 >= argc) {
00626             cmd_ln_print_help_r(cmdln, stderr, defn);
00627             E_ERROR("Argument value for '%s' missing\n", argv[j]);
00628             goto error;
00629         }
00630 
00631         if (argdef == NULL)
00632             val = cmd_ln_val_init(ARG_STRING, argv[j + 1]);
00633         else {
00634             if ((val = cmd_ln_val_init(argdef->type, argv[j + 1])) == NULL) {
00635                 cmd_ln_print_help_r(cmdln, stderr, defn);
00636                 E_ERROR("Bad argument value for %s: %s\n", argv[j],
00637                         argv[j + 1]);
00638                 goto error;
00639             }
00640         }
00641 
00642         if ((v = hash_table_enter(cmdln->ht, argv[j], (void *)val)) != (void *)val) {
00643             if (strict) {
00644                 cmd_ln_val_free(val);
00645                 E_ERROR("Duplicate argument name in arguments: %s\n",
00646                         argdef->name);
00647                 goto error;
00648             }
00649             else {
00650                 v = hash_table_replace(cmdln->ht, argv[j], (void *)val);
00651                 cmd_ln_val_free((cmd_ln_val_t *)v);
00652             }
00653         }
00654     }
00655 
00656     /* Fill in default values, if any, for unspecified arguments */
00657     for (i = 0; i < n; i++) {
00658         cmd_ln_val_t *val;
00659         void *v;
00660 
00661         if (hash_table_lookup(cmdln->ht, defn[i].name, &v) < 0) {
00662             if ((val = cmd_ln_val_init(defn[i].type, defn[i].deflt)) == NULL) {
00663                 E_ERROR
00664                     ("Bad default argument value for %s: %s\n",
00665                      defn[i].name, defn[i].deflt);
00666                 goto error;
00667             }
00668             hash_table_enter(cmdln->ht, defn[i].name, (void *)val);
00669         }
00670     }
00671 
00672     /* Check for required arguments; exit if any missing */
00673     j = 0;
00674     for (i = 0; i < n; i++) {
00675         if (defn[i].type & ARG_REQUIRED) {
00676             void *v;
00677             if (hash_table_lookup(cmdln->ht, defn[i].name, &v) != 0)
00678                 E_ERROR("Missing required argument %s\n", defn[i].name);
00679         }
00680     }
00681     if (j > 0) {
00682         cmd_ln_print_help_r(cmdln, stderr, defn);
00683         goto error;
00684     }
00685 
00686     if (strict && argc == 1) {
00687         E_ERROR("No arguments given, exiting\n");
00688         cmd_ln_print_help_r(cmdln, stderr, defn);
00689         goto error;
00690     }
00691 
00692 #ifndef _WIN32_WCE
00693     /* Print configuration */
00694     E_INFOCONT("Current configuration:\n");
00695     arg_dump_r(cmdln, err_get_logfp(), defn, 0);
00696 #endif
00697     hash_table_free(defidx);
00698     return cmdln;
00699 
00700   error:
00701     if (defidx)
00702         hash_table_free(defidx);
00703     if (inout_cmdln == NULL)
00704         cmd_ln_free_r(cmdln);
00705     E_ERROR("cmd_ln_parse_r failed\n");
00706     return NULL;
00707 }
00708 
00709 cmd_ln_t *
00710 cmd_ln_init(cmd_ln_t *inout_cmdln, const arg_t *defn, int32 strict, ...)
00711 {
00712     va_list args;
00713     const char *arg, *val;
00714     char **f_argv;
00715     int32 f_argc;
00716 
00717     va_start(args, strict);
00718     f_argc = 0;
00719     while ((arg = va_arg(args, const char *))) {
00720         ++f_argc;
00721         val = va_arg(args, const char*);
00722         if (val == NULL) {
00723             E_ERROR("Number of arguments must be even!\n");
00724             return NULL;
00725         }
00726         ++f_argc;
00727     }
00728     va_end(args);
00729 
00730     /* Now allocate f_argv */
00731     f_argv = ckd_calloc(f_argc, sizeof(*f_argv));
00732     va_start(args, strict);
00733     f_argc = 0;
00734     while ((arg = va_arg(args, const char *))) {
00735         f_argv[f_argc] = ckd_salloc(arg);
00736         ++f_argc;
00737         val = va_arg(args, const char*);
00738         f_argv[f_argc] = ckd_salloc(val);
00739         ++f_argc;
00740     }
00741     va_end(args);
00742 
00743     return parse_options(inout_cmdln, defn, f_argc, f_argv, strict);
00744 }
00745 
00746 int
00747 cmd_ln_parse(const arg_t * defn, int32 argc, char *argv[], int strict)
00748 {
00749     cmd_ln_t *cmdln;
00750 
00751     cmdln = cmd_ln_parse_r(global_cmdln, defn, argc, argv, strict);
00752     if (cmdln == NULL) {
00753         /* Old, bogus behaviour... */
00754         E_ERROR("cmd_ln_parse failed, forced exit\n");
00755         exit(-1);
00756     }
00757     /* Initialize global_cmdln if not present. */
00758     if (global_cmdln == NULL) {
00759         global_cmdln = cmdln;
00760     }
00761     return 0;
00762 }
00763 
00764 cmd_ln_t *
00765 cmd_ln_parse_file_r(cmd_ln_t *inout_cmdln, const arg_t * defn, const char *filename, int32 strict)
00766 {
00767     FILE *file;
00768     int argc;
00769     int argv_size;
00770     char *str;
00771     int arg_max_length = 512;
00772     int len = 0;
00773     int quoting, ch;
00774     char **f_argv;
00775     int rv = 0;
00776     const char separator[] = " \t\r\n";
00777 
00778     if ((file = fopen(filename, "r")) == NULL) {
00779         E_ERROR("Cannot open configuration file %s for reading\n",
00780                 filename);
00781         return NULL;
00782     }
00783 
00784     ch = fgetc(file);
00785     /* Skip to the next interesting character */
00786     for (; ch != EOF && strchr(separator, ch); ch = fgetc(file)) ;
00787 
00788     if (ch == EOF) {
00789         fclose(file);
00790         return NULL;
00791     }
00792 
00793     /*
00794      * Initialize default argv, argc, and argv_size.
00795      */
00796     argv_size = 10;
00797     argc = 0;
00798     f_argv = ckd_calloc(argv_size, sizeof(char *));
00799     /* Silently make room for \0 */
00800     str = ckd_calloc(arg_max_length + 1, sizeof(char));
00801     quoting = 0;
00802 
00803     do {
00804         /* Handle arguments that are commented out */
00805         if (len == 0 && argc % 2 == 0) {
00806             while (ch == '#') {
00807                 /* Skip everything until newline */
00808                 for (ch = fgetc(file); ch != EOF && ch != '\n'; ch = fgetc(file)) ;
00809                 /* Skip to the next interesting character */
00810                 for (ch = fgetc(file); ch != EOF && strchr(separator, ch); ch = fgetc(file)) ;
00811             }
00812 
00813             /* Check if we are at the last line (without anything interesting in it) */
00814             if (ch == EOF)
00815                 break;
00816         }
00817 
00818         /* Handle quoted arguments */
00819         if (ch == '"' || ch == '\'') {
00820             if (quoting == ch) /* End a quoted section with the same type */
00821                 quoting = 0;
00822             else if (quoting) {
00823                 E_ERROR("Nesting quotations is not supported!\n");
00824                 rv = 1;
00825                 break;
00826             }
00827             else
00828                 quoting = ch; /* Start a quoted section */
00829         }
00830         else if (ch == EOF || (!quoting && strchr(separator, ch))) {
00831             /* Reallocate argv so it is big enough to contain all the arguments */
00832             if (argc >= argv_size) {
00833                 char **tmp_argv;
00834                 if (!(tmp_argv =
00835                        ckd_realloc(f_argv, argv_size * 2 * sizeof(char *)))) {
00836                     rv = 1;
00837                     break;
00838                 }
00839                 f_argv = tmp_argv;
00840                 argv_size *= 2;
00841             }
00842             /* Add the string to the list of arguments */
00843             f_argv[argc] = ckd_salloc(str);
00844             len = 0;
00845             str[0] = '\0';
00846             argc++;
00847 
00848             if (quoting)
00849                 E_WARN("Unclosed quotation, having EOF close it...\n");
00850 
00851             /* Skip to the next interesting character */
00852             for (; ch != EOF && strchr(separator, ch); ch = fgetc(file)) ;
00853 
00854             if (ch == EOF)
00855                 break;
00856 
00857             /* We already have the next character */
00858             continue;
00859         }
00860         else {
00861             if (len >= arg_max_length) {
00862                 /* Make room for more chars (including the \0 !) */
00863                 char *tmp_str = str;
00864                 if ((tmp_str = ckd_realloc(str, (1 + arg_max_length * 2) * sizeof(char))) == NULL) {
00865                     rv = 1;
00866                     break;
00867                 }
00868                 str = tmp_str;
00869                 arg_max_length *= 2;
00870             }
00871             /* Add the char to the argument string */
00872             str[len++] = ch;
00873             /* Always null terminate */
00874             str[len] = '\0';
00875         }
00876 
00877         ch = fgetc(file);
00878     } while (1);
00879 
00880     fclose(file);
00881 
00882     ckd_free(str);
00883 
00884     if (rv) {
00885         for (ch = 0; ch < argc; ++ch)
00886             ckd_free(f_argv[ch]);
00887         ckd_free(f_argv);
00888         return NULL;
00889     }
00890 
00891     return parse_options(inout_cmdln, defn, argc, f_argv, strict);
00892 }
00893 
00894 int
00895 cmd_ln_parse_file(const arg_t * defn, const char *filename, int32 strict)
00896 {
00897     cmd_ln_t *cmdln;
00898 
00899     cmdln = cmd_ln_parse_file_r(global_cmdln, defn, filename, strict);
00900     if (cmdln == NULL) {
00901         return -1;
00902     }
00903     /* Initialize global_cmdln if not present. */
00904     if (global_cmdln == NULL) {
00905         global_cmdln = cmdln;
00906     }
00907     return 0;
00908 }
00909 
00910 void
00911 cmd_ln_print_help_r(cmd_ln_t *cmdln, FILE * fp, arg_t const* defn)
00912 {
00913     if (defn == NULL)
00914         return;
00915     fprintf(fp, "Arguments list definition:\n");
00916     arg_dump_r(cmdln, fp, defn, 1);
00917     fflush(fp);
00918 }
00919 
00920 int
00921 cmd_ln_exists_r(cmd_ln_t *cmdln, const char *name)
00922 {
00923     void *val;
00924     if (cmdln == NULL)
00925         return FALSE;
00926     return (hash_table_lookup(cmdln->ht, name, &val) == 0);
00927 }
00928 
00929 anytype_t *
00930 cmd_ln_access_r(cmd_ln_t *cmdln, const char *name)
00931 {
00932     void *val;
00933     if (hash_table_lookup(cmdln->ht, name, &val) < 0) {
00934         E_ERROR("Unknown argument: %s\n", name);
00935         return NULL;
00936     }
00937     return (anytype_t *)val;
00938 }
00939 
00940 char const *
00941 cmd_ln_str_r(cmd_ln_t *cmdln, char const *name)
00942 {
00943     anytype_t *val;
00944     val = cmd_ln_access_r(cmdln, name);
00945     if (val == NULL)
00946         return NULL;
00947     return (char const *)val->ptr;
00948 }
00949 
00950 char const **
00951 cmd_ln_str_list_r(cmd_ln_t *cmdln, char const *name)
00952 {
00953     anytype_t *val;
00954     val = cmd_ln_access_r(cmdln, name);
00955     if (val == NULL)
00956         return NULL;
00957     return (char const **)val->ptr;
00958 }
00959 
00960 long
00961 cmd_ln_int_r(cmd_ln_t *cmdln, char const *name)
00962 {
00963     anytype_t *val;
00964     val = cmd_ln_access_r(cmdln, name);
00965     if (val == NULL)
00966         return 0L;
00967     return val->i;
00968 }
00969 
00970 double
00971 cmd_ln_float_r(cmd_ln_t *cmdln, char const *name)
00972 {
00973     anytype_t *val;
00974     val = cmd_ln_access_r(cmdln, name);
00975     if (val == NULL)
00976         return 0.0;
00977     return val->fl;
00978 }
00979 
00980 void
00981 cmd_ln_set_str_r(cmd_ln_t *cmdln, char const *name, char const *str)
00982 {
00983     anytype_t *val;
00984     val = cmd_ln_access_r(cmdln, name);
00985     if (val == NULL) {
00986         E_ERROR("Unknown argument: %s\n", name);
00987         return;
00988     }
00989     ckd_free(val->ptr);
00990     if (str == NULL)
00991         val->ptr = NULL;
00992     else
00993         val->ptr = ckd_salloc(str);
00994 }
00995 
00996 void
00997 cmd_ln_set_int_r(cmd_ln_t *cmdln, char const *name, long iv)
00998 {
00999     anytype_t *val;
01000     val = cmd_ln_access_r(cmdln, name);
01001     if (val == NULL) {
01002         E_ERROR("Unknown argument: %s\n", name);
01003         return;
01004     }
01005     val->i = iv;
01006 }
01007 
01008 void
01009 cmd_ln_set_float_r(cmd_ln_t *cmdln, char const *name, double fv)
01010 {
01011     anytype_t *val;
01012     val = cmd_ln_access_r(cmdln, name);
01013     if (val == NULL) {
01014         E_ERROR("Unknown argument: %s\n", name);
01015         return;
01016     }
01017     val->fl = fv;
01018 }
01019 
01020 cmd_ln_t *
01021 cmd_ln_retain(cmd_ln_t *cmdln)
01022 {
01023     ++cmdln->refcount;
01024     return cmdln;
01025 }
01026 
01027 int
01028 cmd_ln_free_r(cmd_ln_t *cmdln)
01029 {
01030     if (cmdln == NULL)
01031         return 0;
01032     if (--cmdln->refcount > 0)
01033         return cmdln->refcount;
01034 
01035     if (cmdln->ht) {
01036         glist_t entries;
01037         gnode_t *gn;
01038         int32 n;
01039 
01040         entries = hash_table_tolist(cmdln->ht, &n);
01041         for (gn = entries; gn; gn = gnode_next(gn)) {
01042             hash_entry_t *e = gnode_ptr(gn);
01043             cmd_ln_val_free((cmd_ln_val_t *)e->val);
01044         }
01045         glist_free(entries);
01046         hash_table_free(cmdln->ht);
01047         cmdln->ht = NULL;
01048     }
01049 
01050     if (cmdln->f_argv) {
01051         int32 i;
01052         for (i = 0; i < cmdln->f_argc; ++i) {
01053             ckd_free(cmdln->f_argv[i]);
01054         }
01055         ckd_free(cmdln->f_argv);
01056         cmdln->f_argv = NULL;
01057         cmdln->f_argc = 0;
01058     }
01059     ckd_free(cmdln);
01060     return 0;
01061 }
01062 
01063 void
01064 cmd_ln_free(void)
01065 {
01066     cmd_ln_free_r(global_cmdln);
01067     global_cmdln = NULL;
01068 }