| /* File: vms_progname.c |
| * |
| * This module provides a fixup of the program name. |
| * |
| * This module is designed to be a plug in replacement for the |
| * progname module used by many GNU utilities with a few enhancements |
| * needed for GNU Make. |
| * |
| * It does not support the HAVE_DECL_PROGRAM_INVOCATION_* macros at this |
| * time. |
| * |
| * Make sure that the program_name string is set as close as possible to |
| * what the original command was given. |
| * |
| * When run from DCL, The argv[0] element is initialized with an absolute |
| * path name. The decc$ feature logical names can control the format |
| * of this pathname. In some cases it causes the UNIX format name to be |
| * formatted incorrectly. |
| * |
| * This DCL provided name is usually incompatible with what is expected to |
| * be provided by Unix programs and needs to be replaced. |
| * |
| * When run from an exec() call, the argv[0] element is initialized by the |
| * program. This name is compatible with what is expected to be provided |
| * by Unix programs and should be passed through unchanged. |
| * |
| * The DCL provided name can be detected because it always contains the |
| * device name. |
| * |
| * DCL examples: |
| * devname:[dir]program.exe;1 Normal VMS - remove path and .EXE;n |
| * devname:[dir]facility$program.exe;1 Facility also needs removal. |
| * /devname/dir/program.exe |
| * /DISK$VOLUME/dir/program.exe.1 Bug version should not be there. |
| * /DISK$VOLUME/dir/program. Bug Period should not be there. |
| * |
| */ |
| |
| /* Copyright (C) 2014-2016 Free Software Foundation, Inc. |
| |
| GNU Make 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. |
| |
| GNU Make 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, see <http://www.gnu.org/licenses/>. */ |
| |
| |
| /* Per copyright assignment agreement with the Free Software Foundation |
| this software may be available under under other license agreements |
| and copyrights. */ |
| |
| |
| #ifdef HAVE_CONFIG_H |
| # include "config.h" |
| #endif |
| |
| #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> |
| |
| #ifdef USE_PROGNAME_H |
| # include "progname.h" |
| #endif |
| |
| #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); |
| |
| /* String containing name the program is called with. |
| To be initialized by main(). */ |
| |
| const char *program_name = NULL; |
| |
| static int internal_need_vms_symbol = 0; |
| |
| static char vms_new_nam[256]; |
| |
| int |
| need_vms_symbol (void) |
| { |
| return internal_need_vms_symbol; |
| } |
| |
| |
| void |
| set_program_name (const char *argv0) |
| { |
| int status; |
| int result; |
| |
| #ifdef DEBUG |
| printf ("original argv0 = %s\n", argv0); |
| #endif |
| |
| /* Posix requires non-NULL argv[0] */ |
| if (argv0 == NULL) |
| { |
| fputs ("A NULL argv[0] was passed through an exec system call.\n", |
| stderr); |
| abort (); |
| } |
| |
| program_name = argv0; |
| result = 0; |
| internal_need_vms_symbol = 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 (argv0[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; |
| |
| internal_need_vms_symbol = 1; |
| |
| /* 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 (&argv0[1], '/'); |
| if (nextslash != NULL) |
| { |
| length = nextslash - argv0 - 1; |
| |
| /* Cast needed for HP C compiler diagnostic */ |
| devname_dsc.dsc$a_pointer = (char *)&argv0[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 DEBUG |
| printf ("sys$getdviw failed with status %d\n", status); |
| #endif |
| result = 0; |
| } |
| else if (!$VMS_STATUS_SUCCESS (dvi_iosb[0])) |
| { |
| #ifdef DEBUG |
| 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 (vms_new_nam, &argv0[1], length); |
| vms_new_nam[length] = 0; |
| result = (strcasecmp (devnam, vms_new_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 (vms_new_nam, &argv0[1], length); |
| vms_new_nam[length] = 0; |
| result = (strcasecmp (diskvolnam, vms_new_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 *)argv0; /* cast ok */ |
| path_desc.dsc$w_length = strlen (argv0); |
| 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 */ |
| internal_need_vms_symbol = 1; |
| |
| /* 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_new_nam, name, name_len); |
| vms_new_nam[name_len] = 0; |
| |
| /* Commit to using the new name */ |
| program_name = vms_new_nam; |
| |
| /* We only keep the extension if it is not ".exe" */ |
| keep_ext = 0; |
| ext = item_list[3].component; |
| |
| if (item_list[3].length != 1) |
| { |
| keep_ext = 1; |
| if (item_list[3].length == 4) |
| { |
| if ((ext[1] == 'e' || ext[1] == 'E') && |
| (ext[2] == 'x' || ext[2] == 'X') && |
| (ext[3] == 'e' || ext[3] == 'E')) |
| keep_ext = 0; |
| } |
| } |
| |
| if (keep_ext == 1) |
| strncpy (&vms_new_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 (argv0, '/'); |
| |
| 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_new_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 (vms_new_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 (vms_new_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; |
| } |
| } |
| } |
| |
| /* Commit to new name */ |
| program_name = vms_new_nam; |
| |
| } 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"); |
| } |
| } |
| } |
| |
| #ifdef DEBUG |
| |
| int |
| main (int argc, char ** argv, char **env) |
| { |
| |
| char command[1024]; |
| |
| set_program_name (argv[0]); |
| |
| printf ("modified argv[0] = %s\n", program_name); |
| |
| return 0; |
| } |
| #endif |