1 /* $NetBSD: main.c,v 1.2 2017/04/18 04:35:18 maya Exp $ */
2
3 /*
4 * Copyright (C) 1991-1994, 1997, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
5 * Copyright (C) 2016-2017 Philip A. Nelson.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The names Philip A. Nelson and Free Software Foundation may not be
18 * used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY PHILIP A. NELSON ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL PHILIP A. NELSON OR THE FREE SOFTWARE FOUNDATION BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 * THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /* main.c: The main program for bc. */
35
36 #include "bcdefs.h"
37 #include <signal.h>
38 #include <errno.h>
39 #include "proto.h"
40 #include "getopt.h"
41
42
43 /* Variables for processing multiple files. */
44 static char first_file;
45
46 /* Points to the last node in the file name list for easy adding. */
47 static file_node *last = NULL;
48
49 #if defined(LIBEDIT)
50 /* The prompt for libedit. */
51 char el_pmtchars[] = "";
52 static char *el_pmtfunc(void);
el_pmtfunc(void)53 static char *el_pmtfunc(void) { return el_pmtchars; }
54 #endif
55
56 /* long option support */
57 static struct option long_options[] =
58 {
59 {"compile", 0, &compile_only, TRUE},
60 {"help", 0, 0, 'h'},
61 {"interactive", 0, 0, 'i'},
62 {"mathlib", 0, &use_math, TRUE},
63 {"quiet", 0, &quiet, TRUE},
64 {"standard", 0, &std_only, TRUE},
65 {"version", 0, 0, 'v'},
66 {"warn", 0, &warn_not_std, TRUE},
67
68 {0, 0, 0, 0}
69 };
70
71
72 static void
usage(const char * progname)73 usage (const char *progname)
74 {
75 printf ("usage: %s [options] [file ...]\n%s%s%s%s%s%s%s", progname,
76 " -h --help print this usage and exit\n",
77 " -i --interactive force interactive mode\n",
78 " -l --mathlib use the predefined math routines\n",
79 " -q --quiet don't print initial banner\n",
80 " -s --standard non-standard bc constructs are errors\n",
81 " -w --warn warn about non-standard bc constructs\n",
82 " -v --version print version information and exit\n");
83 }
84
85
86 static void
parse_args(int argc,char ** argv)87 parse_args (int argc, char **argv)
88 {
89 int optch;
90 int long_index;
91 file_node *temp;
92
93 /* Force getopt to initialize. Depends on GNU getopt. */
94 optind = 0;
95
96 /* Parse the command line */
97 while (1)
98 {
99 optch = getopt_long (argc, argv, "chilqswv", long_options, &long_index);
100
101 if (optch == EOF) /* End of arguments. */
102 break;
103
104 switch (optch)
105 {
106 case 0: /* Long option setting a var. */
107 break;
108
109 case 'c': /* compile only */
110 compile_only = TRUE;
111 break;
112
113 case 'h': /* help */
114 usage(argv[0]);
115 bc_exit (0);
116 /* NOTREACHED */
117
118 case 'i': /* force interactive */
119 interactive = TRUE;
120 break;
121
122 case 'l': /* math lib */
123 use_math = TRUE;
124 break;
125
126 case 'q': /* quiet mode */
127 quiet = TRUE;
128 break;
129
130 case 's': /* Non standard features give errors. */
131 std_only = TRUE;
132 break;
133
134 case 'v': /* Print the version. */
135 show_bc_version ();
136 bc_exit (0);
137 /* NOTREACHED */
138
139 case 'w': /* Non standard features give warnings. */
140 warn_not_std = TRUE;
141 break;
142
143 default:
144 usage(argv[0]);
145 bc_exit (1);
146 }
147 }
148
149 #ifdef QUIET
150 quiet = TRUE;
151 #endif
152
153 /* Add file names to a list of files to process. */
154 while (optind < argc)
155 {
156 temp = bc_malloc(sizeof(file_node));
157 temp->name = argv[optind];
158 temp->next = NULL;
159 if (last == NULL)
160 file_names = temp;
161 else
162 last->next = temp;
163 last = temp;
164 optind++;
165 }
166 }
167
168 /* The main program for bc. */
169 int
main(int argc,char ** argv)170 main (int argc, char **argv)
171 {
172 char *env_value;
173 char *env_argv[30];
174 int env_argc;
175
176 /* Interactive? */
177 if (isatty(0) && isatty(1))
178 interactive = TRUE;
179
180 #ifdef HAVE_SETVBUF
181 /* attempt to simplify interaction with applications such as emacs */
182 (void) setvbuf(stdout, NULL, _IOLBF, 0);
183 #endif
184
185 /* Environment arguments. */
186 env_value = getenv ("BC_ENV_ARGS");
187 if (env_value != NULL)
188 {
189 env_argc = 1;
190 env_argv[0] = strdup("BC_ENV_ARGS");
191 while (*env_value != 0)
192 {
193 if (*env_value != ' ')
194 {
195 env_argv[env_argc++] = env_value;
196 while (*env_value != ' ' && *env_value != 0)
197 env_value++;
198 if (*env_value != 0)
199 {
200 *env_value = 0;
201 env_value++;
202 }
203 }
204 else
205 env_value++;
206 }
207 parse_args (env_argc, env_argv);
208 }
209
210 /* Command line arguments. */
211 parse_args (argc, argv);
212
213 /* Other environment processing. */
214 if (getenv ("POSIXLY_CORRECT") != NULL)
215 std_only = TRUE;
216
217 env_value = getenv ("BC_LINE_LENGTH");
218 if (env_value != NULL)
219 {
220 line_size = atoi (env_value);
221 if (line_size < 3 && line_size != 0)
222 line_size = 70;
223 }
224 else
225 line_size = 70;
226
227 /* Initialize the machine. */
228 init_storage();
229 init_load();
230
231 /* Set up interrupts to print a message. */
232 if (interactive)
233 signal (SIGINT, use_quit);
234
235 /* Initialize the front end. */
236 init_tree();
237 init_gen ();
238 is_std_in = FALSE;
239 first_file = TRUE;
240 if (!open_new_file ())
241 bc_exit (1);
242
243 #if defined(LIBEDIT)
244 if (interactive) {
245 /* Enable libedit support. */
246 edit = el_init ("bc", stdin, stdout, stderr);
247 hist = history_init();
248 el_set (edit, EL_EDITOR, "emacs");
249 el_set (edit, EL_HIST, history, hist);
250 el_set (edit, EL_PROMPT, el_pmtfunc);
251 el_source (edit, NULL);
252 history (hist, &histev, H_SETSIZE, INT_MAX);
253 }
254 #endif
255
256 #if defined(READLINE)
257 if (interactive) {
258 /* Readline support. Set both application name and input file. */
259 rl_readline_name = "bc";
260 rl_instream = stdin;
261 using_history ();
262 }
263 #endif
264
265 /* Do the parse. */
266 yyparse ();
267
268 /* End the compile only output with a newline. */
269 if (compile_only)
270 printf ("\n");
271
272 bc_exit (0);
273 }
274
275
276 /* This is the function that opens all the files.
277 It returns TRUE if the file was opened, otherwise
278 it returns FALSE. */
279
280 int
open_new_file(void)281 open_new_file (void)
282 {
283 FILE *new_file;
284 file_node *temp;
285
286 /* Set the line number. */
287 line_no = 1;
288
289 /* Check to see if we are done. */
290 if (is_std_in) return (FALSE);
291
292 /* Open the other files. */
293 if (use_math && first_file)
294 {
295 /* Load the code from a precompiled version of the math libarary. */
296 CONST char **mstr;
297
298 /* These MUST be in the order of first mention of each function.
299 That is why "a" comes before "c" even though "a" is defined after
300 after "c". "a" is used in "s"! */
301 (void) lookup (strdup("e"), FUNCT);
302 (void) lookup (strdup("l"), FUNCT);
303 (void) lookup (strdup("s"), FUNCT);
304 (void) lookup (strdup("a"), FUNCT);
305 (void) lookup (strdup("c"), FUNCT);
306 (void) lookup (strdup("j"), FUNCT);
307 mstr = libmath;
308 while (*mstr) {
309 load_code (*mstr);
310 mstr++;
311 }
312 }
313
314 /* One of the argv values. */
315 if (file_names != NULL)
316 {
317 new_file = fopen (file_names->name, "r");
318 if (new_file != NULL)
319 {
320 new_yy_file (new_file);
321 temp = file_names;
322 file_name = temp->name;
323 file_names = temp->next;
324 free (temp);
325 return TRUE;
326 }
327 fprintf (stderr, "File %s is unavailable.\n", file_names->name);
328 bc_exit (1);
329 }
330
331 /* If we fall through to here, we should return stdin. */
332 new_yy_file (stdin);
333 is_std_in = TRUE;
334 return TRUE;
335 }
336
337
338 /* Set yyin to the new file. */
339
340 void
new_yy_file(FILE * file)341 new_yy_file (FILE *file)
342 {
343 if (!first_file) fclose (yyin);
344 yyin = file;
345 first_file = FALSE;
346 }
347
348
349 /* Message to use quit. */
350
351 void
use_quit(int sig)352 use_quit (int sig)
353 {
354 #ifdef DONTEXIT
355 int save = errno;
356 write (1, "\n(interrupt) use quit to exit.\n", 31);
357 signal (SIGINT, use_quit);
358 errno = save;
359 #else
360 write (1, "\n(interrupt) Exiting bc.\n", 26);
361 bc_exit(0);
362 #endif
363 }
364