/*
* gawkmisc.vms --- miscellanious gawk routines that are OS specific.
*/
/*
* Copyright (C) 1986, 1988, 1989, 1991-1996, 2003, 2011, 2014
* the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Progamming Language.
*
* 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 3 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
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <descrip.h>
#include <dvidef.h>
#include <efndef.h>
#include <fscndef.h>
#include <stsdef.h>
#include <time.h>
#include <lnmdef.h>
#pragma member_alignment save
#pragma nomember_alignment longword
struct item_list_3 {
unsigned short len;
unsigned short code;
void * bufadr;
unsigned short * retlen;
};
struct filescan_itmlst_2 {
unsigned short length;
unsigned short itmcode;
char * component;
};
#pragma member_alignment
int SYS$GETDVIW(
unsigned long efn,
unsigned short chan,
const struct dsc$descriptor_s * devnam,
const struct item_list_3 * itmlst,
void * iosb,
void (* astadr)(unsigned long),
unsigned long astprm,
void * nullarg);
int SYS$FILESCAN(
const struct dsc$descriptor_s * srcstr,
struct filescan_itmlst_2 * valuelist,
unsigned long * fldflags,
struct dsc$descriptor_s *auxout,
unsigned short * retlen);
int SYS$TRNLNM(
const unsigned long * attr,
const struct dsc$descriptor_s * table_dsc,
struct dsc$descriptor_s * name_dsc,
const unsigned char * acmode,
const struct item_list_3 * item_list);
char quote = '\'';
char *defpath = DEFPATH;
char *deflibpath = DEFLIBPATH;
char envsep = ',';
#define VMS_NAME_LEN 255
static char vms_name[VMS_NAME_LEN+1];
/* Take all the fun out of simply looking up a logical name */
static int sys_trnlnm
(const char * logname,
char * value,
int value_len)
{
const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
const unsigned long attr = LNM$M_CASE_BLIND;
struct dsc$descriptor_s name_dsc;
int status;
unsigned short result;
struct item_list_3 itlst[2];
itlst[0].len = value_len;
itlst[0].code = LNM$_STRING;
itlst[0].bufadr = value;
itlst[0].retlen = &result;
itlst[1].len = 0;
itlst[1].code = 0;
name_dsc.dsc$w_length = strlen(logname);
name_dsc.dsc$a_pointer = (char *)logname;
name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
name_dsc.dsc$b_class = DSC$K_CLASS_S;
status = SYS$TRNLNM(&attr, &table_dsc, &name_dsc, 0, itlst);
if ($VMS_STATUS_SUCCESS(status)) {
/* Null terminate and return the string */
value[result] = '\0';
}
return status;
}
/* gawk_name --- pull out the "gawk" part from how the OS called us */
/* You would not think that this should be a such a problem, but
* VMS extended file specifications are tricky to parse, and we have
* to tell the difference between a CRTL generated argv[0] and a
* passed exec() argv[0] and handle both cases.
*/
char *
gawk_name(filespec)
const char *filespec;
{
int status;
int result;
char * shell;
int lcname = 0;
/* If the path name starts with a /, then it is an absolute path
* that may have been generated by the CRTL instead of the command
* name. If it is the device name between the slashes, then this
* was likely from the run command and needs to be fixed up.
* If the DECC$POSIX_COMPLIANT_PATHNAMES is set to 2, then it is
* the DISK$VOLUME that will be present, and it will still need to
* be fixed.
*/
result = 0;
if (filespec[0] == '/') {
char * nextslash;
int length;
struct item_list_3 itemlist[3];
unsigned short dvi_iosb[4];
char alldevnam[64];
unsigned short alldevnam_len;
struct dsc$descriptor_s devname_dsc;
char diskvolnam[256];
unsigned short diskvolnam_len;
/* Get some information about the disk */
/*--------------------------------------*/
itemlist[0].len = (sizeof alldevnam) - 1;
itemlist[0].code = DVI$_ALLDEVNAM;
itemlist[0].bufadr = alldevnam;
itemlist[0].retlen = &alldevnam_len;
itemlist[1].len = (sizeof diskvolnam) - 1 - 5;
itemlist[1].code = DVI$_VOLNAM;
itemlist[1].bufadr = &diskvolnam[5];
itemlist[1].retlen = &diskvolnam_len;
itemlist[2].len = 0;
itemlist[2].code = 0;
/* Add the prefix for the volume name. */
/* SYS$GETDVI will append the volume name to this */
strcpy(diskvolnam,"DISK$");
nextslash = strchr(&filespec[1], '/');
if (nextslash != NULL) {
length = nextslash - filespec - 1;
/* DECC requires a cast here */
devname_dsc.dsc$a_pointer = (char *)&filespec[1];
devname_dsc.dsc$w_length = length;
devname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
devname_dsc.dsc$b_class = DSC$K_CLASS_S;
status = SYS$GETDVIW(
EFN$C_ENF,
0,
&devname_dsc,
itemlist,
dvi_iosb,
NULL, 0, 0);
if (!$VMS_STATUS_SUCCESS(status)) {
/* If the sys$getdviw fails, then this path
* was passed by an exec() program and not
* from DCL, so do nothing.
* An example is "/tmp/program" where tmp:
* does not exist
*/
result = 0;
} else if (!$VMS_STATUS_SUCCESS(dvi_iosb[0])) {
result = 0;
} else {
char * devnam;
int devnam_len;
char argv_dev[64];
/* Null terminate the returned alldevnam */
alldevnam[alldevnam_len] = 0;
devnam = alldevnam;
devnam_len = alldevnam_len;
/* Need to skip past any leading underscore */
if (devnam[0] == '_') {
devnam++;
devnam_len--;
}
/* And remove the trailing colon */
if (devnam[devnam_len - 1] == ':') {
devnam_len--;
devnam[devnam_len] = 0;
}
/* Null terminate the returned volnam */
diskvolnam_len += 5;
diskvolnam[diskvolnam_len] = 0;
/* Check first for normal CRTL behavior */
if (devnam_len == length) {
strncpy(vms_name, &filespec[1], length);
vms_name[length] = 0;
result = (strcasecmp(devnam, vms_name) == 0);
}
/* If we have not got a match check for
* POSIX Compliant behavior. To be more
* accurate, we could also check to see
* if that feature is active.
*/
if ((result == 0) &&
(diskvolnam_len == length)) {
int cmp;
strncpy(vms_name, &filespec[1], length);
vms_name[length] = 0;
cmp = strcasecmp(diskvolnam, vms_name);
result = (cmp == 0);
}
}
}
} else {
/* The path did not start with a slash, so it could be VMS
* format. If it is vms format, it has a volume/device in
* it as it must be an absolute path
*/
struct dsc$descriptor_s path_desc;
int status;
unsigned long field_flags;
struct filescan_itmlst_2 item_list[5];
char * volume;
char * name;
int name_len;
char * ext;
/* DECC requires a cast here */
path_desc.dsc$a_pointer = (char *)filespec;
path_desc.dsc$w_length = strlen(filespec);
path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
path_desc.dsc$b_class = DSC$K_CLASS_S;
/* Don't actually need to initialize anything buf itmcode */
/* I just do not like uninitialized input values */
/* Sanity check, this must be the same length as input */
item_list[0].itmcode = FSCN$_FILESPEC;
item_list[0].length = 0;
item_list[0].component = NULL;
/* If the device is present, then it if a VMS spec */
item_list[1].itmcode = FSCN$_DEVICE;
item_list[1].length = 0;
item_list[1].component = NULL;
/* we need the program name and type */
item_list[2].itmcode = FSCN$_NAME;
item_list[2].length = 0;
item_list[2].component = NULL;
item_list[3].itmcode = FSCN$_TYPE;
item_list[3].length = 0;
item_list[3].component = NULL;
/* End the list */
item_list[4].itmcode = 0;
item_list[4].length = 0;
item_list[4].component = NULL;
status = SYS$FILESCAN(
(const struct dsc$descriptor_s *)&path_desc,
item_list, &field_flags, NULL, NULL);
if ($VMS_STATUS_SUCCESS(status) &&
(item_list[0].length == path_desc.dsc$w_length) &&
(item_list[1].length != 0)) {
char * dollar;
int keep_ext;
int i;
/* We need the filescan to be successful,
* same length as input, and a volume to be present.
*
* We will assume that we only get to this path on
* a version of VMS that does not support the EFS
* character set.
*
* There may be a xxx$ prefix on the image name.
* Linux programs do not handle that well, so
* strip the prefix.
*/
name = item_list[2].component;
name_len = item_list[2].length;
dollar = strrchr(name, '$');
if (dollar != NULL) {
dollar++;
name_len = name_len - (dollar - name);
name = dollar;
}
strncpy(vms_name, name, name_len);
vms_name[name_len] = 0;
result = 1;
/* We only keep the extension if it is not ".exe" */
keep_ext = 0;
ext = item_list[3].component;
if (item_list[3].length != 1) {
if (item_list[3].length != 4) {
keep_ext = 1;
} else {
int x;
x = strncmp(ext, ".exe", 4);
if (x != 0) {
keep_ext = 1;
}
}
}
if (keep_ext == 1) {
strncpy(&vms_name[name_len],
ext, item_list[3].length);
}
}
}
if (result) {
char * lastslash;
char * dollar;
char * dotexe;
char * lastdot;
char * extension;
/* This means it is probably the name from a DCL command
* Find the last slash which separates the file from the
* path.
*/
lastslash = strrchr(filespec, '/');
if (lastslash != NULL) {
int i;
lastslash++;
/* There may be a xxx$ prefix on the image name. */
/* Linux programs do not handle that well, so */
/* strip the prefix */
dollar = strrchr(lastslash, '$');
if (dollar != NULL) {
dollar++;
lastslash = dollar;
}
strcpy(vms_name, lastslash);
/* In UNIX mode + EFS character set, there should
* not be a version present, as it is not possible
* when parsing to tell if it is a version or part
* of the UNIX filename as UNIX programs use numeric
* extensions for many reasons.
*/
lastdot = strrchr(vms_name, '.');
if (lastdot != NULL) {
int i;
i = 1;
while (isdigit(lastdot[i])) {
i++;
}
if (lastdot[i] == 0) {
*lastdot = 0;
}
}
/* Find the .exe on the name (case insenstive)
* and toss it
*/
dotexe = strrchr(vms_name, '.');
if (dotexe != NULL) {
if ((dotexe[1] == 'e' || dotexe[1] == 'E') &&
(dotexe[2] == 'x' || dotexe[2] == 'X') &&
(dotexe[3] == 'e' || dotexe[3] == 'E') &&
(dotexe[4] == 0)) {
*dotexe = 0;
} else {
/* Also need to handle a null
* extension because of a CRTL bug.
*/
if (dotexe[1] == 0) {
*dotexe = 0;
}
}
}
}
} else {
/* No changes needed */
strncpy(vms_name, filespec, VMS_NAME_LEN);
vms_name[VMS_NAME_LEN] = 0;
}
/*
* The above fixes up the name, but for the DCL shell
* may leave it in upper case, which messes up the self tests.
* force it to lower case here.
*/
shell = getenv("SHELL");
if (shell != NULL) {
if (strcmp(shell, "DCL") == 0) {
lcname = 1;
}
} else {
lcname = 1;
}
if (lcname == 1) {
int i = 0;
while (vms_name[i] != 0) {
vms_name[i] = tolower(vms_name[i]);
i++;
}
}
return vms_name;
}
/* os_arg_fixup --- fixup the command line */
void
os_arg_fixup(argcp, argvp)
int *argcp;
char ***argvp;
{
char *tz_rule;
int status;
(void) vms_arg_fixup(argcp, argvp);
/* Fix up the time zone */
/* For some reason it gets trashed */
tz_rule = malloc(1024);
status = sys_trnlnm("TZ", tz_rule, 1024);
if ($VMS_STATUS_SUCCESS(status)) {
setenv("TZ", tz_rule, 1);
} else {
status = sys_trnlnm("SYS$TIMEZONE_RULE", tz_rule, 1024);
if ($VMS_STATUS_SUCCESS(status)) {
setenv("TZ", tz_rule, 1);
}
}
free(tz_rule);
}
/* os_devopen --- open special per-OS devices */
int
os_devopen(name, flag)
const char *name;
int flag;
{
return vms_devopen(name, flag);
}
/* optimal_bufsize --- determine optimal buffer size */
size_t
optimal_bufsize(fd, stb)
int fd;
struct stat *stb;
{
/* force all members to zero in case OS doesn't use all of them. */
memset(stb, '\0', sizeof(struct stat));
/*
* These values correspond with the RMS multi-block count used by
* vms_open() in vms/vms_misc.c.
*/
if (isatty(fd) > 0)
return BUFSIZ;
else if (fstat(fd, stb) < 0)
return 8*512; /* conservative in case of DECnet access */
else
return 32*512;
}
/* ispath --- return true if path has directory components */
int
ispath(file)
const char *file;
{
for (; *file; file++) {
switch (*file) {
case ':':
case ']':
case '>':
case '/':
return 1;
}
}
return 0;
}
/* isdirpunct --- return true if char is a directory separator */
int
isdirpunct(c)
int c;
{
return (strchr(":]>/", c) != NULL);
}
/* os_close_on_exec --- set close on exec flag, print warning if fails */
void
os_close_on_exec(fd, name, what, dir)
int fd;
const char *name, *what, *dir;
{
/* no-op */
}
/* os_isdir --- is this an fd on a directory? */
#if ! defined(S_ISDIR) && defined(S_IFDIR)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
int
os_isdir(fd)
int fd;
{
struct stat sbuf;
return (fstat(fd, &sbuf) == 0 && S_ISDIR(sbuf.st_mode));
}
/* os_isreadable --- fd can be read from */
int
os_isreadable(const awk_input_buf_t *iobuf, bool *isdir)
{
*isdir = false;
switch (iobuf->sbuf.st_mode & S_IFMT) {
case S_IFREG:
case S_IFCHR: /* ttys, /dev/null, .. */
#ifdef S_IFSOCK
case S_IFSOCK:
#endif
#ifdef S_IFIFO
case S_IFIFO:
#endif
return true;
case S_IFDIR:
*isdir = true;
/* fall through */
default:
return false;
}
}
/* os_is_setuid --- true if running setuid root */
int
os_is_setuid()
{
return 0;
}
/* os_setbinmode --- set binary mode on file */
int
os_setbinmode (fd, mode)
int fd, mode;
{
return 0;
}
/* os_restore_mode --- restore the original mode of the console device */
void
os_restore_mode (fd)
int fd;
{
/* no-op */
return;
}
/* files_are_same --- deal with VMS struct stat */
int
files_are_same(char *newfile, SRCFILE *oldfile)
{
struct stat st, *f1, *f2;
f1 = &st;
if (stat(newfile, f1) != 0) return 0;
f2 = &oldfile->sbuf;
/* compare device string */
#ifdef _USE_STD_STAT
return (f1->st_dev == f2->st_dev
/* and 48-bit file id cookie */
&& f1->st_ino == f2->st_ino);
#else
return (strcmp(f1->st_dev, f2->st_dev) == 0
/* and 48-bit file id cookie stored in 3 short ints */
&& f1->st_ino[0] == f2->st_ino[0]
&& f1->st_ino[1] == f2->st_ino[1]
&& f1->st_ino[2] == f2->st_ino[2]);
#endif
}
int
os_isatty(int fd)
{
return (isatty(fd) > 0);
}
void
init_sockets(void)
{
}