doxygen: add newline after \file
[blender.git] / source / blender / blenlib / intern / BLI_args.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup bli
22  * \brief A general argument parsing module
23  */
24
25 #include <ctype.h> /* for tolower */
26 #include <string.h>
27
28 #include "MEM_guardedalloc.h"
29
30 #include "BLI_utildefines.h"
31 #include "BLI_listbase.h"
32 #include "BLI_string.h"
33 #include "BLI_args.h"
34 #include "BLI_ghash.h"
35
36 static char NO_DOCS[] = "NO DOCUMENTATION SPECIFIED";
37
38 struct bArgDoc;
39 typedef struct bArgDoc {
40         struct bArgDoc *next, *prev;
41         const char *short_arg;
42         const char *long_arg;
43         const char *documentation;
44         bool done;
45 } bArgDoc;
46
47 typedef struct bAKey {
48         const char *arg;
49         uintptr_t pass; /* cast easier */
50         int case_str; /* case specific or not */
51 } bAKey;
52
53 typedef struct bArgument {
54         bAKey *key;
55         BA_ArgCallback func;
56         void *data;
57         bArgDoc *doc;
58 } bArgument;
59
60 struct bArgs {
61         ListBase docs;
62         GHash  *items;
63         int argc;
64         const char  **argv;
65         int *passes;
66 };
67
68 static uint case_strhash(const void *ptr)
69 {
70         const char *s = ptr;
71         uint i = 0;
72         unsigned char c;
73
74         while ( (c = tolower(*s++)) )
75                 i = i * 37 + c;
76
77         return i;
78 }
79
80 static uint keyhash(const void *ptr)
81 {
82         const bAKey *k = ptr;
83         return case_strhash(k->arg);  /* ^ BLI_ghashutil_inthash((void *)k->pass); */
84 }
85
86 static bool keycmp(const void *a, const void *b)
87 {
88         const bAKey *ka = a;
89         const bAKey *kb = b;
90         if (ka->pass == kb->pass || ka->pass == -1 || kb->pass == -1) { /* -1 is wildcard for pass */
91                 if (ka->case_str == 1 || kb->case_str == 1) {
92                         return (BLI_strcasecmp(ka->arg, kb->arg) != 0);
93                 }
94                 else {
95                         return (!STREQ(ka->arg, kb->arg));
96                 }
97         }
98         else {
99                 return BLI_ghashutil_intcmp((const void *)ka->pass, (const void *)kb->pass);
100         }
101 }
102
103 static bArgument *lookUp(struct bArgs *ba, const char *arg, int pass, int case_str)
104 {
105         bAKey key;
106
107         key.case_str = case_str;
108         key.pass = pass;
109         key.arg = arg;
110
111         return BLI_ghash_lookup(ba->items, &key);
112 }
113
114 bArgs *BLI_argsInit(int argc, const char **argv)
115 {
116         bArgs *ba = MEM_callocN(sizeof(bArgs), "bArgs");
117         ba->passes = MEM_callocN(sizeof(int) * argc, "bArgs passes");
118         ba->items = BLI_ghash_new(keyhash, keycmp, "bArgs passes gh");
119         BLI_listbase_clear(&ba->docs);
120         ba->argc = argc;
121         ba->argv = argv;
122
123         return ba;
124 }
125
126 void BLI_argsFree(struct bArgs *ba)
127 {
128         BLI_ghash_free(ba->items, MEM_freeN, MEM_freeN);
129         MEM_freeN(ba->passes);
130         BLI_freelistN(&ba->docs);
131         MEM_freeN(ba);
132 }
133
134 void BLI_argsPrint(struct bArgs *ba)
135 {
136         int i;
137         for (i = 0; i < ba->argc; i++) {
138                 printf("argv[%d] = %s\n", i, ba->argv[i]);
139         }
140 }
141
142 const char **BLI_argsArgv(struct bArgs *ba)
143 {
144         return ba->argv;
145 }
146
147 static bArgDoc *internalDocs(struct bArgs *ba, const char *short_arg, const char *long_arg, const char *doc)
148 {
149         bArgDoc *d;
150
151         d = MEM_callocN(sizeof(bArgDoc), "bArgDoc");
152
153         if (doc == NULL)
154                 doc = NO_DOCS;
155
156         d->short_arg = short_arg;
157         d->long_arg = long_arg;
158         d->documentation = doc;
159
160         BLI_addtail(&ba->docs, d);
161
162         return d;
163 }
164
165 static void internalAdd(struct bArgs *ba, const char *arg, int pass,
166                         int case_str, BA_ArgCallback cb, void *data, bArgDoc *d)
167 {
168         bArgument *a;
169         bAKey *key;
170
171         a = lookUp(ba, arg, pass, case_str);
172
173         if (a) {
174                 printf("WARNING: conflicting argument\n");
175                 printf("\ttrying to add '%s' on pass %i, %scase sensitive\n",
176                        arg, pass, case_str == 1 ? "not " : "");
177                 printf("\tconflict with '%s' on pass %i, %scase sensitive\n\n",
178                        a->key->arg, (int)a->key->pass, a->key->case_str == 1 ? "not " : "");
179         }
180
181         a = MEM_callocN(sizeof(bArgument), "bArgument");
182         key = MEM_callocN(sizeof(bAKey), "bAKey");
183
184         key->arg = arg;
185         key->pass = pass;
186         key->case_str = case_str;
187
188         a->key = key;
189         a->func = cb;
190         a->data = data;
191         a->doc = d;
192
193         BLI_ghash_insert(ba->items, key, a);
194 }
195
196 void BLI_argsAddCase(struct bArgs *ba, int pass,
197                      const char *short_arg, int short_case,
198                      const char *long_arg, int long_case,
199                      const char *doc, BA_ArgCallback cb, void *data)
200 {
201         bArgDoc *d = internalDocs(ba, short_arg, long_arg, doc);
202
203         if (short_arg)
204                 internalAdd(ba, short_arg, pass, short_case, cb, data, d);
205
206         if (long_arg)
207                 internalAdd(ba, long_arg, pass, long_case, cb, data, d);
208 }
209
210 void BLI_argsAdd(struct bArgs *ba, int pass,
211                  const char *short_arg, const char *long_arg,
212                  const char *doc, BA_ArgCallback cb, void *data)
213 {
214         BLI_argsAddCase(ba, pass, short_arg, 0, long_arg, 0, doc, cb, data);
215 }
216
217 static void internalDocPrint(bArgDoc *d)
218 {
219         if (d->short_arg && d->long_arg)
220                 printf("%s or %s", d->short_arg, d->long_arg);
221         else if (d->short_arg)
222                 printf("%s", d->short_arg);
223         else if (d->long_arg)
224                 printf("%s", d->long_arg);
225
226         printf(" %s\n\n", d->documentation);
227 }
228
229 void BLI_argsPrintArgDoc(struct bArgs *ba, const char *arg)
230 {
231         bArgument *a = lookUp(ba, arg, -1, -1);
232
233         if (a) {
234                 bArgDoc *d = a->doc;
235
236                 internalDocPrint(d);
237
238                 d->done = true;
239         }
240 }
241
242 void BLI_argsPrintOtherDoc(struct bArgs *ba)
243 {
244         bArgDoc *d;
245
246         for (d = ba->docs.first; d; d = d->next) {
247                 if (d->done == 0) {
248                         internalDocPrint(d);
249                 }
250         }
251 }
252
253 void BLI_argsParse(struct bArgs *ba, int pass, BA_ArgCallback default_cb, void *default_data)
254 {
255         int i = 0;
256
257         for (i = 1; i < ba->argc; i++) {  /* skip argv[0] */
258                 if (ba->passes[i] == 0) {
259                         /* -1 signal what side of the comparison it is */
260                         bArgument *a = lookUp(ba, ba->argv[i], pass, -1);
261                         BA_ArgCallback func = NULL;
262                         void *data = NULL;
263
264                         if (a) {
265                                 func = a->func;
266                                 data = a->data;
267                         }
268                         else {
269                                 func = default_cb;
270                                 data = default_data;
271                         }
272
273                         if (func) {
274                                 int retval = func(ba->argc - i, ba->argv + i, data);
275
276                                 if (retval >= 0) {
277                                         int j;
278
279                                         /* use extra arguments */
280                                         for (j = 0; j <= retval; j++) {
281                                                 ba->passes[i + j] = pass;
282                                         }
283                                         i += retval;
284                                 }
285                                 else if (retval == -1) {
286                                         if (a) {
287                                                 if (a->key->pass != -1)
288                                                         ba->passes[i] = pass;
289                                         }
290                                         break;
291                                 }
292                         }
293                 }
294         }
295 }