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	case 0:
131	  break;
132
133	default:
134	  usage(argv[0]);
135	  exit (1);
136	}
137    }
138
139  /* Add file names to a list of files to process. */
140  while (optind < argc)
141    {
142      temp = (file_node *) bc_malloc(sizeof(file_node));
143      temp->name = argv[optind];
144      temp->next = NULL;
145      if (last == NULL)
146	file_names = temp;
147      else
148	last->next = temp;
149      last = temp;
150      optind++;
151    }
152}
153
154/* The main program for bc. */
155int
156main (argc, argv)
157     int argc;
158     char *argv[];
159{
160  char *env_value;
161  char *env_argv[30];
162  int   env_argc;
163
164  /* Initialize many variables. */
165  compile_only = FALSE;
166  use_math = FALSE;
167  warn_not_std = FALSE;
168  std_only = FALSE;
169  if (isatty(0) && isatty(1))
170    interactive = TRUE;
171  else
172    interactive = FALSE;
173  quiet = FALSE;
174  file_names = NULL;
175
176#ifdef HAVE_SETVBUF
177  /* attempt to simplify interaction with applications such as emacs */
178  (void) setvbuf(stdout, NULL, _IOLBF, 0);
179#endif
180
181  /* Environment arguments. */
182  env_value = getenv ("BC_ENV_ARGS");
183  if (env_value != NULL)
184    {
185      env_argc = 1;
186      env_argv[0] = "BC_ENV_ARGS";
187      while (*env_value != 0)
188	{
189	  if (*env_value != ' ')
190	    {
191	      env_argv[env_argc++] = env_value;
192	      while (*env_value != ' ' && *env_value != 0)
193		env_value++;
194	      if (*env_value != 0)
195		{
196		  *env_value = 0;
197		  env_value++;
198		}
199	    }
200	  else
201	    env_value++;
202	}
203      parse_args (env_argc, env_argv);
204    }
205
206  /* Command line arguments. */
207  parse_args (argc, argv);
208
209  /* Other environment processing. */
210  if (getenv ("POSIXLY_CORRECT") != NULL)
211    std_only = TRUE;
212
213  env_value = getenv ("BC_LINE_LENGTH");
214  if (env_value != NULL)
215    {
216      line_size = atoi (env_value);
217      if (line_size < 2)
218	line_size = 70;
219    }
220  else
221    line_size = 70;
222
223  /* Initialize the machine.  */
224  init_storage();
225  init_load();
226
227  /* Set up interrupts to print a message. */
228  if (interactive)
229    signal (SIGINT, use_quit);
230
231  /* Initialize the front end. */
232  init_tree();
233  init_gen ();
234  is_std_in = FALSE;
235  first_file = TRUE;
236  if (!open_new_file ())
237    exit (1);
238
239#if defined(LIBEDIT)
240  if (interactive) {
241    /* Enable libedit support. */
242    edit = el_init ("bc", stdin, stdout, stderr);
243    hist = history_init();
244    el_set (edit, EL_EDITOR, "emacs");
245    el_set (edit, EL_HIST, history, hist);
246    el_set (edit, EL_PROMPT, null_prompt);
247    el_source (edit, NULL);
248    history (hist, &histev, H_SETSIZE, INT_MAX);
249  }
250#endif
251
252#if defined(READLINE)
253  if (interactive) {
254    /* Readline support.  Set both application name and input file. */
255    rl_readline_name = "bc";
256    rl_instream = stdin;
257    using_history ();
258  }
259#endif
260
261  /* Do the parse. */
262  yyparse ();
263
264  /* End the compile only output with a newline. */
265  if (compile_only)
266    printf ("\n");
267
268  exit (0);
269}
270
271
272/* This is the function that opens all the files.
273   It returns TRUE if the file was opened, otherwise
274   it returns FALSE. */
275
276int
277open_new_file ()
278{
279  FILE *new_file;
280  file_node *temp;
281
282  /* Set the line number. */
283  line_no = 1;
284
285  /* Check to see if we are done. */
286  if (is_std_in) return (FALSE);
287
288  /* Open the other files. */
289  if (use_math && first_file)
290    {
291      /* Load the code from a precompiled version of the math libarary. */
292      extern char *libmath[];
293      char **mstr;
294      char tmp;
295      /* These MUST be in the order of first mention of each function.
296	 That is why "a" comes before "c" even though "a" is defined after
297	 after "c".  "a" is used in "s"! */
298      tmp = lookup ("e", FUNCT);
299      tmp = lookup ("l", FUNCT);
300      tmp = lookup ("s", FUNCT);
301      tmp = lookup ("a", FUNCT);
302      tmp = lookup ("c", FUNCT);
303      tmp = lookup ("j", FUNCT);
304      mstr = libmath;
305      while (*mstr) {
306           load_code (*mstr);
307	   mstr++;
308      }
309    }
310
311  /* One of the argv values. */
312  if (file_names != NULL)
313    {
314      new_file = fopen (file_names->name, "r");
315      if (new_file != NULL)
316	{
317	  new_yy_file (new_file);
318	  temp = file_names;
319	  file_name  = temp->name;
320	  file_names = temp->next;
321	  free (temp);
322	  return TRUE;
323	}
324      fprintf (stderr, "File %s is unavailable.\n", file_names->name);
325      exit (1);
326    }
327
328  /* If we fall through to here, we should return stdin. */
329  new_yy_file (stdin);
330  is_std_in = TRUE;
331  return TRUE;
332}
333
334
335/* Set yyin to the new file. */
336
337void
338new_yy_file (file)
339     FILE *file;
340{
341  if (!first_file) fclose (yyin);
342  yyin = file;
343  first_file = FALSE;
344}
345
346
347/* Message to use quit.  */
348
349void
350use_quit (sig)
351     int sig;
352{
353  printf ("\n(interrupt) use quit to exit.\n");
354  signal (SIGINT, use_quit);
355}
356