1 /*  =========================================================================
2     zargs - Platform independent command line argument parsing helpers
3 
4     Copyright (c) the Contributors as noted in the AUTHORS file.
5     This file is part of CZMQ, the high-level C binding for 0MQ:
6     http://czmq.zeromq.org.
7 
8     This Source Code Form is subject to the terms of the Mozilla Public
9     License, v. 2.0. If a copy of the MPL was not distributed with this
10     file, You can obtain one at http://mozilla.org/MPL/2.0/.
11     =========================================================================
12 */
13 
14 /*
15 @header
16     zargs - Platform independent command line argument parsing helpers
17 
18     Platform independent command line argument parsing helpers
19 
20     There are two kind of elements provided by this class
21     foo --named-parameter --parameter with_value positional arguments -a gain-parameter
22     zargs keeps poision only for arguments, parameters are to be accessed like hash.
23 
24     It DOES:
25     * provide easy to use CLASS compatible API for accessing argv
26     * is platform independent
27     * provide getopt_long style -- argument, which delimits parameters from arguments
28     * makes parameters positon independent
29 
30     It does NOT
31     * change argv
32     * provide a "declarative" way to define command line interface
33 
34     In future it SHALL
35     * hide several formats of command line to one (-Idir, --include=dir,
36       --include dir are the same from API pov)
37 @discuss
38 @end
39 */
40 
41 #include "czmq_classes.h"
42 
43 //  Structure of our class
44 static char *ZARG_PARAM_EMPTY = "";
45 
46 struct _zargs_t {
47     char *progname;	 // program name aka argv [0]
48     zlist_t *arguments;  // positional arguments
49     zhash_t *parameters; // --named parameters
50 };
51 
52 
53 //  --------------------------------------------------------------------------
54 //  Create a new zargs
55 
56 zargs_t *
zargs_new(int argc,char ** argv)57 zargs_new (int argc, char **argv)
58 {
59     assert (argc > 0);
60     assert (argv);
61     zargs_t *self = (zargs_t *) zmalloc (sizeof (zargs_t));
62     assert (self);
63     //  Initialize class properties here
64     self->progname = argv [0];
65     assert (self->progname);
66     self->arguments = zlist_new ();
67     assert (self->arguments);
68     self->parameters = zhash_new ();
69     assert (self->parameters);
70 
71     if (argc == 1)
72         return self;
73 
74     int idx = 1;
75     bool params_only = false;
76     while (argv [idx]) {
77         if (params_only || argv [idx][0] != '-')
78             zlist_append (self->arguments, argv [idx]);
79         else {
80 	    if (streq (argv [idx], "--")) {
81 	        params_only = true;
82             idx ++;
83             continue;
84 	    }
85 	    else
86             if (argv [idx+1] && argv [idx+1][0] != '-') {
87                 zhash_insert (self->parameters, argv [idx], argv [idx+1]);
88                 idx ++;
89             }
90             else {
91                 zhash_insert (self->parameters, argv [idx], ZARG_PARAM_EMPTY);
92             }
93         }
94         idx ++;
95     }
96 
97     return self;
98 }
99 
100 //  --------------------------------------------------------------------------
101 //  Destroy the zargs
102 
103 void
zargs_destroy(zargs_t ** self_p)104 zargs_destroy (zargs_t **self_p)
105 {
106     assert (self_p);
107     if (*self_p) {
108         zargs_t *self = *self_p;
109         //  Free class properties here
110         //  Free object itself
111         zlist_destroy (&self->arguments);
112         zhash_destroy (&self->parameters);
113         free (self);
114         *self_p = NULL;
115     }
116 }
117 
118 //  --------------------------------------------------------------------------
119 //  Return the program name (argv[0])
120 
121 const char *
zargs_progname(zargs_t * self)122 zargs_progname (zargs_t *self) {
123     assert (self);
124     return self->progname;
125 }
126 
127 //  --------------------------------------------------------------------------
128 //  Return the number of command line arguments
129 
130 size_t
zargs_arguments(zargs_t * self)131 zargs_arguments (zargs_t *self) {
132     assert (self);
133     return zlist_size (self->arguments);
134 }
135 
136 //  --------------------------------------------------------------------------
137 //  Return first command line argument
138 
139 const char *
zargs_first(zargs_t * self)140 zargs_first (zargs_t *self) {
141     assert (self);
142     return (const char*) zlist_first (self->arguments);
143 }
144 
145 //  --------------------------------------------------------------------------
146 //  Return next command line argument
147 
148 const char *
zargs_next(zargs_t * self)149 zargs_next (zargs_t *self) {
150     assert (self);
151     return (const char*) zlist_next (self->arguments);
152 }
153 
154 //  --------------------------------------------------------------------------
155 //  Return first command line parameter value
156 
157 const char *
zargs_param_first(zargs_t * self)158 zargs_param_first (zargs_t *self) {
159     assert (self);
160     return (const char*) zhash_first (self->parameters);
161 }
162 
163 //  --------------------------------------------------------------------------
164 //  Return next command line parameter value
165 
166 const char *
zargs_param_next(zargs_t * self)167 zargs_param_next (zargs_t *self) {
168     assert (self);
169     return (const char*) zhash_next (self->parameters);
170 }
171 
172 //  --------------------------------------------------------------------------
173 //  Return current command line parameter name
174 
175 const char *
zargs_param_name(zargs_t * self)176 zargs_param_name (zargs_t *self) {
177     assert (self);
178     return (const char*) zhash_cursor (self->parameters);
179 }
180 
181 //  --------------------------------------------------------------------------
182 //	Return value of named parameter, NULL if no given parameter has
183 //	been specified, or special value for wich zargs_param_empty ()
184 //	returns true.
185 
186 const char *
zargs_param_lookup(zargs_t * self,const char * name)187 zargs_param_lookup (zargs_t *self, const char *name) {
188     assert (self);
189     assert (name);
190     const char *ret = NULL;
191     ret = (const char*) zhash_lookup (self->parameters, name);
192     return ret;
193 }
194 
195 //  --------------------------------------------------------------------------
196 //	Return value of named parameter(s), NULL if no given parameter has
197 //	been specified, or special value for wich zargs_param_empty ()
198 //	returns true.
199 
200 const char *
zargs_param_lookupx(zargs_t * self,const char * name,...)201 zargs_param_lookupx (zargs_t *self, const char *name, ...) {
202     assert (self);
203     const char *ret = NULL;
204     va_list args;
205     va_start (args, name);
206     while (name) {
207         ret = zargs_param_lookup (self, name);
208 	    if (ret)
209 	        break;
210 	    name = va_arg (args, const char *);
211     }
212     va_end (args);
213     return ret;
214 }
215 
216 //  --------------------------------------------------------------------------
217 
218 bool
zargs_has_help(zargs_t * self)219 zargs_has_help (zargs_t *self) {
220     return zargs_param_lookupx (self, "--help", "-h", NULL) != NULL;
221 }
222 
223 //  --------------------------------------------------------------------------
224 //  check if argument had a value or not
225 
226 bool
zargs_param_empty(const char * arg)227 zargs_param_empty (const char* arg) {
228     return arg && arg == ZARG_PARAM_EMPTY;
229 }
230 
231 //  --------------------------------------------------------------------------
232 //  Print the zargs instance
233 
234 void
zargs_print(zargs_t * self)235 zargs_print (zargs_t *self) {
236     assert (self);
237     fprintf (stderr, "%s ", self->progname);
238     for (const char *pvalue = zargs_param_first (self);
239                     pvalue != NULL;
240                     pvalue = zargs_param_next (self)) {
241         const char *pname = zargs_param_name (self);
242         if (pvalue == ZARG_PARAM_EMPTY)
243             fprintf (stderr, "%s : None ", pname);
244         else
245             fprintf (stderr, "%s : %s ", pname, pvalue);
246 	fprintf (stderr, ", ");
247     }
248     for (const char *arg = zargs_first (self);
249                      arg != NULL;
250                      arg = zargs_next (self)) {
251         fprintf (stderr, "%s ", arg);
252     }
253     fputs ("", stderr);
254 }
255 //  --------------------------------------------------------------------------
256 //  Self test of this class
257 
258 void
zargs_test(bool verbose)259 zargs_test (bool verbose)
260 {
261     zsys_init ();
262     printf (" * zargs: ");
263 
264     //  @selftest
265     //  Simple create/destroy test
266 
267     char *argv1[] = {"progname", "--named1", "-n1", "val1", "positional1", "--with", "value", "--with2=value2", "-W3value3", "--", "--thisis", "considered", "positional", NULL};
268 
269     zargs_t *self = zargs_new (13, argv1);
270     assert (self);
271 
272     assert (streq (zargs_progname (self), "progname"));
273     assert (streq (zargs_first (self), "positional1"));
274     assert (streq (zargs_next (self), "--thisis"));
275     assert (streq (zargs_next (self), "considered"));
276     assert (streq (zargs_next (self), "positional"));
277     assert (!zargs_next (self));
278 
279     assert (zargs_param_empty (zargs_param_lookup (self, "--named1")));
280     assert (!zargs_param_empty (zargs_param_lookup (self, "-n1")));
281     assert (streq (zargs_param_lookupx (self, "--not at all", "-n1", NULL), "val1"));
282     // TODO: this does not look like an easy hack w/o allocating extra memory
283     //       ???
284     //assert (streq (zargs_param_lookup (self, "--with", NULL), "value2"));
285 
286     zargs_destroy (&self);
287     //  @end
288     printf ("OK\n");
289 }
290