diff options
author | Arnold D. Robbins <arnold@skeeve.com> | 2013-12-12 20:45:59 +0200 |
---|---|---|
committer | Arnold D. Robbins <arnold@skeeve.com> | 2013-12-12 20:45:59 +0200 |
commit | 9953f4cee02f2781ee5da2e42bcb837c1a849cb0 (patch) | |
tree | b322336208f51473e694287d8efe0891c4b10c8a /vms/vms_gawk_main_wrapper.c | |
parent | 70778853494d7ec00a77d42617fdd030c74c9bec (diff) | |
download | egawk-9953f4cee02f2781ee5da2e42bcb837c1a849cb0.tar.gz egawk-9953f4cee02f2781ee5da2e42bcb837c1a849cb0.tar.bz2 egawk-9953f4cee02f2781ee5da2e42bcb837c1a849cb0.zip |
First round of VMS changes.
Diffstat (limited to 'vms/vms_gawk_main_wrapper.c')
-rw-r--r-- | vms/vms_gawk_main_wrapper.c | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/vms/vms_gawk_main_wrapper.c b/vms/vms_gawk_main_wrapper.c new file mode 100644 index 00000000..367f0cc2 --- /dev/null +++ b/vms/vms_gawk_main_wrapper.c @@ -0,0 +1,487 @@ +/* File: vms_gawk_main_wrapper.c + * + * This module provides a wrapper around the main() function of a ported + * program for two functions: + * + * 1. Make sure that the argv[0] string is set as close as possible to + * what the original command was given. + * + * 2. Make sure that the posix exit is called. + * + * 3. Fixup the timezone information. + * + * Copyright 2012, John Malmberg + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + + +#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); + +/* 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; +} + +int original_main(int argc, char ** argv, char **env); + +int main(int argc, char ** argv, char **env) { +int status; +int result; +char arg_nam[256]; +char **new_argv; +char *tz_rule; + +#ifdef TEST_MAIN + printf("original argv[0] = %s\n", argv[0]); +#endif + + new_argv = argv; + result = 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. */ + if (argv[0][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(&argv[0][1], '/'); + if (nextslash != NULL) { + length = nextslash - argv[0] - 1; + + /* Cast needed for HP C compiler diagnostic */ + devname_dsc.dsc$a_pointer = (char *)&argv[0][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 */ +#ifdef TEST_MAIN + printf("sys$getdviw failed with status %d\n", status); +#endif + result = 0; + } else if (!$VMS_STATUS_SUCCESS(dvi_iosb[0])) { +#ifdef TEST_MAIN + printf("sys$getdviw failed with iosb %d\n", dvi_iosb[0]); +#endif + 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(arg_nam, &argv[0][1], length); + arg_nam[length] = 0; + result = (strcasecmp(devnam, arg_nam) == 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)) { + strncpy(arg_nam, &argv[0][1], length); + arg_nam[length] = 0; + result = (strcasecmp(diskvolnam, arg_nam) == 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; + + path_desc.dsc$a_pointer = (char *)argv[0]; /* cast ok */ + path_desc.dsc$w_length = strlen(argv[0]); + 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 */ + + /* Need a new argv array */ + new_argv = malloc((argc + 1) * (sizeof(char *))); + new_argv[0] = arg_nam; + i = 1; + while (i < argc) { + new_argv[i] = argv[i]; + i++; + } + + /* 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(arg_nam, name, name_len); + arg_nam[name_len] = 0; + + /* 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(&arg_nam[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(argv[0], '/'); + + 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(arg_nam, 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(arg_nam, '.'); + 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(arg_nam, '.'); + 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; + } + } + } + + /* Need a new argv array */ + new_argv = malloc((argc + 1) * (sizeof(char *))); + new_argv[0] = arg_nam; + i = 1; + while (i < argc) { + new_argv[i] = argv[i]; + i++; + } + new_argv[i] = 0; + + } else { + /* There is no way that the code should ever get here */ + /* As we already verified that the '/' was present */ + fprintf(stderr, "Sanity failure somewhere we lost a '/'\n"); + } + + } + + /* + * The vms_main_wrapper 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. + */ + char * shell; + int lcname = 0; + shell = getenv("SHELL"); + if (shell != NULL) { + if (strcmp(shell, "DCL") == 0) { + lcname = 1; + } + } else { + lcname = 1; + } + if (lcname == 1) { + int i = 0; + while (new_argv[0][i] != 0) { + new_argv[0][i] = tolower(new_argv[0][i]); + i++; + } + } + + /* Fix up the time zone */ + 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); + + exit(original_main(argc, new_argv, env)); + return 1; /* Needed to silence compiler diagnostic */ +} + +#define main original_main + +#ifdef TEST_MAIN + +int main(int argc, char ** argv, char **env) { + + printf("modified argv[0] = %s\n", argv[0]); + + return 0; +} + +#endif |