/* * 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 #include #include #include #include #include #include #include #include #include #include #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) { }