1 /* gpg-error.c - Determining gpg-error error codes.
2 Copyright (C) 2004, 2016 g10 Code GmbH
3
4 This file is part of libgpg-error.
5
6 libgpg-error is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public License
8 as published by the Free Software Foundation; either version 2.1 of
9 the License, or (at your option) any later version.
10
11 libgpg-error is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with libgpg-error; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA. */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stddef.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <limits.h>
30 #include <stdio.h>
31
32 #ifdef HAVE_LOCALE_H
33 # include <locale.h>
34 #endif
35 #ifdef ENABLE_NLS
36 #ifdef HAVE_W32_SYSTEM
37 # include "gettext.h"
38 #else
39 # include <libintl.h>
40 #endif
41 # define _(a) gettext (a)
42 # ifdef gettext_noop
43 # define N_(a) gettext_noop (a)
44 # else
45 # define N_(a) (a)
46 # endif
47 #else
48 # define _(a) (a)
49 # define N_(a) (a)
50 #endif
51
52 #include <gpg-error.h>
53
54
55 #if HAVE_W32_SYSTEM
56 /* The implementation follows below. */
57 static char *get_locale_dir (void);
58 static void drop_locale_dir (char *locale_dir);
59 #else
60 #define get_locale_dir() LOCALEDIR
61 #define drop_locale_dir(dir)
62 #endif
63
64 static void
i18n_init(void)65 i18n_init (void)
66 {
67 #ifdef ENABLE_NLS
68 char *locale_dir;
69
70 #ifdef HAVE_LC_MESSAGES
71 setlocale (LC_TIME, "");
72 setlocale (LC_MESSAGES, "");
73 #else
74 # ifndef HAVE_W32_SYSTEM
75 setlocale (LC_ALL, "" );
76 # endif
77 #endif
78
79 /* Note that for this program we would only need the textdomain call
80 because libgpg-error already initializes itself to its locale dir
81 (via gpg_err_init or a constructor). However this is only done
82 for the static standard locale and thus if the above setlocale
83 calls select a different locale the bindtext below will do
84 something else. */
85
86 locale_dir = get_locale_dir ();
87 if (locale_dir)
88 {
89 bindtextdomain (PACKAGE, locale_dir);
90 drop_locale_dir (locale_dir);
91 }
92 textdomain (PACKAGE);
93 #endif
94 }
95
96
97 #ifdef HAVE_W32_SYSTEM
98
99 #include <windows.h>
100
101
102 static char *
get_locale_dir(void)103 get_locale_dir (void)
104 {
105 static wchar_t moddir[MAX_PATH+5];
106 char *result, *p;
107 int nbytes;
108
109 if (!GetModuleFileNameW (NULL, moddir, MAX_PATH))
110 *moddir = 0;
111
112 #define SLDIR "\\share\\locale"
113 if (*moddir)
114 {
115 nbytes = WideCharToMultiByte (CP_UTF8, 0, moddir, -1, NULL, 0, NULL, NULL);
116 if (nbytes < 0)
117 return NULL;
118
119 result = malloc (nbytes + strlen (SLDIR) + 1);
120 if (result)
121 {
122 nbytes = WideCharToMultiByte (CP_UTF8, 0, moddir, -1,
123 result, nbytes, NULL, NULL);
124 if (nbytes < 0)
125 {
126 free (result);
127 result = NULL;
128 }
129 else
130 {
131 p = strrchr (result, '\\');
132 if (p)
133 *p = 0;
134 /* If we are installed below "bin" strip that part and
135 use the top directory instead. */
136 p = strrchr (result, '\\');
137 if (p && !strcmp (p+1, "bin"))
138 *p = 0;
139 /* Append the static part. */
140 strcat (result, SLDIR);
141 }
142 }
143 }
144 else /* Use the old default value. */
145 {
146 result = malloc (10 + strlen (SLDIR) + 1);
147 if (result)
148 {
149 strcpy (result, "c:\\gnupg");
150 strcat (result, SLDIR);
151 }
152 }
153 #undef SLDIR
154 return result;
155 }
156
157
158 static void
drop_locale_dir(char * locale_dir)159 drop_locale_dir (char *locale_dir)
160 {
161 free (locale_dir);
162 }
163
164 #endif /* HAVE_W32_SYSTEM */
165
166
167 const char *gpg_strerror_sym (gpg_error_t err);
168 const char *gpg_strsource_sym (gpg_error_t err);
169
170
171 /* Parse string STR assuming it is either a single number N or in the
172 * form K.N to denote an error source code K and and error code N.
173 * Returns false on error (e.g. invalid number) or true for valid
174 * codes; if true is returned a full error code is stored at ERR. */
175 static int
get_err_from_number(char * str,gpg_error_t * err)176 get_err_from_number (char *str, gpg_error_t *err)
177 {
178 unsigned long nr;
179 char *tail;
180
181 gpg_err_set_errno (0);
182 nr = strtoul (str, &tail, 0);
183 if (errno)
184 return 0;
185
186 if (nr > UINT_MAX)
187 return 0;
188
189 if (*tail)
190 {
191 unsigned long cnr = strtoul (tail + 1, &tail, 0);
192 if (errno || *tail)
193 return 0;
194
195 if (nr >= GPG_ERR_SOURCE_DIM || cnr >= GPG_ERR_CODE_DIM)
196 return 0;
197
198 nr = gpg_err_make (nr, cnr);
199 }
200
201 *err = (unsigned int) nr;
202 return 1;
203 }
204
205
206 /* Helper function to parse a symbol either with a "GPG_ERR_SOURCE_"
207 * or "GPG_ERR_" prefix. If the symbol is not available false is
208 * return; else the symbols value is ORed into the value at ERR
209 * (shifted for a GPG_ERR_SOURCE_) and true returned. HAVE_SOURCE and
210 * HAVE_CODE are expected to be addresses where a 0 is stored; a 1 is
211 * stored at the respective address to mark whether a code or source
212 * value was found. If one of those state variables already point to
213 * a true value the function will return 0 and not change the value at
214 * ERR. */
215 static int
get_err_from_symbol_one(char * str,gpg_error_t * err,int * have_source,int * have_code)216 get_err_from_symbol_one (char *str, gpg_error_t *err,
217 int *have_source, int *have_code)
218 {
219 static const char src_prefix[] = "GPG_ERR_SOURCE_";
220 static const char code_prefix[] = "GPG_ERR_";
221
222 if (!strncasecmp (src_prefix, str, sizeof (src_prefix) - 1))
223 {
224 gpg_err_source_t src;
225
226 if (*have_source)
227 return 0;
228 *have_source = 1;
229 str += sizeof (src_prefix) - 1;
230
231 for (src = 0; src < GPG_ERR_SOURCE_DIM; src++)
232 {
233 const char *src_sym;
234
235 src_sym = gpg_strsource_sym (src << GPG_ERR_SOURCE_SHIFT);
236 if (src_sym && !strcasecmp (str, src_sym + sizeof (src_prefix) - 1))
237 {
238 *err |= src << GPG_ERR_SOURCE_SHIFT;
239 return 1;
240 }
241 }
242 }
243 else if (!strncasecmp (code_prefix, str, sizeof (code_prefix) - 1))
244 {
245 gpg_err_code_t code;
246
247 if (*have_code)
248 return 0;
249 *have_code = 1;
250 str += sizeof (code_prefix) - 1;
251
252 for (code = 0; code < GPG_ERR_CODE_DIM; code++)
253 {
254 const char *code_sym = gpg_strerror_sym (code);
255 if (code_sym
256 && !strcasecmp (str, code_sym + sizeof (code_prefix) - 1))
257 {
258 *err |= code;
259 return 1;
260 }
261 }
262 }
263 return 0;
264 }
265
266
267 /* Parse string STR assuming it is either a single symbol C or in the
268 * form S.C to denote an error source symbold S and and error code
269 * symbold C. Returns false on error (e.g. invalid number) or true
270 * for valid codes; if true is returned a full error code is stored at
271 * ERR. */
272 static int
get_err_from_symbol(char * str,gpg_error_t * err)273 get_err_from_symbol (char *str, gpg_error_t *err)
274 {
275 char *str2 = str;
276 int have_source = 0;
277 int have_code = 0;
278 int ret;
279 char *saved_pos = NULL;
280 char saved_char;
281
282 *err = 0;
283 while (*str2 && ((*str2 >= 'A' && *str2 <= 'Z')
284 || (*str2 >= '0' && *str2 <= '9')
285 || *str2 == '_'))
286 str2++;
287 if (*str2)
288 {
289 saved_pos = str2;
290 saved_char = *str2;
291 *str2 = '\0';
292 str2++;
293 }
294 else
295 str2 = NULL;
296
297 ret = get_err_from_symbol_one (str, err, &have_source, &have_code);
298 if (ret && str2)
299 ret = get_err_from_symbol_one (str2, err, &have_source, &have_code);
300
301 if (saved_pos)
302 *saved_pos = saved_char;
303 return ret;
304 }
305
306
307 /* Parse string STR assuming it partial code symbol and store its
308 * value at ERR and return true. */
309 static int
get_err_from_codesymbol(char * str,gpg_error_t * err)310 get_err_from_codesymbol (char *str, gpg_error_t *err)
311 {
312 static const char code_prefix[] = "GPG_ERR_";
313 gpg_err_code_t code;
314
315 *err = 0;
316
317 /* Skip an optional prefix. */
318 if (!strncasecmp (code_prefix, str, sizeof (code_prefix) - 1))
319 str += sizeof (code_prefix) - 1;
320
321 for (code = 0; code < GPG_ERR_CODE_DIM; code++)
322 {
323 const char *code_sym = gpg_strerror_sym (code);
324 if (code_sym
325 && !strcasecmp (str, code_sym + sizeof (code_prefix) - 1))
326 {
327 *err |= code;
328 return 1;
329 }
330 }
331 return 0;
332 }
333
334
335 /* Helper function to parse a string which maps back to a source or
336 * code value. If no source or code for the symbold is available
337 * false is return; else the source or code value is ORed into the
338 * value at ERR (shifted for a GPG_ERR_SOURCE_) and true returned.
339 * The match is first tried on source values and then on code values.
340 * HAVE_SOURCE and HAVE_CODE are expected to be addresses where a 0 is
341 * stored; a 1 is stored at the respective address to mark whether a
342 * code or source value was found. If one of those state variables
343 * already point to a true value the function will return 0 and not
344 * change the value at ERR. */
345 static int
get_err_from_str_one(char * str,gpg_error_t * err,int * have_source,int * have_code)346 get_err_from_str_one (char *str, gpg_error_t *err,
347 int *have_source, int *have_code)
348 {
349 gpg_err_source_t src;
350 gpg_err_code_t code;
351
352 for (src = 0; src < GPG_ERR_SOURCE_DIM; src++)
353 {
354 const char *src_str = gpg_strsource (src << GPG_ERR_SOURCE_SHIFT);
355 if (src_str && !strcasecmp (str, src_str))
356 {
357 if (*have_source)
358 return 0;
359
360 *have_source = 1;
361 *err |= src << GPG_ERR_SOURCE_SHIFT;
362 return 1;
363 }
364 }
365
366 for (code = 0; code < GPG_ERR_CODE_DIM; code++)
367 {
368 const char *code_str = gpg_strerror (code);
369 if (code_str && !strcasecmp (str, code_str))
370 {
371 if (*have_code)
372 return 0;
373
374 *have_code = 1;
375 *err |= code;
376 return 1;
377 }
378 }
379
380 return 0;
381 }
382
383
384 /* Parse string STR assuming it is either a single desription string C
385 * or in the form S.C to denote an error source descrition S and and
386 * error code description C. Returns false on error (e.g. invalid
387 * symbol) or true for valid codes; if true is returned a full error
388 * code is stored at ERR. */
389 static int
get_err_from_str(char * str,gpg_error_t * err)390 get_err_from_str (char *str, gpg_error_t *err)
391 {
392 char *str2 = str;
393 int have_source = 0;
394 int have_code = 0;
395 int ret;
396 char *saved_pos = NULL;
397 char saved_char = 0; /* (avoid warning) */
398
399 *err = 0;
400 /* First match on the entire string to handle the case that it is
401 * code description with spaces. */
402 ret = get_err_from_str_one (str, err, &have_source, &have_code);
403 if (ret)
404 return ret;
405
406 /* Then figure out whether the first string is a simple word. */
407 while (*str2 && ((*str2 >= 'A' && *str2 <= 'Z')
408 || (*str2 >= 'a' && *str2 <= 'z')
409 || (*str2 >= '0' && *str2 <= '9')
410 || *str2 == '_'))
411 str2++;
412 if (*str2)
413 {
414 saved_pos = str2;
415 saved_char = *str2;
416 *((char *) str2) = '\0';
417 str2++;
418 while (*str2 && !((*str2 >= 'A' && *str2 <= 'Z')
419 || (*str2 >= 'a' && *str2 <= 'z')
420 || (*str2 >= '0' && *str2 <= '9')
421 || *str2 == '_'))
422 str2++;
423 }
424 else
425 str2 = NULL;
426
427 ret = get_err_from_str_one (str, err, &have_source, &have_code);
428 if (ret && str2)
429 ret = get_err_from_str_one (str2, err, &have_source, &have_code);
430
431 if (saved_pos)
432 *saved_pos = saved_char;
433 return ret;
434 }
435
436
437 static void
print_desc(const char * symbol)438 print_desc (const char *symbol)
439 {
440 static int initialized;
441 static FILE *fp;
442 char line[512];
443 char *p;
444 int indesc = 0;
445 int blanklines = 0;
446 int last_was_keyword = 0;
447
448 if (!symbol)
449 return;
450
451 if (!initialized)
452 {
453 initialized = 1;
454 fp = fopen (PKGDATADIR "/errorref.txt", "r");
455 }
456 if (!fp)
457 return;
458 rewind (fp);
459 while (fgets (line, sizeof line, fp))
460 {
461 if (*line == '#')
462 continue;
463 if (*line && line[strlen(line)-1] == '\n')
464 line[strlen(line)-1] = 0;
465
466 if (!strncmp (line, "GPG_ERR_", 8))
467 {
468 if (indesc == 1 && last_was_keyword)
469 continue; /* Skip keywords immediately following a matched
470 * keyword. */
471 last_was_keyword = 1;
472
473 indesc = 0;
474 p = strchr (line, ' ');
475 if (!p)
476 continue;
477 *p = 0;
478 if (!strcmp (line, symbol))
479 {
480 indesc = 1;
481 continue; /* Skip this line. */
482 }
483 }
484 else
485 last_was_keyword = 0;
486 if (!indesc)
487 continue;
488 if (indesc == 1 && !*line)
489 continue; /* Skip leading empty lines in a description. */
490 if (indesc == 1)
491 putchar ('\n'); /* One leading empty line. */
492 indesc = 2;
493 if (!*line)
494 {
495 blanklines++;
496 continue;
497 }
498 for (; blanklines; blanklines--)
499 putchar ('\n');
500 printf ("%s\n", line);
501 }
502 putchar ('\n'); /* One trailing blank line. */
503 }
504
505
506
507
508
509 static const char *
my_strusage(int level)510 my_strusage (int level)
511 {
512 const char *p;
513
514 switch (level)
515 {
516 case 9: p = "LGPL-2.1-or-later"; break;
517
518 case 11: p = "gpg-error"; break;
519 case 12: p = PACKAGE_NAME; break;
520 case 13: p = PACKAGE_VERSION; break;
521 case 14: p = "Copyright (C) 2019 g10 Code GmbH"; break;
522 case 19: p = _("Please report bugs to <https://bugs.gnupg.org>.\n"); break;
523
524 case 1:
525 case 40:
526 p = ("Usage: gpg-error [options] error-numbers");
527 break;
528 case 41:
529 p = ("Map error numbers to strings and vice versa.\n");
530 break;
531
532 case 42:
533 p = "1"; /* Flag: print 40 as part of 41. */
534 break;
535
536 default: p = NULL; break;
537 }
538 return p;
539 }
540
541
542
543 int
main(int argc,char * argv[])544 main (int argc, char *argv[])
545 {
546 enum { CMD_DEFAULT = 0,
547 CMD_LIB_VERSION = 501,
548 CMD_LIST,
549 CMD_DEFINES,
550 CMD_LOCALE,
551 OPT_DESC
552 };
553 static gpgrt_opt_t opts[] = {
554 ARGPARSE_c (CMD_LIB_VERSION, "lib-version",
555 "Print library version"),
556 ARGPARSE_c (CMD_LIST, "list",
557 "Print all error codes"),
558 ARGPARSE_c (CMD_DEFINES, "defines",
559 "Print all error codes as #define lines"),
560 #if HAVE_W32_SYSTEM
561 ARGPARSE_c (CMD_LOCALE, "locale",
562 "Return the locale used for gettext"),
563 #else
564 ARGPARSE_c (CMD_LOCALE, "locale",
565 "@"),
566 #endif
567 ARGPARSE_s_n (OPT_DESC, "desc",
568 "Print with error description"),
569 ARGPARSE_end()
570 };
571 gpgrt_argparse_t pargs = { &argc, &argv };
572
573 int i;
574 int libversion = 0;
575 int listmode = 0;
576 int localemode = 0;
577 int desc = 0;
578 const char *s, *s2;
579 const char *source_sym;
580 const char *error_sym;
581 gpg_error_t err;
582
583 gpgrt_init ();
584 i18n_init ();
585 gpgrt_set_strusage (my_strusage);
586 gpgrt_log_set_prefix (gpgrt_strusage (11), GPGRT_LOG_WITH_PREFIX);
587
588
589 while (gpgrt_argparse (NULL, &pargs, opts))
590 {
591 switch (pargs.r_opt)
592 {
593 case CMD_LIB_VERSION: libversion = 1; break;
594 case CMD_LIST: listmode = 1; break;
595 case CMD_DEFINES: listmode = 2; break;
596 case CMD_LOCALE: localemode = 1; break;
597 case OPT_DESC: desc = 1; break;
598 default: pargs.err = ARGPARSE_PRINT_WARNING; break;
599 }
600 }
601 gpgrt_argparse (NULL, &pargs, NULL); /* Free internal memory. */
602
603 if (libversion)
604 {
605 if (argc)
606 gpgrt_usage (1);
607 }
608 else if (localemode)
609 {
610 if (argc > 1)
611 gpgrt_usage (1);
612 }
613 else if ((argc && listmode) || (!argc && !listmode))
614 gpgrt_usage (1);
615
616
617 if (libversion)
618 {
619 argc--; argv++;
620 printf ("Version from header: %s (0x%06x)\n",
621 GPG_ERROR_VERSION, GPG_ERROR_VERSION_NUMBER);
622 printf ("Version from binary: %s\n", gpg_error_check_version (NULL));
623 s = gpg_error_check_version ("\x01\x01");
624 while (*s && *s == '\n')
625 s++;
626 fputs ("Copyright blurb ...: ", stdout);
627 for (; *s; s++)
628 {
629 if (*s == '\n')
630 {
631 for (s2=s+1; *s2 == '\n'; s2++)
632 ;
633 if (!*s2)
634 break; /* Cut off trailing LFs. */
635 fputs ("\n ", stdout);
636 }
637 else
638 putc (*s, stdout);
639 }
640 putc ('\n', stdout);
641 }
642 else if (localemode)
643 {
644 #if HAVE_W32_SYSTEM
645 if (argc)
646 {
647 /* Warning: What we do here is not allowed because
648 * gpgrt_w32_override_locale needs to be called as early as
649 * possible. However for this very purpose it is okay. */
650 if (**argv >= '0' && **argv <= '9')
651 gpgrt_w32_override_locale (NULL, strtoul (*argv, NULL, 0));
652 else
653 gpgrt_w32_override_locale (*argv, 0);
654 }
655
656 printf ("%s\n", gettext_localename ());
657 #else
658 log_info ("this command is only useful on Windows\n");
659 #endif
660 }
661 else if (listmode == 1)
662 {
663 for (i=0; i < GPG_ERR_SOURCE_DIM; i++)
664 {
665 /* We use error code 1 because gpg_err_make requires a
666 non-zero error code. */
667 err = gpg_err_make (i, 1);
668 err -= 1;
669 source_sym = gpg_strsource_sym (err);
670 if (source_sym)
671 {
672 printf ("%u = (%u, -) = (%s, -) = (%s, -)\n",
673 err, gpg_err_source (err),
674 source_sym, gpg_strsource (err));
675 if (desc)
676 print_desc (source_sym);
677 }
678 }
679 for (i=0; i < GPG_ERR_CODE_DIM; i++)
680 {
681 err = gpg_err_make (GPG_ERR_SOURCE_UNKNOWN, i);
682 error_sym = gpg_strerror_sym (err);
683 if (error_sym)
684 {
685 printf ("%u = (-, %u) = (-, %s) = (-, %s)\n",
686 err, gpg_err_code (err),
687 error_sym, gpg_strerror (err));
688 if (desc)
689 print_desc (error_sym);
690 }
691 }
692 }
693 else if (listmode == 2)
694 {
695 int n, nmax;
696
697 for (i=0, nmax=0; i < GPG_ERR_SOURCE_DIM; i++)
698 {
699 err = gpg_err_make (i, 1);
700 source_sym = gpg_strsource_sym (err);
701 if (source_sym)
702 {
703 n = strlen (source_sym);
704 if (n > nmax)
705 nmax = n;
706 }
707 }
708 for (i=0; i < GPG_ERR_SOURCE_DIM; i++)
709 {
710 err = gpg_err_make (i, 1);
711 source_sym = gpg_strsource_sym (err);
712 if (source_sym)
713 printf ("#define %-*s %3u\n", nmax,source_sym,gpg_err_source (err));
714 }
715
716
717 for (i=0, nmax = 0; i < GPG_ERR_CODE_DIM; i++)
718 {
719 err = gpg_err_make (GPG_ERR_SOURCE_UNKNOWN, i);
720 error_sym = gpg_strerror_sym (err);
721 if (error_sym)
722 {
723 n = strlen (error_sym);
724 if (n > nmax)
725 nmax = n;
726 }
727 }
728 for (i=0; i < GPG_ERR_CODE_DIM; i++)
729 {
730 err = gpg_err_make (GPG_ERR_SOURCE_UNKNOWN, i);
731 error_sym = gpg_strerror_sym (err);
732 if (error_sym)
733 printf ("#define %-*s %5u\n", nmax, error_sym, gpg_err_code (err));
734 }
735 }
736 else /* Standard mode. */
737 {
738 for (i=0; i < argc; i++)
739 {
740 /* First check the arg is a number N or K.N,
741 * then check the arg for CODESYM or SOURCESYM.CODESYM,
742 * then check the arg for CODESYM or CODESYM w/o GPG_ERR_ prefix,
743 * then check the arg for code description
744 * or symbol dot code description.
745 */
746 if (get_err_from_number (argv[i], &err)
747 || get_err_from_symbol (argv[i], &err)
748 || get_err_from_codesymbol (argv[i], &err)
749 || get_err_from_str (argv[i], &err))
750 {
751 source_sym = gpg_strsource_sym (err);
752 error_sym = gpg_strerror_sym (err);
753
754 printf ("%u = (%u, %u) = (%s, %s) = (%s, %s)\n",
755 err, gpg_err_source (err), gpg_err_code (err),
756 source_sym ? source_sym : "-", error_sym ? error_sym:"-",
757 gpg_strsource (err), gpg_strerror (err));
758 if (desc)
759 print_desc (error_sym);
760 }
761 else
762 log_error (_("warning: could not recognize %s\n"), argv[i]);
763 }
764 }
765
766 exit (0);
767 }
768