(root)/
make-4.4/
src/
vms_progname.c
       1  /* File: vms_progname.c
       2   *
       3   * This module provides a fixup of the program name.
       4   *
       5   * This module is designed to be a plug in replacement for the
       6   * progname module used by many GNU utilities with a few enhancements
       7   * needed for GNU Make.
       8   *
       9   * It does not support the HAVE_DECL_PROGRAM_INVOCATION_* macros at this
      10   * time.
      11   *
      12   * Make sure that the program_name string is set as close as possible to
      13   *    what the original command was given.
      14   *
      15   * When run from DCL, The argv[0] element is initialized with an absolute
      16   * path name.  The decc$ feature logical names can control the format
      17   * of this pathname.  In some cases it causes the UNIX format name to be
      18   * formatted incorrectly.
      19   *
      20   * This DCL provided name is usually incompatible with what is expected to
      21   * be provided by Unix programs and needs to be replaced.
      22   *
      23   * When run from an exec() call, the argv[0] element is initialized by the
      24   * program.  This name is compatible with what is expected to be provided
      25   * by Unix programs and should be passed through unchanged.
      26   *
      27   * The DCL provided name can be detected because it always contains the
      28   * device name.
      29   *
      30   * DCL examples:
      31   *    devname:[dir]program.exe;1         Normal VMS - remove path and .EXE;n
      32   *    devname:[dir]facility$program.exe;1   Facility also needs removal.
      33   *    /devname/dir/program.exe
      34   *    /DISK$VOLUME/dir/program.exe.1     Bug version should not be there.
      35   *    /DISK$VOLUME/dir/program.          Bug Period should not be there.
      36   *
      37   */
      38  
      39  /* Copyright (C) 2014-2022 Free Software Foundation, Inc.
      40  
      41  GNU Make is free software; you can redistribute it and/or modify it under the
      42  terms of the GNU General Public License as published by the Free Software
      43  Foundation; either version 3 of the License, or (at your option) any later
      44  version.
      45  
      46  GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
      47  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      48  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      49  
      50  You should have received a copy of the GNU General Public License along with
      51  this program.  If not, see <https://www.gnu.org/licenses/>.  */
      52  
      53  
      54  /* Per copyright assignment agreement with the Free Software Foundation
      55     this software may be available under under other license agreements
      56     and copyrights. */
      57  
      58  
      59  #ifdef HAVE_CONFIG_H
      60  # include "config.h"
      61  #endif
      62  
      63  #include <stdio.h>
      64  #include <string.h>
      65  #include <ctype.h>
      66  #include <stdlib.h>
      67  
      68  #include <descrip.h>
      69  #include <dvidef.h>
      70  #include <efndef.h>
      71  #include <fscndef.h>
      72  #include <stsdef.h>
      73  
      74  #ifdef USE_PROGNAME_H
      75  # include "progname.h"
      76  #endif
      77  
      78  #pragma member_alignment save
      79  #pragma nomember_alignment longword
      80  struct item_list_3
      81  {
      82    unsigned short len;
      83    unsigned short code;
      84    void * bufadr;
      85    unsigned short * retlen;
      86  };
      87  
      88  struct filescan_itmlst_2
      89  {
      90    unsigned short length;
      91    unsigned short itmcode;
      92    char * component;
      93  };
      94  
      95  #pragma member_alignment
      96  
      97  int
      98  SYS$GETDVIW (unsigned long efn,
      99               unsigned short chan,
     100               const struct dsc$descriptor_s * devnam,
     101               const struct item_list_3 * itmlst,
     102               void * iosb,
     103               void (* astadr)(unsigned long),
     104               unsigned long astprm,
     105               void * nullarg);
     106  
     107  int
     108  SYS$FILESCAN (const struct dsc$descriptor_s * srcstr,
     109                struct filescan_itmlst_2 * valuelist,
     110                unsigned long * fldflags,
     111                struct dsc$descriptor_s *auxout,
     112                unsigned short * retlen);
     113  
     114  /* String containing name the program is called with.
     115     To be initialized by main().  */
     116  
     117  const char *program_name = NULL;
     118  
     119  static int internal_need_vms_symbol = 0;
     120  
     121  static char vms_new_nam[256];
     122  
     123  int
     124  need_vms_symbol (void)
     125  {
     126    return internal_need_vms_symbol;
     127  }
     128  
     129  
     130  void
     131  set_program_name (const char *argv0)
     132  {
     133    int status;
     134    int result;
     135  
     136  #ifdef DEBUG
     137    printf ("original argv0 = %s\n", argv0);
     138  #endif
     139  
     140    /* Posix requires non-NULL argv[0] */
     141    if (argv0 == NULL)
     142      {
     143        fputs ("A NULL argv[0] was passed through an exec system call.\n",
     144               stderr);
     145        abort ();
     146      }
     147  
     148    program_name = argv0;
     149    result = 0;
     150    internal_need_vms_symbol = 0;
     151  
     152    /* If the path name starts with a /, then it is an absolute path         */
     153    /* that may have been generated by the CRTL instead of the command name  */
     154    /* If it is the device name between the slashes, then this was likely    */
     155    /* from the run command and needs to be fixed up.                        */
     156    /* If the DECC$POSIX_COMPLIANT_PATHNAMES is set to 2, then it is the     */
     157    /* DISK$VOLUME that will be present, and it will still need to be fixed. */
     158    if (argv0[0] == '/')
     159      {
     160        char * nextslash;
     161        int length;
     162        struct item_list_3 itemlist[3];
     163        unsigned short dvi_iosb[4];
     164        char alldevnam[64];
     165        unsigned short alldevnam_len;
     166        struct dsc$descriptor_s devname_dsc;
     167        char diskvolnam[256];
     168        unsigned short diskvolnam_len;
     169  
     170        internal_need_vms_symbol = 1;
     171  
     172         /* Get some information about the disk */
     173        /*--------------------------------------*/
     174        itemlist[0].len = (sizeof alldevnam) - 1;
     175        itemlist[0].code = DVI$_ALLDEVNAM;
     176        itemlist[0].bufadr = alldevnam;
     177        itemlist[0].retlen = &alldevnam_len;
     178        itemlist[1].len = (sizeof diskvolnam) - 1 - 5;
     179        itemlist[1].code = DVI$_VOLNAM;
     180        itemlist[1].bufadr = &diskvolnam[5];
     181        itemlist[1].retlen = &diskvolnam_len;
     182        itemlist[2].len = 0;
     183        itemlist[2].code = 0;
     184  
     185        /* Add the prefix for the volume name. */
     186        /* SYS$GETDVI will append the volume name to this */
     187        strcpy (diskvolnam, "DISK$");
     188  
     189        nextslash = strchr (&argv0[1], '/');
     190        if (nextslash != NULL)
     191          {
     192            length = nextslash - argv0 - 1;
     193  
     194            /* Cast needed for HP C compiler diagnostic */
     195            devname_dsc.dsc$a_pointer = (char *)&argv0[1];
     196            devname_dsc.dsc$w_length = length;
     197            devname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
     198            devname_dsc.dsc$b_class = DSC$K_CLASS_S;
     199  
     200            status = SYS$GETDVIW (EFN$C_ENF, 0, &devname_dsc, itemlist,
     201                                  dvi_iosb, NULL, 0, 0);
     202            if (!$VMS_STATUS_SUCCESS (status))
     203              {
     204                /* If the sys$getdviw fails, then this path was passed by */
     205                /* An exec() program and not from DCL, so do nothing */
     206                /* An example is "/tmp/program" where tmp: does not exist */
     207  #ifdef DEBUG
     208                printf ("sys$getdviw failed with status %d\n", status);
     209  #endif
     210                result = 0;
     211               }
     212             else if (!$VMS_STATUS_SUCCESS (dvi_iosb[0]))
     213               {
     214  #ifdef DEBUG
     215                  printf ("sys$getdviw failed with iosb %d\n", dvi_iosb[0]);
     216  #endif
     217                  result = 0;
     218                }
     219              else
     220                {
     221                  char * devnam;
     222                  int devnam_len;
     223                  char argv_dev[64];
     224  
     225                  /* Null terminate the returned alldevnam */
     226                  alldevnam[alldevnam_len] = 0;
     227                  devnam = alldevnam;
     228                  devnam_len = alldevnam_len;
     229  
     230                  /* Need to skip past any leading underscore */
     231                  if (devnam[0] == '_')
     232                    {
     233                      devnam++;
     234                      devnam_len--;
     235                    }
     236  
     237                  /* And remove the trailing colon */
     238                  if (devnam[devnam_len - 1] == ':')
     239                    {
     240                      devnam_len--;
     241                      devnam[devnam_len] = 0;
     242                    }
     243  
     244                  /* Null terminate the returned volnam */
     245                  diskvolnam_len += 5;
     246                  diskvolnam[diskvolnam_len] = 0;
     247  
     248                  /* Check first for normal CRTL behavior */
     249                  if (devnam_len == length)
     250                    {
     251                      strncpy (vms_new_nam, &argv0[1], length);
     252                      vms_new_nam[length] = 0;
     253                      result = (strcasecmp (devnam, vms_new_nam) == 0);
     254                    }
     255  
     256                  /* If we have not got a match, check for POSIX Compliant */
     257                  /* behavior.  To be more accurate, we could also check */
     258                  /* to see if that feature is active. */
     259                  if ((result == 0) && (diskvolnam_len == length))
     260                    {
     261                      strncpy (vms_new_nam, &argv0[1], length);
     262                      vms_new_nam[length] = 0;
     263                      result = (strcasecmp (diskvolnam, vms_new_nam) == 0);
     264                    }
     265                }
     266          }
     267        }
     268      else
     269        {
     270          /* The path did not start with a slash, so it could be VMS format */
     271          /* If it is vms format, it has a volume/device in it as it must   */
     272          /* be an absolute path */
     273          struct dsc$descriptor_s path_desc;
     274          int status;
     275          unsigned long field_flags;
     276          struct filescan_itmlst_2 item_list[5];
     277          char * volume;
     278          char * name;
     279          int name_len;
     280          char * ext;
     281  
     282          path_desc.dsc$a_pointer = (char *)argv0; /* cast ok */
     283          path_desc.dsc$w_length = strlen (argv0);
     284          path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
     285          path_desc.dsc$b_class = DSC$K_CLASS_S;
     286  
     287          /* Don't actually need to initialize anything buf itmcode */
     288          /* I just do not like uninitialized input values */
     289  
     290          /* Sanity check, this must be the same length as input */
     291          item_list[0].itmcode = FSCN$_FILESPEC;
     292          item_list[0].length = 0;
     293          item_list[0].component = NULL;
     294  
     295          /* If the device is present, then it if a VMS spec */
     296          item_list[1].itmcode = FSCN$_DEVICE;
     297          item_list[1].length = 0;
     298          item_list[1].component = NULL;
     299  
     300          /* we need the program name and type */
     301          item_list[2].itmcode = FSCN$_NAME;
     302          item_list[2].length = 0;
     303          item_list[2].component = NULL;
     304  
     305          item_list[3].itmcode = FSCN$_TYPE;
     306          item_list[3].length = 0;
     307          item_list[3].component = NULL;
     308  
     309          /* End the list */
     310          item_list[4].itmcode = 0;
     311          item_list[4].length = 0;
     312          item_list[4].component = NULL;
     313  
     314          status = SYS$FILESCAN ((const struct dsc$descriptor_s *)&path_desc,
     315                                 item_list, &field_flags, NULL, NULL);
     316  
     317  
     318          if ($VMS_STATUS_SUCCESS (status) &&
     319             (item_list[0].length == path_desc.dsc$w_length) &&
     320             (item_list[1].length != 0))
     321            {
     322  
     323              char * dollar;
     324              int keep_ext;
     325              int i;
     326  
     327              /* We need the filescan to be successful, */
     328              /* same length as input, and a volume to be present */
     329              internal_need_vms_symbol = 1;
     330  
     331              /* We will assume that we only get to this path on a version */
     332              /* of VMS that does not support the EFS character set */
     333  
     334              /* There may be a xxx$ prefix on the image name.  Linux */
     335              /* programs do not handle that well, so strip the prefix */
     336              name = item_list[2].component;
     337              name_len = item_list[2].length;
     338              dollar = strrchr (name, '$');
     339              if (dollar != NULL)
     340                {
     341                  dollar++;
     342                  name_len = name_len - (dollar - name);
     343                  name = dollar;
     344                }
     345  
     346              strncpy (vms_new_nam, name, name_len);
     347              vms_new_nam[name_len] = 0;
     348  
     349              /* Commit to using the new name */
     350              program_name = vms_new_nam;
     351  
     352              /* We only keep the extension if it is not ".exe" */
     353              keep_ext = 0;
     354              ext = item_list[3].component;
     355  
     356              if (item_list[3].length != 1)
     357                {
     358                  keep_ext = 1;
     359                  if (item_list[3].length == 4)
     360                    {
     361                      if ((ext[1] == 'e' || ext[1] == 'E') &&
     362                          (ext[2] == 'x' || ext[2] == 'X') &&
     363                          (ext[3] == 'e' || ext[3] == 'E'))
     364                        keep_ext = 0;
     365                    }
     366                }
     367  
     368              if (keep_ext == 1)
     369                strncpy (&vms_new_nam[name_len], ext, item_list[3].length);
     370            }
     371        }
     372  
     373      if (result)
     374        {
     375          char * lastslash;
     376          char * dollar;
     377          char * dotexe;
     378          char * lastdot;
     379          char * extension;
     380  
     381          /* This means it is probably the name from a DCL command */
     382          /* Find the last slash which separates the file from the */
     383          /* path. */
     384          lastslash = strrchr (argv0, '/');
     385  
     386          if (lastslash != NULL) {
     387              int i;
     388  
     389              lastslash++;
     390  
     391              /* There may be a xxx$ prefix on the image name.  Linux */
     392              /* programs do not handle that well, so strip the prefix */
     393              dollar = strrchr (lastslash, '$');
     394  
     395              if (dollar != NULL) {
     396                  dollar++;
     397                  lastslash = dollar;
     398              }
     399  
     400              strcpy (vms_new_nam, lastslash);
     401  
     402              /* In UNIX mode + EFS character set, there should not be a */
     403              /* version present, as it is not possible when parsing to  */
     404              /* tell if it is a version or part of the UNIX filename as */
     405              /* UNIX programs use numeric extensions for many reasons.  */
     406  
     407              lastdot = strrchr (vms_new_nam, '.');
     408              if (lastdot != NULL) {
     409                  int i;
     410  
     411                  i = 1;
     412                  while (isdigit ((unsigned char) lastdot[i])) {
     413                      i++;
     414                  }
     415                  if (lastdot[i] == 0) {
     416                      *lastdot = 0;
     417                  }
     418              }
     419  
     420              /* Find the .exe on the name (case insensitive) and toss it */
     421              dotexe = strrchr (vms_new_nam, '.');
     422              if (dotexe != NULL) {
     423                  if ((dotexe[1] == 'e' || dotexe[1] == 'E') &&
     424                      (dotexe[2] == 'x' || dotexe[2] == 'X') &&
     425                      (dotexe[3] == 'e' || dotexe[3] == 'E') &&
     426                      (dotexe[4] == 0)) {
     427  
     428                      *dotexe = 0;
     429                  } else {
     430                       /* Also need to handle a null extension because of a */
     431                       /* CRTL bug. */
     432                       if (dotexe[1] == 0) {
     433                           *dotexe = 0;
     434                      }
     435                  }
     436              }
     437  
     438              /* Commit to new name */
     439              program_name = vms_new_nam;
     440  
     441          } else {
     442              /* There is no way that the code should ever get here */
     443              /* As we already verified that the '/' was present */
     444              fprintf (stderr, "Sanity failure somewhere we lost a '/'\n");
     445          }
     446      }
     447  }
     448  
     449  #ifdef DEBUG
     450  
     451  int
     452  main (int argc, char ** argv, char **env)
     453  {
     454  
     455    char command[1024];
     456  
     457    set_program_name (argv[0]);
     458  
     459    printf ("modified argv[0] = %s\n", program_name);
     460  
     461    return 0;
     462  }
     463  #endif