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 20231030"; 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 36 extern char **environ; 37 extern int nfields; 38 39 int dbg = 0; 40 Awkfloat srand_seed = 1; 41 char *cmdname; /* gets argv[0] for error messages */ 42 extern FILE *yyin; /* lex input file */ 43 char *lexprog; /* points to program argument if it exists */ 44 extern int errorflag; /* non-zero if any syntax errors; set by yyerror */ 45 enum compile_states compile_time = ERROR_PRINTING; 46 47 static char **pfile; /* program filenames from -f's */ 48 static size_t maxpfile; /* max program filename */ 49 static size_t npfile; /* number of filenames */ 50 static size_t curpfile; /* current filename */ 51 52 bool CSV = false; /* true for csv input */ 53 54 bool safe = false; /* true => "safe" mode */ 55 56 size_t awk_mb_cur_max = 1; 57 58 static noreturn void fpecatch(int n 59 #ifdef SA_SIGINFO 60 , siginfo_t *si, void *uc 61 #endif 62 ) 63 { 64 #ifdef SA_SIGINFO 65 static const char *emsg[] = { 66 [0] = "Unknown error", 67 [FPE_INTDIV] = "Integer divide by zero", 68 [FPE_INTOVF] = "Integer overflow", 69 [FPE_FLTDIV] = "Floating point divide by zero", 70 [FPE_FLTOVF] = "Floating point overflow", 71 [FPE_FLTUND] = "Floating point underflow", 72 [FPE_FLTRES] = "Floating point inexact result", 73 [FPE_FLTINV] = "Invalid Floating point operation", 74 [FPE_FLTSUB] = "Subscript out of range", 75 }; 76 #endif 77 FATAL("floating point exception" 78 #ifdef SA_SIGINFO 79 ": %s", (size_t)si->si_code < sizeof(emsg) / sizeof(emsg[0]) && 80 emsg[si->si_code] ? emsg[si->si_code] : emsg[0] 81 #endif 82 ); 83 } 84 85 /* Can this work with recursive calls? I don't think so. 86 void segvcatch(int n) 87 { 88 FATAL("segfault. Do you have an unbounded recursive call?", n); 89 } 90 */ 91 92 static const char * 93 setfs(char *p) 94 { 95 /* wart: t=>\t */ 96 if (p[0] == 't' && p[1] == '\0') { 97 WARNING("-Ft to imply tab separator is deprecated behavior."); 98 return "\t"; 99 } 100 return p; 101 } 102 103 static char * 104 getarg(int *argc, char ***argv, const char *msg) 105 { 106 if ((*argv)[1][2] != '\0') { /* arg is -fsomething */ 107 return &(*argv)[1][2]; 108 } else { /* arg is -f something */ 109 (*argc)--; (*argv)++; 110 if (*argc <= 1) 111 FATAL("%s", msg); 112 return (*argv)[1]; 113 } 114 } 115 116 int main(int argc, char *argv[]) 117 { 118 const char *fs = NULL; 119 char *fn, *vn; 120 121 setlocale(LC_CTYPE, ""); 122 setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */ 123 awk_mb_cur_max = MB_CUR_MAX; 124 cmdname = argv[0]; 125 if (argc == 1) { 126 fprintf(stderr, 127 "usage: %s [-F fs | --csv] [-v var=value] [-f progfile | 'prog'] [file ...]\n", 128 cmdname); 129 exit(1); 130 } 131 #ifdef SA_SIGINFO 132 { 133 struct sigaction sa; 134 sa.sa_sigaction = fpecatch; 135 sa.sa_flags = SA_SIGINFO; 136 sigemptyset(&sa.sa_mask); 137 (void)sigaction(SIGFPE, &sa, NULL); 138 } 139 #else 140 (void)signal(SIGFPE, fpecatch); 141 #endif 142 /*signal(SIGSEGV, segvcatch); experiment */ 143 144 /* Set and keep track of the random seed */ 145 srand_seed = 1; 146 srandom((unsigned long) srand_seed); 147 148 yyin = NULL; 149 symtab = makesymtab(NSYMTAB/NSYMTAB); 150 while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') { 151 if (strcmp(argv[1], "-version") == 0 || strcmp(argv[1], "--version") == 0) { 152 printf("awk %s\n", version); 153 return 0; 154 } 155 if (strcmp(argv[1], "--") == 0) { /* explicit end of args */ 156 argc--; 157 argv++; 158 break; 159 } 160 if (strcmp(argv[1], "--csv") == 0) { /* turn on csv input processing */ 161 CSV = true; 162 argc--; 163 argv++; 164 continue; 165 } 166 switch (argv[1][1]) { 167 case 's': 168 if (strcmp(argv[1], "-safe") == 0) 169 safe = true; 170 break; 171 case 'f': /* next argument is program filename */ 172 fn = getarg(&argc, &argv, "no program filename"); 173 if (npfile >= maxpfile) { 174 maxpfile += 20; 175 pfile = (char **) realloc(pfile, maxpfile * sizeof(*pfile)); 176 if (pfile == NULL) 177 FATAL("error allocating space for -f options"); 178 } 179 pfile[npfile++] = fn; 180 break; 181 case 'F': /* set field separator */ 182 fs = setfs(getarg(&argc, &argv, "no field separator")); 183 break; 184 case 'v': /* -v a=1 to be done NOW. one -v for each */ 185 vn = getarg(&argc, &argv, "no variable name"); 186 if (isclvar(vn)) 187 setclvar(vn); 188 else 189 FATAL("invalid -v option argument: %s", vn); 190 break; 191 case 'd': 192 dbg = atoi(&argv[1][2]); 193 if (dbg == 0) 194 dbg = 1; 195 printf("awk %s\n", version); 196 break; 197 default: 198 WARNING("unknown option %s ignored", argv[1]); 199 break; 200 } 201 argc--; 202 argv++; 203 } 204 /* argv[1] is now the first argument */ 205 if (npfile == 0) { /* no -f; first argument is program */ 206 if (argc <= 1) { 207 if (dbg) 208 exit(0); 209 FATAL("no program given"); 210 } 211 DPRINTF("program = |%s|\n", argv[1]); 212 lexprog = argv[1]; 213 argc--; 214 argv++; 215 } 216 recinit(recsize); 217 syminit(); 218 compile_time = COMPILING; 219 argv[0] = cmdname; /* put prog name at front of arglist */ 220 DPRINTF("argc=%d, argv[0]=%s\n", argc, argv[0]); 221 arginit(argc, argv); 222 if (!safe) 223 envinit(environ); 224 yyparse(); 225 #if 0 226 // Doing this would comply with POSIX, but is not compatible with 227 // other awks and with what most users expect. So comment it out. 228 setlocale(LC_NUMERIC, ""); /* back to whatever it is locally */ 229 #endif 230 if (fs) 231 *FS = qstring(fs, '\0'); 232 DPRINTF("errorflag=%d\n", errorflag); 233 if (errorflag == 0) { 234 compile_time = RUNNING; 235 run(winner); 236 } else 237 bracecheck(); 238 return(errorflag); 239 } 240 241 int pgetc(void) /* get 1 character from awk program */ 242 { 243 int c; 244 245 for (;;) { 246 if (yyin == NULL) { 247 if (curpfile >= npfile) 248 return EOF; 249 if (strcmp(pfile[curpfile], "-") == 0) 250 yyin = stdin; 251 else if ((yyin = fopen(pfile[curpfile], "r")) == NULL) 252 FATAL("can't open file %s", pfile[curpfile]); 253 lineno = 1; 254 } 255 if ((c = getc(yyin)) != EOF) 256 return c; 257 if (yyin != stdin) 258 fclose(yyin); 259 yyin = NULL; 260 curpfile++; 261 } 262 } 263 264 char *cursource(void) /* current source file name */ 265 { 266 if (npfile > 0) 267 return pfile[curpfile < npfile ? curpfile : curpfile - 1]; 268 else 269 return NULL; 270 } 271