]> sjero.net Git - wget/blob - src/vms.c
Fix compiler warnings
[wget] / src / vms.c
1 /*
2  *    VMS supplement for "wget".
3  *
4  *======================================================================
5  *
6  *       vms_init()
7  *
8  *    On non-VAX systems, uses LIB$INITIALIZE to set a collection of C
9  *    RTL features without using the DECC$* logical name method.
10  *
11  *----------------------------------------------------------------------
12  *
13  *       vms_arch()
14  *
15  *    Returns (run-time) VMS architecture string.
16  *
17  *----------------------------------------------------------------------
18  *
19  *       vms_basename()
20  *
21  *    Returns the basename from a VMS file spec.
22  *
23  *----------------------------------------------------------------------
24  *
25  *       vms_getpass().
26  *
27  *    VMS-specific substitute for GNU getpass().
28  *
29  *----------------------------------------------------------------------
30  *
31  *       vms_ver()
32  *
33  *    Returns (run-time) VMS version string.
34  *
35  *----------------------------------------------------------------------
36  *
37  *       set_ods5_dest()
38  *
39  *    Sets a global flag ("ods5_dest") according to the file system type
40  *    of the destination device.
41  *
42  *----------------------------------------------------------------------
43  *
44  *       ods_conform()
45  *
46  *   Simplifies a fancy URL-derived file name into an ODS2- or
47  *   ODS5-compatible file name.
48  *    
49  *----------------------------------------------------------------------
50  *
51  *       utime()
52  *
53  *    VMS C RTL before V7.3 lacks utime().  In V7.3, utime() sets only
54  *    the modified (revised) date, not the created date of a file.
55  *
56  *    UNIX "ls -l" reports the modified time.  VMS "DIRECTORY /DATE"
57  *    reports the creation time.  Reconciling these in FTP DIR reports
58  *    is non-trivial.
59  *
60  *    UNIX utime() sets revision and access times.  VMS does not always
61  *    maintain access times, so this utime() replacement sets the
62  *    creation and revision times to the specified revision (or
63  *    creation?) time.  Any access time is ignored.
64  *
65  *----------------------------------------------------------------------
66  *
67  *       getpwuid()
68  *
69  *    VMS C RTL before V7.0 lacks getpwuid().
70  *
71  *----------------------------------------------------------------------
72  *
73  */
74
75 #include "vms.h"
76
77 #include <ctype.h>
78 #include <errno.h>
79 #include <limits.h>
80 #include <stdio.h>
81 #include <stdlib.h>
82 #include <string.h>
83 #include <time.h>
84 #include <unixlib.h>
85
86 #include <atrdef.h>
87 #include <descrip.h>
88 #include <dcdef.h>
89 #include <dvidef.h>
90 #include <fibdef.h>
91 #include <iodef.h>
92 #include <rms.h>
93 #include <stsdef.h>
94 #include <syidef.h>
95 #include <ttdef.h>
96 #include <lib$routines.h>
97 #include <starlet.h>
98
99 /* Use <iosbdef.h> if available.  Otherwise declare IOSB here. */
100
101 #if !defined( __VAX) && (__CRTL_VER >= 70000000)
102 #include <iosbdef.h>
103 #else /* __CRTL_VER >= 70000000 */
104 typedef struct _iosb {
105         unsigned short int iosb$w_status; /* Final I/O status   */
106         unsigned short int iosb$w_bcnt; /* 16-bit byte count    */
107         unsigned int iosb$l_dev_depend; /* 32-bit dev dependent */
108     } IOSB;
109 #endif /* !defined( __VAX) && (__CRTL_VER >= 70000000) */
110
111 /* Ugly work-around for bad type in VAX <atrdef.h>. */
112
113 #ifdef __VAX
114 #define UWA (unsigned int)
115 #else /* def __VAX */
116 #define UWA
117 #endif /* def __VAX */
118
119 #include "config.h"
120 #include "wget.h"
121 #include "utils.h"
122
123
124 /* Define macros for use with either NAM or NAML. */
125
126 #ifdef NAML$C_MAXRSS            /* NAML is available.  Use it. */
127
128 #  define NAM_STRUCT NAML
129
130 #  define FAB_OR_NAML( fab, nam) nam
131 #  define FAB_OR_NAML_FNA naml$l_long_filename
132 #  define FAB_OR_NAML_FNS naml$l_long_filename_size
133
134 #  define CC_RMS_NAM cc$rms_naml
135 #  define FAB_NAM fab$l_naml
136 #  define NAM_ESA naml$l_long_expand
137 #  define NAM_ESL naml$l_long_expand_size
138 #  define NAM_ESS naml$l_long_expand_alloc
139 #  define NAM_RSA naml$l_long_result
140 #  define NAM_RSL naml$l_long_result_size
141 #  define NAM_RSS naml$l_long_result_alloc
142 #  define NAM_MAXRSS NAML$C_MAXRSS
143 #  define NAM_NOP naml$b_nop
144 #  define NAM_M_SYNCHK NAML$M_SYNCHK
145 #  define NAM_B_NAME naml$l_long_name_size
146 #  define NAM_L_NAME naml$l_long_name
147
148 #else /* def NAML$C_MAXRSS */   /* NAML is not available.  Use NAM. */
149
150 #  define NAM_STRUCT NAM
151
152 #  define FAB_OR_NAML( fab, nam) fab
153 #  define FAB_OR_NAML_FNA fab$l_fna
154 #  define FAB_OR_NAML_FNS fab$b_fns
155
156 #  define CC_RMS_NAM cc$rms_nam
157 #  define FAB_NAM fab$l_nam
158 #  define NAM_ESA nam$l_esa
159 #  define NAM_ESL nam$b_esl
160 #  define NAM_ESS nam$b_ess
161 #  define NAM_RSA nam$l_rsa
162 #  define NAM_RSL nam$b_rsl
163 #  define NAM_RSS nam$b_rss
164 #  define NAM_MAXRSS NAM$C_MAXRSS
165 #  define NAM_NOP nam$b_nop
166 #  define NAM_M_SYNCHK NAM$M_SYNCHK
167 #  define NAM_B_NAME nam$b_name
168 #  define NAM_L_NAME nam$l_name
169
170 #endif /* def NAML$C_MAXRSS */
171
172 /*--------------------------------------------------------------------*/
173
174 /* Global storage. */
175
176 /*    Flag for an ODS5 destination directory. */
177
178 int ods5_dest = -1;
179
180 /*    Flag to sense if vms_init() was called. */
181
182 int vms_init_done = -1;
183
184 /*--------------------------------------------------------------------*/
185
186 #if !defined( __VAX) && (__CRTL_VER >= 70301000)
187
188 /* vms_init()
189
190       Uses LIB$INITIALIZE to set a collection of C RTL features without
191       requiring the user to define the corresponding logical names.
192 */
193
194 /* Structure to hold a DECC$* feature name and its desired value. */
195
196 typedef struct
197    {
198    char *name;
199    int value;
200    } decc_feat_t;
201
202 /* Array of DECC$* feature names and their desired values. */
203
204 decc_feat_t decc_feat_array[] = {
205    /* Preserve command-line case with SET PROCESS/PARSE_STYLE=EXTENDED */
206  { "DECC$ARGV_PARSE_STYLE", 1 },
207    /* Preserve case for file names on ODS5 disks. */
208  { "DECC$EFS_CASE_PRESERVE", 1 },
209    /* Enable multiple dots (and most characters) in ODS5 file names,
210       while preserving VMS-ness of ";version". */
211  { "DECC$EFS_CHARSET", 1 },
212    /* List terminator. */
213  { (char *)NULL, 0 } };
214
215 /* LIB$INITIALIZE initialization function. */
216
217 static void vms_init( void)
218 {
219 int feat_index;
220 int feat_value;
221 int feat_value_max;
222 int feat_value_min;
223 int i;
224 int sts;
225
226 /* Set the global flag to indicate that LIB$INITIALIZE worked. */
227
228 vms_init_done = 1;
229
230 /* Loop through all items in the decc_feat_array[]. */
231
232 for (i = 0; decc_feat_array[ i].name != NULL; i++)
233    {
234    /* Get the feature index. */
235    feat_index = decc$feature_get_index( decc_feat_array[ i].name);
236    if (feat_index >= 0)
237       {
238       /* Valid item.  Collect its properties. */
239       feat_value = decc$feature_get_value( feat_index, 1);
240       feat_value_min = decc$feature_get_value( feat_index, 2);
241       feat_value_max = decc$feature_get_value( feat_index, 3);
242
243       if ((decc_feat_array[ i].value >= feat_value_min) &&
244        (decc_feat_array[ i].value <= feat_value_max))
245          {
246          /* Valid value.  Set it if necessary. */
247          if (feat_value != decc_feat_array[ i].value)
248             {
249             sts = decc$feature_set_value( feat_index,
250              1,
251              decc_feat_array[ i].value);
252             }
253          }
254       else
255          {
256          /* Invalid DECC feature value. */
257          printf( " INVALID DECC FEATURE VALUE, %d: %d <= %s <= %d.\n",
258           feat_value,
259           feat_value_min, decc_feat_array[ i].name, feat_value_max);
260          }
261       }
262    else
263       {
264       /* Invalid DECC feature name. */
265       printf( " UNKNOWN DECC FEATURE: %s.\n", decc_feat_array[ i].name);
266       }
267    }
268 }
269
270 /* Get "vms_init()" into a valid, loaded LIB$INITIALIZE PSECT. */
271
272 #pragma nostandard
273
274 /* Establish the LIB$INITIALIZE PSECTs, with proper alignment and
275    other attributes.  Note that "nopic" is significant only on VAX.
276 */
277 #pragma extern_model save
278
279 #pragma extern_model strict_refdef "LIB$INITIALIZE" 2, nopic, nowrt
280 void (*const x_vms_init)() = vms_init;
281
282 #pragma extern_model strict_refdef "LIB$INITIALIZ" 2, nopic, nowrt
283 const int spare[ 8] = { 0 };
284
285 #pragma extern_model restore
286
287 /* Fake reference to ensure loading the LIB$INITIALIZE PSECT. */
288
289 #pragma extern_model save
290 int lib$initialize(void);
291 #pragma extern_model strict_refdef
292 int dmy_lib$initialize = (int) lib$initialize;
293 #pragma extern_model restore
294
295 #pragma standard
296
297 #endif /* !defined( __VAX) && (__CRTL_VER >= 70301000) */
298
299 /*--------------------------------------------------------------------*/
300
301 /* vms_arch()
302   
303       Returns (run-time) VMS architecture string.
304 */
305
306 char *vms_arch( void)
307 {
308 #define ARCH_SIZE 15
309
310 static int sts = 0;
311 static char arch[ ARCH_SIZE+ 1] = "VAX";        /* Only VAX could fail. */
312 unsigned short arch_len;
313
314 struct dsc$descriptor_s arch_descr =
315  { ARCH_SIZE, DSC$K_DTYPE_T, DSC$K_CLASS_S, arch };
316
317 if (sts == 0)
318    {
319    sts = lib$getsyi( &SYI$_ARCH_NAME, 0, &arch_descr, &arch_len, 0, 0);
320    if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)
321       {
322       arch[ arch_len] = '\0';
323
324       /* Trim trailing spaces. */
325       while ((arch_len > 0) && (arch[ arch_len- 1] == ' '))
326          {
327          arch[ --arch_len] = '\0';
328          }
329       }
330    }
331 return arch;
332 }
333
334 /*--------------------------------------------------------------------*/
335
336 /* vms_basename()
337  *
338  *    Extract the basename from a VMS file spec.
339  */
340
341 char *vms_basename( char *file_spec)
342 {
343     /* Static storage for NAM[L], and so on. */
344
345     static struct NAM_STRUCT nam;
346     static char exp_name[ NAM_MAXRSS+ 1];
347     static char res_name[ NAM_MAXRSS+ 1];
348
349     struct FAB fab;
350     int status;
351
352     /* Set up the FAB and NAM[L] blocks. */
353
354     fab = cc$rms_fab;                   /* Initialize FAB. */
355     nam = CC_RMS_NAM;                   /* Initialize NAM[L]. */
356
357     fab.FAB_NAM = &nam;                 /* FAB -> NAM[L] */
358
359 #ifdef NAML$C_MAXRSS
360
361     fab.fab$l_dna = (char *) -1;    /* Using NAML for default name. */
362     fab.fab$l_fna = (char *) -1;    /* Using NAML for file name. */
363
364 #endif /* def NAML$C_MAXRSS */
365
366     /* Arg name and length. */
367     FAB_OR_NAML( fab, nam).FAB_OR_NAML_FNA = file_spec;
368     FAB_OR_NAML( fab, nam).FAB_OR_NAML_FNS = strlen( file_spec);
369
370     nam.NAM_ESA = exp_name;         /* Expanded name. */
371     nam.NAM_ESS = NAM_MAXRSS;       /* Max length. */
372     nam.NAM_RSA = res_name;         /* Resulting name. */
373     nam.NAM_RSS = NAM_MAXRSS;       /* Max length. */
374
375     nam.NAM_NOP = NAM_M_SYNCHK;     /* Syntax-only analysis. */
376
377     /* Parse the file name. */
378     status = sys$parse( &fab);      /* What could go wrong? */
379
380     nam.NAM_L_NAME[ nam.NAM_B_NAME] = '\0';
381
382     return nam.NAM_L_NAME;
383 }
384
385 /*--------------------------------------------------------------------*/
386
387 /* 2009-09-07 SMS.
388  *
389  *       vms_getpass().
390  *
391  *    VMS-specific substitute for GNU getpass().
392  *
393  *       Returns passpord in locally allocated string.
394  */
395
396 /* Terminal characteristics buffer structure. */
397 typedef struct
398 {
399     char class;
400     char type;
401     short page_width;
402     int basic_chars;    /* (The high eight bits are page length.) */
403     int extended_chars;
404 } term_chars_t;
405
406 /* Enable/disable terminal echo. */
407
408 int vms_set_term_echo( int able)
409 {
410     int sts;
411     int sts2;
412     short term_chan;
413     $DESCRIPTOR( term_dscr, "SYS$COMMAND");
414     term_chars_t term_chars;
415     static int initial_echo = -1;
416
417     /* Open a channel to the terminal device. */
418     sts = sys$assign( &term_dscr,       /* Terminal device name. */
419                       &term_chan,       /* Channel. */
420                       0,                /* Access mode. */
421                       0);               /* Mailbox. */
422
423     /* Return immediately on failure. */
424     if ((sts& STS$M_SEVERITY) != STS$K_SUCCESS)
425     {
426         errno = EVMSERR;
427         vaxc$errno = sts;
428         return -1;
429     }
430
431     /* Get the current terminal characteristics (mode). */
432     sts = sys$qiow( 0,                          /* Event flag. */
433                     term_chan,                  /* Channel. */
434                     IO$_SENSEMODE,              /* Function. */
435                     0,                          /* IOSB. */
436                     0,                          /* AST address. */
437                     0,                          /* AST parameter. */
438                     &term_chars,                /* P1 = Buffer address. */
439                     sizeof term_chars,          /* P2 = Buffer size. */
440                     0, 0, 0, 0);                /* P3-P6 not used. */
441
442     if ((sts& STS$M_SEVERITY) != STS$K_SUCCESS)
443     {
444         errno = EVMSERR;
445         vaxc$errno = sts;
446     }
447     else if (term_chars.class != DC$_TERM)
448     {
449         errno = ENOTTY;
450     }
451     else
452     {
453         /* Save the initial echo state, to allow proper restoration. */
454         if (initial_echo < 0)
455         {
456             initial_echo = ((term_chars.basic_chars& TT$M_NOECHO) == 0);
457         }
458
459         if (able < 0)
460         {
461            if (initial_echo)
462            {
463                /* Was initially enabled. */
464                able = 1;
465            }
466            else
467            {
468                /* Was initially disabled. */
469                able = 0;
470            }
471         }
472
473         if (able == 0)
474         {
475             /* Disable.  Set the no-echo bit. */
476             term_chars.basic_chars |= TT$M_NOECHO;
477         }
478         else
479         {
480             /* Enable.  Clear the no-echo bit. */
481             term_chars.basic_chars &= ~TT$M_NOECHO;
482         }
483
484         /* Set the terminal characteristics (mode). */
485         sts = sys$qiow( 0,                      /* Event flag. */
486                         term_chan,              /* Channel. */
487                         IO$_SETMODE,            /* Function. */
488                         0,                      /* IOSB. */
489                         0,                      /* AST address. */
490                         0,                      /* AST parameter. */
491                         &term_chars,            /* P1 = Buffer address. */
492                         sizeof term_chars,      /* P2 = Buffer size. */
493                         0, 0, 0, 0);            /* P3-P6 not used. */
494
495         if ((sts& STS$M_SEVERITY) != STS$K_SUCCESS)
496         {
497             errno = EVMSERR;
498             vaxc$errno = sts;
499         }
500         else
501         {
502             /* All is well. */
503             sts = 0;
504         }
505     }
506
507     /* Close the channel to the terminal device. */
508     sts2 = sys$dassgn( term_chan);              /* Channel. */
509     if ((sts2& STS$M_SEVERITY) != STS$K_SUCCESS)
510     {
511         /* If all was not well, leave the old error codes as were. */
512         if (sts == 0)
513         {
514             /* All was well, but DASSGN failed. */
515             errno = EVMSERR;
516             vaxc$errno = sts2;
517             sts = sts2;
518         }
519     }
520     return sts;
521 }
522
523 static char pw_buf[ PASS_MAX+ 1];
524
525 char *vms_getpass( const char *prompt)
526 {
527     char *ret;
528     int sts;
529     FILE *sdc;
530
531     ret = NULL;
532
533     sdc = fopen( "SYS$COMMAND", "r");
534     if (sdc != NULL)
535     {
536         sts = vms_set_term_echo( 0);
537         if (sts == 0)
538         {
539             /* Read password string. */
540             if (sts == 0)
541             {
542                 fprintf( stdout, "%s", prompt);
543                 sts = fread( pw_buf, 1, PASS_MAX, sdc);
544                 if ((sts > 0) && (pw_buf[ sts- 1]) == '\n')
545                 {
546                     pw_buf[ --sts] = '\0';
547                 }
548                 else
549                 {
550                     pw_buf[ sts = '\0'];
551                 }
552                 ret = pw_buf;
553             }
554             sts = vms_set_term_echo( -1);
555         }
556         fclose( sdc);
557     }
558     return ret;
559 }    
560
561 /*--------------------------------------------------------------------*/
562
563 /* vms_vers()
564   
565       Returns (run-time) VMS version string.
566 */
567
568 char *vms_vers( void)
569 {
570 #define VERS_SIZE 8
571
572 static int sts = 0;
573 static char vers[ VERS_SIZE+ 1] = "";
574 unsigned short vers_len;
575
576 struct dsc$descriptor_s vers_descr =
577  { VERS_SIZE, DSC$K_DTYPE_T, DSC$K_CLASS_S, vers };
578
579 if (sts == 0)
580    {
581    sts = lib$getsyi( &SYI$_VERSION, 0, &vers_descr, &vers_len, 0, 0);
582    if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)
583    {
584       vers[ vers_len] = '\0';
585
586       /* Trim trailing spaces. */
587       while ((vers_len > 0) && (vers[ vers_len- 1] == ' '))
588          {
589          vers[ --vers_len] = '\0';
590          }
591       }
592    }
593 return vers;
594 }
595
596 /*--------------------------------------------------------------------*/
597
598 /* set_ods5_dest()
599
600       Sets global "ods5_dest" according to the file system type of the
601       argument: 0 for ODS2, 1 for ODS5.  (No change if other/unknown or
602       failure.)
603
604       Return value:  Status from sys$getdvi().
605 */
606
607 int set_ods5_dest( char *path)
608 {
609 #ifdef DVI$C_ACP_F11V5
610
611 /* Should know about ODS5 file system.  Do actual check.
612    (This should be non-VAX with __CRTL_VER >= 70200000.)
613 */
614
615 struct dsc$descriptor_s dev_descr =
616  { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0 };
617
618 int acp_code;
619 int sts;
620
621 /* Load path argument into device descriptor.
622    Default to current default device.
623 */
624 if (path == NULL)
625    {
626    dev_descr.dsc$a_pointer = "SYS$DISK";
627    }
628 else
629    {
630    dev_descr.dsc$a_pointer = path;
631    }
632 dev_descr.dsc$w_length = strlen( dev_descr.dsc$a_pointer);
633
634 /* Get filesystem type code.
635    (Text results for this item code have been unreliable.)
636 */
637 sts = lib$getdvi( &((int) DVI$_ACPTYPE),
638                   0,
639                   &dev_descr,
640                   &acp_code,
641                   0,
642                   0);
643
644 if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)
645    {
646    if (acp_code == DVI$C_ACP_F11V2)
647       {
648       ods5_dest = 0;
649       }
650    else if (acp_code == DVI$C_ACP_F11V5)
651       {
652       ods5_dest = 1;
653       }
654    }
655
656 return sts;
657
658 #else /* def DVI$C_ACP_F11V5 */
659
660 /* Too old for ODS5 file system.  Do nothing. */
661
662 return STS$K_SUCCESS;
663
664 #endif /* def DVI$C_ACP_F11V5 */
665 }
666
667 /*--------------------------------------------------------------------*/
668
669 /* ods2_conform()
670
671    Replace ODS2-troublesome characters in the argument, overwriting the
672    original string.  Replace "~" with "-", "@" with "$", and invalid
673    dots and other characters with "_".  (Invalid dots are any in a
674    directory name, and all but the last in a file name.)
675
676    Return value: path.  (Someday this function could be written to leave
677    the original string unchanged, and to return a freshly allocated
678    string, possibly of a dfferent length.)
679
680    2005-02-23 SMS.
681    Changed to use char_prop[] look-up table, and to convert more invalid
682    characters to "_".
683 */
684
685 static
686 char *ods2_conform( char *path)
687 {
688 char *p;
689 char *prd;
690 char *prs;
691 char *prv;
692 unsigned char uchr;
693 unsigned char prop;
694
695 /* Locate the last slash. */
696 prs = rindex( path, '/');
697 if (prs == NULL)
698    {
699    prs = path;
700    }
701
702 /* Locate the last dot after the last slash. */
703 prd = rindex( prs, '.');
704 if (prd == NULL)
705    {
706    prd = prs;
707    }
708
709 /* Locate the version (after the last slash and dot). */
710 for (prv = prs+ strlen( prs) ; (--prv > prs) && isdigit( *prv); );
711 if ((*prv != ';') || (*(prv- 1) == '^'))
712    {
713    prv = prs+ strlen( prs);
714    }
715
716 for (p = path ; p < prv; p++)
717    {
718    prop = char_prop[ uchr = *p];
719    if ((prop& 4))
720       {                                 /* Dot. */
721       if (p < prd)
722          {                              /* Before last dot in name. */
723          *p = '_';                      /* Convert to "_". */
724          }
725       }
726    else if ((prop& (32+ 16)) == 0)
727       {                                 /* ODS2-invalid. */
728       if (uchr == '~')
729          {
730          *p = '-';                      /* Convert to "-". */
731          }
732       else if (uchr == '@')
733          {
734          *p = '$';                      /* Convert to "$". */
735          }
736       else if (uchr != '/')             /* Leave "/" as-is. */
737          {
738          *p = '_';                      /* Convert to "_". */
739          }
740       }
741    }
742 return path;
743 }
744
745 /*--------------------------------------------------------------------*/
746
747 /* ods_conform()
748
749    Replace troublesome characters for the destination file system (ODS2
750    or ODS5) with more legal characters.
751
752    For ODS5, this is simply "?" -> "!" and "*" -> "#".
753
754    For ODS2, see ods2_conform().
755
756    Return value: path.  (Someday this function could be written to leave
757    the original string unchanged, and to return a freshly allocated
758    string, possibly of a dfferent length.)
759 */
760
761 char *ods_conform( char *path)
762 {
763 char *p;
764
765 /* Replacements for invalid (printing) ODS5 characters. */
766 #define ODS5_QUESTION '!'
767 #define ODS5_ASTERISK '#'
768
769 if (ods5_dest <= 0)
770    {
771    /* Return ODS2-conformant file name. */
772    return ods2_conform( path);
773    }
774 else
775    {
776    /* Return ODS5-conformant file name.  ("?" -> "!", "*" -> "#".) */
777    for (p = path; *p != '\0'; p++)
778       {
779       if (*p == '?')
780          {
781          *p = ODS5_QUESTION;
782          }
783       else if (*p == '*')
784          {
785          *p = ODS5_ASTERISK;
786          }
787       }
788    return path;
789    }
790 }
791
792 /*--------------------------------------------------------------------*/
793
794 /* Wget-private utime() code. */
795
796 /* Use long name (NAML) structure only where available.
797    (This should be non-VAX with __CRTL_VER >= 70200000.)
798 */
799
800 #ifdef NAML$C_BID
801
802 /* Use long name (NAML) structure. */
803
804 #define FAB$L_NAMX fab$l_naml
805 #define NAMX NAML
806 #define NAMX$C_MAXRSS NAML$C_MAXRSS
807 #define NAMX$B_DEV naml$l_long_dev_size
808 #define NAMX$L_DEV naml$l_long_dev
809 #define NAMX$L_ESA naml$l_long_expand
810 #define NAMX$B_ESL naml$l_long_expand_size
811 #define NAMX$B_ESS naml$l_long_expand_alloc
812 #define NAMX$W_FID naml$w_fid
813 #define NAMX$L_RSA naml$l_long_result
814 #define NAMX$B_RSL naml$l_long_result_size
815 #define NAMX$B_RSS naml$l_long_result_alloc
816 #define CC$RMS_NAMX cc$rms_naml
817
818 #else /* def NAML$C_BID */
819
820 /* Use short name (NAM) structure. */
821
822 #define FAB$L_NAMX fab$l_nam
823 #define NAMX NAM
824 #define NAMX$C_MAXRSS NAM$C_MAXRSS
825 #define NAMX$B_DEV nam$b_dev
826 #define NAMX$L_DEV nam$l_dev
827 #define NAMX$L_ESA nam$l_esa
828 #define NAMX$B_ESL nam$b_esl
829 #define NAMX$B_ESS nam$b_ess
830 #define NAMX$W_FID nam$w_fid
831 #define NAMX$L_RSA nam$l_rsa
832 #define NAMX$B_RSL nam$b_rsl
833 #define NAMX$B_RSS nam$b_rss
834 #define CC$RMS_NAMX cc$rms_nam
835
836 #endif /* def NAML$C_BID */
837
838 /*--------------------------------------------------------------------*/
839
840 /* Wget-private utime() code. */
841
842 /* Action routine for decc$to_vms(), in utime(). */
843
844 char vms_path[ NAMX$C_MAXRSS+ 1];
845
846 int set_vms_name( char *name, int type)
847 {
848    strncpy( vms_path, name, NAMX$C_MAXRSS);
849    vms_path[ NAMX$C_MAXRSS] = '\0';
850    return 1;
851 }
852
853 /*--------------------------------------------------------------------*/
854
855 /* utime() replacement. */
856
857 int utime( const char *path, const struct utimbuf *times)
858 {
859 time_t utc_unsigned;
860
861 int chan, i;
862 int sts, sts2;
863
864 unsigned short int vms_num_vec_time[ 7];
865 static unsigned int vms_abs_time[ 2];
866 struct tm *tms;
867 struct _iosb iosb_q;
868
869 /* QIOW item list used to set creation and revision dates. */
870
871 struct atrdef ut_atr[ 3] = {
872  {sizeof( vms_abs_time), ATR$C_CREDATE, UWA vms_abs_time},
873  {sizeof( vms_abs_time), ATR$C_REVDATE, UWA vms_abs_time},
874  {0,0,0}};
875
876 /* Various RMS structures used for file access. */
877
878 struct FAB ut_fab = cc$rms_fab;
879 struct RAB ut_rab = cc$rms_rab;
880 struct NAMX ut_namx = CC$RMS_NAMX;
881 static struct fibdef ut_fib;
882
883 /* Device and file name buffers and their descriptors. */
884
885 static char dev_namx[ NAMX$C_MAXRSS+ 1];
886 char esa_namx[ NAMX$C_MAXRSS+ 1];
887 char rsa_namx[ NAMX$C_MAXRSS+ 1];
888
889 struct dsc$descriptor dev_dsc =
890  {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, dev_namx};
891
892 struct dsc$descriptor fib_dsc =
893  {sizeof( ut_fib), DSC$K_DTYPE_T, DSC$K_CLASS_S, (char *) &ut_fib};
894
895 /* "wget" provides a UNIX-like path name.  With "-O", a user may provide
896    a VMS-like path name.  If a slash is found in the name, assume that
897    it's UNIX-like, and convert it to VMS form.  Otherwise, use it as-is.
898 */
899
900 if (strchr( path, '/') != NULL)
901    {
902    sts = decc$to_vms( path, set_vms_name, 0, 0);
903    path = vms_path;
904    }
905
906 /* Install the VMS file specification into the FAB. */
907
908 ut_fab.fab$l_fna = (char *) path;
909 ut_fab.fab$b_fns = (unsigned char) strlen( path);
910
911 ut_fab.fab$l_dna = "";
912 ut_fab.fab$b_dns = 0;
913
914 /* Point the FAB to the NAMX. */
915
916 ut_fab.FAB$L_NAMX = &ut_namx;
917
918 /* Install the name buffers into the NAM. */
919
920 ut_namx.NAMX$L_ESA = esa_namx;
921 ut_namx.NAMX$B_ESL = 0;
922 ut_namx.NAMX$B_ESS = sizeof( esa_namx)- 1;
923
924 ut_namx.NAMX$L_RSA = rsa_namx;
925 ut_namx.NAMX$B_RSL = 0;
926 ut_namx.NAMX$B_RSS = sizeof( rsa_namx)- 1;
927
928 /* Convert the modification time (UTC time_t) to local "tm" time. */
929
930 tms = localtime( &(times-> modtime));
931
932 /* Move (translate) "tm" structure local time to VMS vector time. */
933
934 if (tms != NULL)
935    {
936    vms_num_vec_time[ 0] = tms-> tm_year+ 1900;
937    vms_num_vec_time[ 1] = tms-> tm_mon+ 1;
938    vms_num_vec_time[ 2] = tms-> tm_mday;
939    vms_num_vec_time[ 3] = tms-> tm_hour;
940    vms_num_vec_time[ 4] = tms-> tm_min;
941    vms_num_vec_time[ 5] = tms-> tm_sec;
942    vms_num_vec_time[ 6] = 0;  /* centiseconds */
943
944 /* Convert VMS vector time to VMS absolute time (quadword). */
945
946    sts = lib$cvt_vectim( vms_num_vec_time, vms_abs_time);
947
948    if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)
949       {
950 /* Parse the file specification. */
951
952       sts = sys$parse( &ut_fab, 0, 0);
953
954       if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)
955          {
956 /* Locate the file. (Gets the FID.) */
957
958          sts = sys$search( &ut_fab, 0, 0);
959
960          if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)
961             {
962 /* Form the device name descriptor. */
963
964             dev_dsc.dsc$w_length = ut_namx.NAMX$B_DEV;
965             dev_dsc.dsc$a_pointer = (char *) ut_namx.NAMX$L_DEV;
966
967 /* Assign a channel to the disk device. */
968
969             sts = sys$assign( &dev_dsc, &chan, 0, 0);
970
971             if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)
972                {
973 /* Move the FID (and not the DID) into the FIB. */
974
975                memset( (void *) &ut_fib, 0, sizeof( ut_fib));
976
977                for (i = 0; i < 3; i++)
978                   {
979                   ut_fib.fib$w_fid[ i] = ut_namx.NAMX$W_FID[ i];
980                   ut_fib.fib$w_did[ i] = 0;
981                   }
982
983 /* Prevent this QIOW from setting the revision time to now. */
984
985                ut_fib.fib$l_acctl = FIB$M_NORECORD;
986
987 /* Set the file dates. */
988
989                sts = sys$qiow( 0,
990                                chan,
991                                IO$_MODIFY,
992                                &iosb_q,
993                                0,
994                                0,
995                                &fib_dsc,
996                                0,
997                                0,
998                                0,
999                                ut_atr,
1000                                0);
1001
1002                if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)
1003                   {
1004                    sts = iosb_q.iosb$w_status;
1005                   }
1006                sts2 = sys$dassgn( chan);
1007
1008                if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)
1009                   {
1010                   sts = sts2;
1011                   }
1012                }
1013             }
1014          }
1015       }
1016    }
1017
1018 /* Convert successful VMS status to zero = success status.
1019    If failure, set errno and vaxc$errno, and return -1 = failure status.
1020 */
1021
1022 if ((sts& STS$M_SEVERITY) == STS$K_SUCCESS)
1023    {
1024    sts = 0;
1025    }
1026 else
1027    {
1028    errno = EVMSERR;
1029    vaxc$errno = sts;
1030    sts = -1;
1031    }
1032
1033 return sts;
1034 }
1035
1036 /*--------------------------------------------------------------------*/
1037
1038 /* 2005-04-14 SMS.
1039  *
1040  *       vms_init_diag().
1041  *
1042  *    Get Wget debug option value.
1043  */
1044
1045 int vms_init_diag( void)
1046 {
1047 #ifdef ENABLE_DEBUG
1048 return (opt.debug > 0);
1049 #else /* def ENABLE_DEBUG */
1050 return 0;
1051 #endif /* def ENABLE_DEBUG */
1052 }
1053
1054 /*--------------------------------------------------------------------*/
1055
1056 /* 2004-11-23 SMS.
1057  *
1058  *       get_rms_defaults().
1059  *
1060  *    Get user-specified values from (DCL) SET RMS_DEFAULT.  FAB/RAB
1061  *    items of particular interest are:
1062  *
1063  *       fab$w_deq         default extension quantity (blocks) (write).
1064  *       rab$b_mbc         multi-block count.
1065  *       rab$b_mbf         multi-buffer count (used with rah and wbh).
1066  */
1067
1068 /* Default RMS parameter values. */
1069
1070 #define RMS_DEQ_DEFAULT 16384   /* About 1/4 the max (65535 blocks). */
1071 #define RMS_MBC_DEFAULT 127     /* The max, */
1072 #define RMS_MBF_DEFAULT 2       /* Enough to enable rah and wbh. */
1073
1074 /* GETJPI item descriptor structure. */
1075 typedef struct
1076     {
1077     short buf_len;
1078     short itm_cod;
1079     void *buf;
1080     int *ret_len;
1081     } jpi_item_t;
1082
1083 /* Durable storage */
1084
1085 static int rms_defaults_known = 0;
1086
1087 /* JPI item buffers. */
1088 static unsigned short rms_ext;
1089 static char rms_mbc;
1090 static unsigned char rms_mbf;
1091
1092 /* Active RMS item values. */
1093 unsigned short rms_ext_active;
1094 char rms_mbc_active;
1095 unsigned char rms_mbf_active;
1096
1097 /* GETJPI item lengths. */
1098 static int rms_ext_len;         /* Should come back 2. */
1099 static int rms_mbc_len;         /* Should come back 1. */
1100 static int rms_mbf_len;         /* Should come back 1. */
1101
1102 /* Desperation attempts to define unknown macros.  Probably doomed.
1103  * If these get used, expect sys$getjpiw() to return %x00000014 =
1104  * %SYSTEM-F-BADPARAM, bad parameter value.
1105  * They keep compilers with old header files quiet, though.
1106  */
1107 #ifndef JPI$_RMS_EXTEND_SIZE
1108 #  define JPI$_RMS_EXTEND_SIZE 542
1109 #endif /* ndef JPI$_RMS_EXTEND_SIZE */
1110
1111 #ifndef JPI$_RMS_DFMBC
1112 #  define JPI$_RMS_DFMBC 535
1113 #endif /* ndef JPI$_RMS_DFMBC */
1114
1115 #ifndef JPI$_RMS_DFMBFSDK
1116 #  define JPI$_RMS_DFMBFSDK 536
1117 #endif /* ndef JPI$_RMS_DFMBFSDK */
1118
1119 /* GETJPI item descriptor set. */
1120
1121 struct
1122     {
1123     jpi_item_t rms_ext_itm;
1124     jpi_item_t rms_mbc_itm;
1125     jpi_item_t rms_mbf_itm;
1126     int term;
1127     } jpi_itm_lst =
1128      { { 2, JPI$_RMS_EXTEND_SIZE, &rms_ext, &rms_ext_len },
1129        { 1, JPI$_RMS_DFMBC, &rms_mbc, &rms_mbc_len },
1130        { 1, JPI$_RMS_DFMBFSDK, &rms_mbf, &rms_mbf_len },
1131        0
1132      };
1133
1134 int get_rms_defaults()
1135 {
1136 int sts;
1137
1138 /* Get process RMS_DEFAULT values. */
1139
1140 sts = sys$getjpiw( 0, 0, 0, &jpi_itm_lst, 0, 0, 0);
1141 if ((sts& STS$M_SEVERITY) != STS$M_SUCCESS)
1142     {
1143     /* Failed.  Don't try again. */
1144     rms_defaults_known = -1;
1145     }
1146 else
1147     {
1148     /* Fine, but don't come back. */
1149     rms_defaults_known = 1;
1150     }
1151
1152 /* Limit the active values according to the RMS_DEFAULT values. */
1153
1154 if (rms_defaults_known > 0)
1155     {
1156     /* Set the default values. */
1157
1158     rms_ext_active = RMS_DEQ_DEFAULT;
1159     rms_mbc_active = RMS_MBC_DEFAULT;
1160     rms_mbf_active = RMS_MBF_DEFAULT;
1161
1162     /* Default extend quantity.  Use the user value, if set. */
1163     if (rms_ext > 0)
1164         {
1165         rms_ext_active = rms_ext;
1166         }
1167
1168     /* Default multi-block count.  Use the user value, if set. */
1169     if (rms_mbc > 0)
1170         {
1171         rms_mbc_active = rms_mbc;
1172         }
1173
1174     /* Default multi-buffer count.  Use the user value, if set. */
1175     if (rms_mbf > 0)
1176         {
1177         rms_mbf_active = rms_mbf;
1178         }
1179     }
1180
1181 if (vms_init_diag() > 0)
1182     {
1183     fprintf( stderr,
1184      "Get RMS defaults.  getjpi sts = %%x%08x.\n",
1185      sts);
1186
1187     if (rms_defaults_known > 0)
1188         {
1189         fprintf( stderr,
1190          "               Default: deq = %6d, mbc = %3d, mbf = %3d.\n",
1191          rms_ext, rms_mbc, rms_mbf);
1192         }
1193     }
1194 return sts;
1195 }
1196
1197 /*--------------------------------------------------------------------*/
1198
1199 /* 2004-11-23 SMS.
1200  *
1201  *       acc_cb(), access callback function for DEC C [f]open().
1202  *
1203  *    Set some RMS FAB/RAB items, with consideration of user-specified
1204  * values from (DCL) SET RMS_DEFAULT.  Items of particular interest are:
1205  *
1206  *       fab$w_deq         default extension quantity (blocks).
1207  *       rab$b_mbc         multi-block count.
1208  *       rab$b_mbf         multi-buffer count (used with rah and wbh).
1209  *
1210  *    See also the FOP* macros in OSDEP.H.  Currently, no notice is
1211  * taken of the caller-ID value, but options could be set differently
1212  * for read versus write access.  (I assume that specifying fab$w_deq,
1213  * for example, for a read-only file has no ill effects.)
1214  */
1215
1216 /* acc_cb() */
1217
1218 int acc_cb( int *id_arg, struct FAB *fab, struct RAB *rab)
1219 {
1220 int sts;
1221
1222 /* Get process RMS_DEFAULT values, if not already done. */
1223 if (rms_defaults_known == 0)
1224     {
1225     get_rms_defaults();
1226     }
1227
1228 /* If RMS_DEFAULT (and adjusted active) values are available, then set
1229  * the FAB/RAB parameters.  If RMS_DEFAULT values are not available,
1230  * suffer with the default parameters.
1231  */
1232 if (rms_defaults_known > 0)
1233     {
1234     /* Set the FAB/RAB parameters accordingly. */
1235     fab-> fab$w_deq = rms_ext_active;
1236     rab-> rab$b_mbc = rms_mbc_active;
1237     rab-> rab$b_mbf = rms_mbf_active;
1238
1239     /* Truncate at EOF on close, as we'll probably over-extend. */
1240     fab-> fab$v_tef = 1;
1241
1242     /* If using multiple buffers, enable read-ahead and write-behind. */
1243     if (rms_mbf_active > 1)
1244         {
1245         rab-> rab$v_rah = 1;
1246         rab-> rab$v_wbh = 1;
1247         }
1248
1249     if (vms_init_diag() > 0)
1250         {
1251         fprintf( stderr,
1252          "Open callback.  ID = %d, deq = %6d, mbc = %3d, mbf = %3d.\n",
1253          *id_arg, fab-> fab$w_deq, rab-> rab$b_mbc, rab-> rab$b_mbf);
1254         }
1255     }
1256
1257 /* Declare success. */
1258 return 0;
1259 }
1260
1261 /*--------------------------------------------------------------------*/
1262
1263 /* Added J.Lauret 05-Dec-1999 . Copied from Mosaic distribution */
1264
1265 /*
1266  * Here is a replacement for getpwuid for VMS.  It returns pointers
1267  * to userid (*pw_name) and owner (*pw_gecos) only.  Other fields
1268  * may be added later.
1269  * Note that sys$getuai returns owner as a counted string.
1270  */
1271
1272 #if __CRTL_VER < 70000000
1273
1274 #include <uaidef.h>
1275
1276 static struct passwd vms_passwd;
1277 static char vms_userid[16];
1278 static char vms_owner[40];
1279
1280 struct passwd *getpwuid()
1281 {
1282 struct  dsc$descriptor_s
1283 {
1284   unsigned short  dsc$w_length;
1285   unsigned char   dsc$b_dtype;
1286   unsigned char   dsc$b_class;
1287   char            *dsc$a_pointer;
1288 } user_desc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL};
1289
1290   char *t_userid, owner[40];
1291   int status, length;
1292   struct {
1293     short buffer_length;
1294     short item_code;
1295     int buffer_address;
1296     int return_length_address;
1297     int terminator;
1298   } itmlst;
1299
1300 #ifdef __GNUC__
1301   (int)t_userid = cuserid((char *) NULL);
1302 #else
1303   t_userid = cuserid((char *) NULL);
1304 #endif /* GNU C is strange, GEC */
1305   user_desc.dsc$w_length       = strlen(t_userid);
1306   user_desc.dsc$a_pointer      = t_userid;
1307   itmlst.buffer_length         = sizeof(owner);
1308   itmlst.item_code             = UAI$_OWNER;
1309   itmlst.buffer_address        = (int)owner;
1310   itmlst.return_length_address = (int)&length;
1311   itmlst.terminator            = 0;
1312   status = sys$getuai(0, 0, &user_desc, &itmlst, 0, 0, 0);
1313   if ((stats& STS$M_SEVERITY) == STS$K_SUCCESS) {
1314     length = (int)owner[0];
1315     owner[length+1] = '\0';
1316     strcpy(vms_userid, t_userid);
1317     strcpy(vms_owner, &owner[1]);
1318   } else {
1319     vms_userid[0] = '\0';
1320     vms_owner[0] = '\0';
1321   }
1322   vms_passwd.pw_name = vms_userid;
1323   vms_passwd.pw_gecos = vms_owner;
1324   return (&vms_passwd);
1325 }
1326
1327 /* Approximate localtime_r as best we can in its absence.  */
1328 struct tm *
1329 localtime_r (t, tp)
1330      const time_t *t;
1331      struct tm *tp;
1332 {
1333   struct tm *l = localtime (t);
1334   if (! l)
1335     return 0;
1336   *tp = *l;
1337   return tp;
1338 }
1339
1340 #endif  /* __CRTL_VER < 70000000 */
1341
1342 /*--------------------------------------------------------------------*/
1343
1344 /*
1345  * "version.c" information.
1346  */
1347
1348 const char *compilation_string = NULL;
1349 const char *link_string = NULL;
1350 const char *version_string = VERSION;
1351
1352 /*--------------------------------------------------------------------*/
1353