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 <http://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 129 : parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
73 : {
74 129 : int no_more_options = 0;
75 :
76 258 : 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 129 : }
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 774 : load (scheme *sc, char *file_name,
125 : int lookup_in_cwd, int lookup_in_path)
126 : {
127 774 : gpg_error_t err = 0;
128 : size_t n;
129 : const char *directory;
130 774 : char *qualified_name = file_name;
131 : int use_path;
132 774 : FILE *h = NULL;
133 :
134 774 : use_path =
135 774 : lookup_in_path && ! (file_name[0] == '/' || scmpath_len == 0);
136 :
137 774 : if (file_name[0] == '/' || lookup_in_cwd || scmpath_len == 0)
138 : {
139 129 : h = fopen (file_name, "r");
140 129 : if (! h)
141 1 : err = gpg_error_from_syserror ();
142 : }
143 :
144 774 : if (h == NULL && use_path)
145 1293 : for (directory = scmpath, n = scmpath_len; n;
146 1 : directory += strlen (directory) + 1, n--)
147 : {
148 647 : if (asprintf (&qualified_name, "%s/%s", directory, file_name) < 0)
149 0 : return gpg_error_from_syserror ();
150 :
151 647 : h = fopen (qualified_name, "r");
152 647 : if (h)
153 646 : 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 774 : 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 774 : if (verbose > 1)
176 0 : fprintf (stderr, "Loading %s...\n", qualified_name);
177 774 : scheme_load_named_file (sc, h, qualified_name);
178 773 : fclose (h);
179 :
180 773 : 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 773 : if (file_name != qualified_name)
188 645 : free (qualified_name);
189 773 : return 0;
190 : }
191 :
192 :
193 :
194 : int
195 129 : 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 129 : char pathsep = ':';
206 : #endif
207 129 : char *script = NULL;
208 :
209 : /* Save argv[0] so that we can re-exec. */
210 129 : argv0 = argv[0];
211 :
212 : /* Parse path. */
213 129 : if (getenv ("GPGSCM_PATH"))
214 129 : scmpath = getenv ("GPGSCM_PATH");
215 :
216 129 : p = scmpath = strdup (scmpath);
217 129 : if (p == NULL)
218 0 : return 2;
219 :
220 129 : if (*p)
221 129 : scmpath_len++;
222 12291 : for (; *p; p++)
223 12162 : if (*p == pathsep)
224 128 : *p = 0, scmpath_len++;
225 :
226 129 : set_strusage (my_strusage);
227 129 : log_set_prefix ("gpgscm", GPGRT_LOG_WITH_PREFIX);
228 :
229 : /* Make sure that our subsystems are ready. */
230 129 : i18n_init ();
231 129 : init_common_subsystems (&argc, &argv);
232 :
233 129 : if (!gcry_check_version (GCRYPT_VERSION))
234 : {
235 0 : fputs ("libgcrypt version mismatch\n", stderr);
236 0 : exit (2);
237 : }
238 :
239 : /* Parse the command line. */
240 129 : pargs.argc = &argc;
241 129 : pargs.argv = &argv;
242 129 : pargs.flags = 0;
243 129 : parse_arguments (&pargs, opts);
244 :
245 129 : if (log_get_errorcount (0))
246 0 : exit (2);
247 :
248 129 : sc = scheme_init_new_custom_alloc (gcry_malloc, gcry_free);
249 129 : if (! sc) {
250 0 : fprintf (stderr, "Could not initialize TinyScheme!\n");
251 0 : return 2;
252 : }
253 129 : scheme_set_input_port_file (sc, stdin);
254 129 : scheme_set_output_port_file (sc, stderr);
255 :
256 129 : if (argc)
257 : {
258 129 : script = argv[0];
259 129 : argc--, argv++;
260 : }
261 :
262 129 : err = load (sc, "init.scm", 0, 1);
263 129 : if (! err)
264 129 : err = load (sc, "ffi.scm", 0, 1);
265 129 : if (! err)
266 129 : err = ffi_init (sc, argv0, argc, (const char **) argv);
267 129 : if (! err)
268 129 : err = load (sc, "lib.scm", 0, 1);
269 129 : if (! err)
270 129 : err = load (sc, "repl.scm", 0, 1);
271 129 : if (! err)
272 129 : err = load (sc, "tests.scm", 0, 1);
273 129 : if (err)
274 : {
275 0 : fprintf (stderr, "Error initializing gpgscm: %s.\n",
276 : gpg_strerror (err));
277 0 : exit (2);
278 : }
279 :
280 129 : if (script == NULL)
281 : {
282 : /* Interactive shell. */
283 0 : fprintf (stderr, "gpgscm/"ts_banner".\n");
284 0 : scheme_load_string (sc, "(interactive-repl)");
285 : }
286 : else
287 : {
288 129 : err = load (sc, script, 1, 1);
289 128 : if (err)
290 0 : log_fatal ("%s: %s", script, gpg_strerror (err));
291 : }
292 :
293 128 : scheme_deinit (sc);
294 128 : xfree (sc);
295 128 : return EXIT_SUCCESS;
296 : }
|