Imported Upstream version 4.2.1
[platform/upstream/make.git] / src / vms_export_symbol.c
1 /* File: vms_export_symbol.c
2  *
3  * Some programs need special environment variables deported as DCL
4  * DCL symbols.
5  */
6
7 /* Copyright (C) 2014-2020 Free Software Foundation, Inc.
8
9 GNU Make is free software; you can redistribute it and/or modify it under the
10 terms of the GNU General Public License as published by the Free Software
11 Foundation; either version 3 of the License, or (at your option) any later
12 version.
13
14 GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License along with
19 this program.  If not, see <http://www.gnu.org/licenses/>.  */
20
21
22 /* Per copyright assignment agreement with the Free Software Foundation
23    this software may be available under under other license agreements
24    and copyrights. */
25
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <errno.h>
31
32 #include <descrip.h>
33 #include <stsdef.h>
34 #include <ssdef.h>
35 #include <unixlib.h>
36 #include <libclidef.h>
37
38 #pragma member_alignment save
39 #pragma nomember_alignment longword
40 struct item_list_3
41 {
42   unsigned short len;
43   unsigned short code;
44   void * bufadr;
45   unsigned short * retlen;
46 };
47
48
49 #pragma member_alignment
50
51 int
52 LIB$GET_SYMBOL (const struct dsc$descriptor_s * symbol,
53                 struct dsc$descriptor_s * value,
54                 unsigned short * value_len,
55                 const unsigned long * table);
56
57 int
58 LIB$SET_SYMBOL (const struct dsc$descriptor_s * symbol,
59                 const struct dsc$descriptor_s * value,
60                 const unsigned long * table);
61
62 int
63 LIB$DELETE_SYMBOL (const struct dsc$descriptor_s * symbol,
64                    const unsigned long * table);
65
66 #define MAX_DCL_SYMBOL_LEN (255)
67 #if __CRTL_VER >= 70302000 && !defined(__VAX)
68 # define MAX_DCL_SYMBOL_VALUE (8192)
69 #else
70 # define MAX_DCL_SYMBOL_VALUE (1024)
71 #endif
72
73 struct dcl_symbol
74 {
75   struct dcl_symbol * link;
76   struct dsc$descriptor_s name_desc;
77   struct dsc$descriptor_s value_desc;
78   char name[MAX_DCL_SYMBOL_LEN + 1];    /* + 1 byte for null terminator */
79   char value[MAX_DCL_SYMBOL_VALUE +1];  /* + 1 byte for null terminator */
80   char pad[3]; /* Pad structure to longword allignment */
81 };
82
83 static struct dcl_symbol * vms_dcl_symbol_head = NULL;
84
85 /* Restore symbol state to original condition. */
86 static unsigned long
87 clear_dcl_symbol (struct dcl_symbol * symbol)
88 {
89
90   const unsigned long symtbl = LIB$K_CLI_LOCAL_SYM;
91   int status;
92
93   if (symbol->value_desc.dsc$w_length == (unsigned short)-1)
94     status = LIB$DELETE_SYMBOL (&symbol->name_desc, &symtbl);
95   else
96     status = LIB$SET_SYMBOL (&symbol->name_desc,
97                              &symbol->value_desc, &symtbl);
98   return status;
99 }
100
101
102 /* Restore all exported symbols to their original conditions */
103 static void
104 clear_exported_symbols (void)
105 {
106
107   struct dcl_symbol * symbol;
108
109   symbol = vms_dcl_symbol_head;
110
111   /* Walk the list of symbols.  This is done durring exit,
112    * so no need to free memory.
113    */
114   while (symbol != NULL)
115   {
116     clear_dcl_symbol (symbol);
117     symbol = symbol->link;
118   }
119
120 }
121
122
123 /* Restore the symbol back to the original value
124  * symbol name is either a plain name or of the form "symbol=name" where
125  * the name portion is ignored.
126  */
127 void
128 vms_restore_symbol (const char * string)
129 {
130
131   struct dcl_symbol * symbol;
132   char name[MAX_DCL_SYMBOL_LEN + 1];
133   int status;
134   char * value;
135   int name_len;
136
137   symbol = vms_dcl_symbol_head;
138
139   /* Isolate the name from the value */
140   value = strchr (string, '=');
141   if (value != NULL)
142     {
143       /* Copy the name from the string */
144       name_len = (value - string);
145     }
146   else
147     name_len = strlen (string);
148
149   if (name_len > MAX_DCL_SYMBOL_LEN)
150     name_len = MAX_DCL_SYMBOL_LEN;
151
152   strncpy (name, string, name_len);
153   name[name_len] = 0;
154
155   /* Walk the list of symbols.  The saved symbol is not freed
156    * symbols are likely to be overwritten multiple times, so this
157    * saves time in saving them each time.
158    */
159   while (symbol != NULL)
160     {
161       int result;
162       result = strcmp (symbol->name, name);
163       if (result == 0)
164         {
165           clear_dcl_symbol (symbol);
166           break;
167         }
168       symbol = symbol->link;
169     }
170 }
171
172 int
173 vms_export_dcl_symbol (const char * name, const char * value)
174 {
175
176   struct dcl_symbol * symbol;
177   struct dcl_symbol * next;
178   struct dcl_symbol * link;
179   int found;
180   const unsigned long symtbl = LIB$K_CLI_LOCAL_SYM;
181   struct dsc$descriptor_s value_desc;
182   int string_len;
183   int status;
184   char new_value[MAX_DCL_SYMBOL_VALUE + 1];
185   char * dollarp;
186
187   next = vms_dcl_symbol_head;
188   link = vms_dcl_symbol_head;
189
190   /* Is symbol already exported? */
191   found = 0;
192   while ((found == 0) && (link != NULL))
193     {
194       int x;
195       found = !strncasecmp (link->name, name, MAX_DCL_SYMBOL_LEN);
196       if (found)
197         symbol = link;
198       next = link;
199       link = link->link;
200     }
201
202   /* New symbol, set it up */
203   if (found == 0)
204     {
205       symbol = malloc (sizeof (struct dcl_symbol));
206       if (symbol == NULL)
207         return SS$_INSFMEM;
208
209       /* Construct the symbol descriptor, used for both saving
210        * the old symbol and creating the new symbol.
211        */
212       symbol->name_desc.dsc$w_length = strlen (name);
213       if (symbol->name_desc.dsc$w_length > MAX_DCL_SYMBOL_LEN)
214         symbol->name_desc.dsc$w_length = MAX_DCL_SYMBOL_LEN;
215
216       strncpy (symbol->name, name, symbol->name_desc.dsc$w_length);
217       symbol->name[symbol->name_desc.dsc$w_length] = 0;
218       symbol->name_desc.dsc$a_pointer = symbol->name;
219       symbol->name_desc.dsc$b_dtype = DSC$K_DTYPE_T;
220       symbol->name_desc.dsc$b_class = DSC$K_CLASS_S;
221
222       /* construct the value descriptor, used only for saving
223        * the old symbol.
224        */
225       symbol->value_desc.dsc$a_pointer = symbol->value;
226       symbol->value_desc.dsc$w_length = MAX_DCL_SYMBOL_VALUE;
227       symbol->value_desc.dsc$b_dtype = DSC$K_DTYPE_T;
228       symbol->value_desc.dsc$b_class = DSC$K_CLASS_S;
229     }
230
231   if (found == 0)
232     {
233       unsigned long old_symtbl;
234       unsigned short value_len;
235
236       /* Look up the symbol */
237       status = LIB$GET_SYMBOL (&symbol->name_desc, &symbol->value_desc,
238                                &value_len, &old_symtbl);
239       if (!$VMS_STATUS_SUCCESS (status))
240         value_len = (unsigned short)-1;
241       else if (old_symtbl != symtbl)
242         value_len = (unsigned short)-1;
243
244       symbol->value_desc.dsc$w_length = value_len;
245
246       /* Store it away */
247       if (value_len != (unsigned short) -1)
248         symbol->value[value_len] = 0;
249
250       /* Make sure atexit scheduled */
251       if (vms_dcl_symbol_head == NULL)
252         {
253           vms_dcl_symbol_head = symbol;
254           atexit (clear_exported_symbols);
255         }
256       else
257         {
258           /* Extend the chain */
259           next->link = symbol;
260         }
261     }
262
263   /* Create or replace a symbol */
264   value_desc.dsc$a_pointer = new_value;
265   string_len = strlen (value);
266   if (string_len > MAX_DCL_SYMBOL_VALUE)
267     string_len = MAX_DCL_SYMBOL_VALUE;
268
269   strncpy (new_value, value, string_len);
270   new_value[string_len] = 0;
271
272   /* Special handling for GNU Make.  GNU Make doubles the dollar signs
273    * in environment variables read in from getenv().  Make exports symbols
274    * with the dollar signs already doubled.  So all $$ must be converted
275    * back to $.
276    * If the first $ is not doubled, then do not convert at all.
277    */
278   dollarp = strchr (new_value, '$');
279   while (dollarp && dollarp[1] == '$')
280     {
281       int left;
282       dollarp++;
283       left = string_len - (dollarp - new_value - 1);
284       string_len--;
285       if (left > 0)
286         {
287           memmove (dollarp, &dollarp[1], left);
288           dollarp = strchr (&dollarp[1], '$');
289         }
290       else
291         {
292           /* Ended with $$, simple case */
293           dollarp[1] = 0;
294           break;
295         }
296     }
297   value_desc.dsc$w_length = string_len;
298   value_desc.dsc$b_dtype = DSC$K_DTYPE_T;
299   value_desc.dsc$b_class = DSC$K_CLASS_S;
300   status = LIB$SET_SYMBOL (&symbol->name_desc, &value_desc, &symtbl);
301   return status;
302 }
303
304 /* export a DCL symbol using a string in the same syntax as putenv */
305 int
306 vms_putenv_symbol (const char * string)
307 {
308
309   char name[MAX_DCL_SYMBOL_LEN + 1];
310   int status;
311   char * value;
312   int name_len;
313
314   /* Isolate the name from the value */
315   value = strchr (string, '=');
316   if (value == NULL)
317     {
318       errno = EINVAL;
319       return -1;
320     }
321
322   /* Copy the name from the string */
323   name_len = (value - string);
324   if (name_len > MAX_DCL_SYMBOL_LEN)
325     name_len = MAX_DCL_SYMBOL_LEN;
326
327   strncpy (name, string, name_len);
328   name[name_len] = 0;
329
330   /* Skip past the "=" */
331   value++;
332
333   /* Export the symbol */
334   status = vms_export_dcl_symbol (name, value);
335
336   /* Convert the error to Unix format */
337   if (!$VMS_STATUS_SUCCESS (status))
338     {
339       errno = EVMSERR;
340       vaxc$errno = status;
341       return -1;
342     }
343   return 0;
344 }
345
346 #if __CRTL_VER >= 70301000
347 # define transpath_parm transpath
348 #else
349 static char transpath[MAX_DCL_SYMBOL_VALUE];
350 #endif
351
352 /* Helper callback routine for converting Unix paths to VMS */
353 static int
354 to_vms_action (char * vms_spec, int flag, char * transpath_parm)
355 {
356   strncpy (transpath, vms_spec, MAX_DCL_SYMBOL_VALUE - 1);
357   transpath[MAX_DCL_SYMBOL_VALUE - 1] = 0;
358   return 0;
359 }
360
361 #ifdef __DECC
362 # pragma message save
363   /* Undocumented extra parameter use triggers a ptrmismatch warning */
364 # pragma message disable ptrmismatch
365 #endif
366
367 /* Create a foreign command only visible to children */
368 int
369 create_foreign_command (const char * command, const char * image)
370 {
371   char vms_command[MAX_DCL_SYMBOL_VALUE + 1];
372   int status;
373
374   vms_command[0] = '$';
375   vms_command[1] = 0;
376   if (image[0] == '/')
377     {
378 #if __CRTL_VER >= 70301000
379       /* Current decc$to_vms is reentrant */
380       decc$to_vms (image, to_vms_action, 0, 1, &vms_command[1]);
381 #else
382       /* Older decc$to_vms is not reentrant */
383       decc$to_vms (image, to_vms_action, 0, 1);
384       strncpy (&vms_command[1], transpath, MAX_DCL_SYMBOL_VALUE - 1);
385       vms_command[MAX_DCL_SYMBOL_VALUE] = 0;
386 #endif
387     }
388   else
389     {
390       strncpy (&vms_command[1], image, MAX_DCL_SYMBOL_VALUE - 1);
391       vms_command[MAX_DCL_SYMBOL_VALUE] = 0;
392     }
393   status = vms_export_dcl_symbol (command, vms_command);
394
395   return status;
396 }
397 #ifdef __DECC
398 # pragma message restore
399 #endif
400
401
402 #ifdef DEBUG
403
404 int
405 main(int argc, char ** argv, char **env)
406 {
407
408   char value[MAX_DCL_SYMBOL_VALUE +1];
409   int status = 0;
410   int putenv_status;
411   int vms_status;
412   struct dsc$descriptor_s name_desc;
413   struct dsc$descriptor_s value_desc;
414   const unsigned long symtbl = LIB$K_CLI_LOCAL_SYM;
415   unsigned short value_len;
416   unsigned long old_symtbl;
417   int result;
418   const char * vms_command = "vms_export_symbol";
419   const char * vms_image = "test_image.exe";
420   const char * vms_symbol1 = "test_symbol1";
421   const char * value1 = "test_value1";
422   const char * vms_symbol2 = "test_symbol2";
423   const char * putenv_string = "test_symbol2=value2";
424   const char * value2 = "value2";
425
426   /* Test creating a foreign command */
427   vms_status = create_foreign_command (vms_command, vms_image);
428   if (!$VMS_STATUS_SUCCESS (vms_status))
429     {
430       printf("Create foreign command failed: %d\n", vms_status);
431       status = 1;
432     }
433
434   name_desc.dsc$a_pointer = (char *)vms_command;
435   name_desc.dsc$w_length = strlen (vms_command);
436   name_desc.dsc$b_dtype = DSC$K_DTYPE_T;
437   name_desc.dsc$b_class = DSC$K_CLASS_S;
438
439   value_desc.dsc$a_pointer = value;
440   value_desc.dsc$w_length = MAX_DCL_SYMBOL_VALUE;
441   value_desc.dsc$b_dtype = DSC$K_DTYPE_T;
442   value_desc.dsc$b_class = DSC$K_CLASS_S;
443
444   vms_status = LIB$GET_SYMBOL (&name_desc, &value_desc,
445                                &value_len, &old_symtbl);
446   if (!$VMS_STATUS_SUCCESS (vms_status))
447     {
448       printf ("lib$get_symbol for command failed: %d\n", vms_status);
449       status = 1;
450     }
451
452   value[value_len] = 0;
453   result = strncasecmp (&value[1], vms_image, value_len - 1);
454   if (result != 0)
455     {
456       printf ("create_foreign_command failed!  expected '%s', got '%s'\n",
457               vms_image, &value[1]);
458       status = 1;
459     }
460
461   /* Test exporting a symbol */
462   vms_status = vms_export_dcl_symbol (vms_symbol1, value1);
463   if (!$VMS_STATUS_SUCCESS (vms_status))
464     {
465       printf ("vms_export_dcl_symbol for command failed: %d\n", vms_status);
466       status = 1;
467     }
468
469   name_desc.dsc$a_pointer = (char *)vms_symbol1;
470   name_desc.dsc$w_length = strlen (vms_symbol1);
471   vms_status = LIB$GET_SYMBOL(&name_desc, &value_desc,
472                               &value_len, &old_symtbl);
473   if (!$VMS_STATUS_SUCCESS(vms_status))
474     {
475       printf ("lib$get_symbol for command failed: %d\n", vms_status);
476       status = 1;
477     }
478
479   value[value_len] = 0;
480   result = strncmp (value, value1, value_len);
481   if (result != 0)
482     {
483       printf ("vms_export_dcl_symbol failed!  expected '%s', got '%s'\n",
484               value1, value);
485       status = 1;
486     }
487
488   /* Test putenv for DCL symbols */
489   putenv_status = vms_putenv_symbol (putenv_string);
490   if (putenv_status != 0)
491     {
492       perror ("vms_putenv_symbol");
493       status = 1;
494     }
495
496   name_desc.dsc$a_pointer = (char *)vms_symbol2;
497   name_desc.dsc$w_length = strlen(vms_symbol2);
498   vms_status = LIB$GET_SYMBOL (&name_desc, &value_desc,
499                                &value_len, &old_symtbl);
500   if (!$VMS_STATUS_SUCCESS (vms_status))
501     {
502       printf ("lib$get_symbol for command failed: %d\n", vms_status);
503       status = 1;
504     }
505
506   value[value_len] = 0;
507   result = strncmp (value, value2, value_len);
508   if (result != 0)
509   {
510     printf ("vms_putenv_symbol failed!  expected '%s', got '%s'\n",
511             value2, value);
512     status = 1;
513   }
514
515   vms_restore_symbol (putenv_string);
516   vms_status = LIB$GET_SYMBOL (&name_desc, &value_desc,
517                                &value_len, &old_symtbl);
518   if ($VMS_STATUS_SUCCESS (vms_status))
519     {
520       printf ("lib$get_symbol for command succeeded, should have failed\n");
521       status = 1;
522     }
523
524   exit (status);
525 }
526
527 #endif