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 20240122";
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
fpecatch(int n,siginfo_t * si,void * uc)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 *
setfs(char * p)93 setfs(char *p)
94 {
95 /* wart: t=>\t */
96 if (p[0] == 't' && p[1] == '\0')
97 return "\t";
98 return p;
99 }
100
101 static char *
getarg(int * argc,char *** argv,const char * msg)102 getarg(int *argc, char ***argv, const char *msg)
103 {
104 if ((*argv)[1][2] != '\0') { /* arg is -fsomething */
105 return &(*argv)[1][2];
106 } else { /* arg is -f something */
107 (*argc)--; (*argv)++;
108 if (*argc <= 1)
109 FATAL("%s", msg);
110 return (*argv)[1];
111 }
112 }
113
main(int argc,char * argv[])114 int main(int argc, char *argv[])
115 {
116 const char *fs = NULL;
117 char *fn, *vn;
118
119 setlocale(LC_CTYPE, "");
120 setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */
121 awk_mb_cur_max = MB_CUR_MAX;
122 cmdname = argv[0];
123 if (argc == 1) {
124 fprintf(stderr,
125 "usage: %s [-F fs | --csv] [-v var=value] [-f progfile | 'prog'] [file ...]\n",
126 cmdname);
127 exit(1);
128 }
129 #ifdef SA_SIGINFO
130 {
131 struct sigaction sa;
132 sa.sa_sigaction = fpecatch;
133 sa.sa_flags = SA_SIGINFO;
134 sigemptyset(&sa.sa_mask);
135 (void)sigaction(SIGFPE, &sa, NULL);
136 }
137 #else
138 (void)signal(SIGFPE, fpecatch);
139 #endif
140 /*signal(SIGSEGV, segvcatch); experiment */
141
142 /* Set and keep track of the random seed */
143 srand_seed = 1;
144 srandom((unsigned long) srand_seed);
145
146 yyin = NULL;
147 symtab = makesymtab(NSYMTAB/NSYMTAB);
148 while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') {
149 if (strcmp(argv[1], "-version") == 0 || strcmp(argv[1], "--version") == 0) {
150 printf("awk %s\n", version);
151 return 0;
152 }
153 if (strcmp(argv[1], "--") == 0) { /* explicit end of args */
154 argc--;
155 argv++;
156 break;
157 }
158 if (strcmp(argv[1], "--csv") == 0) { /* turn on csv input processing */
159 CSV = true;
160 argc--;
161 argv++;
162 continue;
163 }
164 switch (argv[1][1]) {
165 case 's':
166 if (strcmp(argv[1], "-safe") == 0)
167 safe = true;
168 break;
169 case 'f': /* next argument is program filename */
170 fn = getarg(&argc, &argv, "no program filename");
171 if (npfile >= maxpfile) {
172 maxpfile += 20;
173 pfile = (char **) realloc(pfile, maxpfile * sizeof(*pfile));
174 if (pfile == NULL)
175 FATAL("error allocating space for -f options");
176 }
177 pfile[npfile++] = fn;
178 break;
179 case 'F': /* set field separator */
180 fs = setfs(getarg(&argc, &argv, "no field separator"));
181 break;
182 case 'v': /* -v a=1 to be done NOW. one -v for each */
183 vn = getarg(&argc, &argv, "no variable name");
184 if (isclvar(vn))
185 setclvar(vn);
186 else
187 FATAL("invalid -v option argument: %s", vn);
188 break;
189 case 'd':
190 dbg = atoi(&argv[1][2]);
191 if (dbg == 0)
192 dbg = 1;
193 printf("awk %s\n", version);
194 break;
195 default:
196 WARNING("unknown option %s ignored", argv[1]);
197 break;
198 }
199 argc--;
200 argv++;
201 }
202
203 if (CSV && (fs != NULL || lookup("FS", symtab) != NULL))
204 WARNING("danger: don't set FS when --csv is in effect");
205
206 /* argv[1] is now the first argument */
207 if (npfile == 0) { /* no -f; first argument is program */
208 if (argc <= 1) {
209 if (dbg)
210 exit(0);
211 FATAL("no program given");
212 }
213 DPRINTF("program = |%s|\n", argv[1]);
214 lexprog = argv[1];
215 argc--;
216 argv++;
217 }
218 recinit(recsize);
219 syminit();
220 compile_time = COMPILING;
221 argv[0] = cmdname; /* put prog name at front of arglist */
222 DPRINTF("argc=%d, argv[0]=%s\n", argc, argv[0]);
223 arginit(argc, argv);
224 if (!safe)
225 envinit(environ);
226 yyparse();
227 #if 0
228 // Doing this would comply with POSIX, but is not compatible with
229 // other awks and with what most users expect. So comment it out.
230 setlocale(LC_NUMERIC, ""); /* back to whatever it is locally */
231 #endif
232 if (fs)
233 *FS = qstring(fs, '\0');
234 DPRINTF("errorflag=%d\n", errorflag);
235 if (errorflag == 0) {
236 compile_time = RUNNING;
237 run(winner);
238 } else
239 bracecheck();
240 return(errorflag);
241 }
242
pgetc(void)243 int pgetc(void) /* get 1 character from awk program */
244 {
245 int c;
246
247 for (;;) {
248 if (yyin == NULL) {
249 if (curpfile >= npfile)
250 return EOF;
251 if (strcmp(pfile[curpfile], "-") == 0)
252 yyin = stdin;
253 else if ((yyin = fopen(pfile[curpfile], "r")) == NULL)
254 FATAL("can't open file %s", pfile[curpfile]);
255 lineno = 1;
256 }
257 if ((c = getc(yyin)) != EOF)
258 return c;
259 if (yyin != stdin)
260 fclose(yyin);
261 yyin = NULL;
262 curpfile++;
263 }
264 }
265
cursource(void)266 char *cursource(void) /* current source file name */
267 {
268 if (npfile > 0)
269 return pfile[curpfile < npfile ? curpfile : curpfile - 1];
270 else
271 return NULL;
272 }
273