1 /* vms_gawk.c -- parse GAWK command line using DCL syntax
2
3 Copyright (C) 1991-1993, 1996, 2003, 2005, 2011, 2014, 2022, 2023,
4 the 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 3, or (at your option)
9 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; if not, write to the Free Software Foundation,
18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19
20
21 /*
22 * vms_gawk.c - routines to parse the command line as a native DCL command
23 * rather than as a foreign command string.
24 * Pat Rankin, Nov'89
25 * [ revised for 2.12, May'91 ]
26 * [ revised for 4.0.0, Feb'11 ]
27 */
28
29 #include "awk.h"
30 #include "vms.h"
31
32 #define USAGE_PROG_RQRD 1
33 #define USAGE_FILE_RQRD 2
34 #define USAGE_BAD_COMBO 3
35 #define USAGE_RUN_CMD 4
36 #define STS$M_INHIB_MSG 0x10000000
37
38 #define Present(arg) vmswork(Cli_Present(arg))
39 #define Get_Value(arg,buf,siz) vmswork(Cli_Get_Value(arg,buf,siz))
40
41 #ifndef __DECC
42 extern void GAWK_CMD(); /* created with $ SET COMMAND/OBJECT */
43 #define gawk_cmd ((const void *)GAWK_CMD) */
44 #else /* Use ANSI definitions for DEC C */
45 #pragma extern_model save
46 #pragma extern_model strict_refdef
47 /* (could use globalvalue rather than _refdef if we omit GAWK_CMD's `&') */
48 extern void *GAWK_CMD;
49 #pragma extern_model restore
50 #define gawk_cmd ((const void *)&GAWK_CMD)
51 #endif
52 extern void _exit(int);
53 static int vms_usage(int);
54
55 static const char *CmdName = "GAWK";
56
57 #define ARG_SIZ 250
58 union arg_w_prefix { /* structure used to simplify prepending of "-" */
59 char value[2+ARG_SIZ+1];
60 struct {
61 char prefix[2]; /* for "-?" */
62 char buf[ARG_SIZ];
63 char suffix[1]; /* room for '\0' */
64 } arg;
65 };
66
67 #define chk_option(qualifier,optname) \
68 if (Present(qualifier)) \
69 strcat(strcat(buf.arg.buf, W_cnt++ ? "," : ""), optname)
70
71
72 /* vms_gawk() - parse GAWK command line using DCL and convert it into the */
73 /* appropriate "-arg" values for compatability with GNU code */
74 int
75 vms_gawk()
76 {
77 U_Long sts;
78 union arg_w_prefix buf;
79 char misc_args[10], *misc_argp;
80 int argc, W_cnt;
81 int native_dcl = 1, /* assume true until we know otherwise */
82 short_circ; /* some options make P1, /commands, /input superfluous */
83
84 /* check "GAWK_P1"--it's required; its presence will tip us off */
85 sts = Cli_Present("GAWK_P1");
86 if (CondVal(sts) == CondVal(CLI$_SYNTAX)) {
87 native_dcl = 0; /* not invoked via a native command verb */
88 /* syntax error indicates that we weren't invoked as a native DCL
89 command, so we'll now attempt to generate a command from the
90 foreign command string and parse that.
91 */
92 sts = Cli_Parse_Command(gawk_cmd, "GAWK"); /* (*not* CmdName) */
93 if (vmswork(sts))
94 sts = Cli_Present("GAWK_P1");
95 }
96 short_circ = Present("USAGE") || Present("VERSION") || Present("COPYRIGHT");
97 if (vmswork(sts)) /* command parsed successfully */
98 v_add_arg(argc = 0, CmdName); /* save "GAWK" as argv[0] */
99 else if (CondVal(sts) == CondVal(CLI$_INSFPRM))
100 /* vms_usage() will handle /usage, /version, and /copyright */
101 return short_circ ? vms_usage(0)
102 : native_dcl ? vms_usage(USAGE_FILE_RQRD) : 0; /* insufficient parameters */
103 else if (CondVal(sts) == CondVal(CLI$_CONFLICT))
104 return vms_usage(USAGE_BAD_COMBO); /* conflicting qualifiers (/input+/command) */
105 #if 0 /* 3.1.2: removed since this can't distinguish RUN vs fork+exec */
106 else if (CondVal(sts) == CondVal(CLI$_RUNUSED))
107 return vms_usage(USAGE_RUN_CMD); /* RUN GAWK won't work (no command line) */
108 #endif
109 else
110 return 0; /* forced to rely on original parsing */
111
112 if (short_circ) /* give usage message & quit or have main() */
113 return vms_usage(0); /* give --version or --copyleft mesg & quit */
114 else if (! (Present("PROGRAM") || Present("PROGFILE")) )
115 return native_dcl ? vms_usage(USAGE_PROG_RQRD) : 0; /* missing required option */
116
117 misc_argp = misc_args;
118 *misc_argp++ = '-'; /* now points at &misc_args[1] */
119 if (Present("OPTIMIZE"))
120 *misc_argp++ = 'O';
121 W_cnt = 0, buf.arg.buf[0] = '\0';
122 strncpy(buf.arg.prefix, "-W", 2);
123 if (Present("LINT")) {
124 if (!Present("LINT.FATAL") && !Present("LINT.INVALID"))
125 chk_option("LINT.WARN", "lint");
126 chk_option("LINT.FATAL", "lint=fatal");
127 chk_option("LINT.INVALID", "lint=invalid");
128 chk_option("LINT.OLD", "lint-old"); /* distinct option */
129 }
130 chk_option("POSIX", "posix");
131 if (CondVal(Cli_Present("TRADITIONAL")) != CondVal(CLI$_NEGATED))
132 chk_option("STRICT", "traditional"); /* /strict is synonym for /traditional */
133 if (CondVal(Cli_Present("STRICT")) != CondVal(CLI$_NEGATED))
134 chk_option("TRADITIONAL", "traditional");
135 chk_option("RE_INTERVAL", "re-interval"); /* only used with /traditional */
136 chk_option("SANDBOX", "sandbox");
137 /* potentially a problem due to leading "NO" */
138 chk_option("NON_DECIMAL_DATA", "non-decimal-data");
139 /* note: locale and translation stuff is not supported by vms gawk */
140 chk_option("CHARACTERS_AS_BYTES", "characters-as-bytes");
141 chk_option("USE_LC_NUMERIC", "use-lc-numeric");
142 chk_option("GEN_POT", "gen-pot");
143 # if 0
144 /* /copyright and /version don't reach here anymore (short_circ above) */
145 chk_option("COPYRIGHT", "copyright"); /* --copyleft */
146 chk_option("VERSION", "version");
147 # endif
148 if (W_cnt > 0) /* got something */
149 v_add_arg(++argc, strdup(buf.value));
150
151 #ifdef DEBUG /* most debugging functionality moved to separate DGAWK program */
152 if (Present("DEBUG"))
153 *misc_argp++ = 'Y'; /* --parsedebug */
154 #endif
155 *misc_argp = '\0'; /* terminate misc_args[] */
156 if (misc_argp > &misc_args[1]) /* got something */
157 v_add_arg(++argc, misc_args); /* store it/them */
158
159 if (Present("PROFILE")) { /* /profile[=file] */
160 strncpy(buf.arg.prefix, "-p", 2);
161 if (Get_Value("PROFILE", buf.arg.buf, sizeof buf.arg.buf))
162 v_add_arg(++argc, strdup(buf.value));
163 }
164 if (Present("DUMP_VARIABLES")) { /* /dump_variables[=file] */
165 strncpy(buf.arg.prefix, "-d", 2);
166 if (Get_Value("DUMP_VARIABLES", buf.arg.buf, sizeof buf.arg.buf))
167 v_add_arg(++argc, strdup(buf.value));
168 }
169 if (Present("FIELD_SEP")) { /* field separator */
170 strncpy(buf.arg.prefix, "-F", 2);
171 if (Get_Value("FIELD_SEP", buf.arg.buf, sizeof buf.arg.buf))
172 v_add_arg(++argc, strdup(buf.value));
173 }
174 if (Present("VARIABLES")) { /* variables to init prior to BEGIN */
175 strncpy(buf.arg.prefix, "-v", 2);
176 while (Get_Value("VARIABLES", buf.arg.buf, sizeof buf.arg.buf))
177 v_add_arg(++argc, strdup(buf.value));
178 }
179 /* the relative order of -e and -f args matters; unfortunately,
180 we're losing that here... */
181 if (Present("MOREPROG")) { /* /extra_input=text -> -e text */
182 strncpy(buf.arg.prefix, "-e", 2);
183 if (Get_Value("MOREPROG", buf.arg.buf, sizeof buf.arg.buf))
184 v_add_arg(++argc, strdup(buf.value));
185 }
186 if (Present("PROGFILE")) { /* program files, /input=file -> -f file */
187 strncpy(buf.arg.prefix, "-f", 2);
188 while (Get_Value("PROGFILE", buf.arg.buf, sizeof buf.arg.buf))
189 v_add_arg(++argc, strdup(buf.value));
190 v_add_arg(++argc, "--");
191 } else if (Present("PROGRAM")) { /* program text, /commands -> 'text' */
192 v_add_arg(++argc, "--");
193 if (Get_Value("PROGRAM", buf.value, sizeof buf.value))
194 v_add_arg(++argc, strdup(buf.value));
195 }
196
197 /* we know that "GAWK_P1" is present [data files and/or 'var=value'] */
198 while (Get_Value("GAWK_P1", buf.value, sizeof buf.value))
199 v_add_arg(++argc, strdup(buf.value));
200
201 if (Present("OUTPUT")) { /* let other parser treat this as 'stdout' */
202 strncpy(buf.arg.prefix, ">$", 2);
203 if (Get_Value("OUTPUT", buf.arg.buf, sizeof buf.arg.buf))
204 v_add_arg(++argc, strdup(buf.value));
205 }
206
207 return ++argc; /*(increment to account for arg[0])*/
208 }
209
210 /* vms_usage() - display one or more messages and then terminate */
211 static int /* note: usually doesn't return */
212 vms_usage( int complaint )
213 {
214 static const char
215 *usage_txt = "\n\
216 usage: %s /COMMANDS=\"awk program text\" data_file[,data_file,...] \n\
217 or %s /INPUT=awk_file data_file[,\"Var=value\",data_file,...] \n\
218 or %s /INPUT=(awk_file1,awk_file2,...) data_file[,...] \n\
219 or %s /INPUT=awk_file /EXTRA_COMMANDS=\"program text\" data_file \n\
220 ",
221 *options_txt = "\n\
222 options: /FIELD_SEPARATOR=\"FS_value\" \n\
223 and /VARIABLES=(\"Var1=value1\",\"Var2=value2\",...) \n\
224 and /OPTIMIZE /PROFILE[=file] /DUMP_VARIABLES[=file] \n\
225 and /POSIX /[NO]TRADITIONAL /[NO]STRICT /RE_INTERVAL \n\
226 and /SANDBOX /NON_DECIMAL_DATA \n\
227 and /LINT[=WARN] or /LINT=OLD or /LINT=FATAL \n\
228 and /VERSION /COPYRIGHT /USAGE \n\
229 and /OUTPUT=out_file \n\
230 ", /* omitted: /LINT=INVALID /CHARACTERS_AS_BYTES /USE_LC_NUMERIC /GEN_POT */
231 *no_prog = "missing required element: /COMMANDS or /INPUT",
232 *no_file = "missing required element: data_file \n\
233 (use \"SYS$INPUT:\" to read data lines from the terminal)",
234 *bad_combo = "invalid combination of qualifiers \n\
235 (/INPUT=awk_file and /COMMANDS=\"awk program\" are mutually exclusive)",
236 *run_used = "\"RUN\" was used; required command components missing";
237 int status, argc;
238
239 /* presence of /usage, /version, or /copyright for feedback+quit
240 supersedes absence of required program or data file */
241 if (Present("USAGE")) {
242 complaint = 0; /* clean exit */
243 } else if (Present("VERSION") || Present("COPYRIGHT")) {
244 /* construct a truncated Unix-style command line to control main() */
245 v_add_arg(argc=0, CmdName); /* save "GAWK" as argv[0] */
246 #if 0
247 v_add_arg(++argc, Present("VERSION") ? "-V" : "-C");
248 #else
249 v_add_arg(++argc, "-W");
250 v_add_arg(++argc, Present("VERSION") ? "version" : "copyright");
251 #endif
252 /* kludge to suppress 'usage' message from main() */
253 v_add_arg(++argc, "--"); /* no more arguments */
254 v_add_arg(++argc, "{}"); /* dummy program text */
255 v_add_arg(++argc, "NL:"); /* dummy input file */
256 return ++argc; /* count argv[0] too */
257 }
258
259 fflush(stdout);
260 switch (complaint) {
261 case USAGE_PROG_RQRD:
262 fprintf(stderr, "\n%%%s-W-%s, %s \n", CmdName, "PROG_RQRD", no_prog);
263 status = CLI$_VALREQ | STS$M_INHIB_MSG;
264 break;
265 case USAGE_FILE_RQRD:
266 fprintf(stderr, "\n%%%s-W-%s, %s \n", CmdName, "FILE_RQRD", no_file);
267 status = CLI$_INSFPRM | STS$M_INHIB_MSG;
268 break;
269 case USAGE_BAD_COMBO:
270 fprintf(stderr, "\n%%%s-W-%s, %s \n", CmdName, "BAD_COMBO", bad_combo);
271 status = CLI$_CONFLICT | STS$M_INHIB_MSG;
272 break;
273 case USAGE_RUN_CMD:
274 fprintf(stderr, "\n%%%s-W-%s, %s \n", CmdName, "RUN_CMD", run_used);
275 status = CLI$_NOOPTPRS | STS$M_INHIB_MSG;
276 break;
277 default:
278 status = 1;
279 break;
280 }
281 fprintf(stderr, usage_txt, CmdName, CmdName, CmdName, CmdName);
282 fprintf(stderr, options_txt);
283 fflush(stderr);
284
285 errno = EVMSERR;
286 vaxc$errno = status;
287 _exit(status);
288 /* NOTREACHED */
289 return 0;
290 }