xref: /netbsd-src/external/bsd/bc/dist/main.c (revision 341df6449e0758ecbc5c5009a483a1f5c365efea)
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