vms.c
上传用户:andy_li
上传日期:2007-01-06
资源大小:1019k
文件大小:96k
源码类别:

压缩解压

开发平台:

MultiPlatform

  1. /*---------------------------------------------------------------------------
  2.   vms.c                                        Igor Mandrichenko and others
  3.   This file contains routines to extract VMS file attributes from a zipfile
  4.   extra field and create a file with these attributes.  The code was almost
  5.   entirely written by Igor, with a couple of routines by GRR and lots of
  6.   modifications and fixes by Christian Spieler.
  7.   Contains:  check_format()
  8.              open_outfile()
  9.              find_vms_attrs()
  10.              flush()
  11.              close_outfile()
  12.              dos_to_unix_time()         (TIMESTAMP only)
  13.              stamp_file()               (TIMESTAMP only)
  14.              do_wild()
  15.              mapattr()
  16.              mapname()
  17.              checkdir()
  18.              check_for_newer()
  19.              return_VMS
  20.              screenlines()
  21.              version()
  22.   ---------------------------------------------------------------------------
  23.      Portions copyright (C) 1992-93 Igor Mandrichenko.
  24.      Permission is granted to any individual or institution to use, copy,
  25.      or redistribute this software so long as all of the original files
  26.      are included unmodified and that this copyright notice is retained.
  27.   ---------------------------------------------------------------------------*/
  28. #ifdef VMS                      /* VMS only! */
  29. #define UNZIP_INTERNAL
  30. #include "unzip.h"
  31. #include "vms.h"
  32. #include "vmsdefs.h"
  33. #include <lib$routines.h>
  34. #include <unixlib.h>
  35. #define ASYNCH_QIO              /* Try out asynchronous PK-style QIO writes */
  36. #define BUFS512 (OUTBUFSIZ&(~512))      /* Must be a multiple of 512 ! */
  37. #define BUFDBLS512 (BUFS512 * 2)        /* locbuf size, max. record size */
  38. #define OK(s)   ((s)&1)         /* VMS success or warning status */
  39. #define STRICMP(s1,s2)  STRNICMP(s1,s2,2147483647)
  40. /*
  41.  *   Local static storage
  42.  */
  43. static struct FAB       fileblk;
  44. static struct XABDAT    dattim;
  45. static struct XABRDT    rdt;
  46. static struct RAB       rab;
  47. static struct NAM       nam;
  48. static struct FAB *outfab = NULL;
  49. static struct RAB *outrab = NULL;
  50. static struct XABFHC *xabfhc = NULL;
  51. static struct XABDAT *xabdat = NULL;
  52. static struct XABRDT *xabrdt = NULL;
  53. static struct XABPRO *xabpro = NULL;
  54. static struct XABKEY *xabkey = NULL;
  55. static struct XABALL *xaball = NULL;
  56. static struct XAB *first_xab = NULL, *last_xab = NULL;
  57. static char query = 0;
  58. static int  text_output = 0,
  59.             bin_fixed = 0;
  60. #ifdef USE_ORIG_DOS
  61. static int  hostnum;
  62. #endif
  63. static uch rfm;
  64. static uch locbuf[BUFDBLS512];          /* Space for 2 buffers of BUFS512 */
  65. static unsigned loccnt = 0;
  66. static uch *locptr;
  67. static char got_eol = 0;
  68. struct bufdsc
  69. {
  70.     struct bufdsc *next;
  71.     uch *buf;
  72.     unsigned bufcnt;
  73. };
  74. static struct bufdsc b1, b2, *curbuf;   /* buffer ring for asynchronous I/O */
  75. static int  _flush_blocks(__GPRO__ uch *rawbuf, unsigned size, int final_flag),
  76.             _flush_stream(__GPRO__ uch *rawbuf, unsigned size, int final_flag),
  77.             _flush_varlen(__GPRO__ uch *rawbuf, unsigned size, int final_flag),
  78.             _flush_qio(__GPRO__ uch *rawbuf, unsigned size, int final_flag),
  79.             _close_rms(__GPRO),
  80.             _close_qio(__GPRO),
  81. #ifdef ASYNCH_QIO
  82.             WriteQIO(__GPRO__ uch *buf, unsigned len),
  83. #endif
  84.             WriteBuffer(__GPRO__ uch *buf, unsigned len),
  85.             WriteRecord(__GPRO__ uch *rec, unsigned len);
  86. static int  (*_flush_routine)(__GPRO__ uch *rawbuf, unsigned size,
  87.                               int final_flag),
  88.             (*_close_routine)(__GPRO);
  89. static void init_buf_ring(void);
  90. static void set_default_datetime_XABs(__GPRO);
  91. static int  create_default_output(__GPRO),
  92.             create_rms_output(__GPRO),
  93.             create_qio_output(__GPRO);
  94. static int  replace(__GPRO);
  95. static int  find_vms_attrs(__GPRO);
  96. static void free_up(void);
  97. #ifdef CHECK_VERSIONS
  98. static int  get_vms_version(char *verbuf, int len);
  99. #endif /* CHECK_VERSIONS */
  100. static uch  *extract_block(__GPRO__ struct IZ_block *p, int *retlen,
  101.                            uch *init, int needlen);
  102. static void decompress_bits(uch *outptr, int needlen, uch *bitptr);
  103. static unsigned find_eol(uch *p, unsigned n, unsigned *l);
  104. #ifdef TIMESTAMP
  105. static time_t mkgmtime(struct tm *tm);
  106. static void uxtime2vmstime(time_t utimeval, long int binval[2]);
  107. #endif /* TIMESTAMP */
  108. static void vms_msg(__GPRO__ char *string, int status);
  109. int check_format(__G)
  110.     __GDEF
  111. {
  112.     int rtype;
  113.     struct FAB fab;
  114.     fab = cc$rms_fab;
  115.     fab.fab$l_fna = G.zipfn;
  116.     fab.fab$b_fns = strlen(G.zipfn);
  117.     if ((sys$open(&fab) & 1) == 0)
  118.     {
  119.         Info(slide, 1, ((char *)slide, "n
  120.      error:  cannot open zipfile [ %s ] (access denied?).nn",
  121.           G.zipfn));
  122.         return PK_ERR;
  123.     }
  124.     rtype = fab.fab$b_rfm;
  125.     sys$close(&fab);
  126.     if (rtype == FAB$C_VAR || rtype == FAB$C_VFC)
  127.     {
  128.         Info(slide, 1, ((char *)slide, "n
  129.      Error:  zipfile is in variable-length record format.  Pleasen
  130.      run "bilf l %s" to convert the zipfile to stream-LFn
  131.      record format.  (BILF is available at various VMS archives.)nn",
  132.           G.zipfn));
  133.         return PK_ERR;
  134.     }
  135.     return PK_COOL;
  136. }
  137. #define PRINTABLE_FORMAT(x)      ( (x) == FAB$C_VAR     
  138.                                 || (x) == FAB$C_STMLF   
  139.                                 || (x) == FAB$C_STMCR   
  140.                                 || (x) == FAB$C_STM     )
  141. /* VMS extra field types */
  142. #define VAT_NONE    0
  143. #define VAT_IZ      1   /* old Info-ZIP format */
  144. #define VAT_PK      2   /* PKWARE format */
  145. static int  vet;
  146. /*
  147.  *  open_outfile() assignments:
  148.  *
  149.  *  VMS attributes ?        create_xxx      _flush_xxx
  150.  *  ----------------        ----------      ----------
  151.  *  not found               'default'       text mode ?
  152.  *                                          yes -> 'stream'
  153.  *                                          no  -> 'block'
  154.  *
  155.  *  yes, in IZ format       'rms'           uO.cflag ?
  156.  *                                          yes -> switch(fab.rfm)
  157.  *                                              VAR  -> 'varlen'
  158.  *                                              STM* -> 'stream'
  159.  *                                              default -> 'block'
  160.  *                                          no -> 'block'
  161.  *
  162.  *  yes, in PK format       'qio'           uO.cflag ?
  163.  *                                          yes -> switch(pka_rattr)
  164.  *                                              VAR  -> 'varlen'
  165.  *                                              STM* -> 'stream'
  166.  *                                              default -> 'block'
  167.  *                                          no -> 'qio'
  168.  *
  169.  *  "text mode" == G.pInfo->textmode || uO.cflag
  170.  */
  171. int open_outfile(__G)           /* return 1 (PK_WARN) if fail */
  172.     __GDEF
  173. {
  174.     switch(vet = find_vms_attrs(__G))
  175.     {
  176.         case VAT_NONE:
  177.         default:
  178.             return  create_default_output(__G);
  179.         case VAT_IZ:
  180.             return  create_rms_output(__G);
  181.         case VAT_PK:
  182.             return  create_qio_output(__G);
  183.     }
  184. }
  185. static void init_buf_ring()
  186. {
  187.     locptr = &locbuf[0];
  188.     loccnt = 0;
  189.     b1.buf = &locbuf[0];
  190.     b1.bufcnt = 0;
  191.     b1.next = &b2;
  192.     b2.buf = &locbuf[BUFS512];
  193.     b2.bufcnt = 0;
  194.     b2.next = &b1;
  195.     curbuf = &b1;
  196. }
  197. /* Static data storage for time conversion: */
  198. /*   string constants for month names */
  199. static ZCONST char *month[] =
  200.             {"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
  201.              "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
  202. /*   buffer for time string */
  203. static char timbuf[24];         /* length = first entry in "date_str" + 1 */
  204. /*   fixed-length string descriptor for timbuf: */
  205. static ZCONST struct dsc$descriptor date_str =
  206.             {sizeof(timbuf)-1, DSC$K_DTYPE_T, DSC$K_CLASS_S, timbuf};
  207. static void set_default_datetime_XABs(__GPRO)
  208. {
  209.     unsigned yr, mo, dy, hh, mm, ss;
  210. #ifdef USE_EF_UT_TIME
  211.     iztimes z_utime;
  212.     struct tm *t;
  213.     if (G.extra_field &&
  214. #ifdef IZ_CHECK_TZ
  215.         G.tz_is_valid &&
  216. #endif
  217.         (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
  218.                           G.lrec.last_mod_dos_datetime, &z_utime, NULL)
  219.          & EB_UT_FL_MTIME))
  220.         t = localtime(&(z_utime.mtime));
  221.     else
  222.         t = (struct tm *)NULL;
  223.     if (t != (struct tm *)NULL)
  224.     {
  225.         yr = t->tm_year + 1900;
  226.         mo = t->tm_mon;
  227.         dy = t->tm_mday;
  228.         hh = t->tm_hour;
  229.         mm = t->tm_min;
  230.         ss = t->tm_sec;
  231.     }
  232.     else
  233.     {
  234.         yr = ((G.lrec.last_mod_dos_datetime >> 25) & 0x7f) + 1980;
  235.         mo = ((G.lrec.last_mod_dos_datetime >> 21) & 0x0f) - 1;
  236.         dy = (G.lrec.last_mod_dos_datetime >> 16) & 0x1f;
  237.         hh = (G.lrec.last_mod_dos_datetime >> 11) & 0x1f;
  238.         mm = (G.lrec.last_mod_dos_datetime >> 5) & 0x3f;
  239.         ss = (G.lrec.last_mod_dos_datetime << 1) & 0x3e;
  240.     }
  241. #else /* !USE_EF_UT_TIME */
  242.     yr = ((G.lrec.last_mod_dos_datetime >> 25) & 0x7f) + 1980;
  243.     mo = ((G.lrec.last_mod_dos_datetime >> 21) & 0x0f) - 1;
  244.     dy = (G.lrec.last_mod_dos_datetime >> 16) & 0x1f;
  245.     hh = (G.lrec.last_mod_dos_datetime >> 11) & 0x1f;
  246.     mm = (G.lrec.last_mod_dos_datetime >> 5) & 0x3f;
  247.     ss = (G.lrec.last_mod_dos_datetime << 1) & 0x1f;
  248. #endif /* ?USE_EF_UT_TIME */
  249.     dattim = cc$rms_xabdat;     /* fill XABs with default values */
  250.     rdt = cc$rms_xabrdt;
  251.     sprintf(timbuf, "%02u-%3s-%04u %02u:%02u:%02u.00", dy, month[mo],
  252.             yr, hh, mm, ss);
  253.     sys$bintim(&date_str, &dattim.xab$q_cdt);
  254.     memcpy(&rdt.xab$q_rdt, &dattim.xab$q_cdt, sizeof(rdt.xab$q_rdt));
  255. }
  256. static int create_default_output(__GPRO)        /* return 1 (PK_WARN) if fail */
  257. {
  258.     int ierr;
  259.     text_output = G.pInfo->textmode ||
  260.                   uO.cflag;     /* extract the file in text
  261.                                  * (variable-length) format */
  262.     bin_fixed = text_output || (uO.bflag == 0)
  263.                 ? 0
  264.                 : ((uO.bflag - 1) ? 1 : !G.pInfo->textfile);
  265. #ifdef USE_ORIG_DOS
  266.     hostnum = G.pInfo->hostnum;
  267. #endif
  268.     rfm = FAB$C_STMLF;  /* Default, stream-LF format from VMS or UNIX */
  269.     if (!uO.cflag)              /* Redirect output */
  270.     {
  271.         rab = cc$rms_rab;       /* fill RAB with default values */
  272.         fileblk = cc$rms_fab;   /* fill FAB with default values */
  273.         outfab = &fileblk;
  274.         outfab->fab$l_xab = NULL;
  275.         if (text_output)
  276.         {   /* Default format for output `real' text file */
  277.             outfab->fab$b_rfm = FAB$C_VAR;      /* variable length records */
  278.             outfab->fab$b_rat = FAB$M_CR;       /* implied (CR) carriage ctrl */
  279.         }
  280.         else if (bin_fixed)
  281.         {   /* Default format for output `real' binary file */
  282.             outfab->fab$b_rfm = FAB$C_FIX;      /* fixed length record format */
  283.             outfab->fab$w_mrs = 512;            /* record size 512 bytes */
  284.             outfab->fab$b_rat = 0;              /* no carriage ctrl */
  285.         }
  286.         else
  287.         {   /* Default format for output misc (bin or text) file */
  288.             outfab->fab$b_rfm = FAB$C_STMLF;    /* stream-LF record format */
  289.             outfab->fab$b_rat = FAB$M_CR;       /* implied (CR) carriage ctrl */
  290.         }
  291.         outfab->fab$l_fna = G.filename;
  292.         outfab->fab$b_fns = strlen(outfab->fab$l_fna);
  293.         {
  294.             set_default_datetime_XABs(__G);
  295.             dattim.xab$l_nxt = outfab->fab$l_xab;
  296.             outfab->fab$l_xab = (void *) &dattim;
  297.         }
  298.         outfab->fab$w_ifi = 0;  /* Clear IFI. It may be nonzero after ZIP */
  299.         outfab->fab$b_fac = FAB$M_BRO | FAB$M_PUT;  /* {block|record} output */
  300.         ierr = sys$create(outfab);
  301.         if (ierr == RMS$_FEX)
  302.             ierr = replace(__G);
  303.         if (ierr == 0)          /* Canceled */
  304.             return (free_up(), PK_WARN);
  305.         if (ERR(ierr))
  306.         {
  307.             char buf[256];
  308.             sprintf(buf, "[ Cannot create output file %s ]n", G.filename);
  309.             vms_msg(__G__ buf, ierr);
  310.             vms_msg(__G__ "", outfab->fab$l_stv);
  311.             free_up();
  312.             return PK_WARN;
  313.         }
  314.         outrab = &rab;
  315.         rab.rab$l_fab = outfab;
  316.         if (!text_output)
  317.         {
  318.             rab.rab$l_rop |= (RAB$M_BIO | RAB$M_ASY);
  319.         }
  320.         rab.rab$b_rac = RAB$C_SEQ;
  321.         if ((ierr = sys$connect(outrab)) != RMS$_NORMAL)
  322.         {
  323. #ifdef DEBUG
  324.             vms_msg(__G__ "create_default_output: sys$connect failed.n", ierr);
  325.             vms_msg(__G__ "", outfab->fab$l_stv);
  326. #endif
  327.             Info(slide, 1, ((char *)slide,
  328.                  "Can't create output file:  %sn", G.filename));
  329.             free_up();
  330.             return PK_WARN;
  331.         }
  332.     }                   /* end if (!uO.cflag) */
  333.     init_buf_ring();
  334.     _flush_routine = text_output ? got_eol=0,_flush_stream : _flush_blocks;
  335.     _close_routine = _close_rms;
  336.     return PK_COOL;
  337. }
  338. static int create_rms_output(__GPRO)           /* return 1 (PK_WARN) if fail */
  339. {
  340.     int ierr;
  341.     text_output = uO.cflag;     /* extract the file in text
  342.                                  * (variable-length) format;
  343.                                  * we ignore "-a" when attributes saved */
  344. #ifdef USE_ORIG_DOS
  345.     hostnum = G.pInfo->hostnum;
  346. #endif
  347.     rfm = outfab->fab$b_rfm;    /* Use record format from VMS extra field */
  348.     if (uO.cflag)
  349.     {
  350.         if (!PRINTABLE_FORMAT(rfm))
  351.         {
  352.             Info(slide, 1, ((char *)slide,
  353.                "[ File %s has illegal record format to put to screen ]n",
  354.                G.filename));
  355.             free_up();
  356.             return PK_WARN;
  357.         }
  358.     }
  359.     else                        /* Redirect output */
  360.     {
  361.         rab = cc$rms_rab;       /* fill RAB with default values */
  362.         /* The output FAB has already been initialized with the values
  363.          * found in the Zip file's "VMS attributes" extra field */
  364.         outfab->fab$l_fna = G.filename;
  365.         outfab->fab$b_fns = strlen(outfab->fab$l_fna);
  366.         if (!(xabdat && xabrdt))        /* Use date/time info
  367.                                          *  from zipfile if
  368.                                          *  no attributes given
  369.                                          */
  370.         {
  371.             set_default_datetime_XABs(__G);
  372.             if (xabdat == NULL)
  373.             {
  374.                 dattim.xab$l_nxt = outfab->fab$l_xab;
  375.                 outfab->fab$l_xab = (void *) &dattim;
  376.             }
  377.         }
  378.         outfab->fab$w_ifi = 0;  /* Clear IFI. It may be nonzero after ZIP */
  379.         outfab->fab$b_fac = FAB$M_BIO | FAB$M_PUT;      /* block-mode output */
  380.         ierr = sys$create(outfab);
  381.         if (ierr == RMS$_FEX)
  382.             ierr = replace(__G);
  383.         if (ierr == 0)          /* Canceled */
  384.             return (free_up(), PK_WARN);
  385.         if (ERR(ierr))
  386.         {
  387.             char buf[256];
  388.             sprintf(buf, "[ Cannot create output file %s ]n", G.filename);
  389.             vms_msg(__G__ buf, ierr);
  390.             vms_msg(__G__ "", outfab->fab$l_stv);
  391.             free_up();
  392.             return PK_WARN;
  393.         }
  394.         if (outfab->fab$b_org & (FAB$C_REL | FAB$C_IDX)) {
  395.             /* relative and indexed files require explicit allocation */
  396.             ierr = sys$extend(outfab);
  397.             if (ERR(ierr))
  398.             {
  399.                 char buf[256];
  400.                 sprintf(buf, "[ Cannot allocate space for %s ]n", G.filename);
  401.                 vms_msg(__G__ buf, ierr);
  402.                 vms_msg(__G__ "", outfab->fab$l_stv);
  403.                 free_up();
  404.                 return PK_WARN;
  405.             }
  406.         }
  407.         outrab = &rab;
  408.         rab.rab$l_fab = outfab;
  409.         {
  410.             rab.rab$l_rop |= (RAB$M_BIO | RAB$M_ASY);
  411.         }
  412.         rab.rab$b_rac = RAB$C_SEQ;
  413.         if ((ierr = sys$connect(outrab)) != RMS$_NORMAL)
  414.         {
  415. #ifdef DEBUG
  416.             vms_msg(__G__ "create_rms_output: sys$connect failed.n", ierr);
  417.             vms_msg(__G__ "", outfab->fab$l_stv);
  418. #endif
  419.             Info(slide, 1, ((char *)slide,
  420.                  "Can't create output file:  %sn", G.filename));
  421.             free_up();
  422.             return PK_WARN;
  423.         }
  424.     }                   /* end if (!uO.cflag) */
  425.     init_buf_ring();
  426.     if ( text_output )
  427.         switch(rfm)
  428.         {
  429.             case FAB$C_VAR:
  430.                 _flush_routine = _flush_varlen;
  431.                 break;
  432.             case FAB$C_STM:
  433.             case FAB$C_STMCR:
  434.             case FAB$C_STMLF:
  435.                 _flush_routine = _flush_stream;
  436.                 got_eol = 0;
  437.                 break;
  438.             default:
  439.                 _flush_routine = _flush_blocks;
  440.                 break;
  441.         }
  442.     else
  443.         _flush_routine = _flush_blocks;
  444.     _close_routine = _close_rms;
  445.     return PK_COOL;
  446. }
  447. static  int pka_devchn;
  448. static  int pka_io_pending;
  449. static  unsigned pka_vbn;
  450. #if defined(__DECC) || defined(__DECCXX)
  451. #pragma __member_alignment __save
  452. #pragma __nomember_alignment
  453. #endif /* __DECC || __DECCXX */
  454. static struct
  455. {
  456.     short   status;
  457.     long    count;
  458.     short   dummy;
  459. } pka_io_sb;
  460. #if defined(__DECC) || defined(__DECCXX)
  461. #pragma __member_alignment __restore
  462. #endif /* __DECC || __DECCXX */
  463. static struct
  464. {
  465.     short   status;
  466.     short   dummy;
  467.     void    *addr;
  468. } pka_acp_sb;
  469. static struct fibdef    pka_fib;
  470. static struct atrdef    pka_atr[VMS_MAX_ATRCNT];
  471. static int              pka_idx;
  472. static ulg              pka_uchar;
  473. static struct fatdef    pka_rattr;
  474. static struct dsc$descriptor    pka_fibdsc =
  475. {   sizeof(pka_fib), DSC$K_DTYPE_Z, DSC$K_CLASS_S, (void *) &pka_fib  };
  476. static struct dsc$descriptor_s  pka_devdsc =
  477. {   0, DSC$K_DTYPE_T, DSC$K_CLASS_S, &nam.nam$t_dvi[1]  };
  478. static struct dsc$descriptor_s  pka_fnam =
  479. {   0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL  };
  480. static char exp_nam[NAM$C_MAXRSS];
  481. static char res_nam[NAM$C_MAXRSS];
  482. #define PK_PRINTABLE_RECTYP(x)   ( (x) == FAT$C_VARIABLE 
  483.                                 || (x) == FAT$C_STREAMLF 
  484.                                 || (x) == FAT$C_STREAMCR 
  485.                                 || (x) == FAT$C_STREAM   )
  486. static int create_qio_output(__GPRO)            /* return 1 (PK_WARN) if fail */
  487. {
  488.     int status;
  489.     int i;
  490.     if ( uO.cflag )
  491.     {
  492.         int rtype = pka_rattr.fat$v_rtype;
  493.         if (!PK_PRINTABLE_RECTYP(rtype))
  494.         {
  495.             Info(slide, 1, ((char *)slide,
  496.                "[ File %s has illegal record format to put to screen ]n",
  497.                G.filename));
  498.             return PK_WARN;
  499.         }
  500.         init_buf_ring();
  501.         switch(rtype)
  502.         {
  503.             case FAT$C_VARIABLE:
  504.                 _flush_routine = _flush_varlen;
  505.                 break;
  506.             case FAT$C_STREAM:
  507.             case FAT$C_STREAMCR:
  508.             case FAT$C_STREAMLF:
  509.                 _flush_routine = _flush_stream;
  510.                 got_eol = 0;
  511.                 break;
  512.             default:
  513.                 _flush_routine = _flush_blocks;
  514.                 break;
  515.         }
  516.         _close_routine = _close_rms;
  517.     }
  518.     else                        /* !(uO.cflag) : redirect output */
  519.     {
  520.         fileblk = cc$rms_fab;
  521.         fileblk.fab$l_fna = G.filename;
  522.         fileblk.fab$b_fns = strlen(G.filename);
  523.         nam = cc$rms_nam;
  524.         fileblk.fab$l_nam = &nam;
  525.         nam.nam$l_esa = exp_nam;
  526.         nam.nam$b_ess = sizeof(exp_nam);
  527.         nam.nam$l_rsa = res_nam;
  528.         nam.nam$b_rss = sizeof(res_nam);
  529.         if ( ERR(status = sys$parse(&fileblk)) )
  530.         {
  531.             vms_msg(__G__ "create_qio_output: sys$parse failed.n", status);
  532.             return PK_WARN;
  533.         }
  534.         pka_devdsc.dsc$w_length = (unsigned short)nam.nam$t_dvi[0];
  535.         if ( ERR(status = sys$assign(&pka_devdsc,&pka_devchn,0,0)) )
  536.         {
  537.             vms_msg(__G__ "create_qio_output: sys$assign failed.n", status);
  538.             return PK_WARN;
  539.         }
  540.         pka_fnam.dsc$a_pointer = nam.nam$l_name;
  541.         pka_fnam.dsc$w_length  = nam.nam$b_name + nam.nam$b_type;
  542.         if ( uO.V_flag /* keep versions */ )
  543.             pka_fnam.dsc$w_length += nam.nam$b_ver;
  544.         for (i=0;i<3;i++)
  545.         {
  546.             pka_fib.FIB$W_DID[i]=nam.nam$w_did[i];
  547.             pka_fib.FIB$W_FID[i]=0;
  548.         }
  549.         pka_fib.FIB$L_ACCTL = FIB$M_WRITE;
  550.         /* Allocate space for the file */
  551.         pka_fib.FIB$W_EXCTL = FIB$M_EXTEND;
  552.         if ( pka_uchar & FCH$M_CONTIG )
  553.             pka_fib.FIB$W_EXCTL |= FIB$M_ALCON | FIB$M_FILCON;
  554.         if ( pka_uchar & FCH$M_CONTIGB )
  555.             pka_fib.FIB$W_EXCTL |= FIB$M_ALCONB;
  556. #define SWAPW(x)        ( (((x)>>16)&0xFFFF) + ((x)<<16) )
  557.         pka_fib.fib$l_exsz = SWAPW(pka_rattr.fat$l_hiblk);
  558.         status = sys$qiow(0, pka_devchn, IO$_CREATE|IO$M_CREATE|IO$M_ACCESS,
  559.                           &pka_acp_sb, 0, 0,
  560.                           &pka_fibdsc, &pka_fnam, 0, 0, &pka_atr, 0);
  561.         if ( !ERR(status) )
  562.             status = pka_acp_sb.status;
  563.         if ( ERR(status) )
  564.         {
  565.             vms_msg(__G__ "[ Create file QIO failed. ]n", status);
  566.             sys$dassgn(pka_devchn);
  567.             return PK_WARN;
  568.         }
  569. #ifdef ASYNCH_QIO
  570.         init_buf_ring();
  571.         pka_io_pending = FALSE;
  572. #else
  573.         locptr = locbuf;
  574.         loccnt = 0;
  575. #endif
  576.         pka_vbn = 1;
  577.         _flush_routine = _flush_qio;
  578.         _close_routine = _close_qio;
  579.     }                   /* end if (!uO.cflag) */
  580.     return PK_COOL;
  581. }
  582. static int replace(__GPRO)
  583. {                       /*
  584.                          *      File exists. Inquire user about further action.
  585.                          */
  586.     char answ[10];
  587.     struct NAM nam;
  588.     int ierr;
  589.     if (query == 0)
  590.     {
  591.         do
  592.         {
  593.             Info(slide, 0x81, ((char *)slide,
  594.                  "%s exists:  [o]verwrite, new [v]ersion or [n]o extract?n
  595.   (uppercase response [O,V,N] = do same for all files): ",
  596.                  G.filename));
  597.             fflush(stderr);
  598.         } while (fgets(answ, 9, stderr) == NULL && !isalpha(answ[0])
  599.                  && tolower(answ[0]) != 'o'
  600.                  && tolower(answ[0]) != 'v'
  601.                  && tolower(answ[0]) != 'n');
  602.         if (isupper(answ[0]))
  603.             query = answ[0] = tolower(answ[0]);
  604.     }
  605.     else
  606.         answ[0] = query;
  607.     switch (answ[0])
  608.     {
  609.         case 'n':
  610.             ierr = 0;
  611.             break;
  612.         case 'v':
  613.             nam = cc$rms_nam;
  614.             nam.nam$l_rsa = G.filename;
  615.             nam.nam$b_rss = FILNAMSIZ - 1;
  616.             outfab->fab$l_fop |= FAB$M_MXV;
  617.             outfab->fab$l_nam = &nam;
  618.             ierr = sys$create(outfab);
  619.             if (!ERR(ierr))
  620.             {
  621.                 outfab->fab$l_nam = NULL;
  622.                 G.filename[outfab->fab$b_fns = nam.nam$b_rsl] = '';
  623.             }
  624.             break;
  625.         case 'o':
  626.             outfab->fab$l_fop |= FAB$M_SUP;
  627.             ierr = sys$create(outfab);
  628.             break;
  629.     }
  630.     return ierr;
  631. }
  632. #define W(p)    (*(unsigned short*)(p))
  633. #define L(p)    (*(unsigned long*)(p))
  634. #define EQL_L(a,b)      ( L(a) == L(b) )
  635. #define EQL_W(a,b)      ( W(a) == W(b) )
  636. /****************************************************************
  637.  * Function find_vms_attrs scans ZIP entry extra field if any   *
  638.  * and looks for VMS attribute records. Returns 0 if either no  *
  639.  * attributes found or no fab given.                            *
  640.  ****************************************************************/
  641. int find_vms_attrs(__G)
  642.     __GDEF
  643. {
  644.     uch *scan = G.extra_field;
  645.     struct  EB_header *hdr;
  646.     int len;
  647.     int type=VAT_NONE;
  648.     outfab = NULL;
  649.     xabfhc = NULL;
  650.     xabdat = NULL;
  651.     xabrdt = NULL;
  652.     xabpro = NULL;
  653.     first_xab = last_xab = NULL;
  654.     if (scan == NULL)
  655.         return VAT_NONE;
  656.     len = G.lrec.extra_field_length;
  657. #define LINK(p) {/* Link xaballs and xabkeys into chain */      
  658.                 if ( first_xab == NULL )                
  659.                         first_xab = (void *) p;         
  660.                 if ( last_xab != NULL )                 
  661.                         last_xab->xab$l_nxt = (void *) p;       
  662.                 last_xab = (void *) p;                  
  663.                 p->xab$l_nxt = NULL;                    
  664.         }
  665.     /* End of macro LINK */
  666.     while (len > 0)
  667.     {
  668.         hdr = (struct EB_header *) scan;
  669.         if (EQL_W(&hdr->tag, IZ_SIGNATURE))
  670.         {
  671.             /*
  672.              *  Info-ZIP style extra block decoding
  673.              */
  674.             struct IZ_block *blk;
  675.             uch *block_id;
  676.             type = VAT_IZ;
  677.             blk = (struct IZ_block *)hdr;
  678.             block_id = (uch *) &blk->bid;
  679.             if (EQL_L(block_id, FABSIG))
  680.             {
  681.                 outfab = (struct FAB *) extract_block(__G__ blk, 0,
  682.                                                 (uch *)&cc$rms_fab, FABL);
  683.             }
  684.             else if (EQL_L(block_id, XALLSIG))
  685.             {
  686.                 xaball = (struct XABALL *) extract_block(__G__ blk, 0,
  687.                                                 (uch *)&cc$rms_xaball, XALLL);
  688.                 LINK(xaball);
  689.             }
  690.             else if (EQL_L(block_id, XKEYSIG))
  691.             {
  692.                 xabkey = (struct XABKEY *) extract_block(__G__ blk, 0,
  693.                                                 (uch *)&cc$rms_xabkey, XKEYL);
  694.                 LINK(xabkey);
  695.             }
  696.             else if (EQL_L(block_id, XFHCSIG))
  697.             {
  698.                 xabfhc = (struct XABFHC *) extract_block(__G__ blk, 0,
  699.                                                 (uch *)&cc$rms_xabfhc, XFHCL);
  700.             }
  701.             else if (EQL_L(block_id, XDATSIG))
  702.             {
  703.                 xabdat = (struct XABDAT *) extract_block(__G__ blk, 0,
  704.                                                 (uch *)&cc$rms_xabdat, XDATL);
  705.             }
  706.             else if (EQL_L(block_id, XRDTSIG))
  707.             {
  708.                 xabrdt = (struct XABRDT *) extract_block(__G__ blk, 0,
  709.                                                 (uch *)&cc$rms_xabrdt, XRDTL);
  710.             }
  711.             else if (EQL_L(block_id, XPROSIG))
  712.             {
  713.                 xabpro = (struct XABPRO *) extract_block(__G__ blk, 0,
  714.                                                 (uch *)&cc$rms_xabpro, XPROL);
  715.             }
  716.             else if (EQL_L(block_id, VERSIG))
  717.             {
  718. #ifdef CHECK_VERSIONS
  719.                 char verbuf[80];
  720.                 int verlen = 0;
  721.                 uch *vers;
  722.                 char *m;
  723.                 get_vms_version(verbuf, sizeof(verbuf));
  724.                 vers = extract_block(__G__ blk, &verlen, 0, 0);
  725.                 if ((m = strrchr((char *) vers, '-')) != NULL)
  726.                     *m = '';  /* Cut out release number */
  727.                 if (strcmp(verbuf, (char *) vers) && uO.qflag < 2)
  728.                 {
  729.                     Info(slide, 0, ((char *)slide,
  730.                          "[ Warning: VMS version mismatch."));
  731.                     Info(slide, 0, ((char *)slide,
  732.                          "   This version %s --", verbuf));
  733.                     strncpy(verbuf, (char *) vers, verlen);
  734.                     verbuf[verlen] = '';
  735.                     Info(slide, 0, ((char *)slide,
  736.                          " version made by %s ]n", verbuf));
  737.                 }
  738.                 free(vers);
  739. #endif /* CHECK_VERSIONS */
  740.             }
  741.             else
  742.                 Info(slide, 1, ((char *)slide,
  743.                      "[ Warning: Unknown block signature %s ]n",
  744.                      block_id));
  745.         }
  746.         else if (hdr->tag == PK_SIGNATURE)
  747.         {
  748.             /*
  749.              *  PKWARE-style extra block decoding
  750.              */
  751.             struct  PK_header   *blk;
  752.             register byte   *scn;
  753.             register int    len;
  754.             type = VAT_PK;
  755.             blk = (struct PK_header *)hdr;
  756.             len = blk->size - (PK_HEADER_SIZE - EB_HEADSIZE);
  757.             scn = (byte *)(&blk->data);
  758.             pka_idx = 0;
  759.             if (blk->crc32 != crc32(CRCVAL_INITIAL, scn, (extent)len))
  760.             {
  761.                 Info(slide, 1, ((char *)slide,
  762.                   "[Warning: CRC error, discarding PKWARE extra field]n"));
  763.                 len = 0;
  764.                 type = VAT_NONE;
  765.             }
  766.             while (len > PK_FLDHDR_SIZE)
  767.             {
  768.                 register struct  PK_field  *fld;
  769.                 int skip=0;
  770.                 fld = (struct PK_field *)scn;
  771.                 switch(fld->tag)
  772.                 {
  773.                     case ATR$C_UCHAR:
  774.                         pka_uchar = L(&fld->value);
  775.                         break;
  776.                     case ATR$C_RECATTR:
  777.                         pka_rattr = *(struct fatdef *)(&fld->value);
  778.                         break;
  779.                     case ATR$C_UIC:
  780.                     case ATR$C_ADDACLENT:
  781.                         skip = !uO.X_flag;
  782.                         break;
  783.                 }
  784.                 if ( !skip )
  785.                 {
  786.                     pka_atr[pka_idx].atr$w_size = fld->size;
  787.                     pka_atr[pka_idx].atr$w_type = fld->tag;
  788.                     pka_atr[pka_idx].atr$l_addr = &fld->value;
  789.                     ++pka_idx;
  790.                 }
  791.                 len -= fld->size + PK_FLDHDR_SIZE;
  792.                 scn += fld->size + PK_FLDHDR_SIZE;
  793.             }
  794.             pka_atr[pka_idx].atr$w_size = 0;    /* End of list */
  795.             pka_atr[pka_idx].atr$w_type = 0;
  796.             pka_atr[pka_idx].atr$l_addr = 0; /* NULL when DECC VAX gets fixed */
  797.         }
  798.         len -= hdr->size + EB_HEADSIZE;
  799.         scan += hdr->size + EB_HEADSIZE;
  800.     }
  801.     if ( type == VAT_IZ )
  802.     {
  803.         if (outfab != NULL)
  804.         {   /* Do not link XABPRO,XABRDT now. Leave them for sys$close() */
  805.             outfab->fab$l_xab = NULL;
  806.             if (xabfhc != NULL)
  807.             {
  808.                 xabfhc->xab$l_nxt = outfab->fab$l_xab;
  809.                 outfab->fab$l_xab = (void *) xabfhc;
  810.             }
  811.             if (xabdat != NULL)
  812.             {
  813.                 xabdat->xab$l_nxt = outfab->fab$l_xab;
  814.                 outfab->fab$l_xab = (void *) xabdat;
  815.             }
  816.             if (first_xab != NULL)      /* Link xaball,xabkey subchain */
  817.             {
  818.                 last_xab->xab$l_nxt = outfab->fab$l_xab;
  819.                 outfab->fab$l_xab = (void *) first_xab;
  820.             }
  821.         }
  822.         else
  823.             type = VAT_NONE;
  824.     }
  825.     return type;
  826. }
  827. static void free_up()
  828. {
  829.     /*
  830.      * Free up all allocated xabs
  831.      */
  832.     if (xabdat != NULL) free(xabdat);
  833.     if (xabpro != NULL) free(xabpro);
  834.     if (xabrdt != NULL) free(xabrdt);
  835.     if (xabfhc != NULL) free(xabfhc);
  836.     while (first_xab != NULL)
  837.     {
  838.         struct XAB *x;
  839.         x = (struct XAB *) first_xab->xab$l_nxt;
  840.         free(first_xab);
  841.         first_xab = x;
  842.     }
  843.     if (outfab != NULL && outfab != &fileblk)
  844.         free(outfab);
  845. }
  846. #ifdef CHECK_VERSIONS
  847. static int get_vms_version(verbuf, len)
  848.     char *verbuf;
  849.     int len;
  850. {
  851.     int i = SYI$_VERSION;
  852.     int verlen = 0;
  853.     struct dsc$descriptor version;
  854.     char *m;
  855.     version.dsc$a_pointer = verbuf;
  856.     version.dsc$w_length  = len - 1;
  857.     version.dsc$b_dtype   = DSC$K_DTYPE_B;
  858.     version.dsc$b_class   = DSC$K_CLASS_S;
  859.     if (ERR(lib$getsyi(&i, 0, &version, &verlen, 0, 0)) || verlen == 0)
  860.         return 0;
  861.     /* Cut out trailing spaces "V5.4-3   " -> "V5.4-3" */
  862.     for (m = verbuf + verlen, i = verlen - 1; i > 0 && verbuf[i] == ' '; --i)
  863.         --m;
  864.     *m = '';
  865.     /* Cut out release number "V5.4-3" -> "V5.4" */
  866.     if ((m = strrchr(verbuf, '-')) != NULL)
  867.         *m = '';
  868.     return strlen(verbuf) + 1;  /* Transmit ending '' too */
  869. }
  870. #endif /* CHECK_VERSIONS */
  871. /*
  872.  * Extracts block from p. If resulting length is less then needed, fill
  873.  * extra space with corresponding bytes from 'init'.
  874.  * Currently understands 3 formats of block compression:
  875.  * - Simple storing
  876.  * - Compression of zero bytes to zero bits
  877.  * - Deflation (see memextract() in extract.c)
  878.  */
  879. static uch *extract_block(__G__ p, retlen, init, needlen)
  880.     __GDEF
  881.     struct IZ_block *p;
  882.     int *retlen;
  883.     uch *init;
  884.     int needlen;
  885. {
  886.     uch *block;         /* Pointer to block allocated */
  887.     int cmptype;
  888.     int usiz, csiz, max;
  889.     cmptype = p->flags & BC_MASK;
  890.     csiz = p->size - EXTBSL - RESL;
  891.     usiz = (cmptype == BC_STORED ? csiz : p->length);
  892.     if (needlen == 0)
  893.         needlen = usiz;
  894.     if (retlen)
  895.         *retlen = usiz;
  896. #ifndef MAX
  897. # define MAX(a,b)   ((a) > (b) ? (a) : (b))
  898. #endif
  899.     if ((block = (uch *) malloc(MAX(needlen, usiz))) == NULL)
  900.         return NULL;
  901.     if (init && (usiz < needlen))
  902.         memcpy(block, init, needlen);
  903.     switch (cmptype)
  904.     {
  905.         case BC_STORED: /* The simplest case */
  906.             memcpy(block, &(p->body[0]), usiz);
  907.             break;
  908.         case BC_00:
  909.             decompress_bits(block, usiz, &(p->body[0]));
  910.             break;
  911.         case BC_DEFL:
  912.             memextract(__G__ block, usiz, &(p->body[0]), csiz);
  913.             break;
  914.         default:
  915.             free(block);
  916.             block = NULL;
  917.     }
  918.     return block;
  919. }
  920. /*
  921.  *  Simple uncompression routine. The compression uses bit stream.
  922.  *  Compression scheme:
  923.  *
  924.  *  if (byte!=0)
  925.  *      putbit(1),putbyte(byte)
  926.  *  else
  927.  *      putbit(0)
  928.  */
  929. static void decompress_bits(outptr, needlen, bitptr)
  930.     uch *outptr;        /* Pointer into output block */
  931.     int needlen;        /* Size of uncompressed block */
  932.     uch *bitptr;        /* Pointer into compressed data */
  933. {
  934.     ulg bitbuf = 0;
  935.     int bitcnt = 0;
  936. #define _FILL   if (bitcnt+8 <= 32)                     
  937.                 {       bitbuf |= (*bitptr++) << bitcnt;
  938.                         bitcnt += 8;                    
  939.                 }
  940.     while (needlen--)
  941.     {
  942.         if (bitcnt <= 0)
  943.             _FILL;
  944.         if (bitbuf & 1)
  945.         {
  946.             bitbuf >>= 1;
  947.             if ((bitcnt -= 1) < 8)
  948.                 _FILL;
  949.             *outptr++ = (uch) bitbuf;
  950.             bitcnt -= 8;
  951.             bitbuf >>= 8;
  952.         }
  953.         else
  954.         {
  955.             *outptr++ = '';
  956.             bitcnt -= 1;
  957.             bitbuf >>= 1;
  958.         }
  959.     }
  960. }
  961. /* flush contents of output buffer */
  962. int flush(__G__ rawbuf, size, unshrink)    /* return PK-type error code */
  963.     __GDEF
  964.     uch *rawbuf;
  965.     ulg size;
  966.     int unshrink;
  967. {
  968.     G.crc32val = crc32(G.crc32val, rawbuf, (extent)size);
  969.     if (uO.tflag)
  970.         return PK_COOL; /* Do not output. Update CRC only */
  971.     else
  972.         return (*_flush_routine)(__G__ rawbuf, size, 0);
  973. }
  974. static int _flush_blocks(__G__ rawbuf, size, final_flag)
  975.                                                 /* Asynchronous version */
  976.     __GDEF
  977.     uch *rawbuf;
  978.     unsigned size;
  979.     int final_flag;   /* 1 if this is the final flushout */
  980. {
  981.     int status;
  982.     unsigned off = 0;
  983.     while (size > 0)
  984.     {
  985.         if (curbuf->bufcnt < BUFS512)
  986.         {
  987.             unsigned ncpy;
  988.             ncpy = size > (BUFS512 - curbuf->bufcnt) ?
  989.                    (BUFS512 - curbuf->bufcnt) : size;
  990.             memcpy(curbuf->buf + curbuf->bufcnt, rawbuf + off, ncpy);
  991.             size -= ncpy;
  992.             curbuf->bufcnt += ncpy;
  993.             off += ncpy;
  994.         }
  995.         if (curbuf->bufcnt == BUFS512)
  996.         {
  997.             status = WriteBuffer(__G__ curbuf->buf, curbuf->bufcnt);
  998.             if (status)
  999.                 return status;
  1000.             curbuf = curbuf->next;
  1001.             curbuf->bufcnt = 0;
  1002.         }
  1003.     }
  1004.     return (final_flag && (curbuf->bufcnt > 0)) ?
  1005.         WriteBuffer(__G__ curbuf->buf, curbuf->bufcnt) :
  1006.         PK_COOL;
  1007. }
  1008. #ifdef ASYNCH_QIO
  1009. static int WriteQIO(__G__ buf, len)
  1010.     __GDEF
  1011.     uch *buf;
  1012.     unsigned len;
  1013. {
  1014.     int status;
  1015.     if (pka_io_pending) {
  1016.         status = sys$synch(0, &pka_io_sb);
  1017.         if (!ERR(status))
  1018.             status = pka_io_sb.status;
  1019.         if (ERR(status))
  1020.         {
  1021.             vms_msg(__G__ "[ WriteQIO: sys$synch found I/O failure ]n",
  1022.                     status);
  1023.             return PK_DISK;
  1024.         }
  1025.         pka_io_pending = FALSE;
  1026.     }
  1027.     /*
  1028.      *   Put content of buffer as a single VB
  1029.      */
  1030.     status = sys$qio(0, pka_devchn, IO$_WRITEVBLK,
  1031.                      &pka_io_sb, 0, 0,
  1032.                      buf, len, pka_vbn,
  1033.                      0, 0, 0);
  1034.     if (ERR(status))
  1035.     {
  1036.         vms_msg(__G__ "[ WriteQIO: sys$qio failed ]n", status);
  1037.         return PK_DISK;
  1038.     }
  1039.     pka_io_pending = TRUE;
  1040.     pka_vbn += (len>>9);
  1041.     return PK_COOL;
  1042. }
  1043. static int _flush_qio(__G__ rawbuf, size, final_flag)
  1044.                                                 /* Asynchronous version */
  1045.     __GDEF
  1046.     uch *rawbuf;
  1047.     unsigned size;
  1048.     int final_flag;   /* 1 if this is the final flushout */
  1049. {
  1050.     int status;
  1051.     unsigned off = 0;
  1052.     while (size > 0)
  1053.     {
  1054.         if (curbuf->bufcnt < BUFS512)
  1055.         {
  1056.             unsigned ncpy;
  1057.             ncpy = size > (BUFS512 - curbuf->bufcnt) ?
  1058.                    (BUFS512 - curbuf->bufcnt) : size;
  1059.             memcpy(curbuf->buf + curbuf->bufcnt, rawbuf + off, ncpy);
  1060.             size -= ncpy;
  1061.             curbuf->bufcnt += ncpy;
  1062.             off += ncpy;
  1063.         }
  1064.         if (curbuf->bufcnt == BUFS512)
  1065.         {
  1066.             status = WriteQIO(__G__ curbuf->buf, curbuf->bufcnt);
  1067.             if (status)
  1068.                 return status;
  1069.             curbuf = curbuf->next;
  1070.             curbuf->bufcnt = 0;
  1071.         }
  1072.     }
  1073.     return (final_flag & (curbuf->bufcnt > 0)) ?
  1074.         WriteQIO(curbuf->buf, (curbuf->bufcnt+1)&(~1)) : /* even byte count! */
  1075.         PK_COOL;
  1076. }
  1077. #else /* !ASYNCH_QIO */
  1078. static int _flush_qio(__G__ rawbuf, size, final_flag)
  1079.     __GDEF
  1080.     uch *rawbuf;
  1081.     unsigned size;
  1082.     int final_flag;   /* 1 if this is the final flushout */
  1083. {
  1084.     int status;
  1085.     uch *out_ptr=rawbuf;
  1086.     if ( final_flag )
  1087.     {
  1088.         if ( loccnt > 0 )
  1089.         {
  1090.             status = sys$qiow(0, pka_devchn, IO$_WRITEVBLK,
  1091.                               &pka_io_sb, 0, 0,
  1092.                               locbuf,
  1093.                               (loccnt+1)&(~1), /* Round up to even byte count */
  1094.                               pka_vbn,
  1095.                               0, 0, 0);
  1096.             if (!ERR(status))
  1097.                 status = pka_io_sb.status;
  1098.             if (ERR(status))
  1099.             {
  1100.                 vms_msg(__G__ "[ Write QIO failed ]n", status);
  1101.                 return PK_DISK;
  1102.             }
  1103.         }
  1104.         return PK_COOL;
  1105.     }
  1106.     if ( loccnt > 0 )
  1107.     {
  1108.         /*
  1109.          *   Fill local buffer upto 512 bytes then put it out
  1110.          */
  1111.         unsigned ncpy;
  1112.         ncpy = 512-loccnt;
  1113.         if ( ncpy > size )
  1114.             ncpy = size;
  1115.         memcpy(locptr, out_ptr, ncpy);
  1116.         locptr += ncpy;
  1117.         loccnt += ncpy;
  1118.         size -= ncpy;
  1119.         out_ptr += ncpy;
  1120.         if ( loccnt == 512 )
  1121.         {
  1122.             status = sys$qiow(0, pka_devchn, IO$_WRITEVBLK,
  1123.                               &pka_io_sb, 0, 0,
  1124.                               locbuf, loccnt, pka_vbn,
  1125.                               0, 0, 0);
  1126.             if (!ERR(status))
  1127.                 status = pka_io_sb.status;
  1128.             if (ERR(status))
  1129.             {
  1130.                 vms_msg(__G__ "[ Write QIO failed ]n", status);
  1131.                 return PK_DISK;
  1132.             }
  1133.             pka_vbn++;
  1134.             loccnt = 0;
  1135.             locptr = locbuf;
  1136.         }
  1137.     }
  1138.     if ( size >= 512 )
  1139.     {
  1140.         unsigned nblk, put_cnt;
  1141.         /*
  1142.          *   Put rest of buffer as a single VB
  1143.          */
  1144.         put_cnt = (nblk = size>>9)<<9;
  1145.         status = sys$qiow(0, pka_devchn, IO$_WRITEVBLK,
  1146.                           &pka_io_sb, 0, 0,
  1147.                           out_ptr, put_cnt, pka_vbn,
  1148.                           0, 0, 0);
  1149.         if (!ERR(status))
  1150.             status = pka_io_sb.status;
  1151.         if (ERR(status))
  1152.         {
  1153.             vms_msg(__G__ "[ Write QIO failed ]n", status);
  1154.             return PK_DISK;
  1155.         }
  1156.         pka_vbn += nblk;
  1157.         out_ptr += put_cnt;
  1158.         size -= put_cnt;
  1159.     }
  1160.     if ( size > 0 )
  1161.     {
  1162.         memcpy(locptr, out_ptr, size);
  1163.         loccnt += size;
  1164.         locptr += size;
  1165.     }
  1166.     return PK_COOL;
  1167. }
  1168. #endif /* ?ASYNCH_QIO */
  1169. /*
  1170.  * The routine _flush_varlen() requires: "(size & 1) == 0"
  1171.  * (The variable-length record algorithm assumes an even byte-count!)
  1172.  */
  1173. static int _flush_varlen(__G__ rawbuf, size, final_flag)
  1174.     __GDEF
  1175.     uch *rawbuf;
  1176.     unsigned size;
  1177.     int final_flag;
  1178. {
  1179.     unsigned nneed;
  1180.     unsigned reclen;
  1181.     uch *inptr=rawbuf;
  1182.     /*
  1183.      * Flush local buffer
  1184.      */
  1185.     if ( loccnt > 0 )           /* incomplete record left from previous call */
  1186.     {
  1187.         reclen = *(ush*)locbuf;
  1188.         nneed = reclen + 2 - loccnt;
  1189.         if ( nneed > size )
  1190.         {
  1191.             if ( size+loccnt > BUFDBLS512 )
  1192.             {
  1193.                 char buf[80];
  1194.                 Info(buf, 1, (buf,
  1195.                      "[ Record too long (%u bytes) ]n", reclen));
  1196.                 return PK_DISK;
  1197.             }
  1198.             memcpy(locbuf+loccnt, inptr, size);
  1199.             loccnt += size;
  1200.             size = 0;
  1201.         }
  1202.         else
  1203.         {
  1204.             memcpy(locbuf+loccnt, inptr, nneed);
  1205.             loccnt += nneed;
  1206.             size -= nneed;
  1207.             inptr += nneed;
  1208.             if ( reclen & 1 )
  1209.             {
  1210.                 size--;
  1211.                 inptr++;
  1212.             }
  1213.             if ( WriteRecord(__G__ locbuf+2, reclen) )
  1214.                 return PK_DISK;
  1215.             loccnt = 0;
  1216.         }
  1217.     }
  1218.     /*
  1219.      * Flush incoming records
  1220.      */
  1221.     while (size > 0)
  1222.     {
  1223.         reclen = *(ush*)inptr;
  1224.         if ( reclen+2 <= size )
  1225.         {
  1226.             if (WriteRecord(__G__ inptr+2, reclen))
  1227.                 return PK_DISK;
  1228.             size -= 2+reclen;
  1229.             inptr += 2+reclen;
  1230.             if ( reclen & 1)
  1231.             {
  1232.                 --size;
  1233.                 ++inptr;
  1234.             }
  1235.         }
  1236.         else
  1237.         {
  1238.             memcpy(locbuf, inptr, size);
  1239.             loccnt = size;
  1240.             size = 0;
  1241.         }
  1242.     }
  1243.     /*
  1244.      * Final flush rest of local buffer
  1245.      */
  1246.     if ( final_flag && loccnt > 0 )
  1247.     {
  1248.         char buf[80];
  1249.         Info(buf, 1, (buf,
  1250.              "[ Warning, incomplete record of length %u ]n",
  1251.              (unsigned)*(ush*)locbuf));
  1252.         if ( WriteRecord(__G__ locbuf+2, loccnt-2) )
  1253.             return PK_DISK;
  1254.     }
  1255.     return PK_COOL;
  1256. }
  1257. /*
  1258.  *   Routine _flush_stream breaks decompressed stream into records
  1259.  *   depending on format of the stream (fab->rfm, G.pInfo->textmode, etc.)
  1260.  *   and puts out these records. It also handles CR LF sequences.
  1261.  *   Should be used when extracting *text* files.
  1262.  */
  1263. #define VT      0x0B
  1264. #define FF      0x0C
  1265. /* The file is from MSDOS/OS2/NT -> handle CRLF as record end, throw out ^Z */
  1266. /* GRR NOTES:  cannot depend on hostnum!  May have "flip'd" file or re-zipped
  1267.  * a Unix file, etc. */
  1268. #ifdef USE_ORIG_DOS
  1269. # define ORG_DOS   (hostnum==FS_FAT_ || hostnum==FS_HPFS_ || hostnum==FS_NTFS_)
  1270. #else
  1271. # define ORG_DOS    1
  1272. #endif
  1273. /* Record delimiters */
  1274. #ifdef undef
  1275. #define RECORD_END(c,f)                                                 
  1276. (    ( ORG_DOS || G.pInfo->textmode ) && c==CTRLZ                      
  1277.   || ( f == FAB$C_STMLF && c==LF )                                      
  1278.   || ( f == FAB$C_STMCR || ORG_DOS || G.pInfo->textmode ) && c==CR     
  1279.   || ( f == FAB$C_STM && (c==CR || c==LF || c==FF || c==VT) )           
  1280. )
  1281. #else
  1282. #   define  RECORD_END(c,f)   ((c) == LF || (c) == (CR))
  1283. #endif
  1284. static unsigned find_eol(p,n,l)
  1285. /*
  1286.  *  Find first CR, LF, CR/LF or LF/CR in string 'p' of length 'n'.
  1287.  *  Return offset of the sequence found or 'n' if not found.
  1288.  *  If found, return in '*l' length of the sequence (1 or 2) or
  1289.  *  zero if sequence end not seen, i.e. CR or LF is last char
  1290.  *  in the buffer.
  1291.  */
  1292.     uch *p;
  1293.     unsigned n;
  1294.     unsigned *l;
  1295. {
  1296.     unsigned off = n;
  1297.     uch *q;
  1298.     *l = 0;
  1299.     for (q=p ; n > 0 ; --n,++q)
  1300.         if ( RECORD_END(*q,rfm) )
  1301.         {
  1302.             off = q-p;
  1303.             break;
  1304.         }
  1305.     if ( n > 1 )
  1306.     {
  1307.         *l = 1;
  1308.         if ( ( q[0] == CR && q[1] == LF ) || ( q[0] == LF && q[1] == CR ) )
  1309.             *l = 2;
  1310.     }
  1311.     return off;
  1312. }
  1313. /* Record delimiters that must be put out */
  1314. #define PRINT_SPEC(c)   ( (c)==FF || (c)==VT )
  1315. static int _flush_stream(__G__ rawbuf, size, final_flag)
  1316.     __GDEF
  1317.     uch *rawbuf;
  1318.     unsigned size;
  1319.     int final_flag; /* 1 if this is the final flushout */
  1320. {
  1321.     int rest;
  1322.     unsigned end = 0, start = 0;
  1323.     if (size == 0 && loccnt == 0)
  1324.         return PK_COOL;         /* Nothing to do ... */
  1325.     if ( final_flag )
  1326.     {
  1327.         unsigned recsize;
  1328.         /*
  1329.          * This is flush only call. size must be zero now.
  1330.          * Just eject everything we have in locbuf.
  1331.          */
  1332.         recsize = loccnt - (got_eol ? 1 : 0);
  1333.         /*
  1334.          *  If the last char of file was ^Z ( end-of-file in MSDOS ),
  1335.          *  we will see it now.
  1336.          */
  1337.         if ( recsize==1 && locbuf[0] == CTRLZ )
  1338.             return PK_COOL;
  1339.         return WriteRecord(__G__ locbuf, recsize);
  1340.     }
  1341.     if ( loccnt > 0 )
  1342.     {
  1343.         /* Find end of record partially saved in locbuf */
  1344.         unsigned recsize;
  1345.         int complete=0;
  1346.         if ( got_eol )
  1347.         {
  1348.             recsize = loccnt - 1;
  1349.             complete = 1;
  1350.             if ( (got_eol == CR && rawbuf[0] == LF) ||
  1351.                  (got_eol == LF && rawbuf[0] == CR) )
  1352.                 end = 1;
  1353.             got_eol = 0;
  1354.         }
  1355.         else
  1356.         {
  1357.             unsigned eol_len;
  1358.             unsigned eol_off;
  1359.             eol_off = find_eol(rawbuf, size, &eol_len);
  1360.             if ( loccnt+eol_off > BUFDBLS512 )
  1361.             {
  1362.                 /*
  1363.                  *  No room in locbuf. Dump it and clear
  1364.                  */
  1365.                 char buf[80];           /* CANNOT use slide for Info() */
  1366.                 recsize = loccnt;
  1367.                 start = 0;
  1368.                 Info(buf, 1, (buf,
  1369.                      "[ Warning: Record too long (%u) ]n", loccnt+eol_off));
  1370.                 complete = 1;
  1371.                 end = 0;
  1372.             }
  1373.             else
  1374.             {
  1375.                 if ( eol_off >= size )
  1376.                 {
  1377.                     end = size;
  1378.                     complete = 0;
  1379.                 }
  1380.                 else if ( eol_len == 0 )
  1381.                 {
  1382.                     got_eol = rawbuf[eol_off];
  1383.                     end = size;
  1384.                     complete = 0;
  1385.                 }
  1386.                 else
  1387.                 {
  1388.                     memcpy(locptr, rawbuf, eol_off);
  1389.                     recsize = loccnt + eol_off;
  1390.                     locptr += eol_off;
  1391.                     loccnt += eol_off;
  1392.                     end = eol_off + eol_len;
  1393.                     complete = 1;
  1394.                 }
  1395.             }
  1396.         }
  1397.         if ( complete )
  1398.         {
  1399.             if (WriteRecord(__G__ locbuf, recsize))
  1400.                 return PK_DISK;
  1401.             loccnt = 0;
  1402.             locptr = locbuf;
  1403.         }
  1404.     }                           /* end if ( loccnt ) */
  1405.     for (start = end; start < size && end < size; )
  1406.     {
  1407.         unsigned eol_off, eol_len;
  1408.         got_eol = 0;
  1409. #ifdef undef
  1410.         if (uO.cflag)
  1411.             /* skip CR's at the beginning of record */
  1412.             while (start < size && rawbuf[start] == CR)
  1413.                 ++start;
  1414. #endif
  1415.         if ( start >= size )
  1416.             continue;
  1417.         /* Find record end */
  1418.         end = start+(eol_off = find_eol(rawbuf+start, size-start, &eol_len));
  1419.         if ( end >= size )
  1420.             continue;
  1421.         if ( eol_len > 0 )
  1422.         {
  1423.             if ( WriteRecord(__G__ rawbuf+start, end-start) )
  1424.                 return PK_DISK;
  1425.             start = end + eol_len;
  1426.         }
  1427.         else
  1428.         {
  1429.             got_eol = rawbuf[end];
  1430.             end = size;
  1431.             continue;
  1432.         }
  1433.     }
  1434.     rest = size - start;
  1435.     if (rest > 0)
  1436.     {
  1437.         if ( rest > BUFDBLS512 )
  1438.         {
  1439.             unsigned recsize;
  1440.             char buf[80];               /* CANNOT use slide for Info() */
  1441.             recsize = rest - (got_eol ? 1 : 0 );
  1442.             Info(buf, 1, (buf,
  1443.                  "[ Warning: Record too long (%u) ]n", recsize));
  1444.             got_eol = 0;
  1445.             return WriteRecord(__G__ rawbuf+start, recsize);
  1446.         }
  1447.         else
  1448.         {
  1449.             memcpy(locptr, rawbuf + start, rest);
  1450.             locptr += rest;
  1451.             loccnt += rest;
  1452.         }
  1453.     }
  1454.     return PK_COOL;
  1455. }
  1456. static int WriteBuffer(__G__ buf, len)
  1457.     __GDEF
  1458.     uch *buf;
  1459.     unsigned len;
  1460. {
  1461.     int status;
  1462.     status = sys$wait(outrab);
  1463.     if (ERR(status))
  1464.     {
  1465.         vms_msg(__G__ "[ WriteBuffer: sys$wait failed ]n", status);
  1466.         vms_msg(__G__ "", outrab->rab$l_stv);
  1467.     }
  1468.     outrab->rab$w_rsz = len;
  1469.     outrab->rab$l_rbf = (char *) buf;
  1470.     if (ERR(status = sys$write(outrab)))
  1471.     {
  1472.         vms_msg(__G__ "[ WriteBuffer: sys$write failed ]n", status);
  1473.         vms_msg(__G__ "", outrab->rab$l_stv);
  1474.         return PK_DISK;
  1475.     }
  1476.     return PK_COOL;
  1477. }
  1478. static int WriteRecord(__G__ rec, len)
  1479.     __GDEF
  1480.     uch *rec;
  1481.     unsigned len;
  1482. {
  1483.     int status;
  1484.     if (uO.cflag)
  1485.     {
  1486.         (void)(*G.message)((zvoid *)&G, rec, len, 0);
  1487.         (void)(*G.message)((zvoid *)&G, (uch *) ("n"), 1, 0);
  1488.     }
  1489.     else
  1490.     {
  1491.         if (ERR(status = sys$wait(outrab)))
  1492.         {
  1493.             vms_msg(__G__ "[ WriteRecord: sys$wait failed ]n", status);
  1494.             vms_msg(__G__ "", outrab->rab$l_stv);
  1495.         }
  1496.         outrab->rab$w_rsz = len;
  1497.         outrab->rab$l_rbf = (char *) rec;
  1498.         if (ERR(status = sys$put(outrab)))
  1499.         {
  1500.             vms_msg(__G__ "[ WriteRecord: sys$put failed ]n", status);
  1501.             vms_msg(__G__ "", outrab->rab$l_stv);
  1502.             return PK_DISK;
  1503.         }
  1504.     }
  1505.     return PK_COOL;
  1506. }
  1507. void close_outfile(__G)
  1508.     __GDEF
  1509. {
  1510.     int status;
  1511.     status = (*_flush_routine)(__G__ NULL, 0, 1);
  1512.     if (status)
  1513.         return /* PK_DISK */;
  1514.     if (uO.cflag)
  1515.         return /* PK_COOL */;   /* Don't close stdout */
  1516.     /* return */ (*_close_routine)(__G);
  1517. }
  1518. static int _close_rms(__GPRO)
  1519. {
  1520.     int status;
  1521.     struct XABPRO pro;
  1522.     /* Link XABRDT, XABDAT and optionally XABPRO */
  1523.     if (xabrdt != NULL)
  1524.     {
  1525.         xabrdt->xab$l_nxt = NULL;
  1526.         outfab->fab$l_xab = (void *) xabrdt;
  1527.     }
  1528.     else
  1529.     {
  1530.         rdt.xab$l_nxt = NULL;
  1531.         outfab->fab$l_xab = (void *) &rdt;
  1532.     }
  1533.     if (xabdat != NULL)
  1534.     {
  1535.         xabdat->xab$l_nxt = outfab->fab$l_xab;
  1536.         outfab->fab$l_xab = (void *)xabdat;
  1537.     }
  1538.     if (xabpro != NULL)
  1539.     {
  1540.         if ( !uO.X_flag )
  1541.             xabpro->xab$l_uic = 0;    /* Use default (user's) uic */
  1542.         xabpro->xab$l_nxt = outfab->fab$l_xab;
  1543.         outfab->fab$l_xab = (void *) xabpro;
  1544.     }
  1545.     else
  1546.     {
  1547.         pro = cc$rms_xabpro;
  1548.         pro.xab$w_pro = G.pInfo->file_attr;
  1549.         pro.xab$l_nxt = outfab->fab$l_xab;
  1550.         outfab->fab$l_xab = (void *) &pro;
  1551.     }
  1552.     status = sys$wait(outrab);
  1553.     if (ERR(status))
  1554.     {
  1555.         vms_msg(__G__ "[ _close_rms: sys$wait failed ]n", status);
  1556.         vms_msg(__G__ "", outrab->rab$l_stv);
  1557.     }
  1558.     status = sys$close(outfab);
  1559. #ifdef DEBUG
  1560.     if (ERR(status))
  1561.     {
  1562.         vms_msg(__G__
  1563.           "r[ Warning: cannot set owner/protection/time attributes ]n",
  1564.           status);
  1565.         vms_msg(__G__ "", outfab->fab$l_stv);
  1566.     }
  1567. #endif
  1568.     free_up();
  1569.     return PK_COOL;
  1570. }
  1571. static int _close_qio(__GPRO)
  1572. {
  1573.     int status;
  1574.     pka_fib.FIB$L_ACCTL =
  1575.         FIB$M_WRITE | FIB$M_NOTRUNC ;
  1576.     pka_fib.FIB$W_EXCTL = 0;
  1577.     pka_fib.FIB$W_FID[0] =
  1578.     pka_fib.FIB$W_FID[1] =
  1579.     pka_fib.FIB$W_FID[2] =
  1580.     pka_fib.FIB$W_DID[0] =
  1581.     pka_fib.FIB$W_DID[1] =
  1582.     pka_fib.FIB$W_DID[2] = 0;
  1583. #ifdef ASYNCH_QIO
  1584.     if (pka_io_pending) {
  1585.         status = sys$synch(0, &pka_io_sb);
  1586.         if (!ERR(status))
  1587.             status = pka_io_sb.status;
  1588.         if (ERR(status))
  1589.         {
  1590.             vms_msg(__G__ "[ _close_qio: sys$synch found I/O failure ]n",
  1591.                     status);
  1592.         }
  1593.         pka_io_pending = FALSE;
  1594.     }
  1595. #endif /* ASYNCH_QIO */
  1596.     status = sys$qiow(0, pka_devchn, IO$_DEACCESS, &pka_acp_sb,
  1597.                       0, 0,
  1598.                       &pka_fibdsc, 0, 0, 0,
  1599.                       &pka_atr, 0);
  1600.     sys$dassgn(pka_devchn);
  1601.     if ( !ERR(status) )
  1602.         status = pka_acp_sb.status;
  1603.     if ( ERR(status) )
  1604.     {
  1605.         vms_msg(__G__ "[ Deaccess QIO failed ]n", status);
  1606.         return PK_DISK;
  1607.     }
  1608.     return PK_COOL;
  1609. }
  1610. #ifdef TIMESTAMP
  1611. /* Nonzero if `y' is a leap year, else zero. */
  1612. #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
  1613. /* Number of leap years from 1970 to `y' (not including `y' itself). */
  1614. #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
  1615. /* Accumulated number of days from 01-Jan up to start of current month. */
  1616. static ZCONST short ydays[] = {
  1617.     0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
  1618. };
  1619. /***********************/
  1620. /* Function mkgmtime() */
  1621. /***********************/
  1622. static time_t mkgmtime(tm)
  1623.     struct tm *tm;
  1624. {
  1625.     time_t m_time;
  1626.     int yr, mo, dy, hh, mm, ss;
  1627.     unsigned days;
  1628.     yr = tm->tm_year - 70;
  1629.     mo = tm->tm_mon;
  1630.     dy = tm->tm_mday - 1;
  1631.     hh = tm->tm_hour;
  1632.     mm = tm->tm_min;
  1633.     ss = tm->tm_sec;
  1634.     /* calculate days from BASE to this year and add expired days this year */
  1635.     dy = (unsigned)dy + ((unsigned)yr * 365) + (unsigned)nleap(yr+1970) +
  1636.          (unsigned)ydays[mo] + ((mo > 1) && leap(yr+1970));
  1637.     /* convert date & time to seconds relative to 00:00:00, 01/01/1970 */
  1638.     return (time_t)((unsigned long)(unsigned)dy * 86400L +
  1639.                     (unsigned long)hh * 3600L +
  1640.                     (unsigned long)(mm * 60 + ss));
  1641. } /* end function mkgmtime() */
  1642. /*******************************/
  1643. /* Function dos_to_unix_time() */  /* only used for timestamping of archives */
  1644. /*******************************/
  1645. time_t dos_to_unix_time(dosdatetime)
  1646.     ulg dosdatetime;
  1647. {
  1648.     struct tm *ltm;             /* Local time. */
  1649.     time_t loctime;             /* The time_t value of local time. */
  1650.     time_t then;                /* The time to return. */
  1651.     long tzoffset_adj;          /* timezone-adjustment `remainder' */
  1652.     int bailout_cnt;            /* counter of tries for tz correction */
  1653.     then = time(NULL);
  1654.     ltm = localtime(&then);
  1655.     /* dissect date */
  1656.     ltm->tm_year = ((int)(dosdatetime >> 25) & 0x7f) + 80;
  1657.     ltm->tm_mon  = ((int)(dosdatetime >> 21) & 0x0f) - 1;
  1658.     ltm->tm_mday = ((int)(dosdatetime >> 16) & 0x1f);
  1659.     /* dissect time */
  1660.     ltm->tm_hour = (int)(dosdatetime >> 11) & 0x1f;
  1661.     ltm->tm_min  = (int)(dosdatetime >> 5) & 0x3f;
  1662.     ltm->tm_sec  = (int)(dosdatetime << 1) & 0x3e;
  1663.     loctime = mkgmtime(ltm);
  1664.     /* Correct for the timezone and any daylight savings time.
  1665.        The correction is verified and repeated when not correct, to
  1666.        take into account the rare case that a change to or from daylight
  1667.        savings time occurs between when it is the time in `tm' locally
  1668.        and when it is that time in Greenwich. After the second correction,
  1669.        the "timezone & daylight" offset should be correct in all cases. To
  1670.        be sure, we allow a third try, but then the loop is stopped. */
  1671.     bailout_cnt = 3;
  1672.     then = loctime;
  1673.     do {
  1674.       ltm = localtime(&then);
  1675.       tzoffset_adj = (ltm != NULL) ? (loctime - mkgmtime(ltm)) : 0L;
  1676.       if (tzoffset_adj == 0L)
  1677.         break;
  1678.       then += tzoffset_adj;
  1679.     } while (--bailout_cnt > 0);
  1680.     if ( (dosdatetime >= DOSTIME_2038_01_18) &&
  1681.          (then < (time_t)0x70000000L) )
  1682.         then = U_TIME_T_MAX;    /* saturate in case of (unsigned) overflow */
  1683.     if (then < (time_t)0L)      /* a converted DOS time cannot be negative */
  1684.         then = S_TIME_T_MAX;    /*  -> saturate at max signed time_t value */
  1685.     return then;
  1686. } /* end function dos_to_unix_time() */
  1687. /*******************************/
  1688. /*  Function uxtime2vmstime()  */
  1689. /*******************************/
  1690. static void uxtime2vmstime(  /* convert time_t value into 64 bit VMS bintime */
  1691.     time_t utimeval,
  1692.     long int binval[2] )
  1693. {
  1694.     time_t m_time = utimeval;
  1695.     struct tm *t = localtime(&m_time);
  1696.     if (t == (struct tm *)NULL) {
  1697.         /* time conversion error; use current time instead, hoping
  1698.            that localtime() does not reject it as well! */
  1699.         m_time = time(NULL);
  1700.         t = localtime(&m_time);
  1701.     }
  1702.     sprintf(timbuf, "%02d-%3s-%04d %02d:%02d:%02d.00",
  1703.             t->tm_mday, month[t->tm_mon], t->tm_year + 1900,
  1704.             t->tm_hour, t->tm_min, t->tm_sec);
  1705.     sys$bintim(&date_str, binval);
  1706. } /* end function uxtime2vmstime() */
  1707. /***************************/
  1708. /*  Function stamp_file()  */  /* adapted from VMSmunch...it just won't die! */
  1709. /***************************/
  1710. int stamp_file(fname, modtime)
  1711.     ZCONST char *fname;
  1712.     time_t modtime;
  1713. {
  1714.     int status;
  1715.     int i;
  1716.     static long int Cdate[2], Rdate[2], Edate[2], Bdate[2];
  1717.     static short int revisions;
  1718. #if defined(__DECC) || defined(__DECCXX)
  1719. #pragma __member_alignment __save
  1720. #pragma __nomember_alignment
  1721. #endif /* __DECC || __DECCXX */
  1722.     static union {
  1723.       unsigned short int value;
  1724.       struct {
  1725.         unsigned system : 4;
  1726.         unsigned owner : 4;
  1727.         unsigned group : 4;
  1728.         unsigned world : 4;
  1729.       } bits;
  1730.     } prot;
  1731. #if defined(__DECC) || defined(__DECCXX)
  1732. #pragma __member_alignment __restore
  1733. #endif /* __DECC || __DECCXX */
  1734.     static unsigned long uic;
  1735.     static struct fjndef jnl;
  1736.     static struct atrdef Atr[] = {
  1737.         {sizeof(pka_rattr), ATR$C_RECATTR, &pka_rattr},
  1738.         {sizeof(pka_uchar), ATR$C_UCHAR, &pka_uchar},
  1739.         {sizeof(Cdate), ATR$C_CREDATE, &Cdate[0]},
  1740.         {sizeof(Rdate), ATR$C_REVDATE, &Rdate[0]},
  1741.         {sizeof(Edate), ATR$C_EXPDATE, &Edate[0]},
  1742.         {sizeof(Bdate), ATR$C_BAKDATE, &Bdate[0]},
  1743.         {sizeof(revisions), ATR$C_ASCDATES, &revisions},
  1744.         {sizeof(prot), ATR$C_FPRO, &prot},
  1745.         {sizeof(uic), ATR$C_UIC, &uic},
  1746.         {sizeof(jnl), ATR$C_JOURNAL, &jnl},
  1747.         {0, 0, 0}
  1748.     };
  1749.     fileblk = cc$rms_fab;
  1750.     fileblk.fab$l_fna = (char *)fname;
  1751.     fileblk.fab$b_fns = strlen(fname);
  1752.     nam = cc$rms_nam;
  1753.     fileblk.fab$l_nam = &nam;
  1754.     nam.nam$l_esa = exp_nam;
  1755.     nam.nam$b_ess = sizeof(exp_nam);
  1756.     nam.nam$l_rsa = res_nam;
  1757.     nam.nam$b_rss = sizeof(res_nam);
  1758.     if ( ERR(status = sys$parse(&fileblk)) )
  1759.     {
  1760.         vms_msg(__G__ "stamp_file: sys$parse failed.n", status);
  1761.         return -1;
  1762.     }
  1763.     pka_devdsc.dsc$w_length = (unsigned short)nam.nam$t_dvi[0];
  1764.     if ( ERR(status = sys$assign(&pka_devdsc,&pka_devchn,0,0)) )
  1765.     {
  1766.         vms_msg(__G__ "stamp_file: sys$assign failed.n", status);
  1767.         return -1;
  1768.     }
  1769.     pka_fnam.dsc$a_pointer = nam.nam$l_name;
  1770.     pka_fnam.dsc$w_length  = nam.nam$b_name + nam.nam$b_type + nam.nam$b_ver;
  1771.     for (i=0;i<3;i++)
  1772.     {
  1773.         pka_fib.FIB$W_DID[i]=nam.nam$w_did[i];
  1774.         pka_fib.FIB$W_FID[i]=nam.nam$w_fid[i];
  1775.     }
  1776.     /* Use the IO$_ACCESS function to return info about the file */
  1777.     /* Note, used this way, the file is not opened, and the expiration */
  1778.     /* and revision dates are not modified */
  1779.     status = sys$qiow(0, pka_devchn, IO$_ACCESS,
  1780.                       &pka_acp_sb, 0, 0,
  1781.                       &pka_fibdsc, &pka_fnam, 0, 0, &Atr, 0);
  1782.     if ( !ERR(status) )
  1783.         status = pka_acp_sb.status;
  1784.     if ( ERR(status) )
  1785.     {
  1786.         vms_msg(__G__ "[ Access file QIO failed. ]n", status);
  1787.         sys$dassgn(pka_devchn);
  1788.         return -1;
  1789.     }
  1790.     uxtime2vmstime(modtime, Cdate);
  1791.     memcpy(Rdate, Cdate, sizeof(Cdate));
  1792.     /* note, part of the FIB was cleared by earlier QIOW, so reset it */
  1793.     pka_fib.FIB$L_ACCTL = FIB$M_NORECORD;
  1794.     for (i=0;i<3;i++)
  1795.     {
  1796.         pka_fib.FIB$W_DID[i]=nam.nam$w_did[i];
  1797.         pka_fib.FIB$W_FID[i]=nam.nam$w_fid[i];
  1798.     }
  1799.     /* Use the IO$_MODIFY function to change info about the file */
  1800.     /* Note, used this way, the file is not opened, however this would */
  1801.     /* normally cause the expiration and revision dates to be modified. */
  1802.     /* Using FIB$M_NORECORD prohibits this from happening. */
  1803.     status = sys$qiow(0, pka_devchn, IO$_MODIFY,
  1804.                       &pka_acp_sb, 0, 0,
  1805.                       &pka_fibdsc, &pka_fnam, 0, 0, &Atr, 0);
  1806.     if ( !ERR(status) )
  1807.         status = pka_acp_sb.status;
  1808.     if ( ERR(status) )
  1809.     {
  1810.         vms_msg(__G__ "[ Modify file QIO failed. ]n", status);
  1811.         sys$dassgn(pka_devchn);
  1812.         return -1;
  1813.     }
  1814.     if ( ERR(status = sys$dassgn(pka_devchn)) )
  1815.     {
  1816.         vms_msg(__G__ "stamp_file: sys$dassgn failed.n", status);
  1817.         return -1;
  1818.     }
  1819.     return 0;
  1820. } /* end function stamp_file() */
  1821. #endif /* TIMESTAMP */
  1822. #ifdef DEBUG
  1823. #if 0   /* currently not used anywhere ! */
  1824. void dump_rms_block(p)
  1825.     unsigned char *p;
  1826. {
  1827.     unsigned char bid, len;
  1828.     int err;
  1829.     char *type;
  1830.     char buf[132];
  1831.     int i;
  1832.     err = 0;
  1833.     bid = p[0];
  1834.     len = p[1];
  1835.     switch (bid)
  1836.     {
  1837.         case FAB$C_BID:
  1838.             type = "FAB";
  1839.             break;
  1840.         case XAB$C_ALL:
  1841.             type = "xabALL";
  1842.             break;
  1843.         case XAB$C_KEY:
  1844.             type = "xabKEY";
  1845.             break;
  1846.         case XAB$C_DAT:
  1847.             type = "xabDAT";
  1848.             break;
  1849.         case XAB$C_RDT:
  1850.             type = "xabRDT";
  1851.             break;
  1852.         case XAB$C_FHC:
  1853.             type = "xabFHC";
  1854.             break;
  1855.         case XAB$C_PRO:
  1856.             type = "xabPRO";
  1857.             break;
  1858.         default:
  1859.             type = "Unknown";
  1860.             err = 1;
  1861.             break;
  1862.     }
  1863.     printf("Block @%08X of type %s (%d).", p, type, bid);
  1864.     if (err)
  1865.     {
  1866.         printf("n");
  1867.         return;
  1868.     }
  1869.     printf(" Size = %dn", len);
  1870.     printf(" Offset - Hex - Decn");
  1871.     for (i = 0; i < len; i += 8)
  1872.     {
  1873.         int j;
  1874.         printf("%3d - ", i);
  1875.         for (j = 0; j < 8; j++)
  1876.             if (i + j < len)
  1877.                 printf("%02X ", p[i + j]);
  1878.             else
  1879.                 printf("   ");
  1880.         printf(" - ");
  1881.         for (j = 0; j < 8; j++)
  1882.             if (i + j < len)
  1883.                 printf("%03d ", p[i + j]);
  1884.             else
  1885.                 printf("    ");
  1886.         printf("n");
  1887.     }
  1888. }
  1889. #endif                          /* never */
  1890. #endif                          /* DEBUG */
  1891. static void vms_msg(__GPRO__ char *string, int status)
  1892. {
  1893.     static char msgbuf[256];
  1894.     $DESCRIPTOR(msgd, msgbuf);
  1895.     int msglen = 0;
  1896.     if (ERR(lib$sys_getmsg(&status, &msglen, &msgd, 0, 0)))
  1897.         Info(slide, 1, ((char *)slide,
  1898.              "%s[ VMS status = %d ]n", string, status));
  1899.     else
  1900.     {
  1901.         msgbuf[msglen] = '';
  1902.         Info(slide, 1, ((char *)slide, "%s[ %s ]n", string, msgbuf));
  1903.     }
  1904. }
  1905. #ifndef SFX
  1906. char *do_wild( __G__ wld )
  1907.     __GDEF
  1908.     char *wld;
  1909. {
  1910.     int status;
  1911.     static char filenam[256];
  1912.     static char efn[256];
  1913.     static char last_wild[256];
  1914.     static struct FAB fab;
  1915.     static struct NAM nam;
  1916.     static int first_call=1;
  1917.     static ZCONST char deflt[] = "[]*.zip";
  1918.     if ( first_call || strcmp(wld, last_wild) )
  1919.     {   /* (Re)Initialize everything */
  1920.         strcpy( last_wild, wld );
  1921.         first_call = 1;            /* New wild spec */
  1922.         fab = cc$rms_fab;
  1923.         fab.fab$l_fna = last_wild;
  1924.         fab.fab$b_fns = strlen(last_wild);
  1925.         fab.fab$l_dna = (char *) deflt;
  1926.         fab.fab$b_dns = sizeof(deflt)-1;
  1927.         fab.fab$l_nam = &nam;
  1928.         nam = cc$rms_nam;
  1929.         nam.nam$l_esa = efn;
  1930.         nam.nam$b_ess = sizeof(efn)-1;
  1931.         nam.nam$l_rsa = filenam;
  1932.         nam.nam$b_rss = sizeof(filenam)-1;
  1933.         if ( !OK(sys$parse(&fab)) )
  1934.             return (char *)NULL;     /* Initialization failed */
  1935.         first_call = 0;
  1936.         if ( !OK(sys$search(&fab)) )
  1937.         {
  1938.             strcpy( filenam, wld );
  1939.             return filenam;
  1940.         }
  1941.     }
  1942.     else
  1943.     {
  1944.         if ( !OK(sys$search(&fab)) )
  1945.         {
  1946.             first_call = 1;        /* Reinitialize next time */
  1947.             return (char *)NULL;
  1948.         }
  1949.     }
  1950.     filenam[nam.nam$b_rsl] = 0;
  1951.     return filenam;
  1952. } /* end function do_wild() */
  1953. #endif /* !SFX */
  1954. static ulg unix_to_vms[8]={ /* Map from UNIX rwx to VMS rwed */
  1955.                             /* Note that unix w bit is mapped to VMS wd bits */
  1956.                                                               /* no access */
  1957.     XAB$M_NOREAD | XAB$M_NOWRITE | XAB$M_NODEL | XAB$M_NOEXE,    /* --- */
  1958.     XAB$M_NOREAD | XAB$M_NOWRITE | XAB$M_NODEL,                  /* --x */
  1959.     XAB$M_NOREAD |                               XAB$M_NOEXE,    /* -w- */
  1960.     XAB$M_NOREAD,                                                /* -wx */
  1961.                    XAB$M_NOWRITE | XAB$M_NODEL | XAB$M_NOEXE,    /* r-- */
  1962.                    XAB$M_NOWRITE | XAB$M_NODEL,                  /* r-x */
  1963.                                                  XAB$M_NOEXE,    /* rw- */
  1964.     0                                                            /* rwx */
  1965.                                                               /* full access */
  1966. };
  1967. #define SETDFPROT   /* We are using undocumented VMS System Service     */
  1968.                     /* SYS$SETDFPROT here. If your version of VMS does  */
  1969.                     /* not have that service, undef SETDFPROT.          */
  1970.                     /* IM: Maybe it's better to put this to Makefile    */
  1971.                     /* and DESCRIP.MMS */
  1972. #ifdef SETDFPROT
  1973. extern int SYS$SETDFPROT();
  1974. #endif
  1975. int mapattr(__G)
  1976.     __GDEF
  1977. {
  1978.     ulg  tmp=G.crec.external_file_attributes, theprot;
  1979.     static ulg  defprot = -1L,
  1980.                 sysdef,owndef,grpdef,wlddef;  /* Default protection fields */
  1981.     /* IM: The only field of XABPRO we need to set here is */
  1982.     /*     file protection, so we need not to change type */
  1983.     /*     of G.pInfo->file_attr. WORD is quite enough. */
  1984.     if ( defprot == -1L )
  1985.     {
  1986.         /*
  1987.          * First time here -- Get user default settings
  1988.          */
  1989. #ifdef SETDFPROT    /* Undef this if linker cat't resolve SYS$SETDFPROT */
  1990.         defprot = 0L;
  1991.         if ( !ERR(SYS$SETDFPROT(0,&defprot)) )
  1992.         {
  1993.             sysdef = defprot & ( (1L<<XAB$S_SYS)-1 ) << XAB$V_SYS;
  1994.             owndef = defprot & ( (1L<<XAB$S_OWN)-1 ) << XAB$V_OWN;
  1995.             grpdef = defprot & ( (1L<<XAB$S_GRP)-1 ) << XAB$V_GRP;
  1996.             wlddef = defprot & ( (1L<<XAB$S_WLD)-1 ) << XAB$V_WLD;
  1997.         }
  1998.         else
  1999.         {
  2000. #endif /* SETDFPROT */
  2001.             umask(defprot = umask(0));
  2002.             defprot = ~defprot;
  2003.             wlddef = unix_to_vms[defprot & 07] << XAB$V_WLD;
  2004.             grpdef = unix_to_vms[(defprot>>3) & 07] << XAB$V_GRP;
  2005.             owndef = unix_to_vms[(defprot>>6) & 07] << XAB$V_OWN;
  2006.             sysdef = owndef << (XAB$V_SYS - XAB$V_OWN);
  2007.             defprot = sysdef | owndef | grpdef | wlddef;
  2008. #ifdef SETDFPROT
  2009.         }
  2010. #endif /* SETDFPROT */
  2011.     }
  2012.     switch (G.pInfo->hostnum) {
  2013.         case AMIGA_:
  2014.             tmp = (unsigned)(tmp>>16 & 0x0f);   /* Amiga RWED bits */
  2015.             G.pInfo->file_attr =  (tmp << XAB$V_OWN) |
  2016.                                    grpdef | sysdef | wlddef;
  2017.             break;
  2018.         case UNIX_:
  2019.         case VMS_:  /*IM: ??? Does VMS Zip store protection in UNIX format ?*/
  2020.                     /* GRR:  Yup.  Bad decision on my part... */
  2021.         case ACORN_:
  2022.         case ATARI_:
  2023.         case BEOS_:
  2024.         case QDOS_:
  2025.         case TANDEM_:
  2026.             {
  2027.               unsigned uxattr = (unsigned)(tmp >> 16);  /* drwxrwxrwx */
  2028.               int r = FALSE;
  2029.               if (uxattr == 0 && G.extra_field) {
  2030.                 /* Some (non-Info-ZIP) implementations of Zip for Unix and
  2031.                    VMS (and probably others ??) leave 0 in the upper 16-bit
  2032.                    part of the external_file_attributes field. Instead,
  2033.                    they store file permission attributes in an e.f. block.
  2034.                    As a work-around, we search for the presence of one of
  2035.                    these extra fields and fall back to the MSDOS compatible
  2036.                    part of external_file_attributes if one of the known
  2037.                    e.f. types has been detected.
  2038.                    Later, we might implement extraction of the permission
  2039.                    bits from the VMS extra field. But for now, the
  2040.                    work-around should be sufficient to provide "readable"
  2041.                    extracted files.
  2042.                    (For ASI Unix e.f., an experimental remap of the e.f.
  2043.                    mode value IS already provided!)
  2044.                  */
  2045.                 ush ebID;
  2046.                 unsigned ebLen;
  2047.                 uch *ef = G.extra_field;
  2048.                 unsigned ef_len = G.crec.extra_field_length;
  2049.                 while (!r && ef_len >= EB_HEADSIZE) {
  2050.                     ebID = makeword(ef);
  2051.                     ebLen = (unsigned)makeword(ef+EB_LEN);
  2052.                     if (ebLen > (ef_len - EB_HEADSIZE))
  2053.                         /* discoverd some e.f. inconsistency! */
  2054.                         break;
  2055.                     switch (ebID) {
  2056.                       case EF_ASIUNIX:
  2057.                         if (ebLen >= (EB_ASI_MODE+2)) {
  2058.                             uxattr =
  2059.                               (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
  2060.                             /* force stop of loop: */
  2061.                             ef_len = (ebLen + EB_HEADSIZE);
  2062.                             break;
  2063.                         }
  2064.                         /* else: fall through! */
  2065.                       case EF_PKVMS:
  2066.                         /* "found nondecypherable e.f. with perm. attr" */
  2067.                         r = TRUE;
  2068.                       default:
  2069.                         break;
  2070.                     }
  2071.                     ef_len -= (ebLen + EB_HEADSIZE);
  2072.                     ef += (ebLen + EB_HEADSIZE);
  2073.                 }
  2074.               }
  2075.               if (!r) {
  2076.                   theprot  = (unix_to_vms[uxattr & 07] << XAB$V_WLD)
  2077.                            | (unix_to_vms[(uxattr>>3) & 07] << XAB$V_GRP)
  2078.                            | (unix_to_vms[(uxattr>>6) & 07] << XAB$V_OWN);
  2079.                   if ( tmp & 0x4000 )
  2080.                       /* Directory -- set D bits */
  2081.                       theprot |= (XAB$M_NODEL << XAB$V_SYS)
  2082.                               | (XAB$M_NODEL << XAB$V_OWN)
  2083.                               | (XAB$M_NODEL << XAB$V_GRP)
  2084.                               | (XAB$M_NODEL << XAB$V_WLD);
  2085.                   G.pInfo->file_attr = theprot;
  2086.                   break;
  2087.               }
  2088.             }
  2089.             /* fall through! */
  2090.         /* all remaining cases:  expand MSDOS read-only bit into write perms */
  2091.         case FS_FAT_:
  2092.         case FS_HPFS_:
  2093.         case FS_NTFS_:
  2094.         case MAC_:
  2095.         case TOPS20_:
  2096.         default:
  2097.             theprot = defprot;
  2098.             if ( tmp & 1 )   /* Test read-only bit */
  2099.             {   /* Bit is set -- set bits in all fields */
  2100.                 tmp = XAB$M_NOWRITE | XAB$M_NODEL;
  2101.                 theprot |= (tmp << XAB$V_SYS) | (tmp << XAB$V_OWN) |
  2102.                            (tmp << XAB$V_GRP) | (tmp << XAB$V_WLD);
  2103.             }
  2104.             G.pInfo->file_attr = theprot;
  2105.             break;
  2106.     } /* end switch (host-OS-created-by) */
  2107.     return 0;
  2108. } /* end function mapattr() */
  2109. #ifndef EEXIST
  2110. #  include <errno.h>    /* For mkdir() status codes */
  2111. #endif
  2112. #include <fscndef.h> /* for filescan */
  2113. #   define FN_MASK   7
  2114. #   define USE_DEFAULT  (FN_MASK+1)
  2115. /*
  2116.  * Checkdir function codes:
  2117.  *      ROOT        -   set root path from unzip qq d:[dir]
  2118.  *      INIT        -   get ready for "filename"
  2119.  *      APPEND_DIR  -   append pathcomp
  2120.  *      APPEND_NAME -   append filename
  2121.  *      APPEND_NAME | USE_DEFAULT   -    expand filename using collected path
  2122.  *      GETPATH     -   return resulting filespec
  2123.  *      END         -   free dynamically allocated space prior to program exit
  2124.  */
  2125. static  int created_dir;
  2126. int mapname(__G__ renamed)
  2127.             /* returns: */
  2128.             /* 0 (PK_COOL) if no error, */
  2129.             /* 1 (PK_WARN) if caution (filename trunc), */
  2130.             /* 2 (PK_ERR)  if warning (skip file because dir doesn't exist), */
  2131.             /* 3 (PK_BADERR) if error (skip file), */
  2132.             /* 77 (IZ_CREATED_DIR) if has created directory, */
  2133.             /* 78 (IZ_VOL_LABEL) if path was volume label (skip it) */
  2134.             /* 10 if no memory (skip file) */
  2135.     __GDEF
  2136.     int renamed;
  2137. {
  2138.     char pathcomp[FILNAMSIZ];   /* path-component buffer */
  2139.     char *pp, *cp=NULL;         /* character pointers */
  2140.     char *lastsemi = NULL;      /* pointer to last semi-colon in pathcomp */
  2141.     char *last_dot = NULL;      /* last dot not converted to underscore */
  2142.     int quote = FALSE;          /* flag:  next char is literal */
  2143.     int dotname = FALSE;        /* flag:  path component begins with dot */
  2144.     int error = 0;
  2145.     register unsigned workch;   /* hold the character being tested */
  2146.     if ( renamed )
  2147.     {
  2148.         if ( !(error = checkdir(__G__ pathcomp, APPEND_NAME | USE_DEFAULT)) )
  2149.             strcpy(G.filename, pathcomp);
  2150.         return error;
  2151.     }
  2152. /*---------------------------------------------------------------------------
  2153.     Initialize various pointers and counters and stuff.
  2154.   ---------------------------------------------------------------------------*/
  2155.     if (G.pInfo->vollabel)
  2156.         return IZ_VOL_LABEL;    /* can't set disk volume labels on VMS */
  2157.     /* can create path as long as not just freshening, or if user told us */
  2158.     G.create_dirs = !uO.fflag;
  2159.     created_dir = FALSE;        /* not yet */
  2160. /* GRR:  for VMS, convert to internal format now or later? or never? */
  2161.     if (checkdir(__G__ pathcomp, INIT) == 10)
  2162.         return 10;              /* initialize path buffer, unless no memory */
  2163.     *pathcomp = '';           /* initialize translation buffer */
  2164.     pp = pathcomp;              /* point to translation buffer */
  2165.     if (uO.jflag)               /* junking directories */
  2166. /* GRR:  watch out for VMS version... */
  2167.         cp = (char *)strrchr(G.filename, '/');
  2168.     if (cp == NULL)             /* no '/' or not junking dirs */
  2169.         cp = G.filename;        /* point to internal zipfile-member pathname */
  2170.     else
  2171.         ++cp;                   /* point to start of last component of path */
  2172. /*---------------------------------------------------------------------------
  2173.     Begin main loop through characters in G.filename.
  2174.   ---------------------------------------------------------------------------*/
  2175.     while ((workch = (uch)*cp++) != 0) {
  2176.         if (quote) {              /* if character quoted, */
  2177.             *pp++ = (char)workch; /*  include it literally */
  2178.             quote = FALSE;
  2179.         } else
  2180.             switch (workch) {
  2181.             case '/':             /* can assume -j flag not given */
  2182.                 *pp = '';
  2183.                 if ((error = checkdir(__G__ pathcomp, APPEND_DIR)) > 1)
  2184.                     return error;
  2185.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  2186.                 last_dot = NULL;  /* directory names must not contain dots */
  2187.                 lastsemi = NULL;  /* leave directory semi-colons alone */
  2188.                 break;
  2189.             case ':':
  2190.                 *pp++ = '_';      /* drive names not stored in zipfile, */
  2191.                 break;            /*  so no colons allowed */
  2192.             case '.':
  2193.                 if (pp == pathcomp) {     /* nothing appended yet... */
  2194.                     if (*cp == '/') {     /* don't bother appending a "./" */
  2195.                         ++cp;             /*  component to the path:  skip */
  2196.                                           /*  to next char after the '/' */
  2197.                     } else if (*cp == '.' && cp[1] == '/') {   /* "../" */
  2198.                         *pp++ = '.';      /* add first dot, unchanged... */
  2199.                         *pp++ = '.';      /* add second dot, unchanged... */
  2200.                         ++cp;             /* skip second dot */
  2201.                     }                     /* next char is  the '/' */
  2202.                     break;
  2203.                 }
  2204.                 last_dot = pp;    /* point at last dot so far... */
  2205.                 *pp++ = '_';      /* convert dot to underscore for now */
  2206.                 break;
  2207.             case ';':             /* start of VMS version? */
  2208.                 if (lastsemi)
  2209.                     *lastsemi = '_';   /* convert previous one to underscore */
  2210.                 lastsemi = pp;
  2211.                 *pp++ = ';';      /* keep for now; remove VMS vers. later */
  2212.                 break;
  2213.             case ' ':
  2214.                 *pp++ = '_';
  2215.                 break;
  2216.             default:
  2217.                 if ( isalpha(workch) || isdigit(workch) ||
  2218.                     workch=='$' || workch=='-' )
  2219.                     *pp++ = (char)workch;
  2220.                 else
  2221.                     *pp++ = '_';  /* convert everything else to underscore */
  2222.                 break;
  2223.             } /* end switch */
  2224.     } /* end while loop */
  2225.     *pp = '';                   /* done with pathcomp:  terminate it */
  2226.     /* if not saving them, remove VMS version numbers (appended "###") */
  2227.     if (lastsemi) {
  2228.         pp = lastsemi + 1;        /* expect all digits after semi-colon */
  2229.         while (isdigit((uch)(*pp)))
  2230.             ++pp;
  2231.         if (*pp)                  /* not version number:  convert ';' to '_' */
  2232.             *lastsemi = '_';
  2233.         else if (!uO.V_flag)      /* only digits between ';' and end:  nuke */
  2234.             *lastsemi = '';
  2235.         /* else only digits and we're saving version number:  do nothing */
  2236.     }
  2237.     if (last_dot != NULL)         /* one dot is OK:  put it back in */
  2238.         *last_dot = '.';
  2239. /*---------------------------------------------------------------------------
  2240.     Report if directory was created (and no file to create:  filename ended
  2241.     in '/'), check name to be sure it exists, and combine path and name be-
  2242.     fore exiting.
  2243.   ---------------------------------------------------------------------------*/
  2244.     if (G.filename[strlen(G.filename) - 1] == '/') {
  2245.         checkdir(__G__ "", APPEND_NAME);   /* create directory, if not found */
  2246.         checkdir(__G__ G.filename, GETPATH);
  2247.         if (created_dir) {
  2248.             if (QCOND2) {
  2249.                 Info(slide, 0, ((char *)slide, "   creating: %sn",
  2250.                   G.filename));
  2251.             }
  2252.             return IZ_CREATED_DIR;   /* set dir time (note trailing '/') */
  2253.         }
  2254.         return 2;   /* dir existed already; don't look for data to extract */
  2255.     }
  2256.     if (*pathcomp == '') {
  2257.         Info(slide, 1, ((char *)slide,
  2258.              "mapname:  conversion of %s failedn", G.filename));
  2259.         return 3;
  2260.     }
  2261.     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
  2262.     checkdir(__G__ G.filename, GETPATH);
  2263.     return error;
  2264. } /* end function mapname() */
  2265. int checkdir(__G__ pathcomp, fcn)
  2266. /*
  2267.  * returns:  1 - (on APPEND_NAME) truncated filename
  2268.  *           2 - path doesn't exist, not allowed to create
  2269.  *           3 - path doesn't exist, tried to create and failed; or
  2270.  *               path exists and is not a directory, but is supposed to be
  2271.  *           4 - path is too long
  2272.  *          10 - can't allocate memory for filename buffers
  2273.  */
  2274.     __GDEF
  2275.     char *pathcomp;
  2276.     int fcn;
  2277. {
  2278.     int function=fcn & FN_MASK;
  2279.     static char pathbuf[FILNAMSIZ];
  2280.     static char lastdir[FILNAMSIZ]="t"; /* directory created last time */
  2281.                                          /* initially - impossible dir. spec. */
  2282.     static char *pathptr=pathbuf;        /* For debugger */
  2283.     static char *devptr, *dirptr, *namptr;
  2284.     static int  devlen, dirlen, namlen;
  2285.     static int  root_dirlen;
  2286.     static char *end;
  2287.     static int  first_comp,root_has_dir;
  2288.     static int  rootlen=0;
  2289.     static char *rootend;
  2290.     static int  mkdir_failed=0;
  2291.     int status;
  2292. /************
  2293.  *** ROOT ***
  2294.  ************/
  2295. #if (!defined(SFX) || defined(SFX_EXDIR))
  2296.     if (function==ROOT)
  2297.     {        /*  Assume VMS root spec */
  2298.         char  *p = pathcomp;
  2299.         char  *q;
  2300.         struct
  2301.         {
  2302.             short  len;
  2303.             short  code;
  2304.             char   *addr;
  2305.         } itl [4] =
  2306.         {
  2307.             {  0,  FSCN$_DEVICE,    NULL  },
  2308.             {  0,  FSCN$_ROOT,      NULL  },
  2309.             {  0,  FSCN$_DIRECTORY, NULL  },
  2310.             {  0,  0,               NULL  }   /* End of itemlist */
  2311.         };
  2312.         int fields = 0;
  2313.         struct dsc$descriptor  pthcmp;
  2314.         /*
  2315.          *  Initialize everything
  2316.          */
  2317.         end = devptr = dirptr = rootend = pathbuf;
  2318.         devlen = dirlen = rootlen = 0;
  2319.         pthcmp.dsc$a_pointer = pathcomp;
  2320.         if ( (pthcmp.dsc$w_length = strlen(pathcomp)) > 255 )
  2321.             return 4;
  2322.         status = sys$filescan(&pthcmp, itl, &fields);
  2323.         if ( !OK(status) )
  2324.             return 3;
  2325.         if ( fields & FSCN$M_DEVICE )
  2326.         {
  2327.             strncpy(devptr = end, itl[0].addr, itl[0].len);
  2328.             dirptr = (end += (devlen = itl[0].len));
  2329.         }
  2330.         root_has_dir = 0;
  2331.         if ( fields & FSCN$M_ROOT )
  2332.         {
  2333.             int   len;
  2334.             strncpy(dirptr = end, itl[1].addr,
  2335.                 len = itl[1].len - 1);        /* Cut out trailing ']' */
  2336.             end += len;
  2337.             root_has_dir = 1;
  2338.         }
  2339.         if ( fields & FSCN$M_DIRECTORY )
  2340.         {
  2341.             char  *ptr;
  2342.             int   len;
  2343.             len = itl[2].len-1;
  2344.             ptr = itl[2].addr;
  2345.             if ( root_has_dir /* i.e. root specified */ )
  2346.             {
  2347.                 --len;                            /* Cut out leading dot */
  2348.                 ++ptr;                            /* ??? [a.b.c.][.d.e] */
  2349.             }
  2350.             strncpy(dirptr=end, ptr, len);  /* Replace trailing ']' */
  2351.             *(end+=len) = '.';                    /* ... with dot */
  2352.             ++end;
  2353.             root_has_dir = 1;
  2354.         }
  2355.         /* When user specified "[a.b.c.]" or "[qq...]", we have too many
  2356.         *  trailing dots. Let's cut them out. Now we surely have at least
  2357.         *  one trailing dot and "end" points just behind it. */
  2358.         dirlen = end - dirptr;
  2359.         while ( dirlen > 1 && end[-2] == '.' )
  2360.             --dirlen,--end;
  2361.         first_comp = !root_has_dir;
  2362.         root_dirlen = end - dirptr;
  2363.         *(rootend = end) = '';
  2364.         rootlen = rootend - devptr;
  2365.         return 0;
  2366.     }
  2367. #endif /* !SFX || SFX_EXDIR */
  2368. /************
  2369.  *** INIT ***
  2370.  ************/
  2371.     if ( function == INIT )
  2372.     {
  2373.         if ( strlen(G.filename) + rootlen + 13 > 255 )
  2374.             return 4;
  2375.         if ( rootlen == 0 )     /* No root given, reset everything. */
  2376.         {
  2377.             devptr = dirptr = rootend = pathbuf;
  2378.             devlen = dirlen = 0;
  2379.         }
  2380.         end = rootend;
  2381.         first_comp = !root_has_dir;
  2382.         if ( dirlen = root_dirlen )
  2383.             end[-1] = '.';
  2384.         *end = '';
  2385.         return 0;
  2386.     }
  2387. /******************
  2388.  *** APPEND_DIR ***
  2389.  ******************/
  2390.     if ( function == APPEND_DIR )
  2391.     {
  2392.         int cmplen;
  2393.         cmplen = strlen(pathcomp);
  2394.         if ( first_comp )
  2395.         {
  2396.             *end++ = '[';
  2397.             if ( cmplen )
  2398.                 *end++ = '.';   /*       "dir/..." --> "[.dir...]"    */
  2399.             /*                     else  "/dir..." --> "[dir...]"     */
  2400.             first_comp = 0;
  2401.         }
  2402.         if ( cmplen == 1 && *pathcomp == '.' )
  2403.             ; /* "..././..." -- ignore */
  2404.         else if ( cmplen == 2 && pathcomp[0] == '.' && pathcomp[1] == '.' )
  2405.         {   /* ".../../..." -- convert to "...-..." */
  2406.             *end++ = '-';
  2407.             *end++ = '.';
  2408.         }
  2409.         else if ( cmplen + (end-pathptr) > 255 )
  2410.             return 4;
  2411.         else
  2412.         {
  2413.             strcpy(end, pathcomp);
  2414.             *(end+=cmplen) = '.';
  2415.             ++end;
  2416.         }
  2417.         dirlen = end - dirptr;
  2418.         *end = '';
  2419.         return 0;
  2420.     }
  2421. /*******************
  2422.  *** APPEND_NAME ***
  2423.  *******************/
  2424.     if ( function == APPEND_NAME )
  2425.     {
  2426.         if ( fcn & USE_DEFAULT )
  2427.         {   /* Expand renamed filename using collected path, return
  2428.              *  at pathcomp */
  2429.             struct        FAB fab;
  2430.             struct        NAM nam;
  2431.             fab = cc$rms_fab;
  2432.             fab.fab$l_fna = G.filename;
  2433.             fab.fab$b_fns = strlen(G.filename);
  2434.             fab.fab$l_dna = pathptr;
  2435.             fab.fab$b_dns = end-pathptr;
  2436.             fab.fab$l_nam = &nam;
  2437.             nam = cc$rms_nam;
  2438.             nam.nam$l_esa = pathcomp;
  2439.             nam.nam$b_ess = 255;            /* Assume large enaugh */
  2440.             if (!OK(status = sys$parse(&fab)) && status == RMS$_DNF )
  2441.                                          /* Directory not found: */
  2442.             {                            /* ... try to create it */
  2443.                 char    save;
  2444.                 char    *dirend;
  2445.                 int     mkdir_failed;
  2446.                 dirend = (char*)nam.nam$l_dir + nam.nam$b_dir;
  2447.                 save = *dirend;
  2448.                 *dirend = '';
  2449.                 if ( (mkdir_failed = mkdir(nam.nam$l_dev, 0)) &&
  2450.                      errno == EEXIST )
  2451.                     mkdir_failed = 0;
  2452.                 *dirend = save;
  2453.                 if ( mkdir_failed )
  2454.                     return 3;
  2455.                 created_dir = TRUE;
  2456.             }                                /* if (sys$parse... */
  2457.             pathcomp[nam.nam$b_esl] = '';
  2458.             return 0;
  2459.         }                                /* if (USE_DEFAULT) */
  2460.         else
  2461.         {
  2462.             *end = '';
  2463.             if ( dirlen )
  2464.             {
  2465.                 dirptr[dirlen-1] = ']'; /* Close directory */
  2466.                 /*
  2467.                  *  Try to create the target directory.
  2468.                  *  Don't waste time creating directory that was created
  2469.                  *  last time.
  2470.                  */
  2471.                 if ( STRICMP(lastdir,pathbuf) )
  2472.                 {
  2473.                     mkdir_failed = 0;
  2474.                     if ( mkdir(pathbuf,0) )
  2475.                     {
  2476.                         if ( errno != EEXIST )
  2477.                             mkdir_failed = 1;   /* Mine for GETPATH */
  2478.                     }
  2479.                     else
  2480.                         created_dir = TRUE;
  2481.                     strcpy(lastdir,pathbuf);
  2482.                 }
  2483.             }
  2484.             else
  2485.             {   /*
  2486.                  * Target directory unspecified.
  2487.                  * Try to create "sys$disk:[]"
  2488.                  */
  2489.                 if ( strcmp(lastdir,"sys$disk:[]") )
  2490.                 {
  2491.                     strcpy(lastdir,"sys$disk:[]");
  2492.                     mkdir_failed = 0;
  2493.                     if ( mkdir(lastdir,0) && errno != EEXIST )
  2494.                         mkdir_failed = 1;   /* Mine for GETPATH */
  2495.                 }
  2496.             }
  2497.             if ( strlen(pathcomp) + (end-pathbuf) > 255 )
  2498.                 return 1;
  2499.             strcpy(end, pathcomp);
  2500.             end += strlen(pathcomp);
  2501.             return 0;
  2502.         }
  2503.     }
  2504. /***************
  2505.  *** GETPATH ***
  2506.  ***************/
  2507.     if ( function == GETPATH )
  2508.     {
  2509.         if ( mkdir_failed )
  2510.             return 3;
  2511.         *end = '';                    /* To be safe */
  2512.         strcpy( pathcomp, pathbuf );
  2513.         return 0;
  2514.     }
  2515. /***********
  2516.  *** END ***
  2517.  ***********/
  2518.     if ( function == END )
  2519.     {
  2520.         Trace((stderr, "checkdir(): nothing to free...n"));
  2521.         return 0;
  2522.     }
  2523.     return 99;  /* should never reach */
  2524. }
  2525. int check_for_newer(__G__ filenam)   /* return 1 if existing file newer or */
  2526.     __GDEF                           /*  equal; 0 if older; -1 if doesn't */
  2527.     char *filenam;                   /*  exist yet */
  2528. {
  2529. #ifdef USE_EF_UT_TIME
  2530.     iztimes z_utime;
  2531.     struct tm *t;
  2532. #endif
  2533.     unsigned short timbuf[7];
  2534.     unsigned dy, mo, yr, hh, mm, ss, dy2, mo2, yr2, hh2, mm2, ss2;
  2535.     struct FAB fab;
  2536.     struct XABDAT xdat;
  2537.     if (stat(filenam, &G.statbuf))
  2538.         return DOES_NOT_EXIST;
  2539.     fab  = cc$rms_fab;
  2540.     xdat = cc$rms_xabdat;
  2541.     fab.fab$l_xab = (char *) &xdat;
  2542.     fab.fab$l_fna = filenam;
  2543.     fab.fab$b_fns = strlen(filenam);
  2544.     fab.fab$l_fop = FAB$M_GET | FAB$M_UFO;
  2545.     if ((sys$open(&fab) & 1) == 0)       /* open failure:  report exists and */
  2546.         return EXISTS_AND_OLDER;         /*  older so new copy will be made  */
  2547.     sys$numtim(&timbuf,&xdat.xab$q_cdt);
  2548.     fab.fab$l_xab = NULL;
  2549.     sys$dassgn(fab.fab$l_stv);
  2550.     sys$close(&fab);   /* be sure file is closed and RMS knows about it */
  2551. #ifdef USE_EF_UT_TIME
  2552.     if (G.extra_field &&
  2553. #ifdef IZ_CHECK_TZ
  2554.         G.tz_is_valid &&
  2555. #endif
  2556.         (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
  2557.                           G.lrec.last_mod_dos_datetime, &z_utime, NULL)
  2558.          & EB_UT_FL_MTIME))
  2559.         t = localtime(&(z_utime.mtime));
  2560.     else
  2561.         t = (struct tm *)NULL;
  2562.     if (t != (struct tm *)NULL)
  2563.     {
  2564.         yr2 = (unsigned)(t->tm_year) + 1900;
  2565.         mo2 = (unsigned)(t->tm_mon) + 1;
  2566.         dy2 = (unsigned)(t->tm_mday);
  2567.         hh2 = (unsigned)(t->tm_hour);
  2568.         mm2 = (unsigned)(t->tm_min);
  2569.         ss2 = (unsigned)(t->tm_sec);
  2570.         /* round to nearest sec--may become 60,
  2571.            but doesn't matter for compare */
  2572.         ss = (unsigned)((float)timbuf[5] + (float)timbuf[6]*.01 + 0.5);
  2573.         TTrace((stderr, "check_for_newer:  using Unix extra field mtimen"));
  2574.     }
  2575.     else
  2576. #endif /* USE_EF_UT_TIME */
  2577.     {
  2578.         yr2 = ((G.lrec.last_mod_dos_datetime >> 25) & 0x7f) + 1980;
  2579.         mo2 = (G.lrec.last_mod_dos_datetime >> 21) & 0x0f;
  2580.         dy2 = (G.lrec.last_mod_dos_datetime >> 16) & 0x1f;
  2581.         hh2 = (G.lrec.last_mod_dos_datetime >> 11) & 0x1f;
  2582.         mm2 = (G.lrec.last_mod_dos_datetime >> 5) & 0x3f;
  2583.         ss2 = (G.lrec.last_mod_dos_datetime << 1) & 0x1f;
  2584.         /* round to nearest 2 secs--may become 60,
  2585.            but doesn't matter for compare */
  2586.         ss = (unsigned)((float)timbuf[5] + (float)timbuf[6]*.01 + 1.) & (~1);
  2587.     }
  2588.     yr = timbuf[0];
  2589.     mo = timbuf[1];
  2590.     dy = timbuf[2];
  2591.     hh = timbuf[3];
  2592.     mm = timbuf[4];
  2593.     if (yr > yr2)
  2594.         return EXISTS_AND_NEWER;
  2595.     else if (yr < yr2)
  2596.         return EXISTS_AND_OLDER;
  2597.     if (mo > mo2)
  2598.         return EXISTS_AND_NEWER;
  2599.     else if (mo < mo2)
  2600.         return EXISTS_AND_OLDER;
  2601.     if (dy > dy2)
  2602.         return EXISTS_AND_NEWER;
  2603.     else if (dy < dy2)
  2604.         return EXISTS_AND_OLDER;
  2605.     if (hh > hh2)
  2606.         return EXISTS_AND_NEWER;
  2607.     else if (hh < hh2)
  2608.         return EXISTS_AND_OLDER;
  2609.     if (mm > mm2)
  2610.         return EXISTS_AND_NEWER;
  2611.     else if (mm < mm2)
  2612.         return EXISTS_AND_OLDER;
  2613.     if (ss >= ss2)
  2614.         return EXISTS_AND_NEWER;
  2615.     return EXISTS_AND_OLDER;
  2616. }
  2617. #ifdef RETURN_CODES
  2618. void return_VMS(__G__ err)
  2619.     __GDEF
  2620. #else
  2621. void return_VMS(err)
  2622. #endif
  2623.     int err;
  2624. {
  2625.     int severity;
  2626. #ifdef RETURN_CODES
  2627. /*---------------------------------------------------------------------------
  2628.     Do our own, explicit processing of error codes and print message, since
  2629.     VMS misinterprets return codes as rather obnoxious system errors ("access
  2630.     violation," for example).
  2631.   ---------------------------------------------------------------------------*/
  2632.     switch (err) {
  2633.         case PK_COOL:
  2634.             break;   /* life is fine... */
  2635.         case PK_WARN:
  2636.             Info(slide, 1, ((char *)slide, "n
  2637. [return-code %d:  warning error 
  2638. (e.g., failed CRC or unknown compression method)]n", err));
  2639.             break;
  2640.         case PK_ERR:
  2641.         case PK_BADERR:
  2642.             Info(slide, 1, ((char *)slide, "n
  2643. [return-code %d:  error in zipfile 
  2644. (e.g., cannot find local file header sig)]n", err));
  2645.             break;
  2646.         case PK_MEM:
  2647.         case PK_MEM2:
  2648.         case PK_MEM3:
  2649.         case PK_MEM4:
  2650.         case PK_MEM5:
  2651.             Info(slide, 1, ((char *)slide,
  2652.               "n[return-code %d:  insufficient memory]n", err));
  2653.             break;
  2654.         case PK_NOZIP:
  2655.             Info(slide, 1, ((char *)slide,
  2656.               "n[return-code %d:  zipfile not found]n", err));
  2657.             break;
  2658.         case PK_PARAM:   /* exit(PK_PARAM); gives "access violation" */
  2659.             Info(slide, 1, ((char *)slide, "n
  2660. [return-code %d:  bad or illegal parameters specified on command line]n",
  2661.               err));
  2662.             break;
  2663.         case PK_FIND:
  2664.             Info(slide, 1, ((char *)slide,
  2665.               "n[return-code %d:  no files found to extract/view/etc.]n",
  2666.               err));
  2667.             break;
  2668.         case PK_DISK:
  2669.             Info(slide, 1, ((char *)slide,
  2670.               "n[return-code %d:  disk full or other I/O error]n", err));
  2671.             break;
  2672.         case PK_EOF:
  2673.             Info(slide, 1, ((char *)slide, "n
  2674. [return-code %d:  unexpected EOF in zipfile (i.e., truncated)]n", err));
  2675.             break;
  2676.         case IZ_CTRLC:
  2677.             Info(slide, 1, ((char *)slide,
  2678.               "n[return-code %d:  you hit ctrl-C to terminate]n", err));
  2679.             break;
  2680.         case IZ_UNSUP:
  2681.             Info(slide, 1, ((char *)slide, "n
  2682. [return-code %d:  unsupported compression or encryption for all files]n",
  2683.               err));
  2684.             break;
  2685.         case IZ_BADPWD:
  2686.             Info(slide, 1, ((char *)slide,
  2687.               "n[return-code %d:  bad decryption password for all files]n",
  2688.               err));
  2689.             break;
  2690.         default:
  2691.             Info(slide, 1, ((char *)slide,
  2692.               "n[return-code %d:  unknown return-code (screw-up)]n", err));
  2693.             break;
  2694.     }
  2695. #endif /* RETURN_CODES */
  2696. /*---------------------------------------------------------------------------
  2697.     Return an intelligent status/severity level:
  2698.         $STATUS          $SEVERITY = $STATUS & 7
  2699.         31 .. 16 15 .. 3   2 1 0
  2700.                            -----
  2701.         VMS                0 0 0  0    Warning
  2702.         FACILITY           0 0 1  1    Success
  2703.         Number             0 1 0  2    Error
  2704.                  MESSAGE   0 1 1  3    Information
  2705.                  Number    1 0 0  4    Severe (fatal) error
  2706.     0x7FFF0000 was chosen (by experimentation) to be outside the range of
  2707.     VMS FACILITYs that have dedicated message numbers.  Hopefully this will
  2708.     always result in silent exits--it does on VMS 5.4.  Note that the C li-
  2709.     brary translates exit arguments of zero to a $STATUS value of 1 (i.e.,
  2710.     exit is both silent and has a $SEVERITY of "success").
  2711.   ---------------------------------------------------------------------------*/
  2712.     severity = (err == PK_WARN) ? 1 :           /* warn  */
  2713.                (err == 2 ||                     /* error */
  2714.                 (err >= 9 && err <= 11) ||      /*  ...  */
  2715.                 (err >= 80 && err <= 82)) ? 2 : /*  ...  */
  2716.                4;                               /* fatal */
  2717.     exit(                                       /* $SEVERITY:              */
  2718.          (err == PK_COOL) ? 1 :                 /*   success               */
  2719.          (0x7FFF0000 | (err << 4) | severity)   /*   warning, error, fatal */
  2720.         );
  2721. } /* end function return_VMS() */
  2722. #ifdef MORE
  2723. int screenlines(void)
  2724. {
  2725.     /*
  2726.      * For VMS v5.x:
  2727.      *   IO$_SENSEMODE/SETMODE info:  Programming, Vol. 7A, System Programming,
  2728.      *     I/O User's: Part I, sec. 8.4.1.1, 8.4.3, 8.4.5, 8.6
  2729.      *   sys$assign(), sys$qio() info:  Programming, Vol. 4B, System Services,
  2730.      *     System Services Reference Manual, pp. sys-23, sys-379
  2731.      *   fixed-length descriptor info:  Programming, Vol. 3, System Services,
  2732.      *     Intro to System Routines, sec. 2.9.2
  2733.      * GRR, 15 Aug 91 / SPC, 07 Aug 1995
  2734.      */
  2735. #ifndef OUTDEVICE_NAME
  2736. #define OUTDEVICE_NAME  "SYS$OUTPUT"
  2737. #endif
  2738.     static int scrnlines = -1;
  2739.     static ZCONST struct dsc$descriptor_s OutDevDesc =
  2740.         {(sizeof(OUTDEVICE_NAME) - 1), DSC$K_DTYPE_T, DSC$K_CLASS_S,
  2741.          OUTDEVICE_NAME};
  2742.      /* {dsc$w_length, dsc$b_dtype, dsc$b_class, dsc$a_pointer}; */
  2743.     short  OutDevChan, iosb[4];
  2744.     long   status;
  2745.     struct tt_characts
  2746.     {
  2747.         uch class, type;
  2748.         ush pagewidth;
  2749.         uch ttcharsbits[3];
  2750.         uch pagelength;
  2751.     }      ttmode;              /* total length = 8 bytes */
  2752.     if (scrnlines < 0)
  2753.     {
  2754.         /* assign a channel to standard output */
  2755.         status = sys$assign(&OutDevDesc, &OutDevChan, 0, 0);
  2756.         if (status & 1)
  2757.         {
  2758.             /* use sys$qiow and the IO$_SENSEMODE function to determine
  2759.              * the current tty status.
  2760.              */
  2761.             status = sys$qiow(0, OutDevChan, IO$_SENSEMODE, &iosb, 0, 0,
  2762.                               &ttmode, sizeof(ttmode), 0, 0, 0, 0);
  2763.             /* deassign the output channel by way of clean-up */
  2764.             (void) sys$dassgn(OutDevChan);
  2765.         }
  2766.         scrnlines = ( ( (status & 1) &&
  2767.                         ((status = iosb[0]) & 1) &&
  2768.                         (ttmode.pagelength >= 5)
  2769.                       )
  2770.                      ? (int) (ttmode.pagelength)        /* TT device value */
  2771.                      : (24) );                          /* VT 100 default  */
  2772.     }
  2773.     return (scrnlines);
  2774. }
  2775. #endif /* MORE */
  2776. #ifndef SFX
  2777. /************************/
  2778. /*  Function version()  */
  2779. /************************/
  2780. void version(__G)
  2781.     __GDEF
  2782. {
  2783.     int len;
  2784. #ifdef VMS_VERSION
  2785.     char buf[40];
  2786. #endif
  2787. #ifdef __DECC_VER
  2788.     char buf2[40];
  2789.     int  vtyp;
  2790. #endif
  2791. /*  DEC C in ANSI mode does not like "#ifdef MACRO" inside another
  2792.     macro when MACRO is equated to a value (by "#define MACRO 1").   */
  2793.     len = sprintf((char *)slide, LoadFarString(CompiledWith),
  2794. #ifdef __GNUC__
  2795.       "gcc ", __VERSION__,
  2796. #else
  2797. #  if defined(DECC) || defined(__DECC) || defined (__DECC__)
  2798.       "DEC C",
  2799. #    ifdef __DECC_VER
  2800.       (sprintf(buf2, " %c%d.%d-%03d",
  2801.                ((vtyp = (__DECC_VER / 10000) % 10) == 6 ? 'T' :
  2802.                 (vtyp == 8 ? 'S' : 'V')),
  2803.                __DECC_VER / 10000000,
  2804.                (__DECC_VER % 10000000) / 100000, __DECC_VER % 1000), buf2),
  2805. #    else
  2806.       "",
  2807. #    endif
  2808. #  else
  2809. #  ifdef VAXC
  2810.       "VAX C", "",
  2811. #  else
  2812.       "unknown compiler", "",
  2813. #  endif
  2814. #  endif
  2815. #endif
  2816. #ifdef VMS_VERSION
  2817. #  if defined(__alpha)
  2818.       "OpenVMS",   /* version has trailing spaces ("V6.1   "), so truncate: */
  2819.       (sprintf(buf, " (%.4s for Alpha)", VMS_VERSION), buf),
  2820. #  else /* VAX */
  2821.       (VMS_VERSION[1] >= '6') ? "OpenVMS" : "VMS",
  2822.       (sprintf(buf, " (%.4s for VAX)", VMS_VERSION), buf),
  2823. #  endif
  2824. #else
  2825.       "VMS",
  2826.       "",
  2827. #endif /* ?VMS_VERSION */
  2828. #ifdef __DATE__
  2829.       " on ", __DATE__
  2830. #else
  2831.       "", ""
  2832. #endif
  2833.     );
  2834.     (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
  2835. } /* end function version() */
  2836. #endif /* !SFX */
  2837. #endif /* VMS */