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. */
44static char first_file;
45
46/* Points to the last node in the file name list for easy adding. */
47static file_node *last = NULL;
48
49#if defined(LIBEDIT)
50/* The prompt for libedit. */
51char el_pmtchars[] = "";
52static char *el_pmtfunc(void);
53static char *el_pmtfunc(void) { return el_pmtchars; }
54#endif
55
56/* long option support */
57static 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
72static void
73usage (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
86static void
87parse_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. */
169int
170main (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
280int
281open_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
340void
341new_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
351void
352use_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