1 /**************************************************************** 2 Copyright (C) Lucent Technologies 1997 3 All Rights Reserved 4 5 Permission to use, copy, modify, and distribute this software and 6 its documentation for any purpose and without fee is hereby 7 granted, provided that the above copyright notice appear in all 8 copies and that both that the copyright notice and this 9 permission notice and warranty disclaimer appear in supporting 10 documentation, and that the name Lucent Technologies or any of 11 its entities not be used in advertising or publicity pertaining 12 to distribution of the software without specific, written prior 13 permission. 14 15 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 16 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 17 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY 18 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 20 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 21 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 22 THIS SOFTWARE. 23 ****************************************************************/ 24 25 const char *version = "version 20200702"; 26 27 #define DEBUG 28 #include <stdio.h> 29 #include <ctype.h> 30 #include <locale.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <signal.h> 34 #include "awk.h" 35 #include "ytab.h" 36 37 extern char **environ; 38 extern int nfields; 39 40 int dbg = 0; 41 Awkfloat srand_seed = 1; 42 char *cmdname; /* gets argv[0] for error messages */ 43 extern FILE *yyin; /* lex input file */ 44 char *lexprog; /* points to program argument if it exists */ 45 extern int errorflag; /* non-zero if any syntax errors; set by yyerror */ 46 enum compile_states compile_time = ERROR_PRINTING; 47 48 static char **pfile; /* program filenames from -f's */ 49 static size_t maxpfile; /* max program filename */ 50 static size_t npfile; /* number of filenames */ 51 static size_t curpfile; /* current filename */ 52 53 bool safe = false; /* true => "safe" mode */ 54 55 static noreturn void fpecatch(int n 56 #ifdef SA_SIGINFO 57 , siginfo_t *si, void *uc 58 #endif 59 ) 60 { 61 #ifdef SA_SIGINFO 62 static const char *emsg[] = { 63 [0] = "Unknown error", 64 [FPE_INTDIV] = "Integer divide by zero", 65 [FPE_INTOVF] = "Integer overflow", 66 [FPE_FLTDIV] = "Floating point divide by zero", 67 [FPE_FLTOVF] = "Floating point overflow", 68 [FPE_FLTUND] = "Floating point underflow", 69 [FPE_FLTRES] = "Floating point inexact result", 70 [FPE_FLTINV] = "Invalid Floating point operation", 71 [FPE_FLTSUB] = "Subscript out of range", 72 }; 73 #endif 74 FATAL("floating point exception" 75 #ifdef SA_SIGINFO 76 ": %s", (size_t)si->si_code < sizeof(emsg) / sizeof(emsg[0]) && 77 emsg[si->si_code] ? emsg[si->si_code] : emsg[0] 78 #endif 79 ); 80 } 81 82 /* Can this work with recursive calls? I don't think so. 83 void segvcatch(int n) 84 { 85 FATAL("segfault. Do you have an unbounded recursive call?", n); 86 } 87 */ 88 89 static const char * 90 setfs(char *p) 91 { 92 /* wart: t=>\t */ 93 if (p[0] == 't' && p[1] == '\0') 94 return "\t"; 95 else if (p[0] != '\0') 96 return p; 97 return NULL; 98 } 99 100 static char * 101 getarg(int *argc, char ***argv, const char *msg) 102 { 103 if ((*argv)[1][2] != '\0') { /* arg is -fsomething */ 104 return &(*argv)[1][2]; 105 } else { /* arg is -f something */ 106 (*argc)--; (*argv)++; 107 if (*argc <= 1) 108 FATAL("%s", msg); 109 return (*argv)[1]; 110 } 111 } 112 113 int main(int argc, char *argv[]) 114 { 115 const char *fs = NULL; 116 char *fn, *vn; 117 118 setlocale(LC_CTYPE, ""); 119 setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */ 120 cmdname = argv[0]; 121 if (argc == 1) { 122 fprintf(stderr, 123 "usage: %s [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]\n", 124 cmdname); 125 exit(1); 126 } 127 #ifdef SA_SIGINFO 128 { 129 struct sigaction sa; 130 sa.sa_sigaction = fpecatch; 131 sa.sa_flags = SA_SIGINFO; 132 sigemptyset(&sa.sa_mask); 133 (void)sigaction(SIGFPE, &sa, NULL); 134 } 135 #else 136 (void)signal(SIGFPE, fpecatch); 137 #endif 138 /*signal(SIGSEGV, segvcatch); experiment */ 139 140 /* Set and keep track of the random seed */ 141 srand_seed = 1; 142 srandom((unsigned long) srand_seed); 143 144 yyin = NULL; 145 symtab = makesymtab(NSYMTAB/NSYMTAB); 146 while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') { 147 if (strcmp(argv[1], "-version") == 0 || strcmp(argv[1], "--version") == 0) { 148 printf("awk %s\n", version); 149 return 0; 150 } 151 if (strcmp(argv[1], "--") == 0) { /* explicit end of args */ 152 argc--; 153 argv++; 154 break; 155 } 156 switch (argv[1][1]) { 157 case 's': 158 if (strcmp(argv[1], "-safe") == 0) 159 safe = true; 160 break; 161 case 'f': /* next argument is program filename */ 162 fn = getarg(&argc, &argv, "no program filename"); 163 if (npfile >= maxpfile) { 164 maxpfile += 20; 165 pfile = realloc(pfile, maxpfile * sizeof(*pfile)); 166 if (pfile == NULL) 167 FATAL("error allocating space for -f options"); 168 } 169 pfile[npfile++] = fn; 170 break; 171 case 'F': /* set field separator */ 172 fs = setfs(getarg(&argc, &argv, "no field separator")); 173 if (fs == NULL) 174 WARNING("field separator FS is empty"); 175 break; 176 case 'v': /* -v a=1 to be done NOW. one -v for each */ 177 vn = getarg(&argc, &argv, "no variable name"); 178 if (isclvar(vn)) 179 setclvar(vn); 180 else 181 FATAL("invalid -v option argument: %s", vn); 182 break; 183 case 'd': 184 dbg = atoi(&argv[1][2]); 185 if (dbg == 0) 186 dbg = 1; 187 printf("awk %s\n", version); 188 break; 189 default: 190 WARNING("unknown option %s ignored", argv[1]); 191 break; 192 } 193 argc--; 194 argv++; 195 } 196 /* argv[1] is now the first argument */ 197 if (npfile == 0) { /* no -f; first argument is program */ 198 if (argc <= 1) { 199 if (dbg) 200 exit(0); 201 FATAL("no program given"); 202 } 203 DPRINTF("program = |%s|\n", argv[1]); 204 lexprog = argv[1]; 205 argc--; 206 argv++; 207 } 208 recinit(recsize); 209 syminit(); 210 compile_time = COMPILING; 211 argv[0] = cmdname; /* put prog name at front of arglist */ 212 DPRINTF("argc=%d, argv[0]=%s\n", argc, argv[0]); 213 arginit(argc, argv); 214 if (!safe) 215 envinit(environ); 216 yyparse(); 217 #if 0 218 // Doing this would comply with POSIX, but is not compatible with 219 // other awks and with what most users expect. So comment it out. 220 setlocale(LC_NUMERIC, ""); /* back to whatever it is locally */ 221 #endif 222 if (fs) 223 *FS = qstring(fs, '\0'); 224 DPRINTF("errorflag=%d\n", errorflag); 225 if (errorflag == 0) { 226 compile_time = RUNNING; 227 run(winner); 228 } else 229 bracecheck(); 230 return(errorflag); 231 } 232 233 int pgetc(void) /* get 1 character from awk program */ 234 { 235 int c; 236 237 for (;;) { 238 if (yyin == NULL) { 239 if (curpfile >= npfile) 240 return EOF; 241 if (strcmp(pfile[curpfile], "-") == 0) 242 yyin = stdin; 243 else if ((yyin = fopen(pfile[curpfile], "r")) == NULL) 244 FATAL("can't open file %s", pfile[curpfile]); 245 lineno = 1; 246 } 247 if ((c = getc(yyin)) != EOF) 248 return c; 249 if (yyin != stdin) 250 fclose(yyin); 251 yyin = NULL; 252 curpfile++; 253 } 254 } 255 256 char *cursource(void) /* current source file name */ 257 { 258 if (npfile > 0) 259 return pfile[curpfile]; 260 else 261 return NULL; 262 } 263