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 "cmd_ln.h"
00079 #include "err.h"
00080 #include "ckd_alloc.h"
00081 #include "hash_table.h"
00082 #include "case.h"
00083 
00084 typedef struct cmd_ln_val_s {
00085     anytype_t val;
00086     int type;
00087 } cmd_ln_val_t;
00088 
00089 struct cmd_ln_s {
00090     int refcount;
00091     hash_table_t *ht;
00092     char **f_argv;
00093     uint32 f_argc;
00094 };
00095 
00097 cmd_ln_t *global_cmdln;
00098 static void arg_dump_r(cmd_ln_t *cmdln, FILE * fp, arg_t const *defn, int32 doc);
00099 static cmd_ln_t * parse_options(cmd_ln_t *cmdln, const arg_t *defn, int32 argc, char* argv[], int32 strict);
00100 
00101 /*
00102  * Find max length of name and default fields in the given defn array.
00103  * Return #items in defn array.
00104  */
00105 static int32
00106 arg_strlen(const arg_t * defn, int32 * namelen, int32 * deflen)
00107 {
00108     int32 i, l;
00109 
00110     *namelen = *deflen = 0;
00111     for (i = 0; defn[i].name; i++) {
00112         l = strlen(defn[i].name);
00113         if (*namelen < l)
00114             *namelen = l;
00115 
00116         if (defn[i].deflt)
00117             l = strlen(defn[i].deflt);
00118         else
00119             l = strlen("(null)");
00120         /*      E_INFO("string default, %s , name %s, length %d\n",defn[i].deflt,defn[i].name,l); */
00121         if (*deflen < l)
00122             *deflen = l;
00123     }
00124 
00125     return i;
00126 }
00127 
00128 
00129 static int32
00130 cmp_name(const void *a, const void *b)
00131 {
00132     return (strcmp_nocase
00133             ((* (arg_t**) a)->name,
00134              (* (arg_t**) b)->name));
00135 }
00136 
00137 static const arg_t **
00138 arg_sort(const arg_t * defn, int32 n)
00139 {
00140     const arg_t ** pos;
00141     int32 i;
00142 
00143     pos = (const arg_t **) ckd_calloc(n, sizeof(arg_t *));
00144     for (i = 0; i < n; ++i)
00145         pos[i] = &defn[i];
00146     qsort(pos, n, sizeof(arg_t *), cmp_name);
00147 
00148     return pos;
00149 }
00150 
00151 static size_t
00152 strnappend(char **dest, size_t *dest_allocation, 
00153        const char *source, size_t n)
00154 {
00155     size_t source_len, required_allocation;
00156 
00157     if (dest == NULL || dest_allocation == NULL)
00158         return -1;
00159     if (*dest == NULL && *dest_allocation != 0)
00160         return -1;
00161     if (source == NULL)
00162         return *dest_allocation;
00163 
00164     source_len = strlen(source);
00165     if (n && n < source_len)
00166         source_len = n;
00167 
00168     required_allocation = (*dest ? strlen(*dest) : 0) + source_len + 1;
00169     if (*dest_allocation < required_allocation) {
00170         if (*dest_allocation == 0) {
00171             *dest = ckd_calloc(required_allocation * 2, 1);
00172         } else {
00173             *dest = ckd_realloc(*dest, required_allocation * 2);
00174         }
00175         *dest_allocation = required_allocation * 2;
00176     } 
00177 
00178     strncat(*dest, source, source_len);
00179 
00180     return *dest_allocation;
00181 }
00182 
00183 static size_t
00184 strappend(char **dest, size_t *dest_allocation, 
00185        const char *source)
00186 {
00187     return strnappend(dest, dest_allocation, source, 0);
00188 }
00189 
00190 static char*
00191 arg_resolve_env(const char *str)
00192 {
00193     char *resolved_str = NULL;
00194     char env_name[100];
00195     const char *env_val;
00196     size_t alloced = 0;
00197     const char *i = str, *j;
00198 
00199     /* calculate required resolved_str size */
00200     do {
00201         j = strstr(i, "$(");
00202         if (j != NULL) {
00203             if (j != i) {
00204                 strnappend(&resolved_str, &alloced, i, j - i);
00205                 i = j;
00206             }
00207             j = strchr(i + 2, ')');
00208             if (j != NULL) {
00209                 if (j - (i + 2) < 100) {
00210                     strncpy(env_name, i + 2, j - (i + 2));
00211                     env_name[j - (i + 2)] = '\0';
00212                     #if !defined(_WIN32_WCE)
00213                     env_val = getenv(env_name);
00214                     if (env_val)
00215                         strappend(&resolved_str, &alloced, env_val);
00216                     #else
00217                     env_val = 0;
00218                     #endif
00219                 }
00220                 i = j + 1;
00221             } else {
00222                 /* unclosed, copy and skip */
00223                 j = i + 2;
00224                 strnappend(&resolved_str, &alloced, i, j - i);
00225                 i = j;
00226             }
00227         } else {
00228             strappend(&resolved_str, &alloced, i);
00229         }
00230     } while(j != NULL);
00231 
00232     return resolved_str;
00233 }
00234 
00235 static void
00236 arg_dump_r(cmd_ln_t *cmdln, FILE * fp, const arg_t * defn, int32 doc)
00237 {
00238     const arg_t **pos;
00239     int32 i, l, n;
00240     int32 namelen, deflen;
00241     anytype_t *vp;
00242 
00243     /* No definitions, do nothing. */
00244     if (defn == NULL)
00245         return;
00246     if (fp == NULL)
00247         return;
00248 
00249     /* Find max lengths of name and default value fields, and #entries in defn */
00250     n = arg_strlen(defn, &namelen, &deflen);
00251     /*    E_INFO("String length %d. Name length %d, Default Length %d\n",n, namelen, deflen); */
00252     namelen = namelen & 0xfffffff8;     /* Previous tab position */
00253     deflen = deflen & 0xfffffff8;       /* Previous tab position */
00254 
00255     fprintf(fp, "[NAME]");
00256     for (l = strlen("[NAME]"); l < namelen; l += 8)
00257         fprintf(fp, "\t");
00258     fprintf(fp, "\t[DEFLT]");
00259     for (l = strlen("[DEFLT]"); l < deflen; l += 8)
00260         fprintf(fp, "\t");
00261 
00262     if (doc) {
00263         fprintf(fp, "\t[DESCR]\n");
00264     }
00265     else {
00266         fprintf(fp, "\t[VALUE]\n");
00267     }
00268 
00269     /* Print current configuration, sorted by name */
00270     pos = arg_sort(defn, n);
00271     for (i = 0; i < n; i++) {
00272         fprintf(fp, "%s", pos[i]->name);
00273         for (l = strlen(pos[i]->name); l < namelen; l += 8)
00274             fprintf(fp, "\t");
00275 
00276         fprintf(fp, "\t");
00277         if (pos[i]->deflt) {
00278             fprintf(fp, "%s", pos[i]->deflt);
00279             l = strlen(pos[i]->deflt);
00280         }
00281         else
00282             l = 0;
00283         for (; l < deflen; l += 8)
00284             fprintf(fp, "\t");
00285 
00286         fprintf(fp, "\t");
00287         if (doc) {
00288             if (pos[i]->doc)
00289                 fprintf(fp, "%s", pos[i]->doc);
00290         }
00291         else {
00292             vp = cmd_ln_access_r(cmdln, pos[i]->name);
00293             if (vp) {
00294                 switch (pos[i]->type) {
00295                 case ARG_INTEGER:
00296                 case REQARG_INTEGER:
00297                     fprintf(fp, "%ld", vp->i);
00298                     break;
00299                 case ARG_FLOATING:
00300                 case REQARG_FLOATING:
00301                     fprintf(fp, "%e", vp->fl);
00302                     break;
00303                 case ARG_STRING:
00304                 case REQARG_STRING:
00305                     if (vp->ptr)
00306                         fprintf(fp, "%s", (char *)vp->ptr);
00307                     break;
00308                 case ARG_BOOLEAN:
00309                 case REQARG_BOOLEAN:
00310                     fprintf(fp, "%s", vp->i ? "yes" : "no");
00311                     break;
00312                 default:
00313                     E_ERROR("Unknown argument type: %d\n", pos[i]->type);
00314                 }
00315             }
00316         }
00317 
00318         fprintf(fp, "\n");
00319     }
00320     ckd_free(pos);
00321 
00322     fprintf(fp, "\n");
00323     fflush(fp);
00324 }
00325 
00326 static cmd_ln_val_t *
00327 cmd_ln_val_init(int t, const char *str)
00328 {
00329     cmd_ln_val_t *v;
00330     anytype_t val;
00331     char *e_str;
00332 
00333     if (!str) {
00334         /* For lack of a better default value. */
00335         memset(&val, 0, sizeof(val));
00336     }
00337     else {
00338         int valid = 1;
00339         e_str = arg_resolve_env(str);
00340 
00341         switch (t) {
00342         case ARG_INTEGER:
00343         case REQARG_INTEGER:
00344             if (sscanf(e_str, "%ld", &val.i) != 1)
00345                 valid = 0;
00346             break;
00347         case ARG_FLOATING:
00348         case REQARG_FLOATING:
00349             if (sscanf(e_str, "%lf", &val.fl) != 1)
00350                 valid = 0;
00351             break;
00352         case ARG_BOOLEAN:
00353         case REQARG_BOOLEAN:
00354             if ((e_str[0] == 'y') || (e_str[0] == 't') ||
00355                 (e_str[0] == 'Y') || (e_str[0] == 'T') || (e_str[0] == '1')) {
00356                 val.i = TRUE;
00357             }
00358             else if ((e_str[0] == 'n') || (e_str[0] == 'f') ||
00359                      (e_str[0] == 'N') || (e_str[0] == 'F') |
00360                      (e_str[0] == '0')) {
00361                 val.i = FALSE;
00362             }
00363             else {
00364                 E_ERROR("Unparsed boolean value '%s'\n", str);
00365                 valid = 0;
00366             }
00367             break;
00368         case ARG_STRING:
00369         case REQARG_STRING:
00370             val.ptr = ckd_salloc(e_str);
00371             break;
00372         default:
00373             E_ERROR("Unknown argument type: %d\n", t);
00374             valid = 0;
00375         }
00376 
00377         ckd_free(e_str);
00378         if (valid == 0)
00379             return NULL;
00380     }
00381 
00382     v = ckd_calloc(1, sizeof(*v));
00383     memcpy(v, &val, sizeof(val));
00384     v->type = t;
00385 
00386     return v;
00387 }
00388 
00389 /*
00390  * Handles option parsing for cmd_ln_parse_file_r() and cmd_ln_init()
00391  * also takes care of storing argv.
00392  * DO NOT call it from cmd_ln_parse_r()
00393  */
00394 static cmd_ln_t *
00395 parse_options(cmd_ln_t *cmdln, const arg_t *defn, int32 argc, char* argv[], int32 strict)
00396 {
00397     cmd_ln_t *new_cmdln;
00398 
00399     new_cmdln = cmd_ln_parse_r(cmdln, defn, argc, argv, strict);
00400     /* If this failed then clean up and return NULL. */
00401     if (new_cmdln == NULL) {
00402         int32 i;
00403         for (i = 0; i < argc; ++i)
00404             ckd_free(argv[i]);
00405         ckd_free(argv);
00406         return NULL;
00407     }
00408 
00409     /* Otherwise, we need to add the contents of f_argv to the new object. */
00410     if (new_cmdln == cmdln) {
00411         /* If we are adding to a previously passed-in cmdln, then
00412          * store our allocated strings in its f_argv. */
00413         new_cmdln->f_argv = ckd_realloc(new_cmdln->f_argv,
00414                                         (new_cmdln->f_argc + argc)
00415                                         * sizeof(*new_cmdln->f_argv));
00416         memcpy(new_cmdln->f_argv + new_cmdln->f_argc, argv,
00417                argc * sizeof(*argv));
00418         ckd_free(argv);
00419         new_cmdln->f_argc += argc;
00420     }
00421     else {
00422         /* Otherwise, store f_argc and f_argv. */
00423         new_cmdln->f_argc = argc;
00424         new_cmdln->f_argv = argv;
00425     }
00426 
00427     return new_cmdln;
00428 }
00429 
00430 void
00431 cmd_ln_val_free(cmd_ln_val_t *val)
00432 {
00433     if (val->type & ARG_STRING)
00434         ckd_free(val->val.ptr);
00435     ckd_free(val);
00436 }
00437 
00438 cmd_ln_t *
00439 cmd_ln_get(void)
00440 {
00441     return global_cmdln;
00442 }
00443 
00444 void
00445 cmd_ln_appl_enter(int argc, char *argv[],
00446                   const char *default_argfn,
00447                   const arg_t * defn)
00448 {
00449     /* Look for default or specified arguments file */
00450     const char *str;
00451 
00452     str = NULL;
00453 
00454     if ((argc == 2) && (strcmp(argv[1], "help") == 0)) {
00455         cmd_ln_print_help(stderr, defn);
00456         exit(1);
00457     }
00458 
00459     if ((argc == 2) && (argv[1][0] != '-'))
00460         str = argv[1];
00461     else if (argc == 1) {
00462         FILE *fp;
00463         E_INFO("Looking for default argument file: %s\n", default_argfn);
00464 
00465         if ((fp = fopen(default_argfn, "r")) == NULL) {
00466             E_INFO("Can't find default argument file %s.\n",
00467                    default_argfn);
00468         }
00469         else {
00470             str = default_argfn;
00471         }
00472         if (fp != NULL)
00473             fclose(fp);
00474     }
00475 
00476 
00477     if (str) {
00478         /* Build command line argument list from file */
00479         E_INFO("Parsing command lines from file %s\n", str);
00480         if (cmd_ln_parse_file(defn, str, TRUE)) {
00481             E_INFOCONT("Usage:\n");
00482             E_INFOCONT("\t%s argument-list, or\n", argv[0]);
00483             E_INFOCONT("\t%s [argument-file] (default file: . %s)\n\n",
00484                     argv[0], default_argfn);
00485             cmd_ln_print_help(stderr, defn);
00486             exit(1);
00487         }
00488     }
00489     else {
00490         cmd_ln_parse(defn, argc, argv, TRUE);
00491     }
00492 }
00493 
00494 void
00495 cmd_ln_appl_exit()
00496 {
00497     cmd_ln_free();
00498 }
00499 
00500 
00501 cmd_ln_t *
00502 cmd_ln_parse_r(cmd_ln_t *inout_cmdln, const arg_t * defn, int32 argc, char *argv[], int strict)
00503 {
00504     int32 i, j, n;
00505     hash_table_t *defidx = NULL;
00506     cmd_ln_t *cmdln;
00507 
00508     /* Echo command line */
00509 #ifndef _WIN32_WCE
00510     E_INFO("Parsing command line:\n");
00511     for (i = 0; i < argc; i++) {
00512         if (argv[i][0] == '-')
00513             E_INFOCONT("\\\n\t");
00514         E_INFOCONT("%s ", argv[i]);
00515     }
00516     E_INFOCONT("\n\n");
00517     fflush(stderr);
00518 #endif
00519 
00520     /* Construct command-line object */
00521     if (inout_cmdln == NULL) {
00522         cmdln = ckd_calloc(1, sizeof(*cmdln));
00523         cmdln->refcount = 1;
00524     }
00525     else
00526         cmdln = inout_cmdln;
00527 
00528     /* Build a hash table for argument definitions */
00529     defidx = hash_table_new(50, 0);
00530     if (defn) {
00531         for (n = 0; defn[n].name; n++) {
00532             void *v;
00533 
00534             v = hash_table_enter(defidx, defn[n].name, (void *)&defn[n]);
00535             if (strict && (v != &defn[n])) {
00536                 E_ERROR("Duplicate argument name in definition: %s\n", defn[n].name);
00537                 goto error;
00538             }
00539         }
00540     }
00541     else {
00542         /* No definitions. */
00543         n = 0;
00544     }
00545 
00546     /* Allocate memory for argument values */
00547     if (cmdln->ht == NULL)
00548         cmdln->ht = hash_table_new(n, 0 /* argument names are case-sensitive */ );
00549 
00550     /* Parse command line arguments (name-value pairs); skip argv[0] if argc is odd */
00551     for (j = argc % 2; j < argc; j += 2) {
00552         arg_t *argdef;
00553         cmd_ln_val_t *val;
00554         void *v;
00555 
00556         if (j + 1 >= argc) {
00557             cmd_ln_print_help_r(cmdln, stderr, defn);
00558             E_ERROR("Argument value for '%s' missing\n", argv[j]);
00559             goto error;
00560         }
00561         if (hash_table_lookup(defidx, argv[j], &v) < 0) {
00562             if (strict) {
00563                 E_ERROR("Unknown argument name '%s'\n", argv[j]);
00564                 goto error;
00565             }
00566             else if (defn == NULL)
00567                 v = NULL;
00568             else
00569                 continue;
00570         }
00571         argdef = v;
00572 
00573         /* Enter argument value */
00574         if (argdef == NULL)
00575             val = cmd_ln_val_init(ARG_STRING, argv[j + 1]);
00576         else {
00577             if ((val = cmd_ln_val_init(argdef->type, argv[j + 1])) == NULL) {
00578                 cmd_ln_print_help_r(cmdln, stderr, defn);
00579                 E_ERROR("Bad argument value for %s: %s\n", argv[j],
00580                         argv[j + 1]);
00581                 goto error;
00582             }
00583         }
00584 
00585         if ((v = hash_table_enter(cmdln->ht, argv[j], (void *)val)) != (void *)val) {
00586             if (strict) {
00587                 cmd_ln_val_free(val);
00588                 E_ERROR("Duplicate argument name in arguments: %s\n",
00589                         argdef->name);
00590                 goto error;
00591             }
00592             else {
00593                 v = hash_table_replace(cmdln->ht, argv[j], (void *)val);
00594                 cmd_ln_val_free((cmd_ln_val_t *)v);
00595             }
00596         }
00597     }
00598 
00599     /* Fill in default values, if any, for unspecified arguments */
00600     for (i = 0; i < n; i++) {
00601         cmd_ln_val_t *val;
00602         void *v;
00603 
00604         if (hash_table_lookup(cmdln->ht, defn[i].name, &v) < 0) {
00605             if ((val = cmd_ln_val_init(defn[i].type, defn[i].deflt)) == NULL) {
00606                 E_ERROR
00607                     ("Bad default argument value for %s: %s\n",
00608                      defn[i].name, defn[i].deflt);
00609                 goto error;
00610             }
00611             hash_table_enter(cmdln->ht, defn[i].name, (void *)val);
00612         }
00613     }
00614 
00615     /* Check for required arguments; exit if any missing */
00616     j = 0;
00617     for (i = 0; i < n; i++) {
00618         if (defn[i].type & ARG_REQUIRED) {
00619             void *v;
00620             if (hash_table_lookup(cmdln->ht, defn[i].name, &v) != 0)
00621                 E_ERROR("Missing required argument %s\n", defn[i].name);
00622         }
00623     }
00624     if (j > 0) {
00625         cmd_ln_print_help_r(cmdln, stderr, defn);
00626         goto error;
00627     }
00628 
00629     if (strict && argc == 1) {
00630         E_ERROR("No arguments given, exiting\n");
00631         cmd_ln_print_help_r(cmdln, stderr, defn);
00632         goto error;
00633     }
00634 
00635 #ifndef _WIN32_WCE
00636     /* Print configuration */
00637     E_INFOCONT("Current configuration:\n");
00638     arg_dump_r(cmdln, err_get_logfp(), defn, 0);
00639 #endif
00640     hash_table_free(defidx);
00641     return cmdln;
00642 
00643   error:
00644     if (defidx)
00645         hash_table_free(defidx);
00646     if (inout_cmdln == NULL)
00647         cmd_ln_free_r(cmdln);
00648     E_ERROR("cmd_ln_parse_r failed\n");
00649     return NULL;
00650 }
00651 
00652 cmd_ln_t *
00653 cmd_ln_init(cmd_ln_t *inout_cmdln, const arg_t *defn, int32 strict, ...)
00654 {
00655     va_list args;
00656     const char *arg, *val;
00657     char **f_argv;
00658     int32 f_argc;
00659 
00660     va_start(args, strict);
00661     f_argc = 0;
00662     while ((arg = va_arg(args, const char *))) {
00663         ++f_argc;
00664         val = va_arg(args, const char*);
00665         if (val == NULL) {
00666             E_ERROR("Number of arguments must be even!\n");
00667             return NULL;
00668         }
00669         ++f_argc;
00670     }
00671     va_end(args);
00672 
00673     /* Now allocate f_argv */
00674     f_argv = ckd_calloc(f_argc, sizeof(*f_argv));
00675     va_start(args, strict);
00676     f_argc = 0;
00677     while ((arg = va_arg(args, const char *))) {
00678         f_argv[f_argc] = ckd_salloc(arg);
00679         ++f_argc;
00680         val = va_arg(args, const char*);
00681         f_argv[f_argc] = ckd_salloc(val);
00682         ++f_argc;
00683     }
00684     va_end(args);
00685 
00686     return parse_options(inout_cmdln, defn, f_argc, f_argv, strict);
00687 }
00688 
00689 int
00690 cmd_ln_parse(const arg_t * defn, int32 argc, char *argv[], int strict)
00691 {
00692     cmd_ln_t *cmdln;
00693 
00694     cmdln = cmd_ln_parse_r(global_cmdln, defn, argc, argv, strict);
00695     if (cmdln == NULL) {
00696         /* Old, bogus behaviour... */
00697         E_ERROR("cmd_ln_parse failed, forced exit\n");
00698         exit(-1);
00699     }
00700     /* Initialize global_cmdln if not present. */
00701     if (global_cmdln == NULL) {
00702         global_cmdln = cmdln;
00703     }
00704     return 0;
00705 }
00706 
00707 cmd_ln_t *
00708 cmd_ln_parse_file_r(cmd_ln_t *inout_cmdln, const arg_t * defn, const char *filename, int32 strict)
00709 {
00710     FILE *file;
00711     int argc;
00712     int argv_size;
00713     char *str;
00714     int arg_max_length = 512;
00715     int len = 0;
00716     int quoting, ch;
00717     char **f_argv;
00718     int rv = 0;
00719     const char separator[] = " \t\r\n";
00720 
00721     if ((file = fopen(filename, "r")) == NULL) {
00722         E_ERROR("Cannot open configuration file %s for reading\n",
00723                 filename);
00724         return NULL;
00725     }
00726 
00727     ch = fgetc(file);
00728     /* Skip to the next interesting character */
00729     for (; ch != EOF && strchr(separator, ch); ch = fgetc(file)) ;
00730 
00731     if (ch == EOF) {
00732         fclose(file);
00733         return NULL;
00734     }
00735 
00736     /*
00737      * Initialize default argv, argc, and argv_size.
00738      */
00739     argv_size = 10;
00740     argc = 0;
00741     f_argv = ckd_calloc(argv_size, sizeof(char *));
00742     /* Silently make room for \0 */
00743     str = ckd_calloc(arg_max_length + 1, sizeof(char));
00744     quoting = 0;
00745 
00746     do {
00747         /* Handle arguments that are commented out */
00748         if (len == 0 && argc % 2 == 0) {
00749             while (ch == '#') {
00750                 /* Skip everything until newline */
00751                 for (ch = fgetc(file); ch != EOF && ch != '\n'; ch = fgetc(file)) ;
00752                 /* Skip to the next interesting character */
00753                 for (ch = fgetc(file); ch != EOF && strchr(separator, ch); ch = fgetc(file)) ;
00754             }
00755 
00756             /* Check if we are at the last line (without anything interesting in it) */
00757             if (ch == EOF)
00758                 break;
00759         }
00760 
00761         /* Handle quoted arguments */
00762         if (ch == '"' || ch == '\'') {
00763             if (quoting == ch) /* End a quoted section with the same type */
00764                 quoting = 0;
00765             else if (quoting) {
00766                 E_ERROR("Nesting quotations is not supported!\n");
00767                 rv = 1;
00768                 break;
00769             }
00770             else
00771                 quoting = ch; /* Start a quoted section */
00772         }
00773         else if (ch == EOF || (!quoting && strchr(separator, ch))) {
00774             /* Reallocate argv so it is big enough to contain all the arguments */
00775             if (argc >= argv_size) {
00776                 char **tmp_argv;
00777                 if (!(tmp_argv =
00778                        ckd_realloc(f_argv, argv_size * 2 * sizeof(char *)))) {
00779                     rv = 1;
00780                     break;
00781                 }
00782                 f_argv = tmp_argv;
00783                 argv_size *= 2;
00784             }
00785             /* Add the string to the list of arguments */
00786             f_argv[argc] = ckd_salloc(str);
00787             len = 0;
00788             str[0] = '\0';
00789             argc++;
00790 
00791             if (quoting)
00792                 E_WARN("Unclosed quotation, having EOF close it...\n");
00793 
00794             /* Skip to the next interesting character */
00795             for (; ch != EOF && strchr(separator, ch); ch = fgetc(file)) ;
00796 
00797             if (ch == EOF)
00798                 break;
00799 
00800             /* We already have the next character */
00801             continue;
00802         }
00803         else {
00804             if (len >= arg_max_length) {
00805                 /* Make room for more chars (including the \0 !) */
00806                 char *tmp_str = str;
00807                 if ((tmp_str = ckd_realloc(str, (1 + arg_max_length * 2) * sizeof(char))) == NULL) {
00808                     rv = 1;
00809                     break;
00810                 }
00811                 str = tmp_str;
00812                 arg_max_length *= 2;
00813             }
00814             /* Add the char to the argument string */
00815             str[len++] = ch;
00816             /* Always null terminate */
00817             str[len] = '\0';
00818         }
00819 
00820         ch = fgetc(file);
00821     } while (1);
00822 
00823     fclose(file);
00824 
00825     ckd_free(str);
00826 
00827     if (rv) {
00828         for (ch = 0; ch < argc; ++ch)
00829             ckd_free(f_argv[ch]);
00830         ckd_free(f_argv);
00831         return NULL;
00832     }
00833 
00834     return parse_options(inout_cmdln, defn, argc, f_argv, strict);
00835 }
00836 
00837 int
00838 cmd_ln_parse_file(const arg_t * defn, const char *filename, int32 strict)
00839 {
00840     cmd_ln_t *cmdln;
00841 
00842     cmdln = cmd_ln_parse_file_r(global_cmdln, defn, filename, strict);
00843     if (cmdln == NULL) {
00844         return -1;
00845     }
00846     /* Initialize global_cmdln if not present. */
00847     if (global_cmdln == NULL) {
00848         global_cmdln = cmdln;
00849     }
00850     return 0;
00851 }
00852 
00853 void
00854 cmd_ln_print_help_r(cmd_ln_t *cmdln, FILE * fp, arg_t const* defn)
00855 {
00856     if (defn == NULL)
00857         return;
00858     fprintf(fp, "Arguments list definition:\n");
00859     arg_dump_r(cmdln, fp, defn, 1);
00860     fflush(fp);
00861 }
00862 
00863 int
00864 cmd_ln_exists_r(cmd_ln_t *cmdln, const char *name)
00865 {
00866     void *val;
00867     if (cmdln == NULL)
00868         return FALSE;
00869     return (hash_table_lookup(cmdln->ht, name, &val) == 0);
00870 }
00871 
00872 anytype_t *
00873 cmd_ln_access_r(cmd_ln_t *cmdln, const char *name)
00874 {
00875     void *val;
00876     if (hash_table_lookup(cmdln->ht, name, &val) < 0) {
00877         E_ERROR("Unknown argument: %s\n", name);
00878         return NULL;
00879     }
00880     return (anytype_t *)val;
00881 }
00882 
00883 char const *
00884 cmd_ln_str_r(cmd_ln_t *cmdln, char const *name)
00885 {
00886     anytype_t *val;
00887     val = cmd_ln_access_r(cmdln, name);
00888     if (val == NULL)
00889         return NULL;
00890     return (char const *)val->ptr;
00891 }
00892 
00893 long
00894 cmd_ln_int_r(cmd_ln_t *cmdln, char const *name)
00895 {
00896     anytype_t *val;
00897     val = cmd_ln_access_r(cmdln, name);
00898     if (val == NULL)
00899         return 0L;
00900     return val->i;
00901 }
00902 
00903 double
00904 cmd_ln_float_r(cmd_ln_t *cmdln, char const *name)
00905 {
00906     anytype_t *val;
00907     val = cmd_ln_access_r(cmdln, name);
00908     if (val == NULL)
00909         return 0.0;
00910     return val->fl;
00911 }
00912 
00913 void
00914 cmd_ln_set_str_r(cmd_ln_t *cmdln, char const *name, char const *str)
00915 {
00916     anytype_t *val;
00917     val = cmd_ln_access_r(cmdln, name);
00918     if (val == NULL) {
00919         E_ERROR("Unknown argument: %s\n", name);
00920         return;
00921     }
00922     ckd_free(val->ptr);
00923     if (str == NULL)
00924         val->ptr = NULL;
00925     else
00926         val->ptr = ckd_salloc(str);
00927 }
00928 
00929 void
00930 cmd_ln_set_int_r(cmd_ln_t *cmdln, char const *name, long iv)
00931 {
00932     anytype_t *val;
00933     val = cmd_ln_access_r(cmdln, name);
00934     if (val == NULL) {
00935         E_ERROR("Unknown argument: %s\n", name);
00936         return;
00937     }
00938     val->i = iv;
00939 }
00940 
00941 void
00942 cmd_ln_set_float_r(cmd_ln_t *cmdln, char const *name, double fv)
00943 {
00944     anytype_t *val;
00945     val = cmd_ln_access_r(cmdln, name);
00946     if (val == NULL) {
00947         E_ERROR("Unknown argument: %s\n", name);
00948         return;
00949     }
00950     val->fl = fv;
00951 }
00952 
00953 cmd_ln_t *
00954 cmd_ln_retain(cmd_ln_t *cmdln)
00955 {
00956     ++cmdln->refcount;
00957     return cmdln;
00958 }
00959 
00960 int
00961 cmd_ln_free_r(cmd_ln_t *cmdln)
00962 {
00963     if (cmdln == NULL)
00964         return 0;
00965     if (--cmdln->refcount > 0)
00966         return cmdln->refcount;
00967 
00968     if (cmdln->ht) {
00969         glist_t entries;
00970         gnode_t *gn;
00971         int32 n;
00972 
00973         entries = hash_table_tolist(cmdln->ht, &n);
00974         for (gn = entries; gn; gn = gnode_next(gn)) {
00975             hash_entry_t *e = gnode_ptr(gn);
00976             cmd_ln_val_free((cmd_ln_val_t *)e->val);
00977         }
00978         glist_free(entries);
00979         hash_table_free(cmdln->ht);
00980         cmdln->ht = NULL;
00981     }
00982 
00983     if (cmdln->f_argv) {
00984         int32 i;
00985         for (i = 0; i < cmdln->f_argc; ++i) {
00986             ckd_free(cmdln->f_argv[i]);
00987         }
00988         ckd_free(cmdln->f_argv);
00989         cmdln->f_argv = NULL;
00990         cmdln->f_argc = 0;
00991     }
00992     ckd_free(cmdln);
00993     return 0;
00994 }
00995 
00996 void
00997 cmd_ln_free(void)
00998 {
00999     cmd_ln_free_r(global_cmdln);
01000     global_cmdln = NULL;
01001 }

Generated on Mon Jan 24 21:36:19 2011 for SphinxBase by  doxygen 1.4.7