aboutsummaryrefslogtreecommitdiffstats
path: root/vms
diff options
context:
space:
mode:
Diffstat (limited to 'vms')
-rw-r--r--vms/gawk.hlp116
-rw-r--r--vms/vms.h15
-rw-r--r--vms/vms_args.c40
-rw-r--r--vms/vms_fwrite.c6
-rw-r--r--vms/vms_gawk.c6
-rw-r--r--vms/vms_misc.c6
-rw-r--r--vms/vms_popen.c199
7 files changed, 292 insertions, 96 deletions
diff --git a/vms/gawk.hlp b/vms/gawk.hlp
index 68892393..660e0353 100644
--- a/vms/gawk.hlp
+++ b/vms/gawk.hlp
@@ -5,10 +5,10 @@
!
1 GAWK
GAWK is GNU awk, the Free Software Foundation's implementation of
- the awk programming language. awk is an interperative language which
+ the awk programming language. awk is an interpretive language which
can handle many data-reformatting jobs with just a few lines of code.
It has powerful string manipulation and pattern matching capabilities
- built in. This version should be compatable with POSIX 1003.2 awk.
+ built in. This version should be compatible with POSIX 1003.2 awk.
The VMS version of GAWK supports both the original UN*X-style command
interface and a DCL interface. The only setup requirement for GAWK
@@ -38,10 +38,10 @@
-v var=val assign a value of 'val' to the variable 'var'
-W 'options' additional gawk-specific options; multiple values may
be separated by commas, or by spaces if they're quoted,
- or mulitple occurences of -W may be used.
+ or mulitple occurrences of -W may be used.
-W compat use awk "compatibility mode" to disable GAWK extensions
and get the behavior of UN*X awk.
- -W copyright [or -W copyleft] display an abbreivated version of
+ -W copyright [or -W copyleft] display an abbreviated version of
the GNU copyright information
-W lint warn about suspect or non-portable awk program code
-W posix compatibility mode with additional restrictions
@@ -54,13 +54,13 @@
$ gawk -- "BEGIN {print ""\nHello, World!\n""}"
This program would print a blank line (based on first "\n"), followed
by a line reading "Hello, World!", followed by another blank line
- (since awk's 'print' statement includes trailing 'newline').
+ (since awk's 'print' statement includes the trailing 'newline').
On VMS, to include a quote character inside of a quoted string, two
successive quotes ("") must be used.
3 data_files
After all dash-options are examined, and after the program text if
- there were no occurences of the -f option, remaining (space separated)
+ there were no occurrences of the -f option, remaining (space separated)
command line arguments are considered to be data files for the awk
program to process. If any of these actually contains an equals sign
(=), then it is interpreted as a variable assignment instead of a data
@@ -88,8 +88,8 @@
implied carriage control)
2>&1 route error messages into the regular output stream
1>&2 send output data to the error destination
- <<sentinal error; reading stdin until 'sentinal' not supported
- <-, >- error; closer of stdin or stdout from cmd line not supported
+ <<sentinel error; reading stdin until 'sentinel' not supported
+ <-, >- error; closure of stdin or stdout from cmd line not supported
>>$vfile incorrect; would be interpreted as file "$vfile" in stream-lf
format rather than as file "vfile" in RMS 'text' format
| error; command line pipes not supported
@@ -141,7 +141,7 @@
subtopic 'GAWK GNU_syntax wildcard_expansion' for details.
At least one data_file parameter value is required. An exception is
- made if /usage, /version, or /copyright is specifed *and* if GAWK is
+ made if /usage, /version, or /copyright is specified *and* if GAWK is
defined as a 'foreign' command rather than a 'native' DCL command.
3 Qualifiers
/COMMANDS
@@ -153,7 +153,7 @@
$ gawk/commands="BEGIN {print ""\nHello, World!\n""}" NL:
This program would print a blank line (based on first "\n"), followed
by a line reading "Hello, World!", followed by another blank line
- (since awk's 'print' statement includes trailing 'newline').
+ (since awk's 'print' statement includes the trailing 'newline').
To include a quote character inside of a quoted string, two
successive quotes ("") must be used.
@@ -181,15 +181,6 @@
/REG_EXPR
/REG_EXPR={AWK | EGREP | POSIX} (-a vs -e options [obsolete])
- Specify regular expression syntax.
-
- /REG_EXPR=AWK use the original awk syntax for regular expressions
- /REG_EXPR=EGREP use the egrep syntax for regular expressions
- /REG_EXPR=POSIX equivalent to /REG_EXPR=EGREP
-
- If /REG_EXTR is omitted, then /REG_EXPR=AWK is the default. However,
- if /REG_EXTR is included but its value is omitted, EGREP is used.
-
This qualifier is obsolete and has no effect.
/STRICT
/[NO]STRICT (-"W compat" option)
@@ -322,9 +313,10 @@
(and also of a comparison operation) will be 0 when false
or 1 when true
|| or [expression (a || b) is true if either a is true or b
- is true or both a and b are true; it is false otherwise]
+ is true or both a and b are true; it is false otherwise;
+ b is not evaluated unless a is false (ie, short-circuit)]
&& and [expression (a && b) is true if both a and b are true;
- it is false otherwise]
+ it is false otherwise; b is only evaluated if a is true]
! not [expression (!a) is true if a is false, false otherwise]
in array membership; the keyword 'in' tests whether the value
on the left represents a current subscript in the array
@@ -332,7 +324,7 @@
Conditional operator
? : the conditional operator takes three operands; the first is
an expression to evaluate, the second is the expression to
- use if the first was true, the third is the expession to
+ use if the first was true, the third is the expression to
use if it was false [simple example (a < b ? b : a) gives
the maximum of a and b]
Assignment operators
@@ -357,7 +349,7 @@
there is no explicit operator for conversion; adding 0
to a string with force it to be converted to a number
(the numeric value will be 0 if the string does not
- represent a decimal or floating point number); the
+ represent an integer or floating point number); the
reverse, converting a number into a string, is done by
concatenating a null string ("") to it [the expression
(5.75 "") evaluates to "5.75"]
@@ -378,7 +370,7 @@
awk rule]
Escape 'operator'
\ In quoted character strings, the backslash (\) character
- causes the following character to be intrepreted in a
+ causes the following character to be interpreted in a
special manner [string "one\ntwo" has an embedded newline
character (linefeed on VMS, but treated as if it were both
carriage-return and linefeed); string "\033[" has an ASCII
@@ -402,7 +394,7 @@
unary plus (+), unary minus (-), boolean not (!)
multiplication (*), division (/), remainder (%)
addition (+), subtraction (-)
- concatentation (no special symbol; implied by context)
+ concatenation (no special symbol; implied by context)
relational (==, !=, <, >=, etc), and redirection (<, >, >>, |)
Relational and redirection operators have the same precedence
and use similar symbols; context distinguishes between them
@@ -413,9 +405,9 @@
conditional (? :)
assignment (=, +=, etc)
4 escaped_characters
- Inside of a quoted string, the backslash (\) character gives special
- meaning the the character(s) after it. Special character letters
- are case sensitive.
+ Inside of a quoted string or constant regular expression, the
+ backslash (\) character gives special meaning to the character(s)
+ after it. Special character letters are case sensitive.
\\ results in one backslash in the string
\a is an 'alert' (<ctrl/G>. the ASCII <bell> character)
\b is a backspace (BS, <ctrl/H>)
@@ -428,12 +420,12 @@
\### is an arbitrary character, where '###' represents 1 to 3
octal (ie, 0 thru 7) digits
\x## is an alternate arbitrary character, where '##' represents
- 1 or more hexadecimal (ie, 0 thru 9 and/or A thru E and/or
- a thru e) digits; if more than two digits follow, the
- result is undefined; not recognized if POSIX compatibility
- mode is specified.
+ 1 or more hexadecimal (ie, 0 thru 9 and/or A through E
+ and/or a through e) digits; if more than two digits
+ follow, the result is undefined; not recognized if POSIX
+ compatibility mode is specified.
3 statements
- A statement refers to a unit of intruction found in the action
+ A statement refers to a unit of instruction found in the action
part of an awk rule, and also found in the definition of a function.
The distinction between action, statement, and expression usually
won't matter to an awk programmer.
@@ -512,7 +504,8 @@
5 while_example
# strip fields from the input record until there's nothing left
while (NF > 0) {
- $1 = "" #this causes $0 to be reconstructed
+ $1 = "" #this will affect the value of $0
+ $0 = $0 #this causes $0 and NF to be re-evaluated
print
}
5 do_while_example
@@ -521,11 +514,12 @@
# echo input record until all fields have been stripped
do {
print #output $0
- $1 = "" #this causes $0 to be reconstructed
+ $1 = "" #this will affect the value of $0
+ $0 = $0 #this causes $0 and NF to be re-evaluated
} while (NF > 0)
5 for_example
- # print the ASCII alphabet (in lowercase)
- for ( letter = 'a'; letter <= 'z'; letter++ ) print letter
+ # echo command line arguments (won't include option switches)
+ for ( i = 0; i < ARGC; i++ ) print ARGV[i]
# display contents of builtin environment array
for (itm in ENVIRON)
@@ -594,11 +588,9 @@
3 fields, the value of $5 would be "").
Assigning a new value to $0 causes all the other field values (and NF)
- to be re-evaluated. Changing a specific field, causes $0 to receive
- a new value, but the other existing fields remain unchanged.
-
- For efficiency, gawk only performs field splitting at the first time
- a specific field (or NF) is actually needed.
+ to be re-evaluated. Changing a specific field will cause $0 to receive
+ a new value once it's re-evaluated, but until then the other existing
+ fields remain unchanged.
3 variables
Variables in awk can hold both numeric and string values and do not
have to be pre-declared. In fact, there is no way to explicitly
@@ -720,13 +712,12 @@
on them, and returns a single result.
The syntax for calling a function consists of the function name
- immediately followed by an open paren (left parenthesis '('),
- optionally followed by white space (spaces and/or tabs), followed
- by an appropriate argument value (number, string, variable, array
- reference, or expression involving the above and/or nested function
- call), optionally followed by more white space. That is followed by
- either a closing paren (right parenthesis, ')'), or by a comma (,)
- and another argument and so on until finally a closing paren.
+ immediately followed by an open parenthesis (left parenthesis '('),
+ followed by an argument list, followed by a closing parenthesis
+ (right parenthesis ')'). The argument list is a sequence of values
+ (numbers, strings, variables, array references, or expressions
+ involving the above and/or nested function calls), separated by
+ commas and optional white space.
The parentheses are required punctuation, except for the 'print' and
'printf' builtin IO functions, where they're optional, and for the
@@ -741,7 +732,7 @@
exp(n) the exponential of n ('e' raised to the 'n'th power)
log(n) natural logarithm of n
sin(n) sine of n (in radians)
- cos(n) cosine of n
+ cos(n) cosine of n (radians)
atan2(m,n) arctangent of m/n (radians)
rand() random number in the range 0 to 1 (exclusive)
srand(s) sets the random number 'seed' to s, so that a sequence
@@ -753,8 +744,9 @@
Builtin string functions
index(s,t) search string s for substring t; result is 1-based
offset of t within s, or 0 if not found
- length(s) returns the length of string s; 'length' without
- parenthesized argument returns length of $0
+ length(s) returns the length of string s; either 'length()'
+ with its argument omitted or 'length' without any
+ parenthesized argument list will return length of $0
match(s,r) search string s for regular expression r; the offset
of the longest, left-most substring which matches
is returned, or 0 if no match was found; the builtin
@@ -813,6 +805,7 @@
e day of month with leading space instead of leading 0 ( 1-31)
E ignored; following format character used
H hour (24 hour clock) as two digit number (00-23)
+ h abbreviated month name (Jan,Feb,...) [same as %b]
I hour (12 hour clock) as two digit number (01-12)
j day of year as three digit number (001-366)
m month as two digit number (01-12)
@@ -857,7 +850,7 @@
note: parentheses around the argument are *not*
allowed; return value is 1 for successful read, 0
if end of file is encountered, or -1 if some sort
- of error occured; [see 'redirection' for several
+ of error occurred; [see 'redirection' for several
variants]
close(s) close a file or pipe specified by the string s; the
string used should have the same value as the one
@@ -922,14 +915,15 @@
percent sign (%))
% include a literal percent sign (%) in the result
c format the next argument as a single ASCII character
- (argument should be numeric in the range 0 to 255)
+ (prints first character of string argument, or corresponding
+ ASCII character if numeric argument, e.g. 65 is 'A')
s format the next argument as a string (numeric arguments are
converted into strings on demand)
d decimal number (ie, integer value in base 10)
i integer (equivalent to decimal)
o octal number (integer in base 8)
- x hecadecimal number (integer in base 16) [lowercase]
- X hecadecimal number [digits 'A' thru 'E' in uppercase]
+ x hexadecimal number (integer in base 16) [lowercase]
+ X hexadecimal number [digits 'A' thru 'E' in uppercase]
f floating point number (digits, decimal point, fraction digits)
e exponential (scientific notation) number (digit, decimal
point, fraction digits, letter 'e', sign '+' or '-',
@@ -1076,7 +1070,7 @@
with the current command line I/O redirection. '>>$' isn't supported.
4 RS_peculiarities
Changing the record separator to something other than newline ('\n')
- will produce anomolous results for ordinary files. For example,
+ will produce anomalous results for ordinary files. For example,
using RS = "\f" and FS = "\n" with the following input
|rec 1, line 1
|rec 1, line 2
@@ -1094,7 +1088,7 @@
The following awk code will work-around this problem by inserting
a null first field in the first record, so that all records can be
handled the same by subsequent processing.
- # fixup for first record (RS != "\n")
+ # fix up for first record (RS != "\n")
FNR == 1 { if ( $0 == "" ) #leading separator
next #skip its null record
else #otherwise,
@@ -1109,7 +1103,7 @@
a pair of null fields separated by that newline. The following code
fragment will fix that provided there are no null records (in this
case, that would be two consecutive lines containing just form-feeds).
- # fixup for last record (RS != "\n")
+ # fix up for last record (RS != "\n")
$0 == FS { next } #drop spurious final record
Note that the "record not terminated" warning will persist.
4 cmd_inconsistency
@@ -1131,7 +1125,7 @@
'lint' and 'posix' run-time options added
'-W' command line option syntax supercedes '-c', '-C', and '-V'
'-a' and '-e' regular expression options made obsolete
- Various bug fixes and effiency improvements
+ Various bug fixes and efficiency improvements
More platforms supported ('officially' including VMS)
VMS-specific
@@ -1141,6 +1135,8 @@
Problem redirecting stderr (>&efile) at same time as stdin (<ifile)
or stdout (>ofile) has been fixed
``2>&1'' and ``1>&2'' redirection constructs added
+ Interaction between command line I/O redirection and gawk pipes
+ fixed; also, name used for pseudo-pipe temporary file expanded
3 license
GAWK is covered by the "GNU General Public License", the gist of which
is that if you supply this software to a third party, you are expressly
diff --git a/vms/vms.h b/vms/vms.h
index 6491a1f5..378adba2 100644
--- a/vms/vms.h
+++ b/vms/vms.h
@@ -33,11 +33,19 @@
#define CLI$_NOOPTPRS 0x00038840 /* no option present */
#endif
+#if 0
+#include <psldef.h>
+#else
+#define PSL$C_USER 3 /* user mode */
+#endif
+
#if !defined(_TYPES_) || !defined(__GNUC__)
typedef unsigned long u_long;
typedef unsigned short u_short;
#endif
typedef struct _dsc { int len; char *adr; } Dsc; /* limited string descriptor */
+ /* standard VMS itemlist-3 structure */
+typedef struct _itm { u_short len, code; void *buffer; u_short *retlen; } Itm;
#define vmswork(sts) ((sts)&1)
#define vmsfail(sts) (!vmswork(sts))
@@ -55,6 +63,13 @@ extern u_long SYS$QIO P((long, short, long, void *, const void *, long,
const char *, int, int, u_long, int, int));
extern u_long SYS$SYNCH P((long, void *));
#endif !NO_TTY_FWRITE
+ /* system services for logical name manipulation */
+extern u_long SYS$TRNLNM P((const u_long *,const Dsc *,const Dsc *,
+ const unsigned char *,Itm *));
+extern u_long SYS$CRELNM P((const u_long *,const Dsc *,const Dsc *,
+ const unsigned char *,const Itm *));
+extern u_long SYS$CRELOG P((int,const Dsc *,const Dsc *,unsigned char));
+extern u_long SYS$DELLNM P((const Dsc *,const Dsc *,const unsigned char *));
extern void v_add_arg P((int, const char *));
extern void vms_exit P((int));
diff --git a/vms/vms_args.c b/vms/vms_args.c
index b6736ff3..b317d8d0 100644
--- a/vms/vms_args.c
+++ b/vms/vms_args.c
@@ -11,8 +11,8 @@
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 1, or (at your option)
- * any later version.
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,7 +21,7 @@
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
@@ -73,6 +73,7 @@
#include "awk.h" /* really "../awk.h" */
#include "vms.h"
+#include <lnmdef.h>
void v_add_arg(int, const char *);
static char *skipblanks(const char *);
@@ -236,13 +237,13 @@ ordinary_arg:
&& (f_err[len] == ':' || f_err[len] == '\0'))
err_to_out_redirect = 1;
else
- vms_define("SYS$ERROR", f_err);
+ (void) vms_define("SYS$ERROR", f_err);
}
/* do stdin before stdout, so we bomb we won't create empty output file */
if (f_in) { /* [re]open file and define logical name */
stdin = freopen(f_in, "r", stdin, "mbf=2");
if (stdin != NULL)
- vms_define("SYS$INPUT", f_in);
+ (void) vms_define("SYS$INPUT", f_in);
else
fatal("<%s (%s)", f_in, strerror(errno));
}
@@ -254,21 +255,21 @@ ordinary_arg:
# define BIGBUF 8*BUFSIZ /* maximum record size: 4096 instead of 512 */
setvbuf(stdout, malloc(BIGBUF), _IOFBF, BIGBUF);
#endif
- vms_define("SYS$OUTPUT", f_out);
+ (void) vms_define("SYS$OUTPUT", f_out);
} else
fatal(">%s%s (%s)", (*out_mode == 'a' ? ">" : ""),
f_out, strerror(errno));
}
if (err_to_out_redirect) { /* special case for ``2>&1'' construct */
- fclose(stderr);
- dup(1, 2); /* make file 2 (stderr) share file 1 (stdout) */
+ (void) fclose(stderr);
+ (void) dup2(1, 2); /* make file 2 (stderr) share file 1 (stdout) */
stderr = stdout;
- vms_define("SYS$ERROR", "SYS$OUTPUT:");
+ (void) vms_define("SYS$ERROR", "SYS$OUTPUT:");
} else if (out_to_err_redirect) { /* ``1>&2'' */
- fclose(stdout);
- dup(2, 1); /* make file 1 (stdout) share file 2 (stderr) */
+ (void) fclose(stdout);
+ (void) dup2(2, 1); /* make file 1 (stdout) share file 2 (stderr) */
stdout = stderr;
- vms_define("SYS$OUTPUT", "SYS$ERROR:");
+ (void) vms_define("SYS$OUTPUT", "SYS$ERROR:");
}
#ifndef NO_DCL_CMD
@@ -366,18 +367,21 @@ skipblanks( const char *ptr )
static u_long
vms_define( const char *log_name, const char *trans_val )
{
- Dsc log_dsc, trn_dsc;
-# define LOG_PROCESS_TABLE 2 /* <obsolete> */
-# define LOG_USERMODE 3 /* PSL$C_USER */
- extern u_long SYS$CRELOG(); /* <superceded by $CRELNM> */
+ Dsc log_dsc;
+ static Descrip(lnmtable,"LNM$PROCESS_TABLE");
+ static long attr = LNM$M_CONFINE;
+ static Itm itemlist[] = { {sizeof attr,LNM$_ATTRIBUTES,&attr,0},
+ {0,LNM$_STRING,0,0}, {0,0} };
+ static unsigned char acmode = PSL$C_USER;
/* avoid "define SYS$OUTPUT sys$output:" for redundant ">sys$output:" */
if (strncasecmp(log_name, trans_val, strlen(log_name)) == 0)
return 0;
log_dsc.len = strlen(log_dsc.adr = (char *)log_name);
- trn_dsc.len = strlen(trn_dsc.adr = (char *)trans_val);
- return SYS$CRELOG(LOG_PROCESS_TABLE, &log_dsc, &trn_dsc, LOG_USERMODE);
+ itemlist[1].buffer = (char *)trans_val;
+ itemlist[1].len = strlen(trans_val);
+ return SYS$CRELNM((u_long *)0, &lnmtable, &log_dsc, &acmode, itemlist);
}
/* t_strstr -- strstr() substitute; search 'str' for 'sub' */
diff --git a/vms/vms_fwrite.c b/vms/vms_fwrite.c
index c0282c14..94c345a5 100644
--- a/vms/vms_fwrite.c
+++ b/vms/vms_fwrite.c
@@ -10,8 +10,8 @@
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 1, or (at your option)
- * any later version.
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,7 +20,7 @@
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "awk.h" /* really "../awk.h" */
diff --git a/vms/vms_gawk.c b/vms/vms_gawk.c
index 57abff7e..ec4747d3 100644
--- a/vms/vms_gawk.c
+++ b/vms/vms_gawk.c
@@ -10,8 +10,8 @@
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 1, or (at your option)
- * any later version.
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,7 +20,7 @@
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
diff --git a/vms/vms_misc.c b/vms/vms_misc.c
index 8c7aee6a..c7044348 100644
--- a/vms/vms_misc.c
+++ b/vms/vms_misc.c
@@ -10,8 +10,8 @@
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 1, or (at your option)
- * any later version.
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,7 +20,7 @@
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "awk.h" /* really "../awk.h" */
diff --git a/vms/vms_popen.c b/vms/vms_popen.c
index f0eaa037..654364c4 100644
--- a/vms/vms_popen.c
+++ b/vms/vms_popen.c
@@ -10,8 +10,8 @@
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 1, or (at your option)
- * any later version.
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,7 +20,7 @@
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef NO_VMS_PIPES
@@ -59,21 +59,39 @@ fork()
* Simulate pipes using temporary files; hope that the user
* doesn't expect pipe i/o to be interleaved with other i/o ;-}.
*
- * This is essentially the same as the MSDOS version. The
+ * This was initially based on the MSDOS version, but cannot
+ * use a static array to hold pipe info, because there's no
+ * fixed limit on the range of valid 'fileno's. Another
* difference is that redirection is handled using LIB$SPAWN
* rather than constructing a command for system() which uses
* '<' or '>'.
*/
#include "vms.h"
#include <errno.h>
+#include <lnmdef.h> /* logical name definitions */
+
+static void push_logicals P((void));
+static void pop_logicals P((void));
+static Itm *save_translation P((const Dsc *));
+static void restore_translation P((const Dsc *, const Itm *));
typedef enum { unopened = 0, reading, writing } pipemode;
-static
-struct {
+typedef struct pipe_info {
char *command;
char *name;
pipemode pmode;
-} pipes[_NFILE];
+} PIPE;
+static PIPE *pipes;
+static int pipes_lim = 0;
+
+#define psize(n) ((n) * sizeof(PIPE))
+#define expand_pipes(k) do { PIPE *new_p; \
+ int new_p_lim = ((k) / _NFILE + 1) * _NFILE; \
+ emalloc(new_p, PIPE *, psize(new_p_lim), "expand_pipes"); \
+ if (pipes_lim > 0) \
+ memcpy(new_p, pipes, psize(pipes_lim)), free(pipes); \
+ memset(new_p + psize(pipes_lim), 0, psize(new_p_lim - pipes_lim)); \
+ pipes = new_p, pipes_lim = new_p_lim; } while(0)
FILE *
popen( const char *command, const char *mode )
@@ -91,18 +109,20 @@ popen( const char *command, const char *mode )
return NULL;
/* make a name for the temporary file */
- if ((name = mktemp(strdup("sys$scratch:pipe_XXXX.tmp"))) == 0)
+ if ((name = mktemp(strdup("sys$scratch:gawk-pipe_XXXXXX.tmp"))) == 0)
return NULL;
if (curmode == reading) {
/* an input pipe reads a temporary file created by the command */
vms_execute(command, (char *)0, name); /* 'command >tempfile' */
}
- if ((current = fopen(name, mode)) == NULL) {
+ if ((current = fopen(name, mode, "mbf=2")) == NULL) {
free(name);
return NULL;
}
cur = fileno(current);
+ if (cur >= pipes_lim) expand_pipes(cur);
+ /* assert( cur >= 0 && cur < pipes_lim ); */
pipes[cur].name = name;
pipes[cur].pmode = curmode;
pipes[cur].command = strdup(command);
@@ -114,6 +134,7 @@ pclose( FILE *current )
{
int rval, cur = fileno(current);
+ /* assert( cur >= 0 && cur < pipes_lim ); */
if (pipes[cur].pmode == unopened)
return -1; /* should never happen */
@@ -152,8 +173,10 @@ vms_execute( const char *command, const char *input, const char *output )
else
out_p = 0;
+ push_logicals(); /* guard against user-mode definitions of sys$Xput */
sts = LIB$SPAWN(&cmd, in_p, out_p, (long *)0,
(Dsc *)0, (u_long *)0, &cmpltn_sts);
+ pop_logicals(); /* restore environment */
if (vmswork(sts) && vmsfail(cmpltn_sts)) sts = cmpltn_sts;
if (vmsfail(sts)) {
@@ -163,6 +186,164 @@ vms_execute( const char *command, const char *input, const char *output )
return 0;
}
+/*----*
+ This rigmarole is to guard against interference from the current
+ environment. User-mode definitions of SYS$INPUT and/or SYS$OUTPUT
+ will interact with spawned subprocesses--including LIB$SPAWN with
+ explicit input and/or output arguments specified--if they were
+ defined without the 'CONFINED' attribute. The definitions created
+ in vms_args.c as part of command line I/O redirection happened to
+ fall into this category :-(, but even though that's been fixed,
+ there's still the possibility of the user doing something like
+ |$ define/user sys$output foo.out
+ prior to starting the program. Without ``/name_attr=confine'',
+ that will really screw up pipe simulation, so we've got to work-
+ around it here. This is true whether pipes are implemented via
+ mailboxes or temporary files, as long as lib$spawn() is being used.
+
+ push_logicals() calls save_translation() the first time it's
+ invoked; the latter allocates some memory to hold a full logical
+ name translation and uses $trnlnm to fill that in. Then if either
+ sys$input or sys$output has a user-mode, non-confined translation,
+ push_logicals() will delete the definition(s) using $dellnm.
+ After the spawned command has returned, pop_logicals() is called;
+ it calls restore_translation() for any deleted values; the latter
+ uses $crllnm or $crelog to recreate the original definition.
+
+ SYS$ERROR is currently ignored; perhaps it should receive the same
+ treatment...
+*----*/
+
+ /* logical name table, and names of interest; these are all constant */
+static const Descrip(lnmtable,"LNM$PROCESS_TABLE");
+static const Descrip(sys_input,"SYS$INPUT");
+static const Descrip(sys_output,"SYS$OUTPUT");
+static const unsigned char acmode = PSL$C_USER; /* only care about user-mode */
+
+ /* macros for simplfying the code a bunch */
+#define DelTrans(l) SYS$DELLNM(&lnmtable, (l), &acmode)
+#define GetTrans(l,i) SYS$TRNLNM((u_long *)0, &lnmtable, (l), &acmode, (i))
+#define SetTrans(l,i) SYS$CRELNM((u_long *)0, &lnmtable, (l), &acmode, (i))
+ /* itemlist manipulation macros; separate versions for aggregate and scalar */
+#define SetItmA(i,c,p,r) ((i).code = (c), (i).len = sizeof (p),\
+ (i).buffer = (p), (i).retlen = (u_short *)(r))
+#define SetItmS(i,c,p) ((i).code = (c), (i).len = sizeof *(p),\
+ (i).buffer = (p), (i).retlen = (u_short *)0)
+#define EndItm0(i) ((i).code = (i).len = 0)
+
+ /* translate things once, then hold the results here for multiple re-use */
+static Itm *input_definition, *output_definition;
+
+static void
+push_logicals( void ) /* deassign sys$input and/or sys$output */
+{
+ static int init_done = 0;
+
+ if (!init_done) { /* do logical name lookups one-time only */
+ input_definition = save_translation(&sys_input);
+ output_definition = save_translation(&sys_output);
+ init_done = 1;
+ }
+ if (input_definition) DelTrans(&sys_input); /* kill sys$input */
+ if (output_definition) DelTrans(&sys_output); /* and sys$output */
+}
+
+static void
+pop_logicals( void ) /* redefine sys$input and/or sys$output */
+{
+ if (input_definition) restore_translation(&sys_input, input_definition);
+ if (output_definition) restore_translation(&sys_output, output_definition);
+}
+
+static Itm *
+save_translation( const Dsc *logname )
+{
+ Itm trans[4], *itmlst;
+ long trans_attr, max_trans_indx; /* 0-based translation index count */
+ unsigned char trans_acmode; /* translation's access mode */
+ unsigned itmlst_size;
+ register int i, j;
+
+ itmlst = 0;
+ /* Want translation index count for non-confined, user-mode definition;
+ unfortunately, $trnlnm does not provide that much control. Try to
+ fetch several values of interest, then decide based on the result.
+ */
+ SetItmS(trans[0], LNM$_MAX_INDEX, &max_trans_indx), max_trans_indx = -1;
+ SetItmS(trans[1], LNM$_ACMODE, &trans_acmode), trans_acmode = 0;
+ SetItmS(trans[2], LNM$_ATTRIBUTES, &trans_attr), trans_attr = 0;
+ EndItm0(trans[3]);
+ if (vmswork(GetTrans(logname, trans)) && max_trans_indx >= 0
+ && trans_acmode == PSL$C_USER && !(trans_attr & LNM$M_CONFINE)) {
+ /* Now know that definition of interest exists;
+ allocate and initialize an item list and associated buffers;
+ use three entries for each translation.
+ */
+ itmlst_size = (3 * (max_trans_indx + 1) + 1) * sizeof(Itm);
+ emalloc(itmlst, Itm *, itmlst_size, "save_translation");
+ for (i = 0; i <= max_trans_indx; i++) {
+ struct def { u_long indx, attr; u_short len;
+ char str[LNM$C_NAMLENGTH], eos; } *wrk;
+ emalloc(wrk, struct def *, sizeof (struct def), "save_translation");
+ wrk->indx = (u_long)i; /* this one's an input value for $trnlnm */
+ SetItmS(itmlst[3*i+0], LNM$_INDEX, &wrk->indx);
+ SetItmS(itmlst[3*i+1], LNM$_ATTRIBUTES, &wrk->attr), wrk->attr = 0;
+ SetItmA(itmlst[3*i+2], LNM$_STRING, &wrk->str, &wrk->len), wrk->len = 0;
+ }
+ EndItm0(itmlst[3*i]); /* assert( i == max_trans_indx+1 ); */
+ /* Time to perform full logical name translation,
+ then update item list for subsequent restoration.
+ If there are any holes [don't know whether that's possible]
+ collapse them out of the list; don't want them at restore time.
+ */
+ if (vmswork(GetTrans(logname, itmlst))) {
+ for (i = 0, j = -1; i <= max_trans_indx; i++) {
+ u_long *attr_p;
+ attr_p = itmlst[3*i+1].buffer; /* copy (void *) to true type */
+ if (*attr_p & LNM$M_EXISTS) {
+ *attr_p &= ~LNM$M_EXISTS; /* must clear this bit */
+ if (++j < i) itmlst[3*j+0] = itmlst[3*i+0],
+ itmlst[3*j+1] = itmlst[3*i+1],
+ itmlst[3*j+2] = itmlst[3*i+2];
+ if (itmlst[3*j+2].retlen) { /* fixup buffer length */
+ itmlst[3*j+2].len = *itmlst[3*j+2].retlen;
+ itmlst[3*j+2].retlen = (u_short *)0;
+ }
+ }
+ }
+ if (++j < i) EndItm0(itmlst[3*j]);
+ } else /* should never happen; tolerate potential memory leak */
+ free(itmlst), itmlst = 0; /*('wrk' buffer(s) will become lost)*/
+ }
+ return itmlst;
+}
+
+static void
+restore_translation( const Dsc *logname, const Itm *itemlist )
+{
+ Dsc trans_val;
+ u_long *attr_p;
+# define LOG_PROCESS_TABLE 2 /* <obsolete> */
+# define LOG_USERMODE PSL$C_USER
+
+ /* assert( itemlist[1].code == LNM$_ATTRIBUTES ); */
+ attr_p = itemlist[1].buffer; /* copy (void *) to (u_long *) */
+ if (*attr_p & LNM$M_CRELOG) { /* check original creation method */
+ /* $crelog values can have only one translation;
+ so it'll be the first string entry in the itemlist.
+ */
+ /* assert( itemlist[2].code == LNM$_STRING ); */
+ trans_val.adr = itemlist[2].buffer;
+ trans_val.len = itemlist[2].len;
+ (void) SYS$CRELOG(LOG_PROCESS_TABLE, logname, &trans_val, LOG_USERMODE);
+ } else {
+ /* $crelnm definition; itemlist could specify multiple translations,
+ but has already been setup properly for use as-is.
+ */
+ (void) SetTrans(logname, itemlist);
+ }
+}
+
#endif /* PIPES_SIMULATED */
#endif /*!NO_VMS_PIPES*/