Annotation of coherent/e/bin/cku179/ckufio.c, revision 1.1.1.1

1.1       root        1: char *ckzv = "File support, 5A(058) 21 Jan 92";
                      2: 
                      3: /* C K U F I O  --  Kermit file system support for UNIX, OS/2, and Aegis */
                      4: 
                      5: /*
                      6:  Author: Frank da Cruz ([email protected], [email protected]),
                      7:  Columbia University Center for Computing Activities.  Many other contributors.
                      8:  First released January 1985.
                      9:  Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New
                     10:  York.  Permission is granted to any individual or institution to use, copy, or
                     11:  redistribute this software so long as it is not sold for profit, provided this
                     12:  copyright notice is retained.
                     13: */
                     14: 
                     15: /* Include Files */
                     16: 
                     17: #include "ckcdeb.h"
                     18: 
                     19: #include <signal.h>
                     20: #ifdef MINIX
                     21: #include <limits.h>
                     22: #endif /* MINIX */
                     23: #ifdef POSIX
                     24: #include <limits.h>
                     25: #endif /* POSIX */
                     26: 
                     27: /* Directory structure header file */
                     28: 
                     29: #ifdef OS2
                     30: /*
                     31:   C-Kermit's OS/2 support originally by Chris Adie <[email protected]>
                     32:   for C-Kermit 4F, Copyright (C) 1988 Edinburgh University Computing Service,
                     33:   Scotland.  Permission is granted to any individual or institution to use,
                     34:   copy, or redistribute this software so long as it is not sold for profit,
                     35:   provided this copyright notice is retained.
                     36: 
                     37:   Adapted to C-Kermit 5A and integrated into the UNIX support module
                     38:   by Kai Uwe Rommel <[email protected]>, Muenchen, Germany,
                     39:   December 1991.
                     40: */
                     41: 
                     42: /*
                     43:   Directory Separator macros, to allow this module to work with both UNIX and
                     44:   OS/2: Because of ambiguity with the command line editor escape \ character,
                     45:   the directory separator is currently left as / for OS/2 too, because the
                     46:   OS/2 kernel accepts / as directory separatore too. But this is subject to
                     47:   change in future versions to conform with the normal OS/2 style.
                     48: */
                     49: #define DIRSEP       '/'
                     50: /* #define DIRSEP       '\\' */
                     51: #define ISDIRSEP(c)  ((c)=='/'||(c)=='\\')
                     52: #else /* not OS2 */
                     53: #define DIRSEP       '/'
                     54: #define ISDIRSEP(c)  ((c)=='/')
                     55: #endif /* OS2 */
                     56: 
                     57: #ifdef SDIRENT
                     58: #define DIRENT
                     59: #endif /* SDIRENT */
                     60: 
                     61: #ifdef XNDIR
                     62: #include <sys/ndir.h>
                     63: #else /* !XNDIR */
                     64: #ifdef NDIR
                     65: #include <ndir.h>
                     66: #else /* !NDIR, !XNDIR */
                     67: #ifdef RTU
                     68: #include "/usr/lib/ndir.h"
                     69: #else /* !RTU, !NDIR, !XNDIR */
                     70: #ifdef DIRENT
                     71: #ifdef SDIRENT
                     72: #include <sys/dirent.h>
                     73: #else
                     74: #include <dirent.h>
                     75: #endif /* SDIRENT */
                     76: #else
                     77: #ifdef OS2
                     78: #define OPENDIR
                     79: #define DIRENT
                     80: #include "ckodir.h"
                     81: #else/* !RTU, !NDIR, !XNDIR, !DIRENT, !OS2, i.e. all others */
                     82: #include <sys/dir.h>
                     83: #endif /* OS2 */
                     84: #endif /* DIRENT */
                     85: #endif /* RTU */
                     86: #endif /* NDIR */
                     87: #endif /* XNDIR */
                     88: 
                     89: #ifdef OS2                             /* OS/2 file system interface */
                     90: #define BSD4                           /* is like Berkeley UNIX */
                     91: #define NOFILEH                                /* with no <file.h> */
                     92: #include <sys/utime.h>
                     93: #include <stdlib.h>
                     94: #include <process.h>
                     95: extern int binary;                     /* We need to know this for open() */
                     96: #define popen    _popen
                     97: #define pclose   _pclose
                     98: #else
                     99: #include <pwd.h>                       /* Password file for shell name */
                    100: #endif /* OS2 */
                    101: 
                    102: #ifdef BSD4                            /* BSD 4.x */
                    103: #define TIMESTAMP                      /* Can do file dates */
                    104: #include <time.h>                      /* Need this */
                    105: #include <sys/timeb.h>                 /* Need this if really BSD */
                    106: #endif /* BSD4 */
                    107: 
                    108: #ifdef ATTSV
                    109: #define TIMESTAMP
                    110: #include <time.h>
                    111: /* void tzset(); (the "void" type upsets some compilers) */
                    112: #ifndef ultrix
                    113: extern long timezone;
                    114: #endif /* ultrix */
                    115: #endif /* ATTSV */
                    116: 
                    117: /* Is `y' a leap year? */
                    118: #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
                    119: 
                    120: /* Number of leap years from 1970 to `y' (not including `y' itself). */
                    121: #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
                    122: 
                    123: #ifdef COMMENT /* not used */
                    124: /* Number of days in each month of the year. */
                    125: static char monlens[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
                    126: #endif /* COMMENT */
                    127: 
                    128: #ifdef CIE
                    129: #include <stat.h>                      /* File status */
                    130: #else
                    131: #include <sys/stat.h>
                    132: #ifdef OS2
                    133: #include <sys/types.h>
                    134: /* because standard stat has trouble with trailing /'s we have to wrap it */
                    135: int os2stat(char *, struct stat *);
                    136: #define stat(path, buf) os2stat(path, buf)
                    137: #endif /* OS2 */
                    138: #endif /* CIE */
                    139: 
                    140: /*
                    141:   Functions (n is one of the predefined file numbers from ckcker.h):
                    142: 
                    143:    zopeni(n,name)   -- Opens an existing file for input.
                    144:    zopeno(n,name,attr,fcb) -- Opens a new file for output.
                    145:    zclose(n)        -- Closes a file.
                    146:    zchin(n,&c)      -- Gets the next character from an input file.
                    147:    zsinl(n,&s,x)    -- Read a line from file n, max len x, into address s.
                    148:    zsout(n,s)       -- Write a null-terminated string to output file, buffered.
                    149:    zsoutl(n,s)      -- Like zsout, but appends a line terminator.
                    150:    zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
                    151:    zchout(n,c)      -- Add a character to an output file, unbuffered.
                    152:    zchki(name)      -- Check if named file exists and is readable, return size.
                    153:    zchko(name)      -- Check if named file can be created.
                    154:    zchkspa(name,n)  -- Check if n bytes available to create new file, name.
                    155:    znewn(name,s)    -- Make a new unique file name based on the given name.
                    156:    zdelet(name)     -- Delete the named file.
                    157:    zxpand(string)   -- Expands the given wildcard string into a list of files.
                    158:    znext(string)    -- Returns the next file from the list in "string".
                    159:    zxcmd(n,cmd)     -- Execute the command in a lower fork on file number n.
                    160:    zclosf()         -- Close input file associated with zxcmd()'s lower fork.
                    161:    zrtol(n1,n2)     -- Convert remote filename into local form.
                    162:    zltor(n1,n2)     -- Convert local filename into remote form.
                    163:    zchdir(dirnam)   -- Change working directory.
                    164:    zhome()          -- Return pointer to home directory name string.
                    165:    zkself()         -- Kill self, log out own job.
                    166:    zsattr(struct zattr *) -- Return attributes for file which is being sent.
                    167:    zstime(f, struct zattr *, x) - Set file creation date from attribute packet.
                    168:    zrename(old, new) -- Rename a file.
                    169:  */
                    170: 
                    171: /* Kermit-specific includes */
                    172: /*
                    173:   Definitions here supersede those from system include files.
                    174:   ckcdeb.h is included above.
                    175: */
                    176: #include "ckcker.h"                    /* Kermit definitions */
                    177: #include "ckucmd.h"                    /* For sys-dependent keyword tables */
                    178: #include "ckuver.h"                    /* Version herald */
                    179: 
                    180: char *ckzsys = HERALD;
                    181: 
                    182: /* Support for tilde-expansion in file and directory names */
                    183: 
                    184: #ifdef POSIX
                    185: #define NAMEENV "LOGNAME"
                    186: #endif /* POSIX */
                    187: 
                    188: #ifdef BSD4
                    189: #define NAMEENV "USER"
                    190: #endif /* BSD4 */
                    191: 
                    192: #ifdef ATTSV
                    193: #define NAMEENV "LOGNAME"
                    194: #endif /* ATTSV */
                    195: 
                    196: /* Berkeley Unix Version 4.x */
                    197: /* 4.1bsd support from Charles E Brooks, EDN-VAX */
                    198: 
                    199: #ifdef BSD4
                    200: #ifdef MAXNAMLEN
                    201: #define BSD42
                    202: #endif /* MAXNAMLEN */
                    203: #endif /* BSD4 */
                    204: 
                    205: /* Definitions of some system commands */
                    206: 
                    207: #ifdef OS2
                    208: char *DELCMD = "del ";                 /* For file deletion */
                    209: char *PWDCMD = "chdir ";               /* For saying where I am */
                    210: char *TYPCMD = "type ";                        /* For typing a file */
                    211: char *DIRCMD = "dir ";                 /* For directory listing */
                    212: char *DIRCM2 = "dir ";                 /* For directory listing, no args */
                    213: char *WHOCMD = "";                     /* Who's there? */
                    214: char *SPACMD = "chkdsk ";              /* For space on disk */
                    215: char *SPACM2 = "chkdsk ";              /* For space on disk */
                    216: #else
                    217: char *DELCMD = "rm -f ";               /* For file deletion */
                    218: char *PWDCMD = "pwd ";                 /* For saying where I am */
                    219: #ifdef COMMENT
                    220: char *DIRCMD = "/bin/ls -ld ";         /* For directory listing */
                    221: char *DIRCM2 = "/bin/ls -ld *";                /* For directory listing, no args */
                    222: #else
                    223: char *DIRCMD = "/bin/ls -l ";          /* For directory listing */
                    224: char *DIRCM2 = "/bin/ls -l ";          /* For directory listing, no args */
                    225: #endif
                    226: char *TYPCMD = "cat ";                 /* For typing a file */
                    227: 
                    228: #ifdef FT18                            /* Fortune For:Pro 1.8 */
                    229: #undef BSD4
                    230: #endif /* FT18 */
                    231: 
                    232: #ifdef BSD4
                    233: char *SPACMD = "pwd ; df .";           /* Space in current directory */
                    234: #else
                    235: #ifdef FT18
                    236: char *SPACMD = "pwd ; du ; df .";
                    237: #else
                    238: char *SPACMD = "df ";
                    239: #endif /* FT18 */
                    240: #endif /* BSD4 */
                    241: 
                    242: char *SPACM2 = "df ";                  /* For space in specified directory */
                    243: 
                    244: #ifdef FT18
                    245: #define BSD4
                    246: #endif /* FT18 */
                    247: 
                    248: #ifdef BSD4
                    249: char *WHOCMD = "finger ";              /* For seeing who's logged in */
                    250: #else
                    251: char *WHOCMD = "who ";                 /* For seeing who's logged in */
                    252: #endif /* BSD4 */
                    253: #endif /* OS2 */
                    254: 
                    255: #ifdef DTILDE                          /* For tilde expansion */
                    256: _PROTOTYP( char * tilde_expand, (char *) );
                    257: #endif /* DTILDE */
                    258: 
                    259: /* More system-dependent includes, which depend on symbols defined */
                    260: /* in the Kermit-specific includes.  Oh what a tangled web we weave... */
                    261: 
                    262: #ifdef COHERENT
                    263: #define NOFILEH
                    264: #endif /* COHERENT */
                    265: 
                    266: #ifdef MINIX
                    267: #define NOFILEH
                    268: #endif /* MINIX */
                    269: 
                    270: #ifdef aegis                           /* <sys/file.h> */
                    271: #define NOFILEH
                    272: #endif /* aegis */
                    273: 
                    274: #ifdef unos
                    275: #define NOFILEH
                    276: #endif /* unos */
                    277: 
                    278: #ifndef NOFILEH
                    279: #include <sys/file.h>
                    280: #endif /* NOFILEH */
                    281: 
                    282: 
                    283: #ifndef is68k                          /* Whether to include <fcntl.h> */
                    284: #ifndef BSD41                          /* All but a couple UNIXes have it. */
                    285: #ifndef FT18
                    286: #include <fcntl.h>
                    287: #endif /* FT18  */
                    288: #endif /* BSD41 */
                    289: #endif /* not is68k */
                    290: 
                    291: #ifndef _POSIX_SOURCE
                    292: #ifndef NEXT
                    293: #ifndef SVR4
                    294: /* POSIX <pwd.h> already gave prototypes for these. */
                    295: #ifdef IRIX40
                    296: _PROTOTYP( struct passwd * getpwnam, (const char *) );
                    297: #else
                    298: _PROTOTYP( struct passwd * getpwnam, (char *) );
                    299: #endif /* IRIX40 */
                    300: _PROTOTYP( struct passwd * getpwuid, (PWID_T) );
                    301: _PROTOTYP( struct passwd * getpwent, (void) );
                    302: #endif /* SVR4 */
                    303: #endif /* NEXT */
                    304: #endif /* _POSIX_SOURCE */
                    305: 
                    306: /* Define macros for getting file type */
                    307: 
                    308: #ifdef OXOS
                    309: /*
                    310:   Olivetti X/OS 2.3 has S_ISREG and S_ISDIR defined
                    311:   incorrectly, so we force their redefinition.
                    312: */
                    313: #undef S_ISREG
                    314: #undef S_ISDIR
                    315: #endif /* OXOS */
                    316: 
                    317: #ifdef UTSV                            /* Same deal for Amdah UTSV */
                    318: #undef S_ISREG
                    319: #undef S_ISDIR
                    320: #endif /* UTSV */
                    321: 
                    322: #ifndef S_ISREG
                    323: #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
                    324: #endif /* S_ISREG */
                    325: #ifndef S_ISDIR
                    326: #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
                    327: #endif /* S_ISDIR */
                    328: 
                    329: /* Define maximum length for a file name if not already defined */
                    330: 
                    331: #ifndef MAXNAMLEN
                    332: #ifdef sun
                    333: #define MAXNAMLEN 255
                    334: #else
                    335: #ifdef FILENAME_MAX
                    336: #define MAXNAMLEN FILENAME_MAX
                    337: #else
                    338: #ifdef NAME_MAX
                    339: #define MAXNAMLEN NAME_MAX
                    340: #else
                    341: #ifdef _POSIX_NAME_MAX
                    342: #define MAXNAMLEN _POSIX_NAME_MAX
                    343: #else
                    344: #ifdef _D_NAME_MAX
                    345: #define MAXNAMLEN _D_NAME_MAX
                    346: #else
                    347: #ifdef DIRSIZ
                    348: #define MAXNAMLEN DIRSIZ
                    349: #else
                    350: #define MAXNAMLEN 14
                    351: #endif /* DIRSIZ */
                    352: #endif /* _D_NAME_MAX */
                    353: #endif /* _POSIX_NAME_MAX */
                    354: #endif /* NAME_MAX */
                    355: #endif /* FILENAME_MAX */
                    356: #endif /* sun */
                    357: #endif /* MAXNAMLEN */
                    358: 
                    359: /* Longest pathname */
                    360: 
                    361: #ifdef MAXPATHLEN
                    362: #ifdef MAXPATH
                    363: #undef MAXPATH
                    364: #endif /* MAXPATH */
                    365: #define MAXPATH MAXPATHLEN
                    366: #else
                    367: #ifdef PATH_MAX
                    368: #define MAXPATH PATH_MAX
                    369: #else
                    370: #ifdef _POSIX_PATH_MAX
                    371: #define MAXPATH _POSIX_PATH_MAX
                    372: #else
                    373: #ifdef BSD42
                    374: #define MAXPATH 1024
                    375: #else
                    376: #define MAXPATH 255
                    377: #endif /* BSD42 */
                    378: #endif /* _POSIX_PATH_MAX */
                    379: #endif /* PATH_MAX */
                    380: #endif /* MAXPATHLEN */
                    381: 
                    382: /* Maximum number of filenames for wildcard expansion */
                    383: 
                    384: #ifdef PROVX1
                    385: #define MAXWLD 50
                    386: #else
                    387: #ifdef BSD29
                    388: #define MAXWLD 50
                    389: #else
                    390: #ifdef pdp11
                    391: #define MAXWLD 50
                    392: #else
                    393: #define MAXWLD 1000
                    394: #endif /* pdp11 */
                    395: #endif /* BSD29 */
                    396: #endif /* PROVX1 */
                    397: 
                    398: /* More internal function prototypes */
                    399: /*
                    400:  * The path structure is used to represent the name to match.
                    401:  * Each slash-separated segment of the name is kept in one
                    402:  * such structure, and they are linked together, to make
                    403:  * traversing the name easier.
                    404:  */
                    405: struct path {
                    406:     char npart[MAXNAMLEN+4];           /* name part of path segment */
                    407:     struct path *fwd;                  /* forward ptr */
                    408: };
                    409: _PROTOTYP( int shxpand, (char *, char **, int ) );
                    410: _PROTOTYP( static int fgen, (char *, char **, int ) );
                    411: _PROTOTYP( static VOID traverse, (struct path *, char *, char *) );
                    412: _PROTOTYP( static VOID addresult, (char *) );
                    413: _PROTOTYP( static int match, (char *, char *) );
                    414: _PROTOTYP( static char * whoami, (void) );
                    415: _PROTOTYP( char * xindex, (char *, char) );
                    416: _PROTOTYP( UID_T real_uid, (void) );
                    417: _PROTOTYP( struct path *splitpath, (char *p) );
                    418: 
                    419: /* Some systems define these symbols in include files, others don't... */
                    420: 
                    421: #ifndef R_OK
                    422: #define R_OK 4                         /* For access */
                    423: #endif
                    424: 
                    425: #ifndef W_OK
                    426: #define W_OK 2
                    427: #endif
                    428: 
                    429: #ifndef O_RDONLY
                    430: #define O_RDONLY 000
                    431: #endif
                    432: 
                    433: /* Declarations */
                    434: 
                    435: int maxnam = MAXNAMLEN;                        /* Available to the outside */
                    436: int maxpath = MAXPATH;
                    437: 
                    438: FILE *fp[ZNFILS] = {                   /* File pointers */
                    439:     NULL, NULL, NULL, NULL, NULL, NULL, NULL };
                    440: #ifdef OS2
                    441: int ispipe[ZNFILS];                    /* Flag for file is a pipe */
                    442: #endif /* OS2 */
                    443: 
                    444: /* Buffers and pointers used in buffered file input and output. */
                    445: #ifdef DYNAMIC
                    446: extern char *zinbuffer, *zoutbuffer;
                    447: #else
                    448: extern char zinbuffer[], zoutbuffer[];
                    449: #endif /* DYNAMIC */
                    450: extern char *zinptr, *zoutptr;
                    451: extern int zincnt, zoutcnt;
                    452: extern int wildxpand;
                    453: 
                    454: extern UID_T real_uid();
                    455: 
                    456: static long iflen = -1L;               /* Input file length */
                    457: 
                    458: static PID_T pid = 0;                  /* pid of child fork */
                    459: static int fcount;                     /* Number of files in wild group */
                    460: static char nambuf[MAXNAMLEN+4];       /* Buffer for a filename */
                    461: static char zmbuf[200];                        /* For mail, remote print strings */
                    462: 
                    463: /* static */                           /* Not static, must be global now. */
                    464: char *mtchs[MAXWLD],                   /* Matches found for filename */
                    465:      **mtchptr;                                /* Pointer to current match */
                    466: 
                    467: /*  Z K S E L F  --  Kill Self: log out own job, if possible.  */
                    468: 
                    469: /* Note, should get current pid, but if your system doesn't have */
                    470: /* getppid(), then just kill(0,9)...  */
                    471: 
                    472: #ifndef SVR3
                    473: #ifndef POSIX
                    474: /* Already declared in unistd.h for SVR3 and POSIX */
                    475: #ifdef CK_ANSIC
                    476: extern PID_T getppid(void);
                    477: #else
                    478: #ifndef PS2AIX10
                    479: extern PID_T getppid();
                    480: #endif /* PS2AIX10 */
                    481: #endif /* CK_ANSIC */
                    482: #endif /* POSIX */
                    483: #endif /* SVR3 */
                    484: int
                    485: zkself() {                             /* For "bye", but no guarantee! */
                    486: #ifdef PROVX1
                    487:     return(kill(0,9));
                    488: #else
                    489: #ifdef V7
                    490:     return(kill(0,9));
                    491: #else
                    492: #ifdef TOWER1
                    493:     return(kill(0,9));
                    494: #else
                    495: #ifdef FT18
                    496:     return(kill(0,9));
                    497: #else
                    498: #ifdef aegis
                    499:     return(kill(0,9));
                    500: #else
                    501: #ifdef COHERENT
                    502:     return(kill(getpid(),1));
                    503: #else
                    504: #ifdef OS2
                    505:     exit(3);
                    506: #else
                    507:     exit(kill(getppid(),1));
                    508: #endif
                    509: #endif
                    510: #endif
                    511: #endif
                    512: #endif
                    513: #endif
                    514: #endif
                    515: }
                    516: 
                    517: /*  Z O P E N I  --  Open an existing file for input. */
                    518: 
                    519: int
                    520: zopeni(n,name) int n; char *name; {
                    521:     debug(F111," zopeni",name,n);
                    522:     debug(F101,"  fp","", fp[n]);
                    523:     if (chkfn(n) != 0) return(0);
                    524:     zincnt = 0;                                /* Reset input buffer */
                    525:     if (n == ZSYSFN) {                 /* Input from a system function? */
                    526: /*** Note, this function should not be called with ZSYSFN ***/
                    527: /*** Always call zxcmd() directly, and give it the real file number ***/
                    528: /*** you want to use.  ***/
                    529:         debug(F110,"zopeni called with ZSYSFN, failing!",name,0);
                    530:        *nambuf = '\0';                 /* No filename. */
                    531:        return(0);                      /* fail. */
                    532: #ifdef COMMENT
                    533:        return(zxcmd(n,name));          /* Try to fork the command */
                    534: #endif
                    535:     }
                    536:     if (n == ZSTDIO) {                 /* Standard input? */
                    537:        if (isatty(0)) {
                    538:            fprintf(stderr,"Terminal input not allowed");
                    539:            debug(F110,"zopeni: attempts input from unredirected stdin","",0);
                    540:            return(0);
                    541:        }
                    542:        fp[ZIFILE] = stdin;
                    543: #ifdef OS2
                    544:        setmode(fileno(stdin),O_BINARY);
                    545: #endif /* OS2 */
                    546:        return(1);
                    547:     }
                    548: #ifdef OS2
                    549:     if (n == ZIFILE)
                    550:       fp[n] = fopen(name,"rb");                /* Binary mode */
                    551:     else
                    552: #endif /* OS2 */
                    553:       fp[n] = fopen(name,"r");         /* Real file, open it. */
                    554:     debug(F111," zopeni", name, fp[n]);
                    555:     if (fp[n] == NULL) perror("zopeni");
                    556:     return((fp[n] != NULL) ? 1 : 0);
                    557: }
                    558: 
                    559: /*  Z O P E N O  --  Open a new file for output.  */
                    560: 
                    561: int
                    562: zopeno(n,name,zz,fcb)
                    563: /* zopeno */  int n; char *name; struct zattr *zz; struct filinfo *fcb; {
                    564: 
                    565:     char p[8];                         /* (===OS2 change===) */
                    566: /*  char *p; */                                /* Local-use pointer */
                    567: 
                    568: /* As of Version 5A, the attribute structure and the file information */
                    569: /* structure are included in the arglist. */
                    570: 
                    571:     debug(F111,"zopeno",name,n);
                    572:     if (fcb) {
                    573:        debug(F101,"zopeno fcb disp","",fcb->dsp);
                    574:        debug(F101,"zopeno fcb type","",fcb->typ);
                    575:        debug(F101,"zopeno fcb char","",fcb->cs);
                    576:     } else {
                    577:        debug(F100,"zopeno fcb is NULL","",0);
                    578:     }
                    579:     if (n != ZDFILE)
                    580:       debug(F111," zopeno",name,n);
                    581:     if (chkfn(n) != 0) return(0);
                    582:     if ((n == ZCTERM) || (n == ZSTDIO)) {   /* Terminal or standard output */
                    583:        fp[ZOFILE] = stdout;
                    584:        if (n != ZDFILE)
                    585:          debug(F101," fp[]=stdout", "", fp[n]);
                    586:        zoutcnt = 0;
                    587:        zoutptr = zoutbuffer;
                    588:        return(1);
                    589:     }
                    590: 
                    591: /* A real file.  Open it in desired mode (create or append). */
                    592: 
                    593:     strcpy(p,"w");                     /* Assume write/create mode */
                    594:     if (fcb) {                         /* If called with an FCB... */
                    595:        if (fcb->dsp == XYFZ_A)         /* Does it say Append? */
                    596:          strcpy(p,"a");                /* Yes. */
                    597:     }
                    598: #ifdef OS2
                    599:     if (n == ZOFILE) strcat(p,"b");    /* Binary mode */
                    600: #endif /* OS2 */
                    601:     fp[n] = fopen(name,p);             /* Open the file */
                    602: 
                    603:     if (fp[n] == NULL) {
                    604:         perror("zopeno can't open");
                    605:     } else {
                    606:         if (n == ZDFILE) setbuf(fp[n],NULL); /* Debugging file unbuffered */
                    607:     }
                    608:     zoutcnt = 0;               /* (PWP) reset output buffer */
                    609:     zoutptr = zoutbuffer;
                    610:     if (n != ZDFILE)
                    611:       debug(F101, " fp[n]", "", fp[n]);
                    612:     return((fp[n] != NULL) ? 1 : 0);
                    613: }
                    614: 
                    615: /*  Z C L O S E  --  Close the given file.  */
                    616: 
                    617: /*  Returns 0 if arg out of range, 1 if successful, -1 if close failed.  */
                    618: 
                    619: int
                    620: zclose(n) int n; {
                    621:     int x, x2;
                    622:     if (chkfn(n) < 1) return(0);       /* Check range of n */
                    623: 
                    624:     if ((n == ZOFILE) && (zoutcnt > 0))        /* (PWP) output leftovers */
                    625:       x2 = zoutdump();
                    626:     else
                    627:       x2 = 0;
                    628: 
                    629:     x = 0;                             /* Initialize return code */
                    630:     if (fp[ZSYSFN]) {                  /* If file is really pipe */
                    631:        x = zclosf(n);                  /* do it specially */
                    632:     } else {
                    633:        if ((fp[n] != stdout) && (fp[n] != stdin)) x = fclose(fp[n]);
                    634:        fp[n] = NULL;
                    635:     }
                    636:     iflen = -1L;                       /* Invalidate file length */
                    637:     if (x == EOF)                      /* if we got a close error */
                    638:        return (-1);
                    639:     else if (x2 < 0)           /* or an error flushing the last buffer */
                    640:        return (-1);            /* then return an error */
                    641:     else
                    642:        return (1);
                    643: }
                    644: 
                    645: /*  Z C H I N  --  Get a character from the input file.  */
                    646: 
                    647: /*  Returns -1 if EOF, 0 otherwise with character returned in argument  */
                    648: 
                    649: int
                    650: zchin(n,c) int n; int *c; {
                    651:     int a, x;
                    652: 
                    653:     /* (PWP) Just in case this gets called when it shouldn't. */
                    654:     if (n == ZIFILE) {
                    655:        x = zminchar();
                    656:        *c = x;
                    657:        return(x);
                    658:     }
                    659:     /* if (chkfn(n) < 1) return(-1); */
                    660:     a = getc(fp[n]);
                    661:     if (a == EOF) return(-1);
                    662: #ifdef OS2
                    663:     if (!binary && a == 0x1A)          /* Ctrl-Z marks EOF for text mode*/
                    664:       return(-1);
                    665: #endif
                    666:     *c = (CHAR) a & 0377;
                    667:     return(0);
                    668: }
                    669: 
                    670: /*  Z S I N L  --  Read a line from a file  */
                    671: 
                    672: /*
                    673:   Writes the line into the address provided by the caller.
                    674:   n is the Kermit "channel number".
                    675:   Writing terminates when newline is encountered, newline is not copied.
                    676:   Writing also terminates upon EOF or if length x is exhausted.
                    677:   Returns 0 on success, -1 on EOF or error.
                    678: */
                    679: int
                    680: zsinl(n,s,x) int n, x; char *s; {
                    681:     int a, z = 0;
                    682: 
                    683:     if (chkfn(n) < 1) {                        /* Make sure file is open */
                    684:        return(-1);
                    685:     }
                    686:     a = -1;
                    687:     while (x--) {
                    688: #ifndef NLCHAR
                    689:        int old;
                    690:        old = a;                        /* Previous character */
                    691: #endif
                    692:        if (zchin(n,&a) < 0) {          /* Read a character from the file */
                    693:            z = -1;
                    694:            break;
                    695:        }
                    696: #ifdef NLCHAR
                    697:        if (a == (char) NLCHAR) break;  /* Single-character line terminator */
                    698: #else
                    699:        if (a == '\r') continue;        /* CRLF line terminator */
                    700:        if (old == '\r') {
                    701:            if (a == '\n') break;
                    702:            else *s++ = '\r';
                    703:        }
                    704: #endif /* NLCHAR */
                    705:        *s = a;
                    706:        s++;
                    707:     }
                    708:     *s = '\0';
                    709:     return(z);
                    710: }
                    711: 
                    712: /*
                    713:  * (PWP) (re)fill the buffered input buffer with data.  All file input
                    714:  * should go through this routine, usually by calling the zminchar()
                    715:  * macro (in ckcker.h).
                    716:  */
                    717: 
                    718: /*
                    719:  * Suggestion: if fread() returns 0, call ferror to find out what the
                    720:  * problem was.  If it was not EOF, then return -2 instead of -1.
                    721:  * Upper layers (getpkt function in ckcfns.c) should set cxseen flag
                    722:  * if it gets -2 return from zminchar macro.
                    723:  */
                    724: int
                    725: zinfill() {
                    726:     int x;
                    727: 
                    728:     errno = 0;
                    729:     zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
                    730: #ifdef COMMENT
                    731:     debug(F101,"zinfill fp","",fp[ZIFILE]);
                    732:     debug(F101,"zinfill zincnt","",zincnt);
                    733: #endif
                    734:     if (zincnt == 0) {
                    735: #ifndef UTEK
                    736: #ifdef ferror
                    737:        x = ferror(fp[ZIFILE]);
                    738:        debug(F101,"zinfill errno","",errno);
                    739:        debug(F101,"zinfill ferror","",x);
                    740:        if (x) return(-2);
                    741: #endif /* ferror */
                    742: #else
                    743:        x = feof(fp[ZIFILE]);
                    744:        debug(F101,"zinfill errno","",errno);
                    745:        debug(F101,"zinfill feof","",x);
                    746:        if (!x && ferror(fp[ZIFILE])) return(-2);
                    747: #endif /* UTEK */
                    748:        return(-1);
                    749:     }
                    750:     zinptr = zinbuffer;           /* set pointer to beginning, (== &zinbuffer[0]) */
                    751:     zincnt--;                  /* one less char in buffer */
                    752:     return((int)(*zinptr++) & 0377); /* because we return the first */
                    753: }
                    754: 
                    755: /*  Z S O U T  --  Write a string out to the given file, buffered.  */
                    756: 
                    757: int
                    758: zsout(n,s) int n; char *s; {
                    759:     if (chkfn(n) < 1) return(-1); /* Keep this here, prevents memory faults */
                    760: #ifdef COMMENT
                    761:     while (*s) {                       /* (unbuffered for debugging) */
                    762:        write(fileno(fp[n]),s,1); ++s;
                    763:     }
                    764: #endif
                    765: #ifdef COMMENT
                    766:     return(fputs(s,fp[n]) == EOF ? -1 : 0);
                    767: #else
                    768:     if (n == ZSFILE)
                    769:        return(write(fileno(fp[n]),s,(int)strlen(s)));
                    770:     else
                    771:         return(fputs(s,fp[n]) == EOF ? -1 : 0);
                    772: #endif
                    773: }
                    774: 
                    775: /*  Z S O U T L  --  Write string to file, with line terminator, buffered  */
                    776: 
                    777: int
                    778: zsoutl(n,s) int n; char *s; {
                    779:     /* if (chkfn(n) < 1) return(-1); */
                    780:     if (fputs(s,fp[n]) == EOF) return(-1);
                    781:     if (fputs("\n",fp[n]) == EOF) return(-1); /* (===OS2 ? \r\n) */
                    782:     return(0);
                    783: }
                    784: 
                    785: /*  Z S O U T X  --  Write x characters to file, unbuffered.  */
                    786: 
                    787: int
                    788: zsoutx(n,s,x) int n, x; char *s; {
                    789: #ifdef COMMENT
                    790:     if (chkfn(n) < 1) return(-1);
                    791:     return(write(fp[n]->_file,s,x));
                    792: #endif
                    793:     return(write(fileno(fp[n]),s,x) == x ? x : -1);
                    794: }
                    795: 
                    796: 
                    797: /*  Z C H O U T  --  Add a character to the given file.  */
                    798: 
                    799: /*  Should return 0 or greater on success, -1 on failure (e.g. disk full)  */
                    800: 
                    801: int
                    802: #ifdef CK_ANSIC
                    803: zchout(register int n, char c)
                    804: #else
                    805: zchout(n,c) register int n; char c;
                    806: #endif /* CK_ANSIC */
                    807: /* zchout() */ {
                    808:     /* if (chkfn(n) < 1) return(-1); */
                    809:     if (n == ZSFILE) {                 /* Use unbuffered for session log */
                    810:        return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1);
                    811:     } else {                           /* Buffered for everything else */
                    812:        if (putc(c,fp[n]) == EOF)       /* If true, maybe there was an error */
                    813:          return(ferror(fp[n])?-1:0);   /* Check to make sure */
                    814:        else                            /* Otherwise... */
                    815:          return(0);                    /* There was no error. */
                    816:     }
                    817: }
                    818: 
                    819: /* (PWP) buffered character output routine to speed up file IO */
                    820: 
                    821: int
                    822: zoutdump() {
                    823:     int x;
                    824:     zoutptr = zoutbuffer;              /* Reset buffer pointer in all cases */
                    825:     debug(F101,"zoutdump chars","",zoutcnt);
                    826:     if (zoutcnt == 0) {                        /* Nothing to output */
                    827:        return(0);
                    828:     } else if (zoutcnt < 0) {          /* Unexpected negative argument */
                    829:        zoutcnt = 0;                    /* Reset output buffer count */
                    830:        return(-1);                     /* and fail. */
                    831:     }
                    832: 
                    833: /* Frank Prindle suggested that replacing this fwrite() by an fflush() */
                    834: /* followed by a write() would improve the efficiency, especially when */
                    835: /* writing to stdout.  Subsequent tests showed a 5-fold improvement!   */
                    836: /* if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) {              */
                    837: 
                    838:     fflush(fp[ZOFILE]);
                    839:     if ((x = write(fileno(fp[ZOFILE]),zoutbuffer,zoutcnt)) == zoutcnt) {
                    840:        debug(F101,"zoutdump write ok","",zoutcnt);
                    841:        zoutcnt = 0;                    /* Reset output buffer count */
                    842:        return(0);                      /* write() worked OK */
                    843:     } else {
                    844:        debug(F101,"zoutdump write error","",errno);
                    845:        debug(F101,"zoutdump write returns","",x);
                    846:        zoutcnt = 0;                    /* Reset output buffer count */
                    847:        return(-1);                     /* write() failed */
                    848:     }
                    849: }
                    850: 
                    851: /*  C H K F N  --  Internal function to verify file number is ok  */
                    852: 
                    853: /*
                    854:  Returns:
                    855:   -1: File number n is out of range
                    856:    0: n is in range, but file is not open
                    857:    1: n in range and file is open
                    858: */
                    859: int
                    860: chkfn(n) int n; {
                    861:     switch (n) {
                    862:        case ZCTERM:
                    863:        case ZSTDIO:
                    864:        case ZIFILE:
                    865:        case ZOFILE:
                    866:        case ZDFILE:
                    867:        case ZTFILE:
                    868:        case ZPFILE:
                    869:        case ZSFILE:
                    870:        case ZSYSFN:
                    871:         case ZRFILE:
                    872:         case ZWFILE: break;
                    873:        default:
                    874:            debug(F101,"chkfn: file number out of range","",n);
                    875:            fprintf(stderr,"?File number out of range - %d\n",n);
                    876:            return(-1);
                    877:     }
                    878:     return( (fp[n] == NULL) ? 0 : 1 );
                    879: }
                    880: 
                    881: /*  Z C H K I  --  Check if input file exists and is readable  */
                    882: 
                    883: /*
                    884:   Returns:
                    885:    >= 0 if the file can be read (returns the size).
                    886:      -1 if file doesn't exist or can't be accessed,
                    887:      -2 if file exists but is not readable (e.g. a directory file).
                    888:      -3 if file exists but protected against read access.
                    889: */
                    890: /*
                    891:  For Berkeley Unix, a file must be of type "regular" to be readable.
                    892:  Directory files, special files, and symbolic links are not readable.
                    893: */
                    894: long
                    895: zchki(name) char *name; {
                    896:     struct stat buf;
                    897:     int x;
                    898: 
                    899:     x = stat(name,&buf);
                    900:     if (x < 0) {
                    901:        debug(F111,"zchki stat fails",name,errno);
                    902:        return(-1);
                    903:     }
                    904:     if (!S_ISREG (buf.st_mode)) {      /* Must be regular file */
                    905:        debug(F111,"zchki skipping:",name,x);
                    906:        return(-2);
                    907:     }
                    908:     debug(F111,"zchki stat ok:",name,x);
                    909: 
                    910:     if ((x = access(name,R_OK)) < 0) {         /* Is the file accessible? */
                    911:        debug(F111," access failed:",name,x); /* No */
                    912:        return(-3);
                    913:     } else {
                    914:        iflen = buf.st_size;                  /* Yes, remember size */
                    915:        strncpy(nambuf,name,MAXNAMLEN);       /* and name globally. */
                    916:        debug(F111," access ok:",name,(int) iflen);
                    917:        return( (iflen > -1L) ? iflen : 0L );
                    918:     }
                    919: }
                    920: 
                    921: /*  Z C H K O  --  Check if output file can be created  */
                    922: 
                    923: /*
                    924:  Returns -1 if write permission for the file would be denied, 0 otherwise.
                    925: */
                    926: int
                    927: zchko(name) char *name; {
                    928:     int i, x;
                    929:     char *s;
                    930: 
                    931:     if (!name) return(-1);             /* Watch out for null pointer. */
                    932:     x = (int)strlen(name);             /* Get length of filename */
                    933:     debug(F101," length","",x);
                    934:     s = malloc(x+3);                   /* Must copy because we can't */
                    935:     if (!s) {                          /* write into our argument. */
                    936:        fprintf(stderr,"Malloc error 46\n");
                    937:        return(-1);
                    938:     }
                    939:     strcpy(s,name);
                    940: 
                    941:     for (i = x; i > 0; i--)            /* Strip filename from right. */
                    942:       if (ISDIRSEP(s[i-1])) break;
                    943:     debug(F101," i","",i);
                    944: 
                    945: #ifdef COMMENT
                    946: /* X/OPEN XPG3-compliant systems fail if argument ends with "/"...  */
                    947:     if (i == 0)                                /* If no path, use current directory */
                    948:       strcpy(s,"./");
                    949:     else                               /* Otherwise, use given one. */
                    950:       s[i] = '\0';
                    951: #else
                    952: /* So now we use "path/." if path given, or "." if no path given. */
                    953:     s[i++] = '.';                      /* Append "." to path. */
                    954:     s[i] = '\0';
                    955: #endif /* COMMENT */
                    956: 
                    957:     x = access(s,W_OK);                        /* Check access of path. */
                    958:     if (x < 0)
                    959:       debug(F111,"zchko access failed:",s,errno);
                    960:     else
                    961:       debug(F111,"zchko access ok:",s,x);
                    962:     free(s);                           /* Free temporary storage */
                    963:     return((x < 0) ? -1 : 0);          /* and return. */
                    964: }
                    965: 
                    966: /*  Z D E L E T  --  Delete the named file.  */
                    967: 
                    968: int
                    969: zdelet(name) char *name; {
                    970:     return(unlink(name));
                    971: }
                    972: 
                    973: 
                    974: /*  Z R T O L  --  Convert remote filename into local form  */
                    975: 
                    976: /*  For UNIX, this means changing uppercase letters to lowercase.  */
                    977: 
                    978: VOID
                    979: zrtol(name,name2) char *name, *name2; {
                    980:     for ( ; *name != '\0'; name++) {
                    981:        *name2++ = isupper(*name) ? tolower(*name) : *name;
                    982:     }
                    983:     *name2 = '\0';
                    984:     debug(F110,"zrtol:",name2,0);
                    985: }
                    986: 
                    987: 
                    988: /*  Z S T R I P  --  Strip device & directory name from file specification */
                    989: 
                    990: /*  Strip pathname from filename "name", return pointer to result in name2 */
                    991: 
                    992: static char work[257];         /* buffer for use by zstrip and zltor */
                    993: 
                    994: VOID
                    995: zstrip(name,name2) char *name, **name2; {
                    996:     char *cp, *pp;
                    997:     debug(F110,"zstrip before",name,0);
                    998:     pp = work;
                    999: #ifdef DTILDE
                   1000:     if (*name == '~') name++;
                   1001: #endif /* DTILDE */
                   1002:     for (cp = name; *cp != '\0'; cp++) {
                   1003:        if (ISDIRSEP(*cp))
                   1004:          pp = work;
                   1005:        else
                   1006:          *pp++ = *cp;
                   1007:     }
                   1008:     *pp = '\0';                                /* Terminate the string */
                   1009:     *name2 = work;
                   1010:     debug(F110,"zstrip after",*name2,0);
                   1011: }
                   1012: 
                   1013: /*  Z L T O R  --  Local TO Remote */
                   1014: 
                   1015: VOID
                   1016: zltor(name,name2) char *name, *name2; {
                   1017:     char *cp, *pp;
                   1018:     int dc = 0;
                   1019: #ifdef aegis
                   1020:     char *namechars;
                   1021:     int tilde = 0, bslash = 0;
                   1022: 
                   1023:     if ((namechars = getenv("NAMECHARS")) != NULL) {
                   1024:        if (xindex(namechars, '~' ) != NULL) tilde  = '~';
                   1025:        if (xindex(namechars, '\\') != NULL) bslash = '\\';
                   1026:     } else {
                   1027:         tilde = '~';
                   1028:         bslash = '\\';
                   1029:     }
                   1030: #endif /* aegis */
                   1031: 
                   1032:     debug(F110,"zltor",name,0);
                   1033:     pp = work;
                   1034: #ifdef aegis
                   1035:     cp = name;
                   1036:     if (tilde && *cp == tilde)
                   1037:        ++cp;
                   1038:     for (; *cp != '\0'; cp++) {
                   1039:        if (*cp == '/' || *cp == bslash) {      /* strip path name */
                   1040: #else
                   1041:     for (cp = name; *cp != '\0'; cp++) {       /* strip path name */
                   1042:        if (ISDIRSEP(*cp)) {
                   1043: #endif /* aegis */
                   1044:            dc = 0;
                   1045:            pp = work;
                   1046:        }
                   1047:        else if (islower(*cp)) *pp++ = toupper(*cp); /* Uppercase letters */
                   1048:        else if (*cp == '~') *pp++ = 'X';       /* Change tilde to 'X' */
                   1049:        else if (*cp == '#') *pp++ = 'X';       /* Change number sign to 'X' */
                   1050:        else if ((*cp == '.') && (++dc > 1)) *pp++ = 'X'; /* & extra dots */
                   1051:        else *pp++ = *cp;
                   1052:     }
                   1053:     *pp = '\0';                                /* Tie it off. */
                   1054:     cp = name2;                                /* If nothing before dot, */
                   1055:     if (*work == '.') *cp++ = 'X';     /* insert 'X' */
                   1056:     strcpy(cp,work);
                   1057:     debug(F110," name2",name2,0);
                   1058: }
                   1059: 
                   1060: 
                   1061: /*  Z C H D I R  --  Change directory  */
                   1062: /*
                   1063:   Call with:
                   1064:     dirnam = pointer to name of directory to change to,
                   1065:       which may be "" or NULL to indicate user's home directory.
                   1066:   Returns:
                   1067:     0 on failure
                   1068:     1 on success
                   1069: */
                   1070: int
                   1071: zchdir(dirnam) char *dirnam; {
                   1072:     char *hd, *sp, *p;
                   1073: 
                   1074:     debug(F110,"zchdir",dirnam,0);
                   1075:     if (dirnam == NULL || dirnam == "" || *dirnam == '\0') /* If arg is null */
                   1076:       dirnam = getenv("HOME");         /* use user's home directory. */
                   1077:     sp = dirnam;
                   1078:     debug(F110,"zchdir 2",dirnam,0);
                   1079: 
                   1080: #ifdef DTILDE
                   1081:     hd = tilde_expand(dirnam);         /* Attempt to expand tilde */
                   1082:     if (*hd == '\0') hd = dirnam;      /* in directory name. */
                   1083: #else
                   1084:     hd = dirnam;
                   1085: #endif /* DTILDE */
                   1086:     debug(F110,"zchdir 3",hd,0);
                   1087:     if (chdir(hd) == 0) return(1);     /* Try to cd */ /* (===OS2===) */
                   1088:     p = sp;                            /* Failed, lowercase it. */
                   1089:     while (*p) {
                   1090:        if (isupper(*p)) *p = tolower(*p);
                   1091:        p++;
                   1092:     }
                   1093:     debug(F110,"zchdir 4",hd,0);
                   1094: #ifdef DTILDE
                   1095:     hd = tilde_expand(sp);             /* Try again to expand tilde */
                   1096:     if (*hd == '\0') hd = sp;
                   1097: #else
                   1098:     hd = sp;                           /* Point to result */
                   1099: #endif
                   1100:     debug(F110,"zchdir 5",hd,0);
                   1101: /*  return((chdir(hd) > -1) ? 1 : 0); (===OS2===)  */
                   1102:     return((chdir(hd) == 0) ? 1 : 0);
                   1103: }
                   1104: 
                   1105: 
                   1106: /*  Z H O M E  --  Return pointer to user's home directory  */
                   1107: 
                   1108: char *
                   1109: zhome() {
                   1110:     char *home = NULL;
                   1111:     home = getenv("HOME");
                   1112:     return(home ? home : ".");         /* (===OS2===) */
                   1113: }
                   1114: 
                   1115: /*  Z G T D I R  --  Return pointer to user's current directory  */
                   1116: 
                   1117: #ifdef MAXPATHLEN
                   1118: #define CWDBL MAXPATHLEN
                   1119: #else
                   1120: #define CWDBL 100
                   1121: #endif
                   1122: static char cwdbuf[CWDBL+1];
                   1123: 
                   1124: char *
                   1125: zgtdir() {
                   1126:     char *buf;
                   1127: 
                   1128: #ifdef SVORPOSIX
                   1129:     extern char *getcwd();
                   1130:     buf = cwdbuf;
                   1131:     return(getcwd(buf,CWDBL));
                   1132: #else
                   1133: #ifdef OS2
                   1134:     extern char *getcwd();
                   1135:     buf = cwdbuf;
                   1136:     return(getcwd(buf,CWDBL));
                   1137: #else
                   1138: #ifdef BSD4
                   1139:     extern char *getwd();
                   1140:     buf = cwdbuf;
                   1141:     return(getwd(buf));
                   1142: #else
                   1143:     return("directory unknown");
                   1144: #endif /* BSD4 */
                   1145: #endif /* OS2 */
                   1146: #endif /* SYSVORPOSIX */
                   1147: }
                   1148: 
                   1149: /*  Z X C M D -- Run a system command so its output can be read like a file */
                   1150: 
                   1151: int
                   1152: zxcmd(filnum,comand) int filnum; char *comand; {
                   1153: #ifdef OS2
                   1154:     if (chkfn(filnum) < 0) return(-1); /* Need a valid Kermit file number. */
                   1155:     if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */
                   1156:       return(0);
                   1157:     if (filnum == ZIFILE || filnum == ZRFILE) { /* Input from a command */
                   1158:        if (priv_chk() || ((fp[filnum] = popen(comand,"r")) == NULL))
                   1159:          return(0);
                   1160:     } else { /* Output to a command */
                   1161:        if (priv_chk() || ((fp[filnum] = popen(comand,"w")) == NULL))
                   1162:          return(0);
                   1163:     }
                   1164:     ispipe[filnum] = 1;
                   1165:     return(1);
                   1166: #else /* Not OS2 */
                   1167:     int pipes[2];
                   1168:     int out;
                   1169: 
                   1170:     if (chkfn(filnum) < 0) return(-1); /* Need a valid Kermit file number. */
                   1171:     if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */
                   1172:       return(0);
                   1173: 
                   1174:     out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
                   1175: 
                   1176: /* Output to a command */
                   1177: 
                   1178:     if (out) {                         /* Need popen() to do this. */
                   1179: #ifdef NOPOPEN
                   1180:        return(0);                      /* no popen(), fail. */
                   1181: #else
                   1182: /* Use popen() to run the command. */
                   1183: 
                   1184: #ifdef _POSIX_SOURCE
                   1185: /* Strictly speaking, popen() is not available in POSIX.1 */
                   1186: #define DCLPOPEN
                   1187: #endif /* _POSIX_SOURCE */
                   1188: 
                   1189: #ifdef DCLPOPEN
                   1190: /* popen() needs declaring because it's not declared in <stdio.h> */
                   1191:        FILE *popen();
                   1192: #endif /* DCLPOPEN */
                   1193: 
                   1194:        if (priv_chk() || ((fp[filnum] = popen(comand,"w")) == NULL))
                   1195:          return(0);
                   1196:        else return(1);
                   1197: #endif /* NOPOPEN */
                   1198:     }
                   1199: 
                   1200: /* Input from a command */
                   1201: 
                   1202:     if (pipe(pipes) != 0) {
                   1203:        debug(F100,"zxcmd pipe failure","",0);
                   1204:        return(0);                      /* can't make pipe, fail */
                   1205:     }
                   1206: 
                   1207: /* Create a fork in which to run the named process */
                   1208: 
                   1209: #ifdef aegis
                   1210:     if ((pid = vfork()) == 0) {                /* child */
                   1211: #else
                   1212:     if ((pid = fork()) == 0) {         /* child */
                   1213: #endif
                   1214: 
                   1215: /* We're in the fork. */
                   1216: 
                   1217:        char *shpath, *shname, *shptr;  /* Find user's preferred shell */
                   1218: #ifndef aegis
                   1219:        struct passwd *p;
                   1220:        char *defshell = "/bin/sh";     /* default shell */
                   1221: #endif /* aegis */
                   1222:        if (priv_can()) exit(1);        /* Turn off any privileges! */
                   1223:        debug(F101,"zxcmd pid","",pid);
                   1224:        close(pipes[0]);                /* close input side of pipe */
                   1225:        close(0);                       /* close stdin */
                   1226:        if (open("/dev/null",0) < 0) return(0); /* replace input by null */
                   1227: #ifndef SVORPOSIX
                   1228:        dup2(pipes[1],1);               /* BSD: replace stdout & stderr */
                   1229:        dup2(pipes[1],2);               /* by the pipe */
                   1230: #else
                   1231:        close(1);                       /* AT&T: close stdout */
                   1232:        if ( dup(pipes[1]) != 1 )       /* Send stdout to the pipe */
                   1233:          return(0);
                   1234:        close(2);                       /* Send stderr to the pipe */
                   1235:        if ( dup(pipes[1]) != 2 )
                   1236:          return(0);
                   1237: #endif /* SVORPOSIX */
                   1238:        close(pipes[1]);                /* Don't need this any more. */
                   1239: 
                   1240: #ifdef aegis
                   1241:        if ((shpath = getenv("SERVERSHELL")) == NULL) shpath = "/bin/sh";
                   1242: #else
                   1243:        shpath = getenv("SHELL");       /* What shell? */
                   1244:        if (shpath == NULL) {
                   1245:            p = getpwuid( real_uid() ); /* Get login data */
                   1246:            if (p == (struct passwd *)NULL || !*(p->pw_shell))
                   1247:              shpath = defshell;
                   1248:            else shpath = p->pw_shell;
                   1249:         }
                   1250: #endif /* aegis */
                   1251:        shptr = shname = shpath;
                   1252:        while (*shptr != '\0')
                   1253:          if (*shptr++ == '/')
                   1254:            shname = shptr;
                   1255:        debug(F100,"zxcmd...","",0);
                   1256:        debug(F110,shpath,shname,0);
                   1257: 
                   1258:        execl(shpath,shname,"-c",comand,(char *)NULL); /* Execute the cmd */
                   1259:        exit(0);                        /* just punt if it failed. */
                   1260:     } else if (pid == -1) {
                   1261:        debug(F100,"zxcmd fork failure","",0);
                   1262:        return(0);
                   1263:     }
                   1264:     debug(F101,"zxcmd pid","",pid);
                   1265:     if (out) {
                   1266:        close(pipes[0]);                /* Don't need the input side */
                   1267:        fp[filnum] = fdopen(pipes[1],"w"); /* Open a stream for output. */
                   1268:        fp[ZSYSFN] = fp[filnum];        /* Remember. */
                   1269:        zoutcnt = 0;                    /* (PWP) reset input buffer */
                   1270:        zoutptr = zoutbuffer;
                   1271:     } else {
                   1272:        close(pipes[1]);                /* Don't need the output side */
                   1273:        fp[filnum] = fdopen(pipes[0],"r"); /* Open a stream for input. */
                   1274:        fp[ZSYSFN] = fp[filnum];        /* Remember. */
                   1275:        zincnt = 0;                     /* (PWP) reset input buffer */
                   1276:        zinptr = zinbuffer;
                   1277:     }
                   1278:     return(1);
                   1279: #endif /* OS2 */
                   1280: }
                   1281: 
                   1282: /*  Z C L O S F  - wait for the child fork to terminate and close the pipe. */
                   1283: 
                   1284: int
                   1285: zclosf(filnum) int filnum; {
                   1286:     int wstat;
                   1287:     debug(F101,"zclosf filnum","",filnum);
                   1288: #ifndef NOPOPEN
                   1289: #ifdef OS2
                   1290:     if (ispipe[filnum]) {
                   1291:        int x;
                   1292:        x = pclose(fp[filnum]);
                   1293:        fp[filnum] = NULL;
                   1294:        ispipe[filnum] = 0;
                   1295: #else
                   1296:     if (filnum == ZWFILE) {
                   1297:        int x;
                   1298:        x = pclose(fp[filnum]);
                   1299:        fp[filnum] = fp[ZSYSFN] = NULL;
                   1300: #endif /* OS2 */
                   1301:        return((x < 0) ? 0 : 1);
                   1302:     }
                   1303: #endif /* NOPOPEN */
                   1304:     debug(F101,"zclosf fp[filnum]","", fp[filnum]);
                   1305:     debug(F101,"zclosf fp[ZSYSFN]","", fp[ZSYSFN]);
                   1306: #ifdef OS2
                   1307:     fclose(fp[filnum]);
                   1308:     fp[filnum] = NULL;
                   1309: #else
                   1310:     if (pid != 0) {
                   1311:        debug(F101,"zclosf killing pid","",pid);
                   1312:        kill(pid,9);
                   1313:         while ((wstat = wait((WAIT_T *)0)) != pid && wstat != -1) ;
                   1314:         pid = 0;
                   1315:     }
                   1316:     fclose(fp[filnum]);
                   1317:     fp[filnum] = fp[ZSYSFN] = NULL;
                   1318: #endif /* OS2 */
                   1319:     return(1);
                   1320: }
                   1321: 
                   1322: /*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
                   1323: /*
                   1324:   Returns the number of files that match fn1, with data structures set up
                   1325:   so that first file (if any) will be returned by the next znext() call.
                   1326:   Depends on external variable wildxpand: 0 means we expand wildcards
                   1327:   internally, nonzero means we call the shell to do it.
                   1328: */
                   1329: 
                   1330: int
                   1331: zxpand(fn) char *fn; {
                   1332:     char *p;
                   1333: 
                   1334: #ifdef DTILDE                          /* Built with tilde-expansion? */
                   1335:     char *tnam;
                   1336: #endif /* DTILDE */
                   1337:     debug(F111,"zxpand entry",fn,wildxpand);
                   1338: #ifdef DTILDE                          /* Built with tilde-expansion? */
                   1339:     if (*fn == '~') {                  /* Starts with tilde? */
                   1340:        tnam = tilde_expand(fn);        /* Try to expand it. */
                   1341:        if (tnam) fn = tnam;
                   1342:     }
                   1343:     debug(F110,"zxpand after tilde_x",fn,0);
                   1344: #endif /* DTILDE */
                   1345: #ifndef OS2
                   1346:     if (wildxpand)                     /* Who is expanding wildcards? */
                   1347:       fcount = shxpand(fn,mtchs,MAXWLD); /* Shell */
                   1348:     else
                   1349: #endif /* OS2 */
                   1350:       fcount = fgen(fn,mtchs,MAXWLD);  /* Kermit */
                   1351:     if (fcount > 0) {
                   1352:        mtchptr = mtchs;                /* Save pointer for next. */
                   1353:     }
                   1354:     if (fcount > 0) {
                   1355:        debug(F111,"zxpand ok",mtchs[0],fcount);
                   1356:        return(fcount);
                   1357:     }
                   1358:     debug(F111,"zxpand fgen1",fn,fcount); /* Didn't get one, or got too many */
                   1359:     p = malloc((int)strlen(fn) + 10);  /* Make space */
                   1360:     if (!p) return(0);
                   1361:     zrtol(fn,p);                       /* Try again, maybe lowercase */
                   1362: #ifndef OS2
                   1363:     if (wildxpand)
                   1364:       fcount = shxpand(p,mtchs,MAXWLD); /* Shell */
                   1365:     else
                   1366: #endif /* OS2 */
                   1367:       fcount = fgen(p,mtchs,MAXWLD);   /* Kermit */
                   1368:     if (fcount > 0) {                  /* Got one? */
                   1369:        mtchptr = mtchs;                /* Save pointer for next. */
                   1370:        debug(F111,"zxpand fgen2 ok",mtchs[0],fcount);
                   1371:     } else debug(F111,"zxpand 2 not ok",p,fcount);
                   1372:     free(p);
                   1373:     return(fcount);
                   1374: }
                   1375: 
                   1376: 
                   1377: /*  Z N E X T  --  Get name of next file from list created by zxpand(). */
                   1378: /*
                   1379:  Returns >0 if there's another file, with its name copied into the arg string,
                   1380:  or 0 if no more files in list.
                   1381: */
                   1382: int
                   1383: znext(fn) char *fn; {
                   1384:     if (fcount-- > 0) strcpy(fn,*mtchptr++);
                   1385:     else *fn = '\0';
                   1386:     debug(F111,"znext",fn,fcount+1);
                   1387:     return(fcount+1);
                   1388: }
                   1389: 
                   1390: 
                   1391: /*  Z C H K S P A  --  Check if there is enough space to store the file  */
                   1392: 
                   1393: /*
                   1394:  Call with file specification f, size n in bytes.
                   1395:  Returns -1 on error, 0 if not enough space, 1 if enough space.
                   1396: */
                   1397: int
                   1398: zchkspa(f,n) char *f; long n; {                /* Just dummy for now. */
                   1399:     return(1);                         /* Always say OK. */
                   1400: }
                   1401: 
                   1402: 
                   1403: /*  Z N E W N  --  Make a new name for the given file  */
                   1404: 
                   1405: /*
                   1406:   Given the name, fn, of a file that already exists, this function builds a
                   1407:   new name of the form "<oldname>.~<n>~", where <oldname> is argument name
                   1408:   (fn), and <n> is a version number, one higher than any existing version
                   1409:   number for that file, up to 9999.  This format is consistent with that used
                   1410:   by GNU EMACS.  If the constructed name is too long for the system's maximum,
                   1411:   enough characters are truncated from the end of <fn> to allow the version
                   1412:   number to fit.  If no free version numbers exist between 1 and 9999, a
                   1413:   version number of "xxxx" is used.  Returns a pointer to the new name in
                   1414:   argument s.
                   1415: */
                   1416: 
                   1417: VOID
                   1418: znewn(fn,s) char *fn, **s; {
                   1419: #define ZNEWNBL 255                    /* Name buffer length */
                   1420: #define ZNEWNMD 4                      /* Max digits for version number */
                   1421:     static char buf[ZNEWNBL+1];
                   1422: #ifdef OS2
                   1423: /*
                   1424:   Well, OS/2 should be able to handle 8.3 FAT names still,
                   1425:   although HPFS is so much easier to live with ...
                   1426:   This code should be checked out with long names.
                   1427: */
                   1428:     char *bp, *xp, *yp, *zp, ch, temp[14];
                   1429:     int d, i, n;
                   1430:     xp = bp = buf;
                   1431:     while (*fn) {                      /* Copy name into buf */
                   1432:        ch = *bp++ = *fn++;
                   1433:        if (ISDIRSEP(ch) || (ch == ':')) xp=bp;
                   1434:     }
                   1435:     *bp = '\0';
                   1436:     yp = xp;
                   1437:     i = 1;
                   1438:     while (*yp && (*yp != '.')) {
                   1439:        yp++;
                   1440:        if (++i<=6) zp=yp;
                   1441:     }
                   1442:     /* zp points to 6th character in name, or yp, whichever occurs first. */
                   1443:     strcpy(temp,yp);                   /* Copy extension, if any */
                   1444:     while (zp != xp+8) {
                   1445:        if ( zp < xp+5 ) *zp++='0';
                   1446:        else *zp++='?';                 /* Pad out with wild cards */
                   1447:     }
                   1448:     strcpy(zp,temp);                   /* Get the extension back */
                   1449:     n = zxpand(buf);                   /* Expand the resulting wild name */
                   1450:     d = 0;                             /* Index number */
                   1451:     while (znext(temp)) {
                   1452:        i = atoi(temp+5);
                   1453:        if (i > d) d = i;
                   1454:     }
                   1455:     sprintf(temp,"%03d",d+1);          /* get the number into a string */
                   1456:     memcpy(xp+5, temp, 3);
                   1457: #else /* Not OS2 */
                   1458:     char *bp, *xp;
                   1459:     int len = 0, d = 0, n, t, i, j, k, power = 1;
                   1460: 
                   1461:     int max = MAXNAMLEN;               /* Maximum name length */
                   1462: 
                   1463:     if (max < 14) max = 14;            /* Make it reasonable */
                   1464:     if (max > ZNEWNBL) max = ZNEWNBL;
                   1465:     bp = buf;                          /* Buffer for building new name */
                   1466:     while (*fn) {                      /* Copy old name into buffer */
                   1467:        *bp++ = *fn++;
                   1468:        if (len++ > ZNEWNBL) break;     /* ...up to buffer length */
                   1469:     }
                   1470:     for (i = 1; i < ZNEWNMD + 1; i++) {        /* Version numbers up to 10**i - 1 */
                   1471:        power *= 10;                    /* Next power of 10 */
                   1472:        j = max - len;                  /* Space left for version number */
                   1473:        k = 3 + i;                      /* Space needed for it */
                   1474:        if (j < k) {                    /* Make room if necessary */
                   1475:            len -= (k - j);             /* Adjust length of filename */
                   1476:            bp = buf + len;             /* Point to new end */
                   1477:        }
                   1478:        *bp++ = '*';                    /* Put a star on the end (UNIX) */
                   1479:        *bp-- = '\0';                   /* Terminate with null */
                   1480: 
                   1481:        n = zxpand(buf);                /* Expand the resulting wild name */
                   1482:                                        /* n is the number of matches */
                   1483:        while (n-- > 0) {               /* Find any existing name.~n~ files */
                   1484:            xp = *mtchptr++;            /* Point at matching name */
                   1485:            xp += len;                  /* Look for .~<n>~ at the end of it */
                   1486:            if (*xp == '.' && *(xp+1) == '~') { /* Has a version number */
                   1487:                t = atoi(xp+2);                 /* Get it */
                   1488:                if (t > d) d = t;       /* Save d = highest version number */
                   1489:            }
                   1490:        }
                   1491:        if (d < power-1) {              /* Less than maximum possible? */
                   1492:            sprintf(bp,".~%d~",d+1);    /* Yes, make "name.~<d+1>~" */
                   1493:            *s = buf;                   /* Point to new name */
                   1494:            return;                     /* Done, return it */
                   1495:        }
                   1496:     }
                   1497:     sprintf(bp,".~xxxx~");             /* Too many, use xxxx. */
                   1498: #endif /* OS2 */
                   1499:     *s = buf;
                   1500:     return;
                   1501: }
                   1502: 
                   1503: /*  Z R E N A M E  --  Rename a file  */
                   1504: 
                   1505: /*  Note, link() and unlink() are used because rename() is not available  */
                   1506: /*  in some versions of UNIX.   */
                   1507: /*  Call with old and new names */
                   1508: /*  Returns 0 on success, -1 on failure. */
                   1509: 
                   1510: int
                   1511: zrename(old,new) char *old, *new; {
                   1512: #ifdef OS2
                   1513:    return rename(old, new);
                   1514: #else
                   1515:    if (link(old,new) < 0) {            /* Make a link with the new name. */
                   1516:        debug(F110," link fails",old,0);
                   1517:        return(-1);
                   1518:     }
                   1519:     if (unlink(old) < 0) {             /* Unlink the old name. */
                   1520:        debug(F110," link fails",old,0);
                   1521:        return(-1);
                   1522:     }
                   1523:     return(0);
                   1524: #endif /* OS2 */
                   1525: }
                   1526: 
                   1527: /*  Z S A T T R */
                   1528: /*
                   1529:  Fills in a Kermit file attribute structure for the file which is to be sent.
                   1530:  Returns 0 on success with the structure filled in, or -1 on failure.
                   1531:  If any string member is null, then it should be ignored.
                   1532:  If any numeric member is -1, then it should be ignored.
                   1533: */
                   1534: int
                   1535: zsattr(xx) struct zattr *xx; {
                   1536:     long k;
                   1537: 
                   1538:     k = iflen % 1024L;                 /* File length in K */
                   1539:     if (k != 0L) k = 1L;
                   1540:     xx->lengthk = (iflen / 1024L) + k;
                   1541:     xx->type.len = 0;                  /* File type can't be filled in here */
                   1542:     xx->type.val = "";
                   1543:     if (*nambuf) {
                   1544:        xx->date.val = zfcdat(nambuf);  /* File creation date */
                   1545:        xx->date.len = (int)strlen(xx->date.val);
                   1546:     } else {
                   1547:        xx->date.len = 0;
                   1548:        xx->date.val = "";
                   1549:     }
                   1550:     xx->creator.len = 0;               /* File creator */
                   1551:     xx->creator.val = "";
                   1552:     xx->account.len = 0;               /* File account */
                   1553:     xx->account.val = "";
                   1554:     xx->area.len = 0;                  /* File area */
                   1555:     xx->area.val = "";
                   1556:     xx->passwd.len = 0;                        /* Area password */
                   1557:     xx->passwd.val = "";
                   1558:     xx->blksize = -1L;                 /* File blocksize */
                   1559:     xx->access.len = 0;                        /* File access */
                   1560:     xx->access.val = "";
                   1561:     xx->encoding.len = 0;              /* Transfer syntax */
                   1562:     xx->encoding.val = 0;
                   1563:     xx->disp.len = 0;                  /* Disposition upon arrival */
                   1564:     xx->disp.val = "";
                   1565:     xx->lprotect.len = 0;              /* Local protection */
                   1566:     xx->lprotect.val = "";
                   1567:     xx->gprotect.len = 0;              /* Generic protection */
                   1568:     xx->gprotect.val = "";
                   1569:     xx->systemid.len = 2;              /* System ID */
                   1570:     xx->systemid.val = "U1";
                   1571:     xx->recfm.len = 0;                 /* Record format */
                   1572:     xx->recfm.val = "";
                   1573:     xx->sysparam.len = 0;              /* System-dependent parameters */
                   1574:     xx->sysparam.val = "";
                   1575:     xx->length = iflen;                        /* Length */
                   1576:     return(0);
                   1577: }
                   1578: 
                   1579: #ifdef COMMENT
                   1580: int
                   1581: zfree(f) char *f; {                    /* Check free space */
                   1582:     /* How??? */
                   1583:     return(0);
                   1584: }
                   1585: #endif /* COMMENT */
                   1586: 
                   1587: /* Z F C D A T  --  Get file creation date */
                   1588: /*
                   1589:   Call with pointer to filename.
                   1590:   On success, returns pointer to creation date in yyyymmdd hh:mm:ss format.
                   1591:   On failure, returns pointer to null string.
                   1592: */
                   1593: static char datbuf[40];
                   1594: 
                   1595: /* static */                           /* (===OS2 change===) */
                   1596: char *
                   1597: zfcdat(name) char *name; {
                   1598: 
                   1599: #ifdef TIMESTAMP
                   1600:     struct stat buffer;
                   1601:     struct tm *time_stamp, *localtime();
                   1602:     int yy, ss;
                   1603: 
                   1604:     datbuf[0] = '\0';
                   1605:     if(stat(name,&buffer) != 0) {
                   1606:        debug(F110,"zfcdat stat failed",name,0);
                   1607:        return("");
                   1608:     }
                   1609:     time_stamp = localtime(&(buffer.st_mtime));
                   1610:     yy = time_stamp->tm_year;
                   1611:     if (yy < 100)                      /* In case it returns 2-digit year? */
                   1612:       yy += 1900;
                   1613:     if (yy < 0 || yy > 9999) {         /* Make sure year is ok */
                   1614:        debug(F110,"zfcdat date failed",name,0);
                   1615:        return("");
                   1616:     }
                   1617:     if (time_stamp->tm_mon  < 0 || time_stamp->tm_mon  > 11)
                   1618:       return("");
                   1619:     if (time_stamp->tm_mday < 0 || time_stamp->tm_mday > 31)
                   1620:       return("");
                   1621:     if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 23)
                   1622:       return("");
                   1623:     if (time_stamp->tm_min  < 0 || time_stamp->tm_min  > 59)
                   1624:       return("");
                   1625:     ss = time_stamp->tm_sec;           /* Seconds */
                   1626:     if (ss < 0 || ss  > 59)            /* Some systems give a BIG number */
                   1627:       ss = 0;
                   1628:     sprintf(datbuf,
                   1629: #ifdef pdp11
                   1630: /* For some reason, 2.1x BSD sprintf gets the last field wrong. */
                   1631:            "%04d%02d%02d %02d:%02d:00",
                   1632: #else
                   1633:            "%04d%02d%02d %02d:%02d:%02d",
                   1634: #endif /* pdp11 */
                   1635:            yy,
                   1636:            time_stamp->tm_mon + 1,
                   1637:            time_stamp->tm_mday,
                   1638:            time_stamp->tm_hour,
                   1639:            time_stamp->tm_min
                   1640: #ifndef pdp11
                   1641:            , ss
                   1642: #endif /* pdp11 */
                   1643:            );
                   1644:     yy = (int)strlen(datbuf);
                   1645:     debug(F111,"zfcdat",datbuf,yy);
                   1646:     if (yy > 17) datbuf[17] = '\0';
                   1647:     return(datbuf);
                   1648: #else
                   1649:     return("");
                   1650: #endif /* timestamp */
                   1651: }
                   1652: 
                   1653: /* Z S T I M E  --  Set creation date for incoming file */
                   1654: /*
                   1655:  Call with:
                   1656:  f  = pointer to name of existing file.
                   1657:  yy = pointer to a Kermit file attribute structure in which yy->date.val
                   1658:       is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00.
                   1659:  x  = is a function code: 0 means to set the file's creation date as given.
                   1660:       1 means compare the given date with the file creation date.
                   1661:  Returns:
                   1662:  -1 on any kind of error.
                   1663:   0 if x is 0 and the file date was set successfully.
                   1664:   0 if x is 1 and date from attribute structure <= file creation date.
                   1665:   1 if x is 1 and date from attribute structure > file creation date.
                   1666: */
                   1667: int
                   1668: zstime(f,yy,x) char *f; struct zattr *yy; int x; {
                   1669:     int r = -1;                                /* return code */
                   1670: /*
                   1671:   It is ifdef'd TIMESTAMP because it might not work on V7. [email protected].
                   1672: */
                   1673: #ifdef TIMESTAMP
                   1674: /*
                   1675:   To do: adapt code from OS-9 Kermit's ck9fio.c zstime function, which
                   1676:   is more flexible, allowing [yy]yymmdd[ hh:mm[:ss]].
                   1677: */
                   1678: #ifndef OS2
                   1679:     extern int ftime(), stat(), utime();
                   1680:     /* at least, the declarations for int functions are not needed anyway */
                   1681:     extern struct tm *localtime();
                   1682:     /* and this should have been declared always through a header file */
                   1683: #endif /* OS2 */
                   1684:     long tm, days;
                   1685:     int i, n, isleapyear;
                   1686:                    /*       J  F  M  A   M   J   J   A   S   O   N   D   */
                   1687:                    /*      31 28 31 30  31  30  31  31  30  31  30  31   */
                   1688:     static
                   1689:     int monthdays [13] = {  0,0,31,59,90,120,151,181,212,243,273,304,334 };
                   1690:     char s[5];
                   1691:     struct stat sb;
                   1692: #ifdef OS2
                   1693:     struct utimbuf tp;
                   1694: #else
                   1695: #ifdef V7
                   1696:     struct utimbuf {
                   1697:       time_t timep[2];         /* New access and modificaton time */
                   1698:     } tp;
                   1699:     char *tz;
                   1700:     long timezone;             /* In case timezone not defined in .h file */
                   1701: #else
                   1702:    struct utimbuf {
                   1703:       time_t atime;            /* New access time */
                   1704:       time_t mtime;            /* New modification time */
                   1705:     } tp;
                   1706: #endif /* V7 */
                   1707: #endif /* OS2 */
                   1708: 
                   1709: #ifdef ANYBSD
                   1710:     long timezone;
                   1711:     static struct timeb tbp;
                   1712: #endif
                   1713: 
                   1714:     debug(F110,"zstime",f,0);
                   1715: 
                   1716:     if ((yy->date.len == 0)
                   1717:         || (yy->date.len != 17)
                   1718:         || (yy->date.val[8] != ' ')
                   1719:         || (yy->date.val[11] != ':')
                   1720:         || (yy->date.val[14] != ':') ) {
                   1721:         debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
                   1722:         return(-1);
                   1723:     }
                   1724:     debug(F111,"zstime date check 1",yy->date.val,yy->date.len);
                   1725:     for(i = 0; i < 8; i++) {
                   1726:        if (!isdigit(yy->date.val[i])) {
                   1727:            debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
                   1728:            return(-1);
                   1729:        }
                   1730:     }
                   1731:     debug(F111,"zstime date check 2",yy->date.val,yy->date.len);
                   1732:     i++;
                   1733: 
                   1734:     for (; i < 16; i += 3) {
                   1735:        if ((!isdigit(yy->date.val[i])) || (!isdigit(yy->date.val[i + 1]))) {
                   1736:            debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
                   1737:            return(-1);
                   1738:        }
                   1739:     }
                   1740:     debug(F111,"zstime date check 3",yy->date.val,yy->date.len);
                   1741: 
                   1742: #ifdef ANYBSD
                   1743:     debug(F100,"ztime BSD calling ftime","",0);
                   1744:     ftime(&tbp);
                   1745:     debug(F100,"ztime BSD back from ftime","",0);
                   1746:     timezone = tbp.timezone * 60L;
                   1747:     debug(F101,"ztime BSD timezone","",timezone);
                   1748: #endif
                   1749: 
                   1750: #ifdef ATTSV
                   1751:     tzset();                           /* Set `timezone'. */
                   1752: #endif /* ATTSV */
                   1753: 
                   1754: #ifdef V7
                   1755:     if ((tz = getenv("TZ")) == NULL)
                   1756:       timezone = 0;            /* UTC/GMT */
                   1757:     else
                   1758:       timezone = atoi(&tz[3]); /* Set 'timezone'. */
                   1759:     timezone *= 60L;
                   1760: #endif
                   1761: 
                   1762:     debug(F100,"zstime so far so good","",0);
                   1763: 
                   1764:     s[4] = '\0';
                   1765:     for (i = 0; i < 4; i++)    /* Fix the year */
                   1766:       s[i] = yy->date.val[i];
                   1767: 
                   1768:     debug(F110,"zstime year",s,0);
                   1769: 
                   1770:     n = atoi(s);
                   1771: 
                   1772:     debug(F111,"zstime year",s,n);
                   1773: 
                   1774: /* Previous year's leap days. This won't work after year 2100, */
                   1775: /* I don't care about that! */
                   1776:     isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
                   1777:     days = (long) (n - 1970) * 365;
                   1778:     days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
                   1779: 
                   1780:     s[2] = '\0';
                   1781: 
                   1782:     for (i = 4 ; i < 16; i += 2) {
                   1783:        s[0] = yy->date.val[i];
                   1784:        s[1] = yy->date.val[i + 1];
                   1785:        n = atoi(s);
                   1786:     debug(F110,"zstime entering switch",s,0);
                   1787:        switch (i) {
                   1788:          case 4:                       /* MM: month */
                   1789:            if ((n < 1 ) || ( n > 12)) {
                   1790:                debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
                   1791:                return(-1);
                   1792:            }
                   1793:            days += monthdays [n];
                   1794:            if (isleapyear && n > 2)
                   1795:              ++days;
                   1796:            continue;
                   1797: 
                   1798:          case 6:                       /* DD: day */
                   1799:            if ((n < 1 ) || ( n > 31)) {
                   1800:                debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
                   1801:                return(-1);
                   1802:            }
                   1803:            tm = (days + n - 1) * 24L * 60L * 60L;
                   1804:            i++;                        /* Skip the space */
                   1805:            continue;
                   1806: 
                   1807:          case 9:                       /* hh: hour */
                   1808:            if ((n < 0 ) || ( n > 23)) {
                   1809:                debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
                   1810:                return(-1);
                   1811:            }
                   1812:            tm += n * 60L * 60L;
                   1813:            i++;                        /* Skip the colon */
                   1814:            continue;
                   1815: 
                   1816:          case 12:                      /* mm: minute */
                   1817:            if ((n < 0 ) || ( n > 59)) {
                   1818:                debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
                   1819:                return(-1);
                   1820:            }
                   1821: #ifdef ANYBSD                          /* Correct for time zone */
                   1822:            tm += timezone;
                   1823: #else
                   1824: #ifdef ultrix
                   1825:            tm += (long) timezone;
                   1826: #else
                   1827:            tm += timezone;
                   1828: #endif /* ultrix */
                   1829: #endif /* ANYBSD */
                   1830:            tm += n * 60L;
                   1831:            i++;                        /* Skip the colon */
                   1832:            continue;
                   1833: 
                   1834:          case 15:                      /* ss: second */
                   1835:            if ((n < 0 ) || ( n > 59)) {
                   1836:                debug(F111,"Bad creation date ",yy->date.val,yy->date.len);
                   1837:                return(-1);
                   1838:            }
                   1839:            tm += n;
                   1840:        }
                   1841: 
                   1842:        if (localtime(&tm)->tm_isdst)
                   1843:          tm -= 60L * 60L;              /* Adjust for daylight savings time */
                   1844:     }
                   1845: 
                   1846:     debug(F111,"Attribute creation date ok ",yy->date.val,yy->date.len);
                   1847: 
                   1848:     if (stat(f,&sb)) {                 /* Get the time for the file */
                   1849:        debug(F110,"Can't stat file:",f,0);
                   1850:        return(-1);
                   1851:     }
                   1852: #ifdef OS2
                   1853:     tp.modtime = tm;                   /* Set modif. time to creation date */
                   1854:     tp.actime = sb.st_atime;           /* Don't change the access time */
                   1855: #else
                   1856: #ifdef V7
                   1857:     tp.timep[0] = tm;                  /* Set modif. time to creation date */
                   1858:     tp.timep[1] = sb.st_atime;         /* Don't change the access time */
                   1859: #else
                   1860:     tp.mtime = tm;                     /* Set modif. time to creation date */
                   1861:     tp.atime = sb.st_atime;            /* Don't change the access time */
                   1862: #endif /* V7 */
                   1863: #endif /* OS2 */
                   1864: 
                   1865:     switch (x) {                       /* Execute desired function */
                   1866:       case 0:                          /* Set the creation date of the file */
                   1867:        if (utime(f,&tp)) {             /* Fix modification time */
                   1868:            debug(F110,"Can't set modification time for file: ",f,0);
                   1869:            r = -1;
                   1870:        } else  {
                   1871:            debug(F110,"Modification time is set for file: ",f,0);
                   1872:            r = 0;
                   1873:        }
                   1874:        break;
                   1875:       case 1:                          /* Compare the dates */
                   1876:        debug(F111,"zstime compare",f,sb.st_atime);
                   1877:        debug(F111,"zstime compare","packet",tm);
                   1878:        if (sb.st_atime < tm) r = 0; else r = 1;
                   1879:        break;
                   1880:       default:                         /* Error */
                   1881:        r = -1;
                   1882:     }
                   1883: #endif /* TIMESTAMP */
                   1884:     return(r);
                   1885: }
                   1886: 
                   1887: /* Find initialization file. */
                   1888: 
                   1889: #ifdef NOTUSED
                   1890: int
                   1891: zkermini() {
                   1892: /*  nothing here for Unix.  This function added for benefit of VMS Kermit.  */
                   1893:     return(0);
                   1894: }
                   1895: #endif /* NOTUSED */
                   1896: 
                   1897: #ifndef NOFRILLS
                   1898: int
                   1899: zmail(p,f) char *p; char *f; {         /* Send file f as mail to address p */
                   1900: /*
                   1901:   Returns 0 on success
                   1902:    2 if mail delivered but temp file can't be deleted
                   1903:   -2 if mail can't be delivered
                   1904:   The UNIX version always returns 0 because it can't get a good return
                   1905:   code from zsyscmd.
                   1906: */
                   1907: #ifdef BSD4
                   1908: /* The idea is to use /usr/ucb/mail, rather than regular mail, so that   */
                   1909: /* a subject line can be included with -s.  Since we can't depend on the */
                   1910: /* user's path, we use the convention that /usr/ucb/Mail = /usr/ucb/mail */
                   1911: /* and even if Mail has been moved to somewhere else, this should still  */
                   1912: /* find it...  The search could be made more reliable by actually using  */
                   1913: /* access() to see if /usr/ucb/Mail exists. */
                   1914: 
                   1915: /* Should also make some check on zmbuf overflow... */
                   1916: 
                   1917: #ifdef OXOS
                   1918:     sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
                   1919: #else
                   1920:     sprintf(zmbuf,"Mail -s %c%s%c %s < %s", '"', f, '"', p, f);
                   1921: #endif /* OXOS */
                   1922:     zsyscmd(zmbuf);
                   1923: #else
                   1924: #ifdef SVORPOSIX
                   1925:     sprintf(zmbuf,"mail %s < %s", p, f);
                   1926:     zsyscmd(zmbuf);
                   1927: #else
                   1928:     *zmbuf = '\0';
                   1929: #endif
                   1930: #endif
                   1931:     return(0);
                   1932: }
                   1933: #endif /* NOFRILLS */
                   1934: 
                   1935: #ifndef NOFRILLS
                   1936: int
                   1937: zprint(p,f) char *p; char *f; {                /* Print file f with options p */
                   1938: 
                   1939: #ifdef OS2
                   1940:     sprintf(zmbuf,"print %s %s", p, f); /* Construct print command */
                   1941:     zsyscmd(zmbuf);
                   1942: #else
                   1943: #ifdef UNIX
                   1944: #ifdef ANYBSD                          /* BSD uses lpr to spool */
                   1945: #ifndef OXOS                           /* Except Olivetti... */
                   1946: #define SPOOLER "lpr"
                   1947: #else
                   1948: #define SPOOLER "lp"
                   1949: #endif /* OXOS */
                   1950: #else                                  /* Sys V uses lp */
                   1951: #ifdef TRS16                           /* except for Tandy-16/6000... */
                   1952: #define SPOOLER "lpr"
                   1953: #else
                   1954: #define SPOOLER "lp"
                   1955: #endif
                   1956: #endif
                   1957: /*
                   1958:   Note use of standard input redirection.  In some systems, lp[r] runs
                   1959:   setuid to lp (or ...?), so if user has sent a file into a directory
                   1960:   that lp does not have read access to, it can't be printed unless it is
                   1961:   feed to lp[r] as standard input.
                   1962: */
                   1963:     sprintf(zmbuf,"%s %s < %s", SPOOLER, p, f); /* Construct print command */
                   1964:     zsyscmd(zmbuf);
                   1965: #else /* Not UNIX */
                   1966:     *zmbuf = '\0';
                   1967: #endif /* UNIX */
                   1968: #endif /* OS2 */
                   1969:     return(0);
                   1970: }
                   1971: #endif /* NOFRILLS */
                   1972: 
                   1973: /*
                   1974:   Wildcard expansion functions.  C-Kermit used to do this itself (see #else
                   1975:   part...).  New code (version 5A, 1990-91) just asks UNIX to do it.
                   1976:   This lets users use the wildcard expansion features of their favorite shell.
                   1977:   Operation is slower because of the forking & piping, but flexibility is
                   1978:   greater and program is smaller.  For OS/2, C-Kermit still does this itself.
                   1979: */
                   1980: 
                   1981: static char scratch[MAXPATH+4];                /* Used by both methods */
                   1982: 
                   1983: #ifndef OS2
                   1984: static int oldmtchs = 0;               /* Let shell (ls) expand them. */
                   1985: #ifdef COMMENT
                   1986: static char *lscmd = "/bin/ls -d";     /* Command to use. */
                   1987: #else
                   1988: static char *lscmd = "echo";           /* Command to use. */
                   1989: #endif /* COMMENT */
                   1990: 
                   1991: int
                   1992: shxpand(pat,namlst,len) char *pat, *namlst[]; int len; {
                   1993:     char *fgbuf = NULL;                        /* Buffer for forming ls command */
                   1994:     char *p, *q;                       /* Workers */
                   1995:     int i, x, retcode; char c;         /* ... */
                   1996: 
                   1997:     x = (int)strlen(pat) + (int)strlen(lscmd) + 3; /* Length of ls command */
                   1998:     for (i = 0; i < oldmtchs; i++)     /* Free previous file list */
                   1999:       free(namlst[i]);
                   2000:     fgbuf = malloc(x);                 /* Get buffer for command */
                   2001:     if (!fgbuf) return(-1);            /* Fail if cannot */
                   2002:     sprintf(fgbuf,"%s %s",lscmd,pat);  /* Form the command */
                   2003:     zxcmd(ZIFILE,fgbuf);               /* Start the command */
                   2004:     i = 0;                             /* File counter */
                   2005:     p = scratch;                       /* Point to scratch area */
                   2006:     retcode = -1;                      /* Assume failure */
                   2007:     while ((x = zminchar()) != -1) {   /* Read characters from command */
                   2008:        c = (char) x;
                   2009:        if (c == ' ' || c == '\n') {    /* Got newline or space? */
                   2010:            *p = '\0';                  /* Yes, terminate string */
                   2011:            p = scratch;                /* Point back to beginning */
                   2012:            if (zchki(p) == -1)         /* Does file exist? */
                   2013:              continue;                 /* No, continue */
                   2014:            x = (int)strlen(p);         /* Yes, get length of name */
                   2015:            q = malloc(x+1);            /* Allocate space for it */
                   2016:            if (!q) goto shxfin;        /* Fail if space can't be obtained */
                   2017:            strcpy(q,scratch);          /* Copy name to space */
                   2018:            namlst[i++] = q;            /* Copy pointer to name into array */
                   2019:            if (i > len) goto shxfin;   /* Fail if too many */
                   2020:        } else {                        /* Regular character */
                   2021:            *p++ = c;                   /* Copy it into scratch area */
                   2022:        }
                   2023:     }
                   2024:     retcode = i;                       /* Return number of matching files */
                   2025: shxfin:                                        /* Common exit point */
                   2026:     free(fgbuf);                       /* Free command buffer */
                   2027:     zclosf(ZIFILE);                    /* Delete the command fork. */
                   2028:     oldmtchs = i;                      /* Remember how many files */
                   2029:     return(retcode);
                   2030: }
                   2031: #endif /* OS2 */
                   2032: 
                   2033: /* Directory Functions for Unix, written by Jeff Damens, CUCCA, 1984. */
                   2034: 
                   2035: /* Define the size of the string space for filename expansion. */
                   2036: 
                   2037: #ifndef DYNAMIC
                   2038: #ifdef PROVX1
                   2039: #define SSPACE 500
                   2040: #else
                   2041: #ifdef BSD29
                   2042: #define SSPACE 500
                   2043: #else
                   2044: #ifdef pdp11
                   2045: #define SSPACE 500
                   2046: #else
                   2047: #ifdef aegis
                   2048: #define SSPACE 10000                   /* size of string-generating buffer */
                   2049: static char bslash;                    /* backslash character if active */
                   2050: #else                                  /* Default static buffer size */
                   2051: #define SSPACE 2000                    /* size of string-generating buffer */
                   2052: #endif /* aegis */
                   2053: #endif /* pdp11 */
                   2054: #endif /* BSD29 */
                   2055: #endif /* PROVX1 */
                   2056: static char sspace[SSPACE];             /* buffer for generating filenames */
                   2057: #else /* DYNAMIC */
                   2058: #define SSPACE 10000
                   2059: static char *sspace = (char *)0;
                   2060: #endif /* DYNAMIC */
                   2061: static int ssplen = SSPACE;            /* length of string space buffer */
                   2062: 
                   2063: static char *freeptr,**resptr;                 /* copies of caller's arguments */
                   2064: static int remlen;                      /* remaining length in caller's array*/
                   2065: static int numfnd;                      /* number of matches found */
                   2066: 
                   2067: /*
                   2068:  * splitpath:
                   2069:  *  takes a string and splits the slash-separated portions into
                   2070:  *  a list of path structures.  Returns the head of the list.  The
                   2071:  *  structures are allocated by malloc, so they must be freed.
                   2072:  *  Splitpath is used internally by the filename generator.
                   2073:  *
                   2074:  * Input: A string.
                   2075:  * Returns: A linked list of the slash-separated segments of the input.
                   2076:  */
                   2077: 
                   2078: struct path *
                   2079: splitpath(p) char *p; {
                   2080:     struct path *head,*cur,*prv;
                   2081:     int i;
                   2082: 
                   2083:     debug(F110,"splitpath",p,0);
                   2084: 
                   2085:     head = prv = NULL;
                   2086:     if (ISDIRSEP(*p)) p++;                     /* skip leading slash */
                   2087:     while (*p != '\0') {
                   2088:        cur = (struct path *) malloc(sizeof (struct path));
                   2089:        debug(F101,"splitpath malloc","",cur);
                   2090:        if (cur == NULL)
                   2091:          fatal("malloc fails in splitpath()");
                   2092:        cur -> fwd = NULL;
                   2093:        if (head == NULL)
                   2094:          head = cur;
                   2095:        else
                   2096:          prv -> fwd = cur;             /* link into chain */
                   2097:        prv = cur;
                   2098: #ifdef aegis
                   2099:        /* treat backslash as "../" */
                   2100:        if (bslash && *p == bslash) {
                   2101:            strcpy(cur->npart, "..");
                   2102:            ++p;
                   2103:        } else {
                   2104:            for (i=0; i < MAXNAMLEN && *p && *p != '/' && *p != bslash; i++)
                   2105:              cur -> npart[i] = *p++;
                   2106:            cur -> npart[i] = '\0';     /* end this segment */
                   2107:            if (i >= MAXNAMLEN)
                   2108:              while (*p && *p != '/' && *p != bslash)
                   2109:                p++;
                   2110:        }
                   2111:        if (*p == '/') p++;
                   2112: #else
                   2113: #ifdef OS2
                   2114:        for (i = 0;
                   2115:             i < MAXNAMLEN && !ISDIRSEP(*p) && *p != ':' && *p != '\0';
                   2116:             i++ )
                   2117:            cur -> npart[i] = *p++;
                   2118:         if ( *p == ':' ) {
                   2119:             cur -> npart[i++] = *p++;
                   2120:             if ( !ISDIRSEP(*p) )
                   2121:                 cur -> npart[i++] = '.';
                   2122:         }
                   2123: #else
                   2124:        for (i=0; i < MAXNAMLEN && !ISDIRSEP(*p) && *p != '\0'; i++) {
                   2125:            cur -> npart[i] = *p++;
                   2126:        }
                   2127: #endif /* OS2 */
                   2128:        cur -> npart[i] = '\0';         /* end this segment */
                   2129:        if (i >= MAXNAMLEN)
                   2130:          while (!ISDIRSEP(*p) && *p != '\0') p++;
                   2131:        if (ISDIRSEP(*p))
                   2132:          p++;
                   2133: 
                   2134: #endif /* aegis */
                   2135:     }
                   2136:     return(head);
                   2137: }
                   2138: 
                   2139: /*
                   2140:  * fgen:
                   2141:  *  This is the actual name generator.  It is passed a string,
                   2142:  *  possibly containing wildcards, and an array of character pointers.
                   2143:  *  It finds all the matching filenames and stores them into the array.
                   2144:  *  The returned strings are allocated from a static buffer local to
                   2145:  *  this module (so the caller doesn't have to worry about deallocating
                   2146:  *  them); this means that successive calls to fgen will wipe out
                   2147:  *  the results of previous calls.  This isn't a problem here
                   2148:  *  because we process one wildcard string at a time.
                   2149:  *
                   2150:  * Input: a wildcard string, an array to write names to, the
                   2151:  *        length of the array.
                   2152:  * Returns: the number of matches.  The array is filled with filenames
                   2153:  *          that matched the pattern.  If there wasn't enough room in the
                   2154:  *         array, -1 is returned.
                   2155:  * By: Jeff Damens, CUCCA, 1984.
                   2156:  */
                   2157: static int
                   2158: fgen(pat,resarry,len) char *pat,*resarry[]; int len; {
                   2159:     struct path *head;
                   2160:     char *sptr;
                   2161: #ifdef aegis
                   2162:     char *namechars;
                   2163:     int tilde = 0, bquote = 0;
                   2164: 
                   2165:     if ((namechars = getenv("NAMECHARS")) != NULL) {
                   2166:        if (xindex(namechars, '~' ) != NULL) tilde  = '~';
                   2167:        if (xindex(namechars, '\\') != NULL) bslash = '\\';
                   2168:        if (xindex(namechars, '`' ) != NULL) bquote = '`';
                   2169:     } else {
                   2170:        tilde = '~'; bslash = '\\'; bquote = '`';
                   2171:     }
                   2172: 
                   2173:     sptr = scratch;
                   2174: 
                   2175:     /* copy "`node_data", etc. anchors */
                   2176:     if (bquote && *pat == bquote)
                   2177:       while (*pat && *pat != '/' && *pat != bslash)
                   2178:        *sptr++ = *pat++;
                   2179:     else if (tilde && *pat == tilde)
                   2180:       *sptr++ = *pat++;
                   2181:     while (*pat == '/')
                   2182:       *sptr++ = *pat++;
                   2183:     if (sptr == scratch) {
                   2184:        strcpy(scratch,"./");
                   2185:        sptr = scratch+2;
                   2186:     }                                  /* init buffer correctly */
                   2187:     head = splitpath(pat);
                   2188: #else /* not aegis */
                   2189:     debug(F110,"fgen pat",pat,0);
                   2190:     head = splitpath(pat);
                   2191:     sptr = scratch;
                   2192:     if (!ISDIRSEP(*pat))
                   2193:        *sptr++ = '.';                  /* init buffer correctly */
                   2194:     *sptr++ = DIRSEP;
                   2195: #ifdef OS2
                   2196:     if (isalpha(pat[0]) && pat[1] == ':')
                   2197:         sptr = scratch;                 /* reset in case of leading drive: */
                   2198: #endif
                   2199: #endif /* aegis */
                   2200:     numfnd = 0;                                /* none found yet */
                   2201: #ifdef DYNAMIC
                   2202:     if (!sspace) {                     /* Need to allocate string space? */
                   2203:        while (ssplen > 50) {
                   2204:            if ((sspace = malloc(ssplen+2))) { /* Got it. */
                   2205:                debug(F101,"fgen string space","",ssplen);
                   2206:                break;
                   2207:            }
                   2208:            ssplen = (ssplen / 2) + (ssplen / 4); /* Didn't, reduce by 3/4 */
                   2209:        }
                   2210:        if (ssplen < 50) {              /* Did we get it? */
                   2211:            fprintf(stderr,"fgen can't malloc string space\n");
                   2212:            return(-1);
                   2213:        }
                   2214:     }
                   2215: #endif /* DYNAMIC */
                   2216:     freeptr = sspace;                  /* this is where matches are copied */
                   2217:     resptr = resarry;                  /* static copies of these so */
                   2218:     remlen = len;                      /* recursive calls can alter them */
                   2219:     traverse(head,scratch,sptr);       /* go walk the directory tree */
                   2220: #ifdef COMMENT
                   2221: /*
                   2222:   This code, circa 1984, has never worked right - it references the head
                   2223:   pointer after it has already been freed.  Lord knows what might have been
                   2224:   happening because of this.  Thanks to Steve Walton for finding & fixing this
                   2225:   bug.
                   2226: */
                   2227:     for (; head != NULL; head = head -> fwd)
                   2228:       free(head);                      /* return the path segments */
                   2229: #else
                   2230:     while (head != NULL) {
                   2231:        struct path *next = head -> fwd;
                   2232:        free(head);
                   2233:        head = next;
                   2234:     }
                   2235: #endif /* COMMENT */
                   2236:     return(numfnd);                    /* and return the number of matches */
                   2237: }
                   2238: 
                   2239: /* traverse:
                   2240:  *  Walks the directory tree looking for matches to its arguments.
                   2241:  *  The algorithm is, briefly:
                   2242:  *   If the current pattern segment contains no wildcards, that
                   2243:  *   segment is added to what we already have.  If the name so far
                   2244:  *   exists, we call ourselves recursively with the next segment
                   2245:  *   in the pattern string; otherwise, we just return.
                   2246:  *
                   2247:  *   If the current pattern segment contains wildcards, we open the name
                   2248:  *   we've accumulated so far (assuming it is really a directory), then read
                   2249:  *   each filename in it, and, if it matches the wildcard pattern segment, add
                   2250:  *   that filename to what we have so far and call ourselves recursively on the
                   2251:  *   next segment.
                   2252:  *
                   2253:  *   Finally, when no more pattern segments remain, we add what's accumulated
                   2254:  *   so far to the result array and increment the number of matches.
                   2255:  *
                   2256:  * Input: a pattern path list (as generated by splitpath), a string
                   2257:  *       pointer that points to what we've traversed so far (this
                   2258:  *       can be initialized to "/" to start the search at the root
                   2259:  *       directory, or to "./" to start the search at the current
                   2260:  *       directory), and a string pointer to the end of the string
                   2261:  *       in the previous argument.
                   2262:  * Returns: nothing.
                   2263:  */
                   2264: static VOID
                   2265: traverse(pl,sofar,endcur) struct path *pl; char *sofar, *endcur; {
                   2266: 
                   2267: /* Define LONGFN (long file names) automatically for BSD 2.9 and 4.2 */
                   2268: /* LONGFN can also be defined on the cc command line. */
                   2269: 
                   2270: #ifdef BSD29
                   2271: #ifndef LONGFN
                   2272: #define LONGFN
                   2273: #endif
                   2274: #endif
                   2275: 
                   2276: #ifdef BSD42
                   2277: #ifndef LONGFN
                   2278: #define LONGFN
                   2279: #endif
                   2280: #endif
                   2281: 
                   2282: /* Appropriate declarations for directory routines and structures */
                   2283: /* #define OPENDIR means to use opendir(), readdir(), closedir()  */
                   2284: /* If OPENDIR not defined, we use open(), read(), close() */
                   2285: 
                   2286: #ifdef DIRENT                          /* New way, <dirent.h> */
                   2287: #define OPENDIR
                   2288:     DIR *fd, *opendir();
                   2289:     struct dirent *dirbuf;
                   2290:     struct dirent *readdir();
                   2291: #else /* !DIRENT */
                   2292: #ifdef LONGFN                          /* Old way, <dir.h> with opendir() */
                   2293: #define OPENDIR
                   2294:     DIR *fd, *opendir();
                   2295:     struct direct *dirbuf;
                   2296: #else /* !LONGFN */
                   2297:     int fd;                            /* Old way, <dir.h> with open() */
                   2298:     struct direct dir_entry;
                   2299:     struct direct *dirbuf = &dir_entry;
                   2300: #endif /* LONGFN */
                   2301: #endif /* DIRENT */
                   2302: 
                   2303:     struct stat statbuf;               /* for file info */
                   2304: 
                   2305:     if (pl == NULL) {
                   2306:        *--endcur = '\0';               /* end string, overwrite trailing / */
                   2307:        addresult(sofar);
                   2308:        return;
                   2309:     }
                   2310:     if (!iswild(pl -> npart)) {
                   2311:        strcpy(endcur,pl -> npart);
                   2312:        endcur += (int)strlen(pl -> npart);
                   2313:        *endcur = '\0';                 /* end current string */
                   2314:        if (stat(sofar,&statbuf) == 0) { /* if current piece exists */
                   2315: #ifdef OS2
                   2316:            if (endcur - sofar == 3 && endcur[-1] == '.' && endcur[-2] == ':')
                   2317:              endcur--;
                   2318:            else
                   2319: #endif /* OS2 */
                   2320:              *endcur++ = DIRSEP;       /* add slash to end */
                   2321:            *endcur = '\0';             /* and end the string */
                   2322:            traverse(pl -> fwd,sofar,endcur);
                   2323:        }
                   2324:        return;
                   2325:     }
                   2326: 
                   2327:     /* Segment contains wildcards, have to search directory */
                   2328: 
                   2329:     *endcur = '\0';                            /* end current string */
                   2330:     if (stat(sofar,&statbuf) == -1) return;    /* doesn't exist, forget it */
                   2331:     if (!S_ISDIR (statbuf.st_mode)) return;    /* not a directory, skip */
                   2332: 
                   2333: #ifdef OPENDIR
                   2334:     if ((fd = opendir(sofar)) == NULL) return; /* Can't open, fail. */
                   2335:     while (dirbuf = readdir(fd))
                   2336: #else /* !OPENDIR */
                   2337:     if ((fd = open(sofar,O_RDONLY)) < 0) return; /* Can't open, fail. */
                   2338:     while (read(fd, (char *)dirbuf, sizeof dir_entry))
                   2339: #endif /* OPENDIR */
                   2340:       {
                   2341:          /* Get null-terminated copy!!! */
                   2342:          strncpy(nambuf,dirbuf->d_name,MAXNAMLEN);
                   2343:          nambuf[MAXNAMLEN] = '\0';
                   2344: #ifdef unos
                   2345:          if (dirbuf->d_ino != -1 && match(pl -> npart,nambuf))
                   2346: #else
                   2347: /* #ifdef _POSIX_SOURCE */
                   2348: /*
                   2349:   This is not specified in POSIX.1.  In POSIX.2, there will be
                   2350:   some kind of filename matching facility, but it will not be done this way.
                   2351:   Therefore, we should not even be looking in the directory at all.
                   2352:   Maybe POSIX implementations should force "set wildcard shell"?
                   2353: */
                   2354: 
                   2355: #ifdef sun
                   2356:          if (dirbuf->d_fileno != 0 && match(pl -> npart,nambuf))
                   2357: #else
                   2358: #ifdef ultrix
                   2359:          if (dirbuf->gd_ino != 0 && match(pl -> npart,nambuf))
                   2360: #else
                   2361:          if (dirbuf->d_ino != 0 && match(pl -> npart,nambuf))
                   2362: #endif /* ultrix */
                   2363: #endif /* sun */
                   2364: 
                   2365: /* #else */ /* not _POSIX_SOURCE */
                   2366: /*       if (dirbuf->d_ino != 0 && match(pl -> npart,nambuf)) */
                   2367: /* #endif */ /* _POSIX_SOURCE */
                   2368: 
                   2369: #endif /* unos */
                   2370:          {
                   2371:              char *eos;
                   2372:              strcpy(endcur,nambuf);
                   2373:              eos = endcur + (int)strlen(nambuf);
                   2374:              *eos++ = DIRSEP;          /* end this segment */
                   2375:              traverse(pl -> fwd,sofar,eos);
                   2376:          }
                   2377:       }
                   2378: #ifdef OPENDIR
                   2379:     closedir(fd);
                   2380: #else /* !OPENDIR */
                   2381:     close(fd);
                   2382: #endif /* OPENDIR */
                   2383: }
                   2384: 
                   2385: /*
                   2386:  * addresult:
                   2387:  *  Adds a result string to the result array.  Increments the number
                   2388:  *  of matches found, copies the found string into our string
                   2389:  *  buffer, and puts a pointer to the buffer into the caller's result
                   2390:  *  array.  Our free buffer pointer is updated.  If there is no
                   2391:  *  more room in the caller's array, the number of matches is set to -1.
                   2392:  * Input: a result string.
                   2393:  * Returns: nothing.
                   2394:  */
                   2395: static VOID
                   2396: addresult(str) char *str; {
                   2397:     int l;
                   2398:     debug(F111,"addresult",str,remlen);
                   2399:     if (str[0] == '.' && ISDIRSEP(str[1])) str += 2; /* (===OS2 change===) */
                   2400:     if (--remlen < 0) {
                   2401:        numfnd = -1;
                   2402:        return;
                   2403:     }
                   2404:     l = (int)strlen(str) + 1;          /* size this will take up */
                   2405:     if ((freeptr + l) > (sspace + ssplen)) {
                   2406:        numfnd = -1;                    /* do not record if not enough space */
                   2407:        return;
                   2408:     }
                   2409:     strcpy(freeptr,str);
                   2410:     *resptr++ = freeptr;
                   2411:     freeptr += l;
                   2412:     numfnd++;
                   2413: }
                   2414: 
                   2415: /*
                   2416:  * match:
                   2417:  *  pattern matcher.  Takes a string and a pattern possibly containing
                   2418:  *  the wildcard characters '*' and '?'.  Returns true if the pattern
                   2419:  *  matches the string, false otherwise.
                   2420:  * by: Jeff Damens, CUCCA, 1984
                   2421:  * skipping over dot files and backslash quoting added by fdc, 1990.
                   2422:  *
                   2423:  * Input: a string and a wildcard pattern.
                   2424:  * Returns: 1 if match, 0 if no match.
                   2425:  */
                   2426: static int
                   2427: match(pattern,string) char *pattern,*string; {
                   2428:     char *psave,*ssave;                        /* back up pointers for failure */
                   2429:     int q = 0;                         /* quote flag */
                   2430: 
                   2431:     debug(F110,"match str",string,0);
                   2432:     psave = ssave = NULL;
                   2433: #ifndef MATCHDOT
                   2434:     if (*string == '.' && *pattern != '.') {
                   2435:        debug(F110,"match skip",string,0);
                   2436:        return(0);
                   2437:     }
                   2438: #endif
                   2439:     while (1) {
                   2440:        for (; *pattern == *string; pattern++,string++)  /* skip first */
                   2441:            if (*string == '\0') return(1);     /* end of strings, succeed */
                   2442: 
                   2443:        if (*pattern == '\\' && q == 0) { /* Watch out for quoted */
                   2444:            q = 1;                      /* metacharacters */
                   2445:            pattern++;                  /* advance past quote */
                   2446:            if (*pattern != *string) return(0);
                   2447:            continue;
                   2448:        } else q = 0;
                   2449: 
                   2450:        if (q) {
                   2451:            return(0);
                   2452:        } else {
                   2453:            if (*string != '\0' && *pattern == '?') {
                   2454:                pattern++;              /* '?', let it match */
                   2455:                string++;
                   2456:            } else if (*pattern == '*') { /* '*' ... */
                   2457:                psave = ++pattern;      /* remember where we saw it */
                   2458:                ssave = string;         /* let it match 0 chars */
                   2459:            } else if (ssave != NULL && *ssave != '\0') { /* if not at end  */
                   2460:                                        /* ...have seen a star */
                   2461:                string = ++ssave;       /* skip 1 char from string */
                   2462:                pattern = psave;        /* and back up pattern */
                   2463:            } else return(0);           /* otherwise just fail */
                   2464:        }
                   2465:     }
                   2466: }
                   2467: 
                   2468: /*
                   2469:   The following two functions are for expanding tilde in filenames
                   2470:   Contributed by Howie Kaye, CUCCA, developed for CCMD package.
                   2471: */
                   2472: 
                   2473: /*  W H O A M I  --  Get user's username.  */
                   2474: 
                   2475: /*
                   2476:   1) Get real uid
                   2477:   2) See if the $USER environment variable is set ($LOGNAME on AT&T)
                   2478:   3) If $USER's uid is the same as ruid, realname is $USER
                   2479:   4) Otherwise get logged in user's name
                   2480:   5) If that name has the same uid as the real uid realname is loginname
                   2481:   6) Otherwise, get a name for ruid from /etc/passwd
                   2482: */
                   2483: static char *
                   2484: whoami () {
                   2485: #ifdef DTILDE
                   2486:     static char realname[256];         /* user's name */
                   2487:     static int ruid = -1;              /* user's real uid */
                   2488:     char loginname[256], envname[256]; /* temp storage */
                   2489:     char *c;
                   2490:     struct passwd *p;
                   2491:     _PROTOTYP(extern char * getlogin, (void) );
                   2492: 
                   2493:     if (ruid != -1)
                   2494:       return(realname);
                   2495: 
                   2496:     ruid = real_uid();                 /* get our uid */
                   2497: 
                   2498:   /* how about $USER or $LOGNAME? */
                   2499:     if ((c = getenv(NAMEENV)) != NULL) { /* check the env variable */
                   2500:        strcpy (envname, c);
                   2501:        p = getpwnam(envname);
                   2502:        if (p->pw_uid == ruid) {        /* get passwd entry for envname */
                   2503:            strcpy (realname, envname); /* if the uid's are the same */
                   2504:            return(realname);
                   2505:        }
                   2506:     }
                   2507: 
                   2508:   /* can we use loginname() ? */
                   2509: 
                   2510:     if ((c =  getlogin()) != NULL) {   /* name from utmp file */
                   2511:        strcpy (loginname, c);
                   2512:        if ((p = getpwnam(loginname)) != NULL) /* get passwd entry */
                   2513:          if (p->pw_uid == ruid) {      /* for loginname */
                   2514:              strcpy (realname, loginname); /* if the uid's are the same */
                   2515:              return(realname);
                   2516:          }
                   2517:     }
                   2518: 
                   2519:   /* Use first name we get for ruid */
                   2520: 
                   2521:     if ((p = getpwuid(ruid)) == NULL) { /* name for uid */
                   2522:        realname[0] = '\0';             /* no user name */
                   2523:        ruid = -1;
                   2524:        return(NULL);
                   2525:     }
                   2526:     strcpy (realname, p->pw_name);
                   2527:     return(realname);
                   2528: #else
                   2529:     return(NULL);
                   2530: #endif /* DTILDE */
                   2531: }
                   2532: 
                   2533: /*  T I L D E _ E X P A N D  --  expand ~user to the user's home directory. */
                   2534: 
                   2535: char *
                   2536: tilde_expand(dirname) char *dirname; {
                   2537: #define BUFLEN 256
                   2538: #ifdef DTILDE
                   2539:     struct passwd *user;
                   2540:     static char olddir[BUFLEN];
                   2541:     static char oldrealdir[BUFLEN];
                   2542:     static char temp[BUFLEN];
                   2543:     int i, j;
                   2544: 
                   2545:     debug(F111,"tilde_expand",dirname,dirname[0]);
                   2546: 
                   2547:     if (dirname[0] != '~')             /* Not a tilde...return param */
                   2548:       return(dirname);
                   2549:     if (!strcmp(olddir,dirname)) {     /* Same as last time */
                   2550:       return(oldrealdir);              /* so return old answer. */
                   2551:     } else {
                   2552:        j = (int)strlen(dirname);
                   2553:        for (i = 0; i < j; i++)         /* find username part of string */
                   2554:          if (!ISDIRSEP(dirname[i]))
                   2555:            temp[i] = dirname[i];
                   2556:          else break;
                   2557:        temp[i] = '\0';                 /* tie off with a NULL */
                   2558:        if (i == 1) {                   /* if just a "~" */
                   2559:            user = getpwnam(whoami());  /*  get info on current user */
                   2560:        } else {
                   2561:            user = getpwnam(&temp[1]);  /* otherwise on the specified user */
                   2562:        }
                   2563:     }
                   2564:     if (user != NULL) {                        /* valid user? */
                   2565:        strcpy(olddir, dirname);        /* remember the directory */
                   2566:        strcpy(oldrealdir,user->pw_dir); /* and their home directory */
                   2567:        strcat(oldrealdir,&dirname[i]);
                   2568:        return(oldrealdir);
                   2569:     } else {                           /* invalid? */
                   2570:        strcpy(olddir, dirname);        /* remember for next time */
                   2571:        strcpy(oldrealdir, dirname);
                   2572:        return(oldrealdir);
                   2573:     }
                   2574: #else
                   2575:     return(NULL);
                   2576: #endif /* DTILDE */
                   2577: }
                   2578: 
                   2579: /*
                   2580:   Functions for executing system commands.
                   2581:   zsyscmd() executes the system command in the normal, default way for
                   2582:   the system.  In UNIX, it does what system() does.  Thus, its results
                   2583:   are always predictable.
                   2584:   zshcmd() executes the command using the user's preferred shell.
                   2585: */
                   2586: int
                   2587: zsyscmd(s) char *s; {
                   2588: #ifdef OS2
                   2589:     if (!priv_chk()) system(s);
                   2590: #else
                   2591:     PID_T shpid;
                   2592: #ifdef COMMENT
                   2593: /* This doesn't work... */
                   2594:     WAIT_T status;
                   2595: #else
                   2596:     int status;
                   2597: #endif /* COMMENT */
                   2598: 
                   2599:     if (shpid = fork()) {
                   2600:        if (shpid < (PID_T)0) return(-1); /* Parent */
                   2601:        while (shpid != (PID_T) wait(&status))
                   2602:          ;
                   2603:        return(status);
                   2604:     }
                   2605:     if (priv_can()) {                  /* Child: cancel any priv's */
                   2606:        printf("?Privilege cancellation failure\n");
                   2607:        _exit(255);
                   2608:     }
                   2609:     execl("/bin/sh","sh","-c",s,NULL);
                   2610:     perror("/bin/sh");
                   2611:     _exit(255);
                   2612:     return(0);                         /* Shut up ANSI compilers. */
                   2613: #endif /* OS2 */
                   2614: }
                   2615: 
                   2616: /*
                   2617:   UNIX code by H. Fischer; copyright rights assigned to Columbia Univ.
                   2618:   Adapted to use getpwuid to find login shell because many systems do not
                   2619:   have SHELL in environment, and to use direct calling of shell rather
                   2620:   than intermediate system() call. -- H. Fischer
                   2621:   Call with s pointing to command to execute.
                   2622: */
                   2623: 
                   2624: int
                   2625: zshcmd(s) char *s; {
                   2626:     PID_T pid;
                   2627: 
                   2628: #ifdef OS2
                   2629:     char *shell = getenv("COMSPEC");
                   2630:     if (!priv_chk())
                   2631:       if (*s == '\0')
                   2632:         spawnl(P_WAIT, shell, shell, NULL);
                   2633:       else
                   2634:         system(s);
                   2635: #else
                   2636: #ifdef AMIGA
                   2637:     if (!priv_chk()) system(s);
                   2638: #else
                   2639: #ifdef datageneral
                   2640:     if (priv_chk) return(1);
                   2641:     if (*s == '\0')                    /* Interactive shell requested? */
                   2642: #ifdef mvux
                   2643:        system("/bin/sh ");
                   2644: #else
                   2645:         system("x :cli prefix Kermit_Baby:");
                   2646: #endif /* mvux */
                   2647:     else                               /* Otherwise, */
                   2648:         system(s);                     /* Best for aos/vs?? */
                   2649: 
                   2650: #else
                   2651: #ifdef aegis
                   2652:     if ((pid = vfork()) == 0) {                /* Make child quickly */
                   2653:        char *shpath, *shname, *shptr;  /* For finding desired shell */
                   2654: 
                   2655:        if (priv_can()) exit(1);        /* Turn off privs. */
                   2656:         if ((shpath = getenv("SHELL")) == NULL) shpath = "/com/sh";
                   2657: 
                   2658: #else                                  /* All Unix systems */
                   2659:     if ((pid = fork()) == 0) {         /* Make child */
                   2660:        char *shpath, *shname, *shptr;  /* For finding desired shell */
                   2661:        struct passwd *p;
                   2662:        char *defshell = "/bin/sh";     /* Default */
                   2663: 
                   2664:        if (priv_can()) exit(1);        /* Turn off privs. */
                   2665:        p = getpwuid(real_uid());       /* Get login data */
                   2666:        if (p == (struct passwd *) NULL || !*(p->pw_shell))
                   2667:          shpath = defshell;
                   2668:        else
                   2669:          shpath = p->pw_shell;
                   2670: #endif /* aegis */
                   2671:        shptr = shname = shpath;
                   2672:        while (*shptr != '\0')
                   2673:          if (*shptr++ == DIRSEP)
                   2674:            shname = shptr;
                   2675:        if (s == NULL || *s == '\0') {  /* Interactive shell requested? */
                   2676:            execl(shpath,shname,"-i",NULL); /* Yes, do that */
                   2677:        } else {                        /* Otherwise, */
                   2678:            execl(shpath,shname,"-c",s,NULL); /* exec the given command */
                   2679:        }                               /* If execl() failed, */
                   2680:        exit(BAD_EXIT);                 /* return bad return code. */
                   2681: 
                   2682:     } else {                           /* Parent */
                   2683: 
                   2684:        int wstat;                      /* ... must wait for child */
                   2685:        SIGTYP (*istat)(), (*qstat)();
                   2686: 
                   2687:        if (pid == -1) return(0);       /* fork() failed? */
                   2688: 
                   2689:        istat = signal(SIGINT,SIG_IGN); /* Let the fork handle keyboard */
                   2690:        qstat = signal(SIGQUIT,SIG_IGN); /* interrupts itself... */
                   2691: 
                   2692:        while (((wstat = wait((WAIT_T *)0)) != pid) && (wstat != -1))
                   2693:          ;                             /* Wait for fork */
                   2694:        signal(SIGINT,istat);           /* Restore interrupts */
                   2695:        signal(SIGQUIT,qstat);
                   2696:     }
                   2697: #endif
                   2698: #endif
                   2699: #endif
                   2700:     return(1);
                   2701: }
                   2702: 
                   2703: #ifdef aegis
                   2704: /*
                   2705:  Replacement for strchr() and index(), neither of which seem to be universal.
                   2706: */
                   2707: 
                   2708: static char *
                   2709: #ifdef CK_ANSIC
                   2710: xindex(char * s, char c)
                   2711: #else
                   2712: xindex(s,c) char *s, c;
                   2713: #endif /* CK_ANSIC */
                   2714: /* xindex */ {
                   2715:     while (*s != '\0' && *s != c) s++;
                   2716:     if (*s == c) return(s); else return(NULL);
                   2717: }
                   2718: #endif /* aegis */
                   2719: 
                   2720: /*  I S W I L D  --  Check if filespec is "wild"  */
                   2721: 
                   2722: /*
                   2723:   Returns 0 if it is a single file, 1 if it contains wildcard characters.
                   2724:   Note: must match the algorithm used by match(), hence no [a-z], etc.
                   2725: */
                   2726: int
                   2727: iswild(filespec) char *filespec; {
                   2728:     char c; int x; char *p;
                   2729:     if (wildxpand) {
                   2730:        if ((x = zxpand(filespec)) > 1) return(1);
                   2731:        p = malloc(MAXNAMLEN + 20);
                   2732:        znext(p);
                   2733:        x = (strcmp(filespec,p) != 0);
                   2734:        free(p);
                   2735:        return(x);
                   2736:     } else {
                   2737:        while ((c = *filespec++) != '\0')
                   2738:          if (c == '*' || c == '?') return(1);
                   2739:        return(0);
                   2740:     }
                   2741: }
                   2742: 
                   2743: #ifdef OS2
                   2744: 
                   2745: /*  Z C H D S K  --  Change currently selected disk device */
                   2746: 
                   2747: /* Returns -1 if error, otherwise 0 */
                   2748: 
                   2749: zchdsk(c) char c; {
                   2750:     int i = toupper(c) - 64;
                   2751:     return( _chdrive(i) ? 0 : -1 );
                   2752: }
                   2753: 
                   2754: #undef stat
                   2755: 
                   2756: os2stat(char *path, struct stat *st) {
                   2757:     char local[MAXPATHLEN];
                   2758:     int len;
                   2759: 
                   2760:     strcpy(local, path);
                   2761:     len = strlen(local);
                   2762: 
                   2763:     if ( len == 2 && local[1] == ':' )
                   2764:         local[2] = DIRSEP, local[3] = 0; /* if drive only, append / */
                   2765:     else if ( len == 0 )
                   2766:         local[0] = DIRSEP, local[1] = 0; /* if empty path, take / instead */
                   2767:     else if ( len > 1 && ISDIRSEP(local[len - 1]) && local[len - 2] != ':' )
                   2768:         local[len - 1] = 0; /* strip trailing / except after d: */
                   2769: 
                   2770:     return stat(local, st);
                   2771: }
                   2772: 
                   2773: #endif /* OS2 */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.