2007-08-30 Marcus Brinkmann <marcus@g10code.de>
[gpg4win.git] / src / gpgwrap.c
1 /* gpgwrap.c - Wrapper to call gpg udner Windows.
2  * Copyright (C) 2007 g10 Code GmbH
3  *
4  * This file is part of Gpg4win.
5  *
6  * Gpg4win 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; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Gpg4win is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <process.h>
29 #include <windows.h>
30
31
32 /* Return a copy of ARGV, but with proper quoting.  To release the
33    copy, you have to free argv_quoted[0] and argv_quoted.  */
34 static char **
35 build_commandline (const char * const *argv)
36 {
37   int i;
38   int j;
39   int n = 0;
40   char *buf;
41   char *p;
42   char **argv_quoted;
43
44   /* We have to quote some things because under Windows the program
45      parses the commandline and does some unquoting.  We enclose the
46      whole argument in double-quotes, and escape literal double-quotes
47      as well as backslashes with a backslash.  We end up with a
48      trailing space at the end of the line, but that is harmless.  */
49   for (i = 0; argv[i]; i++)
50     {
51       p = (char *) argv[i];
52       /* The leading double-quote.  */
53       n++;
54       while (*p)
55         {
56           /* An extra one for each literal that must be escaped.  */
57           if (*p == '\\' || *p == '"')
58             n++;
59           n++;
60           p++;
61         }
62       /* The trailing double-quote and the delimiter.  */
63       n += 2;
64     }
65   /* And a trailing zero.  */
66   n++;
67
68   /* Allocate a new vector.  */
69   argv_quoted = malloc (sizeof (char *) * (i + 1));
70   if (!argv_quoted)
71     return NULL;
72
73   buf = p = malloc (n);
74   if (!buf)
75     {
76       free (argv_quoted);
77       return NULL;
78     }
79
80   for (i = 0; argv[i]; i++)
81     {
82       const char *argvp = argv[i];
83
84       argv_quoted[i] = p;
85
86       *(p++) = '"';
87       while (*argvp)
88         {
89           if (*argvp == '\\' || *argvp == '"')
90             *(p++) = '\\';
91           *(p++) = *(argvp++);
92         }
93       *(p++) = '"';
94       *(p++) = 0;
95     }
96   *(p++) = 0;
97   argv_quoted[i] = NULL;
98
99   return argv_quoted;
100 }
101
102
103 int
104 main (int argc, const char * const *argv)
105 {
106   int rc;
107   char pgm[MAX_PATH+100];
108   char *p, *p0;
109   char **argv_quoted;
110
111   if (!GetModuleFileNameA (NULL, pgm, sizeof (pgm) - 1))
112     {
113       fprintf (stderr, "gpgwrap: error getting my own name: rc=%d\n",
114                GetLastError());
115       return 2;
116     }
117
118   /* Remove one directory part of the file name.  */
119   p = strrchr (pgm, '\\');
120   if (!p)
121     goto leave;
122   *p = 0;
123   p0 = strrchr (pgm, '\\');
124   *p = '\\';
125   if (!p0)
126     goto leave;
127   while (*p)
128     *p0++ = *p++;
129   *p0 = 0;
130
131   /* Hack to output our own version along with the real file name
132      before the actual, we require that the --version option is given
133      twice. */
134   if (argc > 2
135       && !strcmp(argv[1], "--version")
136       && !strcmp(argv[2], "--version"))
137     {
138       fputs ("gpgwrap (Gpg4win) " PACKAGE_VERSION " ;", stdout);
139       fputs (pgm, stdout);
140       fputc ('\n', stdout);
141       fflush (stdout);
142     }
143
144   argv_quoted = build_commandline (argv);
145   if (!argv_quoted)
146     goto leave;
147
148   /* Using execv does not replace the existing program image, but
149      spawns a new one and daemonizes it, confusing the command line
150      interpreter.  So we have to use spawnv.  */
151   rc = _spawnv (_P_WAIT, pgm, (const char **) argv_quoted);
152   if (rc < 0)
153     {
154       fprintf (stderr, "gpgwrap: executing `%s' failed: %s\n",
155                pgm, strerror (errno));
156       return 2;
157     }
158
159   return rc;
160
161  leave:
162   fprintf (stderr, "gpgwrap: internal error parsing my own name `%s'\n",
163            pgm);
164   return 2;
165 }