1/* main.c: The main program for bc.  */
2
3/*  This file is part of GNU bc.
4    Copyright (C) 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License , or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; see the file COPYING.  If not, write to
18      The Free Software Foundation, Inc.
19      59 Temple Place, Suite 330
20      Boston, MA 02111 USA
21
22    You may contact the author by:
23       e-mail:  philnelson@acm.org
24      us-mail:  Philip A. Nelson
25                Computer Science Department, 9062
26                Western Washington University
27                Bellingham, WA 98226-9062
28
29*************************************************************************/
30
31#include "bcdefs.h"
32#include <signal.h>
33#include "global.h"
34#include "proto.h"
35#include "getopt.h"
36
37
38/* Variables for processing multiple files. */
39static char first_file;
40
41/* Points to the last node in the file name list for easy adding. */
42static file_node *last = NULL;
43
44/* long option support */
45static struct option long_options[] =
46{
47  {"compile",  0, &compile_only, TRUE},
48  {"help",     0, 0,             'h'},
49  {"interactive", 0, 0,          'i'},
50  {"mathlib",  0, &use_math,     TRUE},
51  {"quiet",    0, &quiet,        TRUE},
52  {"standard", 0, &std_only,     TRUE},
53  {"version",  0, 0,             'v'},
54  {"warn",     0, &warn_not_std, TRUE},
55
56  {0, 0, 0, 0}
57};
58
59
60void
61usage (char *progname)
62{
63  printf ("usage: %s [options] [file ...]\n%s%s%s%s%s%s%s", progname,
64          "  -h  --help         print this usage and exit\n",
65	  "  -i  --interactive  force interactive mode\n",
66	  "  -l  --mathlib      use the predefine math routnes\n",
67	  "  -q  --quiet        don't print initial banner\n",
68	  "  -s  --standard     non-standard bc constructs are errors\n",
69	  "  -w  --warn         warn about non-standard bc constructs\n",
70	  "  -v  --version      print version information and exit\n");
71}
72
73
74void
75parse_args (argc, argv)
76     int argc;
77     char **argv;
78{
79  int optch;
80  int long_index;
81  file_node *temp;
82
83  /* Force getopt to initialize.  Depends on GNU getopt. */
84  optind = 0;
85
86  /* Parse the command line */
87  while (1)
88    {
89      optch = getopt_long (argc, argv, "chilqswv", long_options, &long_index);
90
91      if (optch == EOF)  /* End of arguments. */
92	break;
93
94      switch (optch)
95	{
96	case 'c':  /* compile only */
97	  compile_only = TRUE;
98	  break;
99
100	case 'h':  /* help */
101	  usage(argv[0]);
102	  exit (0);
103	  break;
104
105	case 'i':  /* force interactive */
106	  interactive = TRUE;
107	  break;
108
109	case 'l':  /* math lib */
110	  use_math = TRUE;
111	  break;
112
113	case 'q':  /* quiet mode */
114	  quiet = TRUE;
115	  break;
116
117	case 's':  /* Non standard features give errors. */
118	  std_only = TRUE;
119	  break;
120
121	case 'v':  /* Print the version. */
122	  show_bc_version ();
123	  exit (0);
124	  break;
125
126	case 'w':  /* Non standard features give warnings. */
127	  warn_not_std = TRUE;
128	  break;
129
130	default:
131	  usage(argv[0]);
132	  exit (1);
133	}
134    }
135
136  /* Add file names to a list of files to process. */
137  while (optind < argc)
138    {
139      temp = (file_node *) bc_malloc(sizeof(file_node));
140      temp->name = argv[optind];
141      temp->next = NULL;
142      if (last == NULL)
143	file_names = temp;
144      else
145	last->next = temp;
146      last = temp;
147      optind++;
148    }
149}
150
151/* The main program for bc. */
152int
153main (argc, argv)
154     int argc;
155     char *argv[];
156{
157  char *env_value;
158  char *env_argv[30];
159  int   env_argc;
160
161  /* Initialize many variables. */
162  compile_only = FALSE;
163  use_math = FALSE;
164  warn_not_std = FALSE;
165  std_only = FALSE;
166  if (isatty(0) && isatty(1))
167    interactive = TRUE;
168  else
169    interactive = FALSE;
170  quiet = FALSE;
171  file_names = NULL;
172
173#ifdef HAVE_SETVBUF
174  /* attempt to simplify interaction with applications such as emacs */
175  (void) setvbuf(stdout, NULL, _IOLBF, 0);
176#endif
177
178  /* Environment arguments. */
179  env_value = getenv ("BC_ENV_ARGS");
180  if (env_value != NULL)
181    {
182      env_argc = 1;
183      env_argv[0] = "BC_ENV_ARGS";
184      while (*env_value != 0)
185	{
186	  if (*env_value != ' ')
187	    {
188	      env_argv[env_argc++] = env_value;
189	      while (*env_value != ' ' && *env_value != 0)
190		env_value++;
191	      if (*env_value != 0)
192		{
193		  *env_value = 0;
194		  env_value++;
195		}
196	    }
197	  else
198	    env_value++;
199	}
200      parse_args (env_argc, env_argv);
201    }
202
203  /* Command line arguments. */
204  parse_args (argc, argv);
205
206  /* Other environment processing. */
207  if (getenv ("POSIXLY_CORRECT") != NULL)
208    std_only = TRUE;
209
210  env_value = getenv ("BC_LINE_LENGTH");
211  if (env_value != NULL)
212    {
213      line_size = atoi (env_value);
214      if (line_size < 2)
215	line_size = 70;
216    }
217  else
218    line_size = 70;
219
220  /* Initialize the machine.  */
221  init_storage();
222  init_load();
223
224  /* Set up interrupts to print a message. */
225  if (interactive)
226    signal (SIGINT, use_quit);
227
228  /* Initialize the front end. */
229  init_tree();
230  init_gen ();
231  is_std_in = FALSE;
232  first_file = TRUE;
233  if (!open_new_file ())
234    exit (1);
235
236#if defined(LIBEDIT)
237  if (interactive) {
238    /* Enable libedit support. */
239    edit = el_init ("bc", stdin, stdout, stderr);
240    hist = history_init();
241    el_set (edit, EL_EDITOR, "emacs");
242    el_set (edit, EL_HIST, history, hist);
243    el_set (edit, EL_PROMPT, null_prompt);
244    el_source (edit, NULL);
245    history (hist, &histev, H_SETSIZE, INT_MAX);
246  }
247#endif
248
249#if defined(READLINE)
250  if (interactive) {
251    /* Readline support.  Set both application name and input file. */
252    rl_readline_name = "bc";
253    rl_instream = stdin;
254    using_history ();
255  }
256#endif
257
258  /* Do the parse. */
259  yyparse ();
260
261  /* End the compile only output with a newline. */
262  if (compile_only)
263    printf ("\n");
264
265  exit (0);
266}
267
268
269/* This is the function that opens all the files.
270   It returns TRUE if the file was opened, otherwise
271   it returns FALSE. */
272
273int
274open_new_file ()
275{
276  FILE *new_file;
277  file_node *temp;
278
279  /* Set the line number. */
280  line_no = 1;
281
282  /* Check to see if we are done. */
283  if (is_std_in) return (FALSE);
284
285  /* Open the other files. */
286  if (use_math && first_file)
287    {
288      /* Load the code from a precompiled version of the math libarary. */
289      extern char *libmath[];
290      char **mstr;
291      char tmp;
292      /* These MUST be in the order of first mention of each function.
293	 That is why "a" comes before "c" even though "a" is defined after
294	 after "c".  "a" is used in "s"! */
295      tmp = lookup ("e", FUNCT);
296      tmp = lookup ("l", FUNCT);
297      tmp = lookup ("s", FUNCT);
298      tmp = lookup ("a", FUNCT);
299      tmp = lookup ("c", FUNCT);
300      tmp = lookup ("j", FUNCT);
301      mstr = libmath;
302      while (*mstr) {
303           load_code (*mstr);
304	   mstr++;
305      }
306    }
307
308  /* One of the argv values. */
309  if (file_names != NULL)
310    {
311      new_file = fopen (file_names->name, "r");
312      if (new_file != NULL)
313	{
314	  new_yy_file (new_file);
315	  temp = file_names;
316	  file_name  = temp->name;
317	  file_names = temp->next;
318	  free (temp);
319	  return TRUE;
320	}
321      fprintf (stderr, "File %s is unavailable.\n", file_names->name);
322      exit (1);
323    }
324
325  /* If we fall through to here, we should return stdin. */
326  new_yy_file (stdin);
327  is_std_in = TRUE;
328  return TRUE;
329}
330
331
332/* Set yyin to the new file. */
333
334void
335new_yy_file (file)
336     FILE *file;
337{
338  if (!first_file) fclose (yyin);
339  yyin = file;
340  first_file = FALSE;
341}
342
343
344/* Message to use quit.  */
345
346void
347use_quit (sig)
348     int sig;
349{
350  printf ("\n(interrupt) use quit to exit.\n");
351  signal (SIGINT, use_quit);
352}
353