home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / c-kermit / cklfio.c < prev    next >
C/C++ Source or Header  |  2020-01-01  |  74KB  |  2,735 lines

  1. #include "ckcsym.h"
  2. char *ckzv = "Stratus VOS File support, 7.0.006, 15 January 2000";
  3.  
  4. /* C K L F I O  --  Kermit file system support for Stratus VOS */
  5.  
  6. /*
  7.   Author: Frank da Cruz <fdc@columbia.edu>,
  8.   The Kermit Project, Columbia University
  9.   First released January 1985.
  10.   Adapted to Stratus VOS by David R. Lane, SoftCom System, Inc., Nov 1993
  11.  
  12.   Copyright (C) 1985, 2000,
  13.     Trustees of Columbia University in the City of New York.
  14.     All rights reserved.  See the C-Kermit COPYING.TXT file or the 
  15.     copyright text in the ckcmai.c module for disclaimer and permissions.
  16. */
  17.  
  18. /* Include Files */
  19.  
  20. #include "ckcdeb.h"
  21. #include <signal.h>
  22. #include <limits.h>
  23.  
  24. #define DIRSEP       '>'
  25. #define ISDIRSEP(c)  ((c)=='>'||(c)=='<')
  26.  
  27. #include <stdlib.h>
  28. #include <time.h>            /* Need this */
  29. #include <system_io_constants.h>
  30. #include <get_port_info.h>
  31. #include <file_status_info.h>
  32. #include <error_codes.h>
  33.  
  34. extern int binary;            /* We need to know this for open() */
  35.  
  36. /*
  37.   Functions (n is one of the predefined file numbers from ckcker.h):
  38.  
  39.    zopeni(n,name)   -- Opens an existing file for input.
  40.    zopeno(n,name,attr,fcb) -- Opens a new file for output.
  41.    zclose(n)        -- Closes a file.
  42.    zchin(n,&c)      -- Gets the next character from an input file.
  43.    zsinl(n,&s,x)    -- Read a line from file n, max len x, into address s.
  44.    zsout(n,s)       -- Write a null-terminated string to output file, buffered.
  45.    zsoutl(n,s)      -- Like zsout, but appends a line terminator.
  46.    zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
  47.    zchout(n,c)      -- Add a character to an output file, unbuffered.
  48.    zchki(name)      -- Check if named file exists and is readable, return size.
  49.    zchko(name)      -- Check if named file can be created.
  50.    zchkspa(name,n)  -- Check if n bytes available to create new file, name.
  51.    znewn(name,s)    -- Make a new unique file name based on the given name.
  52.    zdelet(name)     -- Delete the named file.
  53.    zxpand(string)   -- Expands the given wildcard string into a list of files.
  54.    znext(string)    -- Returns the next file from the list in "string".
  55.    zxcmd(n,cmd)     -- Execute the command in a lower fork on file number n.
  56.    zclosf()         -- Close input file associated with zxcmd()'s lower fork.
  57.    zrtol(n1,n2)     -- Convert remote filename into local form.
  58.    zltor(n1,n2)     -- Convert local filename into remote form.
  59.    zchdir(dirnam)   -- Change working directory.
  60.    zhome()          -- Return pointer to home directory name string.
  61.    zkself()         -- Kill self, log out own job.
  62.    zsattr(struct zattr *) -- Return attributes for file which is being sent.
  63.    zstime(f, struct zattr *, x) - Set file creation date from attribute packet.
  64.    zrename(old, new) -- Rename a file.
  65.    zmkdir(path)      -- create a directory
  66.    zrmdir(path)      -- delete a directory
  67.    isdir(path)       -- is this path a directory?
  68.    zfseek(offset)    -- position input file to offset (zero-based)
  69.    zfnqfp(fname, buflen, buf) -- get fully qualified absolute path
  70.  */
  71.  
  72. /* Kermit-specific includes */
  73. /*
  74.   Definitions here supersede those from system include files.
  75.   ckcdeb.h is included above.
  76. */
  77. #include "ckcker.h"            /* Kermit definitions */
  78. #include "ckucmd.h"            /* For sys-dependent keyword tables */
  79. #include "ckuver.h"            /* Version herald */
  80.  
  81. char *ckzsys = HERALD;
  82.  
  83. /* Definitions of some system commands */
  84.  
  85. char *DELCMD = "!delete_file ";        /* For file deletion */
  86. char *PWDCMD = "!display_current_dir ";    /* For saying where I am */
  87. char *TYPCMD = "!display ";        /* For typing a file */
  88. char *DIRCMD = "!list -full -files -dirs -links ";/* For directory listing */
  89.                     /* For directory listing, no args */
  90. char *DIRCM2 = "!list -full -files -dirs -links ";
  91. char *WHOCMD = "!list_users -process login ";/* Who's there? */
  92. char *SPACMD = "!display_disk_info ";    /* For space on disk */
  93. char *SPACM2 = "!display_disk_info -long ";    /* For space on disk */
  94.  
  95. #ifdef DTILDE                /* For tilde expansion */
  96. _PROTOTYP( char * tilde_expand, (char *) );
  97. #endif /* DTILDE */
  98.  
  99. _PROTOTYP( vostty, (int) );
  100.  
  101. /* Prototypes for VOS s$calls */
  102.  
  103. _PROTOTYP( extern VOID s$attach_default_output, (CV(256) *path,
  104.     short *append, short *status) );
  105.  
  106. _PROTOTYP( extern VOID s$attach_port, (CV(32) *port_name, CV(256)* path,
  107.     short *hold, short *port_id, short *status) );
  108.  
  109. _PROTOTYP( extern VOID s$change_current_dir, (CV(256) *dir, short *status) );
  110.  
  111. _PROTOTYP( extern VOID s$clone, (short *status, CV(256) *err_msg) );
  112.  
  113. _PROTOTYP( extern VOID s$close, (short *port_id, short *status) );
  114.  
  115. _PROTOTYP( extern void s$copy_file, (CV(256) *source, CV(256) *dest,
  116.     CV(32) *caller, short *switches, short *status) );
  117.  
  118. _PROTOTYP( extern VOID s$delete_file_on_close, (CV(256) *path,
  119.     short *status) );
  120.  
  121. _PROTOTYP( extern VOID s$create_dir, (CV(256) *path, short *status) );
  122.  
  123. _PROTOTYP( extern VOID s$cv_to_int_date_time, (CV(32) *src_time,
  124.     time_t *time, short *status) );
  125.  
  126. _PROTOTYP( extern VOID s$delete_dir, (CV(256) *path, CV(32) *dummy,
  127.     short *flags, short *status) );
  128.  
  129. _PROTOTYP( extern VOID s$detach_default_output, (short *status) );
  130.  
  131. _PROTOTYP( extern VOID s$detach_port, (short *port_id, short *status) );
  132.  
  133. _PROTOTYP( extern VOID s$get_current_dir, (CV(256) *dir) );
  134.  
  135. _PROTOTYP( extern VOID s$get_disk_info, (CV(66) *disk_name, VOID *info,
  136.     short *status) );
  137.  
  138. _PROTOTYP( extern VOID s$get_file_status, (CV(256) *path, VOID *info,
  139.     short *status) );
  140.  
  141. _PROTOTYP( extern VOID s$get_home_dir, (CV(256) *dir) );
  142.  
  143. _PROTOTYP( extern void s$get_object_type, (CV(256) *path, short *chase,
  144.     short *type, short *status) );
  145.  
  146. _PROTOTYP( extern VOID s$get_port_info, (short *port,
  147.     struct get_port_info *pinfo, short *status) );
  148.  
  149. _PROTOTYP( extern VOID s$get_temp_file, (short *organization,
  150.     short *max_rec_len, CV(256) *path, short *status) );
  151.  
  152. _PROTOTYP( extern VOID s$get_user_name, (CV(65) *name) ); 
  153.  
  154. _PROTOTYP( extern VOID s$expand_path, (CV(256) *src_path, CV(32) *suffix,
  155.     CV(256) *dest_path, short *status) ); 
  156.  
  157. _PROTOTYP( extern VOID s$expand_star, (CV(256) *star_name, short *flags,
  158.     short *max_count, short *count, VOID *dest, short *status) );
  159.  
  160. _PROTOTYP( extern VOID s$open, (short *port_id, short *organization,
  161.     short *max_rec_len, short *io_type, short *locking_type,
  162.     short *access_mode, CV(32) *index_name, short *status) );
  163.  
  164. _PROTOTYP( extern VOID s$read_raw, (short *port_id, short *buff_size,
  165.     short *rec_len, void *buffer, short *status) );
  166.  
  167. _PROTOTYP( extern VOID s$seq_open, (CV(256) *path, short *io_type,
  168.     short *port_id, short *status) );
  169.  
  170. _PROTOTYP( extern VOID s$seq_position,  (short *port_id, short *relation,
  171.     long *rec_number, short *status) );
  172.  
  173. _PROTOTYP( extern VOID s$seq_read, (short *port_id, short *buff_size,
  174.     short *rec_len, VOID *buffer, short *status) );
  175.  
  176. _PROTOTYP( extern VOID s$seq_write, (short *port_id, short *rec_length,
  177.     VOID *buffer, short *status) );
  178.  
  179. _PROTOTYP( extern VOID s$seq_write_partial, (short *port_id,
  180.     short *rec_length, VOID *buffer, short *status) );
  181.  
  182. _PROTOTYP( extern VOID s$set_expiration_date, (CV(256) *path_name,
  183.     time_t *date_time, short *status) );
  184.  
  185. _PROTOTYP( extern VOID s$set_object_times, (CV(256) *path_name,
  186.     time_t *time_array, short *status) ); /* four times in array */
  187.  
  188. _PROTOTYP( extern VOID s$stop_program, (CV(256) *command_line,
  189.         short *status ) );
  190.  
  191. _PROTOTYP( extern VOID s$write_raw, (short *port_id, short *length,
  192.     void *buffer, short *status) );
  193.  
  194. /* Define maximum length for a file name if not already defined */
  195.  
  196. #ifndef MAXNAMLEN
  197. #define MAXNAMLEN 32
  198. #endif /* MAXNAMLEN */
  199.  
  200. /* Longest pathname */
  201.  
  202. #ifdef MAXPATHLEN
  203. #ifdef MAXPATH
  204. #undef MAXPATH
  205. #endif /* MAXPATH */
  206. #define MAXPATH MAXPATHLEN
  207. #else
  208. #ifdef PATH_MAX
  209. #define MAXPATH PATH_MAX
  210. #else
  211. #define MAXPATH 256
  212. #endif /* PATH_MAX */
  213. #endif /* MAXPATHLEN */
  214.  
  215. /* Maximum number of filenames for wildcard expansion */
  216.  
  217. /* VOS directories can be up to 256 data blocks.  This is about 5000 entries */
  218. /* but it can vary a lot, depending on a number of factors, such as the      */
  219. /* lengths of the names, whether or not Access Control Lists are present,    */
  220. /* and so on.  This is about the most you get, in practice, though.          */
  221.  
  222. #define MAXWLD 5000
  223.  
  224. struct path {
  225.     char npart[MAXNAMLEN+4];        /* name part of path segment */
  226.     struct path *fwd;            /* forward ptr */
  227. };
  228.  
  229. _PROTOTYP( int shxpand, (char *, char *[], int ) );
  230.  
  231.  
  232. /* Declarations */
  233.  
  234. int maxnam = MAXNAMLEN;            /* Available to the outside */
  235. int maxpath = MAXPATH;
  236.  
  237. typedef struct {
  238.     short port;
  239.     short io_type;
  240.     short binary;
  241.     short organization;
  242.     short max_record_len;
  243.     short record_len;
  244.     short buffer_size;
  245.     short buffer_pointer;
  246.     short count;
  247.     short count_invalid;
  248.     char  *buffer;
  249. } VFILE;
  250.  
  251. VFILE *fp[ZNFILS] = {             /* File pointers */
  252.     NULL, NULL, NULL, NULL, NULL, NULL, NULL };
  253.  
  254. /* There is not a Stratus-provided include for this.  I typed it in from
  255.  * the VOS C subroutines manual, R068, under s$get_disk_info.
  256.  */
  257.  
  258. struct disk_info {
  259.     short     version;
  260.     CV(32)     module_name;
  261.     long    file_partition_size;
  262.     long    file_partition_used;
  263.     long    page_partition_size;
  264.     long    page_partition_used;
  265.     CV(32)    disk_type;
  266.     long    disk_size;
  267.     short    reserved1[19];
  268.     long    disk_reads;
  269.     long    disk_writes;
  270.     short    reserved2;
  271.     short    recovery_switches;
  272.     long    reserved3;
  273.     short    number_of_members;
  274.     long    fatal_errors;
  275.     long    reserved4;
  276. };
  277.  
  278. /* Buffers and pointers used in buffered file input and output. */
  279. #ifdef DYNAMIC
  280. extern char *zinbuffer, *zoutbuffer;
  281. #else
  282. extern char zinbuffer[], zoutbuffer[];
  283. #endif /* DYNAMIC */
  284. extern char *zinptr, *zoutptr;
  285. extern int zincnt, zoutcnt;
  286. extern int wildxpand;
  287.  
  288. extern UID_T real_uid();
  289.  
  290. static long iflen = -1L;        /* Input file length */
  291.  
  292. static PID_T pid = 0;            /* pid of child fork */
  293. static int fcount;            /* Number of files in wild group */
  294. static int nxpand;            /* copy of fcount */
  295. static char nambuf[MAXPATH+4];         /* Buffer for a filename */
  296. #ifndef NOFRILLS
  297. static char zmbuf[200];            /* For mail, remote print strings */
  298. #endif /* NOFRILLS */
  299.  
  300. /* static */                /* Not static, must be global now. */
  301. int  oldmtchs;                /* number of matches in last expand */
  302. char *mtchs[MAXWLD],            /* Matches found for filename */
  303.      **mtchptr;                /* Pointer to current match */
  304.  
  305. /*  Z K S E L F  --  Kill Self: log out own job, if possible.  */
  306.  
  307. int
  308. zkself() {                /* For "bye", but no guarantee! */
  309.     /* as of 6.1.alpha.11, this really does what it's supposed to do... */
  310.     CV(256) command_line;
  311.     short status;
  312.  
  313.     debug(F100,"zkself","",0);
  314.  
  315.     strcpy(&command_line,"logout");
  316.     status = 0;
  317.     s$stop_program(&command_line,&status);
  318.  
  319.     debug(F101,"zkself did not...","",status);
  320.     exit(0);
  321.     /*NOTREACHED*/
  322. }
  323.  
  324. /*  Z O P E N I  --  Open an existing file for input. */
  325.  
  326. int
  327. zopeni(n,name) int n; char *name; {
  328.     CV(256) path;
  329.     CV(256) src_path;
  330.     CV(32) suffix;
  331.     FILE_STATUS_STRUCT finfo;
  332.     short status;
  333.  
  334.     debug(F111," zopeni",name,n);
  335.     debug(F101,"  fp","", fp[n]);
  336.     if (chkfn(n) != 0) return(0);
  337.     zincnt = 0;                /* Reset input buffer */
  338.  
  339.     if (fp[n])                /* make sure it's closed */
  340.     zclose(n);
  341.  
  342.  if (n == ZSYSFN) {            /* Input from a system function? */
  343. /*** Note, this function should not be called with ZSYSFN ***/
  344. /*** Always call zxcmd() directly, and give it the real file number ***/
  345. /*** you want to use.  ***/
  346.         debug(F110,"zopeni called with ZSYSFN, failing!",name,0);
  347.     *nambuf = '\0';            /* No filename. */
  348.     return(0);            /* fail. */
  349.     }
  350.     if (n == ZSTDIO) {            /* Standard input? */
  351.     if (vostty(DEFAULT_INPUT_PORT_ID)) {
  352.         fprintf(stderr,"Terminal input not allowed");
  353.         debug(F100,"zopeni: attempts input from unredirected stdin","",0);
  354.         *nambuf = '\0';        /* No filename. */
  355.         return(0);
  356.     }
  357.  
  358.     return(1);
  359.     }
  360.  
  361. #ifdef COMMENT
  362.     *nambuf = '\0';            /* Forget filename */
  363. #endif /* COMMENT */
  364.  
  365.     strcpy (&src_path, name);
  366.     strcpy (&suffix, "");
  367.     s$expand_path (&src_path, &suffix, &path, &status);
  368.     if (status) {
  369.     debug(F101,"  zopeni s$expand_path status","",status);
  370.     return 0;
  371.     }
  372.  
  373.     finfo.version = FILE_STAT_VERSION_5;
  374.     s$get_file_status (&path, &finfo, &status);
  375.     if (status) {
  376.     debug(F101,"zopeni s$get_file_status status","",status);
  377.     return 0;
  378.     }
  379.  
  380.     switch (finfo.file_organization) {
  381.     case SEQUENTIAL_FILE:
  382.     case STREAM_FILE:
  383.         iflen = finfo.last_record_number;
  384.         break;
  385.  
  386.     case FIXED_FILE:
  387.         iflen = finfo.last_record_number * 
  388.         finfo.flags_struct.flags_bits_overlay.max_record_size;
  389.         break;
  390.  
  391.     case RELATIVE_FILE:
  392.         iflen = finfo.last_record_number * 
  393.         (finfo.flags_struct.flags_bits_overlay.max_record_size + 2);
  394.         break;
  395.  
  396.     default:    /* uhhhh.... */
  397.         iflen = -1L;
  398.         break;
  399.     }
  400.  
  401.     fp[n] = calloc (1, sizeof (**fp));
  402.     debug(F111," zopeni allocated", name, fp[n]);
  403.     if (fp[n] == NULL) {
  404.     debug(F101,"  zopeni could not allocate fp","",sizeof **fp);
  405.     perror("zopeni");
  406.     return 0;
  407.     }
  408.  
  409.     /* CANNOT RETURN AFTER HERE WITHOUT FREEING MEMORY */
  410.  
  411.     fp[n]->organization = finfo.file_organization;
  412.     fp[n]->max_record_len
  413.     = finfo.flags_struct.flags_bits_overlay.max_record_size;
  414.  
  415.     fp[n]->buffer_size = fp[n]->max_record_len ? fp[n]->max_record_len : 4096;
  416.     fp[n]->buffer = calloc (1, fp[n]->buffer_size);
  417.  
  418.     if (NULL == fp[n]->buffer) {
  419.     debug(F101,"zopeni cannot allocate buffer","",fp[n]->buffer_size);
  420.     free (fp[n]);
  421.     fp[n] = NULL;
  422.     return 0;
  423.     }
  424.  
  425.     fp[n]->io_type = INPUT_TYPE;
  426.     s$seq_open (&path, &fp[n]->io_type, &fp[n]->port, &status);
  427.     if (status) {
  428.     debug(F101,"zopeni s$seq_open status","",status);
  429.     free (fp[n]);
  430.     fp[n] = NULL;
  431.     free (fp[n]->buffer);
  432.     return 0;
  433.     }
  434.  
  435. #ifdef COMMENT
  436.     strcpy (nambuf, name);        /* save filename */
  437. #endif /* COMMENT */
  438.     fp[n]->binary = binary;         /* remember this */
  439.     fp[n]->count_invalid = 1;        /* have no buffered data yet */
  440.     return(1);
  441. }
  442.  
  443. /*  Z O P E N O  --  Open a new file for output.  */
  444.  
  445. int
  446. zopeno(n,name,zz,fcb)
  447. /* zopeno */  int n; char *name; struct zattr *zz; struct filinfo *fcb; {
  448.     short io_type;
  449.     short org;
  450.     short record_len;
  451.     short port = 0;
  452.     CV(256) path;
  453.     CV(256) src_path;
  454.     CV(32) suffix;
  455.     CV(32) index;
  456.     CV(32) portnam;
  457.     short lockmod;
  458.     short access;
  459.     short hold;
  460.     FILE_STATUS_STRUCT finfo;
  461.     short status;
  462.     int  is_binary = 0;
  463.  
  464. /* As of Version 5A, the attribute structure and the file information */
  465. /* structure are included in the arglist. */
  466.  
  467.     if (n != ZDFILE) {
  468.     debug(F111," zopeno",name,n);
  469.     if (fcb) {
  470.         debug(F101,"zopeno fcb disp","",fcb->dsp);
  471.         debug(F101,"zopeno fcb type","",fcb->typ);
  472.         debug(F101,"zopeno fcb char","",fcb->cs);
  473.     } else {
  474.         debug(F100,"zopeno fcb is NULL","",0);
  475.     }
  476.     }
  477.  
  478.     if (chkfn(n) != 0)
  479.     return(0);
  480.  
  481.     io_type = OUTPUT_TYPE;
  482.  
  483.     if (fcb) {                /* If called with an FCB... */
  484.     if (fcb->typ == XYFT_T)        /* Does it say Text? */
  485.         is_binary = 0;        /* Yes, not binary. */
  486.     else
  487.         is_binary = 1;        /* No, it is binary. */
  488.     }
  489.     else {
  490.     switch (n) {
  491.         case ZOFILE:
  492.         case ZWFILE:
  493.         case ZMFILE:
  494.         is_binary = binary;    /* Use current file mode */
  495.         break;
  496.  
  497.         case ZDFILE:
  498.         case ZTFILE:
  499.         case ZSYSFN:
  500.         case ZCTERM:
  501.         case ZSTDIO:
  502.         is_binary = 0;        /* Always text */
  503.         break;
  504.  
  505.         case ZSFILE:
  506.         is_binary = 1;        /* Always binary */
  507.         break;
  508.  
  509.     }
  510.     }
  511.  
  512.     if ((n != ZCTERM) && (n != ZSTDIO)) {  /* Terminal or standard output */
  513.  
  514.     /* A real file.  Open it in desired mode (create or append). */
  515.  
  516.     if (fcb) {                /* If called with an FCB... */
  517.         if (fcb->dsp == XYFZ_A)        /* Does it say Append? */
  518.           io_type = APPEND_TYPE;    /* Yes. */
  519.     }
  520.  
  521.     strcpy (&src_path, name);
  522.     strcpy (&suffix, "");
  523.     s$expand_path (&src_path, &suffix, &path, &status);
  524.     if (status) {
  525.         if (n != ZDFILE)
  526.         debug(F101,"  zopeno s$expand_path status","",status);
  527.         return 0;
  528.     }
  529.  
  530.     finfo.version = FILE_STAT_VERSION_5;
  531.     s$get_file_status (&path, &finfo, &status);
  532.     if (status) {
  533.         if (status != e$object_not_found) {
  534.         if (n != ZDFILE)
  535.             debug(F101,"zopeno s$get_file_status status","",status);
  536.         return 0;
  537.         }
  538.  
  539.         /* the session log is a stream file, because that reflects */
  540.         /* the "log of data sent back and forth" better            */
  541.  
  542.         org = (is_binary) ? STREAM_FILE : SEQUENTIAL_FILE;
  543.         record_len = 4096;
  544.     }
  545.     else {
  546.         org = finfo.file_organization;
  547.         record_len = finfo.flags_struct.flags_bits_overlay.max_record_size;
  548.     }
  549.     }
  550.     else {    /* it is a "terminal" */
  551.     n = ZOFILE;        /* change to output file */
  552.     org = TERMINAL_TYPE;
  553.     record_len = 4096;
  554.     port = DEFAULT_OUTPUT_PORT_ID;
  555.         debug(F101," zopen terminal, ZOFILE", "", n);
  556.     }
  557.  
  558.     fp[n] = calloc (1, sizeof (**fp));
  559.     if (n != ZDFILE)
  560.     debug(F111," zopeno allocated", name, fp[n]);
  561.     if (fp[n] == NULL) {
  562.     if (n != ZDFILE)
  563.         debug(F101,"  zopeno could not allocate fp","",sizeof **fp);
  564.     perror("zopeno");
  565.     return 0;
  566.     }
  567.  
  568.     /* CANNOT RETURN AFTER HERE WITHOUT FREEING MEMORY */
  569.  
  570.     fp[n]->max_record_len = record_len;
  571.     fp[n]->buffer_size = fp[n]->max_record_len ? fp[n]->max_record_len : 4096;
  572.     fp[n]->buffer = calloc (1, fp[n]->buffer_size);
  573.  
  574.     if (NULL == fp[n]->buffer) {
  575.     if (n != ZDFILE)
  576.         debug(F101,"zopeno cannot allocate buffer","",fp[n]->buffer_size);
  577.     free (fp[n]);
  578.     fp[n] = NULL;
  579.     return 0;
  580.     }
  581.  
  582.     /* save pre-calculated information */
  583.  
  584.     fp[n]->io_type = io_type;
  585.     fp[n]->organization = org;
  586.     fp[n]->port = port;
  587.     fp[n]->binary = is_binary;        /* save this! */
  588.  
  589.     if (0 == fp[n]->port) {  /* not set yet */
  590.     strcpy (&portnam, "");
  591.     hold = DONT_HOLD;
  592.     s$attach_port (&portnam, &path, &hold, &fp[n]->port, &status);
  593.     if (status) {
  594.         if (n != ZDFILE)
  595.         debug(F101,"zopeno s$attach_port status","",status);
  596.         free (fp[n]->buffer);
  597.         free (fp[n]);
  598.         fp[n] = NULL;
  599.         return 0;
  600.     }
  601.  
  602.     lockmod = IMPLICIT_LOCKING;
  603.     access = SEQUENTIAL_MODE;
  604.     strcpy (&index, "");
  605.     s$open (&fp[n]->port, &fp[n]->organization, &fp[n]->max_record_len,
  606.         &fp[n]->io_type, &lockmod, &access, &index, &status);
  607.     if (status) {
  608.         if (n != ZDFILE)
  609.         debug(F101,"zopeno s$open status","",status);
  610.         free (fp[n]->buffer);
  611.         free (fp[n]);
  612.         fp[n] = NULL;
  613.         return 0;
  614.     }
  615.     }
  616.  
  617.     zoutcnt = 0;            /* (PWP) reset output buffer */
  618.     zoutptr = zoutbuffer;
  619.     return((fp[n] != NULL) ? 1 : 0);
  620. }
  621.  
  622. /*  Z C L O S E  --  Close the given file.  */
  623.  
  624. /*  Returns 0 if arg out of range, 1 if successful, -1 if close failed.  */
  625.  
  626. int
  627. zclose(n) int n; {
  628.     int x, x2;
  629.     short status;
  630.  
  631.     if (chkfn(n) < 1) return(0);    /* Check range of n */
  632.  
  633.     debug(F101,"zclose file","",n);
  634.  
  635.     if ((n == ZOFILE) && (zoutcnt > 0))    /* (PWP) output leftovers */
  636.       x2 = zoutdump();
  637.     else
  638.       x2 = 0;
  639.  
  640.     x = 0;                /* Initialize return code */
  641.     if (fp[ZSYSFN]) {            /* If file is really pipe */
  642.         x = zclosf(n);            /* do it specially */
  643.     } else {
  644.     if (fp[n]->port > MAX_RESERVED_PORT_ID) { /* only close user ports */
  645.         s$close (&fp[n]->port, &status);
  646.         x = status ? EOF : 0;
  647.         s$detach_port (&fp[n]->port, &status);
  648.         x = (status || x) ? EOF : 0;
  649.     }
  650.     else
  651.         x = 0;
  652.     free (fp[n]->buffer);
  653.     free (fp[n]);
  654.     fp[n] = NULL;
  655.     }
  656.  
  657.     iflen = -1L;            /* Invalidate file length */
  658.  
  659.     if (x == EOF)            /* if we got a close error */
  660.       return(-1);
  661.     else if (x2 < 0)        /* or an error flushing the last buffer */
  662.       return(-1);        /* then return an error */
  663.     else
  664.       return(1);
  665. }
  666.  
  667. /*  Z C H I N  --  Get a character from the input file.  */
  668.  
  669. /*  Returns -1 if EOF, 0 otherwise with character returned in argument  */
  670.  
  671. int
  672. zchin(n,c) int n; int *c; {
  673.     int a, x;
  674.     register VFILE *vp;
  675.     short status;
  676.     short rbuffsize;
  677.  
  678.     /* (PWP) Just in case this gets called when it shouldn't. */
  679.     if (n == ZIFILE) {
  680.     x = zminchar();
  681.     *c = x;
  682.     return((x < 0) ? -1 : 0);
  683.     }
  684.  
  685.     if (chkfn(n) < 1) return(-1);
  686.     vp = fp[n];
  687.  
  688.     if (vp->count_invalid) {    /* need to read more data */
  689.  
  690.     debug(F101,"zchin reading file","",n);
  691.  
  692.     if (!vp->binary) { /* text mode, add appropriate line separators */
  693.  
  694.         rbuffsize = vp->buffer_size - 1;
  695.         /* leave room for line terminator character(s) */
  696.  
  697. #ifndef NLCHAR
  698.         rbuffsize--;
  699. #endif /* NLCHAR */
  700.  
  701.         s$seq_read (&vp->port, &rbuffsize, &vp->record_len,
  702.         vp->buffer, &status);
  703.  
  704.         if (0 != status) {
  705.         debug(F101,"zchin text s$seq_read status","",status);
  706.         return (-1); /* hope it's end of file... */
  707.         }
  708. #ifdef NLCHAR
  709.         vp->buffer[vp->record_len++] = NLCHAR;
  710. #else
  711.         vp->buffer[vp->record_len++] = '\r';
  712.         vp->buffer[vp->record_len++] = '\n';
  713. #endif /* NLCHAR */
  714.     }
  715.     else {        /* binary */
  716.         if (vp->organization == STREAM_FILE) {
  717.         s$read_raw (&vp->port, &vp->buffer_size, &vp->record_len,
  718.             vp->buffer, &status);
  719.         if (status == e$short_record) /* not buffer_size bytes left */
  720.             status = 0;
  721.         }
  722.         else
  723.         s$seq_read (&vp->port, &vp->buffer_size, &vp->record_len,
  724.             vp->buffer, &status);
  725.  
  726.         if (0 != status) {
  727.         debug(F101,"zchin binary s$...read... status","",status);
  728.         return (-1); /* hope it's end of file... */
  729.         }
  730.         }
  731.     /* set up for character stuff... */
  732.     vp->count = vp->record_len;
  733.     vp->buffer_pointer = 0;
  734.     vp->count_invalid = 0;
  735.     }
  736.     /* count is valid */
  737.  
  738.     x = vp->buffer[vp->buffer_pointer++];
  739.     if (0 >= --(vp->count))
  740.     vp->count_invalid = 1;
  741.  
  742.     *c = x;
  743.     return((x < 0) ? -1 : 0);
  744. }
  745.  
  746. /*  Z S I N L  --  Read a line from a file  */
  747.  
  748. /*
  749.   Writes the line into the address provided by the caller.
  750.   n is the Kermit "channel number".
  751.   Writing terminates when newline is encountered, newline is not copied.
  752.   Writing also terminates upon EOF or if length x is exhausted.
  753.   Returns 0 on success, -1 on EOF or error.
  754. */
  755. int
  756. zsinl(n,s,x) int n, x; char *s; {
  757.     int a, z = 0;            /* z is return code. */
  758.  
  759.     if (chkfn(n) < 1) {            /* Make sure file is open */
  760.     return(-1);
  761.     }
  762.     a = -1;                /* Current character, none yet. */
  763.     while (x--) {            /* Up to given length */
  764. #ifndef NLCHAR
  765.     int old;
  766.     old = a;            /* Previous character */
  767. #endif
  768.     if (zchin(n,&a) < 0) {        /* Read a character from the file */
  769.         debug(F101,"zsinl","",a);
  770.         z = -1;            /* EOF or other error */
  771.         break;
  772.     }
  773. #ifdef NLCHAR
  774.     if (a == (char) NLCHAR) break;    /* Single-character line terminator */
  775. #else
  776.     if (a == '\015') continue;    /* CR, get next character */
  777.     if (old == '\015') {        /* Previous character was CR */
  778.         if (a == '\012') break;    /* This one is LF, so we have a line */
  779.         else *s++ = '\015';        /* Not LF, deposit CR */
  780.     }
  781. #endif /* NLCHAR */
  782.     *s = a;                /* Deposit character */
  783.     s++;
  784.     }
  785.     *s = '\0';                /* Terminate the string */
  786.     return(z);
  787. }
  788.  
  789. /*
  790.  * (PWP) (re)fill the buffered input buffer with data.  All file input
  791.  * should go through this routine, usually by calling the zminchar()
  792.  * macro (in ckcker.h).
  793.  */
  794.  
  795. /*
  796.  * Suggestion: if fread() returns 0, call ferror to find out what the
  797.  * problem was.  If it was not EOF, then return -2 instead of -1.
  798.  * Upper layers (getpkt function in ckcfns.c) should set cxseen flag
  799.  * if it gets -2 return from zminchar macro.
  800.  *
  801.  * This always uses zin* buffering for io, not fp->buffer.
  802.  */
  803. int
  804. zinfill() {
  805.     int x;
  806.     short status;
  807.     short buff_len;
  808.     VFILE *vp = fp[ZIFILE];
  809.  
  810.     errno = 0;
  811.     buff_len = INBUFSIZE;
  812.  
  813.     debug(F101,"zinfill INBUFSIZE","",INBUFSIZE);
  814.     debug(F101,"zinfill buff_len 1","",buff_len);
  815.     
  816.     /* leave room for line terminators in text mode */
  817.     if (0 == vp->binary)
  818. #ifdef NLCHAR
  819.     buff_len -= 1;
  820. #else
  821.     buff_len -= 2;
  822. #endif /* NLCHAR */
  823.  
  824.     debug(F101,"zinfill buff_len 2","",buff_len);
  825.     if (vp->binary && vp->organization == STREAM_FILE) {
  826.     debug(F101,"zinfill before s$read_raw status","",status);
  827.     s$read_raw (&vp->port, &buff_len, &vp->record_len, zinbuffer, &status);
  828.     } else {
  829.     debug(F101,"zinfill before s$seq_read status","",status);
  830.     s$seq_read (&vp->port, &buff_len, &vp->record_len, zinbuffer, &status);
  831.     }
  832.  
  833.     zincnt = vp->record_len;
  834.  
  835.     debug(F101,"zinfill zincnt","",zincnt);
  836.     if (status) {
  837.     debug(F101,"zinfill s$...read... status","",status);
  838.     return (status == e$end_of_file) ? (-1) : (-2);
  839.     }
  840.  
  841.     if (0 == vp->binary) { /* text mode, add a NLCHAR to it */
  842. #ifdef NLCHAR
  843.     zinbuffer[zincnt++] = NLCHAR;
  844. #else
  845.     zinbuffer[zincnt++] = '\r';
  846.     zinbuffer[zincnt++] = '\n';
  847. #endif
  848.     }
  849.  
  850.     zinptr = zinbuffer;    /* set pointer to beginning, (== &zinbuffer[0]) */
  851.     zincnt--;        /* one less char in buffer */
  852.     return((int)(*zinptr++) & 0377); /* because we return the first */
  853. }
  854.  
  855. /*  Z S O U T  --  Write a string out to the given file, buffered.  */
  856.  
  857. int
  858. zsout(n,s) int n; char *s; {
  859.     short status;
  860.     int len;
  861.  
  862.     if (chkfn(n) < 1) return(-1); /* Keep this here, prevents memory faults */
  863.     if (n != ZDFILE)
  864.     debug(F111,"zsout writing to file",s,n);
  865.  
  866.     len = strlen (s);
  867.     if ('\n' == s[len-1]) {
  868.     s[--len] = '\0';
  869.     return (zsoutl (n,s));
  870.     }
  871.  
  872.     fp[n]->record_len = (short) strlen(s);
  873.     /* since this never writes an end-of-line, always write partial */
  874.     if (fp[n]->organization == STREAM_FILE && fp[n]->binary)
  875.     s$write_raw (&fp[n]->port, &fp[n]->record_len, s, &status);
  876.     else
  877.     s$seq_write_partial (&fp[n]->port, &fp[n]->record_len, s, &status);
  878.  
  879.     return status ? (-1) : 0;
  880. }
  881.  
  882. /*  Z S O U T L  --  Write string to file, with line terminator, buffered  */
  883.  
  884. int
  885. zsoutl(n,s) int n; char *s; {
  886.     short status;
  887.     int len;
  888.  
  889.     if (chkfn(n) < 1) return(-1);
  890.     if (n != ZDFILE)
  891.     debug(F111,"zsoutl writing to file",s,n);
  892.  
  893.     len = strlen (s);
  894.     if ('\n' == s[len-1])
  895.     len--;
  896.  
  897.     fp[n]->record_len = len;
  898.     /* adding a record terminator, means always use seq_write... */
  899.  
  900.     s$seq_write (&fp[n]->port, &fp[n]->record_len, s, &status);
  901.     return status ? (-1) : 0;
  902. }
  903.  
  904. /*  Z S O U T X  --  Write x characters to file, unbuffered.  
  905.  *  returns -1 on error, or the number of characters written.
  906.  */
  907.  
  908. int
  909. zsoutx(n,s,x) int n, x; char *s; {
  910.     short status;
  911.  
  912.     if (chkfn(n) < 1) return(-1);
  913.     if (n != ZDFILE)
  914.     debug(F101,"zsoutx writing to file","",n);
  915.     fp[n]->record_len = x;
  916.  
  917.     if (fp[n]->binary) {
  918.     if (fp[n]->organization == STREAM_FILE)
  919.         s$write_raw (&fp[n]->port, &fp[n]->record_len, s, &status);
  920.     else
  921.         s$seq_write_partial (&fp[n]->port, &fp[n]->record_len, s, &status);
  922.     }
  923.     else
  924.     s$seq_write (&fp[n]->port, &fp[n]->record_len, s, &status);
  925.  
  926.     return((status == 0) ? x : -1);
  927. }
  928.  
  929.  
  930. /*  Z C H O U T  --  Add a character to the given file.  */
  931.  
  932. /*  Should return 0 or greater on success, -1 on failure (e.g. disk full)  */
  933.  
  934. int
  935. #ifdef CK_ANSIC
  936. zchout(register int n, char c)
  937. #else
  938. zchout(n,c) register int n; char c;
  939. #endif /* CK_ANSIC */
  940. /* zchout() */ {
  941.     short status;
  942.     char  buff[2];
  943.  
  944.     if (chkfn(n) < 1) return(-1);
  945.  
  946.     buff[0] = c;
  947.     buff[1] = '\0';
  948.     if (n != ZDFILE)
  949.     debug(F111,"zchout writing to file",buff,n);
  950.  
  951.     fp[n]->record_len = 1;
  952.  
  953.     if (fp[n]->binary) {
  954.     if (fp[n]->organization == STREAM_FILE)
  955.         s$write_raw (&fp[n]->port, &fp[n]->record_len, &c, &status);
  956.     else
  957.         s$seq_write_partial (&fp[n]->port, &fp[n]->record_len,
  958.         &c, &status);
  959.     }
  960.     else
  961.     s$seq_write (&fp[n]->port, &fp[n]->record_len, &c, &status);
  962.  
  963.     return((status == 0) ? 1 : -1);
  964. }
  965.  
  966. /* (PWP) buffered character output routine to speed up file IO */
  967.  
  968. int
  969. zoutdump() {
  970.     int x;
  971.     VFILE *vp;
  972.     short status = 0;
  973.     int   len;
  974.     register char  *ep;
  975. #ifdef NLCHAR
  976.     char  *cp;
  977. #else
  978.     register int   i;
  979.     static   char  last;
  980. #endif /* NLCHAR */
  981.  
  982.     zoutptr = zoutbuffer;        /* Reset buffer pointer in all cases */
  983.     debug(F101,"zoutdump chars","",zoutcnt);
  984.     if (zoutcnt == 0) {            /* Nothing to output */
  985.     return(0);
  986.     } else if (zoutcnt < 0) {        /* Unexpected negative argument */
  987.     zoutcnt = 0;            /* Reset output buffer count */
  988.     return(-1);            /* and fail. */
  989.     }
  990.  
  991.     vp = fp[ZOFILE];
  992.  
  993.     if (vp->binary) {
  994.     vp->record_len = zoutcnt;
  995.     debug(F101,"zoutdump binary, port","",vp->port);
  996.     if (vp->port == DEFAULT_OUTPUT_PORT_ID) { /* terminal */
  997.         conxo (vp->record_len, zoutbuffer);
  998.     }
  999.     else {
  1000.         if (vp->organization == STREAM_FILE)
  1001.         s$write_raw (&vp->port, &vp->record_len,
  1002.             zoutbuffer, &status);
  1003.         else
  1004.         s$seq_write_partial (&vp->port, &vp->record_len,
  1005.             zoutbuffer, &status);
  1006.     }
  1007.     }
  1008.     else {
  1009.     /* need to deblock the data in the output buffer */
  1010.  
  1011.     debug(F101,"zoutdump text, port","",vp->port);
  1012. #ifdef NLCHAR
  1013.     cp = zoutbuffer;
  1014.  
  1015.     do {
  1016.         for (ep = cp; *ep != NLCHAR; ep++)
  1017.         if (ep > &zoutbuffer[zoutcnt-1]) {
  1018.             ep = NULL;
  1019.             break;
  1020.         }
  1021.         if (NULL != ep) {
  1022.         len = (int) (ep - cp); /* NOT +1 */
  1023.         vp->record_len = len;
  1024.         if (vp->port == DEFAULT_OUTPUT_PORT_ID) { /* terminal */
  1025.             *ep = '\0'; /* conoll wants ASCIIZ string */
  1026.             conoll (cp);
  1027.         }
  1028.         else
  1029.             s$seq_write (&vp->port, &vp->record_len, cp, &status);
  1030.         ep++;    /* move past the NLCHAR or NULL */
  1031.         }
  1032.         else if (cp < &zoutbuffer[zoutcnt-1]) {    /* not done it all */
  1033.         /* it's flushing in the middle of a record */
  1034.         len = zoutcnt - (int) (cp - (&zoutbuffer[0]));
  1035.         vp->record_len = len;
  1036.         if (vp->port == DEFAULT_OUTPUT_PORT_ID) { /* terminal */
  1037.             *(cp+len) = '\0'; /* conol wants ASCIIZ string */
  1038.             conol (cp);
  1039.         }
  1040.         else {
  1041.             if (vp->organization == STREAM_FILE)
  1042.             s$write_raw (&vp->port, &vp->record_len,
  1043.                 cp, &status);
  1044.             else
  1045.             s$seq_write_partial (&vp->port, &vp->record_len,
  1046.                 cp, &status);
  1047.         }
  1048.         }
  1049.         cp = ep;
  1050.     } while ((status == 0) && (NULL != cp) &&
  1051.          ((cp < &zoutbuffer[zoutcnt-1])));
  1052. #else
  1053.     /* this is not tested, but it is identical to log code in dpt.pm */
  1054.     ep = zoutbuffer;
  1055.     for (i = 0; i < zoutcnt; i++)
  1056.     {
  1057.         if ((zoutbuffer[i] == '\r') || (zoutbuffer[i] == '\n'))
  1058.         {
  1059.         len = (zoutbuffer + i) - ep;
  1060.         if ((last != '\r') || (buff[i] != '\n')) {
  1061.             if (vp->port == DEFAULT_OUTPUT_PORT_ID) { /* terminal */
  1062.             *(ep + len) = '\0'; /* conoll wants ASCIIZ string */
  1063.             conoll (ep);
  1064.             }
  1065.             else
  1066.             s$seq_write (&vp->port, &len, ep, &status);
  1067.         }
  1068.         ep = &zoutbuffer[i+1];
  1069.         }
  1070.         last = zoutbuffer[i];
  1071.     }
  1072.  
  1073.     if (ep < (zoutbuffer + len)) /* write out the last bit */
  1074.     {
  1075.         len = (zoutbuff + len) - ep;
  1076.         if (vp->port == DEFAULT_OUTPUT_PORT_ID) { /* terminal */
  1077.         *(ep + len) = '\0'; /* conol wants ASCIIZ string */
  1078.         conol (ep);
  1079.         }
  1080.         else {
  1081.         if (vp->organization == STREAM_FILE)
  1082.             s$write_raw (&vp->port, &len, ep, &status);
  1083.         else
  1084.             s$seq_write_partial (&vp->port, &len, ep, &status);
  1085.         }
  1086.     }
  1087.  
  1088. #endif /* NLCHAR */
  1089.  
  1090.     }
  1091.     if (0 == status) {
  1092.     debug(F101,"zoutdump s$...write... ok","",zoutcnt);
  1093.     zoutcnt = 0;            /* Reset output buffer count */
  1094.     return(0);            /* write() worked OK */
  1095.     } else {
  1096.     debug(F101,"zoutdump s$...write... status","",status);
  1097.     zoutcnt = 0;            /* Reset output buffer count */
  1098.     return(-1);            /* s$write_* failed */
  1099.     }
  1100. }
  1101.  
  1102. /*  C H K F N  --  Internal function to verify file number is ok  */
  1103.  
  1104. /*
  1105.  Returns:
  1106.   -1: File number n is out of range
  1107.    0: n is in range, but file is not open
  1108.    1: n in range and file is open
  1109. */
  1110. int
  1111. chkfn(n) int n; {
  1112.     if (n < 0 || n >= ZNFILS) {
  1113.     debug(F101,"chkfn: file number out of range","",n);
  1114.     return(-1);
  1115.     }
  1116.     else return( (fp[n] == NULL) ? 0 : 1 );
  1117. }
  1118.  
  1119. /*  Z C H K I  --  Check if input file exists and is readable  */
  1120.  
  1121. /*
  1122.   Returns:
  1123.    >= 0 if the file can be read (returns the size).
  1124.      -1 if file doesn't exist or can't be accessed,
  1125.      -2 if file exists but is not readable (e.g. a directory file).
  1126.      -3 if file exists but protected against read access.
  1127.  
  1128.  Directory files, special files, and symbolic links are not readable.
  1129. */
  1130. long
  1131. zchki(name) char *name; {
  1132.     CV(256) path;
  1133.     CV(256) src_path;
  1134.     CV(32) suffix;
  1135.     FILE_STATUS_STRUCT finfo;
  1136.     short status;
  1137.     int x;
  1138.     long size;
  1139.  
  1140.     debug(F110,"zchki checking",name,0);
  1141.  
  1142.     if (!name) return (-1);
  1143.  
  1144.     strcpy (&src_path, name);
  1145.     strcpy (&suffix, "");
  1146.     s$expand_path (&src_path, &suffix, &path, &status);
  1147.     if (status) {
  1148.     debug(F101,"zchki s$expand_path status","",status);
  1149.     return -1; /* doesn't exist or cannot be accessed */
  1150.     }
  1151.  
  1152.     finfo.version = FILE_STAT_VERSION_5;
  1153.     s$get_file_status (&path, &finfo, &status);
  1154.     if (status) {
  1155.     debug(F101,"zchki s$get_file_status status","",status);
  1156.     return -1;
  1157.     }
  1158.  
  1159.     switch ((int) finfo.callers_mode) {
  1160.     case 'r':
  1161.     case 'w':
  1162.         break;        /* exists, and we can read it */
  1163.  
  1164.     case 'e':
  1165.     case 'n':
  1166.     case 'u':
  1167.         return (-3);    /* exists, but protected from reading */
  1168.  
  1169.     default:
  1170.         return (-2);    /* not a file */
  1171.     }
  1172.  
  1173.     switch (finfo.file_organization) {
  1174.     case SEQUENTIAL_FILE:
  1175.     case STREAM_FILE:
  1176.         size = finfo.last_record_number;
  1177.         break;
  1178.  
  1179.     case FIXED_FILE:
  1180.         size = finfo.last_record_number * 
  1181.         finfo.flags_struct.flags_bits_overlay.max_record_size;
  1182.         break;
  1183.  
  1184.     case RELATIVE_FILE:
  1185.         size = finfo.last_record_number * 
  1186.         (finfo.flags_struct.flags_bits_overlay.max_record_size + 2);
  1187.         break;
  1188.  
  1189.     default:    /* uhhhh.... */
  1190.         size = 0;
  1191.         break;
  1192.     }
  1193.  
  1194.     strcpy (nambuf,name);        /* remember the name of the file */
  1195.  
  1196.     if (size > (finfo.blocks_used * 4096))
  1197.     size = (finfo.blocks_used * 4096);
  1198.  
  1199.     if (size < 0)
  1200.     size = 0;
  1201.  
  1202.     return (size);
  1203. }
  1204.  
  1205. /*  Z C H K O  --  Check if output file can be created  */
  1206.  
  1207. /*
  1208.  Returns -1 if write permission for the file would be denied, 0 otherwise.
  1209. */
  1210. int
  1211. zchko(name) char *name; {
  1212.     int i, x;
  1213.     char *s;
  1214.  
  1215.     if (!name) return(-1);        /* Watch out for null pointer. */
  1216.     x = (int)strlen(name);        /* Get length of filename */
  1217.     debug(F111,"zchko length",name,x);
  1218.  
  1219.     return (0);
  1220. }
  1221.  
  1222. /*  Z D E L E T  --  Delete the named file.  */
  1223.  
  1224. int
  1225. zdelet(name) char *name; {
  1226.     debug(F110,"zdelet",name,0);
  1227.     return(remove(name));
  1228. }
  1229.  
  1230. /*  Z R T O L  --  Convert remote filename into local form  */
  1231.  
  1232. /*  For VOS as in UNIX, this means changing uppercase letters to lowercase. */
  1233.  
  1234. VOID
  1235. zrtol(name,name2) char *name, *name2; {
  1236.     char *p; int flag = 0;
  1237.     debug(F101,"zrtol name","",name);
  1238.     debug(F101,"zrtol name2","",name2);
  1239.     if (!name || !name2) return;
  1240.     debug(F101,"zrtol input","",name);
  1241.     p = name2;
  1242.     for ( ; *name != '\0'; name++) {
  1243.     if (*name > ' ') flag = 1;    /* Strip leading blanks and controls */
  1244.     if (flag == 0 && *name < '!') continue;
  1245.         *p++ = isupper(*name) ? tolower(*name) : *name;
  1246.     }
  1247.     *p-- = '\0';            /* Terminate */
  1248.     while (*p < '!' && p > name2)    /* Strip trailing blanks & contronls */
  1249.       *p-- = '\0';
  1250.  
  1251.     debug(F110,"zrtol result",name2,0);
  1252. }
  1253.  
  1254. VOID
  1255. nzrtol(name,name2,fncnv,fnrpath,max)
  1256.     char *name, *name2; int fncnv, fnrpath, max;
  1257. { /* nzrtol */
  1258.     char *p; int flag = 0, n = 0;
  1259.     char tmpbuf[CKMAXPATH+1], *tmp;
  1260.     int devnull = 0;
  1261.     int acase = 0;
  1262.     if (!name2) return;
  1263.     if (!name) name = "";
  1264.     debug(F110,"zrtol original name",name,0);
  1265.  
  1266.     tmpbuf[0] = '\0';
  1267.     tmp = tmpbuf;
  1268.     devnull = !strcmp(name,"/dev/null");
  1269.  
  1270.     if (!devnull && fnrpath == PATH_OFF) { /* RECEIVE PATHNAMES OFF */
  1271.     zstrip(name,&p);
  1272.     strncpy(tmpbuf,p,CKMAXPATH);
  1273.     } else if (!devnull && fnrpath == PATH_ABS) { /* REC PATHNAMES ABSOLUTE */
  1274.     strncpy(tmpbuf,name,CKMAXPATH); 
  1275.     } else if (!devnull && isabsolute(name)) { /* RECEIVE PATHNAMES RELATIVE */
  1276.     sprintf(tmpbuf,".%s",name);
  1277.     } else {                /* Ditto */
  1278.     strncpy(tmpbuf,name,CKMAXPATH); 
  1279.     }
  1280.     tmpbuf[CKMAXPATH] = 0;
  1281.     debug(F110,"zrtol tmpbuf",tmpbuf,0);
  1282.  
  1283.     if (!fncnv || devnull) {        /* Not converting */
  1284.     strncpy(name2,tmpbuf,max);    /* We're done. */
  1285.     return;
  1286.     }
  1287.     name = tmpbuf;            /* Converting */
  1288.  
  1289.     p = name2;
  1290.     for (; *name != '\0' && n < maxnam; name++) {
  1291.     if (*name > ' ') flag = 1;    /* Strip leading blanks and controls */
  1292.     if (flag == 0 && *name < '!')
  1293.       continue;
  1294.     if (isupper(*name))        /* Check for mixed case */
  1295.       acase |= 1;
  1296.     else if (islower(*name))
  1297.       acase |= 2;
  1298.     *p++ = *name;
  1299.     n++;
  1300.     }
  1301.     *p-- = '\0';            /* Terminate */
  1302.     while (*p < '!' && p > name2)    /* Strip trailing blanks & controls */
  1303.       *p-- = '\0';
  1304.  
  1305.     if (*name2 == '\0') {        /* Nothing left? */
  1306.     strcpy(name2,"NONAME");        /* do this... */
  1307.     } else if (acase == 1) {        /* All uppercase? */
  1308.     p = name2;            /* So convert all letters to lower */
  1309.     while (*p) {
  1310.         if (isupper(*p))
  1311.           *p = tolower(*p);
  1312.         p++;
  1313.     }
  1314.     }      
  1315.     debug(F110,"zrtol new name",name2,0);
  1316. }
  1317.  
  1318. /*  Z S T R I P  --  Strip device & directory name from file specification */
  1319.  
  1320. /*  Strip pathname from filename "name", return pointer to result in name2 */
  1321.  
  1322. static char work[257];
  1323.  
  1324. VOID
  1325. zstrip(name,name2) char *name, **name2; {
  1326.     char *cp, *pp;
  1327.     debug(F110,"zstrip before",name,0);
  1328.     pp = work;
  1329. #ifdef DTILDE
  1330.     if (*name == '~') name++;
  1331. #endif /* DTILDE */
  1332.     for (cp = name; *cp != '\0'; cp++) {
  1333.         if (ISDIRSEP(*cp))
  1334.       pp = work;
  1335.     else
  1336.       *pp++ = *cp;
  1337.     }
  1338.     *pp = '\0';                /* Terminate the string */
  1339.     *name2 = work;
  1340.     debug(F110,"zstrip after",*name2,0);
  1341. }
  1342.  
  1343. /*  Z L T O R  --  Local TO Remote */
  1344.  
  1345. VOID
  1346. zltor(name,name2) char *name, *name2; {
  1347.     nzltor(name,name2,1,0,CKMAXPATH);
  1348. }
  1349.  
  1350. /*  N Z L T O R  --  New Local TO Remote */
  1351.  
  1352. VOID
  1353. nzltor(name,name2,fncnv,fnspath,max)
  1354.     char *name, *name2; int fncnv, fnspath, max;
  1355. { /* nzltor */
  1356.     char *cp, *pp;
  1357. #ifdef COMMENT
  1358.     int dc = 0;
  1359. #endif /* COMMENT */
  1360.     int n = 0;
  1361.     char *dotp = NULL;
  1362.     char tmpbuf[CKMAXPATH+1];
  1363.     char *p;
  1364.  
  1365.     debug(F110,"nzltor name",name,0);
  1366.  
  1367.     /* Handle pathname */
  1368.     
  1369.     tmpbuf[0] = 0;
  1370.     if (fnspath == PATH_OFF) {        /* PATHNAMES OFF */
  1371.     zstrip(name,&p);
  1372.     strncpy(tmpbuf,p,CKMAXPATH);
  1373.     tmpbuf[CKMAXPATH] = 0;
  1374.     } else {
  1375.     int ndx;
  1376.  
  1377.     if (fnspath == PATH_ABS) {    /* ABSOLUTE */
  1378.         zfnqfp(name,CKMAXPATH,tmpbuf);
  1379.     } else {            /* RELATIVE */
  1380.         strncpy(tmpbuf,name,CKMAXPATH); 
  1381.     }
  1382.  
  1383.     for (p = tmpbuf; !ISDIRSEP(*p); p++)
  1384.         ;                 /* Look for first '>' */
  1385.     ndx = 0;               /* strip off %system#module */
  1386.     while (*p) {
  1387.         tmpbuf[ndx++] = *p++;
  1388.     }
  1389.  
  1390.     }
  1391.     debug(F110,"zltor tmpbuf",tmpbuf,0);
  1392.  
  1393.     if (!fncnv) {            /* Not converting */
  1394.     strncpy(name2,tmpbuf,max);    /* We're done. */
  1395.     return;
  1396.     }
  1397.     name = tmpbuf;            /* Converting */
  1398.  
  1399.     pp = work;                /* Output buffer */
  1400.  
  1401.     for (cp = name, n = 0; *cp && n < max; cp++,n++) { /* Strip path name */
  1402.  
  1403.     if (islower(*cp))        /* Uppercase letters */
  1404.       *pp++ = toupper(*cp);        /* Change tilde to 'X' */
  1405.     else if (*cp == '~')
  1406.       *pp++ = 'X';
  1407.     else if (*cp == '#')        /* Change number sign to 'X' */
  1408.       *pp++ = 'X';
  1409.     else if (*cp == '*' || *cp == '?')
  1410.       *pp++ = 'X';
  1411.     else if (*cp <= ' ')        /* Change space and controls to 'X' */
  1412.       *pp++ = 'X';
  1413.     else if (*cp == '>' || *cp == '<')    /* Change any dirseps to '/' */
  1414.       *pp++ = '/';                  /* <'s should really be parsed out */
  1415.     else if (*cp == '.') {        /* Change dot to underscore */
  1416.         dotp = pp;            /* Remember where we last did this */
  1417.         *pp++ = '_';
  1418.     } else
  1419.       *pp++ = *cp;
  1420.     }
  1421.     *pp = 0;                /* Tie it off. */
  1422.     if (dotp) *dotp = '.';        /* Restore last dot (if any) */
  1423.     cp = name2;                /* If nothing before dot, */
  1424.     if (*work == '.') *cp++ = 'X';    /* insert 'X' */
  1425.     strncpy(cp,work,max);
  1426.     debug(F110,"zltor name2",name2,0);
  1427. }
  1428.  
  1429.  
  1430.  
  1431. /*  Z C H D I R  --  Change directory  */
  1432. /*
  1433.   Call with:
  1434.     dirnam = pointer to name of directory to change to,
  1435.       which may be "" or NULL to indicate user's home directory.
  1436.   Returns:
  1437.     0 on failure
  1438.     1 on success
  1439. */
  1440. int
  1441. zchdir(dirnam) char *dirnam; {
  1442.     char *hd, *sp, *p;
  1443.     CV(256) rel;
  1444.     CV(256) abs;
  1445.     CV(32) suffix;
  1446.     short status;
  1447.  
  1448.     debug(F110,"zchdir",dirnam,0);
  1449.     if (dirnam == NULL || dirnam == "" || *dirnam == '\0') /* If arg is null */
  1450.       dirnam = zhome();            /* use user's home directory. */
  1451.     sp = dirnam;
  1452.     debug(F110,"zchdir 2",dirnam,0);
  1453.  
  1454. #ifdef DTILDE
  1455.     hd = tilde_expand(dirnam);        /* Attempt to expand tilde */
  1456.     if (*hd == '\0') hd = dirnam;    /* in directory name. */
  1457. #else
  1458.     hd = dirnam;
  1459. #endif /* DTILDE */
  1460.     debug(F110,"zchdir 3",hd,0);
  1461.  
  1462.  
  1463.     strcpy(&rel, hd);
  1464.     strcpy(&abs, "");
  1465.     strcpy(&suffix, "");
  1466.     status = 0;
  1467.  
  1468.     s$expand_path(&rel, &suffix, &abs, &status);
  1469.     debug(F111,"zchdir4",hd,status);
  1470.     if(status)
  1471.     return(0);
  1472.  
  1473.     s$change_current_dir(&abs, &status);
  1474.     debug(F111,"zchdir5",hd,status);
  1475.     if(status)
  1476.      return(0);
  1477.     else
  1478.     return(1);
  1479. }
  1480.  
  1481. /*  Z H O M E  --  Return pointer to user's home directory  */
  1482.  
  1483. /* This is a royal pain under some operating systems, involving reading    */
  1484. /* the password file, etc.  Under VOS we just ask, since the user is    */
  1485. /* allowed to specify a different home dir at login time, they make it  */
  1486. /* easy to get the answer.                        */
  1487.  
  1488. char *
  1489. zhome() {
  1490.     CV(256) cv_home;
  1491.     static char homebuf[257];
  1492.     char *home;
  1493.  
  1494.     s$get_home_dir (&cv_home);
  1495.     strcpy (homebuf, &cv_home);
  1496.     home = homebuf;
  1497.  
  1498.     return(home ? home : ".");
  1499. }
  1500.  
  1501. /*  Z G T D I R  --  Return pointer to user's current directory  */
  1502.  
  1503. char *
  1504. zgtdir() {
  1505.     char *buf;
  1506.     static char currbuf[257];
  1507.     CV(256) cv_curr;
  1508.  
  1509.     s$get_current_dir (&cv_curr);
  1510.     strcpy (currbuf, &cv_curr);
  1511.     buf = currbuf;
  1512.  
  1513.     return buf;
  1514. }
  1515.  
  1516. /*  Z X C M D -- Run a system command so its output can be read like a file */
  1517.  
  1518. /* return 0 on error, 1 on success, -1 if file number is bad. */
  1519.  
  1520. int
  1521. zxcmd(filnum,comand) int filnum; char *comand; {
  1522.     int out;
  1523.     short organization = SEQUENTIAL_FILE;
  1524.     short max_record_len = 4096;
  1525.     short status;
  1526.     short append_switch = 0;
  1527.     CV(256) temp_path;
  1528.     char c_temp_path[257];
  1529.     int save_binary;
  1530.  
  1531.     debug(F111,"zxcmd called",comand,filnum);
  1532.     if (chkfn(filnum) < 0) return(-1);    /* Need a valid Kermit file number. */
  1533.  
  1534.     if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */
  1535.       return(0);
  1536.  
  1537.     out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
  1538.     if (out) {
  1539.       debug(F101,"zxcmd cannot open for output","",filnum);
  1540.       return (0); /* we can't write to the file */
  1541.     }
  1542.  
  1543.     s$get_temp_file (&organization, &max_record_len, &temp_path, &status);
  1544.     if (status) {
  1545.     debug(F101,"zxcmd s$get_temp_file, status","",status);
  1546.     return (0); /* error */
  1547.     }
  1548.     strcpy (c_temp_path, &temp_path);
  1549.     s$attach_default_output (&temp_path, &append_switch, &status);
  1550.     if (status) {
  1551.     debug(F111,"zxcmd s$attach_default_output, status",c_temp_path,status);
  1552.     return (0); /* error */
  1553.     }
  1554.     *nambuf = '\0';        /* forget old name */
  1555.     status = zsyscmd(comand); /* Hope this works. get VOS status back. */ 
  1556.     debug(F111,"zxcmd zsyscmd status",c_temp_path,status);
  1557.     if (status) { /* error */
  1558.     s$detach_default_output (&status); /* ignore this status */
  1559.     s$delete_file_on_close (&temp_path, &status); /* ignore this status */
  1560.     return (0); /* error */
  1561.     }
  1562.     s$detach_default_output (&status);
  1563.     if (status) {
  1564.     debug(F111,"zxcmd s$detach_default_output, status",c_temp_path,status);
  1565.     s$delete_file_on_close (&temp_path, &status); /* ignore this status */
  1566.     return (0); /* error */
  1567.     }
  1568.     save_binary = binary;
  1569.     binary = 0; /* it's a text file */
  1570.     if (0 == zopeni (filnum, c_temp_path)) { /* error? */
  1571.     binary = save_binary;
  1572.     debug(F100,"zxcmd zopeni failed","",0);
  1573.     s$delete_file_on_close (&temp_path, &status); /* ignore this status */
  1574.     return (0); /* error */
  1575.     }
  1576.     binary = save_binary;
  1577.     s$delete_file_on_close (&temp_path, &status); /* will poof at zclosf */
  1578.     if (status) {
  1579.     debug(F111,"zxcmd s$delete_file_on_close, status",c_temp_path,status);
  1580.     /* continue, it will still read, just leaving a loose file around */
  1581.     }
  1582.  
  1583.     return(1); /* Success! */
  1584. }
  1585.  
  1586. /*  Z C L O S F  - wait for the child fork to terminate and close the pipe. */
  1587.  
  1588. int
  1589. zclosf(filnum) int filnum; {
  1590.     debug(F101,"zclosf called","",filnum);
  1591.     return(1);
  1592. }
  1593.  
  1594. /*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
  1595. /*
  1596.   Returns the number of files that match fn1, with data structures set up
  1597.   so  that first file (if any) will be returned by the next znext() call.
  1598.   Depends on external variable wildxpand:  0 means  we  expand  wildcards
  1599.   internally, nonzero means we call the shell to do it. In VOS, we always
  1600.   do it.
  1601. */
  1602.  
  1603. int
  1604. zxpand(fn) char *fn; {
  1605.     char *p;
  1606.  
  1607. #ifdef DTILDE                /* Built with tilde-expansion? */
  1608.     char *tnam;
  1609. #endif /* DTILDE */
  1610.     debug(F111,"zxpand entry",fn,wildxpand);
  1611. #ifdef DTILDE                /* Built with tilde-expansion? */
  1612.     if (*fn == '~') {            /* Starts with tilde? */
  1613.     tnam = tilde_expand(fn);    /* Try to expand it. */
  1614.     if (tnam) fn = tnam;
  1615.     }
  1616.     debug(F110,"zxpand after tilde_x",fn,0);
  1617. #endif /* DTILDE */
  1618.  
  1619.     fcount = shxpand(fn,mtchs,MAXWLD); /* Shell [JUST KIDDING!] */
  1620.  
  1621.     if (fcount > 0) {
  1622.     mtchptr = mtchs;        /* Save pointer for next. */
  1623.     }
  1624.     if (fcount > 0) {
  1625.     debug(F111,"zxpand ok",mtchs[0],fcount);
  1626.     nxpand = fcount;
  1627.     return(fcount);
  1628.     }
  1629.     debug(F111,"zxpand fgen1",fn,fcount); /* Didn't get one, or got too many */
  1630.     p = malloc((int)strlen(fn) + 10);    /* Make space */
  1631.     if (!p) return(0);
  1632.     zrtol(fn,p);            /* Try again, maybe lowercase */
  1633.  
  1634.     fcount = shxpand(p,mtchs,MAXWLD); /* Shell */
  1635.  
  1636.     if (fcount > 0) {            /* Got one? */
  1637.     mtchptr = mtchs;        /* Save pointer for next. */
  1638.     debug(F111,"zxpand fgen2 ok",mtchs[0],fcount);
  1639.     } else debug(F111,"zxpand 2 not ok",p,fcount);
  1640.     free(p);
  1641.     nxpand = fcount;
  1642.     return(fcount);
  1643. }
  1644.  
  1645.  
  1646. /*  Z N E X T  --  Get name of next file from list created by zxpand(). */
  1647. /*
  1648.  Returns >0 if there's another file, with its name copied into the arg string,
  1649.  or 0 if no more files in list.
  1650. */
  1651. int
  1652. znext(fn) char *fn; {
  1653.     if (fcount-- > 0) strcpy(fn,*mtchptr++);
  1654.     else *fn = '\0';
  1655.  
  1656.     debug(F111,"znext",fn,fcount+1);
  1657.     return(fcount+1);
  1658. }
  1659.  
  1660. /*  Z X R E W I N D  -- Rewinds the zxpand() list */
  1661.  
  1662. int
  1663. zxrewind() {
  1664.     debug(F111,"zxrewind",(mtchs?"mtchs ok":"mtchs null"),nxpand);
  1665.     if (!mtchs) return(-1);
  1666.     fcount = nxpand;
  1667.     mtchptr = mtchs;
  1668.     return(nxpand);
  1669. }
  1670.  
  1671.  
  1672. /*  Z C H K S P A  --  Check if there is enough space to store the file  */
  1673.  
  1674. /*
  1675.  Call with file specification f, size n in bytes.
  1676.  Returns -1 on error, 0 if not enough space, 1 if enough space.
  1677.  
  1678.  On  VOS, we pick up the volume name from first part of the path name, then
  1679.  check to make sure it's valid, then check the amount of space available on
  1680.  that volume.  Also, VOS won't let you use the last 200 blocks of the  file
  1681.  partition  on  the  volume, so we count off for that.  Also, the number of
  1682.  indirect blocks is only  partially  accounted  for.  This  isn't  perfect,
  1683.  because  some  of  the different file organizations can cause the reported
  1684.  size of the file not to match the actual size of the  file,  but  this  is
  1685.  only  a problem with files with a large number of records and certain file
  1686.  orgs.  We don't try to figure this out, instead we let the  transfer  fail
  1687.  when  the  disk gets full, which is what a number of implementations do in
  1688.  all cases.
  1689.  
  1690. */
  1691. int
  1692. #ifdef CK_ANSIC
  1693. zchkspa(char *f, long n)
  1694. #else
  1695. zchkspa(f,n) char *f; long n;
  1696. #endif /* CK_ANSIC */
  1697. /* zchkspa() */ {
  1698.     CV(256) path;
  1699.     CV(256) src_path;
  1700.     char    cpath[257];
  1701.     CV(32) suffix;
  1702.     CV(66) disk_name;
  1703.     short status;
  1704.     int x;
  1705.     struct disk_info dinfo;
  1706.     char *cp;
  1707.     long blocks_left;
  1708.  
  1709.     debug(F111,"zchkspa checking",f,n);
  1710.  
  1711.     if (!f) return (-1);
  1712.  
  1713.     strcpy (&src_path, f);
  1714.     strcpy (&suffix, "");
  1715.     s$expand_path (&src_path, &suffix, &path, &status);
  1716.     if (status) {
  1717.     debug(F101,"zchkspa s$expand_path status","",status);
  1718.     return -1; /* an error occurred */
  1719.     }
  1720.  
  1721.     strcpy (cpath, &path);
  1722.     cp = strchr (cpath, DIRSEP);
  1723.     if (cp)
  1724.     *cp = '\0'; /* cut off the directory name from the disk name */
  1725.  
  1726.     strcpy (&disk_name, cpath);
  1727.     dinfo.version = 1;
  1728.     s$get_disk_info (&disk_name, &dinfo, &status);
  1729.     if (status) {
  1730.     debug(F111,"zchkspa s$get_disk_info status",cpath,status);
  1731.     return -1; /* an error occurred */
  1732.     }
  1733.  
  1734.     blocks_left = dinfo.file_partition_size - dinfo.file_partition_used;
  1735.     blocks_left -= 200; /* can't use last 200 blocks */
  1736.  
  1737.     n /= 4096;
  1738.     n++;
  1739.  
  1740.     /* count some of the indirect blocks */
  1741.     /* I used to know how to count these really... */
  1742.  
  1743.     if (n > 16)
  1744.     n++;
  1745.  
  1746.     if (n > 256)
  1747.     n++;
  1748.  
  1749.     if (n >= blocks_left)
  1750.     return(0);                /* not enough space */
  1751.     else
  1752.     return(1);                /* Okay! */
  1753. }
  1754.  
  1755.  
  1756. /*  Z N E W N  --  Make a new name for the given file  */
  1757.  
  1758. /*
  1759.   Given  the  name,  fn, of a file that already exists, this function builds a
  1760.   new name of the form "<oldname>.~<n>~", where  <oldname>  is  argument  name
  1761.   (fn),  and  <n>  is  a  version number, one higher than any existing version
  1762.   number for that file, up to 9999.  This format is consistent with that  used
  1763.   by GNU EMACS.  If the constructed name is too long for the system's maximum,
  1764.   enough  characters  are  truncated from the end of <fn> to allow the version
  1765.   number to fit.  If no free version numbers  exist  between  1  and  9999,  a
  1766.   version  number  of  "xxxx"  is  used.  Returns a pointer to the new name in
  1767.   argument s.
  1768. */
  1769.  
  1770. VOID
  1771. znewn(fn,s) char *fn, **s; {
  1772. #define ZNEWNBL 255
  1773. #define ZNEWNMD 4
  1774.  
  1775.     static char buf[ZNEWNBL+1];
  1776.     char *bp, *xp, *yp;
  1777. #ifdef OS2
  1778.     char *zp, ch, temp[14];
  1779. #endif /* OS2 */
  1780.     int len = 0, d = 0, n, t, i, j, k, power = 1;
  1781.  
  1782.     int max = MAXNAMLEN;        /* Maximum name length */
  1783.  
  1784.     if (max < 14) max = 14;        /* Make it reasonable */
  1785.     if (max > ZNEWNBL) max = ZNEWNBL;
  1786.     bp = buf;                /* Buffer for building new name */
  1787.     yp = fn;
  1788.     while (*yp) {            /* Copy old name into buffer */
  1789.     *bp++ = *yp++;
  1790.     if (len++ > ZNEWNBL) break;    /* ...up to buffer length */
  1791.     }
  1792.     *s = NULL;
  1793.     for (i = 1; i < ZNEWNMD + 1; i++) {    /* Version numbers up to 10**i - 1 */
  1794.     power *= 10;            /* Next power of 10 */
  1795.     j = max - len;            /* Space left for version number */
  1796.     k = 3 + i;            /* Space needed for it */
  1797.     if (j < k) {            /* Make room if necessary */
  1798.         len -= (k - j);        /* Adjust length of filename */
  1799.         bp = buf + len;        /* Point to new end */
  1800.     }
  1801.     *bp++ = '*';            /* Put a star on the end (UNIX) */
  1802.     *bp-- = '\0';            /* Terminate with null */
  1803.  
  1804.     n = zxpand(buf);        /* Expand the resulting wild name */
  1805.                     /* n is the number of matches */
  1806.     while (n-- > 0) {        /* Find any existing name.~n~ files */
  1807.         xp = *mtchptr++;        /* Point at matching name */
  1808.         xp += len;            /* Look for .~<n>~ at the end of it */
  1809.         if (*xp == '.' && *(xp+1) == '~') {    /* Has a version number */
  1810.         t = atoi(xp+2);                /* Get it */
  1811.         if (t > d) d = t;    /* Save d = highest version number */
  1812.         }
  1813.     }
  1814.     if (d < power-1) {        /* Less than maximum possible? */
  1815.         sprintf(bp,".~%d~",d+1);    /* Yes, make "name.~<d+1>~" */
  1816.         *s = buf;            /* Point to new name */
  1817.         break;            /* Done, return it */
  1818.     }
  1819.     }
  1820.     if (*s == NULL) {
  1821.     sprintf(bp,".~xxxx~");        /* Too many, use xxxx. */
  1822.     *s = buf;
  1823.     }
  1824.  
  1825.     return;
  1826. }
  1827.  
  1828.  
  1829. /*  Z R E N A M E  --  Rename a file  */
  1830.  
  1831. /*  Call with old and new names */
  1832. /*  Returns 0 on success, -1 on failure. */
  1833.  
  1834. int
  1835. zrename(old,new) char *old, *new; {
  1836.    debug(F110,"zrename old",old,0);
  1837.    debug(F110,"        new",new,0);
  1838.    return rename(old, new);
  1839. }
  1840.  
  1841.  
  1842. /*  Z C O P Y  --  Copy a file  */
  1843. /*
  1844.    Call with source and destination names.
  1845.    If destination name is the name of a directory, the source file is
  1846.    copied to that directory with the original name.
  1847.    Returns 0 on success, -1 on failure.
  1848. */
  1849. int
  1850. zcopy(source,destination) char *source, *destination; {
  1851.     CV(256) cvsrc;
  1852.     CV(256) cvdest;
  1853.     CV(32)  suffix;
  1854.     CV(256) cvpath;
  1855.     CV(32)  caller;
  1856.     short   switches;
  1857.     short   status;
  1858.     char p[257], *s = destination;
  1859.     int x;
  1860.     
  1861.     debug(F110,"zcopy source",source,0);
  1862.     debug(F110,"zcopy destination",s,0);
  1863.     /* it's easier to code this way, not faster... */
  1864.     if (isdir(destination)) {
  1865.     char *q = NULL;
  1866.     x = strlen(destination);
  1867.  
  1868.     strcpy(p,destination);        /* Directory part */
  1869.     if (!ISDIRSEP(*(destination+x-1))) /* Separator, if needed */
  1870.       strcat(p,">");
  1871.     zstrip(source,&q);        /* Strip path part from old name */
  1872.     strcat(p,q);            /* Concatenate to new directory */
  1873.     s = p;
  1874.     debug(F110,"zcopy dir",s,0);
  1875.     } else
  1876.         debug(F110,"zcopy no dir",s,0);
  1877.  
  1878.     strcpy(&cvpath,source);
  1879.     strcpy(&suffix,"");
  1880.     status = 0;
  1881.     s$expand_path(&cvpath,&suffix,&cvsrc,&status);
  1882.     if (status != 0) {
  1883.         debug(F101,"zcopy failed expand src","",status);
  1884.     return(-1);
  1885.     }
  1886.  
  1887.     strcpy(&cvpath,s);
  1888.     s$expand_path(&cvpath,&suffix,&cvdest,&status);
  1889.     if (status != 0) {
  1890.         debug(F101,"zcopy failed expand dest","",status);
  1891.     return(-1);
  1892.     }
  1893.  
  1894.     switches = 2; /* OVERWRITE */
  1895.     strcpy(&caller,"kermit");
  1896.     s$copy_file(&cvsrc,&cvdest,&caller,&switches,&status);
  1897.     if (status != 0) {
  1898.         debug(F101,"zcopy failed s$copy_file","",status);
  1899.     return(-1);
  1900.     }
  1901.  
  1902.     return(0);
  1903. }
  1904.  
  1905.  
  1906. /*  Z S A T T R */
  1907. /*
  1908.  Fills in a Kermit file attribute structure for the file which is to be sent.
  1909.  Returns 0 on success with the structure filled in, or -1 on failure.
  1910.  If any string member is null, then it should be ignored.
  1911.  If any numeric member is -1, then it should be ignored.
  1912. */
  1913. int
  1914. zsattr(xx) struct zattr *xx; {
  1915.     long k;
  1916.     CV(256) path;
  1917.     CV(256) src_path;
  1918.     CV(32) suffix;
  1919.     FILE_STATUS_STRUCT finfo;
  1920.     short status;
  1921.     int x;
  1922.     char *cp;
  1923.     static char  creator[67];
  1924.     static char  cdate[20];
  1925.     struct tm *tp;
  1926.  
  1927.     k = iflen % 1024L;            /* File length in K */
  1928.     if (k != 0L) k = 1L;
  1929.     xx->lengthk = (iflen / 1024L) + k;
  1930.     xx->type.len = 0;            /* File type can't be filled in here */
  1931.     xx->type.val = "";
  1932.  
  1933.     if (*nambuf) {
  1934.         debug(F110,"zsattr checking",nambuf,0);
  1935.  
  1936.     strcpy (&src_path, nambuf);
  1937.     strcpy (&suffix, "");
  1938.     s$expand_path (&src_path, &suffix, &path, &status);
  1939.     if (status) {
  1940.         debug(F101,"zsattr s$expand_path status","",status);
  1941.         return -1; /* doesn't exist or cannot be accessed */
  1942.     }
  1943.  
  1944.     finfo.version = FILE_STAT_VERSION_5;
  1945.     s$get_file_status (&path, &finfo, &status);
  1946.     if (status) {
  1947.         debug(F101,"zsattr s$get_file_status status","",status);
  1948.         return -1;
  1949.     }
  1950.  
  1951.     tp = localtime ((const time_t *) &finfo.date_time_modified);
  1952.     if (NULL != tp) {
  1953.         sprintf(cdate, "%04d%02d%02d %02d:%02d:%02d", tp->tm_year + 1900,
  1954.         tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min,
  1955.         tp->tm_sec);
  1956.         xx->date.val = cdate;    /* File creation date */
  1957.         xx->date.len = (int)strlen(xx->date.val);
  1958.     }
  1959.     else {
  1960.         xx->date.val = "";    /* File creation date */
  1961.         xx->date.len = 0;
  1962.     }
  1963.  
  1964.     strcpy (creator, &finfo.author);
  1965.     cp = strchr (creator, '.');
  1966.     *cp++ = '\0';
  1967.  
  1968.     /* (person_name (file_info author)) */
  1969.     xx->creator.val = creator;        /* File creator */
  1970.     xx->creator.len = strlen (creator);
  1971.  
  1972.     /* (group_name (file_info author)) */
  1973.     xx->account.len = strlen (cp);        /* File account */
  1974.     xx->account.val = cp;
  1975.  
  1976.     xx->recfm.len = 0;            /* Record format */
  1977.     xx->recfm.val = "";
  1978.  
  1979.     } else {
  1980.     xx->date.len = 0;
  1981.     xx->date.val = "";
  1982.  
  1983.     xx->creator.len = 0;        /* File creator */
  1984.     xx->creator.val = "";
  1985.  
  1986.     xx->recfm.len = 0;            /* Record format */
  1987.     xx->recfm.val = "";
  1988.  
  1989.     xx->account.len = 0;        /* File account */
  1990.     xx->account.val = "";
  1991.     }
  1992.     xx->area.len = 0;            /* File area */
  1993.     xx->area.val = "";
  1994.     xx->password.len = 0;        /* Area password */
  1995.     xx->password.val = "";
  1996.     xx->blksize = -1L;            /* File blocksize */
  1997.     xx->xaccess.len = 0;        /* File access */
  1998.     xx->xaccess.val = "";
  1999.     xx->encoding.len = 0;        /* Transfer syntax */
  2000.     xx->encoding.val = 0;
  2001.     xx->disp.len = 0;            /* Disposition upon arrival */
  2002.     xx->disp.val = "";
  2003.     xx->lprotect.len = 0;        /* Local protection */
  2004.     xx->lprotect.val = "";
  2005.     xx->gprotect.len = 0;        /* Generic protection */
  2006.     xx->gprotect.val = "";
  2007.     xx->systemid.len = 2;        /* System ID */
  2008.     xx->systemid.val = "MV";        /* MISC, VOS */
  2009.     xx->sysparam.len = 0;        /* System-dependent parameters */
  2010.     xx->sysparam.val = "";
  2011.     xx->length = iflen;            /* Length */
  2012.     return(0);
  2013. }
  2014.  
  2015. /* Z F C D A T  --  Get file creation date */
  2016. /*
  2017.   Call with pointer to filename.
  2018.   On success, returns pointer to creation date in yyyymmdd hh:mm:ss format.
  2019.   On failure, returns pointer to null string.
  2020. */
  2021. char *
  2022. zfcdat(name) char *name; {
  2023.     static char  cdate[20];
  2024.     CV(256) path;
  2025.     CV(256) src_path;
  2026.     CV(32) suffix;
  2027.     FILE_STATUS_STRUCT finfo;
  2028.     short status;
  2029.     struct tm  *tp;
  2030.  
  2031.     debug(F110,"zfcdate called",name,0);
  2032.     strcpy (&src_path, name);
  2033.     strcpy (&suffix, "");
  2034.     s$expand_path (&src_path, &suffix, &path, &status);
  2035.     if (status) {
  2036.     debug(F101,"zfcdate s$expand_path status","",status);
  2037.     return ""; /* error */
  2038.     }
  2039.  
  2040.     finfo.version = FILE_STAT_VERSION_5;
  2041.     s$get_file_status (&path, &finfo, &status);
  2042.     if (status) {
  2043.     debug(F101,"zfcdate s$get_file_status status","",status);
  2044.     return "";
  2045.     }
  2046.  
  2047.     tp = localtime ((const time_t *) &finfo.date_time_modified);
  2048.     if (NULL == tp)
  2049.     return("");
  2050.  
  2051.     sprintf(cdate, "%04d%02d%02d %02d:%02d:%02d", tp->tm_year + 1900,
  2052.     tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min,
  2053.     tp->tm_sec);
  2054.  
  2055.     return cdate;
  2056. }
  2057.  
  2058. /* Z S T I M E  --  Set creation date for incoming file */
  2059. /*
  2060.  Call with:
  2061.  f  = pointer to name of existing file.
  2062.  yy = pointer to a Kermit file attribute structure in which yy->date.val
  2063.       is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00.
  2064.  x  = is a function code: 0 means to set the file's creation date as given.
  2065.       1 means compare the given date with the file creation date.
  2066.  Returns:
  2067.  -1 on any kind of error.
  2068.   0 if x is 0 and the file date was set successfully.
  2069.   0 if x is 1 and date from attribute structure <= file creation date.
  2070.   1 if x is 1 and date from attribute structure > file creation date.
  2071. */
  2072.  
  2073. int
  2074. zstime(f,yy,x) char *f; struct zattr *yy; int x; {
  2075.     int r = -1;                /* return code */
  2076.     CV(256) path;
  2077.     CV(256) src_path;
  2078.     CV(32) suffix;
  2079.     FILE_STATUS_STRUCT finfo;
  2080.     short status;
  2081.     struct tm  *tp;
  2082.     struct tm  mytm;
  2083.     char fdate[20];
  2084.     char adate[32];
  2085.     CV(32) cvtime;
  2086.     int dlen; /* length of date part */
  2087.     time_t file_times[4]; /* created, used, modified, saved */
  2088.     time_t idate;
  2089.     char *cp;
  2090.     int scanval;
  2091.     time_t now;
  2092.  
  2093.     now = time(NULL);
  2094.     debug(F111,"zstime called, x",f,x);
  2095.     if (NULL == yy) {
  2096.     debug(F100,"zstime called with NULL attr struct","",0);
  2097.     return r;
  2098.     }
  2099.     if (yy->date.len > 17) { /* too long */
  2100.     debug(F111,"zstime bad date",yy->date.val,yy->date.len);
  2101.     return r;
  2102.     }
  2103.     strncpy (adate, yy->date.val, yy->date.len);
  2104.     adate[yy->date.len] = '\0';
  2105.     debug(F111,"zstime attr date",adate,yy->date.len);
  2106.  
  2107.     memset (&mytm, 0, sizeof mytm); /* clear it out initially */
  2108.     
  2109.     /* "normalize" attribute date string */
  2110.     if (NULL != (cp = strchr (adate, ' '))) {    /* is time present? */
  2111.       dlen = (int)(cp - adate);
  2112.     cp++; /* now get time, let sscanf deal with missing seconds */
  2113.     scanval = sscanf(cp,"%2d:%2d:%2d",
  2114.         &mytm.tm_hour, &mytm.tm_min, &mytm.tm_sec);
  2115.         debug(F111,"   sscan time",cp,scanval);
  2116.     if (scanval != 2 && scanval != 3)
  2117.         return (-1);        /* this is an error */
  2118.     }
  2119.     else
  2120.     dlen = strlen (adate);
  2121.  
  2122.     if (dlen == 8) {         /* yyyymmdd */
  2123.     scanval = sscanf(adate,"%4d%2d%2d",
  2124.         &mytm.tm_year, &mytm.tm_mon, &mytm.tm_mday);
  2125.     debug(F111,"   sscan long date",adate,scanval);
  2126.     if (scanval != 3)
  2127.         return (-1);
  2128.     mytm.tm_year -= 1900;
  2129.     }
  2130.     else if (dlen == 6) {    /* yymmdd */
  2131.     sscanf(adate,"%2d%2d%2d",
  2132.         &mytm.tm_year, &mytm.tm_mon, &mytm.tm_mday);
  2133.     debug(F111,"   sscan short date",adate,scanval);
  2134.     if (scanval != 3)
  2135.         return (-1);
  2136.     if (mytm.tm_year < 80)             /* VOS dates start at 1980 */
  2137.         mytm.tm_year += 100;        /* UNIX could be 1975...   */
  2138.     }
  2139.     else {                    /* error */
  2140.     debug(F110,"zstime bad date",adate,0);
  2141.     return (-1);
  2142.     }
  2143.  
  2144.     mytm.tm_mon -= 1;
  2145.  
  2146.     strcpy (&src_path, f);
  2147.     strcpy (&suffix, "");
  2148.     s$expand_path (&src_path, &suffix, &path, &status);
  2149.     if (status) {
  2150.     debug(F101,"zstime s$expand_path status","",status);
  2151.     return (-1); /* error */
  2152.     }
  2153.  
  2154.     finfo.version = FILE_STAT_VERSION_5;
  2155.     s$get_file_status (&path, &finfo, &status);
  2156.     if (status) {
  2157.     debug(F101,"zstime s$get_file_status status","",status);
  2158.     return (-1);
  2159.     }
  2160.  
  2161.     if (1 == x) { /* compare times only */
  2162.     tp = localtime ((const time_t *) &finfo.date_time_modified);
  2163.     if (NULL == tp)
  2164.         return (-1);
  2165.  
  2166.     sprintf(fdate, "%04d%02d%02d %02d:%02d:%02d", tp->tm_year + 1900,
  2167.         tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min,
  2168.         tp->tm_sec);
  2169.  
  2170.         debug(F111,"zstime file mod date",fdate,finfo.date_time_modified);
  2171.  
  2172.     sprintf(adate, "%04d%02d%02d %02d:%02d:%02d", mytm.tm_year + 1900,
  2173.         mytm.tm_mon + 1, mytm.tm_mday, mytm.tm_hour, mytm.tm_min,
  2174.         mytm.tm_sec);
  2175.  
  2176.         debug(F110,"zstime adate",adate,0);
  2177.  
  2178.     if (strcmp (adate, fdate) > 0)
  2179.         r = 1;
  2180.     else
  2181.         r = 0;
  2182.      debug(F101,"zstime compare","",r);
  2183.     return r;
  2184.     }
  2185.  
  2186.     /* set date on existing file */
  2187.     sprintf (adate,"%02.2ld-%02.2ld-%02.2ld %02.2ld:%02.2ld:%02.2ld",
  2188.     mytm.tm_year, mytm.tm_mon + 1, mytm.tm_mday,
  2189.     mytm.tm_hour, mytm.tm_min, mytm.tm_sec);
  2190.  
  2191.     strcpy (&cvtime, adate);
  2192.     s$cv_to_int_date_time (&cvtime, &idate, &status);
  2193.     if (status) {
  2194.     debug(F111,"zstime s$cv_to_int_date_time status",adate,status);
  2195.     return (-1);
  2196.     }
  2197.  
  2198.     file_times[0] = idate;            /* time created */
  2199.     file_times[1] = now;            /* time used */
  2200.     file_times[2] = idate;            /* time modified */
  2201.     file_times[3] = 0;                /* time saved */
  2202.  
  2203.     s$set_object_times (&path, &file_times[0], &status);
  2204.     if (status) {
  2205.     debug(F111,"zstime s$set_object_times status",adate,status);
  2206.     return (-1);
  2207.     }
  2208.  
  2209.     s$set_expiration_date (&path, &now, &status);
  2210.     if (status) {
  2211.     debug(F111,"zstime s$set_expiration_date status",adate,status);
  2212.     return (-1);
  2213.     }
  2214.  
  2215.     return(0);
  2216. }
  2217.  
  2218. /* Find initialization file. */
  2219.  
  2220. int
  2221. zkermini() {
  2222. /*  nothing here for VOS.  This function added for benefit of VMS Kermit.  */
  2223. /*  VOS always uses "(home_dir)>start_up.kermit"                */
  2224.     return(0);
  2225. }
  2226.  
  2227. #ifndef NOFRILLS
  2228. int
  2229. zmail(p,f) char *p; char *f; {        /* Send file f as mail to address p */
  2230. /*
  2231.   Returns 0 on success
  2232.    2 if mail delivered but temp file should not be deleted
  2233.   -2 if mail can't be delivered
  2234. */
  2235.     char buff[300];
  2236.     int r;
  2237.     int len;
  2238.  
  2239.     len = sprintf (buff, 
  2240.     "!send_mail '%s' 'Kermit:%s' -priority '%s' -letter_path '%s'",
  2241.     p, f, "normal", f);
  2242.     debug(F110,"zmail",buff,len);
  2243.     r = zsyscmd (buff);
  2244.     if (r != 0) {
  2245.     debug(F101,"zmail call to zsyscmd","",r);
  2246.     return(-2);
  2247.     }
  2248.  
  2249.     return 0;
  2250. }
  2251. #endif /* NOFRILLS */
  2252.  
  2253. #ifndef NOFRILLS
  2254. int
  2255. zprint(p,f) char *p; char *f; {        /* Print file f with options p */
  2256. /*
  2257.   Returns 0 on success
  2258.    3 if file is printed but temp file can't be deleted
  2259.   -3 if file can't be printed
  2260. */
  2261.     char buff[300];
  2262.     int r;
  2263.     int len;
  2264.  
  2265.     len = sprintf (buff, "!print -delete %s %s", f, p);
  2266.     debug(F110,"zprint",buff,len);
  2267.     r = zsyscmd (buff);
  2268.     if (r != 0)
  2269.     return(-3);
  2270.  
  2271.     /* the spooler will delete the file after it is printed */
  2272.     /**/ /* should this be 3? check caller */
  2273.     return 0;
  2274. }
  2275. #endif /* NOFRILLS */
  2276.  
  2277. /* S H X P A N D  -- expand star names appropriate to the user's shell.
  2278.    In VOS, use the s$expand_star command, which is the same as the command
  2279.    processor uses.  It is also much easier than doing it our self.
  2280. */
  2281. int
  2282. shxpand(pat,namlst,len) char *pat, *namlst[]; int len; {
  2283.     char *p, *q;            /* Workers */
  2284.     int i, x, retcode; char c;        /* ... */
  2285.     CV(256) relpath;
  2286.     CV(32)  suffix;
  2287.     CV(256) fullpath;
  2288.     char dirnam[256];
  2289.     short status;
  2290.     struct {
  2291.     short version;
  2292.     struct {
  2293.         CV(32) objnam;
  2294.         short         objtyp;
  2295.     } entries[1];
  2296.     } *expinfo = NULL;
  2297.     short switches;
  2298.     short maxnbr;
  2299.     short nbrmatch;
  2300.     short dummy;
  2301.  
  2302.     debug(F111,"shxpand entered pat=",pat,len);
  2303.     for (i = 0; i < oldmtchs; i++)    /* Free previous file list */
  2304.       free(namlst[i]);
  2305.     
  2306.     oldmtchs = 0;
  2307.     strcpy (&relpath, pat);
  2308.     strcpy (&suffix, "");        /* don't append a suffix */
  2309.     s$expand_path (&relpath, &suffix, &fullpath, &status);
  2310.     if (status) {
  2311.     debug(F111,"shxpand expand_path failed",pat,status);
  2312.     return 0;
  2313.     }
  2314.  
  2315.     strcpy (dirnam, &fullpath);
  2316.     *(strrchr (dirnam, DIRSEP) + 1) = '\0'; /* cut it off after last dir */
  2317.  
  2318.     switches = 1; /* FILES, no dirs, no links, don't sort */
  2319.     maxnbr = 0;   /* don't want names, just how many */
  2320.     dummy = 1;      /* looks like expinfo->version, with no data part */
  2321.     s$expand_star (&fullpath, &switches, &maxnbr, &nbrmatch, &dummy, &status);
  2322.     if (status) {
  2323.     debug(F111,"shxpand starmatch 1 failed",pat,status);
  2324.     return 0;
  2325.     }
  2326.  
  2327.     debug(F111,"shxpand found matches",pat,nbrmatch);
  2328.     if (0 == nbrmatch) /* aren't any matches anyway */
  2329.     return 0;
  2330.  
  2331.     nbrmatch += 10; /* add a little bit */
  2332.     if (nbrmatch > MAXWLD)
  2333.       nbrmatch = MAXWLD;
  2334.  
  2335.     expinfo = malloc (sizeof (*expinfo->entries) * (nbrmatch - 1)
  2336.               + sizeof (*expinfo));
  2337.     if (NULL == expinfo) {
  2338.     debug(F101,"shxpand expinfo malloc","",(int)expinfo);
  2339.     return 0;
  2340.     }
  2341.     switches = 9; /* FILES, no dirs, no links, SORT */
  2342.     maxnbr = nbrmatch;
  2343.     expinfo->version = 1;
  2344.     s$expand_star (&fullpath, &switches, &maxnbr, &nbrmatch, expinfo, &status);
  2345.     if (status) {
  2346.     debug(F111,"shxpand starmatch 2 failed",pat,status);
  2347.     free(expinfo);
  2348.     return 0;
  2349.     }
  2350.  
  2351.     strcpy (&relpath, dirnam);    /* set up relnam */
  2352.     for (i = 0; i < nbrmatch; i++) {
  2353.     /* if no dirsep chars, don't put full path in expansion */
  2354.         if ((NULL == strchr(pat,'>')) && (NULL == strchr(pat,'<')))
  2355.         strcpy (&fullpath, &expinfo->entries[i].objnam);
  2356.     else {
  2357.         strcpy (&fullpath, &relpath); /* faster with CV's */
  2358.         strcat (&fullpath, &expinfo->entries[i].objnam);
  2359.     }
  2360.  
  2361.     x = (int)strlen(&fullpath);    /* Yes, get length of name */
  2362.     q = malloc(x+1);        /* Allocate space for it */
  2363.     if (!q) goto shxfin;        /* Fail if space can't be obtained */
  2364.     strcpy(q,&fullpath);        /* Copy name to space */
  2365.  
  2366.     namlst[i] = q;        /* Copy pointer to name into array */
  2367.     if (i >= len) goto shxfin;    /* Fail if too many */
  2368.     }
  2369.     retcode = i;            /* Return number of matching files */
  2370.  
  2371. shxfin:                    /* Common exit point */
  2372.  
  2373.     oldmtchs = i;            /* Remember how many files */
  2374.     free(expinfo);
  2375.  
  2376.     debug(F111,"shxpand returning at end",pat,retcode);
  2377.     
  2378.     return(retcode);
  2379. }
  2380.  
  2381. /*  W H O A M I  --  Get user's username.  */
  2382.  
  2383. /*
  2384.   Under UNIX, this is hard to do, and different methods are used on
  2385.   different implementations (ATT/BSD/MINIX/etc).  On VOS, it's a gimme.
  2386. */
  2387. static char *
  2388. whoami () {
  2389.     CV(65) uname;
  2390.     char realname[66];    
  2391.  
  2392.     s$get_user_name (&uname);
  2393.     strcpy (realname, &uname);
  2394.     return(realname);
  2395. }
  2396.  
  2397. /*  T I L D E _ E X P A N D  --  expand ~user to the user's home directory. */
  2398.  
  2399. /*  This is not implemented correctly.  If DTILDE is defined, you will get  */
  2400. /*  compilation errors.  Tilde expansion is not a VOS concept.              */
  2401.  
  2402. char *
  2403. tilde_expand(dirname) char *dirname; {
  2404. #ifdef DTILDE
  2405.  
  2406. #define BUFLEN 257
  2407.  
  2408.     struct passwd *user;
  2409.     static char olddir[BUFLEN];
  2410.     static char oldrealdir[BUFLEN];
  2411.     static char temp[BUFLEN];
  2412.     int i, j;
  2413.  
  2414.     debug(F111,"tilde_expand",dirname,dirname[0]);
  2415.  
  2416.     if (dirname[0] != '~')        /* Not a tilde...return param */
  2417.       return(dirname);
  2418.     if (!strcmp(olddir,dirname)) {    /* Same as last time */
  2419.       return(oldrealdir);        /* so return old answer. */
  2420.     } else {
  2421.     j = (int)strlen(dirname);
  2422.     for (i = 0; i < j; i++)        /* find username part of string */
  2423.       if (!ISDIRSEP(dirname[i]))
  2424.         temp[i] = dirname[i];
  2425.       else break;
  2426.     temp[i] = '\0';            /* tie off with a NULL */
  2427.     if (i == 1) {            /* if just a "~" */
  2428.         user = getpwnam(whoami());    /*  get info on current user */
  2429.     } else {
  2430.         user = getpwnam(&temp[1]);    /* otherwise on the specified user */
  2431.     }
  2432.     }
  2433.     if (user != NULL) {            /* valid user? */
  2434.     strcpy(olddir, dirname);    /* remember the directory */
  2435.     strcpy(oldrealdir,user->pw_dir); /* and their home directory */
  2436.     strcat(oldrealdir,&dirname[i]);
  2437.     return(oldrealdir);
  2438.     } else {                /* invalid? */
  2439.     strcpy(olddir, dirname);    /* remember for next time */
  2440.     strcpy(oldrealdir, dirname);
  2441.     return(oldrealdir);
  2442.     }
  2443. #else
  2444.     return(NULL);
  2445. #endif /* DTILDE */
  2446. }
  2447.  
  2448. /*
  2449.   Functions for executing system commands.
  2450.  
  2451.   zsyscmd() executes the system command in the normal, default way for
  2452.   the system.  In UNIX, it does what system() does.  Thus, its results
  2453.   are  always  predictable;  in  VOS, it uses system(), so it is NEVER
  2454.   predictable, because system() is one of the weirdest  parts  of  the
  2455.   c-runtime.
  2456.  
  2457.   zshcmd()  executes  the  command "using the user's preferred shell,"
  2458.   which is a meaningless concept under VOS,  so  we  use  zsyscmd  for
  2459.   consistency.
  2460. */
  2461. int
  2462. zsyscmd(s) char *s; {
  2463.     int r;
  2464.     short status;
  2465.     CV(256) msgtext;
  2466.     char msg[257];
  2467.  
  2468.     debug(F110,"zsyscmd",s,0);
  2469.     if (strcmp (s, "login") && 0 != strlen (s)) {
  2470.     r = system(s);
  2471.     if (0 != r) {
  2472.         debug(F101,"system() returned status","",r);
  2473.     }
  2474.     }
  2475.     else {
  2476.     strcpy (&msgtext, "");
  2477.     s$clone (&status, &msgtext);
  2478.     r = status;
  2479.         if (0 != r) {
  2480.         strcpy (msg, &msgtext);
  2481.         debug(F111,"s$clone status",msg,status);
  2482.     }
  2483.     }
  2484.  
  2485. #ifdef COMMENT
  2486.     /* changed in edit 002 */
  2487.     /* 0 == failure, 1 == success */
  2488.     return (0 == r);
  2489. #else
  2490.     return r;
  2491. #endif /* COMMENT */
  2492. }
  2493.  
  2494. int
  2495. zshcmd(s) char *s; {
  2496.     debug(F110,"zshcmd",s,0);
  2497.     return(zsyscmd(s));
  2498. }
  2499.  
  2500. /*  I S W I L D  --  Check if filespec is "wild"  */
  2501.  
  2502. /*
  2503.   Returns 0 if it is a single file, 1 if it contains wildcard characters.
  2504.   Note: must match the algorithm used by match(), hence no [a-z], etc.
  2505. */
  2506. int
  2507. iswild(filespec) char *filespec; {
  2508.     char c; int x; char *p;
  2509.  
  2510.     debug(F111,"iswild, filespec, wildexpand",filespec,wildxpand);
  2511.     if (0 && wildxpand) {
  2512.     if ((x = zxpand(filespec)) > 1) return(1);
  2513.     if (x == 0) return(0);        /* File does not exist */
  2514.     p = malloc(MAXNAMLEN + 20);
  2515.     znext(p);
  2516.     x = (strcmp(filespec,p) != 0);
  2517.     free(p);
  2518.     return(x);
  2519.     } else {
  2520.     while ((c = *filespec++) != '\0')
  2521.       if (c == '*') return(1); /* only '*' allowed in VOS star-names */
  2522.     return(0);
  2523.     }
  2524. }
  2525.  
  2526. /*
  2527.    Tell if string pointer s is the name of a directory.
  2528.    Returns 1 if directory, 0 if not a directory.
  2529. */
  2530. int
  2531. isdir(s) char *s; {
  2532.     int x;
  2533.     CV(256) relpath;
  2534.     CV(32)  suffix;
  2535.     CV(256) fullpath;
  2536.     short   status;
  2537.     short   type;
  2538.     short   chase = 1;
  2539.  
  2540.     if (!s) return(0);
  2541.     if (!*s) return(0);
  2542.  
  2543.     strcpy (&relpath, s);
  2544.     strcpy (&suffix, "");        /* don't append a suffix */
  2545.     s$expand_path (&relpath, &suffix, &fullpath, &status);
  2546.     if (status) {
  2547.     debug(F111,"isdir expand_path failed",s,status);
  2548.     return 0;
  2549.     }
  2550.  
  2551.     s$get_object_type (&fullpath, &chase, &type, &status);
  2552.     if (status) {
  2553.     debug(F111,"isdir s$get_object_type failed",s,status);
  2554.     return 0;
  2555.     }
  2556.  
  2557.     debug(F111,"isdir type",s,type);
  2558.     return( (type == 2) ? 1 : 0 );  /* 1=File, 2=Dir, 3=Link */
  2559. }
  2560.  
  2561. #ifdef CK_MKDIR
  2562. /* Some systems don't have mkdir(), e.g. Tandy Xenix 3.2.. */
  2563.  
  2564. /* Z M K D I R  --  Create directory(s) if necessary */
  2565. /*
  2566.    Call with:
  2567.     A pointer to a file specification that might contain directory
  2568.     information.  The filename is expected to be included.
  2569.     If the file specification does not include any directory separators,
  2570.     then it is assumed to be a plain file.
  2571.     If one or more directories are included in the file specification,
  2572.     this routine tries to create them if they don't already exist.
  2573.    Returns:
  2574.     0 on success, i.e. the directory was created
  2575.    -1 on failure to create the directory
  2576. */
  2577. int
  2578. zmkdir(path) char *path; {
  2579.     char *xp, *tp, c;
  2580.     int x;
  2581.     CV(256) cv_path;
  2582.     short   status;
  2583.  
  2584.     x = strlen(path);
  2585.     debug(F111,"zmkdir",path,x);
  2586.     if (x < 1 || x > MAXPATH)        /* Check length */
  2587.       return(-1);
  2588.     if (!(tp = malloc(x+1)))        /* Make a temporary copy */
  2589.       return(-1);
  2590.     strcpy(tp,path);
  2591.  
  2592.     xp = tp;
  2593.     if (ISDIRSEP(*xp))            /* Don't create root directory! */
  2594.       xp++;
  2595.  
  2596.     /* Go thru filespec from left to right... */
  2597.  
  2598.     for (; *xp; xp++) {            /* Create parts that don't exist */
  2599.     if (!ISDIRSEP(*xp))        /* Find next directory separator */
  2600.       continue;
  2601.     c = *xp;            /* Got one. */
  2602.     *xp = '\0';            /* Make this the end of the string. */
  2603.     if (!isdir(tp)) {        /* This directory exists already? */
  2604.         debug(F110,"zmkdir making",tp,0);
  2605.         strcpy (&cv_path, tp);
  2606.         s$create_dir (&cv_path, &status);
  2607.     
  2608.         if (status != 0) {
  2609.         debug(F101,"zmkdir failed, status","",status);
  2610.         free(tp);        /* Free temporary buffer. */
  2611.         return(-1);        /* Freturn failure code. */
  2612.         }
  2613.     }
  2614.     *xp = c;            /* Replace the separator. */
  2615.     }
  2616.     free(tp);                /* Free temporary buffer. */
  2617.     return(0);                /* Return success code. */
  2618. }
  2619. #endif /* CK_MKDIR */
  2620.  
  2621. int
  2622. zrmdir(path) char *path; {
  2623.     CV(256) cv_path;
  2624.     CV(32)  idunno;
  2625.     short status;
  2626.     short flags;
  2627.  
  2628.     flags = 0;
  2629.     status = 0;
  2630.     strcpy(&idunno, "");
  2631.     strcpy(&cv_path, path);
  2632.     s$delete_dir(&cv_path, &idunno, &flags, &status);    
  2633.     debug(F111,"zrmdir", path, status);
  2634.  
  2635.     return(status == 0);
  2636. }
  2637.  
  2638.  
  2639. /* Z F S E E K  --  Position input file pointer */
  2640. /*
  2641.    Call with:
  2642.     Long int, 0-based, indicating desired position.
  2643.    Returns:
  2644.     0 on success.
  2645.    -1 on failure.
  2646. */
  2647. #ifndef NORESEND
  2648. int
  2649. #ifdef CK_ANSIC
  2650. zfseek(long pos)
  2651. #else
  2652. zfseek(pos) long pos;
  2653. #endif /* CK_ANSIC */
  2654. /* zfseek */ {
  2655.     short port;
  2656.     short position_control;
  2657.     short status;
  2658.     long  nbr_records;
  2659.     int   rv; /* return value */
  2660.     VFILE *vp = fp[ZIFILE];
  2661.  
  2662.     debug(F101,"zfseek","",pos);
  2663.     if (NULL != vp) {
  2664.     port = vp->port;
  2665.     position_control = POS_BEGINNING_OF_FILE;
  2666.     nbr_records = 0;
  2667.     status = 0;
  2668.  
  2669.     s$seq_position (&port, &position_control, &nbr_records, &status);
  2670.     if (status == 0) {
  2671.         port = vp->port;
  2672.         position_control = POS_NUM_BYTES_FORWARD;
  2673.         nbr_records = pos;
  2674.         status = 0;
  2675.  
  2676.         s$seq_position (&port, &position_control, &nbr_records, &status);
  2677.     }
  2678.     } else {
  2679.         status = 1;
  2680.     }
  2681.  
  2682.     rv = (status == 0) ? 0 : -1;
  2683.     return(rv);
  2684. }
  2685. #endif /* NORESEND */
  2686.  
  2687. struct zfnfp *
  2688. zfnqfp(fname, buflen, buf)  char * fname; int buflen; char * buf; {
  2689.     int x = 0, y = 0;
  2690.     char * xp;
  2691.     static struct zfnfp fnfp;
  2692.     CV(256) rel;
  2693.     CV(256) abs;
  2694.     CV(32) suffix = "";
  2695.     short status;
  2696.  
  2697.     if (!fname)
  2698.       return(NULL);
  2699.  
  2700.     /* initialize the data structure */
  2701.     fnfp.len = buflen;
  2702.     fnfp.fpath = buf;
  2703.     fnfp.fname = NULL;
  2704.  
  2705. #ifdef DTILDE
  2706.         if (*fname == '~') {
  2707.         xp = tilde_expand(fname);
  2708.     if (*xp)
  2709.         fname = xp;
  2710.     }
  2711. #endif /* DTILDE */
  2712.  
  2713.     strcpy(&rel, fname);
  2714.     strcpy(&abs, "");
  2715.     strcpy(&suffix, "");
  2716.     status = 0;
  2717.  
  2718.     s$expand_path(&rel, &suffix, &abs, &status);
  2719.     debug(F111,"zfnqfp s$expand_path",fname,status);
  2720.     if(status)
  2721.     return(NULL);
  2722.  
  2723.     if(strlen(&abs)<buflen) {
  2724.     strncpy(buf,&abs,buflen);
  2725.     x = strlen(buf);
  2726.     while(buf[x]!='>')        /* absolute path must have one */
  2727.         x--;
  2728.     fnfp.fname = &buf[x];
  2729.     int)strlen(buf)
  2730.     return(&fnfp);
  2731.     }
  2732.  
  2733.     return(NULL);
  2734. }
  2735.