Change make license
[platform/upstream/make.git] / 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-2020 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 <http://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 (lastdot[i])) {
413                     i++;
414                 }
415                 if (lastdot[i] == 0) {
416                     *lastdot = 0;
417                 }
418             }
419
420             /* Find the .exe on the name (case insenstive) 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