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