source: trunk/third/kermit/ckufio.c @ 10780

Revision 10780, 134.3 KB checked in by brlewis, 27 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r10779, which included commits to RCS files with non-trunk default branches.
Line 
1#ifdef OS2
2#ifdef NT
3char *ckzv = "Win32 File support, 6.0.115 19 Nov 96";
4#else /* NT */
5char *ckzv = "OS/2 File support, 6.0.115 19 Nov 96";
6#endif /* NT */
7#else
8#ifdef aegis
9char *ckzv = "Aegis File support, 6.0.115 6 Sep 96";
10#else
11#ifdef Plan9
12char *ckzv = "Plan 9 File support, 6.0.115 6 Sep 96";
13#else
14char *ckzv = "UNIX File support, 6.0.115 6 Sep 96";
15#endif /* Plan9 */
16#endif /* aegis */
17#endif /* OS2 */
18
19/* C K U F I O  --  Kermit file system support for UNIX, OS/2, and Aegis */
20
21
22/*
23  NOTE TO CONTRIBUTORS: This file, and all the other C-Kermit files, must be
24  compatible with C preprocessors that support only #ifdef, #else, #endif,
25  #define, and #undef.  Please do not use #if, logical operators, or other
26  preprocessor features in any of the portable C-Kermit modules.  You can,
27  of course, use these constructions in system-specific modules when you they
28  are supported.
29*/
30
31/*
32  Author: Frank da Cruz (fdc@columbia.edu),
33  Columbia University Academic Information Systems, New York City,
34  and others noted below.
35
36  Copyright (C) 1985, 1996, Trustees of Columbia University in the City of New
37  York.  All rights reserved.
38*/
39
40/* Include Files */
41
42#include "ckcsym.h"
43#include "ckcdeb.h"
44#include "ckcasc.h"
45#ifdef OS2
46#include "ckuusr.h"
47#include <stdio.h>
48_PROTOTYP( int os2settitle, (char *, int) );
49extern int priority;
50#ifdef NT
51#define timezone _timezone
52#define write _write
53#define fileno _fileno
54#ifndef SEM_INDEFINITE_WAIT
55#define SEM_INDEFINITE_WAIT INFINITE
56#endif /* SEM_INDEFINITE_WAIT */
57#endif /* NT */
58#endif /* OS2 */
59#include <signal.h>
60
61#ifdef MINIX
62#include <limits.h>
63#else
64#ifdef POSIX
65#include <limits.h>
66#else
67#ifdef SVR3
68#include <limits.h>
69#endif /* SVR3 */
70#endif /* POSIX */
71#endif /* MINIX */
72
73/* Directory structure header file */
74
75#ifdef OS2
76/*
77
78  C-Kermit's OS/2 support originally by Chris Adie <C.Adie@uk.ac.edinburgh>
79  Edinburgh University Computing Service, Scotland, for C-Kermit 4F.  Adapted
80  to C-Kermit 5A and integrated into the UNIX support module by Kai Uwe Rommel
81  <rommel@informatik.tu-muenchen.de>, Muenchen, Germany, December 1991.
82*/
83
84/*
85  Directory Separator macros, to allow this module to work with both UNIX and
86  OS/2: Because of ambiguity with the command line editor escape \ character,
87  the directory separator is currently left as / for OS/2 too, because the
88  OS/2 kernel also accepts / as directory separator.  But this is subject to
89  change in future versions to conform to the normal OS/2 style.
90*/
91#define DIRSEP       '/'
92/* #define DIRSEP       '\\' */
93#define ISDIRSEP(c)  ((c)=='/'||(c)=='\\')
94#else /* not OS2 */
95#define DIRSEP       '/'
96#define ISDIRSEP(c)  ((c)=='/')
97#endif /* OS2 */
98
99#ifdef SDIRENT
100#define DIRENT
101#endif /* SDIRENT */
102
103#ifdef XNDIR
104#include <sys/ndir.h>
105#else /* !XNDIR */
106#ifdef NDIR
107#include <ndir.h>
108#else /* !NDIR, !XNDIR */
109#ifdef RTU
110#include "/usr/lib/ndir.h"
111#else /* !RTU, !NDIR, !XNDIR */
112#ifdef DIRENT
113#ifdef SDIRENT
114#include <sys/dirent.h>
115#else
116#include <dirent.h>
117#endif /* SDIRENT */
118#else
119#ifdef OS2
120#define OPENDIR
121#define DIRENT
122#include "ckodir.h"
123#else/* !RTU, !NDIR, !XNDIR, !DIRENT, !OS2, i.e. all others */
124#include <sys/dir.h>
125#endif /* OS2 */
126#endif /* DIRENT */
127#endif /* RTU */
128#endif /* NDIR */
129#endif /* XNDIR */
130
131#ifdef OS2                              /* OS/2 file system interface */
132#define BSD4                            /* is like Berkeley UNIX */
133#define NOFILEH                         /* with no <file.h> */
134#include <sys/utime.h>
135#include <stdlib.h>
136#include <process.h>
137#include <share.h>
138extern int binary;                      /* We need to know this for open() */
139extern int fncact;                      /* Need this for zchkspa()         */
140extern int tlevel, cmdlvl ;             /* Need this for external commands */
141#ifdef __IBMC__
142extern FILE *popen(char *, char *);
143extern int pclose(FILE *);
144#else
145#ifndef __EMX__
146#define popen    _popen
147#define pclose   _pclose
148#define fopen(n, m)  _fsopen(n, m, SH_DENYWR)
149#endif /* __EMX__ */
150#endif /* __IBMC__ */
151#ifdef NT
152#include <io.h>
153#endif /* NT */
154#else /* OS2 */
155#include <pwd.h>                        /* Password file for shell name */
156#endif /* OS2 */
157
158#ifdef POSIX
159#define UTIMEH
160#endif /* POSIX */
161
162#ifndef OS2
163#ifdef SYSUTIMEH                        /* <sys/utime.h> if requested,  */
164#include <sys/utime.h>                  /* for extra fields required by */
165#else                                   /* 88Open spec. */
166#ifdef UTIMEH                           /* or <utime.h> if requested */
167#include <utime.h>                      /* (SVR4, POSIX) */
168#define SYSUTIMEH                       /* Use this for both cases. */
169#endif /* UTIMEH */
170#endif /* SYSUTIMEH */
171#endif /* OS2 */
172
173#ifdef POSIX
174#define TIMESTAMP
175#endif /* POSIX */
176
177#ifdef BSD44                            /* BSD 4.4 */
178#ifndef TIMESTAMP
179#define TIMESTAMP                       /* Can do file dates */
180#endif /* TIMESTAMP */
181#include <sys/time.h>
182#include <sys/timeb.h>
183
184#else  /* Not BSD44 */
185
186#ifdef BSD4                             /* BSD 4.3 and below */
187#define TIMESTAMP                       /* Can do file dates */
188#include <time.h>                       /* Need this */
189#include <sys/timeb.h>                  /* Need this if really BSD */
190
191#else  /* Not BSD 4.3 and below */
192
193#ifdef SVORPOSIX                        /* System V or POSIX */
194#ifndef TIMESTAMP
195#define TIMESTAMP
196#endif /* TIMESTAMP */
197#include <time.h>
198
199/* void tzset(); (the "void" type upsets some compilers) */
200#ifndef IRIX60
201#ifndef ultrix
202#ifndef CONVEX9
203/* ConvexOS 9.0, supposedly POSIX, has extern char *timezone(int,int) */
204#ifndef Plan9
205extern long timezone;
206#endif /* Plan9 */
207#endif /* CONVEX9 */
208#endif /* ultrix */
209#endif /* IRIX60 */
210#endif /* SVORPOSIX */
211#endif /* BSD4 */
212#endif /* BSD44 */
213
214/* Is `y' a leap year? */
215#define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
216
217/* Number of leap years from 1970 to `y' (not including `y' itself). */
218#define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
219
220#ifdef CIE
221#include <stat.h>                       /* File status */
222#else
223#include <sys/stat.h>
224
225#ifdef OS2
226#include <sys/types.h>
227
228/* Because standard stat has trouble with trailing /'s we have to wrap it */
229
230#ifdef NT
231int os2stat(char *, struct _stat *);
232#else /* NT */
233int os2stat(char *, struct stat *);
234#endif /* NT */
235
236#ifdef __IBMC__
237#ifdef system
238#undef system
239#endif
240#ifdef stat
241#undef stat
242#define __IBMC__STAT
243#endif
244#endif
245#define stat(path, buf) os2stat(path, buf)
246#endif /* OS2 */
247#endif /* CIE */
248
249/*
250  Functions (n is one of the predefined file numbers from ckcker.h):
251
252   zopeni(n,name)   -- Opens an existing file for input.
253   zopeno(n,name,attr,fcb) -- Opens a new file for output.
254   zclose(n)        -- Closes a file.
255   zchin(n,&c)      -- Gets the next character from an input file.
256   zsinl(n,&s,x)    -- Read a line from file n, max len x, into address s.
257   zsout(n,s)       -- Write a null-terminated string to output file, buffered.
258   zsoutl(n,s)      -- Like zsout, but appends a line terminator.
259   zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
260   zchout(n,c)      -- Add a character to an output file, unbuffered.
261   zchki(name)      -- Check if named file exists and is readable, return size.
262   zchko(name)      -- Check if named file can be created.
263   zchkspa(name,n)  -- Check if n bytes available to create new file, name.
264   znewn(name,s)    -- Make a new unique file name based on the given name.
265   zdelet(name)     -- Delete the named file.
266   zxpand(string)   -- Expands the given wildcard string into a list of files.
267   znext(string)    -- Returns the next file from the list in "string".
268   zxcmd(n,cmd)     -- Execute the command in a lower fork on file number n.
269   zclosf()         -- Close input file associated with zxcmd()'s lower fork.
270   zrtol(n1,n2)     -- Convert remote filename into local form.
271   zltor(n1,n2)     -- Convert local filename into remote form.
272   zchdir(dirnam)   -- Change working directory.
273   zhome()          -- Return pointer to home directory name string.
274   zkself()         -- Kill self, log out own job.
275   zsattr(struct zattr *) -- Return attributes for file which is being sent.
276   zstime(f, struct zattr *, x) - Set file creation date from attribute packet.
277   zrename(old, new) -- Rename a file.
278   zcopy(source,destination) -- Copy a file.
279   zmkdir(path)       -- Create the directory path if possible 
280   zfnqfp(fname,len,fullpath) - Determine full path for file name.
281*/
282
283/* Kermit-specific includes */
284/*
285  Definitions here supersede those from system include files.
286  ckcdeb.h is included above.
287*/
288#include "ckcker.h"                     /* Kermit definitions */
289#include "ckucmd.h"                     /* For sys-dependent keyword tables */
290#include "ckuver.h"                     /* Version herald */
291
292char *ckzsys = HERALD;
293
294/*
295  File access checking ...  There are two calls to access() in this module.
296  If this program is installed setuid or setgid on a Berkeley-based UNIX
297  system that does NOT incorporate the saved-original-effective-uid/gid
298  feature, then, when we have swapped the effective and original uid/gid,
299  access() fails because it uses what it thinks are the REAL ids, but we have
300  swapped them.  This occurs on systems where ANYBSD is defined, NOSETREU
301  is NOT defined, and SAVEDUID is NOT defined.  So, in theory, we should take
302  care of this situation like so:
303
304    ifdef ANYBSD
305    ifndef NOSETREU
306    ifndef SAVEDUID
307    define SW_ACC_ID
308    endif
309    endif
310    endif
311
312  But we can't test such a general scheme everywhere, so let's only do this
313  when we know we have to...
314*/
315#ifdef NEXT                             /* NeXTSTEP 1.0-3.0 */
316#define SW_ACC_ID
317#endif /* NEXT */
318
319/* Support for tilde-expansion in file and directory names */
320
321#ifdef POSIX
322#define NAMEENV "LOGNAME"
323#endif /* POSIX */
324
325#ifdef BSD4
326#define NAMEENV "USER"
327#endif /* BSD4 */
328
329#ifdef ATTSV
330#define NAMEENV "LOGNAME"
331#endif /* ATTSV */
332
333/* Berkeley Unix Version 4.x */
334/* 4.1bsd support from Charles E Brooks, EDN-VAX */
335
336#ifdef BSD4
337#ifdef MAXNAMLEN
338#define BSD42
339#endif /* MAXNAMLEN */
340#endif /* BSD4 */
341
342/* Definitions of some system commands */
343
344#ifdef OS2
345char *CPYCMD = "copy ";         /* For file copy */
346char *DELCMD = "del ";                  /* For file deletion */
347char *RENCMD = "rename ";       /* For file rename */
348char *PWDCMD = "chdir ";                /* For saying where I am */
349char *TYPCMD = "type ";                 /* For typing a file */
350char *DIRCMD = "dir ";                  /* For directory listing */
351char *DIRCM2 = "dir ";                  /* For directory listing, no args */
352#ifdef NT
353char *WHOCMD = "@echo  Kermit 95 Server"; /* Who's there? */
354#else /* NT */
355char *WHOCMD = "@echo  K/2 Server"; /* Who's there? */
356#endif /* NT */
357char *SPACMD = "dir | find \"bytes free\""; /* For space on disk */
358char *SPACM2 = "dir | find \"bytes free\""; /* For space on disk */
359
360#else /* Not OS2, ergo UNIX (or Aegis) */
361
362char *DELCMD = "rm -f ";                /* For file deletion */
363char *CPYCMD = "cp ";           /* For file copy */
364char *RENCMD = "mv ";           /* For file rename */
365char *PWDCMD = "pwd ";                  /* For saying where I am */
366
367#ifdef COMMENT
368#ifdef HPUX10
369char *DIRCMD = "/usr/bin/ls -l ";       /* For directory listing */
370char *DIRCM2 = "/usr/bin/ls -l ";       /* For directory listing, no args */
371#else
372char *DIRCMD = "/bin/ls -l ";           /* For directory listing */
373char *DIRCM2 = "/bin/ls -l ";           /* For directory listing, no args */
374#endif /* HPUX10 */
375#else
376char *DIRCMD = "ls -l ";                /* For directory listing */
377char *DIRCM2 = "ls -l ";                /* For directory listing, no args */
378#endif /* COMMENT */
379
380char *TYPCMD = "cat ";                  /* For typing a file */
381
382#ifdef FT18                             /* Fortune For:Pro 1.8 */
383#undef BSD4
384#endif /* FT18 */
385
386#ifdef BSD4
387char *SPACMD = "pwd ; df .";            /* Space in current directory */
388#else
389#ifdef FT18
390char *SPACMD = "pwd ; du ; df .";
391#else
392char *SPACMD = "df ";
393#endif /* FT18 */
394#endif /* BSD4 */
395
396char *SPACM2 = "df ";                   /* For space in specified directory */
397
398#ifdef FT18
399#define BSD4
400#endif /* FT18 */
401
402#ifdef BSD4
403char *WHOCMD = "finger ";
404#else
405char *WHOCMD = "who ";
406#endif /* BSD4 */
407
408#endif /* OS2 */
409
410#ifdef DTILDE                           /* For tilde expansion */
411_PROTOTYP( char * tilde_expand, (char *) );
412#endif /* DTILDE */
413
414/* More system-dependent includes, which depend on symbols defined */
415/* in the Kermit-specific includes.  Oh what a tangled web we weave... */
416
417#ifdef COHERENT                         /* <sys/file.h> */
418#define NOFILEH
419#endif /* COHERENT */
420
421#ifdef MINIX
422#define NOFILEH
423#endif /* MINIX */
424
425#ifdef aegis
426#define NOFILEH
427#endif /* aegis */
428
429#ifdef unos
430#define NOFILEH
431#endif /* unos */
432
433#ifndef NOFILEH
434#include <sys/file.h>
435#endif /* NOFILEH */
436
437#ifndef is68k                           /* Whether to include <fcntl.h> */
438#ifndef BSD41                           /* All but a couple UNIXes have it. */
439#ifndef FT18
440#ifndef COHERENT
441#include <fcntl.h>
442#endif /* COHERENT */
443#endif /* FT18  */
444#endif /* BSD41 */
445#endif /* not is68k */
446
447#ifdef COHERENT
448#ifdef _I386
449#include <fcntl.h>
450#else
451#include <sys/fcntl.h>
452#endif /* _I386 */
453#endif /* COHERENT */
454
455/*
456  Change argument to "(const char *)" if this causes trouble.
457  Or... if it causes trouble, then maybe it was already declared
458  in a header file after all, so you can remove this prototype.
459*/
460#ifndef NDGPWNAM /* If not defined No Declare getpwnam... */
461#ifndef _POSIX_SOURCE
462#ifndef NEXT
463#ifndef SVR4
464/* POSIX <pwd.h> already gave prototypes for these. */
465#ifdef IRIX40
466_PROTOTYP( struct passwd * getpwnam, (const char *) );
467#else
468#ifdef IRIX51
469_PROTOTYP( struct passwd * getpwnam, (const char *) );
470#else
471#ifdef M_UNIX
472_PROTOTYP( struct passwd * getpwnam, (const char *) );
473#else
474#ifdef HPUX9
475_PROTOTYP( struct passwd * getpwnam, (const char *) );
476#else
477#ifdef HPUX10
478_PROTOTYP( struct passwd * getpwnam, (const char *) );
479#else
480#ifdef DCGPWNAM
481_PROTOTYP( struct passwd * getpwnam, (const char *) );
482#else
483_PROTOTYP( struct passwd * getpwnam, (char *) );
484#endif /* DCGPWNAM */
485#endif /* HPUX10 */
486#endif /* HPUX9 */
487#endif /* M_UNIX */
488#endif /* IRIX51 */
489#endif /* IRIX40 */
490#ifndef SUNOS4
491#ifndef HPUX9
492#ifndef HPUX10
493#ifndef _SCO_DS
494_PROTOTYP( struct passwd * getpwuid, (PWID_T) );
495#endif /* _SCO_DS */
496#endif /* HPUX10 */
497#endif /* HPUX9 */
498#endif /* SUNOS4 */
499_PROTOTYP( struct passwd * getpwent, (void) );
500#endif /* SVR4 */
501#endif /* NEXT */
502#endif /* _POSIX_SOURCE */
503#endif /* NDGPWNAM */
504
505/* Define macros for getting file type */
506
507#ifdef OXOS
508/*
509  Olivetti X/OS 2.3 has S_ISREG and S_ISDIR defined
510  incorrectly, so we force their redefinition.
511*/
512#undef S_ISREG
513#undef S_ISDIR
514#endif /* OXOS */
515
516#ifdef UTSV                             /* Same deal for Amdahl UTSV */
517#undef S_ISREG
518#undef S_ISDIR
519#endif /* UTSV */
520
521#ifdef UNISYS52                         /* And for UNISYS UTS V 5.2 */
522#undef S_ISREG
523#undef S_ISDIR
524#endif /* UNISYS52 */
525
526#ifdef ICLSVR3                          /* And for old ICL versions */
527#undef S_ISREG
528#undef S_ISDIR
529#endif /* ICLSVR3 */
530
531#ifdef ISDIRBUG                         /* Also allow this from command line */
532#ifdef S_ISREG
533#undef S_ISREG
534#endif /* S_ISREG */
535#ifdef S_ISDIR
536#undef S_ISDIR
537#endif /*  S_ISDIR */
538#endif /* ISDIRBUG */
539
540#ifndef S_ISREG
541#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
542#endif /* S_ISREG */
543#ifndef S_ISDIR
544#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
545#endif /* S_ISDIR */
546
547/* Define maximum length for a file name if not already defined */
548
549#ifdef QNX
550#ifdef _MAX_FNAME
551#define MAXNAMLEN _MAX_FNAME
552#else
553#define MAXNAMLEN 48
554#endif /* _MAX_FNAME */
555#else
556#ifndef MAXNAMLEN
557#ifdef sun
558#define MAXNAMLEN 255
559#else
560#ifdef FILENAME_MAX
561#define MAXNAMLEN FILENAME_MAX
562#else
563#ifdef NAME_MAX
564#define MAXNAMLEN NAME_MAX
565#else
566#ifdef _POSIX_NAME_MAX
567#define MAXNAMLEN _POSIX_NAME_MAX
568#else
569#ifdef _D_NAME_MAX
570#define MAXNAMLEN _D_NAME_MAX
571#else
572#ifdef DIRSIZ
573#define MAXNAMLEN DIRSIZ
574#else
575#define MAXNAMLEN 14
576#endif /* DIRSIZ */
577#endif /* _D_NAME_MAX */
578#endif /* _POSIX_NAME_MAX */
579#endif /* NAME_MAX */
580#endif /* FILENAME_MAX */
581#endif /* sun */
582#endif /* MAXNAMLEN */
583#endif /* QNX */
584
585/* Longest pathname ... */
586/*
587  Beware: MAXPATHLEN is one of UNIX's dirty little secrets.  Where is it
588  defined?  Who knows...  <param.h>, <mod.h>, <unistd.h>, <limits.h>, ...
589  There is not necessarily even a definition for it anywhere, or it might have
590  another name.  If you get it wrong, bad things happen with getcwd() and/or
591  getwd().  If you allocate a buffer that is too short, getwd() might write
592  over memory and getcwd() will fail with ERANGE.  The definitions of these
593  functions (e.g. in SVID or POSIX.1) do not tell you how to determine the
594  maximum path length in order to allocate a buffer that is the right size.
595*/
596#ifdef BSD44
597#include <sys/param.h>                  /* For MAXPATHLEN */
598#endif /* BSD44 */
599
600#ifdef COHERENT
601#include <sys/param.h>  /* for MAXPATHLEN, needed for -DDIRENT */
602#endif /* COHERENT */
603
604#ifdef MAXPATHLEN
605#ifdef MAXPATH
606#undef MAXPATH
607#endif /* MAXPATH */
608#define MAXPATH MAXPATHLEN
609#else
610#ifdef PATH_MAX
611#define MAXPATH PATH_MAX
612#else
613#ifdef _POSIX_PATH_MAX
614#define MAXPATH _POSIX_PATH_MAX
615#else
616#ifdef BSD42
617#define MAXPATH 1024
618#else
619#ifdef SVR4 
620#define MAXPATH 1024
621#else
622#define MAXPATH 255
623#endif /* SVR4 */
624#endif /* BSD42 */
625#endif /* _POSIX_PATH_MAX */
626#endif /* PATH_MAX */
627#endif /* MAXPATHLEN */
628
629/* Maximum number of filenames for wildcard expansion */
630
631#ifdef PROVX1
632#define MAXWLD 50
633#else
634#ifdef pdp11
635#define MAXWLD 50
636#else
637#ifdef BIGBUFOK
638#define MAXWLD 4096
639#else
640#define MAXWLD 1000
641#endif /* BIGBUFOK */
642#endif /* pdp11 */
643#endif /* PROVX1 */
644
645/* More internal function prototypes */
646/*
647 * The path structure is used to represent the name to match.
648 * Each slash-separated segment of the name is kept in one
649 * such structure, and they are linked together, to make
650 * traversing the name easier.
651 */
652struct path {
653    char npart[MAXNAMLEN+4];            /* name part of path segment */
654    struct path *fwd;                   /* forward ptr */
655};
656#ifndef NOPUSH
657_PROTOTYP( int shxpand, (char *, char *[], int ) );
658extern int nopush;
659#endif /* NOPUSH */
660_PROTOTYP( static int fgen, (char *, char *[], int ) );
661_PROTOTYP( static VOID traverse, (struct path *, char *, char *) );
662_PROTOTYP( static VOID addresult, (char *) );
663_PROTOTYP( static int match, (char *, char *) );
664_PROTOTYP( static char * whoami, (void) );
665#ifdef aegis
666_PROTOTYP( static char * xindex, (char *, char) );
667#endif /* aegis */
668_PROTOTYP( UID_T real_uid, (void) );
669_PROTOTYP( struct path *splitpath, (char *p) );
670_PROTOTYP( char * zdtstr, (time_t) );
671_PROTOTYP( time_t zstrdt, (char *, int) );
672
673/* Some systems define these symbols in include files, others don't... */
674
675#ifndef R_OK
676#define R_OK 4                          /* For access */
677#endif /* R_OK */
678
679#ifndef W_OK
680#define W_OK 2
681#endif /* W_OK */
682
683#ifndef O_RDONLY
684#define O_RDONLY 000
685#endif /* O_RDONLY */
686
687/* Declarations */
688
689int maxnam = MAXNAMLEN;                 /* Available to the outside */
690int maxpath = MAXPATH;
691int ck_znewn = -1;
692
693FILE *fp[ZNFILS] = {                    /* File pointers */
694    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
695#ifdef OS2
696int ispipe[ZNFILS];                     /* Flag for file is a pipe */
697#endif /* OS2 */
698
699/* Buffers and pointers used in buffered file input and output. */
700#ifdef DYNAMIC
701extern char *zinbuffer, *zoutbuffer;
702#else
703extern char zinbuffer[], zoutbuffer[];
704#endif /* DYNAMIC */
705extern char *zinptr, *zoutptr;
706extern int zincnt, zoutcnt;
707extern int wildxpand;
708
709extern UID_T real_uid();
710
711static long iflen = -1L;                /* Input file length */
712
713static PID_T pid = 0;                   /* pid of child fork */
714static int fcount;                      /* Number of files in wild group */
715static char nambuf[MAXNAMLEN+4];        /* Buffer for a filename */
716#ifndef NOFRILLS
717static char zmbuf[200];                 /* For mail, remote print strings */
718#endif /* NOFRILLS */
719
720#ifndef OS2
721/* static */                            /* Not static, must be global now. */
722char **mtchs = NULL;                    /* Matches found for filename */
723char **mtchptr = NULL;                  /* Pointer to current match */
724#endif /* OS2 */
725
726#ifdef OS2
727#ifndef NT
728#define INCL_KBD
729#define INCL_DOSERRORS
730#define INCL_DOSFILEMGR
731#define INCL_DOSPROCESS
732#define INCL_DOSSEMAPHORES
733#include <os2.h>
734typedef unsigned short WORD;
735#undef COMMENT
736
737/* Get/Set All Extended Attributes support */
738
739#define FreeMem(p) DosFreeMem(p)
740
741#define MAX_GEA            500L  /* Max size for a GEA List */
742#define MAXEACOUNT          128  /* Max number of EA's supported */
743#define Ref_ASCIIZ            1  /* Reference type for DosEnumAttribute */
744
745/* Definition of level specifiers, required for File Info */
746
747#define GetInfoLevel1         1            /* Get info from SFT */
748#define GetInfoLevel2         2            /* Get size of FEAlist */
749#define GetInfoLevel3         3            /* Get FEAlist given the GEAlist */
750#define GetInfoLevel4         4            /* Get whole FEAlist */
751#define GetInfoLevel5         5            /* Get FSDname */
752
753#define SetInfoLevel1         1            /* Set info in SFT */
754#define SetInfoLevel2         2            /* Set FEAlist */
755
756FEA2LIST *pFEAList = 0;  /* Pointer to buffer containing all EA information */
757ULONG os2attrs = FILE_NORMAL;
758extern unsigned int lf_opts;
759
760#else /* NT */
761
762#include <windows.h>
763#endif /* NT */
764char os2filename[MAXPATH];
765#endif /* OS2 */
766
767/*  Z K S E L F  --  Kill Self: log out own job, if possible.  */
768
769/* Note, should get current pid, but if your system doesn't have */
770/* getppid(), then just kill(0,9)...  */
771
772#ifndef SVR3
773#ifndef POSIX
774#ifndef OSFPC
775/* Already declared in unistd.h for SVR3 and POSIX */
776#ifdef CK_ANSIC
777extern PID_T getppid(void);
778#else
779#ifndef PS2AIX10
780extern PID_T getppid();
781#endif /* PS2AIX10 */
782#endif /* CK_ANSIC */
783#endif /* OSFPC */
784#endif /* POSIX */
785#endif /* SVR3 */
786
787int
788zkself() {                              /* For "bye", but no guarantee! */
789#ifdef PROVX1
790    return(kill(0,9));
791#else
792#ifdef V7
793    return(kill(0,9));
794#else
795#ifdef TOWER1
796    return(kill(0,9));
797#else
798#ifdef FT18
799    return(kill(0,9));
800#else
801#ifdef aegis
802    return(kill(0,9));
803#else
804#ifdef COHERENT
805    return(kill((PID_T)getpid(),1));
806#else
807#ifdef OS2
808    exit(3);
809    return(0);
810#else
811#ifdef PID_T
812    exit(kill((PID_T)getppid(),1));
813    return(0);
814#else
815    exit(kill(getppid(),1));
816    return(0);
817#endif
818#endif
819#endif
820#endif
821#endif
822#endif
823#endif
824#endif
825}
826
827/*  Z O P E N I  --  Open an existing file for input. */
828
829int
830zopeni(n,name) int n; char *name; {
831    int x, y;
832
833    debug(F111," zopeni",name,n);
834    debug(F101,"  fp","", fp[n]);
835    if (chkfn(n) != 0) return(0);
836    zincnt = 0;                         /* Reset input buffer */
837    if (n == ZSYSFN) {                  /* Input from a system function? */
838/*** Note, this function should not be called with ZSYSFN ***/
839/*** Always call zxcmd() directly, and give it the real file number ***/
840/*** you want to use.  ***/
841        debug(F110,"zopeni called with ZSYSFN, failing!",name,0);
842        *nambuf = '\0';                 /* No filename. */
843        return(0);                      /* fail. */
844#ifdef COMMENT
845        return(zxcmd(n,name));          /* Try to fork the command */
846#endif
847    }
848    if (n == ZSTDIO) {                  /* Standard input? */
849        if (is_a_tty(0)) {
850            fprintf(stderr,"Terminal input not allowed");
851            debug(F110,"zopeni: attempts input from unredirected stdin","",0);
852            return(0);
853        }
854        fp[ZIFILE] = stdin;
855#ifdef OS2
856#ifdef NT
857        _setmode(_fileno(stdin),O_BINARY);
858#else
859        setmode(fileno(stdin),O_BINARY);
860#endif /* NT */
861#endif /* OS2 */
862        return(1);
863    }
864#ifdef OS2
865    if (n == ZIFILE || n == ZRFILE) {
866        strncpy( os2filename, name, MAXPATH ) ;
867        errno = 0;
868#ifdef NT
869        fp[n] = _fsopen(name,"rb",_SH_DENYWR);          /* Binary mode */
870#else
871        fp[n] = fopen(name,"rb");/* Binary mode */
872#endif /* NT */
873        if (fp[ZIFILE]) {
874            x = ferror(fp[ZIFILE]);
875#ifdef ZDEBUG
876            printf("ZOPENI errno=%d\n",errno);
877            printf("ZOPENI ferror=%d\n",x);
878#endif /* ZDEBUG */
879        }
880#ifdef CK_LABELED
881        if (binary == XYFT_L) {
882            os2getattr(name);
883            os2geteas(name);
884        }
885#endif /* CK_LABELED */
886    } else
887#ifdef NT
888        fp[n] = _fsopen(name,"rb",_SH_DENYWR); /* Real file, open it. */
889#else
890        fp[n] = fopen(name,"rb");/* Real file, open it. */
891#endif /* NT */
892#else /* OS2 */
893    fp[n] = fopen(name,"r");            /* Real file, open it. */
894#endif /* OS2 */
895    debug(F111," zopeni", name, fp[n]);
896#ifdef ZDEBUG
897    printf("ZOPENI fp[%d]=%ld\n",n,fp[n]);
898#endif /* ZDEBUG */
899    if (fp[n] == NULL)
900      perror("zopeni");
901    else
902      clearerr(fp[n]);
903    return((fp[n] != NULL) ? 1 : 0);
904}
905
906/*  Z O P E N O  --  Open a new file for output.  */
907
908int
909zopeno(n,name,zz,fcb)
910/* zopeno */  int n; char *name; struct zattr *zz; struct filinfo *fcb; {
911
912    char p[8];                          /* (===OS2 change===) */
913
914/* As of Version 5A, the attribute structure and the file information */
915/* structure are included in the arglist. */
916
917#ifdef DEBUG
918    debug(F111,"zopeno",name,n);
919    if (fcb) {
920        debug(F101,"zopeno fcb disp","",fcb->dsp);
921        debug(F101,"zopeno fcb type","",fcb->typ);
922        debug(F101,"zopeno fcb char","",fcb->cs);
923    } else {
924        debug(F100,"zopeno fcb is NULL","",0);
925    }
926    if (n != ZDFILE)
927      debug(F111," zopeno",name,n);
928#endif /* DEBUG */
929
930    if (chkfn(n) != 0)                  /* Already open? */
931      return(0);                        /* Nothing to do. */
932
933    if ((n == ZCTERM) || (n == ZSTDIO)) {   /* Terminal or standard output */
934        fp[ZOFILE] = stdout;
935#ifdef DEBUG
936        if (n != ZDFILE)
937          debug(F101," fp[]=stdout", "", fp[n]);
938#endif /* DEBUG */
939        zoutcnt = 0;
940        zoutptr = zoutbuffer;
941        return(1);
942    }
943
944/* A real file.  Open it in desired mode (create or append). */
945
946    strcpy(p,"w");                      /* Assume write/create mode */
947    if (fcb) {                          /* If called with an FCB... */
948        if (fcb->dsp == XYFZ_A) {       /* Does it say Append? */
949            strcpy(p,"a");              /* Yes. */
950            debug(F100,"zopeno append","",0);
951        }
952    }
953#ifdef OS2
954    if (n == ZOFILE || n == ZSFILE) {   /* OS/2 binary mode */
955        strncpy( os2filename, name, MAXPATH ) ;
956        strcat(p,"b");
957    }
958#endif /* OS2 */
959    debug(F110,"zopeno fopen arg",p,0);
960    fp[n] = fopen(name,p);              /* Try to open the file */
961
962#ifdef ZDEBUG
963printf("ZOPENO fp[%d]=%ld\n",n,fp[n]);
964#endif /* ZDEBUG */
965
966    if (fp[n] == NULL) {                /* Failed */
967        debug(F101,"zopeno failed errno","",errno);
968#ifdef COMMENT                          /* Let upper levels print message. */
969        perror("Can't open output file");
970#endif /* COMMENT */
971    } else {                            /* Succeeded */
972        if (n == ZDFILE)                /* If it's the debug log */
973          setbuf(fp[n],NULL);           /* make it unbuffered */
974        else
975          debug(F100, "zopeno ok", "", 0);
976    }
977    zoutcnt = 0;                        /* (PWP) reset output buffer */
978    zoutptr = zoutbuffer;
979    return((fp[n] != NULL) ? 1 : 0);
980}
981
982/*  Z C L O S E  --  Close the given file.  */
983
984/*  Returns 0 if arg out of range, 1 if successful, -1 if close failed.  */
985
986int
987zclose(n) int n; {
988    int x, x2;
989    if (chkfn(n) < 1) return(0);        /* Check range of n */
990
991    if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */
992      x2 = zoutdump();
993    else
994      x2 = 0;
995
996    x = 0;                              /* Initialize return code */
997    if (fp[ZSYSFN]
998#ifdef OS2
999         || ispipe[n]                   /* If file is really pipe */
1000#endif /* OS2 */
1001        ) {                     
1002        x = zclosf(n);                  /* do it specially */
1003    } else {
1004        if ((fp[n] != stdout) && (fp[n] != stdin))
1005          x = fclose(fp[n]);
1006        fp[n] = NULL;
1007#ifdef OS2
1008#ifdef CK_LABELED
1009        if (binary == XYFT_L) {
1010            debug(F111,"zclose LABELED","file number",n);
1011            if (n == ZOFILE) {
1012                debug(F111,"zclose LABELED",
1013                      "lf_opts && LBL_EXT",
1014                      lf_opts && LBL_EXT
1015                      );
1016                if (lf_opts && LBL_EXT)
1017                  os2seteas(os2filename);     
1018                os2setattr(os2filename);
1019            } else if (n == ZIFILE && pFEAList) {
1020                FreeMem(pFEAList);
1021                pFEAList = 0;
1022            }
1023        }
1024#endif /* CK_LABELED */
1025#endif /* OS2 */
1026    }
1027    iflen = -1L;                        /* Invalidate file length */
1028    if (x == EOF) {                     /* if we got a close error */
1029        debug(F101,"zclose fclose fails","",x);
1030        return(-1);
1031    } else if (x2 < 0) {                /* or error flushing last buffer */
1032        debug(F101,"zclose error flushing last buffer","",x2);
1033        return(-1);             /* then return an error */
1034    } else {
1035        debug(F101,"zclose success","",1);
1036        return(1);
1037    }
1038}
1039
1040/*  Z C H I N  --  Get a character from the input file.  */
1041
1042/*  Returns -1 if EOF, 0 otherwise with character returned in argument  */
1043
1044int
1045zchin(n,c) int n; int *c; {
1046    int a, x;
1047
1048    /* (PWP) Just in case this gets called when it shouldn't. */
1049    if (n == ZIFILE) {
1050        x = zminchar();
1051        *c = x;
1052        return(x);
1053    }
1054    /* if (chkfn(n) < 1) return(-1); */
1055    a = getc(fp[n]);
1056    if (a == EOF) return(-1);
1057#ifdef OS2
1058    if (!binary && a == 0x1A)           /* Ctrl-Z marks EOF for text mode*/
1059      return(-1);
1060#endif
1061    *c = (CHAR) a & 0377;
1062    return(0);
1063}
1064
1065/*  Z S I N L  --  Read a line from a file  */
1066
1067/*
1068  Writes the line into the address provided by the caller.
1069  n is the Kermit "channel number".
1070  Writing terminates when newline is encountered, newline is not copied.
1071  Writing also terminates upon EOF or if length x is exhausted.
1072  Returns 0 on success, -1 on EOF or error.
1073*/
1074int
1075zsinl(n,s,x) int n, x; char *s; {
1076    int a, z = 0;                       /* z is return code. */
1077    extern CHAR feol;                   /* Line terminator */
1078
1079    if (chkfn(n) < 1) {                 /* Make sure file is open */
1080        return(-1);
1081    }
1082    a = -1;                             /* Current character, none yet. */
1083    while (x--) {                       /* Up to given length */
1084        int old;
1085        if (feol) old = a;              /* Previous character */
1086
1087        if (zchin(n,&a) < 0) {          /* Read a character from the file */
1088            debug(F101,"zsinl","",a);
1089            z = -1;                     /* EOF or other error */
1090            break;
1091        }
1092
1093        if (feol) {                     /* Single-character line terminator */
1094            if (a == feol)
1095              break;   
1096        } else {                        /* CRLF line terminator */
1097            if (a == '\015')            /* CR, get next character */
1098              continue;
1099            if (old == '\015') {        /* Previous character was CR */
1100                if (a == '\012')        /* This one is LF, so we have a line */
1101                  break;
1102                else                    /* Not LF, deposit CR */
1103                  *s++ = '\015';
1104            }
1105#ifdef OS2
1106            if (a == '\012') break;     /* Here break on single LF too */
1107#endif /* OS2 */
1108        }
1109        *s = a;                         /* Deposit character */
1110        s++;
1111    }
1112    *s = '\0';                          /* Terminate the string */
1113    return(z);
1114}
1115
1116/*  Z X I N  --  Read x bytes from a file  */
1117
1118/*
1119  Reads x bytes (or less) from channel n and writes them
1120  to the address provided by the caller.
1121  Returns number of bytes read on success, 0 on EOF or error.
1122*/
1123int
1124zxin(n,s,x) int n, x; char *s; {
1125    return(fread(s, sizeof (char), x, fp[n]));
1126}
1127
1128/*
1129  Z I N F I L L  --  Buffered file input.
1130
1131  (re)fill the file input buffer with data.  All file input
1132  should go through this routine, usually by calling the zminchar()
1133  macro defined in ckcker.h.  Returns:
1134
1135  Value 0..255 on success, the character that was read.
1136  -1 on end of file.
1137  -2 on any kind of error other than end of file.
1138*/
1139int
1140zinfill() {
1141    int x;
1142
1143    errno = 0;
1144
1145#ifdef ZDEBUG
1146printf("ZINFILL fp[%d]=%ld\n",ZIFILE,fp[ZIFILE]);
1147#endif /* ZDEBUG */
1148
1149#ifdef OS2
1150#ifdef CK_LABELED
1151    debug(F101,"zinfill: binary","",binary);
1152    debug(F101,"zinfill: pFEAList","",pFEAList);
1153    if ( binary == XYFT_L && pFEAList ) {
1154        zinptr = zinbuffer ;
1155        do_label_send(os2filename) ;
1156        if (feof(fp[ZIFILE]))
1157          return(-1);
1158        clearerr(fp[ZIFILE]);
1159        zincnt += fread(zinptr, sizeof (char), INBUFSIZE - zincnt, fp[ZIFILE]);
1160    } else {
1161#endif /* CK_LABELED */
1162#endif /* OS2 */
1163
1164        if (feof(fp[ZIFILE])) {
1165            debug(F100,"ZINFILL feof","",0);
1166#ifdef ZDEBUG
1167            printf("ZINFILL EOF\n");
1168#endif /* ZDEBUG */
1169            return(-1);
1170        }
1171        clearerr(fp[ZIFILE]);
1172        zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
1173        debug(F101,"ZINFILL fread","",zincnt);
1174#ifdef ZDEBUG
1175        printf("FREAD=%d\n",zincnt);
1176#endif /* ZDEBUG */
1177#ifdef OS2
1178#ifdef CK_LABELED
1179    }
1180#endif /* CK_LABELED */
1181#endif /* OS2 */
1182
1183    if (ferror(fp[ZIFILE])) {
1184        debug(F100,"ZINFILL ferror","",0);
1185        debug(F100,"ZINFILL errno","",errno);
1186#ifdef ZDEBUG
1187        printf("ZINFILL errno=%d\n",errno);
1188#endif /* ZDEBUG */
1189        return(-2);
1190    }
1191    /* In case feof() didn't work just above -- sometimes it doesn't... */
1192    if (zincnt == 0) {
1193       if (feof(fp[ZIFILE]) ) {
1194           debug(F100,"ZINFILL count 0 EOF return -1","",0);
1195           return (-1);
1196       } else {
1197           debug(F100,"ZINFILL count 0 not EOF return -2","",0);
1198           return(-2);
1199       }
1200    }
1201    zinptr = zinbuffer;    /* set pointer to beginning, (== &zinbuffer[0]) */
1202    zincnt--;                           /* One less char in buffer */
1203    return((int)(*zinptr++) & 0377);    /* because we return the first */
1204}
1205
1206/*  Z S O U T  --  Write a string out to the given file, buffered.  */
1207
1208int
1209zsout(n,s) int n; char *s; {
1210    if (chkfn(n) < 1) return(-1);       /* Keep this, prevents memory faults */
1211    if (!s) return(0);                  /* Null pointer, do nothing, succeed */
1212    if (!*s) return(0);                 /* empty string, ditto */
1213#ifdef OS2
1214    if (n == ZCTERM)
1215      return(Vscrnprintf(s));
1216#endif /* OS2 */
1217    if (n == ZSFILE)
1218      return(write(fileno(fp[n]),s,(int)strlen(s)));
1219    return(fputs(s,fp[n]) == EOF ? -1 : 0);
1220}
1221
1222/*  Z S O U T L  --  Write string to file, with line terminator, buffered  */
1223
1224int
1225zsoutl(n,s) int n; char *s; {
1226    if (zsout(n,s) < 0)
1227        return(-1);
1228#ifdef OS2
1229    if (n == ZCTERM)
1230      return(Vscrnprintf("\n"));
1231#endif /* OS2 */
1232    if (n == ZSFILE)                    /* But session log is unbuffered */
1233      return(write(fileno(fp[n]),"\n",1));
1234    if (fputs("\n",fp[n]) == EOF)
1235        return(-1); /* (===OS2 ? \r\n) */
1236    return(0);
1237}
1238
1239/*  Z S O U T X  --  Write x characters to file, unbuffered.  */
1240
1241int
1242zsoutx(n,s,x) int n, x; char *s; {
1243#ifdef COMMENT
1244    if (chkfn(n) < 1) return(-1);
1245    return(write(fp[n]->_file,s,x));
1246#endif /* COMMENT */
1247
1248#ifdef OS2
1249    if (n == ZCTERM) {
1250        int i;
1251        for (i = 0; i < x; i++)
1252          if (!Vscrnprintf("%c",s[i]))
1253            return(-1);
1254        return(x);
1255    }
1256#endif /* OS2 */
1257    return(write(fileno(fp[n]),s,x) == x ? x : -1);
1258}
1259
1260/*  Z C H O U T  --  Add a character to the given file.  */
1261
1262/*  Should return 0 or greater on success, -1 on failure (e.g. disk full)  */
1263
1264int
1265#ifdef CK_ANSIC
1266zchout(register int n, char c)
1267#else
1268zchout(n,c) register int n; char c;
1269#endif /* CK_ANSIC */
1270/* zchout() */ {
1271    /* if (chkfn(n) < 1) return(-1); */
1272#ifdef OS2
1273    if ( n==ZCTERM )
1274        return(Vscrnprintf("%c",c));
1275#endif /* OS2 */
1276    if (n == ZSFILE)                    /* Use unbuffered for session log */
1277        return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1);
1278                                /* Buffered for everything else */
1279        if (putc(c,fp[n]) == EOF)       /* If true, maybe there was an error */
1280        return(ferror(fp[n])?-1:0);     /* Check to make sure */
1281        else                                    /* Otherwise... */
1282        return(0);                      /* There was no error. */
1283}
1284
1285/* (PWP) buffered character output routine to speed up file IO */
1286
1287int
1288zoutdump() {
1289    int x;
1290    zoutptr = zoutbuffer;               /* Reset buffer pointer in all cases */
1291    debug(F101,"zoutdump chars","",zoutcnt);
1292    if (zoutcnt == 0) {                 /* Nothing to output */
1293        return(0);
1294    } else if (zoutcnt < 0) {           /* Unexpected negative argument */
1295        zoutcnt = 0;                    /* Reset output buffer count */
1296        return(-1);                     /* and fail. */
1297    }
1298
1299#ifdef CK_LABELED
1300    if (binary == XYFT_L ) {
1301        x = do_label_recv() ;
1302        if ( x > 0 ) { /* more room in the buffer */
1303            debug(F101,"zoutdump do_label_recv unfilled buffer","",zoutcnt);
1304            return 0 ;
1305        } else if ( x < 0 ) {
1306            debug(F101,"zoutdump do_label_recv error","",x);
1307            return -1 ;
1308        }
1309    }
1310#endif /* CK_LABELED */
1311/* Frank Prindle suggested that replacing this fwrite() by an fflush() */
1312/* followed by a write() would improve the efficiency, especially when */
1313/* writing to stdout.  Subsequent tests showed a 5-fold improvement!   */
1314/* if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) {              */
1315
1316    fflush(fp[ZOFILE]);
1317    if ((x = write(fileno(fp[ZOFILE]),zoutbuffer,zoutcnt)) == zoutcnt) {
1318        debug(F101,"zoutdump write ok","",zoutcnt);
1319        zoutcnt = 0;                    /* Reset output buffer count */
1320        return(0);                      /* write() worked OK */
1321    } else {
1322        debug(F101,"zoutdump write error","",errno);
1323        debug(F101,"zoutdump write returns","",x);
1324        zoutcnt = 0;                    /* Reset output buffer count */
1325        return(-1);                     /* write() failed */
1326    }
1327}
1328
1329/*  C H K F N  --  Internal function to verify file number is ok  */
1330
1331/*
1332 Returns:
1333  -1: File number n is out of range
1334   0: n is in range, but file is not open
1335   1: n in range and file is open
1336*/
1337int
1338chkfn(n) int n; {
1339#ifdef COMMENT                          /* Save some stack space */
1340    switch (n) {
1341        case ZCTERM:
1342        case ZSTDIO:
1343        case ZIFILE:
1344        case ZOFILE:
1345        case ZDFILE:
1346        case ZTFILE:
1347        case ZPFILE:
1348        case ZSFILE:
1349        case ZSYSFN:
1350        case ZRFILE:
1351        case ZWFILE:
1352#ifdef OS2
1353        case ZPRINT:
1354#endif /* OS2 */
1355            break;
1356        default:
1357            debug(F101,"chkfn: file number out of range","",n);
1358            fprintf(stderr,"?File number out of range - %d\n",n);
1359            return(-1);
1360    }
1361    return( (fp[n] == NULL) ? 0 : 1 );
1362#else
1363    if (n < 0 || n >= ZNFILS)
1364      return(-1);
1365    else return( (fp[n] == NULL) ? 0 : 1 );
1366#endif /* COMMENT */
1367}
1368
1369/*  Z C H K I  --  Check if input file exists and is readable  */
1370
1371/*
1372  Returns:
1373   >= 0 if the file can be read (returns the size).
1374     -1 if file doesn't exist or can't be accessed,
1375     -2 if file exists but is not readable (e.g. a directory file).
1376     -3 if file exists but protected against read access.
1377*/
1378/*
1379 For Berkeley Unix, a file must be of type "regular" to be readable.
1380 Directory files, special files, and symbolic links are not readable.
1381*/
1382long
1383zchki(name) char *name; {
1384#ifdef NT
1385    struct _stat buf;
1386#else
1387    struct stat buf;
1388#endif /* NT */
1389    int x;
1390
1391#ifdef UNIX
1392    x = strlen(name);
1393    if (x == 9 && !strcmp(name,"/dev/null"))
1394      return(0);
1395#endif /* UNIX */
1396
1397    x = stat(name,&buf);
1398    if (x < 0) {
1399        debug(F111,"zchki stat fails",name,errno);
1400        return(-1);
1401    }
1402    if (!S_ISREG (buf.st_mode)          /* Must be regular file */
1403#ifdef S_ISFIFO
1404        && !S_ISFIFO (buf.st_mode)      /* or FIFO */
1405#endif /* S_ISFIFO */
1406        ) {                             
1407        debug(F111,"zchki skipping:",name,x);
1408        return(-2);
1409    }
1410    debug(F111,"zchki stat ok:",name,x);
1411
1412#ifdef SW_ACC_ID
1413    debug(F100,"zchki swapping ids for access()","",0);
1414    priv_on();
1415#endif /* SW_ACC_ID */
1416#ifdef NT
1417    x = _access(name,R_OK);
1418#else
1419    x = access(name,R_OK);
1420#endif /* NT */
1421#ifdef SW_ACC_ID
1422    priv_off();
1423    debug(F100,"zchki swapped ids restored","",0);
1424#endif /* SW_ACC_ID */
1425    if (x < 0) {        /* Is the file accessible? */
1426        debug(F111," access failed:",name,x); /* No */
1427        return(-3);
1428    } else {
1429        iflen = buf.st_size;                  /* Yes, remember size */
1430        strncpy(nambuf,name,MAXNAMLEN);       /* and name globally. */
1431        debug(F111," access ok:",name,(int) iflen);
1432        return( (iflen > -1L) ? iflen : 0L );
1433    }
1434}
1435
1436/*  Z C H K O  --  Check if output file can be created  */
1437
1438/*
1439 Returns -1 if write permission for the file would be denied, 0 otherwise.
1440*/
1441int
1442zchko(name) char *name; {
1443    int i, x;
1444    char *s;
1445
1446    if (!name) return(-1);              /* Watch out for null pointer. */
1447    x = (int)strlen(name);              /* Get length of filename */
1448    debug(F101," length","",x);
1449
1450#ifdef UNIX
1451/*
1452  Writing to null device is OK.
1453*/
1454    if (x == 9 && !strcmp(name,"/dev/null"))
1455      return(0);
1456#endif /* UNIX */
1457
1458    if (isdir(name))                    /* Directories are not writeable */
1459      return(-1);
1460
1461    s = malloc(x+3);                    /* Must copy because we can't */
1462    if (!s) {                           /* write into our argument. */
1463        fprintf(stderr,"Malloc error 46\n");
1464        return(-1);
1465    }
1466    strcpy(s,name);
1467
1468    for (i = x; i > 0; i--)             /* Strip filename from right. */
1469      if (ISDIRSEP(s[i-1])) break;
1470    debug(F101," i","",i);
1471
1472#ifdef COMMENT
1473/* X/OPEN XPG3-compliant systems fail if argument ends with "/"...  */
1474    if (i == 0)                         /* If no path, use current directory */
1475      strcpy(s,"./");
1476    else                                /* Otherwise, use given one. */
1477      s[i] = '\0';
1478#else
1479/* So now we use "path/." if path given, or "." if no path given. */
1480    s[i++] = '.';                       /* Append "." to path. */
1481    s[i] = '\0';
1482#endif /* COMMENT */
1483
1484#ifdef SW_ACC_ID
1485    debug(F100,"zchko swapping ids for access()","",0);
1486    priv_on();
1487#endif /* SW_ACC_ID */
1488#ifdef OS2                              /* No unwritable directories in OS/2 */
1489    x = 0;
1490#else
1491#ifdef NT
1492    x = _access(s,W_OK);                /* Check access of path. */
1493#else
1494    x = access(s,W_OK);                 /* Check access of path. */
1495#endif /* NT */
1496#endif /* OS2 */
1497#ifdef SW_ACC_ID
1498    priv_off();
1499    debug(F100,"zchko swapped ids restored","",0);
1500#endif /* SW_ACC_ID */
1501    if (x < 0)
1502      debug(F111,"zchko access failed:",s,errno);
1503    else
1504      debug(F111,"zchko access ok:",s,x);
1505    free(s);                            /* Free temporary storage */
1506    return((x < 0) ? -1 : 0);           /* and return. */
1507}
1508
1509/*  Z D E L E T  --  Delete the named file.  */
1510
1511int
1512zdelet(name) char *name; {
1513#ifdef NT
1514    return(_unlink(name));
1515#else /* NT */
1516    return(unlink(name));
1517#endif /* NT */
1518}
1519
1520/*  Z R T O L  --  Convert remote filename into local form  */
1521
1522VOID
1523zrtol(name,name2) char *name, *name2; {
1524    char *p; int flag = 0, n = 0;
1525    int acase = 0;
1526    if (!name || !name2) return;
1527    debug(F101,"zrtol original name","",name);
1528    p = name2;
1529    for ( ; *name != '\0' && n < maxnam; name++) {
1530        if (*name > ' ') flag = 1;      /* Strip leading blanks and controls */
1531        if (flag == 0 && *name < '!')
1532          continue;
1533        if (isupper(*name))             /* Check for mixed case */
1534          acase |= 1;
1535        else if (islower(*name))
1536          acase |= 2;
1537        *p++ = *name;
1538        n++;
1539    }
1540    *p-- = '\0';                        /* Terminate */
1541    while (*p < '!' && p > name2)       /* Strip trailing blanks & controls */
1542      *p-- = '\0';
1543
1544    if (*name2 == '\0') {               /* Nothing left? */
1545        strcpy(name2,"NONAME");         /* do this... */
1546    } else if (acase == 1) {            /* All uppercase? */
1547        p = name2;                      /* So convert all letters to lower */
1548        while (*p) {
1549            if (isupper(*p))
1550              *p = tolower(*p);
1551            p++;
1552        }
1553    }     
1554#ifdef OS2
1555    if (!IsFileNameValid(name2))
1556      ChangeNameForFAT(name2);
1557#endif /* OS2 */
1558    debug(F110,"zrtol new name",name2,0);
1559}
1560
1561
1562/*  Z S T R I P  --  Strip device & directory name from file specification */
1563
1564/*  Strip pathname from filename "name", return pointer to result in name2 */
1565
1566#ifdef pdp11
1567#define ZSTRPLEN 64
1568#else
1569#define ZSTRPLEN MAXPATH
1570#endif /* pdp11 */
1571static char work[ZSTRPLEN+1];
1572
1573VOID
1574zstrip(name,name2) char *name, **name2; {
1575    char *cp, *pp;
1576    int n = 0;
1577    debug(F110,"zstrip before",name,0);
1578    if (!name) { *name2 = ""; return; }
1579    pp = work;
1580#ifdef DTILDE
1581    /* Strip leading tilde */
1582    if (*name == '~') name++;
1583    debug(F110,"zstrip after tilde-stripping",name,0);
1584#else
1585#ifdef OS2
1586    /* Strip disk letter and colon */
1587    if (isalpha(*name) && (*(name+1) == ':')) name += 2;
1588    debug(F110,"zstrip after disk-stripping",name,0);
1589#endif /* OS2 */
1590#endif /* DTILDE */
1591    for (cp = name; *cp; cp++) {
1592        if (ISDIRSEP(*cp)) {
1593            pp = work;
1594            n = 0;
1595        } else {
1596            *pp++ = *cp;
1597            if (n++ >= ZSTRPLEN)
1598              break;
1599        }
1600    }
1601    *pp = '\0';                         /* Terminate the string */
1602    *name2 = work;
1603    debug(F110,"zstrip after",*name2,0);
1604}
1605
1606/*  Z L T O R  --  Local TO Remote */
1607
1608VOID
1609zltor(name,name2) char *name, *name2; {
1610    char *cp, *pp;
1611    int dc = 0;
1612    char *dotp = NULL;
1613#ifdef aegis
1614    char *namechars;
1615    int tilde = 0, bslash = 0;
1616
1617    if ((namechars = getenv("NAMECHARS")) != NULL) {
1618        if (xindex(namechars, '~' ) != NULL) tilde  = '~';
1619        if (xindex(namechars, '\\') != NULL) bslash = '\\';
1620    } else {
1621        tilde = '~';
1622        bslash = '\\';
1623    }
1624#endif /* aegis */
1625
1626    debug(F110,"zltor",name,0);
1627    pp = work;
1628#ifdef OS2
1629    if (isalpha(*name) && (*(name+1) == ':')) /* Strip disk name */
1630      name += 2;
1631#endif /* OS2 */
1632    for (cp = name; *cp; cp++) {        /* Strip path name */
1633        if (ISDIRSEP(*cp)) {
1634            dc = 0;                     /* Start over... */
1635            dotp = NULL;
1636            pp = work;
1637        } else if (islower(*cp))        /* Uppercase letters */
1638          *pp++ = toupper(*cp);         /* Change tilde to 'X' */
1639        else if (*cp == '~')
1640          *pp++ = 'X';
1641        else if (*cp == '#')            /* Change number sign to 'X' */
1642          *pp++ = 'X';
1643        else if (*cp == '.') {          /* Change dot to underscore */
1644            dotp = pp;                  /* Remember where we last did this */
1645            *pp++ = '_';
1646        } else
1647          *pp++ = *cp;
1648    }
1649    *pp = NUL;                          /* Tie it off. */
1650    if (dotp) *dotp = '.';              /* Restore last dot (if any) */
1651    cp = name2;                         /* If nothing before dot, */
1652    if (*work == '.') *cp++ = 'X';      /* insert 'X' */
1653    strcpy(cp,work);
1654    debug(F110," name2",name2,0);
1655}
1656
1657
1658/*  Z C H D I R  --  Change directory  */
1659/*
1660  Call with:
1661    dirnam = pointer to name of directory to change to,
1662      which may be "" or NULL to indicate user's home directory.
1663  Returns:
1664    0 on failure
1665    1 on success
1666*/
1667int
1668zchdir(dirnam) char *dirnam; {
1669    char *hd, *sp, *p;
1670
1671    debug(F110,"zchdir",dirnam,0);
1672    if (dirnam == NULL || dirnam == "" || *dirnam == '\0') /* If arg is null */
1673      dirnam = zhome();                 /* use user's home directory. */
1674    sp = dirnam;
1675    debug(F110,"zchdir 2",dirnam,0);
1676
1677#ifdef DTILDE
1678    hd = tilde_expand(dirnam);          /* Attempt to expand tilde */
1679    if (*hd == '\0') hd = dirnam;       /* in directory name. */
1680#else
1681    hd = dirnam;
1682#endif /* DTILDE */
1683    debug(F110,"zchdir 3",hd,0);
1684#ifdef pdp11
1685    /* Just to save some space */
1686    return((
1687#ifdef NT
1688            _chdir(hd)
1689#else
1690            chdir(hd)
1691#endif /* NT */
1692            == 0) ? 1 : 0);
1693#else
1694#ifdef OS2
1695    if (isalpha(hd[0]) && hd[1] == ':') {
1696        if (zchdsk(hd[0]))
1697          return(0);
1698        if (hd[2] == 0)
1699          return(1);                    /* Handle drive-only case */
1700    }
1701    {
1702    /* strip trailing DIRSEP except after d:; chdir() doesn't like it */
1703        int len = strlen(hd) ;
1704        if ( len > 1 && ISDIRSEP(hd[len - 1]) && hd[len - 2] != ':' )
1705          hd[len - 1] = 0;
1706    }
1707#endif /* OS2 */
1708    if (
1709#ifdef NT
1710        _chdir(hd)
1711#else
1712        chdir(hd)
1713#endif /* NT */
1714        == 0) return(1);                /* Try to cd */
1715    else return(0);
1716
1717#ifdef COMMENT
1718/* This doesn't help in Windows or OS/2, and it's wrong in UNIX */
1719    p = sp;                             /* Failed, lowercase it. */
1720    while (*p) {
1721        if (isupper(*p)) *p = tolower(*p);
1722        p++;
1723    }
1724    debug(F110,"zchdir 4",hd,0);
1725#ifdef DTILDE
1726    hd = tilde_expand(sp);              /* Try again to expand tilde */
1727    if (*hd == '\0') hd = sp;
1728#else
1729    hd = sp;                            /* Point to result */
1730#endif /* DTILDE */
1731    debug(F110,"zchdir 5",hd,0);
1732    return((
1733#ifdef NT
1734            _chdir(hd)
1735#else
1736            chdir(hd)
1737#endif /* NT */
1738            == 0) ? 1 : 0);
1739#endif /* COMMENT */
1740
1741#endif /* pdp11 */
1742}
1743
1744/*  Z H O M E  --  Return pointer to user's home directory  */
1745
1746char *
1747zhome() {
1748#ifdef Plan9
1749    char *home = getenv("home");
1750#else
1751    char *home = getenv("HOME");
1752#endif /* Plan9 */
1753#ifdef OS2
1754    extern char startupdir[];
1755    return(home ? home : startupdir);
1756#else
1757    return(home ? home : ".");
1758#endif /* OS2 */
1759}
1760
1761/*  Z G T D I R  --  Return pointer to user's current directory  */
1762
1763#ifdef pdp11
1764#define CWDBL 80                        /* Save every byte we can... */
1765#else
1766#define CWDBL MAXPATH
1767#endif /* pdp11 */
1768static char cwdbuf[CWDBL+1];
1769
1770char *
1771zgtdir() {
1772    char *buf;
1773
1774#ifdef BSD44
1775    extern char *getcwd();
1776    buf = cwdbuf;
1777    debug(F101,"zgtdir CWDBL","",CWDBL);
1778    return(getcwd(buf,CWDBL));
1779#else
1780#ifdef SVORPOSIX
1781    extern char *getcwd();
1782    buf = cwdbuf;
1783    debug(F101,"zgtdir CWDBL","",CWDBL);
1784    return(getcwd(buf,CWDBL));
1785#else
1786#ifdef COHERENT
1787#ifdef _I386
1788    extern char *getcwd();
1789    buf = cwdbuf;
1790    debug(F101,"zgtdir CWDBL","",CWDBL);
1791    return(getcwd(buf,CWDBL));
1792#else
1793    extern char *getwd();
1794    buf = cwdbuf;
1795    debug(F101,"zgtdir CWDBL","",CWDBL);
1796    return(getwd(buf));
1797#endif /* _I386 */
1798#else
1799#ifdef OS2
1800#ifndef __IBMC__ /* which has a macro for this */
1801    extern char *getcwd();
1802#endif /* __IBMC__ */
1803    buf = cwdbuf;
1804    debug(F101,"zgtdir CWDBL","",CWDBL);
1805#ifdef NT
1806    _getcwd(buf,CWDBL);
1807#else
1808    getcwd(buf,CWDBL);
1809#endif /* NT */
1810    return(buf);
1811#else  /* OS2 */
1812#ifdef BSD4
1813    extern char *getwd();
1814    buf = cwdbuf;
1815    debug(F100,"zgtdir getwd()","",0);
1816    return(getwd(buf));
1817#else
1818    return("directory unknown");
1819#endif /* BSD4 */
1820#endif /* OS2 */
1821#endif /* COHERENT */
1822#endif /* SYSVORPOSIX */
1823#endif /* BSD44 */
1824}
1825
1826/*  Z X C M D -- Run a system command so its output can be read like a file */
1827
1828#ifdef NT
1829_PROTOTYP( FILE * win95popen, (char *cmd, char *mode) );
1830_PROTOTYP( int win95pclose, (FILE *pipe) );
1831#endif /* NT */
1832
1833int
1834zxcmd(filnum,comand) int filnum; char *comand; {
1835#ifdef OS2
1836    if (chkfn(filnum) < 0) return(-1);  /* Need a valid Kermit file number. */
1837    if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */
1838      return(0);
1839    if (filnum == ZIFILE || filnum == ZRFILE) { /* Input from a command */
1840        if (priv_chk() ||
1841#ifdef NT
1842         isWin95() ?
1843         ((fp[filnum] = win95popen(comand,"rb")) == NULL) :
1844#endif /* NT */
1845         ((fp[filnum] = popen(comand,"rb")) == NULL)
1846         )
1847          return(0);
1848    } else { /* Output to a command */
1849        if (priv_chk() ||
1850#ifdef NT
1851            isWin95() ?
1852            ((fp[filnum] = win95popen(comand,"w")) == NULL) :
1853#endif /* NT */
1854            ((fp[filnum] = popen(comand,"w")) == NULL)
1855            )
1856          return(0);
1857    }
1858    fp[ZSYSFN] = fp[filnum];            /* remember the pipe handle */
1859    ispipe[filnum] = 1;
1860    return(1);
1861#else /* Not OS2 */
1862    int pipes[2];
1863    int out;
1864
1865    if (chkfn(filnum) < 0) return(-1);  /* Need a valid Kermit file number. */
1866    if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */
1867      return(0);
1868
1869    out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
1870
1871/* Output to a command */
1872
1873    if (out) {                          /* Need popen() to do this. */
1874#ifdef NOPOPEN
1875        return(0);                      /* no popen(), fail. */
1876#else
1877/* Use popen() to run the command. */
1878
1879#ifdef _POSIX_SOURCE
1880/* Strictly speaking, popen() is not available in POSIX.1 */
1881#define DCLPOPEN
1882#endif /* _POSIX_SOURCE */
1883
1884#ifdef COHERENT
1885#define DCLPOPEN
1886        FILE * fdopen();
1887#endif /* COHERENT */
1888
1889#ifdef DCLPOPEN
1890        /* popen() needs declaring because it's not declared in <stdio.h> */
1891        FILE *popen();
1892#endif /* DCLPOPEN */
1893
1894        if (priv_chk() || ((fp[filnum] = popen(comand,"w")) == NULL))
1895          return(0);
1896        else return(1);
1897#endif /* NOPOPEN */
1898    }
1899
1900/* Input from a command */
1901
1902    if (pipe(pipes) != 0) {
1903        debug(F100,"zxcmd pipe failure","",0);
1904        return(0);                      /* can't make pipe, fail */
1905    }
1906
1907/* Create a fork in which to run the named process */
1908
1909    if ((
1910#ifdef aegis
1911         pid = vfork()                  /* child */
1912#else
1913         pid = fork()                   /* child */
1914#endif /* aegis */
1915         ) == 0) {
1916
1917/* We're in the fork. */
1918
1919        char *shpath, *shname, *shptr;  /* Find user's preferred shell */
1920#ifndef aegis
1921        struct passwd *p;
1922        char *defshell;
1923#ifdef HPUX10                           /* Default shell */
1924        defshell = "/usr/bin/sh";
1925#else
1926#ifdef Plan9
1927        defshell = "/bin/rc";
1928#else
1929        defshell = "/bin/sh";
1930#endif /* Plan9 */
1931#endif /* HPUX10 */
1932#endif /* aegis */
1933        if (priv_can()) exit(1);        /* Turn off any privileges! */
1934        debug(F101,"zxcmd pid","",pid);
1935        close(pipes[0]);                /* close input side of pipe */
1936        close(0);                       /* close stdin */
1937        if (open("/dev/null",0) < 0) return(0); /* replace input by null */
1938#ifndef OXOS
1939#ifndef SVORPOSIX
1940        dup2(pipes[1],1);               /* BSD: replace stdout & stderr */
1941        dup2(pipes[1],2);               /* by the pipe */
1942#else
1943        close(1);                       /* AT&T: close stdout */
1944        if ( dup(pipes[1]) != 1 )       /* Send stdout to the pipe */
1945          return(0);
1946        close(2);                       /* Send stderr to the pipe */
1947        if ( dup(pipes[1]) != 2 )
1948          return(0);
1949#endif /* SVORPOSIX */
1950#else /* OXOS */
1951        dup2(pipes[1],1);
1952        dup2(pipes[1],2);
1953#endif /* OXOS */
1954        close(pipes[1]);                /* Don't need this any more. */
1955
1956#ifdef aegis
1957        if ((shpath = getenv("SERVERSHELL")) == NULL)
1958          shpath = "/bin/sh";
1959#else
1960        shpath = getenv("SHELL");       /* What shell? */
1961        if (shpath == NULL) {
1962            p = getpwuid( real_uid() ); /* Get login data */
1963            if (p == (struct passwd *)NULL || !*(p->pw_shell))
1964              shpath = defshell;
1965            else shpath = p->pw_shell;
1966        }
1967#endif /* aegis */
1968        shptr = shname = shpath;
1969        while (*shptr != '\0')
1970          if (*shptr++ == '/')
1971            shname = shptr;
1972        debug(F100,"zxcmd...","",0);
1973        debug(F110,shpath,shname,0);
1974
1975        execl(shpath,shname,"-c",comand,(char *)NULL); /* Execute the cmd */
1976        exit(0);                        /* just punt if it failed. */
1977    } else if (pid == (PID_T) -1) {
1978        debug(F100,"zxcmd fork failure","",0);
1979        return(0);
1980    }
1981    debug(F101,"zxcmd pid","",pid);
1982    if (out) {
1983        close(pipes[0]);                /* Don't need the input side */
1984        fp[filnum] = fdopen(pipes[1],"w"); /* Open a stream for output. */
1985        fp[ZSYSFN] = fp[filnum];        /* Remember. */
1986        zoutcnt = 0;                    /* (PWP) reset input buffer */
1987        zoutptr = zoutbuffer;
1988    } else {
1989        close(pipes[1]);                /* Don't need the output side */
1990        fp[filnum] = fdopen(pipes[0],"r"); /* Open a stream for input. */
1991        fp[ZSYSFN] = fp[filnum];        /* Remember. */
1992        zincnt = 0;                     /* (PWP) reset input buffer */
1993        zinptr = zinbuffer;
1994    }
1995    return(1);
1996#endif /* OS2 */
1997} /* zxcmd */
1998
1999/*  Z C L O S F  - wait for the child fork to terminate and close the pipe. */
2000
2001int
2002zclosf(filnum) int filnum; {
2003    int wstat;
2004    debug(F101,"zclosf filnum","",filnum);
2005#ifndef NOPOPEN
2006#ifdef OS2
2007    if (ispipe[filnum]) {
2008        int x;
2009#ifdef NT
2010       x = isWin95() ? win95pclose(fp[filnum]) : pclose(fp[filnum]) ;
2011#else /* NT */
2012       x = pclose(fp[filnum]);
2013#endif /* NT */
2014        fp[filnum] = NULL;
2015        ispipe[filnum] = 0;
2016#else
2017    if (filnum == ZWFILE) {
2018        int x;
2019        x = pclose(fp[filnum]);
2020        fp[filnum] = fp[ZSYSFN] = NULL;
2021#endif /* OS2 */
2022        return((x < 0) ? 0 : 1);
2023    }
2024#endif /* NOPOPEN */
2025    debug(F101,"zclosf fp[filnum]","", fp[filnum]);
2026    debug(F101,"zclosf fp[ZSYSFN]","", fp[ZSYSFN]);
2027#ifdef OS2
2028    fclose(fp[filnum]);
2029    fp[filnum] = NULL;
2030#else
2031    if (pid != (PID_T) 0) {
2032        debug(F101,"zclosf killing pid","",pid);
2033#ifdef Plan9
2034        kill(pid, SIGKILL);
2035#else
2036        kill(pid,9);
2037#endif /* Plan9 */
2038        while ((wstat = wait((WAIT_T *)0)) != pid && wstat != -1) ;
2039        pid = 0;
2040    }
2041    fclose(fp[filnum]);
2042    fp[filnum] = fp[ZSYSFN] = NULL;
2043#endif /* OS2 */
2044    return(1);
2045}
2046
2047/*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
2048/*
2049  Returns the number of files that match fn1, with data structures set up
2050  so that first file (if any) will be returned by the next znext() call.
2051  Depends on external variable wildxpand: 0 means we expand wildcards
2052  internally, nonzero means we call the shell to do it.
2053*/
2054
2055#ifdef OS2
2056/*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
2057/*
2058  Returns the number of files that match fn1, with data structures set up
2059  so that first file (if any) will be returned by the next znext() call.
2060  Depends on external variable wildxpand: 0 means we expand wildcards
2061  internally, nonzero means we call the shell to do it.
2062*/
2063
2064char findpath[MAXPATH];
2065#ifdef NT
2066WIN32_FIND_DATA finddata;
2067HANDLE findhandle = INVALID_HANDLE_VALUE;
2068#else /* NT */
2069FILEFINDBUF3    finddata;
2070HDIR            findhandle = HDIR_CREATE;
2071ULONG           findcount;
2072#endif /* NT */
2073
2074int
2075zxpand(fn) char *fn; {
2076    int i, lasterror = 0;
2077    char localfn[MAXPATH];
2078
2079        if ( !fn || !(*fn) )
2080                return(0);
2081
2082    debug(F110,"zxpand fn",fn,0);
2083#ifdef NT
2084    if (findhandle != INVALID_HANDLE_VALUE) {
2085        FindClose(findhandle);
2086        findhandle = INVALID_HANDLE_VALUE;
2087    }
2088#else
2089    if (findhandle != HDIR_CREATE) {
2090        DosFindClose(findhandle);
2091        findhandle = HDIR_CREATE;
2092    }
2093#endif /* NT */
2094
2095    fcount = 0;
2096    strcpy(findpath, fn);
2097    strcpy(localfn, fn);
2098    for (i = strlen(findpath); i >= 0; i--) {
2099        if (ISDIRSEP(findpath[i])
2100                        || findpath[i] == ':'
2101                        ) {
2102            if (!findpath[i+1])
2103              strcat(localfn, "*");
2104            else
2105              findpath[i+1] = '\0';
2106            break;
2107        }
2108        if (findpath[i] == ':') {
2109            if (!findpath[i+1]) {
2110                strcat(localfn, "*");
2111                break;
2112            }
2113        }
2114    }
2115    if (i < 0)
2116      if (zchki(localfn) == -2) {
2117          if ( strlen(localfn) == 2 && localfn[1] == ':' ) {
2118              strcat(localfn, "./*");
2119          } else {
2120              strcat(findpath, "/");
2121              strcat(localfn, "/*");
2122          }
2123      } else if (!ISDIRSEP(localfn[0]) && localfn[1] != ':') {
2124          findpath[0] = '\0';
2125          strcpy(nambuf, "./");
2126          strcat(nambuf, localfn);
2127          strcpy(localfn, nambuf);
2128      }
2129
2130    debug(F100,"zxpand about to FindFirst","",0);
2131#ifdef NT
2132    findhandle = FindFirstFile( localfn, &finddata );
2133    if ( findhandle == INVALID_HANDLE_VALUE )
2134#else /* NT */
2135    findcount = 1;
2136    DosFindFirst(localfn,
2137                 &findhandle,
2138                 (ULONG) FILE_NORMAL | FILE_ARCHIVED | FILE_DIRECTORY |
2139                   FILE_HIDDEN | FILE_SYSTEM | FILE_READONLY,
2140                 (PVOID) &finddata, (ULONG) sizeof(finddata),
2141                 &findcount,
2142                 FIL_STANDARD
2143                 );
2144    if (findcount != 1)
2145#endif /* NT */
2146      return(fcount);
2147
2148    fcount = 1;
2149    while (
2150#ifdef NT
2151           FindNextFile( findhandle, &finddata )
2152#else /* NT */
2153           !(lasterror = DosFindNext(findhandle,
2154                                     (PVOID) &finddata,
2155                                     (ULONG) sizeof(finddata),
2156                                     &findcount)) &&
2157           findcount == 1
2158#endif /* NT */
2159           )
2160      fcount++;
2161
2162#ifdef NT
2163    lasterror = GetLastError();
2164    FindClose( findhandle );
2165    findhandle = INVALID_HANDLE_VALUE;
2166#else
2167    DosFindClose( findhandle );
2168    findhandle = HDIR_CREATE;
2169#endif /* NT */
2170
2171    if (lasterror != ERROR_NO_MORE_FILES) {
2172        fcount = 0;
2173        return (fcount);
2174    }
2175
2176    debug(F111,"zxpand","fcount",fcount);
2177
2178#ifdef NT
2179    findhandle = FindFirstFile( localfn, &finddata );
2180#else /* NT */
2181    findcount = 1;
2182    DosFindFirst(localfn,
2183                 &findhandle,
2184                 (ULONG) FILE_NORMAL | FILE_ARCHIVED | FILE_DIRECTORY |
2185                   FILE_HIDDEN | FILE_SYSTEM | FILE_READONLY,
2186                 (PVOID) &finddata,
2187                 (ULONG) sizeof(finddata),
2188                 &findcount,
2189                 FIL_STANDARD
2190                 );
2191#endif /* NT */
2192    return(fcount);
2193}
2194
2195/*  Z N E X T  --  Get name of next file from list created by zxpand(). */
2196/*
2197   Returns > 0 if there's another file, with its name copied into the argument
2198   string, or 0 if no more files in list.
2199*/
2200int
2201znext(fn) char *fn; {
2202    int lasterror = 0;
2203
2204    if (fcount-- > 0
2205#ifdef NT
2206         && findhandle != INVALID_HANDLE_VALUE
2207#else
2208         && findhandle != HDIR_CREATE
2209#endif /* NT */
2210        ) {
2211        strcpy(fn,findpath);
2212#ifdef NT
2213        strcat(fn,finddata.cFileName);
2214        if (!FindNextFile( findhandle, &finddata)) {
2215            FindClose( findhandle );
2216            findhandle = INVALID_HANDLE_VALUE;
2217            fcount = 0;
2218        }
2219#else /* NT */
2220        strcat(fn,finddata.achName );
2221        findcount = 1;
2222        lasterror = DosFindNext(findhandle,
2223                                (PVOID) &finddata,
2224                                (ULONG) sizeof(finddata),
2225                                &findcount
2226                                );
2227        if (lasterror || findcount != 1) {
2228            DosFindClose(findhandle);
2229            findhandle = HDIR_CREATE;
2230            fcount = 0;
2231        }
2232#endif /* NT */
2233    } else {
2234        *fn = '\0';
2235        fcount = -1;
2236    }
2237    debug(F111,"znext",fn,fcount+1);
2238    return(fcount+1);
2239}
2240
2241#else /* Not OS2 */
2242
2243int
2244zxpand(fn) char *fn; {
2245    char *p;
2246#ifdef DTILDE                           /* Built with tilde-expansion? */
2247    char *tnam;
2248#endif /* DTILDE */
2249    debug(F111,"zxpand entry",fn,wildxpand);
2250    if ( !fn || !(*fn) )               /* If no argument provided */
2251      return(0);                       /* Return zero files found */
2252
2253#ifdef DTILDE                           /* Built with tilde-expansion? */
2254    if (*fn == '~') {                   /* Starts with tilde? */
2255        tnam = tilde_expand(fn);        /* Try to expand it. */
2256        if (tnam) fn = tnam;
2257    }
2258    debug(F110,"zxpand after tilde_x",fn,0);
2259#endif /* DTILDE */
2260#ifndef NOPUSH
2261    if (!nopush && wildxpand)           /* Who is expanding wildcards? */
2262      fcount = shxpand(fn,mtchs,MAXWLD); /* Shell */
2263    else
2264#endif /* NOPUSH */
2265      fcount = (mtchs == NULL &&        /* Kermit */
2266                (mtchs = (char **)malloc(MAXWLD * sizeof(*mtchs))) == NULL)
2267        ? 0
2268          : fgen(fn,mtchs,MAXWLD);      /* Look up the file. */
2269
2270    if (fcount > 0) {
2271        mtchptr = mtchs;                /* Save pointer for next. */
2272        debug(F111,"zxpand ok",mtchs[0],fcount);
2273        return(fcount);
2274    }
2275    debug(F111,"zxpand fgen1",fn,fcount); /* Didn't get one, or got too many */
2276    p = malloc((int)strlen(fn) + 10);   /* Make space */
2277    if (!p) return(0);
2278    zrtol(fn,p);                        /* Try again, maybe lowercase */
2279
2280#ifndef NOPUSH
2281    if (!nopush && wildxpand)
2282      fcount = shxpand(p,mtchs,MAXWLD); /* Shell */
2283    else
2284#endif /* NOPUSH */
2285      fcount = fgen(p,mtchs,MAXWLD);    /* Kermit */
2286    if (fcount > 0) {                   /* Got at least one? */
2287        mtchptr = mtchs;                /* Save pointer for next. */
2288        debug(F111,"zxpand fgen2 ok",mtchs[0],fcount);
2289    } else debug(F111,"zxpand 2 not ok",p,fcount);
2290    free(p);
2291    return(fcount);
2292}
2293
2294
2295/*  Z N E X T  --  Get name of next file from list created by zxpand(). */
2296/*
2297  Returns >0 if there's another file, with its name copied into the arg string,
2298  or 0 if no more files in list.
2299*/
2300int
2301znext(fn) char *fn; {
2302    if (fcount-- > 0) strcpy(fn,*mtchptr++);
2303    else *fn = '\0';
2304    debug(F111,"znext",fn,fcount+1);
2305    return(fcount+1);
2306}
2307#endif /* OS2 */
2308
2309/*  Z C H K S P A  --  Check if there is enough space to store the file  */
2310
2311/*
2312 Call with file specification f, size n in bytes.
2313 Returns -1 on error, 0 if not enough space, 1 if enough space.
2314*/
2315int
2316#ifdef CK_ANSIC
2317zchkspa(char *f, long n)
2318#else
2319zchkspa(f,n) char *f; long n;
2320#endif /* CK_ANSIC */
2321/* zchkspa() */ {
2322#ifdef OS2
2323/* OS/2 gives us an easy way to do this. */
2324    unsigned long x, filesize = 0L;
2325    debug(F111,"zchkspa",f,n);
2326    if (isalpha(f[0]) && f[1] == ':') {
2327        x = zdskspace(toupper(f[0]) - 'A' + 1);
2328        debug(F101,"zchkspa disk: size","",x);
2329    } else {
2330        x = zdskspace(0);
2331        debug(F101,"zchkspa no disk size","",x);
2332    }
2333    if (fncact == XYFX_U || fncact == XYFX_X) /* Update or Replace */
2334      filesize = zchki(f);
2335
2336   return((x+filesize >= n) ? 1 : 0);
2337#else
2338/* In UNIX there is no good (and portable) way. */
2339    return(1);                          /* Always say OK. */
2340#endif /* OS2 */
2341}
2342
2343
2344/*  Z N E W N  --  Make a new name for the given file  */
2345
2346/*
2347  Given the name, fn, of a file that already exists, this function builds a
2348  new name of the form "<oldname>.~<n>~", where <oldname> is argument name
2349  (fn), and <n> is a version number, one higher than any existing version
2350  number for that file, up to 9999.  This format is consistent with that used
2351  by GNU EMACS.  If the constructed name is too long for the system's maximum,
2352  enough characters are truncated from the end of <fn> to allow the version
2353  number to fit.  If no free version numbers exist between 1 and 9999, a
2354  version number of "xxxx" is used.  Returns a pointer to the new name in
2355  argument s.
2356*/
2357
2358VOID
2359znewn(fn,s) char *fn, **s; {
2360#ifdef pdp11
2361#define ZNEWNBL 63                      /* Name buffer length */
2362#define ZNEWNMD 3                       /* Max digits for version number */
2363#else
2364#define ZNEWNBL 255
2365#define ZNEWNMD 4
2366#endif /* pdp11 */
2367
2368    static char buf[ZNEWNBL+1];
2369    char *bp, *xp, *yp;
2370#ifdef OS2
2371    static char localfn[MAXPATH+1];
2372    char *tp=NULL, *zp=NULL, ch, temp[14];
2373#endif /* OS2 */
2374    int len = 0, d = 0, n, t, i, j, k, power = 1;
2375
2376    int max = MAXNAMLEN;                /* Maximum name length */
2377
2378    if (max < 14) max = 14;             /* Make it reasonable */
2379    if (max > ZNEWNBL) max = ZNEWNBL;
2380    bp = buf;                           /* Buffer for building new name */
2381    yp = fn;
2382    while (*yp) {                       /* Copy old name into buffer */
2383        *bp++ = *yp++;
2384        if (len++ > ZNEWNBL) break;     /* ...up to buffer length */
2385    }
2386    *s = NULL;
2387    for (i = 1; i < ZNEWNMD + 1; i++) { /* Version numbers up to 10**i - 1 */
2388        power *= 10;                    /* Next power of 10 */
2389        j = max - len;                  /* Space left for version number */
2390        k = 3 + i;                      /* Space needed for it */
2391        if (j < k) {                    /* Make room if necessary */
2392            len -= (k - j);             /* Adjust length of filename */
2393            bp = buf + len;             /* Point to new end */
2394        }
2395        *bp++ = '*';                    /* Put a star on the end (UNIX) */
2396        *bp-- = '\0';                   /* Terminate with null */
2397
2398        debug(F110,"znewn: about to expand",buf,0);
2399        n = zxpand(buf);                /* Expand the resulting wild name */
2400                                        /* n is the number of matches */
2401        debug(F101,"znewn matches","",n);
2402        while (n-- > 0) {               /* Find any existing name.~n~ files */
2403#ifdef OS2
2404            znext(localfn);
2405            xp = localfn;
2406#else /* OS2 */
2407            xp = *mtchptr++;            /* Point at matching name */
2408#endif /* OS2 */
2409            xp += len;                  /* Look for .~<n>~ at the end of it */
2410            if (*xp == '.' && *(xp+1) == '~') { /* Has a version number */
2411                t = atoi(xp+2);                 /* Get it */
2412                if (t > d) d = t;       /* Save d = highest version number */
2413            }
2414        }
2415        if (d < power-1) {              /* Less than maximum possible? */
2416            debug(F110,"znewn number ok",buf,0);
2417            sprintf(bp,".~%d~",d+1);    /* Yes, make "name.~<d+1>~" */
2418            *s = buf;                   /* Point to new name */
2419            ck_znewn = d+1;             /* Also make it available globally */
2420            break;                      /* Done, return it */
2421        }
2422    }
2423    if (*s == NULL) {
2424        debug(F110,"znewn: too many names",buf,0);
2425        sprintf(bp,".~xxxx~");          /* Too many, use xxxx. */
2426        ck_znewn = -1;                  /* Also make it available globally */
2427        *s = buf;
2428    }
2429#ifdef OS2
2430    if (IsFileNameValid(buf)) {
2431        debug(F110,"znewn: os2 filename valid",buf,0);
2432        return; /* HPFS */
2433    }
2434    /* otherwise make FAT 8.3 name */
2435    debug(F110,"znewn: os2 filename invalid",buf,0);
2436    xp = bp = buf;
2437    yp = fn;
2438    while (*yp) {                       /* Copy name into buf */
2439        ch = *bp++ = *yp++;
2440        if (ISDIRSEP(ch) || (ch == ':')) xp=bp;
2441    }
2442    *bp = '\0';
2443    yp = xp;
2444    i = 1;
2445    while (*yp && (*yp != '.')) {
2446        yp++;
2447        if (++i<=6)
2448           zp=yp;
2449    }
2450    /* zp points to 6th character in name, or yp, whichever occurs first. */
2451    strcpy(temp,yp);                    /* Copy extension, if any */
2452    while (zp != xp+8) {
2453        if ( zp < xp+5 ) *zp++='0';
2454        else *zp++='?';                 /* Pad out with wild cards */
2455    }
2456    strcpy(zp,temp);                    /* Get the extension back */
2457    debug(F110,"znewn: about to expand",buf,0);
2458    n = zxpand(buf);                    /* Expand the resulting wild name */
2459    debug(F101,"znewn: matches","",n);
2460    d = 0;                              /* Index number */
2461    debug(F110,"znewn: temp",temp,0);
2462    while (znext(temp)) {
2463#ifdef COMMENT
2464        i = atoi(temp+5);
2465        if (i > d) d = i;
2466#else
2467        if ( tp = strrchr( temp, '/' ) )
2468          tp++;
2469        else
2470          tp = temp;   
2471        i = atoi(tp+5);
2472        debug(F111,"znewn: tp=atoi(tp+5)",tp,i);
2473        if (i > d) d = i;
2474        debug(F101,"znewn: d","",d);
2475#endif /* COMMENT */
2476    }
2477    sprintf(temp,"%03d",d+1);           /* Get the number into a string */
2478    ck_znewn = d+1;
2479    memcpy(xp+5, temp, 3);
2480    debug(F110,"znewn: os2 file name is FAT",buf,0);
2481#endif /* OS2 */
2482    return;
2483}
2484
2485/*  Z R E N A M E  --  Rename a file  */
2486/*
2487   Call with old and new names.
2488   If new name is the name of a directory, the 'old' file is moved to
2489   that directory.
2490   Returns 0 on success, -1 on failure.
2491*/
2492int
2493zrename(old,new) char *old, *new; {
2494    char *p = NULL, *s = new;
2495    int x;
2496
2497    debug(F110,"zrename old",old,0);
2498    debug(F110,"zrename new",s,0);
2499    if (isdir(new)) {
2500        char *q = NULL;
2501        x = strlen(new);
2502        if (!(p = malloc(strlen(new) + strlen(old) + 2)))
2503          return(-1);
2504        strcpy(p,new);                  /* Directory part */
2505        if (!ISDIRSEP(*(new+x-1)))      /* Separator, if needed */
2506          strcat(p,"/");
2507        zstrip(old,&q);                 /* Strip path part from old name */
2508        strcat(p,q);                    /* Concatenate to new directory */
2509        s = p;
2510        debug(F110,"zrename dir",s,0);
2511    } else debug(F110,"zrename no dir",s,0);
2512#ifdef RENAME
2513/*
2514  Atomic, preferred, uses a single system call, rename(), if available.
2515  OS/2 rename() returns nonzero, but not necessarily -1 (?), on failure.
2516*/
2517    x = rename(old,s);
2518    if (p) free(p);
2519    return(x ? -1 : 0);
2520#else /* !RENAME */
2521/*
2522  This way has a window of vulnerability.
2523*/
2524    x = -1;                             /* Return code. */
2525    if (link(old,s) < 0) {              /* Make a link with the new name. */
2526        debug(F111,"zrename link fails, errno",old,errno);
2527    } else if (
2528#ifdef NT
2529               _unlink(old)
2530#else
2531               unlink(old)
2532#endif /* NT */
2533               < 0) {   /* Unlink the old name. */
2534        debug(F111,"zrename unlink fails, errno",old,errno);
2535    } else x = 0;
2536    if (p) free(p);
2537    return(x);
2538#endif /* RENAME */
2539}
2540
2541/*  Z C O P Y  --  Copy a file  */
2542/*
2543   Call with source and destination names.
2544   If destination name is the name of a directory, the source file is
2545   copied to that directory with the original name.
2546   Returns 0 on success, -1 on failure.
2547*/
2548int
2549zcopy(source,destination) char *source, *destination; {
2550    char *p = NULL, *s = destination;
2551    int x;
2552   
2553    debug(F110,"zcopy source",source,0);
2554    debug(F110,"zcopy destination",s,0);
2555    if (isdir(destination)) {
2556        char *q = NULL;
2557        x = strlen(destination);
2558        if (!(p = malloc(strlen(destination) + strlen(source) + 2)))
2559          return(-1);
2560        strcpy(p,destination);          /* Directory part */
2561        if (!ISDIRSEP(*(destination+x-1))) /* Separator, if needed */
2562          strcat(p,"/");
2563        zstrip(source,&q);              /* Strip path part from old name */
2564        strcat(p,q);                    /* Concatenate to new directory */
2565        s = p;
2566        debug(F110,"zcopy dir",s,0);
2567    } else debug(F110,"zcopy no dir",s,0);
2568#ifdef OS2
2569#ifndef NT
2570    x = (DosCopy( source, destination, DCPY_FAILEAS ) ? -1 : 0);
2571#else /* NT */
2572    x = (CopyFile( source, destination, FALSE ) ? 0 : -1 );
2573#endif /* NT */
2574#else
2575    /* not yet implemented */
2576    x = -1; 
2577#endif
2578    return x;
2579}
2580
2581/*  Z S A T T R */
2582/*
2583 Fills in a Kermit file attribute structure for the file which is to be sent.
2584 Returns 0 on success with the structure filled in, or -1 on failure.
2585 If any string member is null, then it should be ignored.
2586 If any numeric member is -1, then it should be ignored.
2587*/
2588int
2589zsattr(xx) struct zattr *xx; {
2590    long k;
2591
2592    k = iflen % 1024L;                  /* File length in K */
2593    if (k != 0L) k = 1L;
2594    xx->lengthk = (iflen / 1024L) + k;
2595    xx->type.len = 0;                   /* File type can't be filled in here */
2596    xx->type.val = "";
2597    if (*nambuf) {
2598        xx->date.val = zfcdat(nambuf);  /* File creation date */
2599        xx->date.len = (int)strlen(xx->date.val);
2600    } else {
2601        xx->date.len = 0;
2602        xx->date.val = "";
2603    }
2604    xx->creator.len = 0;                /* File creator */
2605    xx->creator.val = "";
2606    xx->account.len = 0;                /* File account */
2607    xx->account.val = "";
2608    xx->area.len = 0;                   /* File area */
2609    xx->area.val = "";
2610    xx->password.len = 0;               /* Area password */
2611    xx->password.val = "";
2612    xx->blksize = -1L;                  /* File blocksize */
2613    xx->xaccess.len = 0;                /* File access */
2614    xx->xaccess.val = "";
2615    xx->encoding.len = 0;               /* Transfer syntax */
2616    xx->encoding.val = 0;
2617    xx->disp.len = 0;                   /* Disposition upon arrival */
2618    xx->disp.val = "";
2619    xx->lprotect.len = 0;               /* Local protection */
2620    xx->lprotect.val = "";
2621    xx->gprotect.len = 0;               /* Generic protection */
2622    xx->gprotect.val = "";
2623    xx->systemid.len = 2;               /* System ID */
2624#ifdef OS2
2625    xx->systemid.val = "UO";            /* UO = OS/2 */
2626#else
2627    xx->systemid.val = "U1";            /* U1 = UNIX */
2628#endif /* OS2 */
2629    xx->recfm.len = 0;                  /* Record format */
2630    xx->recfm.val = "";
2631    xx->sysparam.len = 0;               /* System-dependent parameters */
2632    xx->sysparam.val = "";
2633    xx->length = iflen;                 /* Length */
2634    return(0);
2635}
2636
2637/* Z F C D A T  --  Get file creation date */
2638/*
2639  Call with pointer to filename.
2640  On success, returns pointer to modification date in yyyymmdd hh:mm:ss format.
2641  On failure, returns pointer to null string.
2642*/
2643static char datbuf[40];
2644
2645char *
2646zdtstr(time) time_t time; {
2647    struct tm * time_stamp;
2648    struct tm * localtime();
2649    int yy, ss;
2650
2651    debug(F101,"zdatstr time","",time);
2652    if (time < 0)
2653      return("");
2654    time_stamp = localtime(&(time));
2655    if (!time_stamp) {
2656        debug(F100,"localtime returns null","",0);
2657        return("");
2658    }
2659    yy = time_stamp->tm_year;           /* Year - 1900 */
2660    yy += 1900;
2661    debug(F101,"zdatstr year","",yy);
2662    if (yy < 1970)                      /* By definition of C library */
2663      return("");
2664
2665    if (time_stamp->tm_mon  < 0 || time_stamp->tm_mon  > 11)
2666      return("");
2667    if (time_stamp->tm_mday < 0 || time_stamp->tm_mday > 31)
2668      return("");
2669    if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 23)
2670      return("");
2671    if (time_stamp->tm_min  < 0 || time_stamp->tm_min  > 59)
2672      return("");
2673    ss = time_stamp->tm_sec;            /* Seconds */
2674    if (ss < 0 || ss  > 59)             /* Some systems give a BIG number */
2675      ss = 0;
2676    sprintf(datbuf,
2677#ifdef pdp11
2678/* For some reason, 2.1x BSD sprintf gets the last field wrong. */
2679            "%04d%02d%02d %02d:%02d:00",
2680#else
2681            "%04d%02d%02d %02d:%02d:%02d",
2682#endif /* pdp11 */
2683            yy,
2684            time_stamp->tm_mon + 1,
2685            time_stamp->tm_mday,
2686            time_stamp->tm_hour,
2687            time_stamp->tm_min
2688#ifndef pdp11
2689            , ss
2690#endif /* pdp11 */
2691            );
2692    yy = (int)strlen(datbuf);
2693    debug(F111,"zdatstr",datbuf,yy);
2694    if (yy > 17) datbuf[17] = '\0';
2695    return(datbuf);
2696}
2697
2698char *
2699zfcdat(name) char *name; {
2700
2701#ifdef TIMESTAMP
2702#ifdef NT
2703    struct _stat buffer;
2704#else /* NT */
2705    struct stat buffer;
2706#endif /* NT */
2707
2708    datbuf[0] = '\0';
2709    if (stat(name,&buffer) != 0) {
2710        debug(F110,"zfcdat stat failed",name,0);
2711        return("");
2712    }
2713    return(zdtstr(buffer.st_mtime));
2714#else
2715    return("");
2716#endif /* TIMESTAMP */
2717}
2718
2719time_t
2720zstrdt(date,len) char * date; int len; {
2721/*
2722  To do: adapt code from OS-9 Kermit's ck9fio.c zstime function, which
2723  is more flexible, allowing [yy]yymmdd[ hh:mm[:ss]].
2724*/
2725#ifndef OS2
2726#ifdef M_UNIX
2727/*
2728  SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
2729  ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
2730  dependence on the XPG4 supplement presence.  So always use
2731  what the system header file supplies in ODT 3.0...
2732*/
2733#ifndef ODT30
2734#ifndef _SCO_DS
2735    extern void ftime();  /* extern void ftime(struct timeb *) */
2736#endif /* _SCO_DS */
2737#endif /* ODT30 */
2738#else
2739    extern int ftime();   
2740#endif /* M_UNIX */
2741    extern int stat();
2742    extern struct tm * localtime();
2743
2744    /* and this should have been declared always through a header file */
2745#endif /* OS2 */
2746    long tmx, days;
2747    int i, n, isleapyear;
2748                   /*       J  F  M  A   M   J   J   A   S   O   N   D   */
2749                   /*      31 28 31 30  31  30  31  31  30  31  30  31   */
2750    static
2751    int monthdays [13] = {  0,0,31,59,90,120,151,181,212,243,273,304,334 };
2752    char s[5];
2753    struct tm *time_stamp;
2754
2755#ifdef BSD44
2756    struct timeval tp[2];
2757    long xtimezone;
2758#else
2759#ifdef OS2
2760
2761#ifdef NT
2762struct _utimbuf tp;
2763#else /* NT */
2764struct utimbuf tp;
2765#endif /* NT */
2766
2767#ifdef __EMX__
2768    long timezone;
2769    struct timeb tbp;
2770#endif /* __EMX__ */
2771#else
2772#ifdef V7
2773    struct utimbuf {
2774      time_t timep[2];          /* New access and modificaton time */
2775    } tp;
2776    char *tz;
2777    long timezone;              /* In case timezone not defined in .h file */
2778#else
2779#ifdef SYSUTIMEH
2780    struct utimbuf tp;
2781#else
2782    struct utimbuf {
2783        time_t atime;
2784        time_t mtime;
2785    } tp;
2786#endif /* SYSUTIMEH */
2787#endif /* V7 */
2788#endif /* OS2 */
2789#endif /* BSD44 */
2790
2791#ifdef ANYBSD
2792    long timezone = 0L;
2793    static struct timeb tbp;
2794#endif /* ANYBSD */
2795
2796#ifdef BEBOX
2797    long timezone = 0L;
2798#endif /* BEBOX */
2799
2800    debug(F111,"zstrdt",date,len);
2801
2802    if ((len == 0)
2803        || (len != 17)
2804        || (date[8] != ' ')
2805        || (date[11] != ':')
2806        || (date[14] != ':') ) {
2807        debug(F111,"Bad creation date ",date,len);
2808        return(-1);
2809    }
2810    debug(F111,"zstime date check 1",date,len);
2811    for(i = 0; i < 8; i++) {
2812        if (!isdigit(date[i])) {
2813            debug(F111,"Bad creation date ",date,len);
2814            return(-1);
2815        }
2816    }
2817    debug(F111,"zstime date check 2",date,len);
2818    i++;
2819
2820    for (; i < 16; i += 3) {
2821        if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
2822            debug(F111,"Bad creation date ",date,len);
2823            return(-1);
2824        }
2825    }
2826    debug(F111,"zstime date check 3",date,len);
2827
2828#ifdef BSD44
2829    {
2830        int x;
2831        struct timezone tzp;
2832        x = gettimeofday(NULL, &tzp);
2833        debug(F101,"zstime BSD44 gettimeofday","",x);
2834        if (x > -1)
2835          xtimezone = tzp.tz_minuteswest * 60L;
2836        else
2837          xtimezone = 0L;
2838        debug(F101,"zstime BSD44 timezone","",xtimezone);
2839    }
2840#else
2841#ifdef ANYBSD
2842    debug(F100,"zstime BSD calling ftime","",0);
2843    ftime(&tbp);
2844    debug(F100,"zstime BSD back from ftime","",0);
2845    timezone = tbp.timezone * 60L;
2846    debug(F101,"zstime BSD timezone","",timezone);
2847#else
2848#ifdef OS2
2849#ifdef __EMX__
2850    ftime(&tbp);
2851    timezone = tbp.timezone * 60L;
2852#endif /* __EMX__ */
2853#else
2854#ifdef SVORPOSIX
2855    tzset();                            /* Set timezone */
2856#else
2857#ifdef V7
2858    if ((tz = getenv("TZ")) == NULL)
2859      timezone = 0;                     /* UTC/GMT */
2860    else
2861      timezone = atoi(&tz[3]);          /* Set 'timezone'. */
2862    timezone *= 60L;
2863#endif /* V7 */
2864#endif /* SVORPOSIX */
2865#endif /* OS2 */
2866#endif /* ANYBSD */
2867#endif /* BSD44 */
2868
2869    debug(F100,"zstime so far so good","",0);
2870
2871    s[4] = '\0';
2872    for (i = 0; i < 4; i++)             /* Fix the year */
2873      s[i] = date[i];
2874
2875    n = atoi(s);
2876    debug(F111,"zstime year",s,n);
2877    if (n < 1970) {
2878        debug(F100,"zstime fails - year","",n);
2879        return(-1);
2880    }
2881
2882/*  Previous year's leap days.  This won't work after year 2100. */
2883
2884    isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
2885    days = (long) (n - 1970) * 365;
2886    days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
2887
2888    s[2] = '\0';
2889
2890    for (i = 4; i < 16; i += 2) {
2891        s[0] = date[i];
2892        s[1] = date[i + 1];
2893        n = atoi(s);
2894        switch (i) {
2895          case 4:                       /* MM: month */
2896            if ((n < 1 ) || ( n > 12)) {
2897                debug(F111,"zstime 4 bad date ",date,len);
2898                return(-1);
2899            }
2900            days += monthdays [n];
2901            if (isleapyear && n > 2)
2902              ++days;
2903            continue;
2904
2905          case 6:                       /* DD: day */
2906            if ((n < 1 ) || ( n > 31)) {
2907                debug(F111,"zstime 6 bad date ",date,len);
2908                return(-1);
2909            }
2910            tmx = (days + n - 1) * 24L * 60L * 60L;
2911            i++;                        /* Skip the space */
2912            continue;
2913
2914          case 9:                       /* hh: hour */
2915            if ((n < 0 ) || ( n > 23)) {
2916                debug(F111,"zstime 9 bad date ",date,len);
2917                return(-1);
2918            }
2919            tmx += n * 60L * 60L;
2920            i++;                        /* Skip the colon */
2921            continue;
2922
2923          case 12:                      /* mm: minute */
2924            if ((n < 0 ) || ( n > 59)) {
2925                debug(F111,"zstime 12 bad date ",date,len);
2926                return(-1);
2927            }
2928#ifdef BSD44                            /* Correct for time zone */
2929            tmx += xtimezone;
2930            debug(F101,"zstime BSD44 tmx","",tmx);
2931#else
2932#ifdef ANYBSD
2933            tmx += timezone;
2934#else
2935#ifndef CONVEX9 /* Don't yet know how to do this here */
2936#ifdef ultrix
2937            tmx += (long) timezone;
2938#else
2939#ifdef Plan9
2940            {
2941                extern time_t tzoffset;
2942                tmx += tzoffset;
2943            }
2944#else
2945            tmx += timezone;
2946#endif /* Plan9 */
2947#endif /* ultrix */
2948#endif /* CONVEX9 */
2949#endif /* BSD44 */
2950#endif /* ANYBSD */
2951            tmx += n * 60L;
2952            i++;                        /* Skip the colon */
2953            continue;
2954
2955          case 15:                      /* ss: second */
2956            if ((n < 0 ) || ( n > 59)) {
2957                debug(F111,"zstime 15 bad date ",date,len);
2958                return(-1);
2959            }
2960            tmx += n;
2961        }
2962        time_stamp = localtime(&tmx);
2963        if (!time_stamp)
2964          return(-1);
2965        if (localtime(&tmx)->tm_isdst)
2966          tmx -= 60L * 60L;             /* Adjust for daylight savings time */
2967    }
2968    return(tmx);
2969}
2970
2971/* Z S T I M E  --  Set creation date for incoming file */
2972/*
2973 Call with:
2974 f  = pointer to name of existing file.
2975 yy = pointer to a Kermit file attribute structure in which yy->date.val
2976      is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00.
2977 x  = is a function code: 0 means to set the file's creation date as given.
2978      1 means compare the given date with the file creation date.
2979 Returns:
2980 -1 on any kind of error.
2981  0 if x is 0 and the file date was set successfully.
2982  0 if x is 1 and date from attribute structure <= file creation date.
2983  1 if x is 1 and date from attribute structure > file creation date.
2984*/
2985int
2986zstime(f,yy,x)
2987#ifdef HPUX10
2988#ifdef CK_ANSIC
2989  const
2990#endif /* CK_ANSIC */
2991#endif /* HPUX10 */
2992  char *f; struct zattr *yy; int x;
2993/* zstime */ {
2994    int r = -1;                         /* Return code */
2995
2996/* It is ifdef'd TIMESTAMP because it might not work on V7. bk@kullmar.se.  */
2997
2998#ifdef TIMESTAMP
2999#ifndef OS2
3000#ifdef BSD44
3001    extern int utimes();
3002#else
3003    extern int utime();
3004#endif /* BSD44 */
3005#endif /* OS2 */
3006
3007/* At least, the declarations for int functions are not needed anyway */
3008
3009#ifdef NT
3010    struct _stat sb;
3011#else /* NT */
3012    struct stat sb;
3013#endif /* NT */
3014
3015#ifdef BSD44
3016    struct timeval tp[2];
3017    long xtimezone;
3018#else
3019#ifdef OS2
3020
3021#ifdef NT
3022struct _utimbuf tp;
3023#else /* NT */
3024struct utimbuf tp;
3025#endif /* NT */
3026
3027#ifdef __EMX__
3028    long timezone;
3029    struct timeb tbp;
3030#endif /* __EMX__ */
3031#else
3032#ifdef V7
3033    struct utimbuf {
3034      time_t timep[2];                  /* New access and modificaton time */
3035    } tp;
3036    char *tz;
3037    long timezone;                      /* In case not defined in .h file */
3038#else
3039#ifdef SYSUTIMEH
3040    struct utimbuf tp;
3041#else
3042    struct utimbuf {
3043        time_t atime;
3044        time_t mtime;
3045    } tp;
3046#endif /* SYSUTIMEH */
3047#endif /* V7 */
3048#endif /* OS2 */
3049#endif /* BSD44 */
3050
3051    long tm;
3052
3053    debug(F110,"zstime",f,0);
3054    debug(F111,"zstime",yy->date.val,yy->date.len);
3055    if ((tm = zstrdt(yy->date.val,yy->date.len)) < 0) {
3056        debug(F101,"zstime: zstrdt fails","",0);
3057        return(-1);
3058    }
3059    debug(F101,"zstime: tm","",tm);
3060    debug(F111,"zstime: A-pkt date ok ",yy->date.val,yy->date.len);
3061
3062    if (stat(f,&sb)) {                  /* Get the time for the file */
3063        debug(F110,"zstime: Can't stat file:",f,0);
3064        return(-1);
3065    }
3066    debug(F101,"zstime: sb.st_atime","",sb.st_atime);
3067
3068#ifdef OS2
3069    tp.modtime = tm;                    /* Set modif. time to creation date */
3070    tp.actime = sb.st_atime;            /* Don't change the access time */
3071#else
3072#ifdef SYSUTIMEH
3073    tp.modtime = tm;                    /* Set modif. time to creation date */
3074    tp.actime = sb.st_atime;            /* Don't change the access time */
3075#else
3076#ifdef V7
3077    tp.timep[0] = tm;                   /* Set modif. time to creation date */
3078    tp.timep[1] = sb.st_atime;          /* Don't change the access time */
3079#else
3080#ifdef BSD44
3081    tp[0].tv_sec = sb.st_atime;         /* Access time first */
3082    tp[1].tv_sec = tm;                  /* Update time second */
3083#else
3084    tp.mtime = tm;                      /* Set modif. time to creation date */
3085    tp.atime = sb.st_atime;             /* Don't change the access time */
3086#endif /* BSD44 */
3087#endif /* V7 */
3088#endif /* SYSUTIMEH */
3089#endif /* OS2 */
3090
3091    switch (x) {                        /* Execute desired function */
3092      case 0:                           /* Set the creation date of the file */
3093        if (
3094#ifdef BSD44
3095            utimes(f,tp)
3096#else
3097#ifdef NT
3098            _utime(f,&tp)
3099#else /* NT */
3100            utime(f,&tp)
3101#endif /* NT */
3102#endif /* BSD44 */
3103            ) {         /* Fix modification time */
3104            debug(F110,"zstime 0: can't set modtime for file",f,0);
3105            r = -1;
3106        } else  {
3107            debug(F110,"zstime 0: modtime set for file",f,0);
3108            r = 0;
3109        }
3110        break;
3111      case 1:                           /* Compare the dates */
3112/*
3113  This was st_atime, which was wrong.  We want the file-data modification
3114  time, st_mtime.
3115*/
3116        debug(F111,"zstime 1: compare",f,sb.st_mtime);
3117        debug(F111,"zstime 1: compare","packet",tm);
3118#ifdef OS2
3119/*
3120  In OS/2, sb.st_mtime, at least on a FAT file system, is always even.
3121  In that case, if the incoming file is only one second newer than the
3122  local file, consider them the same.  (THERE MUST BE A BETTER FIX FOR THIS!)
3123*/
3124        if ((sb.st_mtime & 1) == 0)
3125          if ((tm - sb.st_mtime) == 1)
3126            tm--;
3127#endif /* OS2 */
3128        r = (sb.st_mtime < tm) ? 0 : 1;
3129        break;
3130
3131      default:                          /* Error */
3132        r = -1;
3133    }
3134#endif /* TIMESTAMP */
3135    return(r);
3136}
3137
3138/* Find initialization file. */
3139
3140#ifdef NOTUSED
3141int
3142zkermini() {
3143/*  nothing here for Unix.  This function added for benefit of VMS Kermit.  */
3144    return(0);
3145}
3146#endif /* NOTUSED */
3147
3148#ifndef NOFRILLS
3149int
3150zmail(p,f) char *p; char *f; {          /* Send file f as mail to address p */
3151/*
3152  Returns 0 on success
3153   2 if mail delivered but temp file can't be deleted
3154  -2 if mail can't be delivered
3155  The UNIX version always returns 0 because it can't get a good return
3156  code from zsyscmd.
3157*/
3158#ifdef BSD4
3159/* The idea is to use /usr/ucb/mail, rather than regular mail, so that   */
3160/* a subject line can be included with -s.  Since we can't depend on the */
3161/* user's path, we use the convention that /usr/ucb/Mail = /usr/ucb/mail */
3162/* and even if Mail has been moved to somewhere else, this should still  */
3163/* find it...  The search could be made more reliable by actually using  */
3164/* access() to see if /usr/ucb/Mail exists. */
3165
3166/* Should also make some check on zmbuf overflow... */
3167
3168#ifdef DGUX540
3169    sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
3170#else
3171    sprintf(zmbuf,"Mail -s %c%s%c %s < %s", '"', f, '"', p, f);
3172#endif /* DGUX540 */
3173    zsyscmd(zmbuf);
3174#else
3175#ifdef SVORPOSIX
3176#ifndef OXOS
3177    sprintf(zmbuf,"mail %s < %s", p, f);
3178#else /* OXOS */
3179    sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
3180#endif /* OXOS */
3181    zsyscmd(zmbuf);
3182#else
3183    *zmbuf = '\0';
3184#endif
3185#endif
3186    return(0);
3187}
3188#endif /* NOFRILLS */
3189
3190#ifndef NOFRILLS
3191int
3192zprint(p,f) char *p; char *f; {         /* Print file f with options p */
3193    extern char * printfile;            /* From ckuus3.c */
3194    extern int printpipe;
3195
3196    debug(F110,"zprint file",f,0);
3197    debug(F110,"zprint flags",p,0);
3198    debug(F110,"zprint printfile",printfile,0);
3199    debug(F101,"zprint printpipe","",printpipe);
3200
3201#ifdef NT
3202    if (printfile) {
3203        if (printpipe)
3204          sprintf(zmbuf,"COPY /B %s | %s", f, printfile);
3205        else
3206          sprintf(zmbuf,"COPY /B %s %s", f, printfile);
3207    } else {
3208        sprintf(zmbuf,"COPY /B %s PRN", f);
3209    }
3210    debug(F110,"zprint command",zmbuf,0);
3211    zsyscmd(zmbuf);
3212#else
3213#ifdef OS2
3214    if (printfile) {
3215        if (printpipe)
3216          sprintf(zmbuf,"COPY /B %s | %s", f, printfile);
3217        else
3218          sprintf(zmbuf,"COPY /B %s %s", f, printfile);
3219    } else {
3220        sprintf(zmbuf,"print %s %s", p, f); /* Construct print command */
3221    }
3222    debug(F110,"zprint command",zmbuf,0);
3223    zsyscmd(zmbuf);
3224#else
3225#ifdef UNIX
3226#ifdef ANYBSD                           /* BSD uses lpr to spool */
3227#ifdef DGUX540                          /* And DG/UX */
3228#define SPOOLER "lp"
3229#else
3230#define SPOOLER "lpr"
3231#endif /* DGUX540 */
3232#else                                   /* Sys V uses lp */
3233#ifdef TRS16                            /* except for Tandy-16/6000... */
3234#define SPOOLER "lpr"
3235#else
3236#define SPOOLER "lp"
3237#endif
3238#endif
3239/*
3240  Note use of standard input redirection.  In some systems, lp[r] runs
3241  setuid to lp (or ...?), so if user has sent a file into a directory
3242  that lp does not have read access to, it can't be printed unless it is
3243  fed to lp[r] as standard input.
3244*/
3245    if (printpipe && printfile) {
3246        sprintf(zmbuf,"cat %s | %s", f, printfile);
3247    } else if (printfile) {
3248        sprintf(zmbuf,"cat %s >> %s", f, printfile);
3249    } else {
3250        sprintf(zmbuf,"%s %s < %s", SPOOLER, p, f);
3251    }
3252    debug(F110,"zprint command",zmbuf,0);
3253    zsyscmd(zmbuf);
3254#else /* Not UNIX */
3255    *zmbuf = '\0';
3256#endif /* UNIX */
3257#endif /* OS2 */
3258#endif /* NT */
3259    return(0);
3260}
3261#endif /* NOFRILLS */
3262
3263/*
3264  Wildcard expansion functions.  C-Kermit used to insist on doing this itself
3265  New code (version 5A, 1990-91) gives user option to ask UNIX to do it.
3266  This lets users use the wildcard expansion features of their favorite shell.
3267  Operation is slower because of the forking & piping, but flexibility is
3268  greater and program is smaller.  For OS/2, C-Kermit still does this itself.
3269*/
3270static char scratch[MAXPATH+4];         /* Used by both methods */
3271
3272#ifndef OS2
3273static int oldmtchs = 0;                /* Let shell (ls) expand them. */
3274#ifdef COMMENT
3275static char *lscmd = "/bin/ls -d";      /* Command to use. */
3276#else
3277static char *lscmd = "echo";            /* Command to use. */
3278#endif /* COMMENT */
3279
3280#ifndef NOPUSH
3281int
3282shxpand(pat,namlst,len) char *pat, *namlst[]; int len; {
3283    char *fgbuf = NULL;                 /* Buffer for forming ls command */
3284    char *p, *q;                        /* Workers */
3285    int i, x, retcode; char c;          /* ... */
3286
3287    x = (int)strlen(pat) + (int)strlen(lscmd) + 3; /* Length of ls command */
3288    for (i = 0; i < oldmtchs; i++) {    /* Free previous file list */
3289        if (namlst[i] ) {               /* If memory is allocated  */
3290            free(namlst[i]);            /* Free the memory         */
3291            namlst[i] = NULL ;          /* Remember no memory is allocated */
3292        }
3293    }
3294    oldmtchs = 0 ;                      /* Remember there are no matches */
3295    fgbuf = malloc(x);                  /* Get buffer for command */
3296    if (!fgbuf) return(-1);             /* Fail if cannot */
3297    sprintf(fgbuf,"%s %s",lscmd,pat);   /* Form the command */
3298    zxcmd(ZIFILE,fgbuf);                /* Start the command */
3299    i = 0;                              /* File counter */
3300    p = scratch;                        /* Point to scratch area */
3301    retcode = -1;                       /* Assume failure */
3302    while ((x = zminchar()) != -1) {    /* Read characters from command */
3303        c = (char) x;
3304        if (c == ' ' || c == '\n') {    /* Got newline or space? */
3305            *p = '\0';                  /* Yes, terminate string */
3306            p = scratch;                /* Point back to beginning */
3307            if (zchki(p) == -1)         /* Does file exist? */
3308              continue;                 /* No, continue */
3309            x = (int)strlen(p);         /* Yes, get length of name */
3310            q = malloc(x+1);            /* Allocate space for it */
3311            if (!q) goto shxfin;        /* Fail if space can't be obtained */
3312            strcpy(q,scratch);          /* Copy name to space */
3313            namlst[i++] = q;            /* Copy pointer to name into array */
3314            if (i >= len) goto shxfin;  /* Fail if too many */
3315        } else {                        /* Regular character */
3316            *p++ = c;                   /* Copy it into scratch area */
3317        }
3318    }
3319    retcode = i;                        /* Return number of matching files */
3320shxfin:                                 /* Common exit point */
3321    free(fgbuf);                        /* Free command buffer */
3322    fgbuf = NULL;
3323    zclosf(ZIFILE);                     /* Delete the command fork. */
3324    oldmtchs = i;                       /* Remember how many files */
3325    return(retcode);
3326}
3327#endif /* NOPUSH */
3328#endif /* OS2 */
3329
3330/* Directory Functions for Unix, written by Jeff Damens, CUCCA, 1984. */
3331
3332/* Define the size of the string space for filename expansion. */
3333
3334#ifndef DYNAMIC
3335#ifdef PROVX1
3336#define SSPACE 500
3337#else
3338#ifdef BSD29
3339#define SSPACE 500
3340#else
3341#ifdef pdp11
3342#define SSPACE 500
3343#else
3344#ifdef aegis
3345#define SSPACE 10000                    /* size of string-generating buffer */
3346#else                                   /* Default static buffer size */
3347#ifdef BIGBUFOK
3348#define SSPACE 32768                    /* size of string-generating buffer */
3349#else
3350#define SSPACE 2000                     /* size of string-generating buffer */
3351#endif /* BIGBUFOK */
3352#endif /* aegis */
3353#endif /* pdp11 */
3354#endif /* BSD29 */
3355#endif /* PROVX1 */
3356static char sspace[SSPACE];             /* Buffer for generating filenames */
3357#else /* is DYNAMIC */
3358#ifdef BIGBUFOK
3359#define SSPACE 32768
3360#else
3361#define SSPACE 10000
3362#endif /* BIGBUFOK */
3363static char *sspace = (char *)0;
3364#endif /* DYNAMIC */
3365static int ssplen = SSPACE;             /* Length of string space buffer */
3366
3367static char *freeptr, **resptr;         /* copies of caller's arguments */
3368static int remlen;                      /* remaining length in caller's array*/
3369static int numfnd;                      /* number of matches found */
3370
3371#ifdef aegis
3372static char bslash;
3373#endif /* aegis */
3374
3375/*
3376 * splitpath:
3377 *  takes a string and splits the slash-separated portions into
3378 *  a list of path structures.  Returns the head of the list.  The
3379 *  structures are allocated by malloc, so they must be freed.
3380 *  Splitpath is used internally by the filename generator.
3381 *
3382 * Input: A string.
3383 * Returns: A linked list of the slash-separated segments of the input.
3384 */
3385
3386struct path *
3387splitpath(p) char *p; {
3388    struct path *head,*cur,*prv;
3389    int i;
3390
3391    debug(F110,"splitpath",p,0);
3392
3393    head = prv = NULL;
3394    if (ISDIRSEP(*p)) p++;                      /* skip leading slash */
3395#ifdef OS2
3396    if ( *p == '\0' ) {    /* the user wants the root directory */
3397        cur = (struct path *) malloc(sizeof(struct path));
3398        debug(F101,"splitpath malloc","",cur);
3399            if (cur == NULL) {
3400                debug(F100,"splitpath malloc failure","",0);
3401            return((struct path *)NULL);
3402            }
3403        cur->fwd = NULL;
3404        cur->npart[0] = '.';
3405        cur->npart[1] = '\0';
3406        head = cur;
3407        return head;
3408        }
3409#endif /* OS2 */
3410    while (*p != '\0') {
3411        cur = (struct path *) malloc(sizeof (struct path));
3412        debug(F101,"splitpath malloc","",cur);
3413        if (cur == NULL) {
3414            debug(F100,"splitpath malloc failure","",0);
3415            return((struct path *)NULL);
3416        }
3417        cur -> fwd = NULL;
3418        if (head == NULL)
3419          head = cur;
3420        else
3421          prv -> fwd = cur;             /* link into chain */
3422        prv = cur;
3423#ifdef aegis
3424        /* treat backslash as "../" */
3425        if (bslash && *p == bslash) {
3426            strcpy(cur->npart, "..");
3427            ++p;
3428        } else {
3429            for (i=0; i < MAXNAMLEN && *p && *p != '/' && *p != bslash; i++)
3430              cur -> npart[i] = *p++;
3431            cur -> npart[i] = '\0';     /* end this segment */
3432            if (i >= MAXNAMLEN)
3433              while (*p && *p != '/' && *p != bslash)
3434                p++;
3435        }
3436        if (*p == '/') p++;
3437#else
3438#ifdef OS2
3439        for (i = 0;
3440             i < MAXNAMLEN && !ISDIRSEP(*p) && *p != ':' && *p != '\0';
3441             i++ )
3442            cur -> npart[i] = *p++;
3443        if ( *p == ':' ) {
3444            cur -> npart[i++] = *p++;
3445            if ( !ISDIRSEP(*p) )
3446                cur -> npart[i++] = '.';
3447        }
3448#else
3449        for (i=0; i < MAXNAMLEN && !ISDIRSEP(*p) && *p != '\0'; i++) {
3450            cur -> npart[i] = *p++;
3451        }
3452#endif /* OS2 */
3453        cur -> npart[i] = '\0';         /* end this segment */
3454        if (i >= MAXNAMLEN)
3455          while (!ISDIRSEP(*p) && *p != '\0') p++;
3456        if (ISDIRSEP(*p))
3457          p++;
3458
3459#endif /* aegis */
3460    }
3461    return(head);
3462}
3463
3464/*
3465 * fgen:
3466 *  This is the actual name generator.  It is passed a string,
3467 *  possibly containing wildcards, and an array of character pointers.
3468 *  It finds all the matching filenames and stores pointers to them in the
3469 *  array.  The returned strings are allocated from a static buffer local to
3470 *  this module (so the caller doesn't have to worry about deallocating
3471 *  them); this means that successive calls to fgen will wipe out
3472 *  the results of previous calls.  This isn't a problem here
3473 *  because we process one wildcard string at a time.
3474 *
3475 * Input: a wildcard string, an array to write names to, the
3476 *        length of the array.
3477 * Returns: the number of matches.  The array is filled with filenames
3478 *          that matched the pattern.  If there wasn't enough room in the
3479 *          array, -1 is returned.
3480 * Originally by: Jeff Damens, CUCCA, 1984.  Many changes since then.
3481 */
3482static int
3483fgen(pat,resarry,len) char *pat,*resarry[]; int len; {
3484    struct path *head;
3485    char *sptr;
3486#ifdef aegis
3487    char *namechars;
3488    int tilde = 0, bquote = 0;
3489
3490    if ((namechars = getenv("NAMECHARS")) != NULL) {
3491        if (xindex(namechars, '~' ) != NULL) tilde  = '~';
3492        if (xindex(namechars, '\\') != NULL) bslash = '\\';
3493        if (xindex(namechars, '`' ) != NULL) bquote = '`';
3494    } else {
3495        tilde = '~'; bslash = '\\'; bquote = '`';
3496    }
3497    sptr = scratch;
3498
3499    /* copy "`node_data", etc. anchors */
3500    if (bquote && *pat == bquote)
3501      while (*pat && *pat != '/' && *pat != bslash)
3502        *sptr++ = *pat++;
3503    else if (tilde && *pat == tilde)
3504      *sptr++ = *pat++;
3505    while (*pat == '/')
3506      *sptr++ = *pat++;
3507    if (sptr == scratch) {
3508        strcpy(scratch,"./");
3509        sptr = scratch+2;
3510    }                                   /* init buffer correctly */
3511    if (!(head = splitpath(pat))) return(-1);
3512#else /* not aegis */
3513    debug(F110,"fgen pat",pat,0);
3514#ifdef OS2
3515    head = splitpath(pat);
3516#else /* not OS2 */
3517    if (!(head = splitpath(pat))) return(-1);
3518#endif /* OS2 */
3519    sptr = scratch;
3520    if (!ISDIRSEP(*pat))
3521      *sptr++ = '.';                    /* init buffer correctly */
3522    *sptr++ = DIRSEP;
3523#ifdef OS2
3524    if (isalpha(pat[0]) && pat[1] == ':')
3525        sptr = scratch;                 /* reset in case of leading drive: */
3526#endif /* OS2 */
3527#endif /* aegis */
3528    numfnd = 0;                         /* none found yet */
3529#ifdef DYNAMIC
3530    if (!sspace) {                      /* Need to allocate string space? */
3531        while (ssplen > 50) {
3532            if ((sspace = malloc(ssplen+2))) { /* Got it. */
3533                debug(F101,"fgen string space","",ssplen);
3534                break;
3535            }
3536            ssplen = (ssplen / 2) + (ssplen / 4); /* Didn't, reduce by 3/4 */
3537        }
3538        if (ssplen <= 50) {             /* Did we get it? */
3539            fprintf(stderr,"fgen can't malloc string space\n");
3540            return(-1);
3541        }
3542    }
3543#endif /* DYNAMIC */
3544    freeptr = sspace;                   /* this is where matches are copied */
3545    resptr = resarry;                   /* static copies of these so */
3546    remlen = len;                       /* recursive calls can alter them */
3547    traverse(head,scratch,sptr);        /* go walk the directory tree */
3548#ifdef COMMENT
3549/*
3550  This code, circa 1984, has never worked right - it references the head
3551  pointer after it has already been freed.  Lord knows what might have been
3552  happening because of this.  Thanks to Steve Walton for finding & fixing
3553  this bug.
3554*/
3555    for (; head != NULL; head = head -> fwd) {
3556        free(head);                     /* return the path segments */
3557        head = NULL;
3558    }
3559#else
3560    while (head != NULL) {
3561        struct path *next = head -> fwd;
3562        free(head);
3563        head = next;
3564    }
3565#endif /* COMMENT */
3566    return(numfnd);                     /* and return the number of matches */
3567}
3568
3569/* traverse:
3570 *  Walks the directory tree looking for matches to its arguments.
3571 *  The algorithm is, briefly:
3572 *   If the current pattern segment contains no wildcards, that
3573 *   segment is added to what we already have.  If the name so far
3574 *   exists, we call ourselves recursively with the next segment
3575 *   in the pattern string; otherwise, we just return.
3576 *
3577 *   If the current pattern segment contains wildcards, we open the name
3578 *   we've accumulated so far (assuming it is really a directory), then read
3579 *   each filename in it, and, if it matches the wildcard pattern segment, add
3580 *   that filename to what we have so far and call ourselves recursively on the
3581 *   next segment.
3582 *
3583 *   Finally, when no more pattern segments remain, we add what's accumulated
3584 *   so far to the result array and increment the number of matches.
3585 *
3586 * Input: a pattern path list (as generated by splitpath), a string
3587 *        pointer that points to what we've traversed so far (this
3588 *        can be initialized to "/" to start the search at the root
3589 *        directory, or to "./" to start the search at the current
3590 *        directory), and a string pointer to the end of the string
3591 *        in the previous argument.
3592 * Returns: nothing.
3593 */
3594static VOID
3595traverse(pl,sofar,endcur) struct path *pl; char *sofar, *endcur; {
3596
3597/* Define LONGFN (long file names) automatically for BSD 2.9 and 4.2 */
3598/* LONGFN can also be defined on the cc command line. */
3599
3600#ifdef BSD29
3601#ifndef LONGFN
3602#define LONGFN
3603#endif
3604#endif
3605
3606#ifdef BSD42
3607#ifndef LONGFN
3608#define LONGFN
3609#endif
3610#endif
3611
3612/* Appropriate declarations for directory routines and structures */
3613/* #define OPENDIR means to use opendir(), readdir(), closedir()  */
3614/* If OPENDIR not defined, we use open(), read(), close() */
3615
3616#ifdef DIRENT                           /* New way, <dirent.h> */
3617#define OPENDIR
3618    DIR *fd, *opendir();
3619    struct dirent *dirbuf;
3620    struct dirent *readdir();
3621#else /* !DIRENT */
3622#ifdef LONGFN                           /* Old way, <dir.h> with opendir() */
3623#define OPENDIR
3624    DIR *fd, *opendir();
3625    struct direct *dirbuf;
3626#else /* !LONGFN */
3627    int fd;                             /* Old way, <dir.h> with open() */
3628    struct direct dir_entry;
3629    struct direct *dirbuf = &dir_entry;
3630#endif /* LONGFN */
3631#endif /* DIRENT */
3632
3633#ifdef NT
3634    struct _stat statbuf;               /* for file info */
3635#else /* NT */
3636    struct stat statbuf;                /* for file info */
3637#endif /* NT */
3638
3639    if (pl == NULL) {
3640        *--endcur = '\0';               /* end string, overwrite trailing / */
3641        addresult(sofar);
3642        return;
3643    }
3644    if (!iswild(pl -> npart)) {
3645        strcpy(endcur,pl -> npart);
3646        endcur += (int)strlen(pl -> npart);
3647        *endcur = '\0';                 /* end current string */
3648        if (stat(sofar,&statbuf) == 0) { /* if current piece exists */
3649#ifdef OS2
3650            if (endcur - sofar == 3 && endcur[-1] == '.' && endcur[-2] == ':')
3651              endcur--;
3652            else
3653#endif /* OS2 */
3654              *endcur++ = DIRSEP;       /* add slash to end */
3655            *endcur = '\0';             /* and end the string */
3656            traverse(pl -> fwd,sofar,endcur);
3657        }
3658        return;
3659    }
3660
3661    /* Segment contains wildcards, have to search directory */
3662
3663    *endcur = '\0';                             /* end current string */
3664    if (stat(sofar,&statbuf) == -1) return;     /* doesn't exist, forget it */
3665    if (!S_ISDIR (statbuf.st_mode)) return;     /* not a directory, skip */
3666
3667#ifdef OPENDIR
3668    if ((fd = opendir(sofar)) == NULL) return; /* Can't open, fail. */
3669    while (dirbuf = readdir(fd))
3670#else /* !OPENDIR */
3671    if ((fd = open(sofar,O_RDONLY)) < 0) return; /* Can't open, fail. */
3672    while (read(fd, (char *)dirbuf, sizeof dir_entry))
3673#endif /* OPENDIR */
3674      {
3675          /* Get null-terminated copy!!! */
3676          strncpy(nambuf,dirbuf->d_name,MAXNAMLEN);
3677          nambuf[MAXNAMLEN] = '\0';
3678#ifdef unos
3679          if (dirbuf->d_ino != -1 && match(pl -> npart,nambuf))
3680#else
3681/* #ifdef _POSIX_SOURCE */
3682/*
3683  Directory reading is not specified in POSIX.1.  POSIX.2 gives us glob() and
3684  fnmatch(), which are not yet supported by C-Kermit.  Meanwhile, maybe POSIX
3685  implementations should force "set wildcard shell" and remove all of this
3686  code.
3687*/
3688#ifdef QNX
3689          if (dirbuf->d_stat.st_ino != 0 && match(pl -> npart,nambuf))
3690#else
3691#ifdef SOLARIS
3692          if (dirbuf->d_ino != 0 && match(pl -> npart,nambuf))
3693#else
3694#ifdef sun
3695          if (dirbuf->d_fileno != 0 && match(pl -> npart,nambuf))
3696#else
3697#ifdef bsdi
3698          if (dirbuf->d_fileno != 0 && match(pl -> npart,nambuf))
3699#else
3700#ifdef __386BSD__
3701          if (dirbuf->d_fileno != 0 && match(pl -> npart,nambuf))
3702#else
3703#ifdef __FreeBSD__
3704          if (dirbuf->d_fileno != 0 && match(pl -> npart,nambuf))
3705#else
3706#ifdef ultrix
3707          if (dirbuf->gd_ino != 0 && match(pl -> npart,nambuf))
3708#else
3709#ifdef Plan9
3710          if (match(pl->npart,nambuf))
3711#else
3712          if (dirbuf->d_ino != 0 && match(pl -> npart,nambuf))
3713#endif /* Plan9 */
3714#endif /* ultrix */
3715#endif /* __FreeBSD__ */
3716#endif /* __386BSD__ */
3717#endif /* bsdi */
3718#endif /* sun */
3719#endif /* SOLARIS */
3720#endif /* QNX */
3721
3722/* #else */ /* not _POSIX_SOURCE */
3723/*        if (dirbuf->d_ino != 0 && match(pl -> npart,nambuf)) */
3724/* #endif */ /* _POSIX_SOURCE */
3725
3726#endif /* unos */
3727          {
3728              char *eos;
3729              strcpy(endcur,nambuf);
3730              eos = endcur + (int)strlen(nambuf);
3731              *eos++ = DIRSEP;          /* end this segment */
3732              traverse(pl -> fwd,sofar,eos);
3733          }
3734      }
3735#ifdef OPENDIR
3736    closedir(fd);
3737#else /* !OPENDIR */
3738    close(fd);
3739#endif /* OPENDIR */
3740}
3741
3742/*
3743 * addresult:
3744 *  Adds a result string to the result array.  Increments the number
3745 *  of matches found, copies the found string into our string
3746 *  buffer, and puts a pointer to the buffer into the caller's result
3747 *  array.  Our free buffer pointer is updated.  If there is no
3748 *  more room in the caller's array, the number of matches is set to -1.
3749 * Input: a result string.
3750 * Returns: nothing.
3751 */
3752static VOID
3753addresult(str) char *str; {
3754    int l;
3755    debug(F111,"addresult",str,remlen);
3756    if (str[0] == '.' && ISDIRSEP(str[1])) str += 2; /* (===OS2 change===) */
3757    if (--remlen < 0) {
3758        numfnd = -1;
3759        return;
3760    }
3761    l = (int)strlen(str) + 1;           /* size this will take up */
3762    if ((freeptr + l) > (sspace + ssplen)) {
3763        numfnd = -1;                    /* do not record if not enough space */
3764        return;
3765    }
3766    strcpy(freeptr,str);
3767    *resptr++ = freeptr;
3768    freeptr += l;
3769    numfnd++;
3770}
3771
3772/*
3773 * match:
3774 *  pattern matcher.  Takes a string and a pattern possibly containing
3775 *  the wildcard characters '*' and '?'.  Returns true if the pattern
3776 *  matches the string, false otherwise.
3777 * by: Jeff Damens, CUCCA, 1984
3778 * skipping over dot files and backslash quoting added by fdc, 1990.
3779 *
3780 * Input: a string and a wildcard pattern.
3781 * Returns: 1 if match, 0 if no match.
3782 */
3783static int
3784match(pattern,string) char *pattern,*string; {
3785    char *psave,*ssave;                 /* back up pointers for failure */
3786    int q = 0;                          /* quote flag */
3787
3788    debug(F110,"match str",string,0);
3789    psave = ssave = NULL;
3790#ifndef MATCHDOT
3791    if (*string == '.' && *pattern != '.') {
3792        debug(F110,"match skip",string,0);
3793        return(0);
3794    }
3795#endif
3796    while (1) {
3797#ifdef OS2
3798        for (; tolower(*pattern) == tolower(*string); pattern++,string++)
3799#else
3800        for (; *pattern == *string; pattern++,string++)  /* skip first */
3801#endif /* OS2 */
3802            if (*string == '\0') return(1);     /* end of strings, succeed */
3803
3804        if (*pattern == '\\' && q == 0) { /* Watch out for quoted */
3805            q = 1;                      /* metacharacters */
3806            pattern++;                  /* advance past quote */
3807            if (*pattern != *string) return(0);
3808            continue;
3809        } else q = 0;
3810
3811        if (q) {
3812            return(0);
3813        } else {
3814            if (*string != '\0' && *pattern == '?') {
3815                pattern++;              /* '?', let it match */
3816                string++;
3817            } else if (*pattern == '*') { /* '*' ... */
3818                psave = ++pattern;      /* remember where we saw it */
3819                ssave = string;         /* let it match 0 chars */
3820            } else if (ssave != NULL && *ssave != '\0') { /* if not at end  */
3821                                        /* ...have seen a star */
3822                string = ++ssave;       /* skip 1 char from string */
3823                pattern = psave;        /* and back up pattern */
3824            } else return(0);           /* otherwise just fail */
3825        }
3826    }
3827}
3828
3829/*
3830  The following two functions are for expanding tilde in filenames
3831  Contributed by Howie Kaye, CUCCA, developed for CCMD package.
3832*/
3833
3834/*  W H O A M I  --  Get user's username.  */
3835
3836/*
3837  1) Get real uid
3838  2) See if the $USER environment variable is set ($LOGNAME on AT&T)
3839  3) If $USER's uid is the same as ruid, realname is $USER
3840  4) Otherwise get logged in user's name
3841  5) If that name has the same uid as the real uid realname is loginname
3842  6) Otherwise, get a name for ruid from /etc/passwd
3843*/
3844static char *
3845whoami () {
3846#ifdef DTILDE
3847#ifdef pdp11
3848#define WHOLEN 100
3849#else
3850#define WHOLEN 257
3851#endif /* pdp11 */
3852    static char realname[256];          /* user's name */
3853    static int ruid = -1;               /* user's real uid */
3854    char loginname[256], envname[256];  /* temp storage */
3855    char *c;
3856    struct passwd *p;
3857    _PROTOTYP(extern char * getlogin, (void) );
3858
3859    if (ruid != -1)
3860      return(realname);
3861
3862    ruid = real_uid();                  /* get our uid */
3863
3864  /* how about $USER or $LOGNAME? */
3865    if ((c = getenv(NAMEENV)) != NULL) { /* check the env variable */
3866        strcpy (envname, c);
3867        if ((p = getpwnam(envname)) != NULL) {
3868            if (p->pw_uid == ruid) {    /* get passwd entry for envname */
3869                strcpy (realname, envname); /* if the uid's are the same */
3870                return(realname);
3871            }
3872        }
3873    }
3874
3875  /* can we use loginname() ? */
3876
3877    if ((c =  getlogin()) != NULL) {    /* name from utmp file */
3878        strcpy (loginname, c);
3879        if ((p = getpwnam(loginname)) != NULL) /* get passwd entry */
3880          if (p->pw_uid == ruid) {      /* for loginname */
3881              strcpy (realname, loginname); /* if the uid's are the same */
3882              return(realname);
3883          }
3884    }
3885
3886  /* Use first name we get for ruid */
3887
3888    if ((p = getpwuid(ruid)) == NULL) { /* name for uid */
3889        realname[0] = '\0';             /* no user name */
3890        ruid = -1;
3891        return(NULL);
3892    }
3893    strcpy (realname, p->pw_name);
3894    return(realname);
3895#else
3896    return(NULL);
3897#endif /* DTILDE */
3898}
3899
3900/*  T I L D E _ E X P A N D  --  expand ~user to the user's home directory. */
3901
3902char *
3903tilde_expand(dirname) char *dirname; {
3904#ifdef DTILDE
3905#ifdef pdp11
3906#define BUFLEN 100
3907#else
3908#define BUFLEN 257
3909#endif /* pdp11 */
3910    struct passwd *user;
3911    static char olddir[BUFLEN];
3912    static char oldrealdir[BUFLEN];
3913    static char temp[BUFLEN];
3914    int i, j;
3915
3916    debug(F111,"tilde_expand",dirname,dirname[0]);
3917
3918    if (dirname[0] != '~')              /* Not a tilde...return param */
3919      return(dirname);
3920    if (!strcmp(olddir,dirname)) {      /* Same as last time */
3921      return(oldrealdir);               /* so return old answer. */
3922    } else {
3923        j = (int)strlen(dirname);
3924        for (i = 0; i < j; i++)         /* find username part of string */
3925          if (!ISDIRSEP(dirname[i]))
3926            temp[i] = dirname[i];
3927          else break;
3928        temp[i] = '\0';                 /* tie off with a NULL */
3929        if (i == 1) {                   /* if just a "~" */
3930            user = getpwnam(whoami());  /*  get info on current user */
3931        } else {
3932            user = getpwnam(&temp[1]);  /* otherwise on the specified user */
3933        }
3934    }
3935    if (user != NULL) {                 /* valid user? */
3936        strcpy(olddir, dirname);        /* remember the directory */
3937        strcpy(oldrealdir,user->pw_dir); /* and their home directory */
3938        strcat(oldrealdir,&dirname[i]);
3939        return(oldrealdir);
3940    } else {                            /* invalid? */
3941        strcpy(olddir, dirname);        /* remember for next time */
3942        strcpy(oldrealdir, dirname);
3943        return(oldrealdir);
3944    }
3945#else
3946    return(NULL);
3947#endif /* DTILDE */
3948}
3949
3950/*
3951  Functions for executing system commands.
3952  zsyscmd() executes the system command in the normal, default way for
3953  the system.  In UNIX, it does what system() does.  Thus, its results
3954  are always predictable.
3955  zshcmd() executes the command using the user's preferred shell.
3956*/
3957int
3958zsyscmd(s) char *s; {
3959#ifdef aegis
3960    if (!priv_chk()) return(system(s));
3961#else
3962#ifdef OS2
3963    /*
3964        We must set the priority back to normal.  Otherwise all of children
3965        processes are going to inherit our FOREGROUNDSERVER priority
3966        and that would not be good for the system or ourselves.
3967    */
3968
3969   if (!priv_chk()) {
3970       ULONG rc = 0;
3971#ifndef KUI
3972       char title[80];
3973       title[0] = '\0';
3974       os2gettitle( title, 80 );
3975       RequestScreenMutex(SEM_INDEFINITE_WAIT);
3976#endif /* KUI */
3977       KbdHandlerCleanup();
3978       ResetThreadPrty();
3979       rc = system(s);
3980       SetThreadPrty(priority,15);
3981       KbdHandlerInit();
3982#ifndef KUI
3983       if (
3984#ifndef NOSPL
3985        cmdlvl == 0
3986#else
3987        tlevel < 0
3988#endif /* NOSPL */
3989        )
3990           OS2WaitForKey();
3991       ReleaseScreenMutex();
3992       os2settitle( title, FALSE );
3993#endif /* KUI */
3994       return rc;
3995   }
3996   return 0;
3997#else
3998    PID_T shpid;
3999#ifdef COMMENT
4000/* This doesn't work... */
4001    WAIT_T status;
4002#else
4003    int status;
4004#endif /* COMMENT */
4005
4006    if (shpid = fork()) {
4007        if (shpid < (PID_T)0) return(-1); /* Parent */
4008        while (shpid != (PID_T) wait(&status))
4009         ;
4010        return(status);
4011    }
4012    if (priv_can()) {                   /* Child: cancel any priv's */
4013        printf("?Privilege cancellation failure\n");
4014        _exit(255);
4015    }
4016#ifdef HPUX10
4017    execl("/usr/bin/sh","sh","-c",s,NULL);
4018    perror("/usr/bin/sh");
4019#else
4020#ifdef Plan9
4021    execl("/bin/rc", "rc", "-c", s, NULL);
4022    perror("/bin/rc");
4023#else
4024    execl("/bin/sh","sh","-c",s,NULL);
4025    perror("/bin/sh");
4026#endif /* Plan9 */
4027#endif /* HPUX10 */
4028    _exit(255);
4029    return(0);                          /* Shut up ANSI compilers. */
4030#endif /* OS2 */
4031#endif /* aegis */
4032}
4033
4034/*
4035  UNIX code by H. Fischer; copyright rights assigned to Columbia Univ.
4036  Adapted to use getpwuid to find login shell because many systems do not
4037  have SHELL in environment, and to use direct calling of shell rather
4038  than intermediate system() call. -- H. Fischer
4039  Call with s pointing to command to execute.
4040*/
4041
4042int
4043zshcmd(s) char *s; {
4044    PID_T pid;
4045
4046#ifdef OS2
4047   int rc;
4048   char title[80];
4049#ifdef NT
4050   SIGTYP (* savint)(int);
4051#endif /* NT */
4052   char *shell = getenv("SHELL");
4053    if ( !shell )
4054       shell = getenv("COMSPEC");
4055
4056    if (!priv_chk()) {
4057#ifndef KUI
4058        os2gettitle( title, 80 );
4059        RequestScreenMutex(SEM_INDEFINITE_WAIT);
4060#endif /* KUI */
4061        KbdHandlerCleanup();
4062        ResetThreadPrty();
4063#ifdef NT
4064        savint = signal( SIGINT, SIG_IGN );
4065#endif /* NT */
4066
4067        if (!s || *s == '\0')
4068          rc = system(shell) == 0;      /* was _spawnlp(P_WAIT, shell, NULL) */
4069        else
4070          rc = system(s) == 0;
4071#ifdef NT
4072        signal( SIGINT, savint );
4073#endif /* NT */
4074
4075        SetThreadPrty(priority,15);
4076        KbdHandlerInit();
4077#ifndef KUI
4078        if (
4079#ifndef NOSPL
4080        cmdlvl == 0
4081#else  /* NOSPL */
4082        tlevel < 0
4083#endif /* NOSPL */
4084        )
4085            OS2WaitForKey();
4086        ReleaseScreenMutex();
4087        os2settitle( title, FALSE );
4088#endif /* KUI */
4089        return(rc);
4090    }
4091    return(0);
4092#else /* OS2 */
4093#ifdef AMIGA
4094    if (!priv_chk()) system(s);
4095#else
4096#ifdef datageneral
4097    if (priv_chk) return(1);
4098    if (*s == '\0')                     /* Interactive shell requested? */
4099#ifdef mvux
4100        system("/bin/sh ");
4101#else
4102        system("x :cli prefix Kermit_Baby:");
4103#endif /* mvux */
4104    else                                /* Otherwise, */
4105        system(s);                      /* Best for aos/vs?? */
4106
4107#else
4108#ifdef aegis
4109    if ((pid = vfork()) == 0) {         /* Make child quickly */
4110        char *shpath, *shname, *shptr;  /* For finding desired shell */
4111
4112        if (priv_can()) exit(1);        /* Turn off privs. */
4113        if ((shpath = getenv("SHELL")) == NULL) shpath = "/com/sh";
4114
4115#else                                   /* All Unix systems */
4116    if ((pid = fork()) == 0) {          /* Make child */
4117        char *shpath, *shname, *shptr;  /* For finding desired shell */
4118        struct passwd *p;
4119#ifdef HPUX10                           /* Default */
4120        char *defshell = "/usr/bin/sh";
4121#else
4122#ifdef Plan9
4123        char *defshell = "/bin/rc";
4124#else
4125        char *defshell = "/bin/sh";
4126#endif /* Plan9 */
4127#endif /* HPUX10 */
4128        if (priv_can()) exit(1);        /* Turn off privs. */
4129#ifdef COMMENT
4130/* Old way always used /etc/passwd shell */
4131        p = getpwuid(real_uid());       /* Get login data */
4132        if (p == (struct passwd *) NULL || !*(p->pw_shell))
4133          shpath = defshell;
4134        else
4135          shpath = p->pw_shell;
4136#else
4137/* New way lets user override with SHELL variable, but does not rely on it. */
4138/* This allows user to specify a different shell. */
4139        shpath = getenv("SHELL");       /* What shell? */
4140        if (shpath == NULL) {
4141            p = getpwuid( real_uid() ); /* Get login data */
4142            if (p == (struct passwd *)NULL || !*(p->pw_shell))
4143              shpath = defshell;
4144            else shpath = p->pw_shell;
4145        }
4146#endif /* COMMENT */
4147#endif /* aegis */
4148        shptr = shname = shpath;
4149        while (*shptr != '\0')
4150          if (*shptr++ == DIRSEP)
4151            shname = shptr;
4152        if (s == NULL || *s == '\0') {  /* Interactive shell requested? */
4153            execl(shpath,shname,"-i",NULL); /* Yes, do that */
4154        } else {                        /* Otherwise, */
4155            execl(shpath,shname,"-c",s,NULL); /* exec the given command */
4156        }                               /* If execl() failed, */
4157        exit(BAD_EXIT);                 /* return bad return code. */
4158
4159    } else {                            /* Parent */
4160
4161        int wstat;                      /* ... must wait for child */
4162        int child;          /* Child's exit status */
4163        SIGTYP (*istat)(), (*qstat)();
4164
4165        if (pid == (PID_T) -1) return(-1); /* fork() failed? */
4166
4167        istat = signal(SIGINT,SIG_IGN); /* Let the fork handle keyboard */
4168        qstat = signal(SIGQUIT,SIG_IGN); /* interrupts itself... */
4169
4170#ifdef UNIX
4171#define CK_CHILD                        /* Assume this is safe in UNIX */
4172#endif /* UNIX */
4173
4174#ifdef CK_CHILD
4175        while (((wstat = wait(&child)) != pid) && (wstat != -1))
4176#else
4177        while (((wstat = wait((WAIT_T *)0)) != pid) && (wstat != -1))
4178#endif /* CK_CHILD */
4179          ;                             /* Wait for fork */
4180        signal(SIGINT,istat);           /* Restore interrupts */
4181        signal(SIGQUIT,qstat);
4182#ifdef CK_CHILD
4183        return(child == 0 ? 1 : 0);     /* Return child's status */
4184#endif /* CK_CHILD */
4185    }
4186#endif
4187#endif
4188#endif /* OS2 */
4189    return(1);
4190}
4191
4192#ifdef aegis
4193/*
4194 Replacement for strchr() and index(), neither of which seem to be universal.
4195*/
4196static char *
4197#ifdef CK_ANSIC
4198xindex(char * s, char c)
4199#else
4200xindex(s,c) char *s, c;
4201#endif /* CK_ANSIC */
4202/* xindex */ {
4203    while (*s != '\0' && *s != c) s++;
4204    if (*s == c) return(s); else return(NULL);
4205}
4206#endif /* aegis */
4207
4208/*  I S W I L D  --  Check if filespec is "wild"  */
4209
4210/*
4211  Returns 0 if it is a single file, 1 if it contains wildcard characters.
4212  Note: must match the algorithm used by match(), hence no [a-z], etc.
4213*/
4214int
4215iswild(filespec) char *filespec; {
4216    char c; int x; char *p;
4217    if (wildxpand) {
4218        if ((x = zxpand(filespec)) > 1) return(1);
4219        if (x == 0) return(0);          /* File does not exist */
4220        p = malloc(MAXNAMLEN + 20);
4221        znext(p);
4222#ifdef OS2
4223        x = (
4224#ifdef NT
4225             _stricmp(filespec,p)
4226#else
4227             stricmp(filespec,p)
4228#endif /* NT */
4229             != 0);
4230#else
4231        x = (strcmp(filespec,p) != 0);
4232#endif /* OS2 */
4233        free(p);
4234        p = NULL;
4235        return(x);
4236    } else {
4237        while ((c = *filespec++) != '\0')
4238          if (c == '*' || c == '?') return(1);
4239        return(0);
4240    }
4241}
4242
4243/*
4244   Tell if string pointer s is the name of an existing directory.
4245   Returns 1 if directory, 0 if not a directory.
4246*/
4247int
4248isdir(s) char *s; {
4249    int x;
4250#ifdef NT
4251    struct _stat statbuf;
4252#else /* NT */
4253    struct stat statbuf;
4254#endif /* NT */
4255
4256
4257    if (!s) return(0);
4258    if (!*s) return(0);
4259
4260#ifdef OS2
4261    /* Disk letter like A: is top-level directory on a disk */
4262    if (((int)strlen(s) == 2) && (isalpha(*s)) && (*(s+1) == ':'))
4263      return(1);
4264#endif /* OS2 */
4265    x = stat(s,&statbuf);
4266    debug(F111,"isdir stat",s,x);
4267    if (x == -1) {
4268        debug(F101,"isdir errno","",errno);
4269        return(0);
4270    } else {
4271        debug(F101,"isdir statbuf.st_mode","",statbuf.st_mode);
4272        return( S_ISDIR (statbuf.st_mode) ? 1 : 0 );
4273    }
4274}
4275
4276#ifdef CK_MKDIR
4277/* Some systems don't have mkdir(), e.g. Tandy Xenix 3.2.. */
4278
4279/* Z M K D I R  --  Create directory(s) if necessary */
4280/*
4281   Call with:
4282    A pointer to a file specification that might contain directory
4283    information.  The filename is expected to be included.
4284    If the file specification does not include any directory separators,
4285    then it is assumed to be a plain file.
4286    If one or more directories are included in the file specification,
4287    this routine tries to create them if they don't already exist.
4288   Returns:
4289    0 on success, i.e. the directory was created
4290   -1 on failure to create the directory
4291*/
4292int
4293zmkdir(path) char *path; {
4294    char *xp, *tp, c;
4295    int x;
4296
4297    x = strlen(path);
4298    debug(F111,"zmkdir",path,x);
4299    if (x < 1 || x > MAXPATH)           /* Check length */
4300      return(-1);
4301    if (!(tp = malloc(x+1)))            /* Make a temporary copy */
4302      return(-1);
4303    strcpy(tp,path);
4304#ifdef DTILDE
4305    if (*tp == '~') {                   /* Starts with tilde? */
4306        xp = tilde_expand(tp);          /* Attempt to expand tilde */
4307        if (*xp) {
4308            char *zp;
4309            debug(F110,"zmkdir tilde_expand",xp,0);
4310            if (!(zp = malloc(strlen(xp) + 1))) { /* Make a place for it */
4311                free(tp);
4312                tp = NULL;
4313                return(-1);
4314            }
4315            free(tp);                   /* Free previous buffer */
4316            tp = zp;                    /* Point to new one */
4317            strcpy(tp,xp);              /* Copy expanded name to new buffer */
4318        }
4319        debug(F110,"zmkdir tp after tilde_expansion",tp,0);
4320    }
4321#endif /* DTILDE */
4322    xp = tp;
4323    if (ISDIRSEP(*xp))                  /* Don't create root directory! */
4324      xp++;
4325
4326    /* Go thru filespec from left to right... */
4327
4328    for (; *xp; xp++) {                 /* Create parts that don't exist */
4329        if (!ISDIRSEP(*xp))             /* Find next directory separator */
4330          continue;
4331        c = *xp;                        /* Got one. */
4332        *xp = NUL;                      /* Make this the end of the string. */
4333        if (!isdir(tp)) {               /* This directory exists already? */
4334            debug(F110,"zmkdir making",tp,0);
4335            x =                         /* No, try to create it */
4336#ifdef NOMKDIR
4337               -1                       /* Systems without mkdir() */
4338#else
4339#ifdef OS2                              /* OS/2 */
4340              _mkdir(tp)                /* The IBM way */
4341#else
4342               mkdir(tp,0777)           /* UNIX */
4343#endif /* OS2 */
4344#endif /* NOMKDIR */
4345                 ;
4346            if (x < 0) {
4347                debug(F101,"zmkdir failed, errno","",errno);
4348                free(tp);               /* Free temporary buffer. */
4349                tp = NULL;
4350                return(-1);             /* Freturn failure code. */
4351            }
4352        }
4353        *xp = c;                        /* Replace the separator. */
4354    }
4355    free(tp);                           /* Free temporary buffer. */
4356    return(0);                          /* Return success code. */
4357}
4358#endif /* CK_MKDIR */
4359
4360/* Z F S E E K  --  Position input file pointer */
4361/*
4362   Call with:
4363    Long int, 0-based, indicating desired position.
4364   Returns:
4365    0 on success.
4366   -1 on failure.
4367*/
4368#ifndef NORESEND
4369int
4370#ifdef CK_ANSIC
4371zfseek(long pos)
4372#else
4373zfseek(pos) long pos;
4374#endif /* CK_ANSIC */
4375/* zfseek */ {
4376#ifdef NT
4377    fpos_t fpos = pos ;
4378#endif /* NT */
4379 
4380    zincnt = -1 ;               /* must empty the input buffer */
4381
4382    debug(F101,"zfseek","",pos);
4383#ifdef NT
4384    return(fsetpos(fp[ZIFILE], &fpos)?-1:0);
4385#else
4386    return(fseek(fp[ZIFILE], pos, 0)?-1:0);
4387#endif
4388}
4389#endif /* NORESEND */
4390
4391struct zfnfp *
4392zfnqfp(fname, buflen, buf)  char * fname; int buflen; char * buf; {
4393    int x = 0, y = 0;
4394    char * xp;
4395    static struct zfnfp fnfp;
4396
4397    if (!fname)
4398      return(NULL);
4399
4400    /* initialize the data structure */
4401    fnfp.len = buflen;
4402    fnfp.fpath = buf;
4403    fnfp.fname = NULL;
4404
4405#ifdef OS2
4406#ifdef NT
4407    if ( GetFullPathName( fname, buflen, fnfp.fpath, &fnfp.fname ) )
4408      return(&fnfp);
4409    else
4410#else /* NT */
4411      if (!DosQueryPathInfo(fname,5 /* Full Path Info */, fnfp.fpath,buflen)) {
4412          x = strlen(fnfp.fpath);
4413          for (x = x + 1; x >= 0; x--)  /* Find where the filename starts */
4414            if (fnfp.fpath[x] == '/'
4415                || fnfp.fpath[x] == '\\'
4416                )                       /* There is guaranteed to be one */
4417              fnfp.fname = fnfp.fpath + x; /* Got it, set pointer */
4418          return(&fnfp);                /* and return. */
4419      } else
4420#endif /* NT */
4421#else /* OS2 */
4422
4423#ifdef DTILDE
4424        if (*fname == '~') {            /* Starts with tilde? */
4425            xp = tilde_expand(fname);   /* Attempt to expand tilde */
4426            if (*xp)
4427              fname = xp;
4428        }
4429#endif /* DTILDE */
4430
4431        if (*fname == '/'
4432            ) {                         /* Pathname is absolute */
4433            strncpy(buf,fname,buflen);
4434            x = strlen(buf);
4435            y = 0;
4436        } else {                        /* Pathname is relative */
4437            char * p;
4438            if (p = zgtdir()) {         /* So get current directory */
4439                strncpy(buf,p,buflen);
4440                x = strlen(buf);           
4441                buf[x++] = '/';
4442                buflen -= x;            /* How much room left in buffer */
4443                if ((y = (int)strlen(fname)) > buflen) /* If enough room... */
4444                  return(NULL);
4445                strncpy(buf+x,fname,buflen); /* ... append the filename */
4446            } else {
4447                return(NULL);
4448            }
4449        }
4450
4451    for (x = x + y - 1; x > -1; x--)    /* Find where the filename starts */
4452      if (buf[x] == '/'
4453          ) {                           /* There is guaranteed to be one */
4454          fnfp.fname = buf + x;         /* Got it, set pointer */
4455          return(&fnfp);                /* and return. */
4456      }
4457#endif /* OS2 */
4458    return(NULL);
4459}
4460
4461#ifdef OS2
4462
4463/*  Z C H D S K  --  Change currently selected disk device */
4464
4465/* Returns -1 if error, otherwise 0 */
4466
4467zchdsk(c) int c; {
4468    int i = toupper(c) - 64;
4469    return( _chdrive(i));
4470}
4471
4472#undef stat
4473#ifdef __IBMC__STAT
4474#define stat(p, s) _stat(p, s)
4475#endif /* __IBMC__STAT */
4476
4477os2stat(char *path,
4478#ifdef NT
4479        struct _stat *st
4480#else
4481        struct stat *st
4482#endif /* NT */
4483        ) {
4484    char local[MAXPATH];
4485    int len;
4486
4487    strcpy(local, path);
4488    len = strlen(local);
4489
4490    if ( len == 2 && local[1] == ':' )
4491        local[2] = DIRSEP, local[3] = 0; /* if drive only, append / */
4492    else if ( len == 0 )
4493        local[0] = DIRSEP, local[1] = 0; /* if empty path, take / instead */
4494    else if ( len > 1 && ISDIRSEP(local[len - 1]) && local[len - 2] != ':' )
4495        local[len - 1] = 0; /* strip trailing / except after d: */
4496
4497#ifdef NT
4498    return _stat(local, st);
4499#else /* NT */
4500    return stat(local, st);
4501#endif /* NT */
4502}
4503
4504#ifdef CK_LABELED
4505/* O S 2 S E T L O N G N A M E -- Set .LONGNAME Extended Attribute */
4506
4507/* Returns -1 if error, otherwise 0 */
4508
4509int
4510os2setlongname( char * fn, char * ln ) {
4511   EAOP2         FileInfoBuf;
4512   ULONG         FileInfoSize;
4513   ULONG         PathInfoFlags;
4514   APIRET        rc;
4515   char          FEA2List[1024];
4516   FEA2 *        pfea2;
4517   WORD *        pEAdata;
4518
4519   debug(F110,"os2setlongname: filename is",fn,0);
4520   debug(F110,"os2setlongname: longname is",ln,0);
4521
4522   FileInfoSize = sizeof( EAOP2 );
4523   PathInfoFlags = DSPI_WRTTHRU; /* complete write operation before return */
4524
4525   FileInfoBuf.fpGEA2List = 0;
4526   FileInfoBuf.fpFEA2List = (PFEA2LIST) &FEA2List;
4527   pfea2 = FileInfoBuf.fpFEA2List->list;
4528
4529   pfea2->oNextEntryOffset = 0;
4530   pfea2->fEA = 0;
4531   pfea2->cbName = 9; /* length of ".LONGNAME" without \0 */
4532   pfea2->cbValue = strlen( ln ) + 2 * sizeof( WORD );
4533   strcpy( pfea2->szName, ".LONGNAME" );
4534
4535   pEAdata = (WORD *) pfea2->szName + 10/sizeof(WORD);
4536   *pEAdata = EAT_ASCII;
4537   pEAdata++;
4538   *pEAdata = strlen( ln );
4539   pEAdata++;
4540   strcpy( (char *) pEAdata, ln );
4541   pEAdata += (strlen( ln )+1)/sizeof(WORD);
4542
4543   FileInfoBuf.fpFEA2List->cbList = (char *) pEAdata -
4544                                    (char *) FileInfoBuf.fpFEA2List;
4545     
4546   rc = DosSetPathInfo( fn, 2, &FileInfoBuf, FileInfoSize, PathInfoFlags );
4547   debug(F101,"os2setlongname: rc=","",rc);
4548   if ( !rc )
4549      return 0;
4550   else
4551      return -1;
4552}
4553
4554/* O S 2 G E T L O N G N A M E -- Get .LONGNAME Extended Attribute */
4555
4556/* Returns -1 if error, otherwise 0 */
4557
4558int
4559os2getlongname( char * fn, char ** ln ) {
4560   static char   * LongNameBuf = 0;
4561   EAOP2         FileInfoBuf;
4562   ULONG         FileInfoSize;
4563   ULONG         PathInfoFlags;
4564   APIRET        rc;
4565   char          FEA2List[1024];
4566   FEA2 *        pfea2;
4567   char          GEA2List[1024];
4568   GEA2 *        pgea2;
4569   WORD *        pEAdata;
4570   WORD          LongNameLength;
4571
4572   *ln = 0;
4573   if ( !LongNameBuf )
4574      LongNameBuf = strdup( "Initialization of LongNameBuf" );
4575   debug(F110,"os2getlongname: filename is",fn,0);
4576
4577   FileInfoSize = sizeof( EAOP2 );
4578   PathInfoFlags = DSPI_WRTTHRU; /* Complete write operation before return */
4579
4580   FileInfoBuf.fpGEA2List = (PGEA2LIST) &GEA2List;
4581   FileInfoBuf.fpFEA2List = (PFEA2LIST) &FEA2List;
4582   pgea2 = FileInfoBuf.fpGEA2List->list;
4583   pfea2 = FileInfoBuf.fpFEA2List->list;
4584
4585   pfea2->oNextEntryOffset = 0;
4586   pfea2->fEA = 0;
4587   pfea2->cbName = 9;                   /* Length of ".LONGNAME" without \0 */
4588   pfea2->cbValue = MAXPATH;
4589   strcpy( pfea2->szName, ".LONGNAME" );
4590
4591   FileInfoBuf.fpGEA2List->cbList = sizeof(GEA2LIST)
4592                                  + pgea2->cbName + 1;
4593   pgea2->oNextEntryOffset = 0;
4594   pgea2->cbName = pfea2->cbName;
4595   strcpy(pgea2->szName,pfea2->szName);
4596
4597   FileInfoBuf.fpFEA2List->cbList = 1024;
4598
4599   rc = DosQueryPathInfo(fn,FIL_QUERYEASFROMLIST,&FileInfoBuf,FileInfoSize );
4600   LongNameLength =
4601     *(WORD *)((char *)pfea2 + sizeof(FEA2) + pfea2->cbName + sizeof(WORD));
4602   debug(F101,"os2getlongname: rc=","",rc);
4603   debug(F101,"   cbValue:","",pfea2->cbValue);
4604   debug(F101,"   cbName:","",pfea2->cbName);
4605   debug(F101,"   EA Value Length:","",LongNameLength );
4606   debug(F110,"   EA Value:",(char *)pfea2 + sizeof(FEA2)
4607                             + pfea2->cbName + (2 * sizeof(WORD)),0 );
4608   if ( rc ) {
4609       return -1;
4610   } else if ( pfea2->cbValue ) {
4611       if (LongNameBuf) {
4612           free(LongNameBuf);
4613           LongNameBuf = NULL;
4614       }
4615       LongNameBuf = (char *) malloc( LongNameLength + 1 );
4616       if (LongNameBuf) {
4617           strncpy(LongNameBuf, (char *)pfea2 + sizeof(FEA2)
4618                   + pfea2->cbName + (2 * sizeof(WORD)),
4619                   LongNameLength
4620                   );
4621           LongNameBuf[LongNameLength] = '\0';
4622           debug(F110,"os2getlongname: longname is",LongNameBuf,0);
4623       } else
4624         debug(F100,"os2getlongname: malloc failed","",0);
4625   } else {
4626       if ( LongNameBuf )
4627         free( LongNameBuf );
4628       LongNameBuf = strdup( "" );
4629       debug(F110,
4630             "os2getlongname: there is no longname attribute",
4631             LongNameBuf,
4632             0
4633             );
4634   }
4635   *ln = LongNameBuf;
4636   return 0;
4637}
4638
4639
4640_PROTOTYP( VOID GetMem, (PVOID *, ULONG) );
4641
4642/* O S 2 G E T E A S - Get all OS/2 Extended Attributes */
4643
4644/* Returns 0 on success, -1 on failure */
4645
4646int
4647os2geteas( char * name ) {
4648    CHAR *pAllocc=NULL; /* Holds the FEA struct returned by DosEnumAttribute */
4649                        /*  used to create the GEA2LIST for DosQueryPathInfo */
4650
4651    ULONG ulEntryNum = 1; /* count of current EA to read (1-relative)        */
4652    ULONG ulEnumCnt;      /* Number of EAs for Enum to return, always 1      */
4653
4654    ULONG FEAListSize = sizeof(ULONG);/* starting size of buffer necessary to
4655                                                        hold all FEA blocks */
4656    ULONG GEAListSize = MAXEACOUNT * sizeof(GEA2) + sizeof(ULONG);
4657
4658    FEA2 *pFEA;           /* Used to read from Enum's return buffer          */
4659    GEA2 *pGEA, *pLastGEAIn; /* Used to write to pGEAList buffer             */
4660    GEA2LIST *pGEAList;/*Ptr used to set up buffer for DosQueryPathInfo call */
4661    EAOP2  eaopGet;       /* Used to call DosQueryPathInfo                   */
4662    APIRET rc;
4663    int offset;
4664
4665                                /* Allocate enough room for any GEA List */
4666    GetMem((PPVOID)&pAllocc, MAX_GEA);
4667    pFEA = (FEA2 *) pAllocc;               /* pFEA always uses pAlloc buffer */
4668
4669    GetMem((PPVOID)&pGEAList, GEAListSize ); /* alloc buffer for GEA2 list */
4670    pGEAList->cbList = GEAListSize;
4671    pGEA = pGEAList->list;
4672    pLastGEAIn = 0;
4673
4674    if ( !pAllocc || ! pGEAList ) {
4675        FreeMem( pAllocc );
4676        FreeMem( pGEAList );
4677        return -1;
4678    }
4679    if ( pFEAList ) {
4680        FreeMem( pFEAList );
4681        pFEAList = 0;
4682   }
4683   while(TRUE) {              /* Loop continues until there are no more EAs */
4684       ulEnumCnt = 1;                   /* Only want to get one EA at a time */
4685       if(DosEnumAttribute(Ref_ASCIIZ,            /* Read into pAlloc Buffer */
4686                           name,                  /* Note that this does not */
4687                           ulEntryNum,            /* get the aValue field,   */
4688                           pAllocc,               /* so DosQueryPathInfo must*/
4689                           MAX_GEA,               /* be called to get it.    */
4690                           &ulEnumCnt,
4691                           (LONG) GetInfoLevel1)) {
4692           FreeMem(pGEAList);           /* There was some sort of error */
4693           FreeMem(pAllocc);
4694           return (-1);
4695       }
4696       if(ulEnumCnt != 1)               /* All the EAs have been read */
4697         break;
4698
4699       ulEntryNum++;
4700       FEAListSize += sizeof(FEA2LIST) + pFEA->cbName+1 +
4701                      pFEA->cbValue + 4;
4702
4703       if (pLastGEAIn)
4704         pLastGEAIn->oNextEntryOffset = (BYTE *)pGEA - (BYTE *)pLastGEAIn;
4705       pLastGEAIn = pGEA;
4706
4707       pGEA->oNextEntryOffset = 0L;
4708       pGEA->cbName = pFEA->cbName;
4709       strcpy(pGEA->szName, pFEA->szName);
4710     
4711       /* must align GEA2 blocks on double word boundaries */
4712       offset = sizeof(GEA2) + pGEA->cbName + 1;
4713       offset += ( offset % 4 ? (4 - offset % 4) : 0 );
4714       pGEA = (GEA2 *) ((BYTE *) pGEA + offset);
4715   }
4716   debug(F111,"os2geteas: EA count",name,ulEntryNum-1);
4717   GetMem( (PPVOID) &pFEAList, FEAListSize );
4718   pFEAList->cbList = FEAListSize;
4719
4720   eaopGet.fpGEA2List = pGEAList;
4721   eaopGet.fpFEA2List = pFEAList;
4722
4723   rc = DosQueryPathInfo(name,                   /* Get the complete EA info */
4724                   GetInfoLevel3,
4725                   (PVOID) &eaopGet,
4726                    sizeof(EAOP2));
4727
4728   debug(F111,"os2geteas: DosQueryPathInfo",name,rc);
4729   FreeMem( pGEAList );
4730   FreeMem( pAllocc );
4731   return ( rc ? -1 : 0 );
4732}
4733
4734/* O S 2 S E T E A S - Set all OS/2 Extended Attributes */
4735
4736/* Returns 0 on success, -1 on failure                  */
4737
4738int
4739os2seteas( char * name ) {
4740   EAOP2  eaopSet;       /* Used to call DosSetPathInfo */
4741   APIRET rc;
4742
4743   if ( !pFEAList ) {
4744     debug(F100,"os2seteas: EA List is empty","",0);
4745     return 0;
4746   }
4747   eaopSet.fpGEA2List = 0;
4748   eaopSet.fpFEA2List = pFEAList;
4749
4750   rc = DosSetPathInfo(name,            /* Set the EA info */
4751                    SetInfoLevel2,
4752                   (PVOID) &eaopSet,
4753                    sizeof(EAOP2),
4754                    DSPI_WRTTHRU);
4755   debug(F111,"os2seteas: DosSetPathInfo",name,rc);
4756
4757   if ( !rc ) {
4758      FreeMem( pFEAList );
4759      pFEAList = 0;
4760   }
4761   return ( rc ? -1 : 0 );
4762}
4763
4764/* O S 2 G E T A T T R - Get all OS/2 Normal Attributes */
4765
4766/* Returns 0 on success, -1 on failure                  */
4767
4768int
4769os2getattr( char * name ) {
4770   FILESTATUS3 FileInfoBuf;
4771   APIRET rc;
4772
4773   rc = DosQueryPathInfo(name,                   /* Get the complete EA info */
4774                   GetInfoLevel1,
4775                   (PVOID) &FileInfoBuf,
4776                    sizeof(FILESTATUS3));
4777
4778   if ( !rc ) {
4779      os2attrs = FileInfoBuf.attrFile;
4780      return 0;
4781      }
4782   else {
4783      os2attrs = FILE_NORMAL;
4784      return -1;
4785      }
4786}
4787
4788/* O S 2 S E T A T T R - Set all OS/2 Normal Attributes */
4789
4790/* Returns 0 on success, -1 on failure                  */
4791
4792int
4793os2setattr( char * name ) {
4794   FILESTATUS3 FileInfoBuf;
4795   APIRET rc;
4796
4797   rc = DosQueryPathInfo(name,                   /* Get the complete EA info */
4798                   GetInfoLevel1,
4799                   (PVOID) &FileInfoBuf,
4800                    sizeof(FILESTATUS3));
4801
4802   if ( !rc ) {
4803      FileInfoBuf.attrFile = lf_opts & os2attrs;
4804      rc = DosSetPathInfo( name,
4805                   GetInfoLevel1,
4806                   (PVOID) &FileInfoBuf,
4807                   sizeof(FILESTATUS3),
4808                   0);
4809      if ( !rc )
4810         return 0;
4811      }
4812   return -1;
4813}
4814
4815/****************************************************************\
4816 *                                                              *
4817 *  Name:    GetMem (ppv, cb)                                   *
4818 *                                                              *
4819 *  Purpose: This routine returns a pointer to a available*     *
4820 *           memory space.                                      *
4821 *                                                              *
4822 *  Usage  :                                                    *
4823 *  Method : Routine should be bullet proof as it does its own  *
4824 *           error checking. It assumes that hwnd points to the *
4825 *           correct window with the name listbox in it.        *
4826 *                                                              *
4827 *  Returns: The current EA as determined by querying the l-box *
4828 *           selector.                                          *
4829 *                                                              *
4830\****************************************************************/
4831VOID GetMem (PVOID *ppv, ULONG cb) {
4832    BOOL        f;
4833
4834    f =(BOOL) DosAllocMem(ppv, cb, fPERM|PAG_COMMIT);
4835    if (f) {
4836        *ppv = NULL;
4837        return;
4838    }
4839    return;
4840}
4841
4842extern CHAR os2version[50];
4843extern long vernum;
4844
4845/* D O _ L A B E L _ S E N D - Generate Label Header from EA List     */
4846
4847/* Return 0 on success, -1 on failure                                 */
4848
4849int
4850do_label_send(char * name) {
4851    char scratch[100];
4852    long buffreespace = INBUFSIZE;
4853    long easleft = 0;
4854    long memtocpy = 0;
4855    static BYTE * p = 0;
4856
4857    debug(F110,"do_label_send",name,0);
4858    if ( !pFEAList ) {
4859    debug(F101,"   no EA list","",pFEAList);
4860      return 0;
4861    }
4862
4863    if ( !p ) {
4864       debug(F100,"do_label_send: generate header","",0);
4865       zinptr += sprintf(zinptr,"KERMIT LABELED FILE:02UO04VERS");
4866
4867       sprintf(scratch,"%d",strlen(get_os2_vers()));
4868       zinptr += sprintf(zinptr,"%02d%d%s",
4869          strlen(scratch),
4870                         strlen(get_os2_vers()),
4871          get_os2_vers());
4872
4873       sprintf(scratch,"%d",vernum);
4874       zinptr += sprintf(zinptr,"05KVERS02%02d%d", strlen(scratch), vernum);
4875
4876       sprintf(scratch,"%d",strlen(name));
4877       zinptr += sprintf(zinptr,"08FILENAME%02d%d%s", strlen(scratch),
4878          strlen(name), name);
4879
4880       zinptr += sprintf(zinptr,"04ATTR%02d",sizeof(ULONG));
4881       memcpy( zinptr, (BYTE *) &os2attrs, sizeof(ULONG) );
4882       zinptr += sizeof(ULONG);
4883
4884       sprintf( scratch, "%d", pFEAList->cbList );
4885       zinptr += sprintf(zinptr,"09EABUFSIZE%02d%ld", strlen(scratch),
4886                pFEAList->cbList);
4887       p = (BYTE *) pFEAList;
4888       buffreespace -= (BYTE *) zinptr - (BYTE *) zinbuffer;
4889    }
4890
4891    easleft = pFEAList->cbList - ( (BYTE *) p - (BYTE *) pFEAList );
4892    memtocpy = buffreespace > easleft ? easleft : buffreespace;
4893    memcpy( zinptr, p, memtocpy );
4894    zinptr = (BYTE *) zinptr + memtocpy;
4895    p = (BYTE *) p + memtocpy;
4896    buffreespace -= memtocpy;
4897
4898    if ( buffreespace > 0 ) {
4899       p = 0;
4900       FreeMem( pFEAList );
4901       pFEAList = 0;
4902       debug(F100,"do_label_send: terminate header","",0);
4903    }
4904    zincnt = (zinptr - zinbuffer);              /* Size of this beast */
4905    return(0);
4906}
4907
4908/* D O _ L A B E L _ R E C V - Receive label info and create EA List  */
4909
4910/* Return 0 on success, -1 on failure */
4911
4912int
4913do_label_recv() {
4914    char *recv_ptr = zoutbuffer;
4915    int lblen;
4916    char buffer[16];
4917    size_t memtocpy, lefttocpy;
4918    static BYTE * pFEA = 0;
4919
4920    if ( !pFEAList
4921         && strncmp(zoutbuffer,"KERMIT LABELED FILE:02UO04VERS",30) != 0) {
4922        debug(F100,"do_label_recv: not a labeled file","",0);
4923        return(0);                      /* Just continue if unlabeled */
4924    }
4925
4926    if ( !pFEAList ) {
4927       recv_ptr += 30;                  /* start at front of buffer */
4928       zoutcnt  -= 30;
4929
4930       /* get length of length of OS/2 version */
4931       memcpy(buffer, recv_ptr, 2);
4932       recv_ptr += 2;
4933       zoutcnt  -= 2;
4934       buffer[2] = '\0';
4935       lblen = atoi(buffer);
4936
4937       /* get length of OS/2 version */
4938       memcpy(buffer, recv_ptr, lblen);
4939       recv_ptr += lblen;
4940       zoutcnt  -= lblen;
4941       buffer[lblen] = '\0';
4942       lblen = atoi(buffer);
4943
4944       /* get os2 version */
4945       memcpy(buffer, recv_ptr, lblen);
4946       recv_ptr += lblen;
4947       zoutcnt  -= lblen;
4948       buffer[lblen] = '\0';
4949       debug(F111,"  file created under OS/2: ",buffer,lblen);
4950
4951       /* check sync with Kermit Version */
4952       memcpy(buffer, recv_ptr, 7);
4953       recv_ptr += 7;
4954       zoutcnt  -= 7;
4955       if (strncmp(buffer, "05KVERS", 7) != 0) {
4956           debug(F111,"  lost sync at KVERS",recv_ptr-7,zoutcnt+7);
4957           return(-1);
4958       }
4959
4960       /* get length of length of C-Kermit version */
4961       memcpy(buffer, recv_ptr, 2);
4962       recv_ptr += 2;
4963       zoutcnt  -= 2;
4964       buffer[2] = '\0';
4965       lblen = atoi(buffer);
4966
4967       /* get length of C-Kermit version */
4968       memcpy(buffer, recv_ptr, lblen);
4969       recv_ptr += lblen;
4970       zoutcnt  -= lblen;
4971       buffer[lblen] = '\0';
4972       lblen = atoi(buffer);
4973
4974       /* get C-Kermit version */
4975       memcpy(buffer, recv_ptr, lblen);
4976       recv_ptr += lblen;
4977       zoutcnt  -= lblen;
4978       buffer[lblen] = '\0';
4979       debug(F111,"  file created with OS/2 C-Kermit: ",buffer,lblen);
4980
4981       /* check sync with FILENAME */
4982       memcpy(buffer, recv_ptr, 10);
4983       recv_ptr += 10;
4984       zoutcnt  -= 10;
4985       if (strncmp(buffer, "08FILENAME", 10) != 0) {
4986                debug(F111,"  lost sync at FILENAME",recv_ptr-10,zoutcnt+10);
4987                return(-1);
4988       }
4989
4990       /* get length of length of Filename */
4991       memcpy(buffer, recv_ptr, 2);
4992       recv_ptr += 2;
4993       zoutcnt  -= 2;
4994       buffer[2] = '\0';
4995       lblen = atoi(buffer);
4996
4997       /* get length of File Name */
4998       memcpy(buffer, recv_ptr, lblen);
4999       recv_ptr += lblen;
5000       zoutcnt  -= lblen;
5001       buffer[lblen] = '\0';
5002       lblen = atoi(buffer);
5003
5004       /* get File Name */
5005       memcpy(buffer, recv_ptr, lblen);
5006       recv_ptr += lblen;
5007       zoutcnt  -= lblen;
5008       buffer[lblen] = '\0';
5009       debug(F111,"  file sent with name: ",buffer,lblen);
5010
5011       /* check sync with ATTR */
5012       memcpy(buffer, recv_ptr, 6);
5013       recv_ptr += 6;
5014       zoutcnt  -= 6;
5015       if (strncmp(buffer, "04ATTR", 6) != 0) {
5016           debug(F111,"  lost sync at ATTR",recv_ptr-6,zoutcnt+6);
5017           return(-1);
5018       }
5019
5020       /* get length of attributes - should be sizeof(ULONG) */
5021       memcpy(buffer, recv_ptr, 2);
5022       recv_ptr += 2;
5023       zoutcnt  -= 2;
5024       buffer[2] = '\0';
5025       lblen = atoi(buffer);
5026       if ( lblen != sizeof(ULONG) ) {
5027           debug(F101,"   Attributes have wrong length","",lblen);
5028           return(-1);
5029       }
5030
5031       /* get attributes */
5032       memcpy(&os2attrs, recv_ptr, sizeof(ULONG));
5033       recv_ptr += sizeof(ULONG);
5034       zoutcnt  -= sizeof(ULONG);
5035
5036       /* check sync with EABUFSIZE */
5037       memcpy(buffer, recv_ptr, 11);
5038       recv_ptr += 11;
5039       zoutcnt  -= 11;
5040       if (strncmp(buffer, "09EABUFSIZE", 11) != 0) {
5041           debug(F111,"  lost sync at EABUFSIZE",recv_ptr-11,zoutcnt+11);
5042           return(-1);
5043       }
5044
5045       /* get length of length of EA Buffer Size */
5046       memcpy(buffer, recv_ptr, 2);
5047       recv_ptr += 2;
5048       zoutcnt  -= 2;
5049       buffer[2] = '\0';
5050       lblen = atoi(buffer);
5051
5052       /* get length of EA Buffer Size */
5053       memcpy(buffer, recv_ptr, lblen);
5054       recv_ptr += lblen;
5055       zoutcnt  -= lblen;
5056       buffer[lblen] = '\0';
5057       lblen = atoi(buffer);
5058       debug(F101,"  EA Buffer Size:","",lblen);
5059
5060       GetMem( (PPVOID) &pFEAList, (ULONG) lblen );
5061       if ( !pFEAList ) {
5062         debug(F101,"   pFEAList","",pFEAList);
5063         return -1;
5064       }
5065       pFEAList->cbList = lblen;
5066       pFEA = (BYTE *) pFEAList;
5067    }
5068
5069    if ( pFEAList && pFEA ) {
5070       /* get EA Buffer */
5071       lefttocpy = pFEAList->cbList - ( (BYTE *) pFEA - (BYTE *) pFEAList );
5072       memtocpy = ( zoutcnt < lefttocpy ? zoutcnt : lefttocpy );
5073       memcpy(pFEA, recv_ptr, memtocpy);
5074       recv_ptr += memtocpy;
5075       zoutcnt  -= memtocpy;
5076       pFEA += memtocpy;
5077       debug(F101,"   memtocpy","",memtocpy);
5078       debug(F101,"   zoutcnt","",zoutcnt);
5079
5080       if ( pFEA == ( (BYTE *) pFEAList + pFEAList->cbList ) ) {
5081          pFEA = 0;  /* we are done copying the EA's to the EA List */
5082          debug(F100,"   done copying EA's","",0);
5083       }
5084    }
5085
5086    /* if we have processed some of the data in the output buffer */
5087    /* then move the data in the buffer so that it is properly    */
5088    /* aligned with the beginning of the buffer and reset the ptr */
5089    if ( recv_ptr != zoutbuffer ) {
5090        memmove(zoutbuffer, recv_ptr, zoutcnt);
5091        if ( pFEA ) {
5092            zoutptr = zoutbuffer + zoutcnt ;   
5093            return(1);                          /* Go fill some more */
5094        }
5095    }
5096    return (0);
5097}
5098#endif /* CK_LABELED */
5099
5100#endif /* OS2 */
Note: See TracBrowser for help on using the repository browser.