Remove overengineering, part 1/many
[coreboot.git] / util / nvramtool / nvramtool.c
1 /*****************************************************************************\
2  * nvramtool.c
3  *****************************************************************************
4  *  Copyright (C) 2002-2005 The Regents of the University of California.
5  *  Produced at the Lawrence Livermore National Laboratory.
6  *  Written by Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>.
7  *  UCRL-CODE-2003-012
8  *  All rights reserved.
9  *
10  *  This file is part of nvramtool, a utility for reading/writing coreboot
11  *  parameters and displaying information from the coreboot table.
12  *  For details, see http://coreboot.org/nvramtool.
13  *
14  *  Please also read the file DISCLAIMER which is included in this software
15  *  distribution.
16  *
17  *  This program is free software; you can redistribute it and/or modify it
18  *  under the terms of the GNU General Public License (as published by the
19  *  Free Software Foundation) version 2, dated June 1991.
20  *
21  *  This program is distributed in the hope that it will be useful, but
22  *  WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
23  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the terms and
24  *  conditions of the GNU General Public License for more details.
25  *
26  *  You should have received a copy of the GNU General Public License along
27  *  with this program; if not, write to the Free Software Foundation, Inc.,
28  *  51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
29 \*****************************************************************************/
30
31 #include "common.h"
32 #include "opts.h"
33 #include "lbtable.h"
34 #include "layout.h"
35 #include "layout_file.h"
36 #include "input_file.h"
37 #include "cmos_ops.h"
38 #include "cmos_lowlevel.h"
39 #include "reg_expr.h"
40 #include "hexdump.h"
41
42 typedef void (*op_fn_t) (void);
43
44 static void op_show_version(void);
45 static void op_show_usage(void);
46 static void op_lbtable_show_info(void);
47 static void op_lbtable_dump(void);
48 static void op_show_param_values(void);
49 static void op_cmos_show_one_param(void);
50 static void op_cmos_show_all_params(void);
51 static void op_cmos_set_one_param(void);
52 static void op_cmos_set_params_stdin(void);
53 static void op_cmos_set_params_file(void);
54 static void op_cmos_checksum(void);
55 static void op_show_layout(void);
56 static void op_write_cmos_dump(void);
57 static void op_read_cmos_dump(void);
58 static void op_show_cmos_hex_dump(void);
59 static void op_show_cmos_dumpfile(void);
60 static int list_one_param(const char name[], int show_name);
61 static int list_all_params(void);
62 static void list_param_enums(const char name[]);
63 static void set_one_param(const char name[], const char value[]);
64 static void set_params(FILE * f);
65 static void parse_assignment(char arg[], const char **name, const char **value);
66 static int list_cmos_entry(const cmos_entry_t * e, int show_name);
67 static uint16_t convert_checksum_value(const char value[]);
68
69 static const op_fn_t op_fns[] = { op_show_version,
70         op_show_usage,
71         op_lbtable_show_info,
72         op_lbtable_dump,
73         op_show_param_values,
74         op_cmos_show_one_param,
75         op_cmos_show_all_params,
76         op_cmos_set_one_param,
77         op_cmos_set_params_stdin,
78         op_cmos_set_params_file,
79         op_cmos_checksum,
80         op_show_layout,
81         op_write_cmos_dump,
82         op_read_cmos_dump,
83         op_show_cmos_hex_dump,
84         op_show_cmos_dumpfile
85 };
86
87 static const hexdump_format_t cmos_dump_format =
88     { 16, 2, "", " | ", " ", " | ", '.' };
89
90 /****************************************************************************
91  * main
92  ****************************************************************************/
93 int main(int argc, char *argv[])
94 {
95         cmos_layout_get_fn_t fn;
96
97         parse_nvramtool_args(argc, argv);
98
99         if (nvramtool_op_modifiers[NVRAMTOOL_MOD_USE_CMOS_LAYOUT_FILE].found) {
100                 set_layout_filename(nvramtool_op_modifiers
101                                     [NVRAMTOOL_MOD_USE_CMOS_LAYOUT_FILE].param);
102                 fn = get_layout_from_file;
103         } else
104                 fn = get_layout_from_cmos_table;
105
106         register_cmos_layout_get_fn(fn);
107         op_fns[nvramtool_op.op] ();
108         return 0;
109 }
110
111 /****************************************************************************
112  * op_show_version
113  *
114  * -v
115  *
116  * Show version information for this program.
117  ****************************************************************************/
118 static void op_show_version(void)
119 {
120         printf("This is %s version %s.\n", prog_name, prog_version);
121 }
122
123 /****************************************************************************
124  * op_show_usage
125  *
126  * -h
127  *
128  * Show a usage message for this program.
129  ****************************************************************************/
130 static void op_show_usage(void)
131 {
132         usage(stdout);
133 }
134
135 /****************************************************************************
136  * op_lbtable_show_info
137  *
138  * -l [ARG]
139  *
140  * If ARG is present, show coreboot table information specified by ARG.
141  * Else show all possible values for ARG.
142  ****************************************************************************/
143 static void op_lbtable_show_info(void)
144 {
145         if (nvramtool_op.param == NULL)
146                 list_lbtable_choices();
147         else {
148                 get_lbtable();
149                 list_lbtable_item(nvramtool_op.param);
150         }
151 }
152
153 /****************************************************************************
154  * op_lbtable_dump
155  *
156  * -d
157  *
158  * Do low-level dump of coreboot table.
159  ****************************************************************************/
160 static void op_lbtable_dump(void)
161 {
162         get_lbtable();
163         dump_lbtable();
164 }
165
166 /****************************************************************************
167  * op_show_param_values
168  *
169  * -e NAME option
170  *
171  * Show all possible values for parameter NAME.
172  ****************************************************************************/
173 static void op_show_param_values(void)
174 {
175         get_cmos_layout();
176         list_param_enums(nvramtool_op.param);
177 }
178
179 /****************************************************************************
180  * op_cmos_show_one_param
181  *
182  * [-n] -r NAME
183  *
184  * Show parameter NAME.  If -n is specified, show value only.  Else show name
185  * and value.
186  ****************************************************************************/
187 static void op_cmos_show_one_param(void)
188 {
189         int result;
190
191         get_cmos_layout();
192         result = list_one_param(nvramtool_op.param,
193                                 !nvramtool_op_modifiers
194                                 [NVRAMTOOL_MOD_SHOW_VALUE_ONLY].found);
195         cmos_checksum_verify();
196
197         if (result)
198                 exit(1);
199 }
200
201 /****************************************************************************
202  * op_cmos_show_all_params
203  *
204  * -a
205  *
206  * Show names and values for all parameters.
207  ****************************************************************************/
208 static void op_cmos_show_all_params(void)
209 {
210         int result;
211
212         get_cmos_layout();
213         result = list_all_params();
214         cmos_checksum_verify();
215
216         if (result)
217                 exit(1);
218 }
219
220 /****************************************************************************
221  * op_cmos_set_one_param
222  *
223  * -w NAME=VALUE
224  *
225  * Set parameter NAME to VALUE.
226  ****************************************************************************/
227 static void op_cmos_set_one_param(void)
228 {
229         const char *name, *value;
230
231         get_cmos_layout();
232
233         /* Separate 'NAME=VALUE' syntax into two strings representing NAME and
234          * VALUE.
235          */
236         parse_assignment(nvramtool_op.param, &name, &value);
237
238         set_one_param(name, value);
239 }
240
241 /****************************************************************************
242  * op_cmos_set_params_stdin
243  *
244  * -i
245  *
246  * Set parameters according to standard input.
247  ****************************************************************************/
248 static void op_cmos_set_params_stdin(void)
249 {
250         get_cmos_layout();
251         set_params(stdin);
252 }
253
254 /****************************************************************************
255  * op_cmos_set_params_file
256  *
257  * -p INPUT_FILE
258  *
259  * Set parameters according to INPUT_FILE.
260  ****************************************************************************/
261 static void op_cmos_set_params_file(void)
262 {
263         FILE *f;
264
265         if ((f = fopen(nvramtool_op.param, "r")) == NULL) {
266                 fprintf(stderr, "%s: Can not open file %s for reading: %s\n",
267                         prog_name, nvramtool_op.param, strerror(errno));
268                 exit(1);
269         }
270
271         get_cmos_layout();
272         set_params(f);
273         fclose(f);
274 }
275
276 /****************************************************************************
277  * op_cmos_checksum
278  *
279  * -c [VALUE]
280  *
281  * If VALUE is present, set coreboot CMOS checksum to VALUE.  Else show
282  * checksum value.
283  ****************************************************************************/
284 static void op_cmos_checksum(void)
285 {
286         uint16_t checksum;
287
288         get_cmos_layout();
289
290         if (nvramtool_op.param == NULL) {
291                 set_iopl(3);
292                 checksum = cmos_checksum_read();
293                 set_iopl(0);
294                 printf("0x%x\n", checksum);
295         } else {
296                 checksum = convert_checksum_value(nvramtool_op.param);
297                 set_iopl(3);
298                 cmos_checksum_write(checksum);
299                 set_iopl(0);
300         }
301 }
302
303 /****************************************************************************
304  * op_show_layout
305  *
306  * -Y
307  *
308  * Write CMOS layout information to standard output.
309  ****************************************************************************/
310 static void op_show_layout(void)
311 {
312         get_cmos_layout();
313         write_cmos_layout(stdout);
314 }
315
316 /****************************************************************************
317  * op_write_cmos_dump
318  *
319  * -b OUTPUT_FILE
320  *
321  * Write the contents of CMOS memory to a binary file.
322  ****************************************************************************/
323 static void op_write_cmos_dump(void)
324 {
325         unsigned char data[CMOS_SIZE];
326         FILE *f;
327
328         if ((f = fopen(nvramtool_op.param, "w")) == NULL) {
329                 fprintf(stderr, "%s: Can not open file %s for writing: %s\n",
330                         prog_name, nvramtool_op.param, strerror(errno));
331                 exit(1);
332         }
333
334         set_iopl(3);
335         cmos_read_all(data);
336         set_iopl(0);
337
338         if (fwrite(data, 1, CMOS_SIZE, f) != CMOS_SIZE) {
339                 fprintf(stderr, "%s: Error writing CMOS data to file %s: %s\n",
340                         prog_name, nvramtool_op.param, strerror(errno));
341                 exit(1);
342         }
343
344         fclose(f);
345 }
346
347 /****************************************************************************
348  * op_read_cmos_dump
349  *
350  * -B INPUT_FILE
351  *
352  * Read binary data from a file and write the data to CMOS memory.
353  ****************************************************************************/
354 static void op_read_cmos_dump(void)
355 {
356         unsigned char data[CMOS_SIZE];
357         size_t nr_bytes;
358         FILE *f;
359
360         if ((f = fopen(nvramtool_op.param, "r")) == NULL) {
361                 fprintf(stderr, "%s: Can not open file %s for reading: %s\n",
362                         prog_name, nvramtool_op.param, strerror(errno));
363                 exit(1);
364         }
365
366         if ((nr_bytes = fread(data, 1, CMOS_SIZE, f)) != CMOS_SIZE) {
367                 fprintf(stderr,
368                         "%s: Error: Only able to read %d bytes of CMOS data "
369                         "from file %s.  CMOS data is unchanged.\n", prog_name,
370                         (int)nr_bytes, nvramtool_op.param);
371                 exit(1);
372         }
373
374         fclose(f);
375         set_iopl(3);
376         cmos_write_all(data);
377         set_iopl(0);
378 }
379
380 /****************************************************************************
381  * op_show_cmos_hex_dump
382  *
383  * -x
384  *
385  * Write a hex dump of CMOS memory to standard output.
386  ****************************************************************************/
387 static void op_show_cmos_hex_dump(void)
388 {
389         unsigned char data[CMOS_SIZE];
390
391         set_iopl(3);
392         cmos_read_all(data);
393         set_iopl(0);
394         hexdump(data, CMOS_SIZE, 0, stdout, &cmos_dump_format);
395 }
396
397 /****************************************************************************
398  * op_show_cmos_dumpfile
399  *
400  * -X DUMP_FILE
401  *
402  * Read binary data from a file (presumably a CMOS dump file) and display a
403  * hex dump of the CMOS data from the file.
404  ****************************************************************************/
405 static void op_show_cmos_dumpfile(void)
406 {
407         unsigned char data[CMOS_SIZE];
408         size_t nr_bytes;
409         FILE *f;
410
411         if ((f = fopen(nvramtool_op.param, "r")) == NULL) {
412                 fprintf(stderr, "%s: Can not open file %s for reading: %s\n",
413                         prog_name, nvramtool_op.param, strerror(errno));
414                 exit(1);
415         }
416
417         nr_bytes = fread(data, 1, CMOS_SIZE, f);
418         fclose(f);
419         hexdump(data, nr_bytes, 0, stdout, &cmos_dump_format);
420 }
421
422 /****************************************************************************
423  * list_one_param
424  *
425  * Attempt to list one CMOS parameter given by 'name'.  'show_name' is a
426  * boolean value indicating whether the parameter name should be displayed
427  * along with its value.  Return 1 if error was encountered.  Else return OK.
428  ****************************************************************************/
429 static int list_one_param(const char name[], int show_name)
430 {
431         const cmos_entry_t *e;
432
433         if (is_checksum_name(name) || ((e = find_cmos_entry(name)) == NULL)) {
434                 fprintf(stderr, "%s: CMOS parameter %s not found.\n", prog_name,
435                         name);
436                 exit(1);
437         }
438
439         if (e->config == CMOS_ENTRY_RESERVED) {
440                 fprintf(stderr, "%s: Parameter %s is reserved.\n", prog_name,
441                         name);
442                 exit(1);
443         }
444
445         return (list_cmos_entry(e, show_name) != 0);
446 }
447
448 /****************************************************************************
449  * list_all_params
450  *
451  * Attempt to list all CMOS parameters.  Return 1 if error was encountered.
452  * Else return OK.
453  ****************************************************************************/
454 static int list_all_params(void)
455 {
456         const cmos_entry_t *e;
457         int result;
458
459         result = OK;
460
461         for (e = first_cmos_entry(); e != NULL; e = next_cmos_entry(e)) {
462                 if ((e->config == CMOS_ENTRY_RESERVED)
463                     || is_checksum_name(e->name))
464                         continue;
465
466                 if (list_cmos_entry(e, TRUE))
467                         result = 1;
468         }
469
470         return result;
471 }
472
473 /****************************************************************************
474  * list_param_enums
475  *
476  * List all possible values for CMOS parameter given by 'name'.
477  ****************************************************************************/
478 static void list_param_enums(const char name[])
479 {
480         const cmos_entry_t *e;
481         const cmos_enum_t *p;
482
483         if (is_checksum_name(name) || (e = find_cmos_entry(name)) == NULL) {
484                 fprintf(stderr, "%s: CMOS parameter %s not found.\n", prog_name,
485                         name);
486                 exit(1);
487         }
488
489         switch (e->config) {
490         case CMOS_ENTRY_ENUM:
491                 for (p = first_cmos_enum_id(e->config_id);
492                      p != NULL; p = next_cmos_enum_id(p))
493                         printf("%s\n", p->text);
494
495                 break;
496
497         case CMOS_ENTRY_HEX:
498                 printf("Parameter %s requires a %u-bit unsigned integer.\n",
499                        name, e->length);
500                 break;
501
502         case CMOS_ENTRY_STRING:
503                 printf("Parameter %s requires a %u-byte string.\n", name,
504                        e->length / 8);
505                 break;
506
507         case CMOS_ENTRY_RESERVED:
508                 printf("Parameter %s is reserved.\n", name);
509                 break;
510
511         default:
512                 BUG();
513         }
514 }
515
516 /****************************************************************************
517  * set_one_param
518  *
519  * Set the CMOS parameter given by 'name' to 'value'.  The 'name' parameter
520  * is case-sensitive.  If we are setting an enum parameter, then 'value' is
521  * interpreted as a case-sensitive string that must match the option name
522  * exactly.  If we are setting a 'hex' parameter, then 'value' is treated as
523  * a string representation of an unsigned integer that may be specified in
524  * decimal, hex, or octal.
525  ****************************************************************************/
526 static void set_one_param(const char name[], const char value[])
527 {
528         const cmos_entry_t *e;
529         unsigned long long n;
530
531         if (is_checksum_name(name) || (e = find_cmos_entry(name)) == NULL) {
532                 fprintf(stderr, "%s: CMOS parameter %s not found.", prog_name,
533                         name);
534                 exit(1);
535         }
536
537         switch (prepare_cmos_write(e, value, &n)) {
538         case OK:
539                 break;
540
541         case CMOS_OP_BAD_ENUM_VALUE:
542                 fprintf(stderr, "%s: Bad value for parameter %s.", prog_name,
543                         name);
544                 goto fail;
545
546         case CMOS_OP_NEGATIVE_INT:
547                 fprintf(stderr,
548                         "%s: This program does not support assignment of negative "
549                         "numbers to coreboot parameters.", prog_name);
550                 goto fail;
551
552         case CMOS_OP_INVALID_INT:
553                 fprintf(stderr, "%s: %s is not a valid integer.", prog_name,
554                         value);
555                 goto fail;
556
557         case CMOS_OP_RESERVED:
558                 fprintf(stderr,
559                         "%s: Can not modify reserved coreboot parameter %s.",
560                         prog_name, name);
561                 goto fail;
562
563         case CMOS_OP_VALUE_TOO_WIDE:
564                 fprintf(stderr,
565                         "%s: Can not write value %s to CMOS parameter %s that is "
566                         "only %d bits wide.", prog_name, value, name,
567                         e->length);
568                 goto fail;
569
570         case CMOS_OP_NO_MATCHING_ENUM:
571                 fprintf(stderr,
572                         "%s: coreboot parameter %s has no matching enums.",
573                         prog_name, name);
574                 goto fail;
575
576         case CMOS_AREA_OUT_OF_RANGE:
577                 fprintf(stderr,
578                         "%s: The CMOS area specified by the layout info for "
579                         "coreboot parameter %s is out of range.", prog_name,
580                         name);
581                 goto fail;
582
583         case CMOS_AREA_OVERLAPS_RTC:
584                 fprintf(stderr,
585                         "%s: The CMOS area specified by the layout info for "
586                         "coreboot parameter %s overlaps the realtime clock area.",
587                         prog_name, name);
588                 goto fail;
589
590         case CMOS_AREA_TOO_WIDE:
591                 fprintf(stderr,
592                         "%s: The CMOS area specified by the layout info for "
593                         "coreboot parameter %s is too wide.", prog_name, name);
594                 goto fail;
595
596         default:
597                 fprintf(stderr,
598                         "%s: Unknown error encountered while attempting to modify "
599                         "coreboot parameter %s.", prog_name, name);
600                 goto fail;
601         }
602
603         /* write the value to nonvolatile RAM */
604         set_iopl(3);
605         cmos_write(e, n);
606         cmos_checksum_write(cmos_checksum_compute());
607         set_iopl(0);
608         return;
609
610       fail:
611         fprintf(stderr, "  CMOS write not performed.\n");
612         exit(1);
613 }
614
615 /****************************************************************************
616  * set_params
617  *
618  * Set coreboot parameters according to the contents of file 'f'.
619  ****************************************************************************/
620 static void set_params(FILE * f)
621 {                               /* First process the input file.  Then perform writes only if there were
622                                  * no problems processing the input.  Either all values will be written
623                                  * successfully or no values will be written.
624                                  */
625         do_cmos_writes(process_input_file(f));
626 }
627
628 /****************************************************************************
629  * parse_assignment
630  *
631  * Parse the string 'arg' (which supposedly represents an assignment) into a
632  * NAME and a VALUE.  If 'arg' does not conform to the proper assignment
633  * syntax, exit with a usage message.  Otherwise, on return, 'arg' is broken
634  * into substrings representing NAME and VALUE, and *name and *value are set
635  * to point to these two substrings.
636  ****************************************************************************/
637 static void parse_assignment(char arg[], const char **name, const char **value)
638 {
639         static const size_t N_MATCHES = 4;
640         regmatch_t match[N_MATCHES];
641         regex_t assignment;
642
643         compile_reg_exprs(REG_EXTENDED | REG_NEWLINE, 1, assignment_regex,
644                           &assignment);
645
646         /* Does 'arg' conform to proper assignment syntax?  If not, exit with a
647          * usage message.
648          */
649         if (regexec(&assignment, arg, N_MATCHES, match, 0))
650                 usage(stderr);
651
652         /* Ok, we found a valid assignment.  Break it into two strings
653          * representing NAME and VALUE.
654          */
655         arg[match[1].rm_eo] = '\0';
656         arg[match[2].rm_eo] = '\0';
657         *name = &arg[match[1].rm_so];
658         *value = &arg[match[2].rm_so];
659
660         free_reg_exprs(1, &assignment);
661 }
662
663 /****************************************************************************
664  * list_cmos_entry
665  *
666  * Attempt to list the CMOS entry represented by 'e'.  'show_name' is a
667  * boolean value indicating whether the parameter name should be displayed
668  * along with its value.  On success, return OK.  On error, print an error
669  * message and return 1.
670  ****************************************************************************/
671 static int list_cmos_entry(const cmos_entry_t * e, int show_name)
672 {
673         const cmos_enum_t *p;
674         unsigned long long value;
675         char *w;
676
677         /* sanity check CMOS entry */
678         switch (prepare_cmos_read(e)) {
679         case OK:
680                 break;
681
682         case CMOS_OP_RESERVED:
683                 BUG();
684
685         case CMOS_AREA_OUT_OF_RANGE:
686                 fprintf(stderr,
687                         "%s: Can not read coreboot parameter %s because "
688                         "layout info specifies out of range CMOS area.\n",
689                         prog_name, e->name);
690                 return 1;
691
692         case CMOS_AREA_OVERLAPS_RTC:
693                 fprintf(stderr,
694                         "%s: Can not read coreboot parameter %s because "
695                         "layout info specifies CMOS area that overlaps realtime "
696                         "clock area.\n", prog_name, e->name);
697                 return 1;
698
699         case CMOS_AREA_TOO_WIDE:
700                 fprintf(stderr,
701                         "%s: Can not read coreboot parameter %s because "
702                         "layout info specifies CMOS area that is too wide.\n",
703                         prog_name, e->name);
704                 return 1;
705
706         default:
707                 fprintf(stderr,
708                         "%s: Unknown error encountered while attempting to "
709                         "read coreboot parameter %s\n", prog_name, e->name);
710                 return 1;
711         }
712
713         /* read the value from CMOS */
714         set_iopl(3);
715         value = cmos_read(e);
716         set_iopl(0);
717
718         /* display the value */
719         switch (e->config) {
720         case CMOS_ENTRY_ENUM:
721                 if ((p = find_cmos_enum(e->config_id, value)) == NULL) {
722                         if (show_name)
723                                 printf("# Bad value -> %s = 0x%llx\n", e->name,
724                                        value);
725                         else
726                                 printf("Bad value -> 0x%llx\n", value);
727                 } else {
728                         if (show_name)
729                                 printf("%s = %s\n", e->name, p->text);
730                         else
731                                 printf("%s\n", p->text);
732                 }
733
734                 break;
735
736         case CMOS_ENTRY_HEX:
737                 if (show_name)
738                         printf("%s = 0x%llx\n", e->name, value);
739                 else
740                         printf("0x%llx\n", value);
741
742                 break;
743
744         case CMOS_ENTRY_STRING:
745                 w = (char *)(unsigned long)value;
746                 while (*w) {
747                         if(!isprint(*w)) {
748                                 if (show_name)
749                                         printf("# Bad value -> %s\n", e->name);
750                                 else
751                                         printf("Bad value\n");
752                                 break;
753                         }
754                         w++;
755                 }
756
757                 if (!*w) {
758
759                         if (show_name)
760                                 printf("%s = %s\n", e->name,
761                                        (char *)(unsigned long)value);
762                         else
763                                 printf("%s\n", (char *)(unsigned long)value);
764                 }
765
766                 free((void *)(unsigned long)value);
767
768                 break;
769
770         case CMOS_ENTRY_RESERVED:
771         default:
772                 BUG();
773         }
774
775         return OK;
776 }
777
778 /****************************************************************************
779  * convert_checksum_value
780  *
781  * 'value' is the string representation of a checksum value that the user
782  * wishes to set using the -c option.  Convert the string to a 16-bit
783  * unsigned integer and return the result.  Exit with an error message if
784  * 'value' is invalid.
785  ****************************************************************************/
786 static uint16_t convert_checksum_value(const char value[])
787 {
788         unsigned long n;
789         const char *p;
790         uint16_t result;
791         int negative;
792
793         for (p = value; isspace(*p); p++) ;
794
795         negative = (*p == '-');
796         n = strtoul(value, (char **)&p, 0);
797
798         if (*p) {
799                 fprintf(stderr,
800                         "%s: Checksum value %s is not a valid integer.\n",
801                         prog_name, value);
802                 exit(1);
803         }
804
805         if (negative) {
806                 fprintf(stderr,
807                         "%s: Checksum must be an unsigned integer.\n",
808                         prog_name);
809                 exit(1);
810         }
811
812         result = (uint16_t) n;
813
814         if (result != n) {
815                 fprintf(stderr,
816                         "%s: Checksum value must fit within 16 bits.\n",
817                         prog_name);
818                 exit(1);
819         }
820
821         return result;
822 }