1 /*
2 * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20 * \file
21 * Option reading and saving (implementation).
22 *
23 * Options are stored in the format key:value, one per line.
24 *
25 * For bool options, value is "0" or "1".
26 */
27
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <stdbool.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <strings.h>
34
35 #include "netsurf/plot_style.h"
36 #include "utils/errors.h"
37 #include "utils/log.h"
38 #include "utils/utils.h"
39 #include "utils/nsoption.h"
40
41 /** Length of buffer used to read lines from input file */
42 #define NSOPTION_MAX_LINE_LEN 1024
43
44 struct nsoption_s *nsoptions = NULL;
45 struct nsoption_s *nsoptions_default = NULL;
46
47 #define NSOPTION_BOOL(NAME, DEFAULT) \
48 { #NAME, sizeof(#NAME) - 1, OPTION_BOOL, { .b = DEFAULT } },
49
50 #define NSOPTION_STRING(NAME, DEFAULT) \
51 { #NAME, sizeof(#NAME) - 1, OPTION_STRING, { .cs = DEFAULT } },
52
53 #define NSOPTION_INTEGER(NAME, DEFAULT) \
54 { #NAME, sizeof(#NAME) - 1, OPTION_INTEGER, { .i = DEFAULT } },
55
56 #define NSOPTION_UINT(NAME, DEFAULT) \
57 { #NAME, sizeof(#NAME) - 1, OPTION_UINT, { .u = DEFAULT } },
58
59 #define NSOPTION_COLOUR(NAME, DEFAULT) \
60 { #NAME, sizeof(#NAME) - 1, OPTION_COLOUR, { .c = DEFAULT } },
61
62 /** The table of compiled in default options */
63 static struct nsoption_s defaults[] = {
64 #include "desktop/options.h"
65
66 #if defined(riscos)
67 #include "riscos/options.h"
68 #elif defined(nsgtk)
69 #include "gtk/options.h"
70 #elif defined(nsbeos)
71 #include "beos/options.h"
72 #elif defined(nsamiga)
73 #include "amiga/options.h"
74 #elif defined(nsframebuffer)
75 #include "framebuffer/options.h"
76 #elif defined(nsatari)
77 #include "atari/options.h"
78 #elif defined(nsmonkey)
79 #include "monkey/options.h"
80 #elif defined(nswin32)
81 #include "windows/options.h"
82 #endif
83 { NULL, 0, OPTION_INTEGER, { 0 } }
84 };
85
86 #undef NSOPTION_BOOL
87 #undef NSOPTION_STRING
88 #undef NSOPTION_INTEGER
89 #undef NSOPTION_UINT
90 #undef NSOPTION_COLOUR
91
92 /**
93 * Set an option value based on a string
94 */
95 static bool
strtooption(const char * value,struct nsoption_s * option)96 strtooption(const char *value, struct nsoption_s *option)
97 {
98 bool ret = true;
99 colour rgbcolour; /* RRGGBB */
100
101 switch (option->type) {
102 case OPTION_BOOL:
103 option->value.b = (value[0] == '1');
104 break;
105
106 case OPTION_INTEGER:
107 option->value.i = atoi(value);
108 break;
109
110 case OPTION_UINT:
111 option->value.u = strtoul(value, NULL, 0);
112 break;
113
114 case OPTION_COLOUR:
115 if (sscanf(value, "%x", &rgbcolour) == 1) {
116 option->value.c = (((0x000000FF & rgbcolour) << 16) |
117 ((0x0000FF00 & rgbcolour) << 0) |
118 ((0x00FF0000 & rgbcolour) >> 16));
119 }
120 break;
121
122 case OPTION_STRING:
123 if (option->value.s != NULL) {
124 free(option->value.s);
125 }
126
127 if (*value == 0) {
128 /* do not allow empty strings in text options */
129 option->value.s = NULL;
130 } else {
131 option->value.s = strdup(value);
132 }
133 break;
134
135 default:
136 ret = false;
137 break;
138 }
139
140 return ret;
141 }
142
143 /* validate options to sane values */
nsoption_validate(struct nsoption_s * opts,struct nsoption_s * defs)144 static void nsoption_validate(struct nsoption_s *opts, struct nsoption_s *defs)
145 {
146 int cloop;
147 bool black = true;
148
149 if (opts[NSOPTION_treeview_font_size].value.i < 50) {
150 opts[NSOPTION_treeview_font_size].value.i = 50;
151 }
152
153 if (opts[NSOPTION_treeview_font_size].value.i > 1000) {
154 opts[NSOPTION_treeview_font_size].value.i = 1000;
155 }
156
157 if (opts[NSOPTION_font_size].value.i < 50) {
158 opts[NSOPTION_font_size].value.i = 50;
159 }
160
161 if (opts[NSOPTION_font_size].value.i > 1000) {
162 opts[NSOPTION_font_size].value.i = 1000;
163 }
164
165 if (opts[NSOPTION_font_min_size].value.i < 10) {
166 opts[NSOPTION_font_min_size].value.i = 10;
167 }
168
169 if (opts[NSOPTION_font_min_size].value.i > 500) {
170 opts[NSOPTION_font_min_size].value.i = 500;
171 }
172
173 if (opts[NSOPTION_memory_cache_size].value.i < 0) {
174 opts[NSOPTION_memory_cache_size].value.i = 0;
175 }
176
177 /* to aid migration from old, broken, configuration files this
178 * checks to see if all the system colours are set to black
179 * and returns them to defaults instead
180 */
181
182 for (cloop = NSOPTION_SYS_COLOUR_START;
183 cloop <= NSOPTION_SYS_COLOUR_END;
184 cloop++) {
185 if (opts[cloop].value.c != 0) {
186 black = false;
187 break;
188 }
189 }
190 if (black == true && defs != NULL) {
191 for (cloop = NSOPTION_SYS_COLOUR_START;
192 cloop <= NSOPTION_SYS_COLOUR_END;
193 cloop++) {
194 opts[cloop].value.c = defs[cloop].value.c;
195 }
196 }
197
198 /* To aid migration and ensure that timeouts don't go crazy,
199 * ensure that (a) we allow at least 1 attempt and
200 * (b) the total time that we spend should not exceed 60s
201 */
202 if (opts[NSOPTION_max_retried_fetches].value.u == 0)
203 opts[NSOPTION_max_retried_fetches].value.u = 1;
204 if (opts[NSOPTION_curl_fetch_timeout].value.u < 5)
205 opts[NSOPTION_curl_fetch_timeout].value.u = 5;
206 if (opts[NSOPTION_curl_fetch_timeout].value.u > 60)
207 opts[NSOPTION_curl_fetch_timeout].value.u = 60;
208 while (((opts[NSOPTION_curl_fetch_timeout].value.u *
209 opts[NSOPTION_max_retried_fetches].value.u) > 60) &&
210 (opts[NSOPTION_max_retried_fetches].value.u > 1))
211 opts[NSOPTION_max_retried_fetches].value.u--;
212
213 /* We ignore the result because we can't fail to validate. Yay */
214 (void)nslog_set_filter_by_options();
215 }
216
217 /**
218 * Determines if an option is different between two option tables.
219 *
220 * @param opts The first table to compare.
221 * @param defs The second table to compare.
222 * @param entry The option to compare.
223 * @return true if the option differs false if not.
224 */
225 static bool
nsoption_is_set(const struct nsoption_s * opts,const struct nsoption_s * defs,const enum nsoption_e entry)226 nsoption_is_set(const struct nsoption_s *opts,
227 const struct nsoption_s *defs,
228 const enum nsoption_e entry)
229 {
230 bool ret = false;
231
232 switch (opts[entry].type) {
233 case OPTION_BOOL:
234 if (opts[entry].value.b != defs[entry].value.b) {
235 ret = true;
236 }
237 break;
238
239 case OPTION_INTEGER:
240 if (opts[entry].value.i != defs[entry].value.i) {
241 ret = true;
242 }
243 break;
244
245 case OPTION_UINT:
246 if (opts[entry].value.u != defs[entry].value.u) {
247 ret = true;
248 }
249 break;
250
251 case OPTION_COLOUR:
252 if (opts[entry].value.c != defs[entry].value.c) {
253 ret = true;
254 }
255 break;
256
257 case OPTION_STRING:
258 /* set if:
259 * - defs is null.
260 * - default is null but value is not.
261 * - default and value pointers are different
262 * (acts as a null check because of previous check)
263 * and the strings content differ.
264 */
265 if (((defs[entry].value.s == NULL) &&
266 (opts[entry].value.s != NULL)) ||
267 ((defs[entry].value.s != NULL) &&
268 (opts[entry].value.s == NULL)) ||
269 ((defs[entry].value.s != opts[entry].value.s) &&
270 (strcmp(opts[entry].value.s, defs[entry].value.s) != 0))) {
271 ret = true;
272 }
273 break;
274
275 }
276 return ret;
277 }
278
279 /**
280 * Output choices to file stream
281 *
282 * @param fp The file stream to write to.
283 * @param opts The options table to write.
284 * @param defs The default value table to compare with.
285 * @param all Output all entries not just ones changed from defaults
286 */
287 static nserror
nsoption_output(FILE * fp,struct nsoption_s * opts,struct nsoption_s * defs,bool all)288 nsoption_output(FILE *fp,
289 struct nsoption_s *opts,
290 struct nsoption_s *defs,
291 bool all)
292 {
293 unsigned int entry; /* index to option being output */
294 colour rgbcolour; /* RRGGBB */
295
296 for (entry = 0; entry < NSOPTION_LISTEND; entry++) {
297 if ((all == false) &&
298 (nsoption_is_set(opts, defs, entry) == false)) {
299 continue;
300 }
301
302 switch (opts[entry].type) {
303 case OPTION_BOOL:
304 fprintf(fp, "%s:%c\n",
305 opts[entry].key,
306 opts[entry].value.b ? '1' : '0');
307 break;
308
309 case OPTION_INTEGER:
310 fprintf(fp, "%s:%i\n",
311 opts[entry].key,
312 opts[entry].value.i);
313
314 break;
315
316 case OPTION_UINT:
317 fprintf(fp, "%s:%u\n",
318 opts[entry].key,
319 opts[entry].value.u);
320 break;
321
322 case OPTION_COLOUR:
323 rgbcolour = (((0x000000FF & opts[entry].value.c) << 16) |
324 ((0x0000FF00 & opts[entry].value.c) << 0) |
325 ((0x00FF0000 & opts[entry].value.c) >> 16));
326 fprintf(fp, "%s:%06x\n",
327 opts[entry].key,
328 rgbcolour);
329
330 break;
331
332 case OPTION_STRING:
333 fprintf(fp, "%s:%s\n",
334 opts[entry].key,
335 ((opts[entry].value.s == NULL) ||
336 (*opts[entry].value.s == 0)) ? "" : opts[entry].value.s);
337
338 break;
339 }
340 }
341
342 return NSERROR_OK;
343 }
344
345 /**
346 * Output an option value into a string, in HTML format.
347 *
348 * @param option The option to output the value of.
349 * @param size The size of the string buffer.
350 * @param pos The current position in string
351 * @param string The string in which to output the value.
352 * @return The number of bytes written to string or -1 on error
353 */
354 static size_t
nsoption_output_value_html(struct nsoption_s * option,size_t size,size_t pos,char * string)355 nsoption_output_value_html(struct nsoption_s *option,
356 size_t size,
357 size_t pos,
358 char *string)
359 {
360 size_t slen = 0; /* length added to string */
361 colour rgbcolour; /* RRGGBB */
362
363 switch (option->type) {
364 case OPTION_BOOL:
365 slen = snprintf(string + pos,
366 size - pos,
367 "%s",
368 option->value.b ? "true" : "false");
369 break;
370
371 case OPTION_INTEGER:
372 slen = snprintf(string + pos,
373 size - pos,
374 "%i",
375 option->value.i);
376 break;
377
378 case OPTION_UINT:
379 slen = snprintf(string + pos,
380 size - pos,
381 "%u",
382 option->value.u);
383 break;
384
385 case OPTION_COLOUR:
386 rgbcolour = colour_rb_swap(option->value.c);
387 slen = snprintf(string + pos,
388 size - pos,
389 "<span style=\"font-family:Monospace;\">"
390 "#%06X"
391 "</span> "
392 "<span style=\"background-color: #%06x; "
393 "border: 1px solid #%06x; "
394 "display: inline-block; "
395 "width: 1em; height: 1em;\">"
396 "</span>",
397 rgbcolour,
398 rgbcolour,
399 colour_to_bw_furthest(rgbcolour));
400 break;
401
402 case OPTION_STRING:
403 if (option->value.s != NULL) {
404 slen = snprintf(string + pos, size - pos, "%s",
405 option->value.s);
406 } else {
407 slen = snprintf(string + pos, size - pos,
408 "<span class=\"null-content\">NULL"
409 "</span>");
410 }
411 break;
412 }
413
414 return slen;
415 }
416
417
418 /**
419 * Output an option value into a string, in plain text format.
420 *
421 * @param option The option to output the value of.
422 * @param size The size of the string buffer.
423 * @param pos The current position in string
424 * @param string The string in which to output the value.
425 * @return The number of bytes written to string or -1 on error
426 */
427 static size_t
nsoption_output_value_text(struct nsoption_s * option,size_t size,size_t pos,char * string)428 nsoption_output_value_text(struct nsoption_s *option,
429 size_t size,
430 size_t pos,
431 char *string)
432 {
433 size_t slen = 0; /* length added to string */
434 colour rgbcolour; /* RRGGBB */
435
436 switch (option->type) {
437 case OPTION_BOOL:
438 slen = snprintf(string + pos,
439 size - pos,
440 "%c",
441 option->value.b ? '1' : '0');
442 break;
443
444 case OPTION_INTEGER:
445 slen = snprintf(string + pos,
446 size - pos,
447 "%i",
448 option->value.i);
449 break;
450
451 case OPTION_UINT:
452 slen = snprintf(string + pos,
453 size - pos,
454 "%u",
455 option->value.u);
456 break;
457
458 case OPTION_COLOUR:
459 rgbcolour = (((0x000000FF & option->value.c) << 16) |
460 ((0x0000FF00 & option->value.c) << 0) |
461 ((0x00FF0000 & option->value.c) >> 16));
462 slen = snprintf(string + pos, size - pos, "%06x", rgbcolour);
463 break;
464
465 case OPTION_STRING:
466 if (option->value.s != NULL) {
467 slen = snprintf(string + pos,
468 size - pos,
469 "%s",
470 option->value.s);
471 }
472 break;
473 }
474
475 return slen;
476 }
477
478 /**
479 * Duplicates an option table.
480 *
481 * Allocates a new option table and copies an existing one into it.
482 *
483 * \param[in] src The source table to copy
484 * \param[out] pdst The output table
485 * \return NSERROR_OK on success or appropriate error code.
486 */
487 static nserror
nsoption_dup(struct nsoption_s * src,struct nsoption_s ** pdst)488 nsoption_dup(struct nsoption_s *src, struct nsoption_s **pdst)
489 {
490 struct nsoption_s *dst;
491 dst = malloc(sizeof(defaults));
492 if (dst == NULL) {
493 return NSERROR_NOMEM;
494 }
495 *pdst = dst;
496
497 /* copy the source table into the destination table */
498 memcpy(dst, src, sizeof(defaults));
499
500 while (src->key != NULL) {
501 if ((src->type == OPTION_STRING) &&
502 (src->value.s != NULL)) {
503 dst->value.s = strdup(src->value.s);
504 }
505 src++;
506 dst++;
507 }
508
509 return NSERROR_OK;
510 }
511
512 /**
513 * frees an option table.
514 *
515 * Iterates through an option table a freeing resources as required
516 * finally freeing the option table itself.
517 *
518 * @param opts The option table to free.
519 */
520 static nserror
nsoption_free(struct nsoption_s * opts)521 nsoption_free(struct nsoption_s *opts)
522 {
523 struct nsoption_s *cur; /* option being freed */
524
525 if (opts == NULL) {
526 return NSERROR_BAD_PARAMETER;
527 }
528
529 cur = opts;
530
531 while (cur->key != NULL) {
532 if ((cur->type == OPTION_STRING) && (cur->value.s != NULL)) {
533 free(cur->value.s);
534 }
535 cur++;
536 }
537 free(opts);
538
539 return NSERROR_OK;
540 }
541
542
543 /* exported interface documented in utils/nsoption.h */
544 nserror
nsoption_init(nsoption_set_default_t * set_defaults,struct nsoption_s ** popts,struct nsoption_s ** pdefs)545 nsoption_init(nsoption_set_default_t *set_defaults,
546 struct nsoption_s **popts,
547 struct nsoption_s **pdefs)
548 {
549 nserror ret;
550 struct nsoption_s *defs;
551 struct nsoption_s *opts;
552
553 ret = nsoption_dup(&defaults[0], &defs);
554 if (ret != NSERROR_OK) {
555 return ret;
556 }
557
558 /* update the default table */
559 if (set_defaults != NULL) {
560 /** @todo it would be better if the frontends actually
561 * set values in the passed in table instead of
562 * assuming the global one.
563 */
564 opts = nsoptions;
565 nsoptions = defs;
566
567 ret = set_defaults(defs);
568
569 if (ret != NSERROR_OK) {
570 nsoptions = opts;
571 nsoption_free(defs);
572 return ret;
573 }
574 }
575
576 /* copy the default values into the working set */
577 ret = nsoption_dup(defs, &opts);
578 if (ret != NSERROR_OK) {
579 nsoption_free(defs);
580 return ret;
581 }
582
583 /* return values if wanted */
584 if (popts != NULL) {
585 *popts = opts;
586 } else {
587 nsoptions = opts;
588 }
589
590 if (pdefs != NULL) {
591 *pdefs = defs;
592 } else {
593 nsoptions_default = defs;
594 }
595
596 return NSERROR_OK;
597 }
598
599 /* exported interface documented in utils/nsoption.h */
nsoption_finalise(struct nsoption_s * opts,struct nsoption_s * defs)600 nserror nsoption_finalise(struct nsoption_s *opts, struct nsoption_s *defs)
601 {
602 nserror res;
603
604 /* check to see if global table selected */
605 if (opts == NULL) {
606 res = nsoption_free(nsoptions);
607 if (res == NSERROR_OK) {
608 nsoptions = NULL;
609 }
610 } else {
611 res = nsoption_free(opts);
612 }
613 if (res != NSERROR_OK) {
614 return res;
615 }
616
617 /* check to see if global table selected */
618 if (defs == NULL) {
619 res = nsoption_free(nsoptions_default);
620 if (res == NSERROR_OK) {
621 nsoptions_default = NULL;
622 }
623 } else {
624 res = nsoption_free(defs);
625 }
626
627 return res;
628 }
629
630
631 /* exported interface documented in utils/nsoption.h */
632 nserror
nsoption_read(const char * path,struct nsoption_s * opts)633 nsoption_read(const char *path, struct nsoption_s *opts)
634 {
635 char s[NSOPTION_MAX_LINE_LEN];
636 FILE *fp;
637 struct nsoption_s *defs;
638
639 if (path == NULL) {
640 return NSERROR_BAD_PARAMETER;
641 }
642
643 /* check to see if global table selected */
644 if (opts == NULL) {
645 opts = nsoptions;
646 }
647
648 /** @todo is this and API bug not being a parameter */
649 defs = nsoptions_default;
650
651 if ((opts == NULL) || (defs == NULL)) {
652 return NSERROR_BAD_PARAMETER;
653 }
654
655 fp = fopen(path, "r");
656 if (!fp) {
657 NSLOG(netsurf, INFO, "Failed to open file '%s'", path);
658 return NSERROR_NOT_FOUND;
659 }
660
661 NSLOG(netsurf, INFO, "Successfully opened '%s' for Options file",
662 path);
663
664 while (fgets(s, NSOPTION_MAX_LINE_LEN, fp)) {
665 char *colon, *value;
666 unsigned int idx;
667
668 if ((s[0] == 0) || (s[0] == '#')) {
669 continue;
670 }
671
672 colon = strchr(s, ':');
673 if (colon == 0) {
674 continue;
675 }
676
677 s[strlen(s) - 1] = 0; /* remove \n at end */
678 *colon = 0; /* terminate key */
679 value = colon + 1;
680
681 for (idx = 0; opts[idx].key != NULL; idx++) {
682 if (strcasecmp(s, opts[idx].key) != 0) {
683 continue;
684 }
685
686 strtooption(value, &opts[idx]);
687 break;
688 }
689 }
690
691 fclose(fp);
692
693 nsoption_validate(opts, defs);
694
695 return NSERROR_OK;
696 }
697
698
699 /* exported interface documented in utils/nsoption.h */
700 nserror
nsoption_write(const char * path,struct nsoption_s * opts,struct nsoption_s * defs)701 nsoption_write(const char *path,
702 struct nsoption_s *opts,
703 struct nsoption_s *defs)
704 {
705 FILE *fp;
706 nserror ret;
707
708 if (path == NULL) {
709 return NSERROR_BAD_PARAMETER;
710 }
711
712 /* check to see if global table selected */
713 if (opts == NULL) {
714 opts = nsoptions;
715 }
716
717 /* check to see if global table selected */
718 if (defs == NULL) {
719 defs = nsoptions_default;
720 }
721
722 if ((opts == NULL) || (defs == NULL)) {
723 return NSERROR_BAD_PARAMETER;
724 }
725
726 fp = fopen(path, "w");
727 if (!fp) {
728 NSLOG(netsurf, INFO, "failed to open file '%s' for writing",
729 path);
730 return NSERROR_NOT_FOUND;
731 }
732
733 ret = nsoption_output(fp, opts, defs, false);
734
735 fclose(fp);
736
737 return ret;
738 }
739
740 /* exported interface documented in utils/nsoption.h */
741 nserror
nsoption_dump(FILE * outf,struct nsoption_s * opts)742 nsoption_dump(FILE *outf, struct nsoption_s *opts)
743 {
744 if (outf == NULL) {
745 return NSERROR_BAD_PARAMETER;
746 }
747
748 /* check to see if global table selected and available */
749 if (opts == NULL) {
750 opts = nsoptions;
751 }
752 if (opts == NULL) {
753 return NSERROR_BAD_PARAMETER;
754 }
755
756 return nsoption_output(outf, opts, NULL, true);
757 }
758
759
760 /* exported interface documented in utils/nsoption.h */
761 nserror
nsoption_commandline(int * pargc,char ** argv,struct nsoption_s * opts)762 nsoption_commandline(int *pargc, char **argv, struct nsoption_s *opts)
763 {
764 char *arg;
765 char *val;
766 int arglen;
767 int idx = 1;
768 int mv_loop;
769 unsigned int entry_loop;
770
771 if ((pargc == NULL) || (argv == NULL)) {
772 return NSERROR_BAD_PARAMETER;
773 }
774
775 /* check to see if global table selected and available */
776 if (opts == NULL) {
777 opts = nsoptions;
778 }
779 if (opts == NULL) {
780 return NSERROR_BAD_PARAMETER;
781 }
782
783 while (idx < *pargc) {
784 arg = argv[idx];
785 arglen = strlen(arg);
786
787 /* check we have an option */
788 /* option must start -- and be as long as the shortest option*/
789 if ((arglen < (2+5) ) || (arg[0] != '-') || (arg[1] != '-'))
790 break;
791
792 arg += 2; /* skip -- */
793
794 val = strchr(arg, '=');
795 if (val == NULL) {
796 /* no equals sign - next parameter is val */
797 idx++;
798 if (idx >= *pargc)
799 break;
800 val = argv[idx];
801 } else {
802 /* equals sign */
803 arglen = val - arg ;
804 val++;
805 }
806
807 /* arg+arglen is the option to set, val is the value */
808
809 NSLOG(netsurf, INFO, "%.*s = %s", arglen, arg, val);
810
811 for (entry_loop = 0;
812 entry_loop < NSOPTION_LISTEND;
813 entry_loop++) {
814 if (strncmp(arg, opts[entry_loop].key, arglen) == 0) {
815 strtooption(val, opts + entry_loop);
816 break;
817 }
818 }
819
820 idx++;
821 }
822
823 /* remove processed options from argv */
824 for (mv_loop=0; mv_loop < (*pargc - idx); mv_loop++) {
825 argv[mv_loop + 1] = argv[mv_loop + idx];
826 }
827 *pargc -= (idx - 1);
828
829 nsoption_validate(opts, nsoptions_default);
830
831 return NSERROR_OK;
832 }
833
834 /* exported interface documented in options.h */
835 int
nsoption_snoptionf(char * string,size_t size,enum nsoption_e option_idx,const char * fmt)836 nsoption_snoptionf(char *string,
837 size_t size,
838 enum nsoption_e option_idx,
839 const char *fmt)
840 {
841 size_t slen = 0; /* current output string length */
842 int fmtc = 0; /* current index into format string */
843 struct nsoption_s *option;
844
845 if (fmt == NULL) {
846 return -1;
847 }
848
849 if (option_idx >= NSOPTION_LISTEND) {
850 return -1;
851 }
852
853 if (nsoptions == NULL) {
854 return -1;
855 }
856
857 option = &nsoptions[option_idx]; /* assume the global table */
858 if (option == NULL || option->key == NULL) {
859 return -1;
860 }
861
862 while ((slen < size) && (fmt[fmtc] != 0)) {
863 if (fmt[fmtc] == '%') {
864 fmtc++;
865 switch (fmt[fmtc]) {
866 case 'k':
867 slen += snprintf(string + slen,
868 size - slen,
869 "%s",
870 option->key);
871 break;
872
873 case 'p':
874 if (nsoption_is_set(nsoptions,
875 nsoptions_default,
876 option_idx)) {
877 slen += snprintf(string + slen,
878 size - slen,
879 "user");
880 } else {
881 slen += snprintf(string + slen,
882 size - slen,
883 "default");
884 }
885 break;
886
887 case 't':
888 switch (option->type) {
889 case OPTION_BOOL:
890 slen += snprintf(string + slen,
891 size - slen,
892 "boolean");
893 break;
894
895 case OPTION_INTEGER:
896 slen += snprintf(string + slen,
897 size - slen,
898 "integer");
899 break;
900
901 case OPTION_UINT:
902 slen += snprintf(string + slen,
903 size - slen,
904 "unsigned integer");
905 break;
906
907 case OPTION_COLOUR:
908 slen += snprintf(string + slen,
909 size - slen,
910 "colour");
911 break;
912
913 case OPTION_STRING:
914 slen += snprintf(string + slen,
915 size - slen,
916 "string");
917 break;
918
919 }
920 break;
921
922
923 case 'V':
924 slen += nsoption_output_value_html(option,
925 size,
926 slen,
927 string);
928 break;
929 case 'v':
930 slen += nsoption_output_value_text(option,
931 size,
932 slen,
933 string);
934 break;
935 }
936 fmtc++;
937 } else {
938 string[slen] = fmt[fmtc];
939 slen++;
940 fmtc++;
941 }
942 }
943
944 /* Ensure that we NUL-terminate the output */
945 string[min(slen, size - 1)] = '\0';
946
947 return slen;
948 }
949
950 /* exported interface documented in options.h */
951 nserror
nsoption_set_tbl_charp(struct nsoption_s * opts,enum nsoption_e option_idx,char * s)952 nsoption_set_tbl_charp(struct nsoption_s *opts,
953 enum nsoption_e option_idx,
954 char *s)
955 {
956 struct nsoption_s *option;
957
958 option = &opts[option_idx];
959
960 /* ensure it is a string option */
961 if (option->type != OPTION_STRING) {
962 return NSERROR_BAD_PARAMETER;
963 }
964
965 /* free any existing string */
966 if (option->value.s != NULL) {
967 free(option->value.s);
968 }
969
970 option->value.s = s;
971
972 /* check for empty string */
973 if ((option->value.s != NULL) && (*option->value.s == 0)) {
974 free(option->value.s);
975 option->value.s = NULL;
976 }
977 return NSERROR_OK;
978 }
979