Line data Source code
1 : /* TinyScheme-based test driver.
2 : *
3 : * Copyright (C) 2016 g10 code GmbH
4 : *
5 : * This file is part of GnuPG.
6 : *
7 : * GnuPG is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU General Public License as published by
9 : * the Free Software Foundation; either version 3 of the License, or
10 : * (at your option) any later version.
11 : *
12 : * GnuPG is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU General Public License
18 : * along with this program; if not, see <https://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <config.h>
22 :
23 : #include <assert.h>
24 : #include <ctype.h>
25 : #include <errno.h>
26 : #include <gcrypt.h>
27 : #include <gpg-error.h>
28 : #include <stdio.h>
29 : #include <stdlib.h>
30 : #include <string.h>
31 : #include <unistd.h>
32 :
33 : #include "private.h"
34 : #include "scheme.h"
35 : #include "scheme-private.h"
36 : #include "ffi.h"
37 : #include "i18n.h"
38 : #include "../../common/argparse.h"
39 : #include "../../common/init.h"
40 : #include "../../common/logging.h"
41 : #include "../../common/strlist.h"
42 : #include "../../common/sysutils.h"
43 : #include "../../common/util.h"
44 :
45 : /* The TinyScheme banner. Unfortunately, it isn't in the header
46 : file. */
47 : #define ts_banner "TinyScheme 1.41"
48 :
49 : int verbose;
50 :
51 :
52 :
53 : /* Constants to identify the commands and options. */
54 : enum cmd_and_opt_values
55 : {
56 : aNull = 0,
57 : oVerbose = 'v',
58 : };
59 :
60 : /* The list of commands and options. */
61 : static ARGPARSE_OPTS opts[] =
62 : {
63 : ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
64 : ARGPARSE_end (),
65 : };
66 :
67 : char *scmpath = "";
68 : size_t scmpath_len = 0;
69 :
70 : /* Command line parsing. */
71 : static void
72 51 : parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
73 : {
74 51 : int no_more_options = 0;
75 :
76 102 : while (!no_more_options && optfile_parse (NULL, NULL, NULL, pargs, popts))
77 : {
78 0 : switch (pargs->r_opt)
79 : {
80 : case oVerbose:
81 0 : verbose++;
82 0 : break;
83 :
84 : default:
85 0 : pargs->err = 2;
86 0 : break;
87 : }
88 : }
89 51 : }
90 :
91 : /* Print usage information and and provide strings for help. */
92 : static const char *
93 0 : my_strusage( int level )
94 : {
95 : const char *p;
96 :
97 0 : switch (level)
98 : {
99 0 : case 11: p = "gpgscm (@GNUPG@)";
100 0 : break;
101 0 : case 13: p = VERSION; break;
102 0 : case 17: p = PRINTABLE_OS_NAME; break;
103 0 : case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
104 :
105 : case 1:
106 : case 40:
107 0 : p = _("Usage: gpgscm [options] [file] (-h for help)");
108 0 : break;
109 : case 41:
110 0 : p = _("Syntax: gpgscm [options] [file]\n"
111 : "Execute the given Scheme program, or spawn interactive shell.\n");
112 0 : break;
113 :
114 0 : default: p = NULL; break;
115 : }
116 0 : return p;
117 : }
118 :
119 :
120 : /* Load the Scheme program from FILE_NAME. If FILE_NAME is not an
121 : absolute path, and LOOKUP_IN_PATH is given, then it is qualified
122 : with the values in scmpath until the file is found. */
123 : static gpg_error_t
124 306 : load (scheme *sc, char *file_name,
125 : int lookup_in_cwd, int lookup_in_path)
126 : {
127 306 : gpg_error_t err = 0;
128 : size_t n;
129 : const char *directory;
130 306 : char *qualified_name = file_name;
131 : int use_path;
132 306 : FILE *h = NULL;
133 :
134 306 : use_path =
135 306 : lookup_in_path && ! (file_name[0] == '/' || scmpath_len == 0);
136 :
137 306 : if (file_name[0] == '/' || lookup_in_cwd || scmpath_len == 0)
138 : {
139 51 : h = fopen (file_name, "r");
140 51 : if (! h)
141 1 : err = gpg_error_from_syserror ();
142 : }
143 :
144 306 : if (h == NULL && use_path)
145 513 : for (directory = scmpath, n = scmpath_len; n;
146 1 : directory += strlen (directory) + 1, n--)
147 : {
148 257 : if (asprintf (&qualified_name, "%s/%s", directory, file_name) < 0)
149 0 : return gpg_error_from_syserror ();
150 :
151 257 : h = fopen (qualified_name, "r");
152 257 : if (h)
153 256 : break;
154 :
155 1 : if (n > 1)
156 : {
157 1 : free (qualified_name);
158 1 : continue; /* Try again! */
159 : }
160 :
161 0 : err = gpg_error_from_syserror ();
162 : }
163 :
164 306 : if (h == NULL)
165 : {
166 : /* Failed and no more elements in scmpath to try. */
167 0 : fprintf (stderr, "Could not read %s: %s.\n",
168 : qualified_name, gpg_strerror (err));
169 0 : if (lookup_in_path)
170 0 : fprintf (stderr,
171 : "Consider using GPGSCM_PATH to specify the location "
172 : "of the Scheme library.\n");
173 0 : return err;
174 : }
175 306 : if (verbose > 1)
176 0 : fprintf (stderr, "Loading %s...\n", qualified_name);
177 306 : scheme_load_named_file (sc, h, qualified_name);
178 305 : fclose (h);
179 :
180 305 : if (sc->retcode)
181 : {
182 0 : if (sc->nesting)
183 0 : fprintf (stderr, "%s: Unbalanced parenthesis\n", qualified_name);
184 0 : return gpg_error (GPG_ERR_GENERAL);
185 : }
186 :
187 305 : if (file_name != qualified_name)
188 255 : free (qualified_name);
189 305 : return 0;
190 : }
191 :
192 :
193 :
194 : int
195 51 : main (int argc, char **argv)
196 : {
197 : gpg_error_t err;
198 : char *argv0;
199 : ARGPARSE_ARGS pargs;
200 : scheme *sc;
201 : char *p;
202 : #if _WIN32
203 : char pathsep = ';';
204 : #else
205 51 : char pathsep = ':';
206 : #endif
207 51 : char *script = NULL;
208 :
209 : /* Save argv[0] so that we can re-exec. */
210 51 : argv0 = argv[0];
211 :
212 : /* Parse path. */
213 51 : if (getenv ("GPGSCM_PATH"))
214 51 : scmpath = getenv ("GPGSCM_PATH");
215 :
216 51 : p = scmpath = strdup (scmpath);
217 51 : if (p == NULL)
218 0 : return 2;
219 :
220 51 : if (*p)
221 51 : scmpath_len++;
222 4725 : for (; *p; p++)
223 4674 : if (*p == pathsep)
224 50 : *p = 0, scmpath_len++;
225 :
226 51 : set_strusage (my_strusage);
227 51 : log_set_prefix ("gpgscm", GPGRT_LOG_WITH_PREFIX);
228 :
229 : /* Make sure that our subsystems are ready. */
230 51 : i18n_init ();
231 51 : init_common_subsystems (&argc, &argv);
232 :
233 51 : if (!gcry_check_version (NEED_LIBGCRYPT_VERSION))
234 : {
235 0 : fputs ("libgcrypt version mismatch\n", stderr);
236 0 : exit (2);
237 : }
238 :
239 : /* Parse the command line. */
240 51 : pargs.argc = &argc;
241 51 : pargs.argv = &argv;
242 51 : pargs.flags = 0;
243 51 : parse_arguments (&pargs, opts);
244 :
245 51 : if (log_get_errorcount (0))
246 0 : exit (2);
247 :
248 51 : sc = scheme_init_new_custom_alloc (gcry_malloc, gcry_free);
249 51 : if (! sc) {
250 0 : fprintf (stderr, "Could not initialize TinyScheme!\n");
251 0 : return 2;
252 : }
253 51 : scheme_set_input_port_file (sc, stdin);
254 51 : scheme_set_output_port_file (sc, stderr);
255 :
256 51 : if (argc)
257 : {
258 51 : script = argv[0];
259 51 : argc--, argv++;
260 : }
261 :
262 51 : err = load (sc, "init.scm", 0, 1);
263 51 : if (! err)
264 51 : err = load (sc, "ffi.scm", 0, 1);
265 51 : if (! err)
266 51 : err = ffi_init (sc, argv0, script ? script : "interactive",
267 : argc, (const char **) argv);
268 51 : if (! err)
269 51 : err = load (sc, "lib.scm", 0, 1);
270 51 : if (! err)
271 51 : err = load (sc, "repl.scm", 0, 1);
272 51 : if (! err)
273 51 : err = load (sc, "tests.scm", 0, 1);
274 51 : if (err)
275 : {
276 0 : fprintf (stderr, "Error initializing gpgscm: %s.\n",
277 : gpg_strerror (err));
278 0 : exit (2);
279 : }
280 :
281 51 : if (script == NULL)
282 : {
283 : /* Interactive shell. */
284 0 : fprintf (stderr, "gpgscm/"ts_banner".\n");
285 0 : scheme_load_string (sc, "(interactive-repl)");
286 : }
287 : else
288 : {
289 51 : err = load (sc, script, 1, 1);
290 50 : if (err)
291 0 : log_fatal ("%s: %s", script, gpg_strerror (err));
292 : }
293 :
294 50 : scheme_load_string (sc, "(*run-atexit-handlers*)");
295 50 : scheme_deinit (sc);
296 50 : xfree (sc);
297 50 : return EXIT_SUCCESS;
298 : }
|