1 #include "cado.h" // IWYU pragma: keep
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>              /* isdigit isspace */
5 #include <limits.h>             /* INT_MIN INT_MAX */
6 #include <errno.h>
7 #include <stdarg.h>
8 #include <inttypes.h>
9 #include <pthread.h>
10 #include <stdio.h>
11 #include <gmp.h>
12 
13 #include "params.h"
14 #include "macros.h"
15 #include "version_info.h"
16 #include "verbose.h"
17 #include "portability.h" // strdup // IWYU pragma: keep
18 
19 typedef int (*sortfunc_t) (const void *, const void *);
20 
21 static pthread_mutex_t mutex[1] = {PTHREAD_MUTEX_INITIALIZER};
22 
param_list_init(param_list_ptr pl)23 void param_list_init(param_list_ptr pl)
24 {
25     memset(pl, 0, sizeof(param_list));
26     pl->ndocs_alloc = 16;
27     pl->ndocs = 0;
28     pl->docs = (param_list_doc *) malloc(pl->ndocs_alloc *
29             sizeof(param_list_doc));
30     pl->usage_hdr = NULL;
31     pl->alloc = 16;
32     pl->p = (parameter *) malloc(pl->alloc * sizeof(parameter));
33     pl->consolidated = 1;
34     pl->size = 0;
35     pl->aliases = NULL;
36     pl->naliases = 0;
37     pl->naliases_alloc = 0;
38     pl->switches = NULL;
39     pl->nswitches = 0;
40     pl->nswitches_alloc = 0;
41     ASSERT_ALWAYS(pl->docs != NULL && pl->p != NULL);
42 }
43 
parameter_set(parameter dst,parameter src)44 static void parameter_set(parameter dst, parameter src)
45 {
46     memcpy(dst, src, sizeof(parameter));
47     dst->key = src->key ? strdup(src->key) : NULL;
48     dst->value = src->value ? strdup(src->value) : NULL;
49 }
50 
param_list_doc_set(param_list_doc dst,param_list_doc src)51 static void param_list_doc_set(param_list_doc dst, param_list_doc src)
52 {
53     dst->key = strdup(src->key);
54     dst->doc = strdup(src->doc);
55 }
56 
param_list_alias_set(param_list_alias dst,param_list_alias src)57 static void param_list_alias_set(param_list_alias dst, param_list_alias src)
58 {
59     dst->alias = strdup(src->alias);
60     dst->key = strdup(src->key);
61 }
param_list_switch_set(param_list_switch dst,param_list_switch src)62 static void param_list_switch_set(param_list_switch dst, param_list_switch src)
63 {
64     dst->switchname = strdup(src->switchname);
65     dst->ptr = src->ptr;
66 }
67 
68 #define COPY_LIST(__dst, __src, __data, __size, __alloc, __type) do {	\
69     __dst->__data = NULL;						\
70     __dst->__size = __src->__size;					\
71     __dst->__alloc = __src->__size;					\
72     if (__src->__size) { 						\
73         __dst->__data = malloc(__src->__size * sizeof(__type));		\
74         for(int __i = 0 ; __i < (int) __src->__size ; __i++) {		\
75             __type ## _set(__dst->__data[__i], __src->__data[__i]);	\
76         }								\
77     }									\
78 } while (0)
79 
param_list_set(param_list_ptr pl,param_list_srcptr pl0)80 void param_list_set(param_list_ptr pl, param_list_srcptr pl0)
81 {
82     memcpy(pl, pl0, sizeof(param_list));
83     if (pl->usage_hdr) pl->usage_hdr = strdup(pl->usage_hdr);
84     COPY_LIST(pl, pl0, p, size, alloc, parameter);
85     COPY_LIST(pl, pl0, aliases, naliases, naliases_alloc, param_list_alias);
86     COPY_LIST(pl, pl0, docs, ndocs, ndocs_alloc, param_list_doc);
87     COPY_LIST(pl, pl0, switches, nswitches, nswitches_alloc, param_list_switch);
88 }
89 
param_list_swap(param_list_ptr pl,param_list_ptr pl0)90 void param_list_swap(param_list_ptr pl, param_list_ptr pl0)
91 {
92     param_list x;
93     memcpy(x, pl, sizeof(param_list));
94     memcpy(pl, pl0, sizeof(param_list));
95     memcpy(pl0, x, sizeof(param_list));
96 }
97 
param_list_clear(param_list_ptr pl)98 void param_list_clear(param_list_ptr pl)
99 {
100     free(pl->usage_hdr);
101     for(int i = 0 ; i < pl->ndocs ; i++) {
102         free(pl->docs[i]->key);
103         free(pl->docs[i]->doc);
104     }
105     free(pl->docs);
106     for(unsigned int i = 0 ; i < pl->size ; i++) {
107         if (pl->p[i]->key) free(pl->p[i]->key);
108         free(pl->p[i]->value);
109     }
110     free(pl->p);
111     for(int i = 0 ; i < pl->naliases ; i++) {
112         if (pl->aliases[i]->alias) free(pl->aliases[i]->alias);
113     }
114     free(pl->aliases);
115     for(int i = 0 ; i < pl->nswitches ; i++) {
116         free(pl->switches[i]->switchname);
117     }
118     free(pl->switches);
119     memset(pl, 0, sizeof(pl[0]));
120 }
121 
param_list_usage_header(param_list_ptr pl,const char * hdr,...)122 void param_list_usage_header(param_list_ptr pl, const char * hdr, ...)
123 {
124     va_list ap;
125     va_start(ap, hdr);
126     int rc = vasprintf(&(pl->usage_hdr), hdr, ap);
127     ASSERT_ALWAYS(rc >= 0);
128     va_end(ap);
129 }
130 
131 
param_list_decl_usage(param_list_ptr pl,const char * key,const char * doc)132 void param_list_decl_usage(param_list_ptr pl, const char * key, const char * doc)
133 {
134     /* Note that duplicate calls to param_list_decl_usage for the same
135      * key will not trigger two distinct prints of the same
136      * documentation string. See the collapsing logic in
137      * param_list_print_usage.
138      */
139     if (pl->ndocs == pl->ndocs_alloc) {
140         pl->ndocs_alloc += 16;
141         pl->docs = (param_list_doc *) realloc(pl->docs,
142                 pl->ndocs_alloc * sizeof(param_list_doc));
143         ASSERT_ALWAYS(pl->docs != NULL);
144     }
145     int i = pl->ndocs;
146     pl->ndocs++;
147     pl->docs[i]->key = strdup(key);
148     pl->docs[i]->doc = strdup(doc);
149     ASSERT_ALWAYS(pl->docs[i]->key != NULL && pl->docs[i]->doc != NULL);
150     pl->use_doc = 1;
151 }
152 
153 /* compare two strings, intentionally collating - and _ (except at the
154  * beginning of the string) */
param_strcmp_collate(const char * a,const char * b)155 static int param_strcmp_collate(const char * a, const char * b)
156 {
157     if (!b) return !!a;
158     if (!a) return -!!b;
159     for(int k = 0 ; *a && *b ; a++, b++, k++) {
160         int r = (*a > *b) - (*b > *a);
161         if (k && (*a == '-' || *a == '_') && (*b == '-' || *b == '_')) r = 0;
162         if (r) return r;
163     }
164     return (*a > *b) - (*b > *a);
165 }
166 
is_documented_key(param_list_ptr pl,const char * key)167 static int is_documented_key(param_list_ptr pl, const char *key) {
168     for (int i = 0; i < pl->ndocs; ++i) {
169         if (param_strcmp_collate(key, pl->docs[i]->key) == 0)
170             return 1;
171     }
172     return 0;
173 }
174 
pointed_strcmp(const char ** a,const char ** b)175 int pointed_strcmp(const char ** a, const char **b)
176 {
177     return strcmp(*a, *b);
178 }
179 
alias_sort(const char * (* a)[2],const char * (* b)[2])180 int alias_sort(const char *(*a)[2], const char *(*b)[2])
181 {
182     return strcmp((*a)[0], (*b)[0]);
183 }
184 
pointed_param_list_doc_cmp_bykey(const param_list_doc_srcptr * a,const param_list_doc_srcptr * b)185 int pointed_param_list_doc_cmp_bykey(const param_list_doc_srcptr * a, const param_list_doc_srcptr * b)
186 {
187     return strcmp((*a)->key, (*b)->key);
188 }
189 
pointed_param_list_doc_cmp_byaddr(const param_list_doc_srcptr * a,const param_list_doc_srcptr * b)190 int pointed_param_list_doc_cmp_byaddr(const param_list_doc_srcptr * a, const param_list_doc_srcptr * b)
191 {
192     return ((*a) > (*b)) - ((*b) > (*a));
193 }
194 
param_list_print_usage(param_list_ptr pl,const char * argv0,FILE * f)195 void param_list_print_usage(param_list_ptr pl, const char * argv0, FILE *f)
196 {
197     if (argv0 != NULL)
198         fprintf(f, "Usage: %s <parameters>\n", argv0);
199     if (pl->usage_hdr != NULL)
200         fputs(pl->usage_hdr, f);
201 
202     /* copy the list of switches and aliases, so that we can accurately
203      * report them with -help
204      */
205     const char ** all_switches = malloc(pl->nswitches * sizeof(char*));
206     int nswitches_kept = 0;
207     for(int i = 0 ; i < pl->nswitches ; ++i) {
208         if (strncmp(pl->switches[i]->switchname, "--", 2) == 0) continue;
209         ASSERT_ALWAYS(pl->switches[i]->switchname[0] == '-');
210         all_switches[nswitches_kept++] = pl->switches[i]->switchname + 1;
211     }
212     qsort(all_switches, nswitches_kept, sizeof(const char*), (sortfunc_t) pointed_strcmp);
213 
214     const char *(*all_aliases)[2] = malloc(pl->naliases * 2 * sizeof(char*));
215     int naliases_kept = 0;
216     for(int i = 0 ; i < pl->naliases ; ++i) {
217         if (strncmp(pl->aliases[i]->alias, "--", 2) == 0) continue;
218         if (strncmp(pl->aliases[i]->alias, "-", 1) != 0) continue;
219         all_aliases[naliases_kept][0] = pl->aliases[i]->key;
220         all_aliases[naliases_kept][1] = pl->aliases[i]->alias;
221         naliases_kept++;
222     }
223     qsort(all_aliases, naliases_kept, 2 * sizeof(char*), (sortfunc_t) alias_sort);
224 
225     param_list_doc_srcptr * all_docs = malloc(pl->ndocs * sizeof(param_list_doc_srcptr));
226     for(int i = 0 ; i < pl->ndocs ; i ++)
227         all_docs[i] = pl->docs[i];
228     qsort(all_docs, pl->ndocs, sizeof(param_list_doc_srcptr), (sortfunc_t) pointed_param_list_doc_cmp_bykey);
229     /* consolidate */
230     int ndocs_kept = 0;
231     for(int i = 0, j ; i < pl->ndocs ; i += j) {
232         param_list_doc_srcptr di = all_docs[i];
233         for(j = 1 ; i + j < pl->ndocs ; j++) {
234             param_list_doc_srcptr dij = all_docs[i+j];
235             if (strcmp(di->key, dij->key) != 0) break;
236             if (strcmp(di->doc, dij->doc) == 0) continue;
237             fprintf(stderr, "WARNING: two documentation strings for option %s:\n\t%s\n\t%s\n", di->key, all_docs[i]->doc, dij->doc);
238         }
239         all_docs[ndocs_kept++] = di;
240     }
241     qsort(all_docs, ndocs_kept, sizeof(param_list_doc_srcptr), (sortfunc_t) pointed_param_list_doc_cmp_byaddr);
242 
243     fprintf(f, "The available parameters are the following:\n");
244     char whites[20];
245     for (int i = 0; i < 20; ++i)
246         whites[i] = ' ';
247     for (int i = 0; i < ndocs_kept; ++i) {
248         const char * key = all_docs[i]->key;
249         const char * doc = all_docs[i]->doc;
250 
251         int sw = bsearch(&key, all_switches, nswitches_kept, sizeof(const char *), (sortfunc_t) pointed_strcmp) != NULL;
252         const char *(*al)[2] = bsearch(&key, all_aliases, naliases_kept, 2 * sizeof(const char *), (sortfunc_t) alias_sort);
253         char * al_string = NULL;
254         if (al) {
255             size_t s = 9;       // "(alias) " incl terminating \0
256             for(int j = 0 ; al + j < all_aliases + naliases_kept ; ++j) {
257                 if (strcmp(al[j][0], key) != 0)
258                     break;
259                 s += strlen(al[j][1]) + 1;      /* incl space */
260             }
261             al_string = malloc(s);
262             size_t k = 0;
263             k += snprintf(al_string + k, s - k, "(alias");
264             for(int j = 0 ; al + j < all_aliases + naliases_kept ; ++j) {
265                 if (strcmp(al[j][0], key) != 0)
266                     break;
267                 k += snprintf(al_string + k, s - k, " %s", al[j][1]);
268             }
269             k += snprintf(al_string + k, s - k, ") ");
270             ASSERT_ALWAYS(k + 1 == s);
271         }
272         int l = strlen(key);
273         l = MAX(1, 10-l);
274         whites[l] ='\0';
275         fprintf(f, "    -%s%s%s%s%s\n", key, whites,
276                 sw ? "(switch) " : "",
277                 al_string ? al_string : "",
278                 doc);
279         whites[l] =' ';
280         if (al_string) free(al_string);
281     }
282     free(all_docs);
283     free(all_switches);
284     free(all_aliases);
285 }
286 
make_room(param_list_ptr pl,unsigned int more)287 static void make_room(param_list_ptr pl, unsigned int more)
288 {
289     if (pl->size + more <= pl->alloc) {
290         return;
291     }
292 
293     for( ; pl->size + more > pl->alloc ; ) {
294       pl->alloc += 8 + (pl->alloc >> 1);
295     }
296 
297     pl->p = (parameter *) realloc(pl->p, pl->alloc * sizeof(parameter));
298 }
299 
param_list_add_key_nostrdup(param_list_ptr pl,char * key,char * value,enum parameter_origin o)300 static int param_list_add_key_nostrdup(param_list_ptr pl,
301         char * key, char * value, enum parameter_origin o)
302 {
303     make_room(pl, 1);
304     int r = pl->size;
305     pl->p[pl->size]->key = key;
306     pl->p[pl->size]->value = value;
307     pl->p[pl->size]->origin = o;
308     // switches always count as parsed, of course. Hence the (value==NULL) thing
309     pl->p[pl->size]->parsed = (value == NULL);
310     // values above 1 are built within the sorting step.
311     pl->p[pl->size]->seen = 1;
312     pl->size++;
313     pl->consolidated = 0;
314     return r;
315 }
316 
param_list_add_key(param_list_ptr pl,const char * key,const char * value,enum parameter_origin o)317 int param_list_add_key(param_list_ptr pl,
318         const char * key, const char * value, enum parameter_origin o)
319 {
320     int r = param_list_add_key_nostrdup(pl,
321             key ? strdup(key) : NULL, value ? strdup(value) : NULL, o);
322     return r;
323 }
324 
325 struct sorting_data {
326     parameter_srcptr s;
327     int p;
328 };
329 
paramcmp(const struct sorting_data * a,const struct sorting_data * b)330 int paramcmp(const struct sorting_data * a, const struct sorting_data * b)
331 {
332     int r;
333     r = param_strcmp_collate(a->s->key, b->s->key);
334     if (r) return r;
335     r = a->s->origin - b->s->origin;
336     if (r) return r;
337     /* Compare by pointer position so that the sort is stable */
338     return a->p - b->p;
339 }
340 
341 /* Sort (for searching), and remove duplicates. */
param_list_consolidate(param_list_ptr pl)342 void param_list_consolidate(param_list_ptr pl)
343 {
344     if (pl->consolidated) {
345         return;
346     }
347     struct sorting_data * intermediate;
348     intermediate = malloc(pl->size * sizeof(struct sorting_data));
349     for(unsigned int i = 0 ; i < pl->size ; i++) {
350         intermediate[i].p = i;
351         intermediate[i].s = pl->p[i];
352     }
353     qsort(intermediate, pl->size, sizeof(struct sorting_data),
354             (sortfunc_t) &paramcmp);
355 
356     parameter * np = (parameter *) malloc(pl->alloc * sizeof(parameter));
357     for(unsigned int i = 0 ; i < pl->size ; i++) {
358         memcpy(np[i], pl->p[intermediate[i].p], sizeof(parameter));
359     }
360     free(pl->p);
361     pl->p = np;
362     free(intermediate);
363 
364     // now remove duplicates. The sorting has priorities right.
365     unsigned int j = 0;
366     for(unsigned int i = 0 ; i < pl->size ; i++) {
367         if (pl->p[i]->key != NULL && i + 1 < pl->size && param_strcmp_collate(pl->p[i]->key, pl->p[i+1]->key) == 0) {
368             /* The latest pair in the list is the one having highest
369              * priority. So we don't do the copy at this moment.
370              */
371             free(pl->p[i]->key);
372             free(pl->p[i]->value);
373             // this value is useful for switches
374             pl->p[i+1]->seen += pl->p[i]->seen;
375         } else {
376             // in theory memcpy does not allow overlaps, even trivial ones.
377             if (i != j) {
378                 memcpy(pl->p[j], pl->p[i], sizeof(parameter));
379             }
380             j++;
381         }
382     }
383     pl->size = j;
384 
385     /* consolidated list have to guarantee ordering. We have broken
386      * ordering, clearly */
387     pl->consolidated = 0;
388 }
389 
param_list_remove_key(param_list_ptr pl,const char * key)390 void param_list_remove_key(param_list_ptr pl, const char * key)
391 {
392     unsigned int j = 0;
393     for(unsigned int i = 0 ; i < pl->size ; i++) {
394         if (param_strcmp_collate(pl->p[i]->key, key) == 0) {
395             if (pl->p[i]->key) free(pl->p[i]->key);
396             free(pl->p[i]->value);
397         } else {
398             if (i != j) {
399                 memcpy(pl->p[j], pl->p[i], sizeof(parameter));
400             }
401             j++;
402         }
403     }
404     pl->size = j;
405     /* if the list was consolidated (sorted), it still is. If it wasn't,
406      * then, well, clearly it's not better */
407 }
408 
409 /* If step_on_empty_line is non-zero, then this function reads the file until a
410  * line containing only space caracters (check with isspace) is found. It allows
411  * to read a file contaning more than one polynomial.
412  * Otherwise the function reads the whole file.
413  */
param_list_read_stream(param_list_ptr pl,FILE * f,int stop_on_empty_line)414 int param_list_read_stream(param_list_ptr pl, FILE *f, int stop_on_empty_line)
415 {
416     int all_ok=1;
417     const int linelen = 2048;
418     char line[linelen];
419     char * newkey;
420     char * newvalue;
421     while (!feof(f)) {
422         if (fgets(line, linelen, f) == NULL)
423             break;
424         if (line[0] == '#')
425             continue;
426         // remove possible comment at end of line.
427         char * hash;
428         if ((hash = strchr(line, '#')) != NULL) {
429             *hash = '\0';
430         }
431 
432         char * p = line;
433 
434         // trailing space
435         int l = strlen(p);
436         for( ; l && isspace((int)(unsigned char)p[l-1]) ; l--);
437         p[l] = '\0';
438 
439         // leading space.
440         for( ; *p && isspace((int)(unsigned char)*p) ; p++, l--);
441 
442         // empty ps are ignored (unless stop_on_empty_line is non-zero).
443         if (l == 0)
444         {
445           if (stop_on_empty_line)
446             break;
447           else
448             continue;
449         }
450 
451         // look for a left-hand-side. We grok anything that *BEGINS WITH
452         // A DIGIT* as something that goes with the "NULL" token in the
453         // pl dictionary. That looks like a pretty obscure hack, in fact.
454         // Do we ever use it ?
455         l = 0;
456         if (!(isalpha((int)(unsigned char)p[l]) || p[l] == '_' || p[l] == '-')) {
457             param_list_add_key(pl, NULL, line, PARAMETER_FROM_FILE);
458             continue;
459         }
460         for( ; p[l] && (isalnum((int)(unsigned char)p[l]) || p[l] == '_' || p[l] == '-') ; l++);
461 
462         int lhs_length = l;
463 
464         if (lhs_length == 0) {
465             fprintf(stderr, "Parse error, no usable key for config line:\n%s\n",
466                     line);
467             all_ok=0;
468             continue;
469         }
470 
471         /* Now we can match (whitespace*)(separator)(whitespace*)(data)
472          */
473         char * q = p + lhs_length;
474         for( ; *q && isspace((int)(unsigned char)*q) ; q++);
475 
476         /* match separator, which is one of : = := */
477         if (*q == '=') {
478             q++;
479         } else if (*q == ':') {
480             q++;
481             if (*q == '=')
482                 q++;
483         } else if (q == p + lhs_length) {
484             fprintf(stderr, "Parse error, no separator for config line:\n%s\n",
485                     line);
486             all_ok=0;
487             continue;
488         }
489         for( ; *q && isspace((int)(unsigned char)*q) ; q++);
490 
491         newkey = malloc(lhs_length + 1);
492         memcpy(newkey, p, lhs_length);
493         newkey[lhs_length]='\0';
494 
495         newvalue = strdup(q);
496 
497         param_list_add_key_nostrdup(pl, newkey, newvalue, PARAMETER_FROM_FILE);
498     }
499     param_list_consolidate(pl);
500 
501     return all_ok;
502 }
503 
param_list_read_file(param_list_ptr pl,const char * name)504 int param_list_read_file(param_list_ptr pl, const char * name)
505 {
506     FILE * f;
507     f = fopen(name, "r");
508     if (f == NULL) {
509         fprintf(stderr, "Cannot read %s\n", name);
510         exit(1);
511     }
512     int r = param_list_read_stream(pl, f, 0);
513     fclose(f);
514     return r;
515 }
516 
param_list_configure_alias(param_list_ptr pl,const char * key,const char * alias)517 int param_list_configure_alias(param_list_ptr pl, const char * key, const char * alias)
518 {
519     if (pl->use_doc) {
520         const char *k = key;
521         if (k[0] == '-')
522             k++;
523         if (!is_documented_key(pl, k))
524             fprintf(stderr, "# Warning: an alias %s is declared to the key %s that is undocumented\n", alias, key);
525     }
526 
527     size_t len = strlen(alias);
528 
529     ASSERT_ALWAYS(alias != NULL);
530     ASSERT_ALWAYS(key != NULL);
531 
532     /* A switch may be aliased, but only as another switch !!! */
533     ASSERT_ALWAYS(key[0] != '-' || (alias[0] == '-' && alias[len-1] != '='));
534 
535     if (alias[0] != '-' && alias[len-1] != '=') {
536         /* Then, accept both the -xxx, --xxx, and xxx= forms */
537         char * tmp;
538         tmp = malloc(len + 4);
539         snprintf(tmp, len+4, "-%s", alias);
540         param_list_configure_alias(pl, key, tmp);
541         snprintf(tmp, len+4, "--%s", alias);
542         param_list_configure_alias(pl, key, tmp);
543         snprintf(tmp, len+4, "%s=", alias);
544         param_list_configure_alias(pl, key, tmp);
545         free(tmp);
546         return 0;
547     }
548 
549     if (pl->naliases == pl->naliases_alloc) {
550         pl->naliases_alloc += 1;
551         pl->naliases_alloc <<= 1;
552         pl->aliases = realloc(pl->aliases, pl->naliases_alloc * sizeof(param_list_alias));
553     }
554     pl->aliases[pl->naliases]->alias = strdup(alias);
555     pl->aliases[pl->naliases]->key = key;
556     pl->naliases++;
557     return 0;
558 }
559 
param_list_configure_switch(param_list_ptr pl,const char * switchname,int * ptr)560 int param_list_configure_switch(param_list_ptr pl, const char * switchname, int * ptr)
561 {
562     /* Some switches are passed as switchname = "switch", some as "-switch",
563        and some as "--switch" ... */
564     int offset = (switchname[0] == '-') ? (switchname[1] == '-' ? 2 : 1) : 0;
565     ASSERT_ALWAYS(switchname != NULL);
566     if (pl->use_doc)
567         if (!is_documented_key(pl, offset+switchname))
568             fprintf(stderr, "# Warning: a switch %s is declared but is undocumented\n", switchname);
569 
570     if ((pl->nswitches + 1) >= pl->nswitches_alloc) {
571         pl->nswitches_alloc += 2;
572         pl->nswitches_alloc <<= 1;
573         pl->switches = realloc(pl->switches, pl->nswitches_alloc * sizeof(param_list_switch));
574     }
575     char * tmp;
576     // build "--blah"
577     int rc = asprintf(&tmp, "--%s", switchname + offset);
578     ASSERT_ALWAYS(rc >= 0);
579     // put the -- version
580     pl->switches[pl->nswitches]->switchname = strdup(tmp);
581     pl->switches[pl->nswitches]->ptr = ptr;
582     if (ptr) *ptr = 0;
583     pl->nswitches++;
584     // put the - version
585     pl->switches[pl->nswitches]->switchname = strdup(tmp+1);
586     pl->switches[pl->nswitches]->ptr = ptr;
587     if (ptr) *ptr = 0;
588     pl->nswitches++;
589     free(tmp);
590 
591     return 0;
592 }
593 
594 
param_list_update_cmdline_alias(param_list_ptr pl,param_list_alias al,int * p_argc,char *** p_argv)595 static int param_list_update_cmdline_alias(param_list_ptr pl,
596         param_list_alias al,
597         int * p_argc, char *** p_argv)
598 {
599     if (!pl->cmdline_argv0) {
600         pl->cmdline_argv0 = *p_argv;
601         pl->cmdline_argc0 = *p_argc;
602     }
603     const char * a = (*p_argv[0]);
604     if (al->alias[strlen(al->alias)-1] == '=') {
605         // since switches are aliased only by switches, we know we have a plain
606         // option here.
607         if (strncmp(a, al->alias, strlen(al->alias)) != 0)
608             return 0;
609         a += strlen(al->alias);
610         if (a[0] == '\0')
611             return 0;
612         // no +1 , since the alias contains the = sign already.
613         param_list_add_key(pl, al->key, a, PARAMETER_FROM_CMDLINE);
614         (*p_argv)+=1;
615         (*p_argc)-=1;
616         return 1;
617     }
618     if (param_strcmp_collate(a, al->alias) == 0) {
619         if (al->key[0] == '-') {
620             /* This is a switch ; we have to treat it accordingly. The
621              * difficult part is to properly land on
622              * param_list_update_cmdline_switch at the proper time. This
623              * means in particular not necessarily there. It's
624              * considerably easier to simply change the value in the
625              * command line. Except that it's a const cast, it's ugly.
626              * Okay, my apologies, blah blah.
627              */
628             (*p_argv)[0] = (char*) al->key;
629             /* leave argv and argc unchanged. */
630             return 0;
631         }
632         (*p_argv)+=1;
633         (*p_argc)-=1;
634         if (*p_argc == 0) {
635             fprintf(stderr, "Option %s requires an argument\n", a);
636             exit(1);
637         }
638         param_list_add_key(pl, al->key, (*p_argv[0]), PARAMETER_FROM_CMDLINE);
639         (*p_argv)+=1;
640         (*p_argc)-=1;
641         return 1;
642     }
643     return 0;
644 }
645 
param_list_update_cmdline_switch(param_list_ptr pl,param_list_switch switchpar,int * p_argc,char *** p_argv)646 static int param_list_update_cmdline_switch(param_list_ptr pl,
647         param_list_switch switchpar,
648         int * p_argc, char *** p_argv)
649 {
650     if (!pl->cmdline_argv0) {
651         pl->cmdline_argv0 = *p_argv;
652         pl->cmdline_argc0 = *p_argc;
653     }
654     const char * a = (*p_argv[0]);
655     if (param_strcmp_collate(a, switchpar->switchname) == 0) {
656         param_list_add_key(pl, switchpar->switchname, NULL, PARAMETER_FROM_CMDLINE);
657         (*p_argv)+=1;
658         (*p_argc)-=1;
659         if (switchpar->ptr) (*(switchpar->ptr))++;
660         return 1;
661     }
662     if (strncmp(switchpar->switchname, "--", 2) != 0)
663         return 0;
664     char * inv_switch;
665     int rc = asprintf(&inv_switch, "--no-%s", switchpar->switchname+2);
666     ASSERT_ALWAYS(rc>=0);
667     int match = param_strcmp_collate(inv_switch, a) == 0;
668     free(inv_switch);
669     if (match) {
670         param_list_add_key(pl, a, NULL, PARAMETER_FROM_CMDLINE);
671         (*p_argv)+=1;
672         (*p_argc)-=1;
673         if (switchpar->ptr) (*(switchpar->ptr))=0;
674         return 1;
675     }
676     return 0;
677 }
678 
param_list_update_cmdline(param_list_ptr pl,int * p_argc,char *** p_argv)679 int param_list_update_cmdline(param_list_ptr pl,
680         int * p_argc, char *** p_argv)
681 {
682     if (!pl->cmdline_argv0) {
683         pl->cmdline_argv0 = *p_argv;
684         pl->cmdline_argc0 = *p_argc;
685     }
686     if (*p_argc == 0)
687         return 0;
688 
689     int i;
690 
691     /* We rely on having alias scanning first, because this incurs a
692      * command line changed (could get along without except in the case
693      * of switches where it's particularly handy).
694      */
695     for(i = 0 ; i < pl->naliases ; i++) {
696         if (param_list_update_cmdline_alias(pl, pl->aliases[i], p_argc, p_argv))
697             return 1;
698     }
699 
700     for(i = 0 ; i < pl->nswitches ; i++) {
701         if (param_list_update_cmdline_switch(pl, pl->switches[i], p_argc, p_argv))
702             return 1;
703     }
704 
705     const char * a = (*p_argv[0]);
706     if (*p_argc >= 2 && a[0] == '-') {
707         a++;
708         a+= *a == '-';
709         int x=0;
710         /* parameters _must_ begin by alphabetic characters, or
711          * otherwise we suffer to distinguish immediate numerical
712          * entries.
713          */
714         if (!isalpha((int)(unsigned char)a[x]))
715             return 0;
716         for( ; a[x] && (isalnum((int)(unsigned char)a[x]) || a[x] == '_' || a[x] == '-') ; x++);
717         if (a[x] == '\0') {
718             param_list_add_key(pl, a, (*p_argv)[1], PARAMETER_FROM_CMDLINE);
719             (*p_argv)+=2;
720             (*p_argc)-=2;
721             return 1;
722         }
723     } else {
724         /* Check for <key>=<value> syntax */
725         int x=0;
726         for( ; a[x] && (isalnum((int)(unsigned char)a[x]) || a[x] == '_' || a[x] == '-') ; x++);
727         if (a[x] == '=' && a[x+1]) {
728             char * newkey = malloc(x+1);
729             memcpy(newkey, a, x);
730             newkey[x]='\0';
731             char * newvalue = strdup(a+x+1);
732             param_list_add_key_nostrdup(pl,
733                     newkey, newvalue, PARAMETER_FROM_CMDLINE);
734             (*p_argv)+=1;
735             (*p_argc)-=1;
736             return 1;
737         }
738     }
739     return 0;
740 }
741 
param_strcmp(const char * a,parameter_srcptr b)742 int param_strcmp(const char * a, parameter_srcptr b)
743 {
744     return param_strcmp_collate(a, b->key);
745 }
746 
assoc(param_list_ptr pl,const char * key)747 static int assoc(param_list_ptr pl, const char * key)
748 {
749     if (pl->use_doc) {
750         const char *k = (key[0] == '-') ? (1+key) : key;
751         if (!is_documented_key(pl, k))
752             fprintf(stderr, "# Warning: parameter %s is checked by this program but is undocumented.\n", k);
753     }
754 
755     void * found;
756 
757     param_list_consolidate(pl);
758     found = bsearch(key, pl->p, pl->size,
759             sizeof(parameter), (sortfunc_t) param_strcmp);
760     if (found == NULL)
761         return -1;
762     parameter * c = (parameter *) found;
763     return c-pl->p;
764 }
765 
766 /* Look up an entry in a param_list, and update the parsed flag. It does
767    mutex locking to make look-ups thread safe; the caller must not access
768    any param_list entries by itself. */
769 static int
get_assoc(param_list_ptr pl,const char * const key,char ** const value,int * const seen)770 get_assoc(param_list_ptr pl, const char * const key, char ** const value, int * const seen)
771 {
772     pthread_mutex_lock(mutex);
773     const int v = assoc(pl, key);
774     const int found = (v >= 0);
775     if (found) {
776         pl->p[v]->parsed = 1;
777         if (value != NULL) {
778             *value = pl->p[v]->value;
779         }
780         if (seen != NULL) {
781             *seen = pl->p[v]->seen;
782         }
783     }
784     pthread_mutex_unlock(mutex);
785     return found;
786 }
787 
strtol_expanded(const char * nptr,char ** endptr,int base)788 long strtol_expanded(const char *nptr, char **endptr, int base)
789 {
790     long res;
791     char * eptr;
792     res = strtol(nptr, &eptr, base);
793     if (endptr) *endptr = eptr;
794     if (*eptr != '\0' && (*eptr == '.' || *eptr == 'e')) {
795         /* try to recognize something which is written as a floating
796          * point integer. We require the representation to be exact. */
797         double d = strtod(nptr, &eptr);
798         if (d == (long) d) {
799             if (endptr) *endptr = eptr;
800             return (long) d;
801         }
802     }
803     return res;
804 }
805 
strtoul_expanded(const char * nptr,char ** endptr,int base)806 unsigned long strtoul_expanded(const char *nptr, char **endptr, int base)
807 {
808     unsigned long res;
809     char * eptr;
810     res = strtoul(nptr, &eptr, base);
811     if (endptr) *endptr = eptr;
812     if (*eptr != '\0' && (*eptr == '.' || *eptr == 'e')) {
813         /* try to recognize something which is written as a floating
814          * point integer. We require the representation to be exact. */
815         double d = strtod(nptr, &eptr);
816         if (d == (unsigned long) d) {
817             if (endptr) *endptr = eptr;
818             return (unsigned long) d;
819         }
820     }
821     return res;
822 }
823 
param_list_parse_long(param_list_ptr pl,const char * key,long * r)824 int param_list_parse_long(param_list_ptr pl, const char * key, long * r)
825 {
826     char *value;
827     int seen;
828     if (!get_assoc(pl, key, &value, &seen))
829         return 0;
830     char * end;
831     long res;
832     res = strtol_expanded(value, &end, 0);
833     if (*end != '\0') {
834         fprintf(stderr, "Parse error: parameter for key %s is not a long: %s\n",
835                 key, value);
836         exit(1);
837     }
838     if (r)
839         *r = res;
840     return seen;
841 }
842 
param_list_parse_int(param_list_ptr pl,const char * key,int * r)843 int param_list_parse_int(param_list_ptr pl, const char * key, int * r)
844 {
845     long res;
846     int rc;
847     rc = param_list_parse_long(pl, key, &res);
848     if (rc == 0)
849         return 0;
850     if (res > INT_MAX || res < INT_MIN) {
851         fprintf(stderr, "Parse error:"
852                 " parameter for key %s does not fit within an int: %ld\n",
853                 key, res);
854         exit(1);
855     }
856     if (r)
857         *r  = res;
858     return rc;
859 }
860 
param_list_parse_long_and_long(param_list_ptr pl,const char * key,long * r,const char * sep)861 int param_list_parse_long_and_long(param_list_ptr pl, const char * key, long * r, const char * sep)
862 {
863     char *value;
864     int seen;
865     if (!get_assoc(pl, key, &value, &seen))
866         return 0;
867     char *orig_value = value, * end;
868     long res[2];
869     res[0] = strtol(value, &end, 0);
870     if (strncmp(end, sep, strlen(sep)) != 0) {
871         fprintf(stderr, "Parse error: parameter for key %s"
872                 " must match %%d%s%%d; got %s\n",
873                 key, sep, orig_value);
874         exit(1);
875     }
876     value = end + strlen(sep);
877     res[1] = strtol(value, &end, 0);
878     if (*end != '\0') {
879         fprintf(stderr, "Parse error: parameter for key %s"
880                 " must match %%d%s%%d; got %s\n",
881                 key, sep, orig_value);
882         exit(1);
883     }
884     if (r) {
885         r[0] = res[0];
886         r[1] = res[1];
887     }
888     return seen;
889 }
890 
param_list_parse_ulong_and_ulong(param_list_ptr pl,const char * key,unsigned long * r,const char * sep)891 int param_list_parse_ulong_and_ulong(param_list_ptr pl, const char * key, unsigned long * r, const char * sep)
892 {
893     char *value;
894     int seen;
895     if (!get_assoc(pl, key, &value, &seen))
896         return 0;
897     char *orig_value = value, * end;
898     unsigned long res[2];
899     res[0] = strtoul(value, &end, 0);
900     if (strncmp(end, sep, strlen(sep)) != 0) {
901         fprintf(stderr, "Parse error: parameter for key %s"
902                 " must match %%d%s%%d; got %s\n",
903                 key, sep, orig_value);
904         exit(1);
905     }
906     value = end + strlen(sep);
907     res[1] = strtoul(value, &end, 0);
908     if (*end != '\0') {
909         fprintf(stderr, "Parse error: parameter for key %s"
910                 " must match %%d%s%%d; got %s\n",
911                 key, sep, orig_value);
912         exit(1);
913     }
914     if (r) {
915         r[0] = res[0];
916         r[1] = res[1];
917     }
918     return seen;
919 }
920 
param_list_parse_int_and_int(param_list_ptr pl,const char * key,int * r,const char * sep)921 int param_list_parse_int_and_int(param_list_ptr pl, const char * key, int * r, const char * sep)
922 {
923 #if 1
924   long rr[2] = {0, 0};
925     if (r) {
926         rr[0] = r[0];
927         rr[1] = r[1];
928     }
929     int seen = param_list_parse_long_and_long(pl, key, rr, sep);
930     if (r) {
931         r[0] = rr[0];
932         r[1] = rr[1];
933     }
934     return seen;
935 #else
936     long rr[2];
937     int seen = param_list_parse_long_and_long(pl, key,r ? rr : NULL, sep);
938     if (r) {
939         r[0] = rr[0];
940         r[1] = rr[1];
941     }
942     return seen;
943 #endif
944 }
945 
param_list_parse_uint_and_uint(param_list_ptr pl,const char * key,unsigned int * r,const char * sep)946 int param_list_parse_uint_and_uint(param_list_ptr pl, const char * key, unsigned int * r, const char * sep)
947 {
948     long rr[2] = {0, 0};
949     if (r) {
950         rr[0] = r[0];
951         rr[1] = r[1];
952     }
953     int seen = param_list_parse_long_and_long(pl, key, rr, sep);
954     if (r) {
955         r[0] = rr[0];
956         r[1] = rr[1];
957     }
958     return seen;
959 }
960 
param_list_parse_intxint(param_list_ptr pl,const char * key,int * r)961 int param_list_parse_intxint(param_list_ptr pl, const char * key, int * r)
962 {
963     return param_list_parse_int_and_int(pl, key, r, "x");
964 }
965 
param_list_parse_uint64_and_uint64(param_list_ptr pl,const char * key,uint64_t * r,const char * sep)966 int param_list_parse_uint64_and_uint64(param_list_ptr pl, const char * key,
967     uint64_t * r, const char * sep)
968 {
969     char *value;
970     int seen;
971     if (!get_assoc(pl, key, &value, &seen))
972         return 0;
973     char *orig_value = value, * end;
974     unsigned long long res[2];
975     res[0] = strtoull(value, &end, 0);
976     if (strncmp(end, sep, strlen(sep)) != 0) {
977         fprintf(stderr, "Parse error: parameter for key %s"
978                 " must match %%d%s%%d; got %s\n",
979                 key, sep, orig_value);
980         exit(1);
981     }
982     value = end + strlen(sep);
983     res[1] = strtoull(value, &end, 0);
984     if (*end != '\0') {
985         fprintf(stderr, "Parse error: parameter for key %s"
986                 " must match %%d%s%%d; got %s\n",
987                 key, sep, orig_value);
988         exit(1);
989     }
990     if (r) {
991         r[0] = (uint64_t) res[0];
992         r[1] = (uint64_t) res[1];
993     }
994     return seen;
995 }
996 
param_list_parse_ulong(param_list_ptr pl,const char * key,unsigned long * r)997 int param_list_parse_ulong(param_list_ptr pl, const char * key, unsigned long * r)
998 {
999     char *value;
1000     int seen;
1001     if (!get_assoc(pl, key, &value, &seen))
1002         return 0;
1003     char * end;
1004     unsigned long res;
1005     res = strtoul_expanded(value, &end, 0);
1006     if (*end != '\0') {
1007         fprintf(stderr, "Parse error:"
1008                 " parameter for key %s is not an ulong: %s\n",
1009                 key, value);
1010         exit(1);
1011     }
1012     if (r)
1013         *r = res;
1014     return seen;
1015 }
1016 
param_list_parse_size_t(param_list_ptr pl,const char * key,size_t * r)1017 int param_list_parse_size_t(param_list_ptr pl, const char * key, size_t * r)
1018 {
1019     unsigned long t;
1020     int res;
1021     res = param_list_parse_ulong(pl, key, &t);
1022     if (res && r) { *r = t; }
1023     return res;
1024 }
1025 
1026 
param_list_parse_int64(param_list_ptr pl,const char * key,int64_t * r)1027 int param_list_parse_int64(param_list_ptr pl, const char * key, int64_t * r)
1028 {
1029     char *value;
1030     int seen;
1031     if (!get_assoc(pl, key, &value, &seen))
1032         return 0;
1033     char * end;
1034     int64_t res;
1035     res = strtoimax(value, &end, 0);
1036     if (*end != '\0') {
1037         fprintf(stderr, "Parse error:"
1038                 " parameter for key %s is not an int64_t: %s\n",
1039                 key, value);
1040         exit(1);
1041     }
1042     if (r)
1043         *r = res;
1044     return seen;
1045 }
1046 
param_list_parse_uint64(param_list_ptr pl,const char * key,uint64_t * r)1047 int param_list_parse_uint64(param_list_ptr pl, const char * key, uint64_t * r)
1048 {
1049     char *value;
1050     int seen;
1051     if (!get_assoc(pl, key, &value, &seen))
1052         return 0;
1053     char * end;
1054     uint64_t res;
1055     res = strtoumax(value, &end, 0);
1056     if (*end != '\0') {
1057         fprintf(stderr, "Parse error:"
1058                 " parameter for key %s is not an uint64_t: %s\n",
1059                 key, value);
1060         exit(1);
1061     }
1062     if (r)
1063         *r = res;
1064     return seen;
1065 }
1066 
param_list_parse_uint(param_list_ptr pl,const char * key,unsigned int * r)1067 int param_list_parse_uint(param_list_ptr pl, const char * key, unsigned int * r)
1068 {
1069     unsigned long res;
1070     int rc = param_list_parse_ulong(pl, key, &res);
1071     if (rc == 0)
1072         return 0;
1073     if (res > UINT_MAX) {
1074         fprintf(stderr, "Parse error:"
1075                 " parameter for key %s does not fit within an unsigned int: %ld\n",
1076                 key, res);
1077         exit(1);
1078     }
1079     if (r)
1080         *r  = res;
1081     return rc;
1082 }
1083 
param_list_parse_uchar(param_list_ptr pl,const char * key,unsigned char * r)1084 int param_list_parse_uchar(param_list_ptr pl, const char * key, unsigned char * r)
1085 {
1086     unsigned long res;
1087     int rc = param_list_parse_ulong(pl, key, &res);
1088     if (rc == 0)
1089         return 0;
1090     if (res > UCHAR_MAX) {
1091         fprintf(stderr, "Parse error:"
1092                 " parameter for key %s does not fit within an unsigned int: %ld\n",
1093                 key, res);
1094         exit(1);
1095     }
1096     if (r)
1097         *r  = (unsigned char) res;
1098     return rc;
1099 }
1100 
param_list_parse_double(param_list_ptr pl,const char * key,double * r)1101 int param_list_parse_double(param_list_ptr pl, const char * key, double * r)
1102 {
1103     char *value;
1104     int seen;
1105     if (!get_assoc(pl, key, &value, &seen))
1106         return 0;
1107     char * end;
1108     double res;
1109     res = strtod(value, &end);
1110     if (*end != '\0') {
1111         fprintf(stderr, "Parse error: parameter for key %s is not an int: %s\n",
1112                 key, value);
1113         exit(1);
1114     }
1115     if (r)
1116         *r = res;
1117     return seen;
1118 }
1119 
param_list_parse_double_and_double(param_list_ptr pl,const char * key,double * r,const char * sep)1120 int param_list_parse_double_and_double(param_list_ptr pl, const char * key,
1121     double * r, const char * sep)
1122 {
1123     char *value;
1124     int seen;
1125     if (!get_assoc(pl, key, &value, &seen))
1126         return 0;
1127     char *orig_value = value, * end;
1128     double res[2];
1129     res[0] = strtod(value, &end);
1130     if (strncmp(end, sep, strlen(sep)) != 0) {
1131         fprintf(stderr, "Parse error: parameter for key %s"
1132                 " must match %%d%s%%d; got %s\n",
1133                 key, sep, orig_value);
1134         exit(1);
1135     }
1136     value = end + strlen(sep);
1137     res[1] = strtod(value, &end);
1138     if (*end != '\0') {
1139         fprintf(stderr, "Parse error: parameter for key %s"
1140                 " must match %%d%s%%d; got %s\n",
1141                 key, sep, orig_value);
1142         exit(1);
1143     }
1144     if (r) {
1145         r[0] = res[0];
1146         r[1] = res[1];
1147     }
1148     return seen;
1149 }
1150 
param_list_parse_string(param_list_ptr pl,const char * key,char * r,size_t n)1151 int param_list_parse_string(param_list_ptr pl, const char * key, char * r, size_t n)
1152 {
1153     char *value;
1154     int seen;
1155     if (!get_assoc(pl, key, &value, &seen))
1156         return 0;
1157     if (r && strlen(value) > n-1) {
1158         fprintf(stderr, "Parse error:"
1159                 " parameter for key %s does not fit within string buffer"
1160                 " of length %lu\n", key, (unsigned long) n);
1161         exit(1);
1162     }
1163     if (r)
1164         strncpy(r, value, n);
1165     return seen;
1166 }
1167 
param_list_parse_string_list_alloc(param_list_ptr pl,const char * key,char *** r,int * n,const char * sep)1168 int param_list_parse_string_list_alloc(param_list_ptr pl, const char * key, char *** r, int * n, const char * sep)
1169 {
1170     char * value;
1171     *r = NULL;
1172     *n = 0;
1173     int parsed = 0;
1174     if (!get_assoc(pl, key, &value, NULL))
1175         return 0;
1176     for( ; ; ) {
1177         char * v = strstr(value, sep);
1178         int itemsize;
1179         if (v == NULL) {
1180             itemsize = strlen(value);
1181         } else {
1182             itemsize = v - value;
1183         }
1184         *r = realloc(*r, (parsed + 1) * sizeof(char *));
1185         (*r)[parsed++] = strndup(value, itemsize);
1186         if (!v)
1187             break;
1188         value = v + strlen(sep);
1189     }
1190     *n = parsed;
1191     return parsed;
1192 }
1193 
param_list_get_list_count(param_list_ptr pl,const char * key)1194 int param_list_get_list_count(param_list_ptr pl, const char * key)
1195 {
1196     if (!param_list_lookup_string(pl, key))
1197         return 0;
1198 
1199     char ** names;
1200     int nitems;
1201     int rc = param_list_parse_string_list_alloc(pl, key, &names, &nitems, ",");
1202     if (rc == 0)
1203         return 0;
1204     for(int midx = 0 ; midx < nitems ; midx++) {
1205         free(names[midx]);
1206     }
1207     free(names);
1208     return nitems;
1209 }
1210 
1211 
param_list_parse_int_list(param_list_ptr pl,const char * key,int * r,size_t n,const char * sep)1212 int param_list_parse_int_list(param_list_ptr pl, const char * key, int * r, size_t n, const char * sep)
1213 {
1214     char *value;
1215     if (!get_assoc(pl, key, &value, NULL))
1216         return 0;
1217     char *orig_value = value, * end;
1218     int * res = malloc(n * sizeof(int));
1219     memset(res, 0, n * sizeof(int));
1220     size_t parsed = 0;
1221     for( ;; ) {
1222         res[parsed] = strtol(value, &end, 0);
1223         if (parsed++ == n)
1224             break;
1225         if (parsed && *end == '\0')
1226             break;
1227         if (strncmp(end, sep, strlen(sep)) != 0) {
1228             fprintf(stderr, "Parse error: parameter for key %s"
1229                     " must match %%d(%s%%d)*; got %s\n",
1230                     key, sep, orig_value);
1231             exit(1);
1232         }
1233         value = end + strlen(sep);
1234     }
1235     if (*end != '\0') {
1236         fprintf(stderr, "Parse error: parameter for key %s"
1237                 " must match %%d(%s%%d){0,%zu}; got %s\n",
1238                 key, sep, n-1, orig_value);
1239         exit(1);
1240     }
1241     if (r) {
1242         memcpy(r, res, n * sizeof(int));
1243     }
1244     free(res);
1245     return parsed;
1246 }
1247 
param_list_parse_uint_list(param_list_ptr pl,const char * key,unsigned int * r,size_t n,const char * sep)1248 int param_list_parse_uint_list(param_list_ptr pl, const char * key,
1249     unsigned int * r, size_t n, const char * sep)
1250 {
1251     char *value;
1252     if (!get_assoc(pl, key, &value, NULL))
1253         return 0;
1254     char *orig_value = value, * end;
1255     unsigned int * res = malloc(n * sizeof(unsigned int));
1256     memset(res, 0, n * sizeof(unsigned int));
1257     size_t parsed = 0;
1258     for( ;; ) {
1259         unsigned long tmp = strtoul(value, &end, 0);
1260         ASSERT(tmp <= UINT_MAX);
1261         res[parsed] = tmp;
1262         if (parsed++ == n)
1263             break;
1264         if (parsed && *end == '\0')
1265             break;
1266         if (strncmp(end, sep, strlen(sep)) != 0) {
1267             fprintf(stderr, "Parse error: parameter for key %s"
1268                     " must match %%d(%s%%d)*; got %s\n",
1269                     key, sep, orig_value);
1270             exit(1);
1271         }
1272         value = end + strlen(sep);
1273     }
1274     if (*end != '\0') {
1275         fprintf(stderr, "Parse error: parameter for key %s"
1276                 " must match %%d(%s%%d){0,%zu}; got %s\n",
1277                 key, sep, n-1, orig_value);
1278         exit(1);
1279     }
1280     if (r) {
1281         memcpy(r, res, n * sizeof(unsigned int));
1282     }
1283     free(res);
1284     return parsed;
1285 }
1286 
param_list_parse_uint64_list(param_list_ptr pl,const char * key,uint64_t * r,size_t n,const char * sep)1287 int param_list_parse_uint64_list(param_list_ptr pl, const char * key,
1288     uint64_t * r, size_t n, const char * sep)
1289 {
1290     char *value;
1291     if (!get_assoc(pl, key, &value, NULL))
1292         return 0;
1293     char *orig_value = value, * end;
1294     uint64_t * res = malloc(n * sizeof(uint64_t));
1295     memset(res, 0, n * sizeof(unsigned int));
1296     size_t parsed = 0;
1297     for( ;; ) {
1298         res[parsed] = (uint64_t)strtoull(value, &end, 0);
1299         if (parsed++ == n)
1300             break;
1301         if (parsed && *end == '\0')
1302             break;
1303         if (strncmp(end, sep, strlen(sep)) != 0) {
1304             fprintf(stderr, "Parse error: parameter for key %s"
1305                     " must match %%d(%s%%d)*; got %s\n",
1306                     key, sep, orig_value);
1307             exit(1);
1308         }
1309         value = end + strlen(sep);
1310     }
1311     if (*end != '\0') {
1312         fprintf(stderr, "Parse error: parameter for key %s"
1313                 " must match %%d(%s%%d){0,%zu}; got %s\n",
1314                 key, sep, n-1, orig_value);
1315         exit(1);
1316     }
1317     if (r) {
1318         memcpy(r, res, n * sizeof(uint64_t));
1319     }
1320     free(res);
1321     return parsed;
1322 }
1323 
param_list_parse_uchar_list(param_list_ptr pl,const char * key,unsigned char * r,size_t n,const char * sep)1324 int param_list_parse_uchar_list(param_list_ptr pl, const char * key,
1325     unsigned char * r, size_t n, const char * sep)
1326 {
1327     char *value;
1328     if (!get_assoc(pl, key, &value, NULL))
1329         return 0;
1330     char *orig_value = value, * end;
1331     unsigned char * res = malloc(n * sizeof(unsigned char));
1332     memset(res, 0, n * sizeof(unsigned char));
1333     size_t parsed = 0;
1334     for( ;; ) {
1335         long tmp = strtol(value, &end, 0);
1336         ASSERT(tmp <= UCHAR_MAX);
1337         res[parsed] = tmp;
1338         if (parsed++ == n)
1339             break;
1340         if (parsed && *end == '\0')
1341             break;
1342         if (strncmp(end, sep, strlen(sep)) != 0) {
1343             fprintf(stderr, "Parse error: parameter for key %s"
1344                     " must match %%d(%s%%d)*; got %s\n",
1345                     key, sep, orig_value);
1346             exit(1);
1347         }
1348         value = end + strlen(sep);
1349     }
1350     if (*end != '\0') {
1351         fprintf(stderr, "Parse error: parameter for key %s"
1352                 " must match %%d(%s%%d){0,%zu}; got %s\n",
1353                 key, sep, n-1, orig_value);
1354         exit(1);
1355     }
1356     if (r) {
1357         memcpy(r, res, n * sizeof(unsigned char));
1358     }
1359     free(res);
1360     return parsed;
1361 }
1362 
param_list_parse_int_list_size(param_list_ptr pl,const char * key,int ** r,unsigned int * t)1363 int param_list_parse_int_list_size(param_list_ptr pl, const char * key , int ** r ,
1364     unsigned int * t)
1365 {
1366   char *value;
1367   if (!get_assoc(pl, key, &value, NULL))
1368     return 0;
1369   char *tmp = value;
1370   int i = 0;
1371   * r = (int * ) malloc(sizeof(int) * 1);
1372   * t = 1;
1373   for (;;) {
1374     int ret = sscanf(tmp, "%d", &i);
1375     if (ret != 1) {
1376       fprintf (stderr, "Error while parsing coefficient array %s[%d]\n", key, *t
1377           - 1);
1378       exit(1);
1379     }
1380     (*r)[*t - 1] = i;
1381     if (*tmp == '-') {
1382       tmp++;
1383     }
1384     while (isdigit(*tmp)) {
1385       tmp++;
1386     }
1387     if(*tmp == '\0') {
1388       break;
1389     }
1390     if (*tmp != ',') {
1391       fprintf (stderr, "Error while parsing array %s\n", key);
1392       exit(1);
1393     }
1394     tmp++;
1395     *t = *t + 1;
1396     * r = realloc(* r, sizeof(int) * (*t));
1397   }
1398   return *t;
1399 }
1400 
param_list_parse_double_list(param_list_ptr pl,const char * key,double * r,size_t n,const char * sep)1401 int param_list_parse_double_list(param_list_ptr pl, const char * key,
1402     double * r, size_t n, const char * sep)
1403 {
1404     char *value;
1405     if (!get_assoc(pl, key, &value, NULL))
1406         return 0;
1407     char *orig_value = value, * end;
1408     double * res = (double *) malloc(n * sizeof(double));
1409     memset(res, 0, n * sizeof(double));
1410     size_t parsed = 0;
1411     for( ;; ) {
1412         double tmp = strtod(value, &end);
1413         ASSERT(tmp <= UCHAR_MAX);
1414         res[parsed] = tmp;
1415         if (parsed++ == n)
1416             break;
1417         if (parsed && *end == '\0')
1418             break;
1419         if (strncmp(end, sep, strlen(sep)) != 0) {
1420             fprintf(stderr, "Parse error: parameter for key %s"
1421                     " must match %%d(%s%%d)*; got %s\n",
1422                     key, sep, orig_value);
1423             exit(1);
1424         }
1425         value = end + strlen(sep);
1426     }
1427     if (*end != '\0') {
1428         fprintf(stderr, "Parse error: parameter for key %s"
1429                 " must match %%d(%s%%d){0,%zu}; got %s\n",
1430                 key, sep, n-1, orig_value);
1431         exit(1);
1432     }
1433     if (r) {
1434         memcpy(r, res, n * sizeof(double));
1435     }
1436     free(res);
1437     return parsed;
1438 }
1439 
param_list_parse_mpz_poly(param_list_ptr pl,const char * key,mpz_poly_ptr f)1440 int param_list_parse_mpz_poly(param_list_ptr pl, const char * key,
1441     mpz_poly_ptr f)
1442 {
1443   char *value;
1444   if (!get_assoc(pl, key, &value, NULL))
1445     return 0;
1446   char *tmp = value;
1447   mpz_t coeff;
1448   mpz_init(coeff);
1449   for(unsigned int j = 0; ; j++) {
1450     int ret = gmp_sscanf(tmp, "%Zd", coeff);
1451     if (ret != 1) {
1452       fprintf (stderr, "Error while parsing coefficients of degree %u "
1453           "of polynomial %s\n", j, key);
1454       exit(1);
1455     }
1456     mpz_poly_setcoeff(f, j, coeff);
1457     if (*tmp == '-') {
1458       tmp++;
1459     }
1460     while (isdigit(*tmp)) {
1461       tmp++;
1462     }
1463     if(*tmp == '\0') {
1464       break;
1465     }
1466     if (*tmp != ',' && !isspace(*tmp)) {
1467       fprintf (stderr, "Error while parsing polynomial %s\n", key);
1468       exit(1);
1469     }
1470     tmp++;
1471     for( ; *tmp && isspace(*tmp) ; tmp++);
1472   }
1473   mpz_clear(coeff);
1474   return 1;
1475 }
1476 
param_list_lookup_string(param_list_ptr pl,const char * key)1477 const char * param_list_lookup_string(param_list_ptr pl, const char * key)
1478 {
1479     char *value;
1480     int seen;
1481     if (!get_assoc(pl, key, &value, &seen))
1482         return NULL;
1483     return value;
1484 }
1485 
param_list_parse_mpz(param_list_ptr pl,const char * key,mpz_ptr r)1486 int param_list_parse_mpz(param_list_ptr pl, const char * key, mpz_ptr r)
1487 {
1488     char *value;
1489     int seen;
1490     if (!get_assoc(pl, key, &value, &seen))
1491         return 0;
1492     unsigned int nread;
1493     int rc;
1494     if (r) {
1495         rc = gmp_sscanf(value, "%Zi%n", r, &nread);
1496     } else {
1497         /* scan even when the result is not wanted */
1498         rc = gmp_sscanf(value, "%*Zi%n", &nread);
1499     }
1500     if (rc != 1 || value[nread] != '\0') {
1501         /* also recognize integers written as floating-point, like 6.3e8
1502          */
1503         if (value[nread] == '.' || value[nread] == 'e') {
1504             mpf_t zf;
1505             mpf_init(zf);
1506             rc = gmp_sscanf(value, "%Ff%n", zf, &nread);
1507             rc = (rc == 1 && value[nread] == '\0' && mpf_integer_p(zf));
1508             if (rc && r) mpz_set_f(r, zf);
1509             mpf_clear(zf);
1510             if (rc) return seen;
1511         }
1512 
1513         fprintf(stderr, "Parse error: parameter for key %s is not an mpz: %s\n",
1514                 key, value);
1515         exit(1);
1516     }
1517     return seen;
1518 }
1519 
param_list_parse_switch(param_list_ptr pl,const char * key)1520 int param_list_parse_switch(param_list_ptr pl, const char * key)
1521 {
1522     char *value;
1523     int seen;
1524     if (!get_assoc(pl, key, &value, &seen))
1525         return 0;
1526     if (value != NULL) {
1527         fprintf(stderr, "Parse error: option %s accepts no argument\n", key);
1528         exit(1);
1529     }
1530     return seen;
1531 }
1532 
param_list_all_consumed(param_list_ptr pl,char ** extraneous)1533 int param_list_all_consumed(param_list_ptr pl, char ** extraneous)
1534 {
1535     for(unsigned int i = 0 ; i < pl->size ; i++) {
1536         if (!pl->p[i]->parsed) {
1537             pl->p[i]->parsed = 1;
1538             if (extraneous) {
1539                 *extraneous = pl->p[i]->key;
1540             }
1541             return 0;
1542         }
1543     }
1544     return 1;
1545 }
1546 
param_list_warn_unused(param_list_ptr pl)1547 int param_list_warn_unused(param_list_ptr pl)
1548 {
1549     int u = 0;
1550     for(unsigned int i = 0 ; i < pl->size ; i++) {
1551         if (pl->p[i]->origin != PARAMETER_FROM_FILE && !pl->p[i]->parsed) {
1552             fprintf(stderr, "Warning: unused command-line parameter %s\n",
1553                     pl->p[i]->key);
1554             u++;
1555         }
1556     }
1557     return u;
1558 }
1559 
param_list_display(param_list_ptr pl,FILE * f)1560 void param_list_display(param_list_ptr pl, FILE *f)
1561 {
1562     param_list_consolidate(pl);
1563     for(unsigned int i = 0 ; i < pl->size ; i++) {
1564         fprintf(f,"%s=%s\n", pl->p[i]->key, pl->p[i]->value);
1565     }
1566 }
1567 
param_list_save(param_list_ptr pl,const char * filename)1568 void param_list_save(param_list_ptr pl, const char * filename)
1569 {
1570     FILE * f = fopen(filename, "w");
1571     if (f == NULL) {
1572         fprintf(stderr, "fopen(%s): %s\n", filename, strerror(errno));
1573         exit(1);
1574     }
1575 
1576     param_list_display(pl, f);
1577     fclose(f);
1578 }
1579 
param_list_save_parameter(param_list_ptr pl,enum parameter_origin o,const char * key,const char * format,...)1580 int param_list_save_parameter(param_list_ptr pl, enum parameter_origin o,
1581         const char * key, const char * format, ...)
1582 {
1583     va_list ap;
1584     va_start(ap, format);
1585 
1586     char * tmp;
1587     int rc;
1588     rc = vasprintf(&tmp, format, ap);
1589     param_list_add_key(pl, key, tmp, o);
1590     free(tmp);
1591     va_end(ap);
1592 
1593     return rc;
1594 }
1595 
1596 
param_list_print_command_line(FILE * stream,param_list_ptr pl)1597 void param_list_print_command_line(FILE * stream, param_list_ptr pl)
1598 {
1599     /* remember that the API for calling param_list functions mandates
1600      * that the binary name $0 is stripped from the provided lists */
1601     if (pl->cmdline_argv0 == NULL)
1602         return;
1603 
1604     char **argv = pl->cmdline_argv0-1;
1605     int argc = pl->cmdline_argc0+1;
1606 
1607     if (verbose_enabled(CADO_VERBOSE_PRINT_CMDLINE)) {
1608         /* print command line */
1609         fprintf (stream, "# (%s) %s", cado_revision_string, argv[0]);
1610         for (int i = 1; i < argc; i++)
1611             fprintf (stream, " %s", argv[i]);
1612         fprintf (stream, "\n");
1613     }
1614     if (verbose_enabled(CADO_VERBOSE_PRINT_MODIFIED_FILES)) {
1615         if (strlen(cado_modified_files) > 1)
1616           fprintf (stream, "# List of modified files in working directory and "
1617                    "their SHA1 sum:\n%s", cado_modified_files);
1618     }
1619     if (verbose_enabled(CADO_VERBOSE_PRINT_COMPILATION_INFO)) {
1620 #ifdef  __GNUC__
1621 #ifndef __ICC
1622         fprintf (stream, "# Compiled with gcc " __VERSION__ "\n");
1623 #else
1624         /* icc defines __GNUC__ too */
1625         fprintf (stream, "# Compiled with icc %d.%d.%d (gcc version %d.%d.%d compatibility)\n",
1626                  __ICC / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE,
1627                  __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
1628 #endif
1629 
1630         /* Apple Clang (based on llvm) identifies itself as a flavour of GNUC 4.2.1
1631            but seems to compile CADO-NFS properly
1632            $ echo | clang -dD -E -pipe -
1633 # 1 "<stdin>"
1634 # 1 "<stdin>" 1
1635 # 1 "<built-in>" 1
1636 # 1 "<built-in>" 3
1637 #define __llvm__ 1
1638 #define __clang__ 1
1639 #define __clang_major__ 4
1640 #define __clang_minor__ 1
1641 #define __clang_patchlevel__ 0
1642 #define __clang_version__ "4.1 ((tags/Apple/clang-421.11.66))"
1643 #define __GNUC_MINOR__ 2
1644 #define __GNUC_PATCHLEVEL__ 1
1645 #define __GNUC__ 4
1646 ...
1647 */
1648 
1649 #if GNUC_VERSION_ATLEAST(4,1,2) && GNUC_VERSION_ATMOST(4,2,2) \
1650         && ! (__llvm__ || __clang__)
1651         fprintf (stream, "# WARNING: this version of GCC is known to miscompile CADO-NFS. See https://gforge.inria.fr/tracker/index.php?func=detail&aid=14490\n");
1652 #endif
1653 #endif
1654         fprintf(stream, "# Compilation flags (C) " CFLAGS "\n");
1655         fprintf(stream, "# Compilation flags (C++) " CXXFLAGS "\n");
1656     }
1657 }
1658