home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / ckc095.zip / ckvfio.c < prev    next >
C/C++ Source or Header  |  1989-08-31  |  33KB  |  1,136 lines

  1. /* DEC/CMS REPLACEMENT HISTORY, Element CKVFIO.C */
  2. /* *5    29-AUG-1989 00:31:53 BUDA "Add code to check for system() function by version of VAXC" */
  3. /* *4    12-JUN-1989 23:09:43 BUDA "Add encode logic" */
  4. /* *3    18-APR-1989 23:36:23 BUDA "#ifdef chkfn and code gtdir for V4" */
  5. /*  2U1  18-APR-1989 22:12:00 BUDA "Work on attribute packet" */
  6. /* *2    16-APR-1989 17:57:21 BUDA "Fix remote command failure" */
  7. /* *1    11-APR-1989 22:55:40 BUDA "Initial creation" */
  8. /* DEC/CMS REPLACEMENT HISTORY, Element CKVFIO.C */
  9. char *ckzsys = " VAX/VMS";
  10.  
  11. /* C K V F I O  --  Kermit file system support for VAX/VMS */
  12.  
  13. /* Stew Rubenstein, Harvard University Chemical Labs */
  14. /*  (c) 1985 President and Fellows of Harvard College  */
  15. /*  Based on CKZUNX.C, 4.1(015) 28 Feb 85 */
  16. /* Also, Martin Minow (MM), Digital Equipment Corporation, Maynard MA */
  17. /* Also, Dan Schullman (DS), Digital Equipment Corporation, Maynard MA */
  18. /* Adapted from ckufio.c, by... */
  19. /* F. da Cruz (FdC), Columbia University Center for Computing Activities */
  20.  
  21. /* Edit history
  22.  * 003 20-Mar-85 MM  fixed fprintf bug in zsout.c
  23.  * 004 21-Mar-84 MM  create text files in variable-stream.
  24.  * 005  8-May-85 MM  filled in zkself (not tested), fixed other minor bugs
  25.  * 006  5-Jul-85 DS  handle version number in zltor, zrtol
  26.  * 007 11-Jul-85 FdC fix zclose() to give return codes
  27.  * 008 19-Mar-86 FdC Fix system() for "!", zopeni() for REMOTE commands.
  28.  * 008 17-Sep-87 FdC Define PWDCMD.
  29.  * 090 (???)
  30.  * 010 24-Jan-88 FdC Add zgtdir() function, even tho it doesn't work...
  31.  * 011 14-Feb-89 mab Make zgtdir() work in V2/V3 C envirements,
  32.  *             Make zkself work using delprc() using Will Wood's changes.
  33.  * 012 26-Feb-89 mab Add function that searches for kermit.ini file in various
  34.  *                   ways
  35.  * 013 05-Mar-89 mab Add Barry Archers enhancements/fixes.
  36.  * 014 15-Mar-89 mab Check for non-null data, not array of pointers in
  37.  *                   zkermini
  38.  * 015 04-Apr-89 mab Add latent support for attribute packet.  Clean up
  39.  *             file name translation code.
  40.  * 016 05-Apr-89 mab Add PWP code to optimize packetizing.
  41.  * 017 16-Apr-89 mab PWP changes broke REMOTE command.  Fixed.
  42.  * 018 18-Apr-89 mab #ifdef chkfn.  This removes a lot of overhead.
  43.  *             Add code to gtdir() for V4.x.
  44.  * 019 12-Jun-89 mab Add PWP's encode logic
  45.  * 020 09-Jul-89 mab Add logic to check for system() availability
  46.  * 021 10-Jul-89 mab Fix SHOW USER USERNAME.  Added space after 'SHOW USER'.
  47.  */
  48.  
  49. /* Definitions of some VMS system commands */
  50.  
  51. char *DIRCMD = "DIRECTORY ";        /* For directory listing */
  52. char *DELCMD = "DELETE ";        /* For file deletion */
  53. char *TYPCMD = "TYPE ";            /* For typing a file */
  54. char *SPACMD = "DIRECTORY/TOTAL/SIZE=ALL "; /* Space/quota of cur dir */
  55. char *SPACM2 = "DIRECTORY/TOTAL/SIZE=ALL "; /* Space/quota of cur dir */
  56. char *WHOCMD = "SHOW USERS ";        /* For seeing who's logged in */
  57. char *PWDCMD = "SHOW DEFAULT ";        /* For seeing current directory */
  58.  
  59.  
  60. /*
  61.   Functions (n is one of the predefined file numbers from ckermi.h):
  62.  
  63.    zopeni(n,name)   -- Opens an existing file for input.
  64.    zopeno(n,name)   -- Opens a new file for output.
  65.    zclose(n)        -- Closes a file.
  66.    zchin(n)         -- Gets the next character from an input file.
  67.    zsout(n,s)       -- Write a null-terminated string to output file, buffered.
  68.    zsoutl(n,s)      -- Like zsout, but appends a line terminator.
  69.    zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
  70.    zchout(n,c)      -- Add a character to an output file, unbuffered.
  71.    zchki(name)      -- Check if named file exists and is readable, return size.
  72.    zchko(name)      -- Check if named file can be created.
  73.    znewn(name,s)    -- Make a new unique file name based on the given name.
  74.    zdelet(name)     -- Delete the named file.
  75.    zxpand(string)   -- Expands the given wildcard string into a list of files.
  76.    znext(string)    -- Returns the next file from the list in "string".
  77.    zxcmd(cmd)       -- Execute the command in a lower fork.
  78.    zclosf()         -- Close input file associated with zxcmd()'s lower fork.
  79.    zrtol(n1,n2)     -- Convert remote filename into local form.
  80.    zltor(n1,n2)     -- Convert local filename into remote form.
  81.    zchdir(dirnam)   -- Change working directory.
  82.    zhome()          -- Return pointer to home directory name string.
  83.    zkself()         -- Log self out
  84.    zsattr(struc zattr *) -- Return attributes for file which is being sent.
  85.    zkermini(n1,n2)  -- Find kermit.ini using default scanning process
  86.  */
  87.  
  88.  
  89.  
  90. /* Includes */
  91.  
  92. #include "ckcker.h"
  93. #include "ckcdeb.h"
  94. #include "ckvvms.h"
  95. #include <stdio.h>
  96. #include <stat.h>
  97. #include <ctype.h>
  98. #include <rms.h>
  99. #include <descrip.h>
  100. #include <dvidef.h>
  101. #include <iodef.h>
  102. #include <jpidef.h>
  103. #include <errno.h>
  104. #include <signal.h>
  105.  
  106. #define MAXWLD 500            /* Maximum wildcard filenames */
  107.  
  108. /* (PWP) external def. of things used in buffered file input and output */
  109. extern CHAR zinbuffer[], zoutbuffer[];
  110. extern CHAR *zinptr, *zoutptr;
  111. extern int zincnt, zoutcnt;
  112. extern int binary;
  113.  
  114. /* Declarations */
  115.  
  116. FILE *fp[ZNFILS] = {             /* File pointers */
  117.     NULL, NULL, NULL, NULL, NULL, NULL, NULL };
  118.  
  119. static long iflen = -1;            /* Input file length */
  120. static long oflen = -1;            /* Output file length */
  121. static int fcount;            /* Number of files in wild group */
  122. static char cwdbuf[NAM$C_MAXRSS];
  123. static struct iosb_struct tmpiosb;    /* For QIOW */
  124.  
  125. extern unsigned long vms_status;        /* Used by CHECK_ERR */
  126.  
  127. char *getenv(), *strcpy();        /* For finding home directory */
  128.  
  129. static char *mtchs[MAXWLD],        /* Matches found for filename */
  130.      **mtchptr;                /* Pointer to current match */
  131.  
  132. static unsigned
  133.        int input_mbxchn,
  134.        output_mbxchn,
  135.        child_pid = 0;
  136.  
  137.  
  138.  
  139. /***  Z K S E L F --  Log self out  ***/
  140.  
  141. /*** (someone please check if this works in VMS) ***/
  142.  
  143. zkself() {
  144.     unsigned long int rms_s;
  145.  
  146.     rms_s = SYS$DELPRC(0,0);
  147.     exit(rms_s == SS$_NORMAL);
  148. }
  149.  
  150.  
  151. /*  Z O P E N I  --  Open an existing file for input. */
  152.  
  153. zopeni(n,name) int n; char *name; {
  154.     debug(F111," zopeni",name,n);
  155.     debug(F101,"  fp","",(int) fp[n]);
  156.     if (chkfn(n) != 0) return(0);
  157.     if (n == ZSYSFN) {            /* Input from a system function? */
  158.         debug(F110," invoking zxcmd",name,0);
  159.     return(zxcmd(name));        /* Try to fork the command */
  160.     }
  161.     if (n == ZSTDIO) {            /* Standard input? */
  162.     if (isatty(0)) {
  163.         ermsg("?Terminal input not allowed\n");
  164.         debug(F110,"zopeni: attempts input from unredirected stdin","",0);
  165.         return(0);
  166.     }
  167.     fp[ZIFILE] = stdin;
  168.     return(1);
  169.     }
  170.     zincnt = 0;            /* (PWP) reset input buffer */
  171.     fp[n] = fopen(name,"r");        /* Real file. */
  172.     debug(F111," zopeni", name, (int) fp[n]);
  173.     if (fp[n] == NULL) perror("zopeni");
  174.     return((fp[n] != NULL) ? 1 : 0);
  175. }
  176.  
  177. /*  Z O P E N O  --  Open a new file for output.  */
  178.  
  179. zopeno(n,name) int n; char *name; {
  180.  
  181.     int fildes;
  182.  
  183.     debug(F111," zopeno",name,n);
  184.     if (chkfn(n) != 0) return(0);
  185.  
  186.     zoutcnt = 0;        /* (PWP) reset output buffer */
  187.     zoutptr = zoutbuffer;
  188.  
  189.     if ((n == ZCTERM) || (n == ZSTDIO)) {   /* Terminal or standard output */
  190.     fp[ZOFILE] = stdout;
  191.     debug(F101," fp[]=stdout", "", (int) fp[n]);
  192.     return(1);
  193.     }
  194.     /*
  195.      * Create "binary" output files as fixed-block 512 byte records.
  196.      * This should permit copying task images.  It is rumored that
  197.      * Vax C will null-fill an incomplete final block.
  198.      *
  199.      * Create all debugging files (and normal output files) in
  200.      * "vanilla" RMS -- variable length, implicit carriage control.
  201.      * This way, old brain-damaged programs aren't suprised by
  202.      * bizarre Unix-styled files.
  203.      */
  204.     if (n == ZOFILE && binary)
  205.     fildes = creat(name, 0, "mrs=512", "rfm=fix");
  206.     else
  207.     if (n == ZDFILE || n == ZSFILE)
  208. /* debugging file only, make it stream.  This makes the unbuffered part work */
  209.         fildes = creat(name, 0, "rat=cr", "rfm=stm"); 
  210.     else
  211.         fildes = creat(name, 0, "rat=cr", "rfm=var");
  212.     fp[n] = (fildes == -1) ? NULL : fdopen(fildes, "w");
  213.     if (fp[n] == NULL) perror(name);        /* +1, print useful msg    */
  214. #ifdef mab
  215. /*
  216.  * This code does not work correctly.  Currently for each character that
  217.  * is immediatly written, it is considered a record.   This should be
  218.  * looked at in the future.
  219.  */
  220.     if (n == ZDFILE && isatty(fileno(fp[n])))
  221.     setbuf(fp[n],NULL); /* Make debugging file unbuffered */
  222. #endif
  223. #ifdef mab
  224.     zoutcnt = 0;        /* (PWP) reset output buffer */
  225.     zoutptr = zoutbuffer;
  226. #endif
  227.     debug(F101, " fp[n]", "", (int) fp[n]);
  228.     return((fp[n] != NULL) ? 1 : 0);
  229. }
  230.  
  231. /*  Z C L O S E  --  Close the given file.  */
  232.  
  233. /*  Returns 0 if arg out of range, 1 if successful, -1 if close failed.  */
  234.  
  235. zclose(n) int n; {
  236.     int x=0, x2=0;
  237.  
  238.     debug(F101," zclose","",n);
  239.     if (chkfn(n) < 1) return(0);
  240.  
  241.     if ((n == ZOFILE) && (zoutcnt > 0))    /* (PWP) output leftovers */
  242.     x2 = zoutdump();
  243.  
  244.     if ((child_pid) && ((!fp[n]) || (n == ZSYSFN)))
  245.     x = zclosf();
  246.     else {
  247.     if ((fp[n] != stdout) && (fp[n] != stdin)) x = fclose(fp[n]);
  248.     fp[n] = NULL;
  249.     }
  250.     iflen = -1;                /* Invalidate file length */
  251.     debug(F101,"  x","",x);
  252.     debug(F101,"  x2","",x2);
  253.     if (x == EOF)        /* if we got a close error */
  254.     return (-1);
  255.     else if (x2 < 0)        /* or an error flushing the last buffer */
  256.     return (-1);        /* then return an error */
  257.     else
  258.     return (1);
  259. }
  260.  
  261. static int subprocess_input = 0, sub_count;
  262. static char *sub_ptr, sub_buf[SUB_BUF_SIZE];
  263.  
  264. get_subprc_line() {
  265.     struct iosb_struct subiosb;
  266.  
  267.     if ((vms_status = SYS$QIOW(QIOW_EFN, output_mbxchn, IO$_READVBLK,
  268.     &subiosb, 0, 0, sub_buf, sizeof(sub_buf), 0, 0, 0, 0)) != SS$_NORMAL)
  269.         return(-1);
  270.     if (subiosb.status != SS$_NORMAL) return(-1);
  271.     if (subiosb.size == 29
  272.      && strncmp(sub_buf, ">>> END OF KERMIT COMMAND <<<",
  273.         subiosb.size) == 0) {
  274.     subprocess_input = 0;
  275.     return(-1);
  276.     }
  277.     sub_buf[subiosb.size] = '\n';
  278.     sub_buf[subiosb.size + 1] = '\0';
  279.     sub_count = subiosb.size + 1;
  280.     sub_ptr = sub_buf;
  281.     return(0);
  282. }
  283.  
  284. /*  Z C H I N  --  Get a character from the input file.  */
  285.  
  286. /*  Returns -1 if EOF, 0 otherwise with character returned in argument  */
  287.  
  288. zchin(n,c) int n; char *c; {
  289.     int a;
  290.  
  291. #ifdef DEBUG
  292.     if (chkfn(n) < 1) return(-1);
  293. #endif
  294.  
  295.     if (n == ZIFILE && subprocess_input) {
  296.     if (--sub_count < 0)
  297.         if (get_subprc_line()) return(-1);
  298.     a = *sub_ptr++;
  299.     } else {
  300.     /* (PWP) Just in case this gets called when it shoudn't */
  301.     return (zminchar());
  302. /*    a = getc(fp[n]);    */
  303.     }
  304.     if (a == EOF) return(-1);
  305.     *c = (unsigned char)a;
  306.     return(0);
  307. }
  308.  
  309. /*
  310.  * (PWP) (re)fill the buffered input buffer with data.  All file input
  311.  * should go through this routine, usually by calling the zminchar()
  312.  * macro (in ckcker.h).
  313.  */
  314.  
  315. zinfill() {
  316.  
  317.     if (subprocess_input) {
  318.     if (get_subprc_line()) return(-1);
  319. /*
  320.  * The size problem should never happen.  sub_buf of a size greater then
  321.  * 1k is highly unlikely to be needed.
  322.  */
  323.     if (INBUFSIZE < SUB_BUF_SIZE) {
  324.         fprintf(stderr,"zinfill: sub_buf too large for zinbuffer");
  325.         exit();
  326.     }
  327.     zinptr = sub_buf;
  328.     zincnt = sub_count;
  329.     } else {
  330.         zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
  331.         if (zincnt == 0) return (-1); /* end of file */
  332.     zinptr = zinbuffer; /* set pointer to beginning, (== &zinbuffer[0]) */
  333.     }
  334.     zincnt--;            /* one less char in buffer */
  335.     return((int)(*zinptr++) & 0377); /* because we return the first */
  336. }       
  337.  
  338.  
  339.  
  340. /*  Z S O U T  --  Write a string to the given file, buffered.  */
  341.  
  342. zsout(n,s) int n; char *s; {
  343. #ifdef DEBUG
  344.     if (chkfn(n) < 1) return(-1);
  345. #endif
  346.     fputs(s, fp[n]);            /* Don't use fprintf here MM */
  347.     return(0);
  348. }
  349.  
  350. /*  Z S O U T L  --  Write string to file, with line terminator, buffered  */
  351.  
  352. zsoutl(n,s) int n; char *s; {
  353. #ifdef DEBUG
  354.     if (chkfn(n) < 1) return(-1);
  355. #endif
  356.     fputs(s, fp[n]);            /* Don't use fprintf MM */
  357.     putc('\n', fp[n]);
  358.     return(0);
  359. }
  360.  
  361. /*  Z S O U T X  --  Write x characters to file, unbuffered.  */
  362.  
  363. zsoutx(n,s,x) int n, x; char *s; {
  364. #ifdef DEBUG
  365.     if (chkfn(n) < 1) return(-1);
  366. #endif
  367.     return(write(fileno(fp[n]),s,x));
  368. }
  369.  
  370.  
  371. /*  Z C H O U T  --  Add a character to the given file.  */
  372.  
  373. zchout(n,c) register int n; char c; {
  374. #ifdef DEBUG
  375.     if (chkfn(n) < 1) return(-1);
  376. #endif
  377.     if (n == ZSFILE)
  378.         return(write(fileno(fp[n]),&c,1)); /* Use unbuffered for session log */
  379.     else {
  380.     if (putc(c,fp[n]) == EOF)    /* If true, maybe there was an error */
  381.         return(ferror(fp[n])?-1:0);    /* Check to make sure */
  382.     else                /* Otherwise... */
  383.         return(0);            /* There was no error. */
  384.     }
  385. }
  386.  
  387. /* (PWP) buffered character output routine to speed up file IO */
  388. zoutdump()
  389. {
  390.     static char *partial_buf = NULL;
  391.     static char *partial_buf_pnt = NULL;
  392.     static int  partial_buf_cnt = 0;
  393.     char *vpnt, *tpnt;
  394.     int   vcnt,  tcnt;
  395.  
  396. /*
  397.  * Allocate buffer for temp usage when an overflow occurs.
  398.  */
  399.     if (partial_buf == NULL) {
  400.     partial_buf = calloc(PARTIAL_BUF_SIZE,sizeof(char));
  401.     if (partial_buf == NULL) {
  402.        perror("%CKERMIT-F-CKVFIO, Cannot allocate memory for partial_buf");
  403.         exit();
  404.     }
  405.     partial_buf_pnt = partial_buf;
  406.     }
  407.  
  408.     debug(F111," zoutdump","zoutcnt",zoutcnt);
  409.     debug(F101,"  partial_buf_cnt","",partial_buf_cnt);
  410. /*
  411.  * IF a negative value is passed, then act like everything is OK...
  412.  * If a zero then check and see if anything is in the partial buffer
  413.  * and flush if needed.
  414.  */
  415.     if (zoutcnt <= 0 && !partial_buf_cnt) return (0); /* nothing to output */
  416.  
  417.     if (!binary) { /* Ascii */
  418. /*
  419.  * If there data buffered up from the previous call, then we need
  420.  * to copy data from the new buffer onto the tail of the old one
  421.  * and write it out.  (Copy only up to LF).
  422.  */
  423.     vpnt = zoutbuffer;        /* Point to beginning of buffer */
  424.     if (partial_buf_cnt) {
  425.         while (zoutcnt > 0 && *vpnt != LF) {
  426.         *partial_buf_pnt++ = *vpnt++;
  427.         zoutcnt--;
  428.         partial_buf_cnt++;
  429.         if (partial_buf_cnt > PARTIAL_BUF_SIZE) {
  430.         fprintf(stderr,"%%CKERMIT-F-CKVFIO, Partial buffer overflows");
  431.             exit();
  432.         }
  433. /*
  434.  * Skip over the CR by bumping pointers
  435.  */
  436.         }
  437.         debug(F101,"  partial_buf_cnt","",partial_buf_cnt);
  438.         debug(F101,"  zoutcnt","",zoutcnt);
  439.         if (!fwrite (partial_buf, partial_buf_cnt, 1, fp[ZOFILE])) {
  440.         debug(F101,"  ferror(partial)","",ferror(fp[ZOFILE]));
  441.         return(ferror(fp[ZOFILE])?-1:0); /* Check to make sure */
  442.         }
  443.         zoutcnt--;
  444.         vpnt++;
  445.         partial_buf_cnt = 0;
  446.         partial_buf_pnt = partial_buf;
  447.     }
  448. /*
  449.  * Check and see if there are any more characters to write to the file.
  450.  * If there is not, we did them all in the previous code, exit fucntion.
  451.  */
  452.     if (zoutcnt < 1 && *vpnt != LF) {
  453.         zoutcnt = 0;        /* reset output buffer */
  454.         zoutptr = zoutbuffer;        
  455.         return(0);
  456.     }
  457.  
  458.     while(zoutcnt > 0) {
  459.         tcnt = 0;
  460.         tpnt = vpnt;
  461.  
  462. /*
  463.  * Find out size of next record.
  464.  */
  465.         while (tcnt != zoutcnt) {
  466.         tcnt++;
  467.         if (*tpnt == LF) break;
  468.         tpnt++;
  469.         }
  470. /*
  471.  * If the last byte is a CR, then we have a complete record and we should
  472.  * write the record to the file.
  473.  */
  474.         if (tcnt != zoutcnt) {
  475.         if (!fwrite (vpnt, tcnt, 1, fp[ZOFILE])) {
  476.             debug(F101,"  ferror(partial)","",ferror(fp[ZOFILE]));
  477.             return(ferror(fp[ZOFILE])?-1:0); /* Check to make sure */
  478.         }
  479. /*
  480.  * Make sure we bump over the CR. when updating the variables.
  481.  */
  482.         vpnt = ++tpnt;
  483.         zoutcnt -= tcnt;
  484.         } else {
  485. /*
  486.  * Store overflow data into partial buffer for later writing.
  487.  */
  488.         while (zoutcnt > 0) {
  489.             *partial_buf_pnt++ = *vpnt++;
  490.             partial_buf_cnt++;
  491.             zoutcnt--;
  492.         }
  493.         }
  494.     }
  495.     zoutcnt = 0;
  496.     zoutptr = zoutbuffer;
  497.     return(0);
  498.     } else { /* binary */
  499.  
  500.     if (fwrite (zoutbuffer, zoutcnt, 1, fp[ZOFILE])) {
  501.         zoutcnt = 0;        /* reset output buffer */
  502.         zoutptr = zoutbuffer;
  503.         return(0);        /* things worked OK */
  504.     } else {
  505.         debug(F101,"  ferror","",ferror(fp[ZOFILE]));
  506.         return(ferror(fp[ZOFILE])?-1:0); /* Check to make sure */
  507.     }
  508.     }
  509. }
  510.  
  511.  
  512.  
  513.  
  514. /*  C H K F N  --  Internal function to verify file number is ok  */
  515.  
  516. /*
  517.  Returns:
  518.   -1: File number n is out of range
  519.    0: n is in range, but file is not open
  520.    1: n in range and file is open
  521. */
  522. chkfn(n) int n; {
  523.     switch (n) {
  524.     case ZCTERM:
  525.     case ZSTDIO:
  526.     case ZIFILE:
  527.     case ZOFILE:
  528.     case ZDFILE:
  529.     case ZTFILE:
  530.     case ZPFILE:
  531.     case ZSFILE: break;
  532.     case ZSYSFN:                /* System function's */
  533.         return(0);
  534.     default:
  535.         debug(F101,"chkfn: file number out of range","",n);
  536.         fprintf(stderr,"?File number out of range - %d\n",n);
  537.         return(-1);
  538.     }
  539.     return( (fp[n] == NULL) ? 0 : 1 );
  540. }
  541.  
  542.  
  543.  
  544. /*  Z C H K I  --  Check if input file exists and is readable  */
  545.  
  546. /*
  547.   Returns:
  548.    >= 0 if the file can be read (returns the size).
  549.      -1 if file doesn't exist or can't be accessed,
  550.      -2 if file exists but is not readable (e.g. a directory file).
  551.      -3 if file exists but protected against read access.
  552. */
  553. /*
  554.  For Berkeley Unix, a file must be of type "regular" to be readable.
  555.  Directory files, special files, and symbolic links are not readable.
  556. */
  557. long
  558. zchki(name) char *name; {
  559.     struct stat buf;
  560.     int x; long y; 
  561.  
  562.     x = stat(name,&buf);
  563.     if (x < 0) {
  564.     debug(F111,"zchki stat fails",name,errno);
  565.     return(-1);
  566.     }
  567.     x = buf.st_mode & S_IFMT;        /* Isolate file format field */
  568.     if ((x != 0) && (x != S_IFREG)) {
  569.     debug(F111,"zchki skipping:",name,x);
  570.     return(-2);
  571.     }
  572.     debug(F111,"zchki stat ok:",name,x);
  573.  
  574.     if ((x = access(name,R_OK)) < 0) {     /* Is the file accessible? */
  575.     debug(F111," access failed:",name,x); /* No */
  576.         return(-3);            
  577.     } else {
  578.     iflen = buf.st_size;
  579.     debug(F111," access ok:",name,(int) iflen); /* Yes */
  580.     return( (iflen > -1) ? iflen : 0 );
  581.     }
  582. }
  583.  
  584.  
  585.  
  586. /*  Z C H K O  --  Check if output file can be created  */
  587.  
  588. /*
  589.  Returns -1 if write permission for the file would be denied, 0 otherwise.
  590. */
  591. zchko(name) char *name; {
  592.     return(0);                /* Always creates new version */
  593. }
  594.  
  595.  
  596.  
  597. /*  Z D E L E T  --  Delete the named file.  */
  598.  
  599. zdelet(name) char *name; {
  600.     return(delete(name));
  601. }
  602.  
  603.  
  604. /*  Z R T O L  --  Convert remote filename into local form  */
  605.  
  606. zrtol(name,name2) char *name, *name2; {
  607.     int count = 9, vflag = 0;
  608.     char *cp, c;
  609.     static char *spcl_set = "_-$[]<>:.\";";
  610.  
  611.     for (cp=name2; c = *name; name++) {
  612.     if (islower(c)) c = toupper(c);
  613.     if (!isalnum(c) &&
  614.         !strchr(spcl_set,c)) c = 'X';
  615.     *cp++ = c;
  616.     }
  617.  
  618. #ifdef VMS_V3
  619.     count = 9;
  620.     vflag = 0;
  621.     for ( cp = name2; *name; name++ ) {
  622.     switch (*name) {
  623.         case '.':            /* File type */
  624.         if (!vflag) {
  625.             vflag++;        /* 
  626.                 count = 3;        /* Max length for this field */
  627.             *cp++ = '.';
  628.             break;
  629.         }
  630.         case ';':            /* Version */
  631.             count = 5;
  632.         *cp++ = ';';
  633.         break;
  634.         default:
  635.             if (count > 0 && isalnum(*name)) {
  636.             --count;
  637.             *cp++ = islower(*name) ? toupper(*name) : *name;
  638.         }
  639.         break;
  640.     }
  641.     }
  642. #endif
  643.  
  644.     *cp = '\0';                /* End of name */
  645.     debug(F110,"zrtol: ",name2,0);
  646. }
  647.  
  648.  
  649. /*  Z L T O R  --  Convert filename from local format to common form.   */
  650.  
  651. zltor(name,name2) char *name, *name2; {
  652.     char *cp, *pp;
  653.  
  654. /*
  655.  * Copy name to output string
  656.  */
  657.     strcpy(name2,name);
  658. /*
  659.  * Parse the filename and type, with the defualt filename of "X"
  660.  */
  661.     parse_fname(name2, 100, "X", PARSE_NAME|PARSE_TYPE);
  662.  
  663. #ifdef VMS_V3
  664.     for (cp = pp = name; *cp ; cp++) {    /* strip path name */
  665.         if (*cp == ']' || *cp == ':') {
  666.         pp = cp;
  667.         pp++;
  668.     }
  669.     }
  670.     for ( ; --cp >= pp; ) {        /* From end to beginning */
  671.     if (!isdigit(*cp)) {        /* if not numeric, then */
  672.         if (*cp == '-') --cp;    /* if minus sign, skip over, or */
  673.         if (*cp == ';') *cp = '\0'; /* if version delim, make end */
  674.         break;
  675.     }
  676.     }
  677.     cp = name2;                /* If nothing before dot, */
  678.     if (*pp == '.') *cp++ = 'X';    /* insert 'X' */
  679.     strcpy(cp,pp);
  680. #endif
  681.  
  682.     debug(F110,"zltor: ",name2,0);
  683. }    
  684.  
  685.  
  686. /*  Z C H D I R  --  Change directory  */
  687.  
  688. zchdir(dirnam) char *dirnam; {
  689.  
  690.     char   *zgtdir();
  691.     char   dir_buff[NAM$C_MAXRSS];
  692.     int    status;
  693.  
  694.     if (*dirnam == '\0')
  695.         strcpy(dirnam,getenv("HOME"));            /* default to current dir */
  696.  
  697.     status = chdir(dirnam);         /* change first in parent proc */
  698.         /* then change it in the child process -> keep in synch! */
  699.     if ((child_pid != 0) && (status == 0)) {
  700.     /* construct command for child proc */
  701.     strcpy(dir_buff,"$ set def ");
  702.     strcat(dir_buff,zgtdir());
  703.     SYS$QIOW(QIOW_EFN, input_mbxchn, IO$_WRITEVBLK | IO$M_NOW,
  704.         &tmpiosb, 0, 0, dir_buff, strlen(dir_buff), 0, 0, 0, 0);
  705.        }
  706.     return(status == 0);
  707. }
  708.  
  709.  
  710. /*  Z H O M E  --  Return pointer to user's home directory  */
  711.  
  712. char *
  713. zhome() {
  714.     return(getenv("HOME"));
  715. }
  716.  
  717. /*  Z G T D I R  --  Return pointer to user's current directory  */
  718.  
  719. char *
  720. zgtdir() {
  721. #ifdef VMS_V40 | VMS_V42 | VMS_V44
  722.  
  723.     static char *gtdir_buf = 0;
  724.     static char sysdisk[] = "SYS$DISK";
  725.     char tmp_buf[NAM$C_MAXRSS+1];
  726.     struct dsc$descriptor_s
  727.     tmp_buf_dsc = {sizeof(tmp_buf),DSC$K_DTYPE_T,DSC$K_CLASS_S,&tmp_buf},
  728.     sysdisk_dsc = {sizeof(sysdisk)-1,DSC$K_DTYPE_T,DSC$K_CLASS_S,&sysdisk};
  729.     unsigned short int buf_len;
  730.  
  731. /*
  732.  * Allocate buffer dynamically, first time through.  This makes the image
  733.  * smaller.
  734.  */
  735.     if (!gtdir_buf) gtdir_buf = malloc(NAM$C_MAXRSS+1);
  736.  
  737. /*
  738.  * Translate device name.
  739.  */
  740.  
  741.     LIB$SYS_TRNLOG(    &sysdisk_dsc,
  742.             &buf_len,
  743.             &tmp_buf_dsc,
  744.             0,
  745.             0,
  746.             0);
  747.     tmp_buf[buf_len] = '\0';
  748.     strcpy(gtdir_buf,tmp_buf);
  749.  
  750. /*
  751.  * Get directory name.
  752.  */
  753.     SYS$SETDDIR(    0,    /* New dir addr */
  754.             &buflen,/* length addr */
  755.             &tmp_buf_dsc);
  756.     tmp_buf[buf_len] = '\0';
  757.     strcat(gtdir_buf,tmp_buf);
  758.  
  759.     return(>dir_buf);  /* Can't seem to make LINK find getcwd()... */
  760. #else
  761.     char *getcwd();
  762.     char *buf;
  763.  
  764.     buf = cwdbuf;
  765.     return(getcwd(buf,100));
  766. #endif
  767. }
  768.  
  769.  
  770. /*  Z X C M D -- Run a system command so its output can be read like a file */
  771.  
  772. zxcmd(comand) char *comand; {
  773.     char input_mbxnam[10], output_mbxnam[10];
  774.     char cmdbuf[200];
  775.  
  776.     if (child_pid == 0) {
  777.         struct dsc$descriptor_s
  778.         inpdsc = {sizeof(input_mbxnam),DSC$K_DTYPE_T,
  779.             DSC$K_CLASS_S,&input_mbxnam}, 
  780.         outdsc = {sizeof(output_mbxnam),DSC$K_DTYPE_T,
  781.             DSC$K_CLASS_S,&output_mbxnam};
  782.         struct itmlst dvilst[] = 
  783.         {{sizeof(input_mbxnam),DVI$_DEVNAM,&input_mbxnam,0},
  784.         {0,0,0,0}};
  785.  
  786.         CHECK_ERR("zxcmd:SYS$CREMBX,I",SYS$CREMBX(0, &input_mbxchn, 0,
  787.         0, 0, 0,0));
  788.         CHECK_ERR("zxcmd:SYS$GETDVIW,I",SYS$GETDVIW(0, input_mbxchn, 0,
  789.         dvilst,&tmpiosb, 0, 0, 0));
  790.     debug(F110," zxcmd:input_mbxnam",input_mbxnam,0);
  791.  
  792.         CHECK_ERR("zxcmd:SYS$CREMBX,O",SYS$CREMBX(0, &output_mbxchn,
  793.         SUB_BUF_SIZE, 0, 0, 0, 0));
  794.         dvilst[0].len = sizeof(output_mbxnam);
  795.         dvilst[0].adr = &output_mbxnam;
  796.         CHECK_ERR("zxcmd:SYS$GETDVIW,O",SYS$GETDVIW(0, output_mbxchn, 0,
  797.         dvilst, &tmpiosb, 0, 0, 0));
  798.     debug(F110," zxcmd:output_mbxnam",output_mbxnam,0);
  799.  
  800.         CHECK_ERR("zxcmd:LIB$SPAWN",LIB$SPAWN(0, &inpdsc, &outdsc, &1,
  801.         0, &child_pid));
  802.     CHECK_ERR("zxcmd:SYS$QIOW1",SYS$QIOW(QIOW_EFN, input_mbxchn,
  803.         IO$_WRITEVBLK | IO$M_NOW, &tmpiosb, 0, 0, "$ SET NOON", 10,
  804.         0, 0, 0, 0));
  805.     }
  806.  
  807.     strcpy(cmdbuf, "$ ");
  808.     strcat(cmdbuf, comand);
  809.     debug(F110," zxcmd",cmdbuf,0);
  810.     CHECK_ERR("zxcmd:SYS$QIOW2",SYS$QIOW(QIOW_EFN, input_mbxchn,
  811.     IO$_WRITEVBLK | IO$M_NOW, &tmpiosb, 0, 0, cmdbuf, strlen(cmdbuf),
  812.     0, 0, 0, 0));
  813.     CHECK_ERR("zxcmd:SYS$QIOW3",SYS$QIOW(QIOW_EFN, input_mbxchn,
  814.     IO$_WRITEVBLK | IO$M_NOW, &tmpiosb, 0, 0,
  815.     "$ WRITE SYS$OUTPUT \">>> END OF KERMIT COMMAND <<<\"",
  816.         50, 0, 0, 0, 0));
  817.     subprocess_input = 1;
  818.     sub_count = 0;
  819.     return(1);
  820. }
  821.  
  822. /*  Z C L O S F  - close the suprocess output file.  */
  823.  
  824. zclosf() {
  825.     unsigned long int sys_s;
  826.  
  827.     if (child_pid) {
  828.     debug(F101,"zclosf child_pid =","",child_pid);
  829.     if (CHECK_ERR("zclosf: SYSDELPRC ",SYS$DELPRC(&child_pid,0)))
  830.         child_pid = 0;
  831.     }
  832.     fclose(fp[ZIFILE]);
  833.     fp[ZIFILE] = fp[ZSYSFN] = NULL;
  834.     return(1);
  835. }
  836.  
  837.  
  838.  
  839.  
  840. /*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
  841. /*
  842.   Returns the number of files that match fn1, with data structures set up
  843.   so that first file (if any) will be returned by the next znext() call.
  844. */
  845. zxpand(fn) char *fn; {
  846.     fcount = fgen(fn,mtchs,MAXWLD);    /* Look up the file. */
  847.     if (fcount > 0) {
  848.     mtchptr = mtchs;        /* Save pointer for next. */
  849.     }
  850.     debug(F111,"zxpand",mtchs[0],fcount);
  851.     return(fcount);
  852. }
  853.  
  854.  
  855. /*  Z N E X T  --  Get name of next file from list created by zxpand(). */
  856. /*
  857.  Returns >0 if there's another file, with its name copied into the arg string,
  858.  or 0 if no more files in list.
  859. */
  860. znext(fn) char *fn; {
  861.     if (fcount-- > 0) strcpy(fn,*mtchptr++);
  862.     else *fn = '\0';
  863.     debug(F111,"znext",fn,fcount+1);
  864.     return(fcount+1);
  865. }
  866.  
  867.  
  868. /*  Z N E W N  --  Make a new name for the given file  */
  869.  
  870. znewn(fn,s) char *fn, **s; {
  871.     static char buf[NAM$C_MAXRSS];
  872.  
  873.     strcpy(buf, fn);            /* Version numbers are handled by OS */
  874.     *s = buf;
  875. }
  876.  
  877. /*
  878.  * fgen:
  879.  *  This is the actual name generator.  It is passed a string,
  880.  *  possibly containing wildcards, and an array of character pointers.
  881.  *  It finds all the matching filenames and stores them into the array.
  882.  *  The returned strings are allocated from a static buffer local to
  883.  *  this module (so the caller doesn't have to worry about deallocating
  884.  *  them); this means that successive calls to fgen will wipe out
  885.  *  the results of previous calls.  This isn't a problem here
  886.  *  because we process one wildcard string at a time.
  887.  *
  888.  * Input: a wildcard string, an array to write names to, the
  889.  *        length of the array.
  890.  * Returns: the number of matches.  The array is filled with filenames
  891.  *          that matched the pattern.  If there wasn't enough room in the
  892.  *        array, -1 is returned.
  893.  */
  894. fgen(pat,resarry,len)
  895. char *pat,*resarry[];
  896. int len;
  897. {
  898.     struct dsc$descriptor_s
  899.     file_spec = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
  900.     result = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0},
  901.     deflt = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
  902.     unsigned long context = 0, status;
  903.     int count = 0;
  904.     char *def_str = "*.*";
  905.  
  906.     file_spec.dsc$w_length  = strlen(pat);
  907.     file_spec.dsc$a_pointer = pat;
  908.  
  909.     deflt.dsc$w_length  = sizeof(def_str)-1;
  910.     deflt.dsc$a_pointer = def_str;
  911.  
  912.     while (count < len
  913.        && (status = LIB$FIND_FILE(&file_spec, &result, &context, &deflt))
  914.         == RMS$_NORMAL) {
  915.         resarry[count] = malloc(result.dsc$w_length + 1);
  916.     strncpy(resarry[count], result.dsc$a_pointer, result.dsc$w_length);
  917.     resarry[count][result.dsc$w_length] = '\0';
  918.     count++;
  919.     }
  920. #ifdef DVI$_ALT_HOST_TYPE
  921.     LIB$FIND_FILE_END(&context);    /* Only on V4 and later */
  922. #endif
  923.     LIB$SFREE1_DD(&result);
  924.     if (status == RMS$_FNF) return(0);
  925.     if (status == RMS$_NMF) return(count);
  926.     return(-1);
  927. }
  928.  
  929. #ifdef VAXC023
  930. system(s)
  931. char *s;
  932. {
  933.     if (*s) {
  934.     zxcmd(s);
  935.     while (!get_subprc_line())
  936.         fputs(sub_buf, stdout);
  937.     putchar('\n');
  938.     } else {
  939.     LIB$SPAWN();
  940.     }
  941. }
  942. #endif
  943.  
  944. /*
  945.  * Find ini file.  If none is found, then return NULL string.
  946.  */
  947.  
  948. zkermini(char *s, int s_len, char *def)
  949. {
  950.     FILE fd;
  951.     struct dsc$descriptor_s
  952.         dsc_in = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
  953.         dsc_out = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0},
  954.         dsc_def = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
  955.     int max_len;
  956.     long unsigned int rms_s;
  957.     unsigned long find_file_context = 0;
  958.  
  959.     struct TRNLIST {
  960.     char *name;            /* Ascii */
  961.     unsigned char flag;        /* <> 0 to not use default */
  962.     } *p;
  963.  
  964.     static struct TRNLIST slist[] = {
  965.             {"ckermit_ini:", 0},
  966.             {"sys$login:",   0},
  967.             {"",             0}};
  968.  
  969.     p = slist;
  970.     while(*p->name) {
  971.  
  972.     dsc_in.dsc$w_length = strlen(p->name);    /* length of work area */
  973.     dsc_in.dsc$a_pointer = p->name;        /* Address of string */
  974.  
  975.     if (!(p->flag)) {
  976.             dsc_def.dsc$w_length = strlen(def);    /* length of work area */
  977.         dsc_def.dsc$a_pointer = def;    /* Address of string */
  978.     }
  979.     else {
  980.         dsc_def.dsc$w_length = 0;        /* length of work area */
  981.         dsc_def.dsc$a_pointer = 0;        /* Address of string */
  982.         }
  983.  
  984.     rms_s = LIB$FIND_FILE(
  985.                 &dsc_in,    /* File spec */
  986.                 &dsc_out,    /* Result file spec */
  987.                 &find_file_context, /* Context */
  988.                 &dsc_def,    /* Default file spec */
  989.                 0,        /* Related spec */
  990.                 0,        /* STV error */
  991.                 0);        /* Flags */
  992.  
  993.     if (rms_s == RMS$_NORMAL) {
  994.         max_len = ((unsigned short int) dsc_out.dsc$w_length < s_len ? 
  995.         (unsigned short int) dsc_out.dsc$w_length : 0);
  996.         if (!max_len)
  997.         fprintf(stderr,
  998.         "%%ZKERMINI out string not long enough, ignoring .ini file\n");
  999.         else
  1000.             strncpy(s,dsc_out.dsc$a_pointer,max_len);
  1001.         LIB$FIND_FILE_END(&find_file_context);
  1002.         LIB$SFREE1_DD(&dsc_out);        /* Return dyno memory */
  1003.         return(0);
  1004.     }
  1005.     *p++;
  1006.     LIB$FIND_FILE_END(&find_file_context);
  1007.     };
  1008.     *s = '\0';            /* Return NULL string */
  1009.     LIB$SFREE1_DD(&dsc_out);
  1010.     return(0);
  1011. }
  1012.  
  1013. parse_fname(cp, cp_len, defnam, flag)
  1014. char *cp;        /* Pointer to file spec to parse */
  1015. int cp_len;        /* Length of cp field */
  1016. char *defnam;        /* Default file spec */
  1017. int flag;        /* Flag word PARSE_xxx */
  1018. {
  1019.     struct FAB fab;
  1020.     struct NAM nam;
  1021.     char expanded_name[NAM$C_MAXRSS];
  1022.     int long rms_status;
  1023.     int cur_len = 0;
  1024.  
  1025.     fab = cc$rms_fab;
  1026.     fab.fab$l_nam = &nam;
  1027.     fab.fab$l_fna = cp;
  1028.     fab.fab$b_fns = strlen(cp);
  1029.     if (defnam) {
  1030.     fab.fab$b_dns = strlen(defnam);
  1031.     fab.fab$l_dna = defnam;
  1032.     } else
  1033.     fab.fab$l_dna = 0;
  1034.  
  1035.     nam = cc$rms_nam;
  1036.     nam.nam$l_esa = &expanded_name;
  1037.     nam.nam$b_ess = sizeof(expanded_name);
  1038.  
  1039.     if (!CHECK_ERR("%%CKERMIT-W-PARSE, ",
  1040.         SYS$PARSE(&fab)))
  1041.     return(-1);
  1042.  
  1043.     *cp = NULL;            /* Make a zero length string */
  1044.     if ((PARSE_NODE & flag) && nam.nam$b_node &&
  1045.         cur_len+nam.nam$b_node < cp_len) {
  1046.     cur_len += nam.nam$b_node;
  1047.     strncat(cp, nam.nam$l_node, (int)nam.nam$b_node);
  1048.     }
  1049.     if ((PARSE_DEVICE & flag) && nam.nam$b_dev &&
  1050.         cur_len+nam.nam$b_dev < cp_len) {
  1051.     cur_len += nam.nam$b_dev;
  1052.     strncat(cp, nam.nam$l_dev, (int)nam.nam$b_dev);
  1053.     }
  1054.     if ((PARSE_DIRECTORY & flag) && nam.nam$b_dir &&
  1055.         cur_len+nam.nam$b_dir < cp_len) {
  1056.     cur_len += nam.nam$b_dir;
  1057.     strncat(cp, nam.nam$l_dir, (int)nam.nam$b_dir);
  1058.     }
  1059.     if ((PARSE_NAME & flag) && nam.nam$b_name &&
  1060.         cur_len+nam.nam$b_name < cp_len) {
  1061.     cur_len += nam.nam$b_name;
  1062.     strncat(cp, nam.nam$l_name, (int)nam.nam$b_name);
  1063.     }
  1064.     if ((PARSE_TYPE & flag) && nam.nam$b_type &&
  1065.         cur_len+nam.nam$b_type < cp_len) {
  1066.     cur_len += nam.nam$b_type;
  1067.     strncat(cp, nam.nam$l_type, (int)nam.nam$b_type);
  1068.     }
  1069.     if ((PARSE_VERSION & flag) && nam.nam$b_ver &&
  1070.         cur_len+nam.nam$b_ver < cp_len) {
  1071.     cur_len += nam.nam$b_ver;
  1072.     strncat(cp, nam.nam$l_ver, (int)nam.nam$b_ver);
  1073.     }
  1074.     return(cur_len);
  1075. }
  1076.  
  1077.  
  1078.  
  1079. /*  Z S A T T R */
  1080. /*
  1081.  Fills in a Kermit file attribute structure for the file which is to be sent.
  1082.  Returns 0 on success with the structure filled in, or -1 on failure.
  1083.  If any string member is null, then it should be ignored.
  1084.  If any numeric member is -1, then it should be ignored.
  1085. */
  1086. zsattr(xx) struct zattr *xx; {
  1087.     long k;
  1088.  
  1089.     k = iflen % 1024L;            /* File length in K */
  1090.     if (k != 0L) k = 1L;
  1091.     xx->lengthk = (iflen / 1024L) + k;
  1092.     xx->type.len = 0;            /* File type can't be filled in here */
  1093.     xx->type.val = "";
  1094.     xx->date.len = 0;            /* File creation date */
  1095.     xx->date.val = "";
  1096.     xx->creator.len = 0;        /* File creator */
  1097.     xx->creator.val = "";
  1098.     xx->account.len = 0;        /* File account */
  1099.     xx->account.val = "";
  1100.     xx->area.len = 0;            /* File area */
  1101.     xx->area.val = "";
  1102.     xx->passwd.len = 0;            /* Area password */
  1103.     xx->passwd.val = "";
  1104.     xx->blksize = -1L;            /* File blocksize */
  1105.     xx->access.len = 0;            /* File access */
  1106.     xx->access.val = "";
  1107.     xx->encoding.len = 0;        /* Transfer syntax */
  1108.     xx->encoding.val = 0;
  1109.     xx->disp.len = 0;            /* Disposition upon arrival */
  1110.     xx->disp.val = "";
  1111.     xx->lprotect.len = 0;        /* Local protection */
  1112.     xx->lprotect.val = "";
  1113.     xx->gprotect.len = 0;        /* Generic protection */
  1114.     xx->gprotect.val = "";
  1115.     xx->systemid.len = 2;        /* System ID for DEC/VMS */
  1116.     xx->systemid.val = "D7";
  1117.     xx->recfm.len = 0;            /* Record format */
  1118.     xx->recfm.val = "";
  1119.     xx->sysparam.len = 0;        /* System-dependent parameters */
  1120.     xx->sysparam.val = "";
  1121.     xx->length = iflen;            /* Length */
  1122.     return(0);
  1123. }
  1124.  
  1125. /* Z M A I L - Place holder */
  1126.  
  1127. zmail()
  1128. {
  1129. }
  1130.  
  1131. /* Z P R I N T  - Place holder */
  1132.  
  1133. zprint()
  1134. {
  1135. }
  1136.