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) ¶mcmp);
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