1/* gpg-error.c - Determining gpg-error error codes.
2   Copyright (C) 2004 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.  */
57static char *get_locale_dir (void);
58static void drop_locale_dir (char *locale_dir);
59#else
60#define get_locale_dir() LOCALEDIR
61#define drop_locale_dir(dir)
62#endif
63
64static void
65i18n_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
102static char *
103get_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
158static void
159drop_locale_dir (char *locale_dir)
160{
161  free (locale_dir);
162}
163
164#endif	/* HAVE_W32_SYSTEM */
165
166
167const char *gpg_strerror_sym (gpg_error_t err);
168const char *gpg_strsource_sym (gpg_error_t err);
169
170
171static int
172get_err_from_number (char *str, gpg_error_t *err)
173{
174  unsigned long nr;
175  char *tail;
176
177  gpg_err_set_errno (0);
178  nr = strtoul (str, &tail, 0);
179  if (errno)
180    return 0;
181
182  if (nr > UINT_MAX)
183    return 0;
184
185  if (*tail)
186    {
187      unsigned long cnr = strtoul (tail + 1, &tail, 0);
188      if (errno || *tail)
189	return 0;
190
191      if (nr >= GPG_ERR_SOURCE_DIM || cnr >= GPG_ERR_CODE_DIM)
192	return 0;
193
194      nr = gpg_err_make (nr, cnr);
195    }
196
197  *err = (unsigned int) nr;
198  return 1;
199}
200
201
202static int
203get_err_from_symbol_one (char *str, gpg_error_t *err,
204			 int *have_source, int *have_code)
205{
206  static const char src_prefix[] = "GPG_ERR_SOURCE_";
207  static const char code_prefix[] = "GPG_ERR_";
208
209  if (!strncasecmp (src_prefix, str, sizeof (src_prefix) - 1))
210    {
211      gpg_err_source_t src;
212
213      if (*have_source)
214	return 0;
215      *have_source = 1;
216      str += sizeof (src_prefix) - 1;
217
218      for (src = 0; src < GPG_ERR_SOURCE_DIM; src++)
219	{
220	  const char *src_sym;
221
222	  src_sym = gpg_strsource_sym (src << GPG_ERR_SOURCE_SHIFT);
223	  if (src_sym && !strcasecmp (str, src_sym + sizeof (src_prefix) - 1))
224	    {
225	      *err |= src << GPG_ERR_SOURCE_SHIFT;
226	      return 1;
227	    }
228	}
229    }
230  else if (!strncasecmp (code_prefix, str, sizeof (code_prefix) - 1))
231    {
232      gpg_err_code_t code;
233
234      if (*have_code)
235	return 0;
236      *have_code = 1;
237      str += sizeof (code_prefix) - 1;
238
239      for (code = 0; code < GPG_ERR_CODE_DIM; code++)
240	{
241	  const char *code_sym = gpg_strerror_sym (code);
242	  if (code_sym
243	      && !strcasecmp (str, code_sym + sizeof (code_prefix) - 1))
244	    {
245	      *err |= code;
246	      return 1;
247	    }
248	}
249    }
250  return 0;
251}
252
253
254static int
255get_err_from_symbol (char *str, gpg_error_t *err)
256{
257  char *str2 = str;
258  int have_source = 0;
259  int have_code = 0;
260  int ret;
261  char *saved_pos = NULL;
262  char saved_char;
263
264  *err = 0;
265  while (*str2 && ((*str2 >= 'A' && *str2 <= 'Z')
266		   || (*str2 >= '0' && *str2 <= '9')
267		   || *str2 == '_'))
268    str2++;
269  if (*str2)
270    {
271      saved_pos = str2;
272      saved_char = *str2;
273      *str2 = '\0';
274      str2++;
275    }
276  else
277    str2 = NULL;
278
279  ret = get_err_from_symbol_one (str, err, &have_source, &have_code);
280  if (ret && str2)
281    ret = get_err_from_symbol_one (str2, err, &have_source, &have_code);
282
283  if (saved_pos)
284    *saved_pos = saved_char;
285  return ret;
286}
287
288
289static int
290get_err_from_str_one (char *str, gpg_error_t *err,
291		      int *have_source, int *have_code)
292{
293  gpg_err_source_t src;
294  gpg_err_code_t code;
295
296  for (src = 0; src < GPG_ERR_SOURCE_DIM; src++)
297    {
298      const char *src_str = gpg_strsource (src << GPG_ERR_SOURCE_SHIFT);
299      if (src_str && !strcasecmp (str, src_str))
300	{
301	  if (*have_source)
302	    return 0;
303
304	  *have_source = 1;
305	  *err |= src << GPG_ERR_SOURCE_SHIFT;
306	  return 1;
307	}
308    }
309
310  for (code = 0; code < GPG_ERR_CODE_DIM; code++)
311    {
312      const char *code_str = gpg_strerror (code);
313      if (code_str && !strcasecmp (str, code_str))
314	{
315	  if (*have_code)
316	    return 0;
317
318	  *have_code = 1;
319	  *err |= code;
320	  return 1;
321	}
322    }
323
324  return 0;
325}
326
327
328static int
329get_err_from_str (char *str, gpg_error_t *err)
330{
331  char *str2 = str;
332  int have_source = 0;
333  int have_code = 0;
334  int ret;
335  char *saved_pos = NULL;
336  char saved_char;
337
338  *err = 0;
339  ret = get_err_from_str_one (str, err, &have_source, &have_code);
340  if (ret)
341    return ret;
342
343  while (*str2 && ((*str2 >= 'A' && *str2 <= 'Z')
344		   || (*str2 >= 'a' && *str2 <= 'z')
345		   || (*str2 >= '0' && *str2 <= '9')
346		   || *str2 == '_'))
347    str2++;
348  if (*str2)
349    {
350      saved_pos = str2;
351      saved_char = *str2;
352      *((char *) str2) = '\0';
353      str2++;
354      while (*str2 && !((*str2 >= 'A' && *str2 <= 'Z')
355			|| (*str2 >= 'a' && *str2 <= 'z')
356			|| (*str2 >= '0' && *str2 <= '9')
357			|| *str2 == '_'))
358	str2++;
359    }
360  else
361    str2 = NULL;
362
363  ret = get_err_from_str_one (str, err, &have_source, &have_code);
364  if (ret && str2)
365    ret = get_err_from_str_one (str2, err, &have_source, &have_code);
366
367  if (saved_pos)
368    *saved_pos = saved_char;
369  return ret;
370}
371
372
373
374int
375main (int argc, char *argv[])
376{
377  int i = 1;
378  int listmode = 0;
379  const char *source_sym;
380  const char *error_sym;
381  gpg_error_t err;
382
383#ifndef GPG_ERR_INITIALIZED
384  gpg_err_init ();
385#endif
386
387  i18n_init ();
388
389
390  if (argc == 1)
391    {
392      fprintf (stderr, _("Usage: %s GPG-ERROR [...]\n"),
393               strrchr (argv[0],'/')? (strrchr (argv[0], '/')+1): argv[0]);
394      exit (1);
395    }
396  else if (argc == 2 && !strcmp (argv[1], "--version"))
397    {
398      fputs ("gpg-error (" PACKAGE_NAME ") " PACKAGE_VERSION "\n", stdout);
399      exit (0);
400    }
401  else if (argc == 2 && !strcmp (argv[1], "--list"))
402    {
403      listmode = 1;
404    }
405
406
407  if (listmode)
408    {
409      for (i=0; i <  GPG_ERR_SOURCE_DIM; i++)
410        {
411          /* We use error code 1 because gpg_err_make requires a
412             non-zero error code. */
413          err = gpg_err_make (i, 1);
414          err -= 1;
415	  source_sym = gpg_strsource_sym (err);
416          if (source_sym)
417            printf ("%u = (%u, -) = (%s, -) = (%s, -)\n",
418                    err, gpg_err_source (err),
419                    source_sym, gpg_strsource (err));
420        }
421      for (i=0; i <  GPG_ERR_CODE_DIM; i++)
422        {
423          err = gpg_err_make (GPG_ERR_SOURCE_UNKNOWN, i);
424	  error_sym = gpg_strerror_sym (err);
425          if (error_sym)
426            printf ("%u = (-, %u) = (-, %s) = (-, %s)\n",
427                    err, gpg_err_code (err),
428                    error_sym, gpg_strerror (err));
429        }
430
431      i = argc;  /* Don't run the usual stuff.  */
432    }
433  while (i < argc)
434    {
435      if (get_err_from_number (argv[i], &err)
436	  || get_err_from_symbol (argv[i], &err)
437	  || get_err_from_str (argv[i], &err))
438	{
439	  source_sym = gpg_strsource_sym (err);
440	  error_sym = gpg_strerror_sym (err);
441
442	  printf ("%u = (%u, %u) = (%s, %s) = (%s, %s)\n",
443		  err, gpg_err_source (err), gpg_err_code (err),
444		  source_sym ? source_sym : "-", error_sym ? error_sym : "-",
445		  gpg_strsource (err), gpg_strerror (err));
446	}
447      else
448	fprintf (stderr, _("%s: warning: could not recognize %s\n"),
449		 argv[0], argv[i]);
450      i++;
451    }
452
453  exit (0);
454}
455