aboutsummaryrefslogtreecommitdiffstats
path: root/vms/vms_gawk_main_wrapper.c
diff options
context:
space:
mode:
Diffstat (limited to 'vms/vms_gawk_main_wrapper.c')
-rw-r--r--vms/vms_gawk_main_wrapper.c487
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