1 /*   ncbiargs.c
2 * ===========================================================================
3 *
4 *                            PUBLIC DOMAIN NOTICE
5 *               National Center for Biotechnology Information
6 *
7 *  This software/database is a "United States Government Work" under the
8 *  terms of the United States Copyright Act.  It was written as part of
9 *  the author's official duties as a United States Government employee and
10 *  thus cannot be copyrighted.  This software/database is freely available
11 *  to the public for use. The National Library of Medicine and the U.S.
12 *  Government have not placed any restriction on its use or reproduction.
13 *
14 *  Although all reasonable efforts have been taken to ensure the accuracy
15 *  and reliability of the software and data, the NLM and the U.S.
16 *  Government do not and cannot warrant the performance or results that
17 *  may be obtained by using this software or data. The NLM and the U.S.
18 *  Government disclaim all warranties, express or implied, including
19 *  warranties of performance, merchantability or fitness for any particular
20 *  purpose.
21 *
22 *  Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * File Name:  ncbiargs.c
27 *
28 * Author:  Ostell, Schuler, Vakatov
29 *
30 * Version Creation Date:   07/15/91
31 *
32 * File Description:
33 *     GetArgs() for console applications
34 *
35 * Modifications:
36 * --------------------------------------------------------------------------
37 * $Log: ncbiargs.c,v $
38 * Revision 6.12  2009/08/17 19:42:08  lavr
39 * Formatting
40 *
41 * Revision 6.11  2009/08/14 18:01:09  lavr
42 * Use {Get|Set}ProgramName()
43 *
44 * Revision 6.10  2006/10/19 14:57:03  lavr
45 * Fix repetitive arg error message to show no argument value since it's empty
46 *
47 * Revision 6.9  2006/10/18 19:15:43  lavr
48 * Allow repetitive (boolean) flags to increment arg's intvalue [spec.cased]
49 *
50 * Revision 6.8  2004/04/01 13:43:06  lavr
51 * Spell "occurred", "occurrence", and "occurring"
52 *
53 * Revision 6.7  2002/11/06 21:22:32  ucko
54 * Accept "--help" as a synonym for the less intuitive "-"
55 *
56 * Revision 6.6  2001/07/03 20:03:11  juran
57 * Don't corruptly parse argv in Mac OS.
58 *
59 * Revision 6.5  2000/06/15 20:51:41  vakatov
60 * Use "const" in Args code
61 *
62 * Revision 6.4  1999/03/11 21:12:23  vakatov
63 * Get in-sync the "printf"/"scanf"'s format and args in some places
64 *
65 * Revision 6.3  1998/01/21 20:55:43  vakatov
66 * Nlm_GetArgs():  fixed memory leak(in the override of default str value)
67 *
68 * Revision 6.2  1997/10/29 02:43:14  vakatov
69 * Type castings to pass through the C++ compiler
70 *
71 * Revision 6.1  1997/10/27 22:00:43  vakatov
72 * Check for the Bool, Int and Float arg. value validity;  reset all
73 * arguments if an error occurred
74 *
75 * Revision 5.1  1997/07/22 19:06:56  vakatov
76 * Initial revision: merged GetArgs()'s from former "ncbiargs.msw" and
77 * "ncbimain.[msw,unx,mac,vms]"
78 *
79 * ==========================================================================
80 */
81 
82 #include <ncbi.h>
83 
84 /*****************************************************************************
85 *
86 *   Nlm_GetArgs(ap)
87 *   	returns user startup arguments
88 *
89 *****************************************************************************/
90 
Nlm_GetArgs(const char * progname,Nlm_Int2 numargs,Nlm_ArgPtr ap)91 NLM_EXTERN Nlm_Boolean Nlm_GetArgs(const char* progname,
92                                    Nlm_Int2 numargs, Nlm_ArgPtr ap)
93 {
94   static const char* types[] = {
95     NULL,
96     "T/F",
97     "Integer",
98     "Real",
99     "String",
100     "File In",
101     "File Out",
102     "Data In",
103     "Data Out"
104   };
105 
106   Nlm_Boolean  okay, all_default = TRUE, range;
107   Nlm_Int2     i, j;
108   Nlm_ArgPtr   curarg;
109   Nlm_Boolean* resolved;
110   Nlm_Int4     xx_argc = Nlm_GetArgc();
111   char**       xx_argv = Nlm_GetArgv();
112 
113   if (StringHasNoText(GetProgramName()))
114     SetProgramName(progname);
115 
116   if (!ap  ||  numargs <= 0)
117     return FALSE;
118 
119   resolved = (Nlm_Boolean*) Nlm_MemNew(numargs * sizeof(Nlm_Boolean));
120   if ( !resolved )
121     return FALSE;
122 
123   /* reset all args */
124   curarg = ap;
125   for (i = 0;  i < numargs;  i++, curarg++) {
126     curarg->intvalue   = 0;
127     curarg->floatvalue = 0.0;
128     curarg->strvalue   = NULL;
129   }
130 
131   /* set defaults */
132   curarg = ap;
133   for (i = 0;  i < numargs;  i++, curarg++)
134     {
135       if ((curarg->type < ARG_BOOLEAN) ||
136           (curarg->type > ARG_DATA_OUT))
137         {
138           ErrPostEx(SEV_ERROR, 0,0, "Invalid Arg->type in %s", curarg->prompt);
139           Nlm_MemFree( resolved );
140           Nlm_FreeArgs(i, ap);
141           return FALSE;
142         }
143       curarg->intvalue   = 0;
144       curarg->floatvalue = 0.0;
145       curarg->strvalue   = NULL;
146       if (curarg->defaultvalue)
147         {
148           resolved[i] = TRUE;
149           switch (curarg->type)
150             {
151             case ARG_BOOLEAN:
152               if (TO_UPPER(*curarg->defaultvalue) == 'T')
153                 curarg->intvalue = 1;
154               else
155                 curarg->intvalue = 0;
156               break;
157             case ARG_INT:
158               curarg->intvalue = atol(curarg->defaultvalue);
159               break;
160             case ARG_FLOAT:
161               curarg->floatvalue = atof(curarg->defaultvalue);
162               break;
163             case ARG_STRING:
164             case ARG_FILE_IN:
165             case ARG_FILE_OUT:
166             case ARG_DATA_IN:
167             case ARG_DATA_OUT:
168               curarg->strvalue = StringSave(curarg->defaultvalue);
169               break;
170             }
171         }
172       else if (curarg->optional == FALSE)
173         all_default = FALSE;    /* must have some arguments */
174     }
175 
176   /**** show usage if no args on command line ***/
177   if (xx_argc == 1  &&  all_default)   /* no args ok */
178     {
179       Nlm_MemFree( resolved );
180       return TRUE;
181     }
182 
183   if (xx_argc == 1  ||  *(xx_argv[1]+1) == '\0'
184       ||  !strcmp(xx_argv[1], "--help"))
185     {
186       printf("\n%s   arguments:\n\n", progname);
187       curarg = ap;
188 
189       for (i = 0, j = 0;  i < numargs;  i++, j++, curarg++)
190         {
191           printf("  -%c  %s [%s]", curarg->tag, curarg->prompt,
192                  types[curarg->type]);
193           if (curarg->optional)
194             printf("  Optional");
195           printf("\n");
196           if (curarg->defaultvalue)
197             printf("    default = %s\n", curarg->defaultvalue);
198           if (curarg->from  ||  curarg->to)
199             {
200               if (curarg->type == ARG_DATA_IN  ||
201                   curarg->type == ARG_DATA_OUT)
202                 printf("    Data Type = %s\n", curarg->from);
203               else if (curarg->type == ARG_BOOLEAN  ||
204                        curarg->type == ARG_INT      ||
205                        curarg->type == ARG_FLOAT)
206                 {
207                   printf("    range from %s to %s\n",
208                          (curarg->from ? curarg->from: "?"),
209                          (curarg->to   ? curarg->to  : "?"));
210                 }
211             }
212         }
213 
214       printf("\n");
215       fflush (stdout);
216       Nlm_MemFree( resolved );
217       Nlm_FreeArgs(numargs, ap);
218 #ifdef COMP_MPW
219       Message(MSG_OK, "To exit: ");
220 #endif
221       return FALSE;
222     }
223 
224 
225   /* Parse the arguments */
226   for (i = 1; i < xx_argc; i++)
227     {
228       const char* arg = xx_argv[i];
229       if (*arg != '-')
230         {
231           ErrPostEx(SEV_ERROR, 0, 0, "Arguments must start with '-' (the offending argument #%d was: '%s')", (int)i, arg);
232           Nlm_MemFree( resolved );
233           Nlm_FreeArgs(numargs, ap);
234           return FALSE;
235         }
236       arg++;
237       curarg = ap;
238       for (j = 0; j < numargs; j++, curarg++)
239         {
240           if (*arg == curarg->tag)
241             break;
242         }
243       if (j == numargs)
244         {
245           ErrPostEx(SEV_ERROR, 0, 0, "Invalid argument: %s", xx_argv[i]);
246           Nlm_MemFree( resolved );
247           Nlm_FreeArgs(numargs, ap);
248           return FALSE;
249         }
250       arg++;
251 
252       if (*arg == '\0')
253         {
254           Nlm_Boolean ok = FALSE;
255           if ((i + 1) < xx_argc)  /* argument comes after space */
256             {
257               if (*xx_argv[i + 1] == '-')
258                 {
259                   char tmp = *(xx_argv[i+1]+1);
260                   if ((curarg->type == ARG_INT  ||  curarg->type == ARG_FLOAT)
261                       &&  (tmp == '.'  ||  IS_DIGIT(tmp)))
262                     ok = TRUE;
263                 }
264               else
265                 ok = TRUE;
266 
267               if ( ok ) {
268                 i++;
269                 arg = xx_argv[i];
270               }
271             }
272 
273           if (!ok  &&  curarg->type != ARG_BOOLEAN)
274             {
275               ErrPostEx(SEV_ERROR, 0, 0,
276                         "No argument value given for %s", curarg->prompt);
277               Nlm_MemFree( resolved );
278               Nlm_FreeArgs(numargs, ap);
279               return FALSE;
280             }
281         }
282 
283       resolved[j] = TRUE;
284       switch (curarg->type)
285         {
286         case ARG_BOOLEAN:
287           if (TO_UPPER(*arg) == 'T')
288             curarg->intvalue = 1;
289           else if (TO_UPPER(*arg) == 'F')
290             curarg->intvalue = 0;
291           else if (*arg == '\0')
292             {
293               long idef, ifrom, ito;
294               if (curarg->from  &&  curarg->to  &&
295                   sscanf(curarg->defaultvalue, "%ld", &idef)  > 0  &&
296                   sscanf(curarg->from,         "%ld", &ifrom) > 0  &&
297                   sscanf(curarg->to,           "%ld", &ito)   > 0  &&
298                   idef == 0  &&  ifrom == 0  &&  ito > 1)
299                 {
300                   if (curarg->intvalue >= ito)
301                     {
302                       ErrPostEx(SEV_ERROR, 0, 0,
303                                 "%s allowed no more than %s times",
304                                 curarg->prompt, curarg->to);
305                       Nlm_MemFree( resolved );
306                       Nlm_FreeArgs(numargs, ap);
307                       return FALSE;
308                     }
309                   else
310                     curarg->intvalue++;
311                 }
312               else
313                 curarg->intvalue = 1;
314             }
315           else
316             {
317               ErrPostEx(SEV_ERROR, 0, 0,
318                         "%s [%s] must be one of {'T', 't', 'F', 'f'}"
319                         " or omitted", curarg->prompt, arg);
320               Nlm_MemFree( resolved );
321               Nlm_FreeArgs(numargs, ap);
322               return FALSE;
323             }
324           break;
325 
326         case ARG_INT: {
327           long val;
328           range = TRUE;
329           if (sscanf(arg, "%ld", &val) <= 0)
330             range = FALSE;
331           curarg->intvalue = val;
332           if (range && curarg->from && curarg->intvalue < atol(curarg->from))
333             {
334                 range = FALSE;
335             }
336           if (range && curarg->to && curarg->intvalue > atol(curarg->to))
337             {
338                 range = FALSE;
339             }
340           if ( !range )
341             {
342               ErrPostEx(SEV_ERROR, 0, 0,
343                         "%s [%s] is bad or out of range [%s to %s]",
344                         curarg->prompt, arg,
345                         curarg->from ? curarg->from : "?",
346                         curarg->to   ? curarg->to   : "?");
347               Nlm_MemFree( resolved );
348               Nlm_FreeArgs(numargs, ap);
349               return FALSE;
350             }
351           break;
352         }
353 
354         case ARG_FLOAT: {
355           double val;
356           range = TRUE;
357           if (sscanf(arg, "%lf", &val) <= 0)
358             range = FALSE;
359           curarg->floatvalue = val;
360           if (range && curarg->from && curarg->floatvalue < atof(curarg->from))
361             {
362                 range = FALSE;
363             }
364           if (range && curarg->to && curarg->floatvalue > atof(curarg->to))
365             {
366                 range = FALSE;
367             }
368           if ( !range )
369             {
370               ErrPostEx(SEV_ERROR, 0, 0,
371                         "%s [%s] is bad or out of range [%s to %s]",
372                         curarg->prompt, arg,
373                         curarg->from ? curarg->from : "?",
374                         curarg->to   ? curarg->to   : "?");
375               Nlm_MemFree( resolved );
376               Nlm_FreeArgs(numargs, ap);
377               return FALSE;
378             }
379           break;
380         }
381 
382         case ARG_STRING:
383         case ARG_FILE_IN:
384         case ARG_FILE_OUT:
385         case ARG_DATA_IN:
386         case ARG_DATA_OUT:
387           if ( curarg->strvalue )
388             MemFree(curarg->strvalue);
389           curarg->strvalue = StringSave(arg);
390           break;
391         }    /*** end switch ****/
392     }
393 
394   okay = TRUE;
395   curarg = ap;
396   for (i = 0;  i < numargs;  i++, curarg++)
397     {
398       if (!curarg->optional  &&  !resolved[i])
399         {
400           ErrPostEx(SEV_ERROR, 0, 0,
401                     "%s was not given an argument", curarg->prompt);
402           okay = FALSE;
403         }
404     }
405 
406   Nlm_MemFree( resolved );
407   if ( !okay )
408     Nlm_FreeArgs(numargs, ap);
409 
410   return okay;
411 }
412 
413 
Nlm_GetArgsSilent(const char * progname,Nlm_Int2 numargs,Nlm_ArgPtr ap)414 NLM_EXTERN Nlm_Boolean Nlm_GetArgsSilent(const char* progname,
415                                          Nlm_Int2 numargs, Nlm_ArgPtr ap)
416 {
417   return Nlm_GetArgs(progname, numargs, ap);
418 }
419 
420