mirror of
https://git.savannah.gnu.org/git/make.git
synced 2024-11-24 20:20:35 +00:00
463 lines
14 KiB
C
463 lines
14 KiB
C
/* 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-2017 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
|