dh_installchangelogs: adjust to use ckc301.txt. changelog: explain -O1 usage
[ckermit.git] / ckuus6.c
1 #include "ckcsym.h"
2 #ifndef NOICP
3
4 /*
5   Authors:
6     Frank da Cruz <fdc@columbia.edu>,
7       The Kermit Project, Columbia University, New York City
8     Jeffrey E Altman <jaltman@secure-endpoints.com>
9       Secure Endpoints Inc., New York City
10
11   Copyright (C) 1985, 2011,
12     Trustees of Columbia University in the City of New York.
13     All rights reserved.  See the C-Kermit COPYING.TXT file or the
14     copyright text in the ckcmai.c module for disclaimer and permissions.
15 */
16
17 /* Includes */
18
19 #include "ckcdeb.h"
20 #include "ckcasc.h"
21 #include "ckcker.h"
22 #include "ckuusr.h"
23 #include "ckcxla.h"
24 #include "ckcnet.h"                     /* Network symbols */
25 #include <signal.h>
26
27 #ifndef NOSTAT
28 #ifdef VMS
29 /* 2010-03-09 SMS.  VAX C needs help to find "sys".  It's easier not to try. */
30 #include <stat.h>
31 #else /* def VMS */
32 #include <sys/stat.h>
33 #endif /* def VMS [else] */
34 #endif /* NOSTAT */
35
36 #ifdef VMS
37 #ifndef TCPSOCKET
38 #include <errno.h>
39 #endif /* TCPSOCKET */
40 #endif /* VMS */
41
42 #ifdef datageneral
43 #define fgets(stringbuf,max,fd) dg_fgets(stringbuf,max,fd)
44 #endif /* datageneral */
45
46 #ifdef QNX6
47 #define readblock kreadblock
48 #endif /* QNX6 */
49
50 /* External Kermit Variables, see ckmain.c for description. */
51
52 extern xx_strp xxstring;
53
54 extern int local, xitsta, binary, parity, escape, flow, cmd_rows, turn,
55   turnch, duplex, ckxech, seslog, dfloc, cnflg, tlevel, pflag, msgflg, mdmtyp,
56   zincnt, quiet, repars, techo, network, nzxopts, what, filepeek, recursive;
57
58 extern int xaskmore, tt_rows, tt_cols, cmd_cols, g_matchdot, diractive,
59   xcmdsrc, nscanfile, reliable, nolinks, cmflgs;
60
61 #ifdef VMSORUNIX
62 extern int zgfs_dir, zgfs_link;
63 #endif /* VMSORUNIX */
64
65 #ifdef CK_IFRO
66   extern int remonly;
67 #endif /* CK_IFRO */
68
69 #ifdef OS2
70 extern int StartedFromDialer ;
71 extern int vmode;
72 extern int k95stdout;
73 #ifndef NT
74 #define INCL_NOPM
75 #define INCL_VIO                        /* Needed for ckocon.h */
76 #include <os2.h>
77 #undef COMMENT
78 #else
79 #define APIRET ULONG
80 #include <windows.h>
81 #include <tapi.h>
82 #include "ckntap.h"
83 #endif /* NT */
84 #include "ckocon.h"
85 #endif /* OS2 */
86
87 extern long vernum, speed;
88 extern char *versio, *protv, *ckxv, *ckzv, *fnsv, *connv, *dftty, *cmdv;
89 extern char *dialv, *loginv, *for_def[], *whil_def[], *xif_def[], *sw_def[];
90 extern char *foz_def[];
91 extern char *ckxsys, *ckzsys;
92 #ifndef OS2
93 extern char *DIRCMD;
94 #ifndef UNIX
95 extern char *DELCMD;
96 #endif /* UNIX */
97 #endif /* OS2 */
98 extern char ttname[], filnam[];
99 extern CHAR sstate, feol;
100 extern char *zinptr;
101
102 #ifdef UNIX
103 extern char ** mtchs;                   /* zxpand() file list */
104 #endif /* UNIX */
105
106 #ifndef NOXFER
107 extern int oopts, omode, oname, opath;  /* O-Packet options */
108
109 extern int stdinf, sndsrc, size, rpsiz, urpsiz, fncnv, fnrpath, displa,
110   stdouf, isguest, pktlog, nfils, keep, maxrps, fblksiz, frecl, frecfm,
111   atcapr, atdiso, spsizf, spsiz, spsizr, spmax, wslotr, prefixing,
112   fncact, fnspath, nprotos, g_proto, g_urpsiz, g_spsizf,
113   g_spsiz, g_spsizr, g_spmax, g_wslotr, g_prefixing, g_fncact, g_fncnv,
114   g_fnspath, g_fnrpath, xfrxla, g_xfrxla;
115
116 extern char *cmarg, *cmarg2;
117
118 #ifndef NOMSEND                         /* Multiple SEND */
119 extern char *msfiles[];
120 #endif /* NOMSEND */
121 extern char fspec[];                    /* Most recent filespec */
122 extern int fspeclen;
123
124 #ifdef CK_TMPDIR
125 extern int f_tmpdir;                    /* Directory changed temporarily */
126 extern char savdir[];                   /* For saving current directory */
127 #endif /* CK_TMPDIR */
128
129 extern struct keytab protos[];          /* File transfer protocols */
130 extern struct ck_p ptab[NPROTOS];
131 #endif /* NOXFER */
132
133 #ifdef DCMDBUF                          /* Declarations from cmd package */
134 extern char *cmdbuf, *atmbuf;           /* Command buffers */
135 #else
136 extern char cmdbuf[], atmbuf[];         /* Command buffers */
137 #endif /* DCMDBUF */
138
139 extern int nopush;
140
141 #ifndef NOSPL
142 int askflag = 0;                        /* ASK-class command active */
143 int echostars = 0;                      /* ASKQ should echo asterisks */
144 extern char **a_ptr[];
145 extern int a_dim[];
146 extern char **m_xarg[];
147 extern int n_xarg[];
148 extern struct mtab *mactab;
149 extern int nmac;
150 extern long ck_alarm;
151 extern char alrm_date[], alrm_time[];
152 extern int x_ifnum;
153 #endif /* NOSPL */
154
155 extern int inserver;                    /* I am IKSD */
156 extern int backgrd;                     /* Kermit executing in background */
157 extern char psave[];                    /* For saving & restoring prompt */
158 extern char *tp;                        /* Temporary buffer */
159
160 int readblock = 4096;                   /* READ buffer size */
161 CHAR * readbuf = NULL;                  /* Pointer to read buffer */
162 int readsize = 0;                       /* Number of chars actually read */
163 int getcmd = 0;                         /* GET-class command was given */
164
165 extern int zchkod, zchkid;
166
167 /*  C K U U S 6 --  "User Interface" for Unix Kermit (Part 6)  */
168
169 struct keytab deltab[] = {              /* DELETE Command Options */
170     { "/all",           DEL_ALL,  CM_INV },
171     { "/after",         DEL_AFT,  CM_ARG },
172     { "/ask",           DEL_ASK,  0 },
173     { "/before",        DEL_BEF,  CM_ARG },
174     { "/directories",   DEL_DIR,  0 },
175     { "/dotfiles",      DEL_DOT,  0 },
176     { "/except",        DEL_EXC,  CM_ARG },
177     { "/heading",       DEL_HDG,  0 },
178     { "/l",             DEL_LIS,  CM_INV|CM_ABR },
179     { "/larger-than",   DEL_LAR,  CM_ARG },
180     { "/list",          DEL_LIS,  0 },
181     { "/log",           DEL_LIS,  CM_INV },
182     { "/noask",         DEL_NAS,  0 },
183     { "/nodotfiles",    DEL_NOD,  0 },
184     { "/noheading",     DEL_NOH,  0 },
185     { "/nol",           DEL_NOL,  CM_INV|CM_ABR },
186     { "/nolist",        DEL_NOL,  0 },
187     { "/nolog",         DEL_NOL,  CM_INV },
188     { "/nopage",        DEL_NOP,  0 },
189     { "/not-after",     DEL_NAF,  CM_ARG },
190     { "/not-before",    DEL_NBF,  CM_ARG },
191     { "/not-since",     DEL_NAF,  CM_INV|CM_ARG },
192     { "/page",          DEL_PAG,  0 },
193     { "/quiet",         DEL_QUI,  CM_INV },
194     { "/recursive",     DEL_REC,  0 },
195     { "/simulate",      DEL_SIM,  0 },
196     { "/since",         DEL_AFT,  CM_ARG|CM_INV },
197     { "/smaller-than",  DEL_SMA,  CM_ARG },
198     { "/summary",       DEL_SUM,  0 },
199     { "/tree",          DEL_ALL,  0 },
200     { "/type",          DEL_TYP,  CM_ARG },
201     { "/verbose",       DEL_VRB,  CM_INV }
202 };
203 int ndeltab = sizeof(deltab)/sizeof(struct keytab);
204
205 /* /QUIET-/VERBOSE (/LIST-/NOLIST) (/LOG-/NOLOG) table */
206
207 struct keytab qvswtab[] = {
208     { "/l",           DEL_LIS,  CM_INV|CM_ABR },
209     { "/list",        DEL_LIS,  0 },
210     { "/log",         DEL_LIS,  CM_INV },
211     { "/nol",         DEL_NOL,  CM_INV|CM_ABR },
212     { "/nolist",      DEL_NOL,  0 },
213     { "/nolog",       DEL_NOL,  CM_INV },
214     { "/quiet",       DEL_QUI,  CM_INV },
215     { "/verbose",     DEL_VRB,  CM_INV }
216 };
217 int nqvswtab = sizeof(qvswtab)/sizeof(struct keytab);
218
219 static struct keytab renamsw[] = {
220     { "/collision",   REN_OVW,  CM_ARG },
221 #ifndef NOUNICODE
222     { "/convert",     REN_XLA,  CM_ARG },
223 #endif  /* NOUNICODE */
224     { "/fixspaces",   REN_SPA,  CM_ARG },
225     { "/l",           DEL_LIS,  CM_INV|CM_ABR },
226     { "/list",        DEL_LIS,  0      },
227     { "/log",         DEL_LIS,  CM_INV },
228     { "/lower",       REN_LOW,  CM_ARG },
229     { "/nol",         DEL_NOL,  CM_INV|CM_ABR },
230     { "/nolist",      DEL_NOL,  0      },
231     { "/nolog",       DEL_NOL,  CM_INV },
232     { "/quiet",       DEL_QUI,  CM_INV },
233     { "/replace",     REN_RPL,  CM_ARG },
234     { "/simulate",    DEL_SIM,  0      },
235     { "/upper",       REN_UPP,  CM_ARG },
236     { "/verbose",     DEL_VRB,  CM_INV }
237 };
238 static int nrenamsw = sizeof(renamsw)/sizeof(struct keytab);
239
240 static struct keytab renamset[] = {
241     { "collision",    REN_OVW,  0 },
242     { "list",         DEL_LIS,  0 }
243 };
244 static int nrenamset = sizeof(renamset)/sizeof(struct keytab);
245
246 /* Args for RENAME /LOWER: and /UPPER: */
247
248 static struct keytab r_upper[] = {
249     { "all",   1, 0 },
250     { "lower", 0, 0 }
251 };
252
253 static struct keytab r_lower[] = {
254     { "all",   1, 0 },
255     { "upper", 0, 0 }
256 };
257
258 /* Args for RENAME /COLLISION... */
259
260 #define RENX_FAIL 0
261 #define RENX_OVWR 1
262 #define RENX_SKIP 2
263
264 static struct keytab r_collision[] = {
265     { "fail",      RENX_FAIL, 0 },
266     { "overwrite", RENX_OVWR, 0 },
267     { "proceed",   RENX_SKIP, CM_INV },
268     { "skip",      RENX_SKIP, 0 }
269 };
270 static int nr_collision = sizeof(r_collision)/sizeof(struct keytab);
271
272 struct keytab copytab[] = {
273     { "/append",      998,      0 },
274 #ifndef NOSPL
275     { "/fromb64",     997,      0 },
276 #endif /* NOSPL */
277     { "/l",           DEL_LIS,  CM_INV|CM_ABR },
278     { "/list",        DEL_LIS,  0 },
279     { "/log",         DEL_LIS,  CM_INV },
280     { "/nol",         DEL_NOL,  CM_INV|CM_ABR },
281     { "/nolist",      DEL_NOL,  0 },
282     { "/nolog",       DEL_NOL,  CM_INV },
283     { "/overwrite",   994,      CM_ARG },
284 #ifndef NOXFER
285     { "/preserve",    995,      0 },
286 #endif  /* NOXFER */
287     { "/quiet",       DEL_QUI,  CM_INV },
288     { "/swap-bytes",  999,      0 },
289 #ifndef NOSPL
290     { "/tob64",       996,      0 },
291 #endif /* NOSPL */
292     { "/verbose",     DEL_VRB,  CM_INV }
293 };
294 int ncopytab = sizeof(copytab)/sizeof(struct keytab);
295
296 #define OVW_ALWAYS 0
297 #define OVW_NEVER  1
298 #define OVW_OLDER  2
299 #define OVW_NEWER  3
300
301 static struct keytab ovwtab[] = {
302     { "always", OVW_ALWAYS, 0 },
303     { "never",  OVW_NEVER, 0 },
304     { "newer",  OVW_NEWER, 0 },
305     { "older",  OVW_OLDER, 0 }
306 };
307 static int novwtab = 4;
308
309 #ifndef NOXFER
310 static struct keytab gettab[] = {       /* GET options */
311     { "/as-name",         SND_ASN, CM_ARG },
312     { "/binary",          SND_BIN, 0 },
313 #ifdef CALIBRATE
314     { "/calibrate",       SND_CAL, CM_INV },
315 #endif /* CALIBRATE */
316 #ifdef PIPESEND
317     { "/command",         SND_CMD, CM_PSH },
318 #endif /* PIPESEND */
319     { "/delete",          SND_DEL, 0 },
320     { "/except",          SND_EXC, CM_ARG },
321     { "/filenames",       SND_NAM, CM_ARG },
322 #ifdef PIPESEND
323     { "/filter",          SND_FLT, CM_ARG|CM_PSH },
324 #endif /* PIPESEND */
325 #ifdef VMS
326     { "/image",           SND_IMG, 0 },
327     { "/labeled",         SND_LBL, 0 },
328 #else
329     { "/image",           SND_BIN, CM_INV },
330 #endif /* VMS */
331 #ifdef CK_TMPDIR
332     { "/move-to",         SND_MOV, CM_ARG },
333 #endif /* CK_TMPDIR */
334     { "/pathnames",       SND_PTH, CM_ARG },
335     { "/pipes",           SND_PIP, CM_ARG|CM_PSH },
336     { "/quiet",           SND_SHH, 0 },
337 #ifdef CK_RESEND
338     { "/recover",         SND_RES, 0 },
339 #endif /* CK_RESEND */
340     { "/recursive",       SND_REC, 0 },
341     { "/rename-to",       SND_REN, CM_ARG },
342 #ifdef COMMENT
343     { "/smaller-than",    SND_SMA, CM_ARG },
344 #endif /* COMMENT */
345     { "/subdirectories",  SND_REC, CM_INV },
346     { "/text",            SND_TXT, 0 },
347     { "/transparent",     SND_XPA, 0 }
348 };
349 #define NGETTAB sizeof(gettab)/sizeof(struct keytab)
350 static int ngettab = NGETTAB;
351
352 static struct keytab rcvtab[] = {       /* RECEIVE options */
353     { "/as-name",         SND_ASN, CM_ARG },
354     { "/binary",          SND_BIN, 0 },
355 #ifdef CALIBRATE
356     { "/calibrate",       SND_CAL, CM_INV },
357 #endif /* CALIBRATE */
358 #ifdef PIPESEND
359     { "/command",         SND_CMD, CM_PSH },
360 #endif /* PIPESEND */
361     { "/except",          SND_EXC, CM_ARG },
362     { "/filenames",       SND_NAM, CM_ARG },
363 #ifdef PIPESEND
364     { "/filter",          SND_FLT, CM_ARG|CM_PSH },
365 #endif /* PIPESEND */
366 #ifdef VMS
367     { "/image",           SND_IMG, 0 },
368     { "/labeled",         SND_LBL, 0 },
369 #else
370     { "/image",           SND_BIN, CM_INV },
371 #endif /* VMS */
372 #ifdef CK_TMPDIR
373     { "/move-to",         SND_MOV, CM_ARG },
374 #endif /* CK_TMPDIR */
375     { "/pathnames",       SND_PTH, CM_ARG },
376     { "/pipes",           SND_PIP, CM_ARG|CM_PSH },
377 #ifdef CK_XYZ
378     { "/protocol",        SND_PRO, CM_ARG },
379 #else
380     { "/protocol",        SND_PRO, CM_ARG|CM_INV },
381 #endif /* CK_XYZ */
382     { "/quiet",           SND_SHH, 0 },
383     { "/recursive",       SND_REC, 0 },
384     { "/rename-to",       SND_REN, CM_ARG },
385     { "/text",            SND_TXT, 0 },
386     { "/transparent",     SND_XPA, 0 }
387 };
388 #define NRCVTAB sizeof(rcvtab)/sizeof(struct keytab)
389 static int nrcvtab = NRCVTAB;
390 #endif /* NOXFER */
391
392 /* WAIT table */
393
394 #define WAIT_FIL 997
395 #define WAIT_MDM 998
396
397 struct keytab waittab[] = {
398     { "cd",            BM_DCD,   CM_INV }, /* (Carrier Detect) */
399     { "cts",           BM_CTS,   CM_INV }, /* (Clear To Send)  */
400     { "dsr",           BM_DSR,   CM_INV }, /* (Data Set Ready) */
401     { "file",          WAIT_FIL, 0 },      /* New category selector keywords */
402     { "modem-signals", WAIT_MDM, 0 },      /* ... */
403     { "ri",            BM_RNG,   CM_INV }  /* (Ring Indicator) */
404 };
405 int nwaittab = (sizeof(waittab) / sizeof(struct keytab));
406
407 /* Modem signal table */
408
409 struct keytab mstab[] = {
410     { "cd",    BM_DCD, 0 },             /* Carrier Detect */
411     { "cts",   BM_CTS, 0 },             /* Clear To Send  */
412     { "dsr",   BM_DSR, 0 },             /* Data Set Ready */
413     { "ri",    BM_RNG, 0 }              /* Ring Indicator */
414 };
415 int nms = (sizeof(mstab) / sizeof(struct keytab));
416
417 #define WF_MOD 1
418 #define WF_DEL 2
419 #define WF_CRE 3
420
421 struct keytab wfswi[] = {               /* WAIT FILE switches */
422     { "creation",     WF_CRE, 0 },      /* Wait for file to be created */
423     { "deletion",     WF_DEL, 0 },      /* Wait for file to be deleted */
424     { "modification", WF_MOD, 0 }       /* Wait for file to be modified */
425 };
426 int nwfswi = (sizeof(wfswi) / sizeof(struct keytab));
427
428 #ifndef NOSPL
429 struct keytab asgtab[] = {              /* Assignment operators for "." */
430     { "::=", 2, 0 },                    /* ASSIGN and EVALUATE */
431     { ":=",  1, 0 },                    /* ASSIGN */
432     { "=",   0, 0 }                     /* DEFINE */
433 };
434 int nasgtab = (sizeof(asgtab) / sizeof(struct keytab));
435
436 struct keytab opntab[] = {
437 #ifndef NOPUSH
438     { "!read",  OPN_PI_R, CM_INV },
439     { "!write", OPN_PI_W, CM_INV },
440 #endif /* NOPUSH */
441     { "append", OPN_FI_A, 0 },
442     { "host",   OPN_NET,  0 },
443 #ifdef OS2
444     { "line",   OPN_SER,  CM_INV },
445     { "port",   OPN_SER,  0 },
446 #else
447     { "line",   OPN_SER,  0 },
448     { "port",   OPN_SER,  CM_INV },
449 #endif /* OS2 */
450     { "read",   OPN_FI_R, 0 },
451     { "write",  OPN_FI_W, 0 }
452 };
453 int nopn = (sizeof(opntab) / sizeof(struct keytab));
454
455 /* IF conditions */
456
457 #define  XXIFCO 0       /* IF COUNT */
458 #define  XXIFER 1       /* IF ERRORLEVEL */
459 #define  XXIFEX 2       /* IF EXIST */
460 #define  XXIFFA 3       /* IF FAILURE */
461 #define  XXIFSU 4       /* IF SUCCESS */
462 #define  XXIFNO 5       /* IF NOT */
463 #define  XXIFDE 6       /* IF DEFINED */
464 #define  XXIFEQ 7       /* IF EQUAL (strings) */
465 #define  XXIFAE 8       /* IF = (numbers) */
466 #define  XXIFLT 9       /* IF < (numbers) */
467 #define  XXIFGT 10      /* IF > (numbers) */
468 #define  XXIFLL 11      /* IF Lexically Less Than (strings) */
469 #define  XXIFLG 12      /* IF Lexically Greater Than (strings) */
470 #define  XXIFEO 13      /* IF EOF (READ file) */
471 #define  XXIFBG 14      /* IF BACKGROUND */
472 #define  XXIFNU 15      /* IF NUMERIC */
473 #define  XXIFFG 16      /* IF FOREGROUND */
474 #define  XXIFDI 17      /* IF DIRECTORY */
475 #define  XXIFNE 18      /* IF NEWER */
476 #define  XXIFRO 19      /* IF REMOTE-ONLY */
477 #define  XXIFAL 20      /* IF ALARM */
478 #define  XXIFSD 21      /* IF STARTED-FROM-DIALER */
479 #define  XXIFTR 22      /* IF TRUE */
480 #define  XXIFNT 23      /* IF FALSE */
481 #define  XXIFTM 24      /* IF TERMINAL-MACRO */
482 #define  XXIFEM 25      /* IF EMULATION */
483 #define  XXIFOP 26      /* IF OPEN */
484 #define  XXIFLE 27      /* IF <= */
485 #define  XXIFGE 28      /* IF >= */
486 #define  XXIFIP 29      /* IF INPATH */
487 #define  XXIFTA 30      /* IF TAPI */
488 #define  XXIFMA 31      /* IF MATCH */
489 #define  XXIFFL 32      /* IF FLAG */
490 #define  XXIFAB 33      /* IF ABSOLUTE */
491 #define  XXIFAV 34      /* IF AVAILABLE */
492 #define  XXIFAT 35      /* IF ASKTIMEOUT */
493 #define  XXIFRD 36      /* IF READABLE */
494 #define  XXIFWR 37      /* IF WRITEABLE */
495 #define  XXIFAN 38      /* IF ... AND ... */
496 #define  XXIFOR 39      /* IF ... OR ... */
497 #define  XXIFLP 40      /* IF left parenthesis */
498 #define  XXIFRP 41      /* IF right parenthesis */
499 #define  XXIFNQ 42      /* IF != (== "NOT =") */
500 #define  XXIFQU 43      /* IF QUIET */
501 #define  XXIFCK 44      /* IF C-KERMIT */
502 #define  XXIFK9 45      /* IF K-95 */
503 #define  XXIFMS 46      /* IF MS-KERMIT */
504 #define  XXIFWI 47      /* IF WILD */
505 #define  XXIFLO 48      /* IF LOCAL */
506 #define  XXIFCM 49      /* IF COMMAND */
507 #define  XXIFFP 50      /* IF FLOAT */
508 #define  XXIFIK 51      /* IF IKS */
509 #define  XXIFKB 52      /* IF KBHIT */
510 #define  XXIFKG 53      /* IF KERBANG */
511 #define  XXIFVE 54      /* IF VERSION */
512 #define  XXIFDC 55      /* IF DECLARED */
513 #define  XXIFGU 56      /* IF GUI */
514 #define  XXIFLN 57      /* IF LINK */
515 #define  XXIFDB 58      /* IF DEBUG */
516
517 struct keytab iftab[] = {               /* IF commands */
518     { "!",          XXIFNO, 0 },
519     { "!=",         XXIFNQ, 0 },
520     { "&&",         XXIFAN, 0 },
521     { "(",          XXIFLP, 0 },
522     { ")",          XXIFRP, 0 },
523     { "<",          XXIFLT, 0 },
524     { "<=",         XXIFLE, 0 },
525     { "=",          XXIFAE, 0 },
526     { "==",         XXIFAE, CM_INV },
527     { ">",          XXIFGT, 0 },
528     { ">=",         XXIFGE, 0 },
529     { "absolute",   XXIFAB, 0 },
530     { "alarm",      XXIFAL, 0 },
531     { "and",        XXIFAN, 0 },
532     { "asktimeout", XXIFAT, 0 },
533     { "available",  XXIFAV, 0 },
534     { "background", XXIFBG, 0 },
535     { "c-kermit",   XXIFCK, 0 },
536     { "command",    XXIFCM, 0 },
537     { "count",      XXIFCO, 0 },
538     { "dcl",        XXIFDC, CM_INV },
539     { "debug",      XXIFDB, 0 },
540     { "declared",   XXIFDC, 0 },
541     { "defined",    XXIFDE, 0 },
542 #ifdef CK_TMPDIR
543     { "directory",  XXIFDI, 0 },
544 #endif /* CK_TMPDIR */
545     { "emulation",  XXIFEM, 0 },
546 #ifdef COMMENT
547     { "eof",        XXIFEO, 0 },
548 #endif /* COMMENT */
549     { "equal",      XXIFEQ, 0 },
550     { "error",      XXIFFA, CM_INV },
551     { "exist",      XXIFEX, 0 },
552     { "failure",    XXIFFA, 0 },
553     { "false",      XXIFNT, 0 },
554     { "flag",       XXIFFL, 0 },
555 #ifdef CKFLOAT
556     { "float",      XXIFFP, 0 },
557 #endif /* CKFLOAT */
558     { "foreground", XXIFFG, 0 },
559 #ifdef OS2
560     { "gui",        XXIFGU, 0 },
561 #else
562     { "gui",        XXIFGU, CM_INV },
563 #endif /* OS2 */
564 #ifdef IKSD
565     { "iksd",       XXIFIK, 0 },
566 #else
567     { "iksd",       XXIFIK, CM_INV },
568 #endif /* IKSD */
569     { "integer",    XXIFNU, CM_INV },
570     { "k-95",       XXIFK9, 0 },
571     { "kbhit",      XXIFKB, 0 },
572 #ifdef UNIX
573     { "kerbang",    XXIFKG, 0 },
574 #else
575     { "kerbang",    XXIFKG, CM_INV },
576 #endif /* UNIX */
577     { "lgt",        XXIFLG, 0 },
578 #ifdef UNIX
579     { "link",       XXIFLN, 0 },
580 #endif /* UNIX */
581     { "llt",        XXIFLL, 0 },
582     { "local",      XXIFLO, 0 },
583     { "match",      XXIFMA, 0 },
584     { "ms-kermit",  XXIFMS, CM_INV },
585 #ifdef ZFCDAT
586     { "newer",      XXIFNE, 0 },
587 #endif /* ZFCDAT */
588     { "not",        XXIFNO, 0 },
589     { "numeric",    XXIFNU, 0 },
590     { "ok",         XXIFSU, CM_INV },
591     { "open",       XXIFOP, 0 },
592     { "or",         XXIFOR, 0 },
593     { "quiet",      XXIFQU, 0 },
594     { "readable",   XXIFRD, 0 },
595     { "remote-only",XXIFRO, 0 },
596     { "started-from-dialer",XXIFSD, CM_INV },
597     { "success",    XXIFSU, 0 },
598     { "tapi",       XXIFTA, 0 },
599 #ifdef OS2
600     { "terminal-macro", XXIFTM, 0 },
601 #else
602     { "terminal-macro", XXIFTM, CM_INV },
603 #endif /* OS2 */
604     { "true",       XXIFTR, 0 },
605     { "version",    XXIFVE, 0 },
606     { "wild",       XXIFWI, 0 },
607     { "writeable",  XXIFWR, 0 },
608     { "||",         XXIFOR, 0 },
609     { "", 0, 0 }
610 };
611 int nif = (sizeof(iftab) / sizeof(struct keytab)) - 1;
612
613 struct keytab iotab[] = {               /* Keywords for IF OPEN */
614     { "!read-file",      ZRFILE, CM_INV },
615     { "!write-file",     ZWFILE, CM_INV },
616     { "append-file",     ZWFILE, CM_INV },
617     { "connection",      8888,   0 },
618 #ifdef CKLOGDIAL
619     { "cx-log",          7777,   0 },
620 #endif /* CKLOGDIAL */
621     { "debug-log",       ZDFILE, 0 },
622     { "error",           9999,   0 },
623     { "packet-log",      ZPFILE, 0 },
624     { "read-file",       ZRFILE, 0 },
625     { "screen",          ZSTDIO, 0 },
626     { "session-log",     ZSFILE, 0 },
627     { "transaction-log", ZTFILE, 0 },
628     { "write-file",      ZWFILE, 0 }
629 };
630 int niot = (sizeof(iotab) / sizeof(struct keytab));
631 #endif /* NOSPL */
632
633 /* Variables and prototypes */
634
635 _PROTOTYP(static int doymdir,(int));
636 _PROTOTYP(static int renameone,(char *,char *,
637                                 int,int,int,int,int,int,int,int,int,int,int));
638
639 #ifdef NETCONN
640 extern int nnetdir;                     /* How many network directories */
641 #endif /* NETCONN */
642 #ifdef CK_SECURITY
643 _PROTOTYP(int ck_krb4_is_installed,(void));
644 _PROTOTYP(int ck_krb5_is_installed,(void));
645 _PROTOTYP(int ck_ntlm_is_installed,(void));
646 _PROTOTYP(int ck_srp_is_installed,(void));
647 _PROTOTYP(int ck_ssleay_is_installed,(void));
648 _PROTOTYP(int ck_ssh_is_installed,(void));
649 _PROTOTYP(int ck_crypt_is_installed,(void));
650 #else
651 #define ck_krb4_is_installed() (0)
652 #define ck_krb5_is_installed() (0)
653 #define ck_ntlm_is_installed() (0)
654 #define ck_srp_is_installed() (0)
655 #define ck_ssleay_is_installed() (0)
656 #define ck_ssh_is_installed() (0)
657 #define ck_crypt_is_installed() (0)
658 #endif /* CK_SECURITY */
659
660 #define AV_KRB4   1
661 #define AV_KRB5   2
662 #define AV_NTLM   3
663 #define AV_SRP    4
664 #define AV_SSL    5
665 #define AV_CRYPTO 6
666 #define AV_SSH    7
667
668 struct keytab availtab[] = {             /* Available authentication types */
669     { "crypto",     AV_CRYPTO, CM_INV }, /* and encryption */
670     { "encryption", AV_CRYPTO, 0 },
671     { "k4",         AV_KRB4,   CM_INV },
672     { "k5",         AV_KRB5,   CM_INV },
673     { "kerberos4",  AV_KRB4,   0 },
674     { "kerberos5",  AV_KRB5,   0 },
675     { "krb4",       AV_KRB4,   CM_INV },
676     { "krb5",       AV_KRB5,   CM_INV },
677     { "ntlm",       AV_NTLM,   0 },
678     { "srp",        AV_SRP,    0 },
679     { "ssh",        AV_SSH,    0 },
680     { "ssl",        AV_SSL,    0 },
681     { "tls",        AV_SSL,    0 },
682     { "",           0,         0 }
683 };
684 int availtabn = sizeof(availtab)/sizeof(struct keytab)-1;
685
686 #ifndef NODIAL
687 _PROTOTYP(static int ddcvt, (char *, FILE *, int) );
688 _PROTOTYP(static int dncvt, (int, int, int, int) );
689 _PROTOTYP(char * getdname, (void) );
690
691 static int partial  = 0;                /* For partial dial */
692 static char *dscopy = NULL;
693 int dialtype = -1;
694
695 char *dialnum = (char *)0;              /* Remember DIAL number for REDIAL */
696 int dirline = 0;                        /* Dial directory line number */
697 extern char * dialdir[];                /* Dial directory file names */
698 extern int dialdpy;                     /* DIAL DISPLAY on/off */
699 extern int ndialdir;                    /* How many dial directories */
700 extern int ntollfree;                   /* Toll-free call info */
701 extern int ndialpxx;                    /* List of PBX exchanges */
702 extern char *dialtfc[];
703 char * matchpxx = NULL;                 /* PBX exchange that matched */
704 extern int nlocalac;                    /* Local area-code list */
705 extern char * diallcac[];
706 extern int tttapi;
707 #ifdef CK_TAPI
708 extern int tapiconv;                    /* TAPI Conversions */
709 extern int tapipass;                    /* TAPI Passthrough */
710 #endif /* CK_TAPI */
711 extern int dialatmo;
712 extern char * dialnpr, * dialsfx;
713 extern char * dialixp, * dialixs, * dialmac;
714 extern char * dialldp, * diallds, * dialtfp;
715 extern char * dialpxi, * dialpxo, * diallac;
716 extern char * diallcp, * diallcs, * diallcc;
717 extern char * dialpxx[];
718
719 extern int dialcnf;                     /* DIAL CONFIRMATION */
720 int dialfld = 0;                        /* DIAL FORCE-LONG-DISTANCE */
721 int dialsrt = 1;                        /* DIAL SORT ON */
722 int dialrstr = 6;                       /* DIAL RESTRICTION */
723 int dialtest = 0;                       /* DIAL TEST */
724 int dialcount = 0;                      /* \v(dialcount) */
725
726 extern int dialsta;                     /* Dial status */
727 int dialrtr = -1,                       /* Dial retries */
728     dialint = 10;                       /* Dial retry interval */
729 extern long dialcapas;                  /* Modem capabilities */
730 extern int dialcvt;                     /* DIAL CONVERT-DIRECTORY */
731 #endif /* NODIAL */
732
733 #ifndef NOSPL
734 #define IFCONDLEN 256
735 int ifc,                                /* IF case */
736     not = 0,                            /* Flag for IF NOT */
737     ifargs = 0;                         /* Count of IF condition words */
738 char ifcond[IFCONDLEN];                 /* IF condition text */
739 char *ifcp;                             /* Pointer to IF condition text */
740 extern int vareval;
741 #ifdef DCMDBUF
742 extern int
743  *ifcmd,  *count,  *iftest, *intime,
744     *inpcas, *takerr, *merror, *xquiet, *xvarev;
745 #else
746 extern int ifcmd[];                     /* Last command was IF */
747 extern int iftest[];                    /* Last IF was true */
748 extern int count[];                     /* For IF COUNT, one for each cmdlvl */
749 extern int intime[];                    /* Ditto for other stackables... */
750 extern int inpcas[];
751 extern int takerr[];
752 extern int merror[];
753 extern int xquiet[];
754 extern int xvarev[];
755 #endif /* DCMDBUF */
756 #else
757 extern int takerr[];
758 #endif /* NOSPL */
759
760 #ifdef DCMDBUF
761 extern char *line;                      /* Character buffer for anything */
762 extern char *tmpbuf;
763 #else
764 extern char line[], tmpbuf[];
765 #endif /* DCMDBUF */
766 extern char *lp;                        /* Pointer to line buffer */
767
768 int cwdf = 0;                           /* CWD has been done */
769
770 /* Flags for ENABLE/DISABLE */
771 extern int en_cwd, en_cpy, en_del, en_dir, en_fin,
772    en_get, en_hos, en_ren, en_sen, en_set, en_spa, en_typ, en_who, en_bye,
773    en_asg, en_que, en_ret, en_mai, en_pri, en_mkd, en_rmd, en_xit, en_ena;
774
775 extern FILE *tfile[];                   /* File pointers for TAKE command */
776 extern char *tfnam[];                   /* Names of TAKE files */
777 extern int tfline[];                    /* TAKE-file line number */
778
779 extern int success;                     /* Command success/failure flag */
780 extern int cmdlvl;                      /* Current position in command stack */
781
782 #ifndef NOSPL
783 extern int maclvl;                      /* Macro to execute */
784 extern char *macx[];                    /* Index of current macro */
785 extern char *mrval[];                   /* Macro return value */
786 extern char *macp[];                    /* Pointer to macro */
787 extern int macargc[];                   /* ARGC from macro invocation */
788
789 #ifdef COMMENT
790 extern char *m_line[];
791 #endif /* COMMENT */
792
793 extern char *m_arg[MACLEVEL][NARGS];    /* Stack of macro arguments */
794 extern char *g_var[];                   /* Global variables %a, %b, etc */
795
796 #ifdef DCMDBUF
797 extern struct cmdptr *cmdstk;           /* The command stack itself */
798 #else
799 extern struct cmdptr cmdstk[];          /* The command stack itself */
800 #endif /* DCMDBUF */
801 #endif /* NOSPL */
802
803 #define xsystem(s) zsyscmd(s)
804
805 static int x, y, z = 0;
806 static char *s, *p;
807
808 #ifdef OS2
809 _PROTOTYP( int os2settitle, (char *, int) );
810 #endif /* OS2 */
811
812 extern struct keytab yesno[], onoff[], fntab[];
813 extern int nyesno, nfntab;
814
815 #ifndef NOSPL
816
817 /* Do the ASK, ASKQ, GETOK, and READ commands */
818
819 int asktimedout = 0;
820
821 #define ASK_PUP 1
822 #define ASK_TMO 2
823 #define ASK_GUI 3
824 #define ASK_QUI 4
825 #define ASK_DEF 5
826 #define ASK_ECH 6
827
828 static struct keytab asktab[] = {
829     {  "/default", ASK_DEF, CM_ARG },
830     {  "/gui",     ASK_GUI,      
831 #ifdef KUI
832            0
833 #else /* KUI */
834            CM_INV
835 #endif /* KUI */
836     },
837     { "/popup",    ASK_PUP,   
838 #ifdef OS2
839            0
840 #else /* OS2 */
841            CM_INV
842 #endif /* OS2 */
843     },
844     { "/quiet",    ASK_QUI, 0 },
845     { "/timeout",  ASK_TMO, CM_ARG },
846     { "", 0, 0 }
847 };
848 static int nasktab = sizeof(asktab)/sizeof(struct keytab)-1;
849
850 static struct keytab askqtab[] = {
851     { "/default",  ASK_DEF, CM_ARG },
852     { "/echo",     ASK_ECH, CM_ARG },
853     { "/gui",      ASK_GUI,      
854 #ifdef KUI
855            0
856 #else /* KUI */
857            CM_INV
858 #endif /* KUI */
859     },
860     { "/noecho",   ASK_QUI, CM_INV },
861     { "/popup",    ASK_PUP,   
862 #ifdef OS2
863            0
864 #else /* OS2 */
865            CM_INV
866 #endif /* OS2 */
867     },
868     { "/quiet",    ASK_QUI, 0 },
869     { "/timeout",  ASK_TMO, CM_ARG },
870     { "", 0, 0 }
871 };
872 static int naskqtab = sizeof(askqtab)/sizeof(struct keytab)-1;
873
874 int
875 doask(cx) int cx; {
876     extern int asktimer, timelimit;
877 #ifdef CK_RECALL
878     extern int on_recall;
879 #endif /* CK_RECALL */
880     int echochar = 0;
881     int popupflg = 0;
882     int guiflg = 0;
883     int nomsg = 0;
884     int mytimer = 0;
885 #ifdef CK_APC
886     extern int apcactive, apcstatus;
887 #endif /* CK_APC */
888
889     char dfbuf[1024];                   /* Buffer for default answer */
890     char * dfanswer = NULL;             /* Pointer to it */
891
892     char vnambuf[VNAML+1];              /* Buffer for variable names */
893     char *vnp = NULL;                   /* Pointer to same */
894     
895     dfbuf[0] = NUL;
896     vnambuf[0] = NUL;
897
898 #ifdef CK_APC
899     if ( apcactive != APC_INACTIVE && (apcstatus & APC_NOINP) ) {
900         return(success = 0);
901     }
902 #endif /* CK_APC */
903
904     mytimer = asktimer;                 /* Inherit global ASK timer */
905     echostars = 0;                      /* For ASKQ */
906
907     if (cx == XXASK || cx == XXASKQ) {
908         struct FDB sw, fl;
909         int getval;
910         char c;
911         if (cx == XXASKQ)               /* Don't log ASKQ response */
912           debok = 0;
913         cmfdbi(&sw,                     /* First FDB - command switches */
914                _CMKEY,                  /* fcode */
915                "Variable name or switch",
916                "",                      /* default */
917                "",                      /* addtl string data */
918                ((cx == XXASK) ? nasktab : naskqtab), /* Table size */
919                4,                       /* addtl numeric data 2: 4 = cmswi */
920                xxstring,                /* Processing function */
921                ((cx == XXASK) ? asktab : askqtab), /* Keyword table */
922                &fl                      /* Pointer to next FDB */
923                );
924         cmfdbi(&fl,                     /* Anything that doesn't match */
925                _CMFLD,                  /* fcode */
926                "",                      /* hlpmsg */
927                "",                      /* default */
928                "",                      /* addtl string data */
929                0,                       /* addtl numeric data 1 */
930                0,                       /* addtl numeric data 2 */
931                NULL,
932                NULL,
933                NULL
934                );
935         while (1) {                     /* Parse 0 or more switches */
936             x = cmfdb(&sw);             /* Parse something */
937             if (x < 0)
938               return(x);
939             if (cmresult.fcode != _CMKEY) /* Break out if not a switch */
940               break;
941             c = cmgbrk();
942             if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
943                 printf("?This switch does not take an argument\n");
944                 return(-9);
945             }
946             if (!getval && (cmgkwflgs() & CM_ARG)) {
947                 printf("?This switch requires an argument\n");
948                 return(-9);
949             }
950             switch (cmresult.nresult) {
951               case ASK_QUI:
952                 nomsg = 1;
953                 if (cx == XXASKQ)
954                   echostars = 0;
955                 break;
956               case ASK_PUP:
957                 popupflg = 1;
958                 break;
959               case ASK_GUI:
960                 guiflg = 1;
961                 break;
962               case ASK_TMO: {
963                   if ((y = cmnum("seconds","1",10,&x,xxstring)) < 0)
964                     return(y);
965                   if (x < 0)
966                     x = 0;
967                   mytimer = x;
968                   break;
969               }
970               case ASK_ECH: {
971                   if ((y = cmfld("Character to echo","*",&s,xxstring)) < 0)
972                     return(y);
973                   echochar = *s;
974                   break;
975               }
976               case ASK_DEF: {
977                   if ((y = cmfld("Text to supply if reply is empty",
978                                  "",&s,xxstring)) < 0)
979                     return(y);
980                   ckstrncpy(dfbuf,s,1024);
981                   dfanswer = dfbuf;
982                   break;
983               }
984               default: return(-2);
985             }
986         }
987         /* Have variable name, make copy. */
988         ckstrncpy(vnambuf,cmresult.sresult,VNAML);
989         vnp = vnambuf;
990         if (vnambuf[0] == CMDQ &&
991             (vnambuf[1] == '%' || vnambuf[1] == '&'))
992           vnp++;
993         y = 0;
994         if (*vnp == '%' || *vnp == '&') {
995             if ((y = parsevar(vnp,&x,&z)) < 0)
996               return(y);
997         }
998     } else if (cx != XXGOK && cx != XXRDBL) { /* Get variable name */
999         if ((y = cmfld("Variable name","",&s,NULL)) < 0) {
1000             if (y == -3) {
1001                 printf("?Variable name required\n");
1002                 return(-9);
1003             } else return(y);
1004         }
1005         ckstrncpy(vnambuf,s,VNAML);     /* Make a copy. */
1006         vnp = vnambuf;
1007         if (vnambuf[0] == CMDQ &&
1008             (vnambuf[1] == '%' || vnambuf[1] == '&'))
1009           vnp++;
1010         y = 0;
1011         if (*vnp == '%' || *vnp == '&') {
1012             if ((y = parsevar(vnp,&x,&z)) < 0)
1013               return(y);
1014         }
1015     }
1016     if (cx == XXREA || cx == XXRDBL) {  /* READ or READBLOCK command */
1017         if ((y = cmcfm()) < 0)          /* Get confirmation */
1018           return(y);
1019         if (chkfn(ZRFILE) < 1) {        /* File open? */
1020             printf("?Read file not open\n");
1021             return(success = 0);
1022         }
1023         if (!(s = (char *)readbuf)) {           /* Where to read into. */
1024             printf("?Oops, no READ buffer!\n");
1025             return(success = 0);
1026         }
1027         y = zsinl(ZRFILE, s, readblock); /* Read a line. */
1028         debug(F111,"read zsinl",s,y);
1029         if (y < 0) {                    /* On EOF or other error, */
1030             zclose(ZRFILE);             /* close the file, */
1031             delmac(vnp,0);              /* delete the variable, */
1032             return(success = 0);        /* and return failure. */
1033         } else {                        /* Read was OK. */
1034             readsize = (int) strlen(s);
1035             success = (addmac(vnp,s) < 0 ? 0 : 1); /* Define variable */
1036             debug(F111,"read addmac",vnp,success);
1037             return(success);            /* Return success. */
1038         }
1039     }
1040
1041     /* ASK, ASKQ, GETOK, or GETC */
1042
1043     if (cx == XXGOK) {                  /* GETOK can take switches */
1044         struct FDB sw, fl;
1045         int getval;
1046         char c;
1047         cmfdbi(&sw,                     /* First FDB - command switches */
1048                _CMKEY,                  /* fcode */
1049                "Variable name or question prompt",
1050                "",                      /* default */
1051                "",                      /* addtl string data */
1052                nasktab,                 /* addtl numeric data 1: tbl size */
1053                4,                       /* addtl numeric data 2: 4 = cmswi */
1054                xxstring,                /* Processing function */
1055                asktab,                  /* Keyword table */
1056                &fl                      /* Pointer to next FDB */
1057                );
1058         cmfdbi(&fl,                     /* Anything that doesn't match */
1059                _CMTXT,                  /* fcode */
1060                "",                      /* hlpmsg */
1061                "",                      /* default */
1062                "",                      /* addtl string data */
1063                0,                       /* addtl numeric data 1 */
1064                0,                       /* addtl numeric data 2 */
1065                NULL,
1066                NULL,
1067                NULL
1068                );
1069         while (1) {                     /* Parse 0 or more switches */
1070             x = cmfdb(&sw);             /* Parse something */
1071             if (x < 0)
1072               return(x);
1073             if (cmresult.fcode != _CMKEY) /* Break out if not a switch */
1074               break;
1075             c = cmgbrk();
1076             if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
1077                 printf("?This switch does not take an argument\n");
1078                 return(-9);
1079             }
1080             if (!getval && (cmgkwflgs() & CM_ARG)) {
1081                 printf("?This switch requires an argument\n");
1082                 return(-9);
1083             }
1084             switch (cmresult.nresult) {
1085               case ASK_PUP:
1086                 popupflg = 1;
1087                 break;
1088               case ASK_GUI:
1089                 guiflg = 1;
1090                 break;
1091               case ASK_TMO: {
1092                   if ((y = cmnum("seconds","1",10,&x,xxstring)) < 0)
1093                     return(y);
1094                   if (x < 0)
1095                     x = 0;
1096                   mytimer = x;
1097                   break;
1098               }
1099               case ASK_DEF: {
1100                   if ((y = cmfld("Text to supply if reply is empty",
1101                                  "",&s,xxstring)) < 0)
1102                     return(y);
1103                   ckstrncpy(dfbuf,s,1024);
1104                   dfanswer = dfbuf;
1105                   break;
1106               }
1107               case ASK_QUI:
1108                 nomsg = 1;
1109                 break;
1110               default: return(-2);
1111             }
1112         }
1113         p = cmresult.sresult;
1114     } else
1115       if ((y = cmtxt(
1116 "Prompt,\n\
1117  enclose in { braces } or \" quotes \" to preserve leading and trailing\n\
1118  spaces, precede question mark with backslash (\\).",
1119                    "",&p,xxstring)) < 0)
1120         return(y);
1121
1122     if (!p) p = "";
1123 #ifndef NOLOCAL
1124 #ifdef OS2
1125     if (popupflg) {                     /* Popup requested */
1126         int len = -1;
1127         ckstrncpy(tmpbuf,brstrip(p),TMPBUFSIZ);
1128         p = tmpbuf;
1129         if (cx == XXASK || cx == XXASKQ) {
1130             if (cx == XXASK)
1131               len = popup_readtext(vmode,NULL,p,line,LINBUFSIZ,mytimer);
1132             else
1133               len = popup_readpass(vmode,NULL,p,line,LINBUFSIZ,mytimer);
1134             asktimedout = ( len < 0 && mytimer );
1135         } else if (cx == XXGOK) {
1136             printf("?Sorry, GETOK /POPUP not implemented yet\n");
1137             timelimit = 0;
1138             return(-9);
1139         }
1140         if (len >= 0) {
1141             y = addmac(vnp,(char *)line); /* Add it to the macro table. */
1142         } else if ( asktimedout && dfanswer ) {
1143             y = addmac(vnp,dfanswer);       /* Add it to the macro table. */
1144             asktimedout = 0;
1145             len = 0;
1146         }
1147         timelimit = 0;
1148         return(success = ((len >= 0) && (y >= 0)) && !asktimedout);
1149     }
1150 #ifdef KUI
1151     if (guiflg) {                       /* GUI requested */
1152         int rc, n;
1153         char * s1;
1154         s1 = tmpbuf;
1155         n = TMPBUFSIZ-1;
1156         zzstring(brstrip(p),&s1,&n);
1157         p = tmpbuf;
1158         if (cx == XXASK || cx == XXASKQ) {
1159             rc = gui_txt_dialog(NULL,p,(cx == XXASK),
1160                                 line,LINBUFSIZ,dfanswer,mytimer);
1161             asktimedout = (rc == -1 && mytimer);
1162             if (rc == 1) {
1163                 y = addmac(vnp,(char *)line); /* Add it to the macro table. */
1164             } else if ( asktimedout && dfanswer ) {
1165                 y = addmac(vnp,dfanswer); /* Add default to macro table. */
1166                 asktimedout = 0;
1167                 rc = 1;
1168             }
1169             timelimit = 0;
1170             return(success = (rc == 1 && (y >= 0)) && !asktimedout);
1171         } else if (cx == XXGOK) {
1172             int x;
1173             x = lookup(yesno,dfanswer,nyesno,NULL);
1174             if (x != 1) x = 2;
1175             rc = uq_ok(NULL, p, 3, NULL, x);
1176             return(success = (rc == 1));
1177         }
1178     }
1179 #endif /* KUI */
1180 #endif /* OS2 */
1181 #endif /* NOLOCAL */
1182
1183     concb((char)escape);                /* Enter CBREAK mode */
1184     cmsavp(psave,PROMPTL);              /* Save old prompt */
1185     cmsetp(brstrip(p));                 /* Make new prompt */
1186
1187 reprompt:
1188     if (cx == XXASKQ) {                 /* For ASKQ, */
1189         cmini(0);                       /* no-echo mode. */
1190         if (echochar)
1191           echostars = echochar;
1192     } else {                            /* For others, regular echoing. */
1193         cmini(ckxech);
1194         echostars = 0;
1195     }
1196     askflag = 1;
1197     x = -1;                             /* This means to reparse. */
1198     cmflgs = 0;
1199     if (pflag)
1200       prompt(xxstring);                 /* Issue prompt. */
1201
1202     asktimedout = 0;                    /* Handle timed responses. */
1203     timelimit = mytimer;
1204 reparse:
1205     cmres();
1206     if (cx == XXGOK) {                  /* GETOK */
1207 #ifdef CK_RECALL
1208         on_recall = 0;
1209 #endif /* CK_RECALL */
1210         askflag = 0;
1211         /* GETOK uses keyword table */
1212         x = cmkey(yesno,nyesno,"",dfanswer,xxstring);
1213         if (x < 0) {                    /* Parse error */
1214             if (x == -10) {
1215                 char * ds;
1216                 ds = dfanswer ? dfanswer : "No";
1217                 if (!nomsg)
1218                   printf("?Timed out, assuming \"%s\"",ds);
1219                 printf("\n");
1220                 asktimedout = 1;
1221                 x = lookup(yesno,ds,nyesno,NULL);
1222                 if (x != 1) x = 0;
1223                 goto gokdone;
1224             } else if (x == -3) {       /* No answer? */
1225                 printf("Please respond Yes or No\n"); /* Make them answer */
1226                 cmini(ckxech);
1227                 goto reprompt;
1228             } else if (x == -1) {
1229                 goto reparse;
1230             } else
1231               goto reprompt;
1232         }
1233         if (cmcfm() < 0)                /* Get confirmation */
1234           goto reparse;
1235   gokdone:
1236         askflag = 0;
1237         cmsetp(psave);                  /* Restore prompt */
1238 #ifdef VMS
1239         if (cmdlvl > 0)                 /* In VMS and not at top level, */
1240           conres();                     /*  restore console again. */
1241 #endif /* VMS */
1242         timelimit = 0;
1243         return(x);                      /* Return success or failure */
1244     } else if (cx == XXGETC             /* GETC */
1245 #ifdef OS2
1246                || cx == XXGETK          /* or GETKEYCODE */
1247 #endif /* OS2 */
1248                ) {                      /* GETC */
1249         char tmp[16];
1250         conbin((char)escape);           /* Put keyboard in raw mode */
1251 #ifndef NOSETKEY
1252 #ifdef OS2
1253         if (cx == XXGETK) {             /* GETKEYCODE */
1254             extern int os2gks;
1255             int t;
1256             t = os2gks;                 /* Turn off kverb recognition */
1257             os2gks = 0;
1258             x = congks(timelimit);      /* Read a key event, blocking */
1259             os2gks = t;                 /* Put back kverb recognition */
1260         } else                          /* GETC */
1261 #endif /* OS2 */
1262 #endif /* NOSETKEY */
1263         {
1264             debug(F101,"GETC conchk","",conchk());
1265             x = coninc(timelimit);      /* Just read one character */
1266             debug(F101,"GETC coninc","",x);
1267         }
1268         concb((char)escape);            /* Put keyboard back in cbreak mode */
1269         if (x > -1) {
1270             if (xcmdsrc == 0)
1271               printf("\r\n");
1272 #ifdef OS2
1273             if (cx == XXGETK) {         /* GETKEYCODE */
1274                 sprintf(tmp,"%d",x);    /* SAFE */
1275             } else {
1276 #endif /* OS2 */
1277                 tmp[0] = (char) (x & 0xff);
1278                 tmp[1] = NUL;
1279 #ifdef OS2
1280             }
1281 #endif /* OS2 */
1282             y = addmac(vnp,tmp);        /* Add it to the macro table. */
1283             debug(F111,"getc/getk addmac",vnp,y);
1284         } else y = -1;
1285         cmsetp(psave);                  /* Restore old prompt. */
1286         if (x < -1) {
1287             asktimedout = 1;
1288             if (!quiet && !nomsg)
1289               printf("?Timed out");
1290             printf("\n");
1291         }
1292         timelimit = 0;
1293         return(success = ((y < 0 ? 0 : 1) && (asktimedout == 0)));
1294     } else {                            /* ASK or ASKQ */
1295 #ifdef CK_RECALL
1296         on_recall = 0;                  /* Don't put response in recall buf */
1297 #endif /* CK_RECALL */
1298         askflag = 1;                    /* ASK[Q] always goes to terminal */
1299         y = cmdgquo();                  /* Get current quoting */
1300         cmdsquo(0);                     /* Turn off quoting */
1301         while (x == -1) {               /* Prompt till they answer */
1302             x = cmtxt("Please respond.",dfanswer,&s,NULL);
1303             debug(F111,"ASK cmtxt",s,x);
1304             cmres();
1305         }
1306         cmdsquo(y);                     /* Restore previous quoting */
1307         if (cx == XXASKQ)               /* ASKQ must echo CRLF here */
1308           printf("\r\n");
1309         if (x == -10 && dfanswer) {     /* Don't fail on timeout if */
1310             s = dfanswer;               /* a default was specified */
1311             asktimedout = 0;            /* and don't fail */
1312             x = 0;
1313         }
1314         if (x < 0) {                    /* If cmtxt parse error, */
1315             cmsetp(psave);              /* restore original prompt */
1316 #ifdef VMS
1317             if (cmdlvl > 0)             /* In VMS and not at top level, */
1318               conres();                 /*  restore console again. */
1319 #endif /* VMS */
1320             if (x == -10) {             /* Timed out with no response */
1321                 if (!nomsg)
1322                   printf("?Timed out");
1323                 printf("\n");
1324                 asktimedout = 1;
1325                 if (dfanswer)           /* Supply default answer if any */
1326                   s = dfanswer;
1327                 success = x = 0;        /* (was "x = -9;") */
1328             }
1329             timelimit = 0;
1330             return(x);                  /* and return cmtxt's error code. */
1331         }
1332         if (!s || *s == NUL) {          /* If user typed a bare CR, */
1333             cmsetp(psave);              /* Restore old prompt, */
1334             delmac(vnp,0);              /* delete variable if it exists, */
1335 #ifdef VMS
1336             if (cmdlvl > 0)             /* In VMS and not at top level, */
1337               conres();                 /*  restore console again. */
1338 #endif /* VMS */
1339             timelimit = 0;
1340             return(success = 1);        /* and return. */
1341         }
1342         y = addmac(vnp,s);              /* Add it to the macro table. */
1343         debug(F111,"ask addmac",vnp,y);
1344         cmsetp(psave);                  /* Restore old prompt. */
1345 #ifdef VMS
1346         if (cmdlvl > 0)                 /* In VMS and not at top level, */
1347           conres();                     /*  restore console again. */
1348 #endif /* VMS */
1349         timelimit = 0;
1350         return(success = (y < 0 ? 0 : 1) && (asktimedout == 0));
1351     }
1352 }
1353 #endif /* NOSPL */
1354
1355 #ifndef NOSPL
1356 int
1357 doincr(cx) int cx; {                    /* INCREMENT, DECREMENT */
1358     char vnambuf[VNAML+1];              /* Buffer for variable names */
1359     int eval = 0;
1360     CK_OFF_T x;
1361     eval = (cx == XX_DECR || cx == XX_INCR);
1362
1363     if ((y = cmfld("Variable name","",&s, eval ? xxstring : NULL)) < 0) {
1364         if (y == -3) {
1365             printf("?Variable name required\n");
1366             return(-9);
1367         } else return(y);
1368     }
1369     ckstrncpy(vnambuf,s,VNAML);
1370     if ((y = cmnumw("by amount","1",10,&x,xxstring)) < 0)
1371       return(y);
1372     if ((y = cmcfm()) < 0)
1373       return(y);
1374
1375     z = (cx == XX_INCR || cx == XXINC) ? 1 : 0; /* Increment or decrement? */
1376
1377     if (incvar(vnambuf,x,z) < 0) {
1378         printf("?Variable %s not defined or not numeric\n",vnambuf);
1379         return(success = 0);
1380     }
1381     return(success = 1);
1382 }
1383
1384 /* Used by doundef() */
1385 static int
1386 xxundef(s,verbose,simulate) char * s; int verbose, simulate; {
1387     int rc = 0;
1388     if (!s) return(0);
1389     if (*s == CMDQ && *(s+1) == '%') {
1390         char c = *(s+2), * p = NULL;
1391         if (c >= '0' && c <= '9') {
1392             if (maclvl < 0)
1393               p = g_var[c];
1394             else
1395               p = m_arg[maclvl][c - '0'];
1396         } else {
1397             if (isupper(c)) c += ('a'-'A');
1398             if (c >= 'a' && c <= 'z')
1399               p = g_var[c];
1400         }
1401         if (!p) return(-1);
1402     }
1403     if (verbose)
1404       printf(" %s ",s);
1405     if (simulate) {
1406         printf("(SELECTED)\n");
1407     } else if ((x = delmac(s,1)) > -1) { /* Full name required */
1408         rc = 1;
1409         if (verbose) printf("(OK)\n");
1410     } else if (verbose)
1411       printf("(FAILED)\n");
1412     return(rc);
1413 }
1414
1415 /* Do the (_)DEFINE, (_)ASSIGN, and UNDEFINE commands */
1416
1417 #define UND_MAT 1
1418 #define UND_VRB 2
1419 #define UND_EXC 3
1420 #define UND_SIM 3
1421
1422 static struct keytab undefswi[] = {
1423     { "/list",     UND_VRB, 0 },
1424 #ifdef COMMENT
1425     { "/except",   UND_EXC, CM_ARG },
1426 #endif /* COMMENT */
1427     { "/matching", UND_MAT, 0 },
1428     { "/simulate", UND_SIM, 0 },
1429     { "/verbose",  UND_VRB, CM_INV }
1430 };
1431 static int nundefswi = sizeof(undefswi) / sizeof(struct keytab);
1432
1433 #define UNDEFMAX 64
1434 static char ** undeflist = NULL;
1435 int
1436 doundef(cx) int cx; {                   /* UNDEF, _UNDEF */
1437     int i, j, n, rc = 0, arraymsg = 0;
1438     int domatch = 0, verbose = 0, errors = 0, simulate = 0, flag = 0;
1439     char *vnp, vnbuf[4];
1440 #ifdef COMMENT
1441     char *except = NULL;
1442 #endif /* COMMENT */
1443     struct FDB sw, fl;
1444     int getval;
1445     char c;
1446
1447     if (!undeflist) {                   /* Allocate list if necessary */
1448         undeflist = (char **)malloc(UNDEFMAX * sizeof(char *));
1449         if (!undeflist) {
1450             printf("?Memory allocation failure\n");
1451             return(-9);
1452         }
1453         for (i = 0; i < UNDEFMAX; i++)
1454           undeflist[i] = NULL;
1455     }
1456     cmfdbi(&sw,                         /* First FDB - command switches */
1457            _CMKEY,                      /* fcode */
1458            "Variable name or switch",
1459            "",                          /* default */
1460            "",                          /* addtl string data */
1461            nundefswi,                   /* addtl numeric data 1: tbl size */
1462            4,                           /* addtl numeric data 2: 4 = cmswi */
1463            xxstring,                    /* Processing function */
1464            undefswi,                    /* Keyword table */
1465            &fl                          /* Pointer to next FDB */
1466            );
1467     cmfdbi(&fl,                         /* Anything that doesn't match */
1468            _CMFLD,                      /* fcode */
1469            "",                          /* hlpmsg */
1470            "",                          /* default */
1471            "",                          /* addtl string data */
1472            0,                           /* addtl numeric data 1 */
1473            0,                           /* addtl numeric data 2 */
1474            (cx == XXUNDEF) ? NULL : xxstring,
1475            NULL,
1476            NULL
1477            );
1478     while (1) {                         /* Parse 0 or more switches */
1479         x = cmfdb(&sw);                 /* Parse something */
1480         if (x < 0)
1481           return(x);
1482         if (cmresult.fcode != _CMKEY)   /* Break out if not a switch */
1483           break;
1484         c = cmgbrk();
1485         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
1486             printf("?This switch does not take an argument\n");
1487             return(-9);
1488         }
1489         switch (cmresult.nresult) {
1490           case UND_MAT: domatch  = 1; break;
1491           case UND_SIM: simulate = 1; /* fall thru on purpose */
1492           case UND_VRB: verbose  = 1; break;
1493
1494 #ifdef COMMENT
1495           case UND_EXC:
1496             if (!getval) break;
1497             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
1498                 if (x == -3) {
1499                     printf("?Pattern required\n");
1500                     x = -9;
1501                 }
1502                 goto xgetx;
1503             }
1504             makestr(&except,cmresult.sresult);
1505             break;
1506 #endif /* COMMENT */
1507
1508           default:
1509             return(-2);
1510         }
1511     }
1512     n = 0;
1513     makestr(&(undeflist[n++]),cmresult.sresult);
1514     for (i = 1; i < UNDEFMAX; i++) {
1515         x = cmfld("Macro or variable name","",&s,
1516                   ((cx == XXUNDEF) ? NULL : xxstring)
1517                   );
1518         if (x == -3) {
1519             if ((y = cmcfm()) < 0)
1520               return(y);
1521             break;
1522         } else if (y < 0) {
1523             return(y);
1524         }
1525         makestr(&(undeflist[n++]),s);
1526     }
1527     /* Now we have a list of n variables or patterns to undefine */
1528
1529     for (i = 0; i < n; i++) {
1530         flag = 0;
1531         if (!(vnp = undeflist[i]))
1532           continue;
1533         if (vnp[0] == CMDQ && (vnp[1] == '%' || vnp[1] == '&')) {
1534             flag++;
1535             vnp++;
1536         }
1537         if (!domatch) {                 /* Pattern match not requested */
1538             if (flag) {
1539                 if ((y = parsevar(vnp,&x,&z)) < 0) {
1540                     vnp--;
1541                     if (verbose) printf(" %s...error\n",vnp);
1542                     continue;
1543                 }
1544                 vnp--;
1545             }
1546             x = xxundef(vnp,verbose,simulate);
1547             if (x > -1) {
1548                 if (!x && !simulate) errors++;
1549                 rc += x;
1550             }
1551             continue;
1552         }
1553         /* Pattern match requested */
1554
1555         if (!flag) {                    /* It's a macro */
1556             for (j = 0; j < nmac; j++) {
1557                 if (ckmatch(vnp,mactab[j].kwd,0,1)) {
1558                     x = xxundef(mactab[j].kwd,verbose,simulate);
1559                     if (x > -1) {
1560                         rc += x;
1561                         if (!x) errors++;
1562                     }
1563                     if (!simulate)
1564                       j--;              /* Because mactab shifted up */
1565                 }
1566             }
1567         } else if (vnp[0] == '%') {     /* It's a \%x variable */
1568             vnbuf[0] = CMDQ;
1569             vnbuf[1] = '%';
1570             vnbuf[3] = NUL;
1571             for (j = '0'; j <= 'z'; j++) { /* 0..9 a..z */
1572                 vnbuf[2] = j;
1573                 if (ckmatch(vnp,&vnbuf[1],0,1)) {
1574                     x = xxundef(vnbuf,verbose,simulate);
1575                     if (x > -1) {
1576                         if (!x) errors++;
1577                         rc += x;
1578                     }
1579                 }
1580                 if (j == '9') j = (int)'a' - 1; /* 9 -> a */
1581             }
1582         } else if (vnp[0] == '&') {
1583             if (!arraymsg && !quiet) {
1584                 printf("?UNDEFINE /MATCH can't be used with arrays.\n");
1585                 printf("(Type HELP ARRAY to see other methods.)\n");
1586             }
1587             arraymsg++;
1588             errors++;
1589         }
1590     }
1591     if (verbose)
1592       printf("undefined: %d, errors: %d\n",rc,errors);
1593
1594     for (i = 0; i < UNDEFMAX; i++) {    /* Check them all */
1595         if (undeflist[i]) {             /* in case we were interrupted */
1596             free(undeflist[i]);         /* previously... */
1597             undeflist[i] = NULL;
1598         }
1599     }
1600     return(success = (errors == 0) ? 1 : 0);
1601 }
1602
1603 int
1604 dodef(cx) int cx; {
1605     extern int xxdot;
1606     extern char ppvnambuf[];
1607     int doeval = 0;
1608     char vnambuf[VNAML+1];              /* Buffer for variable names */
1609     char *vnp;                          /* Pointer to same */
1610     int k, mydot;
1611     mydot = xxdot;                      /* Copy */
1612     xxdot = 0;                          /* and reset */
1613 /*
1614   In case we got here from a command that begins like ".\%a", cmkey() has
1615   already evaluated \%a, but we don't want that, so we retrieve the variable
1616   name from a special pre-evaluation buffer in the command module, and we
1617   undo the "unget word" that would be done because of the token, because if
1618   the variable was defined, it will unget its value rather than its name.
1619 */
1620     s = NULL;
1621
1622     if (mydot && ppvnambuf[0] == '.' && ppvnambuf[1]) {
1623         s = ppvnambuf+1;
1624         unungw();
1625     }
1626     if (!s) {
1627         if (cx == XXDFX || cx == XXASX)
1628           /* Evaluate variable name */
1629           y = cmfld("Macro or variable name","",&s,xxstring);
1630         else
1631           /* Don't evaluate the variable name */
1632           y = cmfld("Macro or variable name","",&s,NULL);
1633         if (y < 0) {
1634             if (y == -3) {
1635                 printf("?Variable name required\n");
1636                 return(-9);
1637             } else return(y);
1638         }
1639     }
1640     k = strlen(s);
1641     if (k > VNAML) {
1642         printf("?Name too long: \"%s\"\n",s);
1643         return(-9);
1644     }
1645     ckstrncpy(vnambuf,s,VNAML);
1646     vnambuf[VNAML] = NUL;
1647     vnp = vnambuf;
1648     if (vnambuf[0] == CMDQ && (vnambuf[1] == '%' || vnambuf[1] == '&')) vnp++;
1649     if (*vnp == '%' || *vnp == '&') {
1650         if ((y = parsevar(vnp,&x,&z)) < 0) return(y);
1651 #ifdef COMMENT
1652         if (cx == XXUNDEF) {            /* Undefine */
1653             if ((y = cmtxt("Text to be ignored","",&s,NULL)) < 0) return(y);
1654             delmac(vnp,0);
1655             return(success = 1);
1656         }
1657 #endif /* COMMENT */
1658         debug(F101,"dodef parsevar x","",x);
1659         if (mydot) {
1660             if ((doeval = cmkey(asgtab,nasgtab,"operator","=",NULL)) < 0)
1661               return(doeval);
1662             if (doeval > 0)             /* Type of assignment */
1663               cx = XXASS;
1664         }
1665         if (y == 1) {                   /* Simple variable */
1666             if ((y = cmtxt("Definition of variable","",&s,NULL)) < 0)
1667               return(y);
1668             s = brstrip(s);
1669             debug(F110,"xxdef var name",vnp,0);
1670             debug(F110,"xxdef var def",s,0);
1671         } else if (y == 2) {            /* Array element */
1672             if ((y = arraynam(vnp,&x,&z)) < 0) return(y);
1673             if (x == 96) {
1674                 printf("?Argument vector array is read-only\n");
1675                 return(-9);
1676             }
1677             if (chkarray(x,z) < 0) return(-2);
1678             if ((y = cmtxt("Definition of array element","",&s,NULL)) < 0)
1679               return(y);
1680             debug(F110,"xxdef array ref",vnp,0);
1681             debug(F110,"xxdef array def",s,0);
1682         }
1683     } else {                            /* Macro */
1684 #ifdef COMMENT
1685         if (cx == XXUNDEF) {            /* Undefine */
1686             if ((y = cmtxt("Text to be ignored","",&s,NULL)) < 0) return(y);
1687             delmac(vnp,0);
1688             return(success = 1);
1689         }
1690 #endif /* COMMENT */
1691         if (mydot) {
1692             if ((doeval = cmkey(asgtab,nasgtab,"operator","=",NULL)) < 0)
1693               return(doeval);
1694             if (doeval > 0)
1695               cx = XXASS;
1696         }
1697         if ((y = cmtxt("Definition of macro","",&s,NULL)) < 0) return(y);
1698 #ifdef DEBUG
1699         if (deblog) {
1700             debug(F110,"xxdef macro name",vnp,0);
1701             debug(F010,"xxdef macro def",s,0);
1702         }
1703 #endif /* DEBUG */
1704         s = brstrip(s);
1705     }
1706     if (*s == NUL) {                    /* No arg given, undefine */
1707         delmac(vnp,1);                  /* silently... */
1708         return(success = 1);            /* even if it doesn't exist... */
1709     }
1710     /* Defining a new macro or variable */
1711
1712     if (cx == XXASS || cx == XXASX) {   /* ASSIGN rather than DEFINE? */
1713         int t;
1714         t = LINBUFSIZ-1;
1715         lp = line;                      /* If so, expand its value now */
1716         zzstring(s,&lp,&t);
1717         s = line;
1718     }
1719     if (doeval == 2) {                  /* Arithmetic evaluation wanted too? */
1720         ckstrncpy(line,evala(s),LINBUFSIZ);
1721         line[LINBUFSIZ] = NUL;
1722     }
1723     /* debug(F111,"calling addmac",s,(int)strlen(s)); */
1724
1725     y = addmac(vnp,s);                  /* Add it to the appropriate table. */
1726     if (y < 0) {
1727         printf("?%s failed\n",(cx == XXASS || cx == XXASX) ?
1728                "ASSIGN" : "DEFINE");
1729         return(success = 0);
1730     } else if (cx == XXASX || cx == XXDFX) /* For _ASG or _DEF, */
1731       return(1);                           /* don't change success variable */
1732     else
1733       return(success = 1);
1734 }
1735 #endif /* NOSPL */
1736
1737
1738 #ifndef NODIAL
1739 /*
1740    L U D I A L  --  Lookup up dialing directory entry.
1741
1742    Call with string to look up and file descriptor of open dialing directory
1743    file.  On success, returns number of matches found, with numbers stored
1744    in an array accessible via getdnum().
1745 */
1746 static char *dn_p[MAXDNUMS + 1];        /* Dial Number pointers */
1747 static char *dn_p2[MAXDNUMS + 1];       /* Converted dial number pointers */
1748 static int dn_x[MAXDNUMS + 1];          /* Type of call */
1749 static int dncount = 0;
1750 char * d_name = NULL;                   /* Dial name pointer */
1751
1752 char *                                  /* Get dial directory entry name */
1753 getdname() {
1754     return(d_name ? d_name : "");
1755 }
1756
1757 char *
1758 getdnum(n) int n; {                     /* Get dial number n from directory */
1759     if (n < 0 || n > dncount || n > MAXDNUMS)
1760       return("");
1761     else
1762       return(dn_p[n]);
1763 }
1764
1765 char *                  /* Check area code for spurious leading digit */
1766 chk_ac(i,buf) int i; char buf[]; {
1767     char *p;
1768     if (!buf)
1769       return("");
1770     p = (char *) buf;                   /* Country we are calling: */
1771     if (i ==  44 ||                     /* UK */
1772         i ==  49 ||                     /* Germany */
1773         i ==  39 ||                     /* Italy */
1774         i ==  31 ||                     /* Netherlands */
1775         i == 351 ||                     /* Portugal */
1776         i ==  55 ||                     /* Brazil */
1777         i == 972 ||                     /* Israel */
1778         i ==  41 ||                     /* Switzerland */
1779         i ==  43 ||                     /* Austria */
1780         i ==  42 ||                     /* Czech Republic */
1781         i ==  36 ||                     /* Hungary */
1782         i ==  30 ||                     /* Greece */
1783         i == 352 ||                     /* Luxembourg */
1784         i ==  48 ||                     /* Poland */
1785         i ==  27 ||                     /* South Africa */
1786         i ==  33 ||                     /* France (as of 1997) */
1787         i ==  358                       /* Finland (ditto) */
1788         ) {
1789         if (buf[0] == '0')
1790           p++;
1791     }
1792     return(p);
1793 }
1794
1795 /* Call Is Long Distance -- Expand this to cover 10-digit local dialing etc */
1796 /*
1797    src  = area code of caller
1798    dest = area code of callee
1799    Returns:
1800      0 if call is local
1801      1 if call is long distance
1802      2 if call is local but area code must be dialed anyway
1803 */
1804 static int
1805 callisld(src, dest) char * src, * dest; {
1806     int i;
1807     if (dialfld)                        /* Force long distance? */
1808       return(1);
1809     if (!strcmp(src,dest)) {            /* Area codes are the same */
1810         for (i = 0; i < nlocalac; i++)  /* Is AC in the lc-area-codes list? */
1811           if (!strcmp(src,diallcac[i]))
1812             return(2);                  /* Yes so must be dialed */
1813         return(0);                      /* No so don't dial it. */
1814     }
1815     for (i = 0; i < nlocalac; i++)      /* ACs not the same so look in list */
1816       if (!strcmp(dest,diallcac[i]))    /* Match */
1817         return(2);                      /* So local call with area code */
1818     return(1);                          /* Not local so long-distance */
1819 }
1820
1821 char pdsfx[64] = { NUL, NUL };
1822
1823 #ifndef NOSPL
1824 static char *
1825 xdial(s) char *s; {                     /* Run dial string thru macro */
1826     int x, m;
1827     char * s2;
1828     s2 = NULL;
1829     makestr(&s2,s);                     /* Copy the argument */
1830     if (!dialmac)                       /* Dial macro name given? */
1831       return(NULL);
1832     if ((x = mxlook(mactab,dialmac,nmac)) < 0) /* Is the macro defined? */
1833       return(NULL);
1834     m = maclvl;
1835     x = dodo(x,s2,0);                   /* Set up the macro */
1836     if (s2) free(s2);
1837     if (x > 0) {
1838         while (maclvl > m)              /* Execute the parser */
1839           parser(1);
1840         return(mrval[maclvl+1]);        /* Return the result */
1841     }
1842     return(NULL);
1843 }
1844 #endif /* NOSPL */
1845
1846 static int
1847 dncvt(k,cx, prefix, suffix)
1848     int k, cx, prefix, suffix; {        /* Dial Number Convert */
1849     int i, j, n, what;                  /* cx is top-level command index */
1850     char *ss;                           /* prefix - add prefixes? */
1851     char *p, *p2, *pxo;                 /* suffix - add suffixes? */
1852     char *lac;
1853     char *npr;
1854     char *sfx;
1855     /* char *psfx; */
1856     char ccbuf[128];
1857     int cc;
1858     char acbuf[24];
1859     char *acptr;
1860     char outbuf[256];
1861 /*
1862   First pass for strict (punctuation-based) interpretation.
1863   If it fails, we try the looser (length-based) one.
1864 */
1865     dialtype = -1;
1866     what = 0;                           /* Type of call */
1867     s = dn_p[k];                        /* Number to be converted. */
1868     debug(F111,"dncvt",s,k);
1869     if (dn_p2[k]) {
1870         free(dn_p2[k]);
1871         dn_p2[k] = NULL;
1872     }
1873     if (!s) {
1874         printf("Error - No phone number to convert\n");
1875         return(-1);
1876     }
1877     if ((int)strlen(s) > 200) {
1878         ckstrncpy(outbuf,s,40);
1879         printf("?Too long: \"%s...\"\n",outbuf);
1880         return(-1);
1881     }
1882     npr = (prefix && dialnpr) ? dialnpr : "";
1883     sfx = (suffix && dialsfx) ? dialsfx : "";
1884     /* if (partial) psfx = dialsfx ? dialsfx : ""; */
1885     pxo = (prefix && dialpxo) ? dialpxo : "";
1886     lac = diallac ? diallac : "";       /* Local area code */
1887
1888     outbuf[0] = NUL;                    /* Initialize conversion buffer */
1889     ss = s;                             /* Remember original string */
1890
1891     if (*s != '+') {                    /* Literal number */
1892         dn_x[k] = DN_UNK;               /* Sort key is "unknown". */
1893         ckmakmsg(outbuf,256,            /* Sandwich it between */
1894                  pxo,npr,s,sfx          /* DIAL PREFIX and SUFFIX */
1895                 );
1896 #ifdef CK_TAPI
1897         if (tttapi &&                   /* TAPI does its own conversions */
1898             !tapipass &&                /* if not in passthru mode */
1899             tapiconv == CK_AUTO ||      /* and TAPI conversions are AUTO */
1900             tapiconv == CK_ON           /* OR if TAPI conversions are ON */
1901             ) {
1902             char * p = NULL;
1903             dialtype = -2;
1904             if (!cktapiConvertPhoneNumber(dn_p[k], &p))
1905               return(-1);
1906             makestr(&dn_p2[k], p);
1907             if (p) free(p);
1908             return(0);
1909         } else
1910 #endif /* CK_TAPI */
1911           makestr(&dn_p2[k], outbuf);   /* Not TAPI */
1912         dialtype = what;
1913         return(0);                      /* Done. */
1914     }
1915     i = 0;                              /* Portable number */
1916     s++;                                /* Tiptoe past the plus sign */
1917     ccbuf[0] = NUL;                     /* Do country code first */
1918
1919     if (!diallcc) {                     /* Do we know our own? */
1920         if (cx != XXLOOK)
1921           printf("Error - prior SET DIAL COUNTRY-CODE command required\n");
1922         return(-1);
1923     }
1924
1925     /* Parse the number */
1926
1927     while (1) {                         /* Get the country code */
1928         while (*s == HT || *s == SP)
1929           s++;
1930         if (!s)                         /* Not in standard format */
1931           break;
1932         if (*s == '(') {                /* Beginning of area code  */
1933             s++;                        /* Skip past parenthesis   */
1934             ccbuf[i] = NUL;             /* End of country code */
1935             if (!s) {                   /* Check for end of string */
1936                 printf("Error - phone number ends prematurely: \"%s\"\n",ss);
1937                 return(-1);
1938             }
1939             break;
1940         } else {                        /* Collect country code */
1941             if (isdigit(*s))
1942               ccbuf[i++] = *s;          /* copy this character */
1943             s++;
1944             if (!*s || i > 127)         /* watch out for memory leak */
1945               break;
1946         }
1947     }
1948     cc = atoi(ccbuf);                   /* Numeric version of country code */
1949
1950     i = 0;                              /* Now get area code */
1951     acbuf[0] = NUL;                     /* Initialize area-code buffer */
1952     acptr = acbuf;                      /* and pointer. */
1953     while (1) {
1954         while (*s == HT || *s == SP)    /* Ignore whitespace */
1955           s++;
1956         if (!s)                         /* String finished */
1957           break;
1958         if (*s == ')') {                /* End of area code  */
1959             s++;                        /* Skip past parenthesis   */
1960             acbuf[i] = NUL;             /* Terminate area-code buffer */
1961             break;
1962         } else {                        /* Part of area code */
1963             if (isdigit(*s))            /* If it's a digit, */
1964               acbuf[i++] = *s;          /* copy this character */
1965             s++;                        /* Point to next */
1966             if (!*s || i > 23)          /* Watch out for overflow */
1967               break;
1968         }
1969     }
1970
1971 /*
1972    Here we strip any leading 0 for countries that we know have
1973    0 as a long-distance prefix and do not have any area codes that
1974    start with 0 (formerly also ditto for "9" in Finland...)
1975 */
1976     i = atoi(ccbuf);
1977     acptr = chk_ac(i,acbuf);
1978
1979     while (*s == HT || *s == SP)        /* Skip whitespace */
1980       s++;
1981
1982 /* printf("S=[%s], ACPTR=[%s]\n",s,acptr); */
1983
1984     if (*s && *acptr) {                 /* Area code was delimited */
1985
1986         while (*s == '-' || *s == '.')  /* Skip past gratuitious punctuation */
1987           s++;
1988         if (!*s) s--;                   /* But not to end of string */
1989
1990         if (strcmp(diallcc,ccbuf)) {    /* Out of country? */
1991             if (!dialixp) {             /* Need intl-prefix */
1992                 if (cx != XXLOOK)
1993                   printf("Error - No international dialing prefix defined\n");
1994                 return(-1);
1995             }
1996             what = dn_x[k] = DN_INTL;
1997             p  = (prefix && dialixp) ? dialixp : ""; /* Intl-prefix */
1998             p2 = (suffix && dialixs) ? dialixs : ""; /* Intl-suffix */
1999
2000             /* Form the final phone number */
2001 #ifdef COMMENT
2002             sprintf(pdsfx,"%s%s",p2,sfx); /* UNSAFE */
2003             sprintf(outbuf,
2004                     "%s%s%s%s%s%s%s%s",
2005                     pxo,npr,p,ccbuf,acptr,s,p2,sfx
2006                     );
2007 #else
2008             ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL);
2009             ckmakxmsg(outbuf,256,pxo,npr,p,ccbuf,acptr,s,p2,sfx,
2010                       NULL,NULL,NULL,NULL);
2011 #endif /* COMMENT */
2012
2013         } else if ((x = callisld(lac,acptr)) >= 1) { /* In-country LD */
2014             if (!diallac && cx != XXLOOK) { /* Don't know my own area code */
2015                 if (cc == 1)
2016                   printf("WARNING - Prior SET DIAL AREA-CODE needed\n");
2017             }
2018             if (x == 2) {               /* Local call with area code */
2019                 what = dn_x[k] = DN_LOCAL;      /* Local-call */
2020                 p  = (prefix && diallcp) ? diallcp : ""; /* local-prefix */
2021                 p2 = (suffix && diallcs) ? diallcs : ""; /* local-suffix */
2022             } else {
2023                 what = dn_x[k] = DN_LONG;       /* Long-distance */
2024                 for (i = 0; i < ntollfree; i++) { /* But toll-free too? */
2025                     if (!strcmp(acptr,dialtfc[i])) {
2026                         what = dn_x[k] = DN_FREE;
2027                         break;
2028                     }
2029                 }
2030                 if (what == DN_FREE) {  /* Toll-free call */
2031                     p = (prefix && dialtfp) ? dialtfp :
2032                         ((prefix && dialldp) ? dialldp : "");
2033                     p2 = "";            /* no suffix */
2034                 } else {                        /* normal long distance */
2035                     p  = (prefix && dialldp) ? dialldp : ""; /* ld-prefix */
2036                     p2 = (suffix && diallds) ? diallds : ""; /* ld-suffix */
2037                 }
2038             }
2039             /* Form the number to be dialed */
2040 #ifdef COMMENT
2041             sprintf(outbuf,"%s%s%s%s%s%s%s",
2042                     pxo,npr,p,acptr,s,p2,sfx
2043                     );
2044             sprintf(pdsfx,"%s%s",p2,sfx);
2045 #else
2046             ckmakxmsg(outbuf,256,
2047                       pxo,npr,p,acptr,s,p2,sfx,
2048                       NULL,NULL,NULL,NULL,NULL);
2049             ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL);
2050 #endif /* COMMENT */
2051         } else {                        /* Same country, same area code */
2052             what = dn_x[k] = DN_LOCAL;  /* So it's a local call. */
2053             if (!prefix || !(dialpxo || ndialpxx)) { /* Not dialing from PBX */
2054                 p  = (prefix && diallcp) ? diallcp : ""; /* local-prefix */
2055                 p2 = (suffix && diallcs) ? diallcs : ""; /* local-suffix */
2056 #ifdef COMMENT
2057                 if (x == 2)
2058                   sprintf(outbuf,"%s%s%s%s%s%s",npr,p,acptr,s,p2,sfx);
2059                 else
2060                   sprintf(outbuf,"%s%s%s%s%s",npr,p,s,p2,sfx);
2061                 sprintf(pdsfx,"%s%s",p2,sfx);
2062 #else
2063                 if (x == 2)
2064                   ckmakxmsg(outbuf,256,
2065                             npr,p,acptr,s,p2,sfx,
2066                             NULL,NULL,NULL,NULL,NULL,NULL);
2067                 else
2068                   ckmakxmsg(outbuf,256,
2069                             npr,p,s,p2,sfx,
2070                             NULL,NULL,NULL,NULL,NULL,NULL,NULL);
2071                 ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL);
2072 #endif /* COMMENT */
2073
2074             } else {                    /* Dialing from a PBX and not TAPI */
2075                 if (ndialpxx) {         /* Is it internal? */
2076 #ifdef COMMENT
2077                     i = (int) strlen(dialpxx);
2078                     j = (int) strlen(s);
2079                     x = -1;
2080                     if (j > i)
2081                       x = ckstrcmp(dialpxx,s,i,0);
2082 #else
2083                     int kx;
2084                     x = -1;
2085                     j = (int) strlen(s);
2086                     for (kx = 0; kx < ndialpxx; kx++) {
2087                         i = (int) strlen(dialpxx[kx]);
2088                         if (j > i)
2089                           if (!(x = ckstrcmp(dialpxx[kx],s,i,0)))
2090                             break;
2091                     }
2092 #endif /* COMMENT */
2093                     if (!x) {
2094                         char * icp, buf[32];
2095                         makestr(&matchpxx,dialpxx[kx]);
2096                         debug(F111,"dncvt matchpxx",matchpxx,kx);
2097                         what = dn_x[kx] = DN_INTERN;   /* Internal call. */
2098                         s += i;
2099                         /* Internal-call prefix */
2100                         icp = dialpxi;
2101 #ifndef NOSPL
2102                         if (icp) {
2103                             if (*icp == '\\') {
2104                                 char c, *bp;
2105                                 int n;
2106                                 c = *(icp+1);
2107                                 if (isupper(c)) c = tolower(c);
2108                                 if (c == 'v' || c == 'f') {
2109                                     n = 32;
2110                                     bp = buf;
2111                                     zzstring(icp,&bp,&n);
2112                                     icp = buf;
2113                                 }
2114                             }
2115                         }
2116 #endif /* NOSPL */
2117                         p = (prefix && icp) ? icp : "";
2118 #ifdef COMMENT
2119                         sprintf(outbuf,"%s%s%s%s",npr,p,s,sfx);
2120 #else
2121                         ckmakmsg(outbuf,256,npr,p,s,sfx);
2122 #endif /* COMMENT */
2123                     } else {            /* External local call */
2124                         /* local-prefix */
2125                         p  = (prefix && diallcp) ? diallcp : "";
2126                         /* local-suffix */
2127                         p2 = (prefix && diallcs) ? diallcs : "";
2128 #ifdef COMMENT
2129                         if (x == 2)
2130                           sprintf(outbuf,"%s%s%s%s%s%s%s",
2131                                   dialpxo ? dialpxo : "",
2132                                   npr,p,acptr,s,p2,sfx);
2133                         else
2134                           sprintf(outbuf,
2135                                   "%s%s%s%s%s%s",
2136                                   dialpxo ? dialpxo : "",
2137                                   npr,p,s,p2,sfx
2138                                   );
2139 #else
2140                         if (x == 2)
2141                           ckmakxmsg(outbuf, 256,
2142                                    dialpxo ? dialpxo : "",
2143                                    npr,p,acptr,s,p2,sfx,
2144                                    NULL,NULL,NULL,NULL,NULL);
2145                         else
2146                           ckmakxmsg(outbuf, 256,
2147                                     dialpxo ? dialpxo : "",
2148                                     npr,p,s,p2,sfx,
2149                                     NULL,NULL,NULL,NULL,NULL,NULL);
2150 #endif /* COMMENT */
2151                     }
2152                 }
2153             }
2154         }
2155
2156     } else {                            /* Area code was not delimited */
2157
2158         char xbuf[256];                 /* Comparison based only on length */
2159         char ybuf[256];
2160         int x, j;
2161
2162         s = ss;
2163
2164         for (i = 0; i < 255; i++) {
2165             if (!*s) break;
2166             while (!isdigit(*s)) {      /* Pay attention only to digits */
2167                 s++;
2168                 if (!*s) break;
2169             }
2170             xbuf[i] = *s++;
2171         }
2172         xbuf[i] = NUL;
2173
2174         x = 1;                          /* Assume LD */
2175         n = 0;
2176         if (!dialfld) {                 /* If LD not forced */
2177             for (j = 0; j < nlocalac; j++) { /* check local AC list? */
2178                 ckmakmsg(ybuf,256,diallcc,diallcac[j],NULL,NULL);
2179                 n = (int) strlen(ybuf);
2180                 if (n > 0 && !ckstrcmp(xbuf,ybuf,n,0)) {
2181                     x = 2;
2182                     break;
2183                 }
2184             }
2185             if (x == 1) {               /* Or exact match with local CC+AC? */
2186                 ckmakmsg(ybuf,256,diallcc,lac,NULL,NULL);
2187                 n = (int) strlen(ybuf);
2188                 if (n > 0 && !ckstrcmp(xbuf,ybuf,n,0))
2189                   x = 0;
2190             }
2191         }
2192         if (x == 0 || x == 2) {         /* Local call */
2193             int xx,kx;                  /* Begin 1 Dec 2001... */
2194             /* Account for PBX internal calls */
2195             if (ndialpxx) {
2196                 xx = -1;
2197                 j = (int) strlen(ybuf);
2198                 for (kx = 0; kx < ndialpxx; kx++) {
2199                     i = (int) strlen(dialpxx[kx]);
2200                     if (j >= i)
2201                       if (!(xx = ckstrcmp(dialpxx[kx],&xbuf[j],i,0)))
2202                         break;
2203                 }
2204             }
2205             if (!xx) {
2206                 char * icp, buf[32];
2207                 makestr(&matchpxx,dialpxx[kx]);
2208                 debug(F111,"dncvt matchpxx",matchpxx,kx);
2209                 what = dn_x[kx] = DN_INTERN; /* Internal call. */
2210                 s = xbuf + j + i;
2211                 icp = dialpxi;          /* Internal-call prefix */
2212 #ifndef NOSPL
2213                 if (icp) {
2214                     if (*icp == '\\') {
2215                         char c, *bp;
2216                         int n;
2217                         c = *(icp+1);
2218                         if (isupper(c)) c = tolower(c);
2219                         if (c == 'v' || c == 'f') {
2220                             n = 32;
2221                             bp = buf;
2222                             zzstring(icp,&bp,&n);
2223                             icp = buf;
2224                         }
2225                     }
2226                 }
2227 #endif /* NOSPL */
2228                 p = (prefix && icp) ? icp : "";
2229                 ckmakmsg(outbuf,256,npr,p,s,sfx);
2230                 /* End 1 Dec 2001... */
2231
2232             } else {                    /* Not PBX internal */
2233
2234                 dn_x[k] = DN_LOCAL;
2235                 p = (prefix && diallcp) ? diallcp : "";
2236                 p2 = (suffix && diallcs) ? diallcs : "";
2237                 s = (char *) (xbuf + ((x == 0) ? n : (int)strlen(diallcc)));
2238                 ckmakxmsg(outbuf,256,
2239                           pxo,npr,p,s,p2,sfx,
2240                           NULL,NULL,NULL,NULL,NULL,NULL);
2241                 ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL);
2242             }
2243         } else {                        /* Not local */
2244             n = ckstrncpy(ybuf,diallcc,256);
2245             if (n > 0 && !ckstrcmp(xbuf,ybuf,n,0)) { /* Long distance */
2246                 dn_x[k] = DN_LONG;
2247                 p = (prefix && dialldp) ? dialldp : "";
2248                 p2 = (suffix && diallds) ? diallds : "";
2249                 s = xbuf + n;
2250                 while (*s == '-' || *s == '.')
2251                   s++;
2252 #ifdef COMMENT
2253                 sprintf(outbuf,"%s%s%s%s%s%s",pxo,npr,p,s,p2,sfx);
2254                 sprintf(pdsfx,"%s%s",p2,sfx);
2255 #else
2256                 ckmakxmsg(outbuf,256,
2257                           pxo,npr,p,s,p2,sfx,
2258                          NULL,NULL,NULL,NULL,NULL,NULL);
2259                 ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL);
2260 #endif /* COMMENT */
2261             } else {
2262                 dn_x[k] = DN_INTL;      /* International */
2263                 if (!dialixp) {
2264                     if (cx != XXLOOK) {
2265                         printf(
2266                           "Error - No international dialing prefix defined\n"
2267                                );
2268                         return(-1);
2269                     }
2270                 }
2271                 p = (prefix && dialixp) ? dialixp : "";
2272                 p2 = (suffix && dialixs) ? dialixs : "";
2273 #ifdef COMMENT
2274                 sprintf(outbuf,"%s%s%s%s%s%s",pxo,npr,p,xbuf,p2,sfx);
2275                 sprintf(pdsfx,"%s%s",p2,sfx);
2276 #else
2277                 ckmakxmsg(outbuf,256,
2278                           pxo,npr,p,xbuf,p2,sfx,
2279                           NULL,NULL,NULL,NULL,NULL,NULL);
2280                 ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL);
2281 #endif /* COMMENT */
2282             }
2283         }
2284     }
2285 #ifdef CK_TAPI
2286     if (tttapi &&                       /* TAPI performs the conversions */
2287         !tapipass &&
2288         tapiconv == CK_AUTO ||
2289         tapiconv == CK_ON
2290         ) {
2291         p = NULL;
2292         dialtype = -2;
2293         if (!cktapiConvertPhoneNumber(dn_p[k],&p))
2294           return(-1);
2295         makestr(&dn_p2[k], p);
2296         if (p) free(p);
2297         return(0);
2298     } else {
2299 #endif /* CK_TAPI */
2300         makestr(&dn_p2[k], outbuf);
2301 #ifdef CK_TAPI
2302     }
2303 #endif /* CK_TAPI */
2304     dialtype = what;
2305     return(0);
2306 }
2307
2308 static int
2309 ddcvt(s, f, n) char * s; FILE * f; int n; { /* Dial Directory Convert */
2310     char linebuf[1024], *s2;            /* Buffers and pointers */
2311 #ifdef VMS
2312     char * temp = NULL;
2313 #endif /* VMS */
2314     char *info[8];                      /* Pointers to words from entry */
2315     FILE * f2 = NULL;
2316     int x, rc;
2317     rc = -1;
2318
2319     debug(F110,"ddcvt file",s,0);
2320
2321     if (!s || !f)                       /* No filename or file */
2322       return(-1);
2323     if (!*s)
2324
2325     fclose(f);
2326     znewn(s,&s2);                       /* s2 = address of static buffer */
2327     debug(F110,"ddcvt newname",s2,0);
2328
2329 #ifdef VMS
2330     /* In VMS, znewn() returns the same file name with a new version number */
2331     makestr(&temp,s);                   /* Swap - otherwise the new */
2332     s = s2;                             /* version has the older version */
2333     s2 = temp;                          /* number... */
2334     debug(F110,"ddcvt after swap s",s,0);
2335     debug(F110,"ddcvt after swap s2",s2,0);
2336     makestr(&(dialdir[n]),s);           /* New file gets new version number */
2337     debug(F110,"ddcvt after makestr s2",s2,0);
2338     debug(F111,"ddcvt dialdir[n]",dialdir[n],n);
2339 #else
2340     if (zrename(s,s2) < 0) {            /* Not VMS - rename old file */
2341         perror(s2);                     /* to new (wierd) name. */
2342         goto ddexit;
2343     }
2344 #endif /* VMS */
2345     debug(F110,"ddcvt s2 (old)",s2,0);
2346     if ((f = fopen(s2,"r")) == NULL) {  /* Reopen old file with wierd name */
2347         debug(F110,"ddcvt s2 open error",ck_errstr(),0);
2348         dirline = 0;                    /* (or in VMS, old version) */
2349         perror(s2);
2350         goto ddexit;
2351     }
2352     debug(F110,"ddcvt fopen(s2) OK",s2,0);
2353
2354     debug(F110,"ddcvt s (new)",s,0);
2355     if ((f2 = fopen(s,"w")) == NULL) {  /* Create new file with old name */
2356         debug(F110,"ddcvt s open error",ck_errstr(),0);
2357         perror(s);                      /* (or in VMS, new version) */
2358         goto ddexit;
2359     }
2360     debug(F110,"ddcvt fopen(s) OK",s,0);
2361
2362     printf("\nSaving old directory as %s.\nConverting %s...",s2,s);
2363     fprintf(f2,"; %s - Kermit dialing directory\n", s);
2364     fprintf(f2,"%-16s %-20s ; %5s %-6s ; %s\n",
2365                "; Name","Number","Speed","Parity","Comment"
2366                );
2367
2368     while (1) {
2369         linebuf[0] = NUL;               /* Read a line */
2370         if (fgets(linebuf,1023,f) == NULL)
2371           break;
2372         debug(F110,"ddcvt linebuf",linebuf,0);
2373         if (!linebuf[0]) {              /* Empty line */
2374             fprintf(f2,"\n");
2375             continue;
2376         }
2377         x = (int) strlen(linebuf);      /* Strip line terminator, */
2378         while (x-- > 0) {               /* if any. */
2379             if (linebuf[x] <= SP)
2380               linebuf[x] = NUL;
2381             else
2382               break;
2383         }
2384         xwords(linebuf,5,info,1);       /* Parse it the old way */
2385         for (x = 1; x < 6; x++)
2386           if (!info[x]) info[x] = "";
2387         fprintf(f2,"%-16s %-20s ; %5s %-6s %s\n",
2388                info[1],info[2],info[3],info[4],info[5]
2389                );
2390     }
2391     printf(" OK\n\n");
2392     rc = 0;                             /* Success */
2393   ddexit:
2394     if (f) fclose(f);
2395     if (f2) fclose(f2);
2396 #ifdef VMS
2397     if (temp) free(temp);
2398 #endif /* VMS */
2399     return(rc);
2400 }
2401
2402 int                                     /* s = name to look up   */
2403 #ifdef CK_ANSIC                         /* cx = index of command */
2404 ludial(char *s, int cx)                 /* (DIAL, LOOKUP, etc)   */
2405 #else
2406 ludial(s, cx) char *s; int cx;
2407 #endif /* CK_ANSIC */
2408 /* ludial */ {
2409
2410     int dd, n1, n2, n3, i, j, t;        /* Workers */
2411     int olddir, newdir, oldentry, newentry;
2412     int pass = 0;
2413     int oldflg = 0;
2414     int ambiguous = 0;                  /* Flag for lookup was ambiguous */
2415     char *info[7];                      /* Pointers to words from entry */
2416     char *pp;                           /* Pointer to element of array */
2417     FILE * f;
2418     char *line;                         /* File input buffer */
2419
2420 /* #define LUDEBUG */
2421
2422 #ifdef LUDEBUG
2423 int zz = 1;
2424 #endif /* LUDEBUG */
2425
2426     if (!s || ndialdir < 1)             /* Validate arguments */
2427       return(-1);
2428
2429     if ((n1 = (int) strlen(s)) < 1)     /* Length of string to look up */
2430       return(-1);
2431
2432     if (!(line = malloc(1024)))         /* Allocate input buffer */
2433       return(-1);
2434
2435 #ifdef LUDEBUG
2436 if (zz) printf("LUDIAL 1 s[%s], n1=%d\n",s,n1);
2437 #endif /* LUDEBUG */
2438
2439     pass = 0;
2440   lu_again:
2441     f = NULL;                           /* Dial directory file descriptor */
2442     t = dncount = 0;                    /* Dial-number match count */
2443     dd = 0;                             /* Directory counter */
2444     olddir = 0;
2445     newdir = 0;
2446 /*
2447   We need to recognize both old- and new-style directories.
2448   But we can't allow old-style and new-style entries in the same
2449   directory because there is no way to tell for sure the difference between
2450   an old-style entry like this:
2451
2452     foo  5551212  9600
2453
2454   and a new-style literal entry like this:
2455
2456     foo  555 9600
2457
2458   I.e. is the "9600" a speed, or part of the phone number?
2459 */
2460     while (1) {                         /* We make one pass */
2461         if (!f) {                       /* Directory not open */
2462             if (dd >= ndialdir)         /* No directories left? */
2463               break;                    /* Done. */
2464             debug(F111,"ludial dialdir[dd]",dialdir[dd],dd);
2465             if ((f = fopen(dialdir[dd],"r")) == NULL) { /* Open it */
2466                 perror(dialdir[dd]);    /* Can't, print message saying why */
2467                 if (line) {
2468                     free(line);
2469                     line = NULL;
2470                 }
2471                 dd++;                   /* Go on to next one, if any... */
2472                 continue;
2473             }
2474             dirline = 0;                /* Directory file line number */
2475             if (dialdpy && !pass)
2476               printf("Opening: %s...\n",dialdir[dd]);
2477             dd++;
2478             if (!oldflg) olddir = 0;
2479             newdir = 0;
2480         }
2481         oldentry = 0;
2482         newentry = 0;
2483         line[0] = NUL;
2484         if (getnct(line,1023,f,1) < 0) { /* Read a line */
2485             if (f) {                    /* f can be clobbered! */
2486                 fclose(f);              /* Close the file */
2487                 f = NULL;               /* Indicate next one needs opening */
2488                 oldflg = 0;
2489             }
2490             continue;
2491         }
2492         if (!line[0])                   /* Empty line */
2493           continue;
2494 #ifdef LUDEBUG
2495 if (zz) printf("LUDIAL 2 s[%s]\n",s);
2496 #endif /* LUDEBUG */
2497
2498         /* Make a copy and parse it the old way */
2499         /* A copy is needed because xwords() pokes NULs into the string */
2500
2501         if ((pp = malloc((int)strlen(line) + 1))) {
2502             strcpy(pp,line);            /* safe */
2503             xwords(pp,5,info,0);        /* Parse it the old way */
2504
2505 #ifdef LUDEBUG
2506 if (zz) printf("LUDIAL 3 s[%s]\n",s);
2507 #endif /* LUDEBUG */
2508
2509             if (!info[1])
2510               continue;
2511             if (*info[1] == ';') {      /* If full-line comment, */
2512                 newdir = 1;             /* (only new directories have them) */
2513                 continue;               /* keep reading. */
2514             }
2515             if (!info[2])
2516               continue;
2517             if (*info[2] == '+')
2518               newentry = 1;
2519             if (info[4]) {
2520                 if ((*info[4] == '=') ||
2521                     !ckstrcmp(info[4],"none", 4,0) ||
2522                     !ckstrcmp(info[4],"even", 4,0) ||
2523                     !ckstrcmp(info[4],"space",5,0) ||
2524                     !ckstrcmp(info[4],"mark", 4,0) ||
2525                     !ckstrcmp(info[4],"odd",  3,0)
2526                     )
2527                   oldentry = 1;
2528             }
2529         }
2530         if (pp) {
2531             free(pp);
2532             pp = NULL;
2533         }
2534
2535         /* Check consistency */
2536
2537         if ((oldentry || olddir) && (newentry || newdir)) {
2538             printf(
2539 "\nERROR: You seem to have old- and new-format entries mixed in your\n");
2540             printf(
2541 "dialing directory.  You'll have to edit it by hand to convert it to the\n");
2542 #ifndef NOHELP
2543             printf("new format.  Type HELP DIAL for further information.\n\n");
2544 #else
2545             printf("new format.\n\n");
2546 #endif /* NOHELP */
2547             if (line) {
2548                 free(line);
2549                 line = NULL;
2550             }
2551             return(-1);
2552         }
2553         if (!olddir && oldentry) {
2554             int convert = 0;
2555             olddir = 1;
2556             if (dialcvt == 2) {         /* 2 == ASK */
2557                 sprintf(tmpbuf,
2558 "WARNING: Old-style dialing directory detected:\n%s", line);
2559                 convert = uq_ok(tmpbuf,
2560                                 "Shall I convert it for you? ",3,NULL,0);
2561             } else
2562               convert = dialcvt;
2563             if (convert) {
2564                 debug(F111,"ludial calling ddcvt",dialdir[dd-1],dd);
2565                 if (ddcvt(dialdir[dd-1],f,dd-1) < 0) {
2566                     debug(F111,"ludial ddcvt failed",dialdir[dd-1],dd);
2567                     oldflg = 1;
2568                     printf(
2569 "  Sorry, can't convert.");
2570                     printf(
2571 "  Will ignore speed and parity fields, continuing...\n\n");
2572                 } else {
2573                     olddir = newdir = 0;
2574                     debug(F111,"ludial ddcvt ok",dialdir[dd-1],dd);
2575                 }
2576                 dd--;
2577                 f = NULL;
2578                 continue;
2579             } else {
2580                 if (dialcvt == 2)
2581                   printf(
2582 "  OK, will ignore speed and parity fields, continuing...\n\n");
2583                 olddir = 1;
2584             }
2585         }
2586
2587 #ifdef LUDEBUG
2588 if (zz) printf("LUDIAL XX s[%s], n1=%d\n",s,n1);
2589 #endif /* LUDEBUG */
2590
2591         /* Now parse again for real */
2592
2593         if (oldentry)                   /* Parse it the old way */
2594           xwords(line,5,info,0);
2595         else                            /* Parse it the new way */
2596           xwords(line,2,info,1);
2597
2598 #ifdef LUDEBUG
2599 if (zz) printf("LUDIAL YY s[%s], n1=%d\n",s,n1);
2600 if (zz) printf("%s [%s]\n",info[1],info[2]);
2601 #endif /* LUDEBUG */
2602
2603         if (info[1]) {                  /* First word is entry name */
2604             if ((n3 = (int) strlen(info[1])) < 1) /* Its length */
2605               continue;                 /* If no first word, keep reading. */
2606             if (n3 < n1)                /* Search name is longer */
2607               continue;                 /* Can't possibly match */
2608             if (ambiguous && n3 != n1)
2609               continue;
2610
2611 #ifdef LUDEBUG
2612 if (zz) printf("MATCHING: [%s] [%s], n1=%d\n",s,info[1],n1);
2613 #endif /* LUDEBUG */
2614
2615             if (ckstrcmp(s,info[1],n1,0)) /* Caseless string comparison */
2616               continue;
2617
2618 #ifdef LUDEBUG
2619 if (zz) printf("MATCH OK: [%s] [%s], n1=%d\n",s,info[1],n1);
2620 #endif /* LUDEBUG */
2621
2622             if (!info[2])               /* No phone number given */
2623               continue;
2624             if ((n2 = (int) strlen(info[2])) < 1) /* Length of phone number */
2625               continue;                 /* Ignore empty phone numbers */
2626
2627             /* Got one */
2628
2629             if (!(pp = (char *)malloc(n2 + 1))) { /* Allocate storage for it */
2630                 printf("?internal error - ludial malloc 1\n");
2631                 if (line) {
2632                     free(line);
2633                     line = NULL;
2634                 }
2635                 dncount = 0;
2636                 return(-1);
2637             }
2638             strcpy(pp,info[2]);         /* safe */
2639
2640             if (dncount > MAXDNUMS) {
2641                 printf("Warning: %d matches found, %d max\n",
2642                        dncount,
2643                        MAXDNUMS
2644                        );
2645                 dncount = MAXDNUMS;
2646                 break;
2647             }
2648             dn_p[dncount++] = pp;       /* Add pointer to array. */
2649             if (dncount == 1) {         /* First one... */
2650                 if (d_name) free(d_name);
2651                 if (!(d_name = (char *)malloc(n3 + 1))) { /* Save its name */
2652                     printf("?internal error - ludial malloc 2\n");
2653                     if (line) {
2654                         free(line);
2655                         line = NULL;
2656                     }
2657                     dncount = 0;
2658                     return(-1);
2659                 }
2660                 t = n3;                 /* And its length */
2661                 strcpy(d_name,info[1]); /* safe */
2662             } else {                    /* Second or subsequent one */
2663
2664 #ifdef LUDEBUG
2665                 if (zz)
2666                   printf("d_name=[%s],info[1]=%s,t=[%d]\n",d_name,info[1],t);
2667 #endif /* LUDEBUG */
2668
2669                 if ((int) strlen(info[1]) == t) /* Lengths compare */
2670                   if (!ckstrcmp(d_name,info[1],t,0)) /* Caseless compare OK */
2671                     continue;
2672
2673                 /* Name given by user matches entries with different names */
2674
2675                 if (ambiguous)          /* Been here before */
2676                   break;
2677
2678                 ambiguous = 1;          /* Now an exact match is required */
2679                 for (j = 0; j < dncount; j++) { /* Clean out previous list */
2680                     if (dn_p[j]) {
2681                         free(dn_p[j]);
2682                         dn_p[j] = NULL;
2683                     }
2684                 }
2685                 pass++;                 /* Second pass... */
2686                 goto lu_again;          /* Do it all over again. */
2687             }
2688         }
2689     }
2690     if (line) free(line);
2691     if (dncount == 0 && ambiguous) {
2692         printf(" Lookup: \"%s\" - ambiguous%s\n",
2693                s,
2694                cx == XXLOOK ? "" : " - dialing skipped"
2695                );
2696         return(-2);
2697     }
2698     return(dncount);
2699 }
2700
2701 char *
2702 pncvt(s) char *s; {                     /* Phone number conversion */
2703     char *p = NULL;                     /* (just a wrapper for dncvt() */
2704     char *q = NULL;
2705     static char pnbuf[128];
2706     makestr(&p,dn_p[0]);                /* Save these in case they are */
2707     makestr(&q,dn_p2[0]);               /* being used */
2708     makestr(&dn_p[0],s);                /* Copy the argument string to here */
2709     dncvt(0,XXLOOK,1,1);                /* Convert it */
2710     if (!dn_p2[0])                      /* Put result where can return it */
2711       pnbuf[0] = NUL;
2712     else
2713       ckstrncpy(pnbuf,dn_p2[0],127);
2714     makestr(&dn_p[0],p);                /* Restore these */
2715     makestr(&dn_p2[0],q);
2716     makestr(&p,NULL);                   /* Free these */
2717     makestr(&q,NULL);
2718     return((char *)pnbuf);
2719 }
2720
2721 int
2722 dodial(cx) int cx; {                    /* DIAL or REDIAL */
2723     int i = 0, x = 0;                   /* Workers */
2724     int sparity = -1;                   /* For saving global parity value */
2725     int previous = 0;
2726     int len = 0;
2727     int literal = 0;
2728     int flowsave;
2729     int lufound = 0;                    /* Did any lookup succeed? */
2730     int prefix = 1;
2731     int postfix = 1;
2732     int wasalpha = 0;
2733     int xredial = 0;
2734     int braces = 0;
2735
2736     char *p = NULL, *s3 = NULL, * sav = NULL;
2737     int j = 0, t = 0, n = 0;
2738     int xretries, xlcc;
2739
2740 #ifdef COMMENT
2741     debug(F101,"dodial cx","",cx);
2742     debug(F111,"dodial diallcc",diallcc,diallcc);
2743 #endif  /* COMMENT */
2744
2745     xretries = dialrtr;                 /* If retries not set, */
2746     if (diallcc) {                      /* choose default based on */
2747         xlcc = atoi(diallcc);           /* local country code. */
2748         if (xretries < 0) {
2749             switch (xlcc) {
2750               case 1: xretries = 10; break; /* No restrictions in NANP */
2751                 /* Add other country codes here */
2752                 /* that are known to have no restrictions on redialing. */
2753               default: xretries = 1;
2754             }
2755         }
2756     }
2757     if (cx == XXPDIA) {                 /* Shortcut... */
2758         cx = XXDIAL;
2759         partial = 1;
2760         debug(F100,"PDIAL sets partial=1","",0);
2761         postfix = 0;                    /* Do not add postfix */
2762     } else {
2763         partial = 0;
2764         debug(F100,"DIAL sets partial=0","",0);
2765     }
2766     previous = dialsta;                 /* Status of previous call, if any */
2767     if (previous == DIA_PART) {
2768         prefix = 0;                     /* do not add prefix */
2769     }
2770     s = NULL;                           /* Initialize user's dial string */
2771     if (cx == XXRED) {                  /* REDIAL or... */
2772         if ((y = cmcfm()) < 0)
2773           return(y);
2774     } else if (cx == XXANSW) {          /* ANSWER or ... */
2775         if ((y = cmnum("timeout (seconds)","0",10,&x,xxstring)) < 0)
2776           return(y);
2777         dialatmo = x;
2778         if ((y = cmcfm()) < 0)
2779           return(y);
2780     } else {                            /* DIAL or LOOKUP */
2781         if (ndialdir > 0)
2782           s3 = "Number to dial or entry from dial directory";
2783         else
2784           s3 = "Number to dial";
2785         if ((x = cmtxt(s3, dialnum ? dialnum : "",&s,xxstring)) < 0)
2786           return(x);
2787         if (s) {
2788             len = (int) strlen(s);
2789             ckstrncpy(tmpbuf,s,TMPBUFSIZ); /* Save literal copy */
2790 #ifdef COMMENT
2791             if (len > 1) {              /* Strip outer braces if given */
2792                 if (*s == '{') {
2793                     if (s[len-1] == '}') {
2794                         s[len-1] = NUL;
2795                         s++;
2796                         len -= 2;
2797                     }
2798                 }
2799             }
2800 #else
2801             s = brstrip(s);             /* Strip outer braces or quotes */
2802 #endif /* COMMENT */
2803         }
2804     }
2805
2806     if (cx != XXLOOK) {                 /* Not LOOKUP */
2807 #ifdef IKSD
2808         if (inserver) {
2809             printf("Sorry, dialing is disabled.\r\n");
2810             return(success = 0);
2811         }
2812 #endif /* IKSD */
2813 #ifdef CK_TAPI
2814         if (tttapi && !tapipass) {
2815           ;                             /* Skip the modem test if TAPI */
2816         } else
2817 #endif /* CK_TAPI */
2818         if (mdmtyp < 1 && !dialtest) {
2819             if (network
2820 #ifdef TN_COMPORT
2821                  && !istncomport()
2822 #endif /* TN_COMPORT */
2823                  )
2824               printf("Please SET HOST first, and then SET MODEM TYPE\n");
2825             else
2826               printf("Sorry, you must SET MODEM TYPE first\n");
2827             dialsta = DIA_NOMO;
2828             return(success = 0);
2829         }
2830         if (!local && !dialtest) {
2831             printf("Sorry, you must SET %s or SET HOST first\n",
2832 #ifdef OS2
2833                    "PORT"
2834 #else
2835                    "LINE"
2836 #endif /* OS2 */
2837                    );
2838             dialsta = DIA_NOLI;
2839             return(success = 0);
2840         }
2841         if ((!network 
2842 #ifdef TN_COMPORT
2843               || istncomport()
2844 #endif /* TN_COMPORT */
2845               ) && !dialtest &&
2846 #ifdef CK_TAPI
2847              !tttapi &&
2848 #endif /* CK_TAPI */
2849             (speed < 0L)
2850 #ifdef UNIX
2851             && (strcmp(ttname,"/dev/null"))
2852 #else
2853 #ifdef OSK
2854             && (strcmp(ttname,"/nil"))
2855 #endif /* OSK */
2856 #endif /* UNIX */
2857             ) {
2858             printf("\nSorry, you must SET SPEED first\n");
2859             dialsta = DIA_NOSP;
2860             return(success = 0);
2861         }
2862     }
2863     if (cx != XXANSW) {
2864         for (j = 0; j < MAXDNUMS; j++) { /* Initialize dial-number list */
2865             if (!dialnum) {             /* First time dialing */
2866                 dn_p[j] = NULL;         /* initialize all pointers. */
2867                 dn_p2[j] = NULL;
2868             } else if (dn_p[j]) {       /* Not the first time, */
2869                 free(dn_p[j]);          /* free previous, if any, */
2870                 dn_p[j] = NULL;         /* then set to NULL. */
2871                 if (dn_p2[j])
2872                   free(dn_p2[j]);
2873                 dn_p2[j] = NULL;
2874             } else break;               /* Already NULL */
2875         }
2876         if (len == 0)
2877           s = NULL;
2878         if (!s)
2879           s = dialnum;
2880         if (!s) {
2881             if (cx == XXLOOK)
2882               printf("?Lookup what?\n");
2883             else
2884               printf("%s\n", (cx == XXRED) ?
2885                    "?No DIAL command given yet" :
2886                    "?You must specify a number to dial"
2887                    );
2888             return(-9);
2889         }
2890
2891     /* Now we have the "raw" dial or lookup string and s is not NULL */
2892
2893         makestr(&dscopy,s);             /* Put it in a safe place */
2894         s = dscopy;
2895         n = 0;
2896
2897         debug(F111,"dodial",s,ndialdir);
2898
2899         wasalpha = 0;
2900         if (isalpha(*s)) {
2901             wasalpha = 1;
2902             if (ndialdir > 0) {         /* Do we have a dialing directory? */
2903                 n = ludial(s,cx);       /* Look up what the user typed */
2904                 if (n == 0)
2905                   printf(" Lookup: \"%s\" - not found%s\n",
2906                          s,
2907                          cx == XXLOOK ? "" : " - dialing as given\n"
2908                          );
2909             }
2910             debug(F101,"dodial",s,n);
2911             if (n < 0 && cx != XXLOOK) { /* Error out if they wanted to dial */
2912                 if (n == -1)            /* -2 means ludial already gave msg */
2913                   printf(" Lookup: fatal error - dialing skipped\n");
2914                 dialsta = DIA_DIR;
2915                 return(-9);
2916             }
2917             if (n > 0)                  /* A successful lookup */
2918               lufound = 1;
2919         } else if (*s == '=') {         /* If number starts with = sign */
2920             s++;                        /* strip it */
2921             literal = 1;                /* remember this */
2922             while (*s == SP) s++;       /* and then also any leading spaces */
2923         } else if (tmpbuf[0] == '{' && tmpbuf[1] == '{') {
2924             makelist(tmpbuf,dn_p,MAXDNUMS);
2925             makestr(&dscopy,tmpbuf);
2926             s = tmpbuf;
2927             for (n = 0; n < MAXDNUMS; n++) /* (have to count how many) */
2928               if (!dn_p[n]) break;
2929             braces = 1;
2930         }
2931         if (cx == XXLOOK && !wasalpha && !braces) {
2932             /* We've been told to lookup a number or a quoted name */
2933             char *p;
2934             n = 0;
2935             p = literal ? s : pncvt(dscopy);
2936             if (!p) p = "";
2937             if (*p) {
2938                 printf("%s  => %s\n", dscopy, p);
2939                 return(success = 1);
2940             } else {
2941                 printf("?Bad phone number\n");
2942                 return(success = 0);
2943             }
2944         }
2945         /* Save DIAL or successful LOOKUP string for future DIAL or REDIAL */
2946         /* But don't save pieces of partial dial ... */
2947
2948         debug(F101,"DIAL save dialnum partial","",partial);
2949         debug(F101,"DIAL save dialnum previous","",previous);
2950         if ((cx == XXDIAL && partial == 0 && previous != DIA_PART) ||
2951             (cx == XXLOOK && n > 0)) {
2952             makestr(&dialnum,dscopy);
2953             if (!quiet && dscopy && !dialnum)
2954               printf("WARNING - memory allocation failure: redial number\n");
2955         }
2956         if (n > 0) {
2957             if (!quiet && !backgrd && !braces /* && dialdpy */ ) {
2958                 if (!strcmp(d_name,s))
2959                   printf(" Lookup: \"%s\" - exact match\n",s);
2960                 else
2961                   printf(" Lookup: \"%s\" - uniquely matches \"%s\"\n",
2962                          s,
2963                          d_name
2964                          );
2965             }
2966             if ((cx == XXLOOK) ||
2967                 ((n > 1) && !quiet && !backgrd /* && dialdpy */ )) {
2968                 printf(" %d telephone number%sfound for \"%s\"%s\n",
2969                        n,
2970                        (n == 1) ? " " : "s ",
2971                        s,
2972                        (n > 0) ? ":" : "."
2973                        );
2974                 s3 = getdname();
2975             }
2976             for (i = 0; i < n; i++) {   /* Convert */
2977                 dn_x[i] = -1;
2978                 if (dncvt(i,cx,prefix,postfix) < 0) {
2979                     if (cx != XXLOOK) {
2980                         dialsta = DIA_DIR;
2981                         return(-9);
2982                     }
2983                 }
2984             }
2985             if (dialsrt && n > 1) {     /* Sort into optimal order */
2986                 for (i = 0; i < n-1; i++) {
2987                     for (j = i+1; j < n; j++) {
2988                         if (dn_x[j] < dn_x[i]) {
2989                             t = dn_x[j];
2990                             dn_x[j] = dn_x[i];
2991                             dn_x[i] = t;
2992                             p = dn_p[j];
2993                             dn_p[j] = dn_p[i];
2994                             dn_p[i] = p;
2995                             p = dn_p2[j];
2996                             dn_p2[j] = dn_p2[i];
2997                             dn_p2[i] = p;
2998                         }
2999                     }
3000                 }
3001             }
3002             if ((cx == XXLOOK) ||
3003                 ((n > 1) && !quiet && !backgrd /* && dialdpy */ )) {
3004                 int nn = n;
3005 #ifndef NOSPL
3006                 char * p;
3007 #endif /* NOSPL */
3008                 if (cx != XXLOOK)
3009                   if (n > 12) nn = 12;
3010                 for (i = 0; i < nn; i++) {
3011                     printf("%3d. %-12s  %-20s =>  %-20s  (%d)\n",i+1,
3012                            s3, dn_p[i],
3013                            dn_p2[i] ? dn_p2[i] : "(processing failed)",
3014                            dn_x[i]
3015                            );
3016                 }
3017                 if (cx != XXLOOK && n != nn)
3018                   printf("And %d more...\n", n - nn);
3019             }
3020         } else if (n == 0) {            /* Not found in directory */
3021             makestr(&(dn_p[0]),literal ? s : dscopy);
3022             makestr(&d_name,literal ? s : dscopy);
3023             dncount = 1;
3024             n = 1;
3025             if (dncvt(0,cx,prefix,postfix) < 0) { /* In case they typed a */
3026                 dialsta = DIA_DIR;      /* portable-format number ... */
3027                 return(-9);
3028             }
3029         }
3030
3031 #ifndef NONET
3032 #ifdef NETCONN
3033         /* It's not good that the networks directory depends on NOT-NODIAL.. */
3034         if (cx == XXLOOK && dscopy) {   /* Networks here too... */
3035             extern char *nh_p[], *nh_p2[], *n_name;
3036             extern char *nh_px[4][MAXDNUMS+1];
3037             n = -1;
3038             if (nnetdir > 0) {          /* Do we have a network directory? */
3039                 dirline = 0;
3040                 n = lunet(dscopy);      /* Look up what the user typed */
3041             }
3042             if (n > -1) {
3043                 int k;
3044                 if (n > 0)              /* A successful lookup */
3045                   lufound = 1;
3046                 if (cx == XXLOOK && n == 0)
3047                   printf(" Lookup: \"%s\" - not found\n",dscopy);
3048                 else
3049                   printf("%s %d network entr%s found for \"%s\"%s\n",
3050                          cx == XXLOOK ? " Lookup:" : "",
3051                          n,
3052                          (n == 1) ? "y" : "ies",
3053                          dscopy,
3054                          (n > 0) ? ":" : "."
3055                          );
3056
3057                 for (i = 0; i < n; i++) {
3058
3059                     printf("%3d. %-12s => %-9s %s",
3060                            i+1,n_name,nh_p2[i],nh_p[i]);
3061                     for (k = 0; k < 4; k++) {
3062                         if (nh_px[k][i]) {
3063                             printf(" %s",nh_px[k][i]);
3064                         } else
3065                           break;
3066                     }
3067                     printf("\n");
3068                 }
3069             }
3070         }
3071 #endif /* NETCONN */
3072 #endif /* NONET */
3073         if (cx == XXLOOK)
3074           return(success = lufound);
3075     } /* cx != XXANSW */
3076
3077 #ifdef VMS
3078     conres();                   /* So Ctrl-C/Y will work */
3079 #endif /* VMS */
3080 /*
3081   Some modems do not react well to parity.  Also, if we are dialing through a
3082   TCP/IP TELNET modem server, parity can be fatally misinterpreted as TELNET
3083   negotiations.
3084
3085   This should work even if the user interrupts the DIAL command, because the
3086   DIAL module has its own interrupt handler.  BUT... if, for some reason, a
3087   dialing device actually *requires* parity (e.g. CCITT V.25bis says that even
3088   parity should be used), this might prevent successful dialing.  For that
3089   reason, we don't do this for V.25bis modems.
3090 */
3091     sparity = parity;                   /* Save current parity */
3092     if ((dialcapas & CKD_V25) == 0)     /* If not V.25bis...  */
3093       parity = 0;                       /* Set parity to NONE */
3094
3095     flowsave = flow;
3096 /*
3097   These modems use some kind of screwy flow control while in command mode,
3098   and do not present CTS as they should.  So if RTS/CTS is set (or even if
3099   it isn't) disable flow control during dialing.
3100 */
3101 #ifndef MINIDIAL
3102     if (mdmtyp == n_ATT1910 || mdmtyp == n_ATT1900) {
3103         flow = FLO_NONE;                /* This is not enough */
3104 #ifdef CK_TTSETFLOW
3105         ttsetflow(FLO_NONE);            /* Really turn it off */
3106 #endif /* CK_TTSETFLOW */
3107     }
3108 #endif /* MINIDIAL */
3109     if (!network
3110 #ifdef TN_COMPORT
3111         || istncomport()
3112 #endif /* TN_COMPORT */
3113          ) {
3114         int x;
3115         if ((x = ttgmdm()) > -1) {
3116             if (!x && msgflg) {
3117                 printf(
3118 "WARNING - No modem signals detected.  Is your modem turned on?  If not,\n\
3119 use Ctrl-C to interrupt dialing, turn on your modem, then %s.\n",
3120                        cx == XXANSW ?
3121                        "ANSWER again" :
3122                        "REDIAL"
3123                        );
3124             }
3125             if (flow == FLO_RTSC) {
3126                 if (!(x & BM_CTS)) {
3127                     if (msgflg)
3128                       printf(
3129 "WARNING - SET FLOW RTS/CTS is in effect but modem's CTS signal is off.\n\
3130 Disabling flow control temporarily %s...\n",
3131                              cx == XXANSW ?
3132                              "while waiting for call" :
3133                              "during dialing"
3134                              );
3135                     flow = FLO_NONE;
3136                 }
3137             }
3138         }
3139     }
3140     if (cx == XXANSW) {                 /* ANSWER */
3141         success = ckdial("",0,0,1,0);
3142         goto dialfin;
3143     }
3144
3145 /* Edit 192 adds the ability to dial repeatedly. */
3146
3147     i = 0;
3148     dialcount = 0;
3149     do {
3150         if (i > 0) printf("\nDial attempt %d of %d...\n", i+1, xretries);
3151         dialcount = i+1;
3152         success = 0;
3153         /* And the ability to dial alternate numbers. */
3154         /* Loop to dial each in a list of numbers for the same name... */
3155         for (j = 0; j < n && !success; j++) { /* until one answers. */
3156             s = dn_p2[j];               /* Next number in list */
3157             if (dn_x[j] >= dialrstr) {  /* Dial restriction */
3158                 printf("Restricted: %s, skipping...\n",dn_p[j]);
3159                 continue;
3160             }
3161             xredial = (i == 0 && j == 0) ? 0 : 1;
3162             if (!s) s = dn_p[j];
3163
3164 #ifndef NOSPL
3165             sav = s;
3166             p = xdial(s);               /* Apply DIAL macro now */
3167             if (p) if (*p) s = p;
3168 #endif /* NOSPL */
3169
3170             /* Dial confirmation */
3171             /* NOTE: the uq_xxx() calls allow for a GUI dialog */
3172
3173             if (i == 0 && dialcnf) {
3174                 char msgbuf[128];
3175                 ckmakmsg(msgbuf,128,"Dialing ",s,NULL,NULL);
3176                 x = uq_ok(msgbuf,"Is this number correct? ",3,NULL,0);
3177                 if (!x) {
3178
3179 #ifndef COMMENT
3180                     x = uq_txt(         /* Allow GUI dialog */
3181 #ifdef OS2
3182 " Please enter the correct number,\r\n or press Enter to skip.",
3183 #else
3184 " Please enter the correct number,\r\n or press Return to skip.",
3185 #endif /* OS2 */
3186                               "Corrected phone number: ",
3187                                1,
3188                                NULL,
3189                                atmbuf,
3190                                ATMBL,
3191                                s,
3192                                DEFAULT_UQ_TIMEOUT
3193                                );
3194                     if (x && atmbuf[0]) { /* They gave a new one */
3195                         s = atmbuf;
3196                         makestr(&(dn_p2[j]), s);
3197                     }                   
3198
3199 #else  /* COMMENT */
3200
3201 #ifdef CK_RECALL
3202                     extern int on_recall;
3203 #endif /* CK_RECALL */
3204                     cmsavp(psave,PROMPTL);
3205                     cmsetp(
3206 #ifdef OS2
3207 " Please enter the correct number,\r\n or press Enter to skip: "
3208 #else
3209 " Please enter the correct number,\r\n or press Return to skip: "
3210 #endif /* OS2 */
3211                            );
3212                     cmini(ckxech);
3213                     x = -1;
3214                     if (pflag) prompt(NULL);
3215 #ifdef CK_RECALL
3216                     on_recall = 0;
3217 #endif /* CK_RECALL */
3218                     y = cmdgquo();
3219                     cmdsquo(0);
3220                     while (x < 0) {
3221                         x = cmtxt("Corrected phone number","",&s,NULL);
3222                         cmres();
3223                     }
3224                     if ((int) strlen(s) < 1) {
3225                         cmsetp(psave);
3226                         continue;
3227                     }
3228                     makestr(&(dn_p2[j]), s);
3229                     cmdsquo(y);
3230                     cmsetp(psave);
3231 #endif /* COMMENT */
3232                 }
3233             }
3234             if (dialtest) {             /* Just testing */
3235                 if (i + j == 0)
3236                   printf("\nTESTING...\n");
3237                 if (dialmac)
3238                   printf(" Number: \"%s\" => \"%s\"\n",sav,s);
3239                 else
3240                   printf(" Number: \"%s\"\n",s);
3241                 dialsta = DIA_BUSY;
3242                 success = 0;
3243             } else {
3244                 what |= W_DIALING;
3245                 success = ckdial(s,i,j,partial ? 3 : 0, xredial); /* Dial it */
3246                 what &= ~(W_DIALING);
3247                 if (!success) {
3248                     if (dialsta < 8 ||  /* Break out if unrecoverable error */
3249                         dialsta  == DIA_INTR ||
3250                         dialsta  == DIA_ERR  ||
3251                         previous == DIA_PART
3252                         )
3253                       break;
3254                 }
3255             }
3256         }
3257         if (success)                    /* Succeeded, leave the outer loop */
3258           break;
3259         if (dialsta < 8 ||              /* Break out if unrecoverable error */
3260             dialsta == DIA_INTR ||      /* Interrupted */
3261             dialsta == DIA_NODT ||      /* No dialtone */
3262             dialsta == DIA_NOAC ||      /* Access forbidden */
3263             dialsta == DIA_BLCK ||      /* Blacklisted */
3264             dialsta == DIA_DIR  ||      /* Dialing directory error */
3265             dialsta == DIA_ERR  ||      /* Modem command error */
3266             previous == DIA_PART)
3267           break;
3268         if (++i >= xretries)            /* Break out if too many tries */
3269           break;
3270         if (!backgrd && !quiet) {
3271             if (dialint > 5)
3272               printf(
3273 "\nWill redial in %d second%s- press any key to redial immediately.\n",
3274                      dialint,
3275                      dialint == 1 ? " " : "s "
3276                      );
3277             printf("Ctrl-C to cancel...\n");
3278         }
3279         x = dialint;                    /* Redial interval */
3280         while (x-- > 0) {
3281             if ((y = conchk()) > 0) {   /* Did they type something? */
3282                 while (y--) coninc(0);  /* Yes, absorb it */
3283                 break;                  /* And wake up */
3284             }
3285             sleep(1);                   /* No interrupt, sleep a sec */
3286         }
3287     } while (!success);
3288
3289   dialfin:
3290
3291     if (cx != XXLOOK) {
3292         if (!success)
3293           bleep((short) BP_FAIL);
3294         else if (!quiet)
3295           bleep((short) BP_NOTE);
3296 #ifdef OS2
3297         setint();                       /* Fix OS/2 interrupts */
3298 #endif /* OS2 */
3299         if (sparity > -1)
3300           parity = sparity;             /* Restore parity if we saved it */
3301         flow = flowsave;
3302 #ifdef OS2
3303         ttres();                        /* Restore DIAL device */
3304 #endif /* OS2 */
3305 #ifdef VMS
3306         concb((char)escape);            /* Restore console */
3307 #endif /* VMS */
3308 #ifdef OS2
3309         {                               /* Set session title */
3310             char * p, name[72];         /* in window list. */
3311             char * q;
3312             if (cx == XXANSW) {
3313                 q = "Incoming call";
3314             } else {
3315                 if (d_name)
3316                   q = d_name;
3317                 else if (dialnum)
3318                   q = dialnum;
3319                 else if (ttname[0])
3320                   q = ttname;
3321                 else q = "";
3322             }
3323             p = name;
3324             if (success) {
3325                 strncpy(name,q,48);
3326                 while (*p) {            /* Uppercase it for emphasis. */
3327                     if (islower(*p))
3328                       *p = toupper(*p);
3329                     p++;
3330                 }
3331             } else
3332               name[0] = NUL ;
3333             os2settitle((char *) name, TRUE);
3334         }
3335 #endif /* OS2 */
3336     }
3337     if (cx != XXLOOK) {
3338         if (success) {
3339             if (reliable == SET_AUTO) { /* It's not a reliable connection. */
3340                 reliable = SET_OFF;
3341                 debug(F101,"dodial reliable","",reliable);
3342             }
3343         } else {
3344 #ifndef NOHINTS
3345             extern int hints;
3346             if (hints && !quiet && dialsta != 9) { /* 9 == User interrupted */
3347                 extern int dialmhu, dialhng, dialdpy;
3348                 extern char * dialmsg[];
3349                 printf("\n*************************\n");
3350                 printf("DIAL-class command failed.\n");
3351                 printf("Modem type:  %s\n", gmdmtyp());
3352                 printf("Device:      %s\n", ttname);
3353                 printf("Speed:       %ld\n", speed);
3354                 printf("Dial status: %d",dialsta);
3355                 if (dialsta < 35 && dialmsg[dialsta])
3356                   printf(" [%s]",dialmsg[dialsta]);
3357                 printf("\n");
3358                 if (dialsta == DIA_TIMO ||
3359                     dialsta == DIA_NRDY ||
3360                    (dialsta > 13 && dialsta != DIA_BUSY && dialsta != DIA_NOAN)
3361                     ) {
3362                     switch (dialsta) {
3363                       case DIA_TIMO:
3364                         printf(
3365 " . SET DIAL TIMEOUT to a greater value and try again.\n"
3366                                );
3367                         break;
3368                       case DIA_NRSP:
3369                       case DIA_NRDY:
3370                       case DIA_NOIN:
3371                         printf(
3372 " . Is the modem turned on?\n"
3373                                );
3374                         printf(
3375 " . Are you using the right communication port?\n"
3376                                );
3377                         break;
3378                       case DIA_NODT:
3379                         printf(
3380 " . Is the modem connected to the telephone line?\n"
3381                                );
3382                     }
3383                     if (mdmtyp == n_GENERIC) {
3384                         printf(
3385 " . Please choose a specific modem type with SET MODEM TYPE and try again.\n"
3386                                );
3387                         printf(
3388 "    SET MODEM TYPE ? to see the list of known modem types.\n"
3389                                );
3390                     } else {
3391                         printf(
3392 " . Are you sure you have chosen the appropriate modem type?\n"
3393                                );
3394                     }
3395                     if (speed > 19200L) {
3396                         printf(
3397 " . Maybe the interface speed (%ld) is too fast:\n", speed
3398                                );
3399                         printf(
3400 "    SET SPEED to a lower speed and try again.\n"
3401                                );
3402                         printf(
3403 "    SET SPEED ? to see the list of valid speeds.\n"
3404                                );
3405                     }
3406                     if (dialhng) {
3407                         if (dialmhu)
3408                           printf(
3409 " . SET MODEM HANGUP-METHOD RS232 and try again.\n"
3410                                  );
3411                         else
3412                           printf(
3413 " . SET MODEM HANGUP-METHOD MODEM-COMMAND and try again.\n"
3414                                  );
3415                         printf(
3416 " . If that doesn't work, try again with SET DIAL HANGUP OFF.\n"
3417                                );
3418                     } else {
3419                         printf(
3420 " . Give a HANGUP or SET DIAL HANGUP ON command and try again.\n"
3421                                );
3422                     }
3423                     if (!dialdpy)
3424                       printf(
3425 " . Use SET DIAL DISPLAY ON to watch the dialog between Kermit and modem.\n"
3426                              );
3427                 }
3428 #ifndef NOSHOW
3429                 printf(
3430 " . SHOW COMMUNICATIONS, SHOW MODEM, SHOW DIAL to see current settings.\n"
3431                        );
3432 #endif /* NOSHOW */
3433
3434 #ifndef NOHELP
3435                 printf(
3436 " . HELP SET MODEM, HELP SET DIAL, and HELP DIAL for more information.\n"
3437                        );
3438 #endif /* NOHELP */
3439                 printf("(Use SET HINTS OFF to suppress future hints.)\n");
3440                 printf("*************************\n\n");
3441             }
3442 #endif /* NOHINTS */
3443         }
3444     }
3445     return(success);
3446 }
3447 #endif /* NODIAL */
3448
3449 /*  D O T Y P E  --  Type (display) a file with various options...  */
3450
3451 #ifdef BIGBUFOK
3452 #define TYPBUFL 16384
3453 #else
3454 #define TYPBUFL 256
3455 #endif /* BIGBUFOK */
3456
3457 int typ_lines = 0;                      /* \v(ty_ln) */
3458 int typ_mtchs = 0;                      /* \v(ty_lm) */
3459 static int typ_int = 0;                 /* Flag if TYPE interrupted */
3460
3461 #ifdef UNICODE
3462 extern int fcharset, fileorder, byteorder, ucsorder;
3463 #define TYPXBUFL TYPBUFL+TYPBUFL+TYPBUFL+4
3464 static char * mp = NULL;
3465 static char * mbuf = NULL;
3466 static long xn = 0L;
3467
3468 static int
3469 #ifdef CK_ANSIC
3470 storechar(char c)
3471 #else
3472 storechar(c) char c;
3473 #endif /* CK_ANSIC */
3474 {
3475     if (!mp) return(-1);
3476     if (++xn > TYPXBUFL)
3477       return(-1);
3478     debug(F111,"storechar xn",ckitoa((int)c),xn);
3479     *mp++ = c;
3480     return(0);
3481 }
3482 #endif /* UNICODE */
3483
3484 static FILE * ofp = NULL;               /* For /OUTPUT: file */
3485
3486 static int
3487 typeline(buf,len,outcs,ofp) char * buf; int len, outcs; FILE * ofp; {
3488     register int i;
3489
3490     debug(F011,"typeline buf",buf,len);
3491     /* debug(F101,"typeline outcs","",outcs); */
3492
3493 #ifdef OS2
3494 #ifndef NOLOCAL
3495 #ifdef UNICODE
3496     /* In K95 only, the buffer is guaranteed to be in UCS-2 if outcs >= 0. */
3497     /* Len is its length in bytes.  There is no line terminator. */
3498     /* outcs is the file character-set number (FC_xxx) of the target set */
3499     /* that was requested by the user. */
3500     if (!inserver && !k95stdout) {
3501         extern int wherex[], wherey[];
3502         extern unsigned char colorcmd;
3503
3504         VscrnWrtUCS2StrAtt( VCMD, (unsigned short *)buf, len/2,
3505                            wherey[VCMD], wherex[VCMD], &colorcmd);
3506         printf("\r\n");
3507         return(0);
3508     }
3509 #endif /* UNICODE */
3510 #endif /* NOLOCAL */
3511 #endif /* OS2 */
3512
3513 /* In Unix, VMS, etc, the line has already been converted to the desired  */
3514 /* character-set, if one was given.  OR... on all platforms, including in */
3515 /* K95, we don't know the character set.  In either case we dump the line */
3516 /* byte by byte in case it contains NULs (printf() would truncate). */
3517
3518 #ifdef COMMENT
3519     for (i = 0; i < len; i++)
3520       putchar(buf[i]);
3521 #else
3522     for (i = 0; i < len; i++) {
3523         if (ofp == stdout) {
3524             putchar(buf[i]);
3525         } else {
3526             putc(buf[i],ofp);
3527         }
3528     }
3529 #endif /* COMMENT */
3530
3531 #ifdef IKSD
3532     if (inserver) {
3533 #ifdef UNICODE
3534         if (outcs == FC_UCS2) {
3535             if (ofp == stdout) {
3536                 putchar(NUL);
3537             } else {
3538                 putc(NUL,ofp);
3539             }
3540         }
3541 #endif /* UNICODE */
3542         if (ofp == stdout) {
3543             putchar('\r');
3544         } else {
3545             putc('\r',ofp);
3546         }
3547     }
3548 #endif /* IKSD */
3549 #ifdef UNICODE
3550     if (outcs == FC_UCS2) {
3551         if (ofp == stdout) {
3552             putchar(NUL);
3553         } else {
3554             putc(NUL,ofp);
3555         }
3556     }
3557 #endif /* UNICODE */
3558     if (ofp == stdout) {
3559         putchar('\n');
3560     } else {
3561         putc('\n',ofp);
3562     }
3563     fflush(stdout);
3564     return(0);
3565 }
3566
3567 static int                              /* Get translated line */
3568 typegetline(incs, outcs, buf, n) int incs, outcs, n; char * buf; {
3569     int x = 0, c0, c1, len = 0, count = 0, eof = 0, xlate = 0;
3570 #ifdef UNICODE
3571     int xxn = -1;
3572     int yyn = -9;
3573     xn = 0L;
3574
3575 #ifdef DEBUG
3576     if (deblog && typ_lines == 0) {
3577         debug(F101,"typegetline incs","",incs);
3578         debug(F101,"typegetline outcs","",outcs);
3579         debug(F101,"typegetline feol","",feol);
3580         debug(F101,"typegetline byteorder","",byteorder);
3581         debug(F101,"typegetline ucsorder ","",ucsorder);
3582         debug(F111,"typegetline fileorder","1",fileorder);
3583     }
3584 #endif /* DEBUG */
3585
3586     if (incs < 0)                       /* Shouldn't happen */
3587       return(-2);
3588
3589     if (outcs == -1)                    /* Can happen */
3590       outcs = incs;
3591
3592     if (incs != outcs || incs == FC_UCS2) { /* See if we should translate */
3593         xlate = 1;
3594         if (!mbuf) {                    /* Allocate buffer if not allocated */
3595             mbuf = (char *)malloc(TYPXBUFL+1); /* yet */
3596             if (!mbuf) {
3597                 printf("WARNING: Translation buffer allocation failure.\n");
3598                 printf("Translation will be skipped...\n");
3599                 xlate = 0;
3600             }
3601         }
3602     }
3603     if (xlate) {                        /* Translating... */
3604         mp = mbuf;                      /* Reset working buffer pointer */
3605 /*
3606   Here we call xgnbyte() in a loop, having it return UCS-2 bytes.  In K95, we
3607   use UCS-2 directly.  Elsewhere, we feed the UCS-2 bytes into xpnbyte() to
3608   convert them to the desired target character set.  But since we are using
3609   UCS-2, we have several sources for confusion: (1) xgnbyte() might return in
3610   LE or BE byte order, with no explicit indication of what the order is; but
3611   (2) xpnbyte() wants BE; but (3) Windows wants LE.
3612 */
3613         while (1) {
3614             if (typ_int)                /* Quit if interrupted */
3615               return(0);
3616             c0 = xgnbyte(FC_UCS2,incs,NULL); /* Convert to UCS-2 */
3617             debug(F000,"typegetline c0","",c0);
3618             if (c0 < 0) {               /* EOF */
3619                 eof++;
3620                 break;
3621             }
3622             c1 = xgnbyte(FC_UCS2,incs,NULL); /* Convert to UCS-2 */
3623             debug(F000,"typegetline c1","",c1);
3624             if (c1 < 0) {               /* EOF */
3625                 eof++;
3626                 break;
3627             }
3628 #ifdef DEBUG
3629             if (deblog && typ_lines == 0) {
3630                 if (count == 0) /* Check fileorder after BOM */
3631                   debug(F111,"typegetline fileorder","2",fileorder);
3632             }
3633 #endif /* DEBUG */
3634
3635 #ifdef COMMENT
3636 /* Now we have the two UCS-2 bytes.  Which order are they in? */
3637
3638             if (fileorder > 0) {        /* Little Endian */
3639                 int t;                  /* So swap them */
3640                 debug(F100,"typegetline swapping","",0);
3641                 t = c1;
3642                 c1 = c0;
3643                 c0 = t;
3644             }
3645 #endif /* COMMENT */
3646             if (c0 == 0 && c1 == 0x0D)  /* Now see if we have EOL */
3647               yyn = xn;
3648
3649             if (c0 == 0 && c1 == 0x0A)  /* Now see if we have EOL */
3650               xxn = xn;
3651
3652             count++;                    /* Count byte */
3653
3654 /* Give the two bytes to xpnbyte() in BE order */
3655
3656             if ((x = xpnbyte(c0,TC_UCS2,outcs,storechar)) < 0) return(-1);
3657             if ((x = xpnbyte(c1,TC_UCS2,outcs,storechar)) < 0) return(-1);
3658
3659             if (xxn > -1) {             /* Have end of line? */
3660                 xn = xxn;
3661                 if (yyn == xxn - 2)     /* Adjust for CRLF */
3662                   xn = yyn;
3663                 break;                  /* And break out of loop. */
3664             }
3665         }
3666         mbuf[xn] = NUL;
3667         if (xn > n)                     /* Can truncate here... */
3668           xn = n;
3669         memcpy(buf,mbuf,xn);
3670         debug(F011,"typegetline xlate",buf,xn);
3671         return((eof && (xn == 0)) ? -1 : xn);
3672     }
3673 #endif /* UNICODE */
3674 #ifdef COMMENT
3675     /* We can't use this because, stupidly, zsinl() doesn't return a length. */
3676     /* It could be changed but then we'd have to change all ck?fio.c modules */
3677     x = zsinl(ZIFILE,buf,n);
3678 #else
3679     /* So instead, we copy zsinl() to here... */
3680     /* But note: This does not necessarily handle UCS-2 alignment properly;  */
3681     /* that's what the code in the first section of this routine is for. */
3682     /* But it does tolerate files that contain NULs. */
3683     {
3684         int a;
3685         char *s;
3686
3687         s = buf;
3688         a = -1;                         /* Current character, none yet. */
3689         debug(F101,"typegetline zsinl simulation","",n);
3690         while (n--) {                   /* Up to given length */
3691 #ifdef COMMENT
3692             int old = 0;
3693             if (feol)                   /* Previous character */
3694               old = a;
3695 #endif /* COMMENT */
3696             if (zchin(ZIFILE,&a) < 0) { /* Read a character from the file */
3697                 debug(F101,"typegetline zchin fail","",count);
3698                 if (count == 0)
3699                   x = -1;               /* EOF or other error */
3700                 break;
3701             } else
3702               count++;
3703             if (feol) {                 /* Single-character line terminator */
3704                 if (a == feol)
3705                   break;
3706             } else {                    /* CRLF line terminator */
3707 #ifdef COMMENT
3708 /* Debug log shows that in Windows, <CR><LF> is returned as <LF>. */
3709 /* Apparently we're not reading the file in binary mode. */
3710
3711                 if (a == '\015')        /* CR, get next character */
3712                   continue;
3713                 if (old == '\015') {    /* Previous character was CR */
3714                     if (a == '\012') {  /* This one is LF, so we have a line */
3715                         break;
3716                     } else {            /* Not LF, deposit CR */
3717                         *s++ = '\015';
3718                         n--;
3719                         len++;
3720                     }
3721                 }
3722 #else
3723                 if (a == LF) {
3724                     if (s[len] == CR) { /* This probably won't happen */
3725                         s[len] = NUL;
3726                         s--;
3727                         len--;
3728                     }
3729                     break;
3730                 }
3731 #endif /* COMMENT */
3732             }
3733             *s = a;                     /* Deposit character */
3734             s++;
3735             len++;
3736         }
3737         *s = '\0';                      /* Terminate the string */
3738     }
3739 #endif /* COMMENT */
3740     return(x < 0 ? -1 : len);
3741 }
3742
3743
3744 #ifndef MAC
3745 SIGTYP
3746 #ifdef CK_ANSIC
3747 tytrap(int foo)                         /* TYPE interrupt trap */
3748 #else
3749 tytrap(foo) int foo;
3750 #endif /* CK_ANSIC */
3751 /* tytrap */ {
3752 #ifdef __EMX__
3753     signal(SIGINT, SIG_ACK);
3754 #endif
3755     debug(F100,"type tytrap SIGINT","",0);
3756     typ_int = 1;                        /* (Need arg for ANSI C) */
3757     SIGRETURN;
3758 }
3759 #endif /* MAC */
3760
3761 int
3762 dotype(file, paging, first, head, pat, width, prefix, incs, outcs, outfile, z)
3763     char * file, * pat, * prefix; int paging, first, head, width, incs, outcs;
3764     char * outfile; int z;
3765 /* dotype */ {
3766     extern CK_OFF_T ffc;
3767     char buf[TYPBUFL+2];
3768     char * s = NULL;
3769     int rc = 1, lines = 0, ucs2 = 0;
3770     char ** tail = NULL;
3771     int * tlen = NULL;
3772     int tailing = 0, counting = 0;
3773     int x, c, n, i, j, k = 0;
3774     int number = 0, save, len, pfxlen = 0, evalpfx = 1;
3775 #ifdef UNICODE
3776     int ucsbom_sav;
3777     extern int ucsbom;
3778 #endif /* UNICODE */
3779 #ifdef NT
3780     int gui = 0;
3781 #endif /* NT */
3782
3783 #ifndef MAC
3784 #ifdef OS2
3785 #ifdef NT
3786     SIGTYP (* oldsig)(int);             /* For saving old interrupt trap. */
3787 #else /* NT */
3788     SIGTYP (* volatile oldsig)(int);
3789 #endif /* NT */
3790 #else /* OS2 */
3791     SIGTYP (* oldsig)();
3792 #endif /* OS2 */
3793 #endif /* MAC */
3794
3795 #ifdef KUI
3796     if (outfile == (char *)1) {
3797         gui = 1;
3798         outfile = "";
3799     }
3800 #endif /* KUI */
3801
3802     if (!file) file = "";
3803     if (!*file) return(-2);
3804
3805     if (ofp != stdout) {                /* In case of previous interruption */
3806         if (ofp) fclose(ofp);
3807         ofp = stdout;
3808     }
3809     if (!outfile) outfile = "";
3810     if (outfile[0]) {
3811         ofp = fopen(outfile,"w");       /* Open output file */
3812         if (!ofp) {
3813             printf("?Can't open output file %s: %s\n",outfile,ck_errstr());
3814             ofp = stdout;
3815             return(-9);
3816         }
3817     }
3818     number = z;
3819     if (number && prefix) prefix = NULL;
3820
3821 #ifdef UNICODE
3822     ucsbom_sav = ucsbom;                /* We are not creating a file */
3823     ucsbom = 0;                         /* Do not use BOM bytes */
3824 #endif /* UNICODE */
3825
3826     typ_int = 0;
3827
3828     save = binary;                      /* Save file type */
3829
3830     debug(F101,"dotype incs","",incs);
3831     debug(F101,"dotype outcs","",outcs);
3832
3833 #ifdef UNICODE
3834     debug(F111,"dotype fileorder","A",fileorder);
3835 #ifdef OS2
3836     if (!inserver && !k95stdout)
3837       outcs = FC_UCS2;
3838 #endif /* OS2 */
3839
3840     if (outcs == FC_UCS2)               /* Output is UCS-2? */
3841       ucs2 = 1;
3842     if (fileorder < 0)
3843       fileorder = ucsorder;
3844     debug(F111,"dotype fileorder","B",fileorder);
3845 #endif /* UNICODE */
3846
3847 #ifdef CK_TTGWSIZ
3848 #ifdef OS2
3849     ttgcwsz();
3850 #else /* OS2 */
3851     /* Check whether window size changed */
3852     if (ttgwsiz() > 0) {
3853         if (tt_rows > 0 && tt_cols > 0) {
3854             cmd_rows = tt_rows;
3855             cmd_cols = tt_cols;
3856             debug(F101,"dotype cmd_rows","",cmd_rows);
3857             debug(F101,"dotype cmd_cols","",cmd_cols);
3858         }
3859     }
3860 #endif /* OS2 */
3861 #endif /* CK_TTGWSIZ */
3862
3863     if (prefix)
3864       pfxlen = strlen(prefix);
3865
3866     if (paging < 0) {                   /* Count only, don't print */
3867         counting = 1;
3868         prefix = NULL;
3869         width = 0;
3870         paging = 0;
3871     }
3872     if (ucs2)                           /* Crude... */
3873       width *= 2;
3874
3875 #ifdef OS2
3876     if (*file) {
3877         ckstrncpy(buf, file, TYPBUFL);  /* Change / to \. */
3878         p = buf;
3879         while (*p) {
3880             if (*p == '/') *p = '\\';
3881             p++;
3882         }
3883         file = buf;
3884     } else {
3885         rc = 0;
3886         goto xdotype;
3887     }
3888 #endif /* OS2 */
3889
3890     if (zchki(file) == -2) {            /* It's a directory */
3891         debug(F111,"dotype zchki failure",file,-2);
3892         if (xcmdsrc == 0) {
3893             printf("?Not a regular file: \"%s\"\n",file);
3894             rc = -9;
3895         } else
3896           rc = 0;
3897         goto xdotype;
3898     }
3899     if (!zopeni(ZIFILE, file)) {        /* Not a directory, open it */
3900         debug(F111,"dotype zopeni failure",file,0);
3901         if (xcmdsrc == 0) {
3902             printf("?Can't open file: \"%s\"\n",file);
3903             rc = -9;
3904         } else
3905           rc = 0;
3906         goto xdotype;
3907     }
3908
3909 #ifndef AMIGA
3910 #ifndef MAC
3911     errno = 0;
3912     oldsig = signal(SIGINT, tytrap);    /* Save current interrupt trap. */
3913     /* debug(F111,"type SIGINT trap set",ckitoa(errno),oldsig); */
3914 #endif /* MAC */
3915 #endif /* AMIGA */
3916
3917     if (paging > -1)                    /* More-prompting */
3918       xaskmore = paging;
3919
3920     binary = 0;
3921
3922     if (head < 0) {                     /* "tail" was requested */
3923         tailing = 1;                    /* Set flag */
3924         head = 0 - head;                /* Get absolute number of lines */
3925         if (!counting) {
3926             tail = (char **) malloc(head * sizeof(char *)); /* Allocate list */
3927             if (!tail) {
3928                 printf("?Memory allocation failure\n");
3929                 goto xdotype;
3930
3931             }
3932             tlen = (int *) malloc(head * sizeof(int));
3933             if (!tlen) {
3934                 printf("?Memory allocation failure\n");
3935                 goto xdotype;
3936
3937             }
3938             for (i = 0; i < head; i++) { /* Initialize each pointer in list. */
3939                 tail[i] = NULL;
3940                 tlen[i] = 0;
3941             }
3942         }
3943     }
3944     typ_lines = 0;
3945     typ_mtchs = 0;
3946
3947 #ifdef UNICODE
3948     if (outcs > -1 && (incs != outcs || incs == FC_UCS2)) { /* Translating? */
3949         ffc = (CK_OFF_T)0;
3950         initxlate(incs,outcs);          /* Set up translation functions */
3951     } else
3952 #endif /* UNICODE */
3953       outcs = -1;                       /* Means we don't know the charset */
3954
3955     debug(F101,"dotype ffc","",ffc);
3956     debug(F101,"dotype outcs 2","",outcs);
3957 #ifdef UNICODE
3958     debug(F111,"dotype fileorder","C",fileorder);
3959 #endif /* UNICODE */
3960
3961     /* Allow the buffer to contain NULs */
3962
3963     for (n = first;
3964          (len = typegetline(incs,outcs,buf,TYPBUFL)) > -1;
3965          lines++
3966          ) {
3967         debug(F011,"dotype line",buf,len);
3968 #ifndef MAC
3969         if (typ_int) {                  /* Interrupted? */
3970             typ_int = 0;
3971             debug(F101,"type interrupted line","",lines);
3972             printf("^C...\n");          /* Print message */
3973             if (ofp != stdout) {        /* Close any output file */
3974                 if (ofp) fclose(ofp);
3975                 ofp = stdout;
3976             }
3977             goto xxdotype;
3978         }
3979 #endif /* MAC */
3980         typ_lines++;                    /* For \v(ty_ln) */
3981         if (pat)                        /* Matching? */
3982           if (!ckmatch(pat,buf,1,1+4))  /* Line matches pattern? */
3983             continue;                   /* No, skip it */
3984         typ_mtchs++;
3985
3986         if (head > 0 && !tailing && lines == head) /* Handle /HEAD:n */
3987           break;
3988
3989         buf[TYPBUFL+1] = NUL;           /* Just in case... */
3990         if (prefix) {                   /* Add specified prefix to each line */
3991             char pbuf[64];
3992             char * pp;
3993             pp = prefix;
3994 #ifndef NOSPL
3995             if (evalpfx) {              /* Prefix is a variable? */
3996                 int n = 63;             /* Maybe - evaluate it and see */
3997                 char * p = pbuf;
3998                 zzstring(prefix,&p,&n); /* If there is no change */
3999                 if (!strcmp(prefix,pbuf)) { /* it's not a variable */
4000                     evalpfx = 0;        /* So don't do this again. */
4001                 } else {                /* It was a variable */
4002                     pp = pbuf;          /* So substitute its value */
4003                     pfxlen = 63 - n;    /* and get its new length */
4004                 }
4005             }
4006 #endif /* NOSPL */
4007             if (len + pfxlen + 2 < TYPBUFL) {
4008                 /* Shift right to make room for prefix */
4009                 memcpy((char *)line+pfxlen,(char *)buf,len);
4010                 lset((char *)line,pp,pfxlen,SP);
4011                 debug(F110,"dotype prefix",line,pfxlen);
4012                 len += pfxlen;
4013                 memcpy((char *)buf,(char *)line,len);
4014             }
4015         } else if (number) {            /* Line numbers */
4016             int x;
4017             sprintf(line,"%4d. ",typ_lines);
4018             x = strlen(line);
4019             len += x;
4020             if (len < LINBUFSIZ) {
4021                 memcpy((char *)&line[x],(char *)buf,len);
4022                 memcpy((char *)buf,(char *)line,len);
4023             }
4024         }
4025         if (width > 0 && width <= TYPBUFL) { /* Truncate at given width. */
4026             char * obuf = line;         /* But to do that first we must */
4027             int i,k,z;                  /* expand tabs; assume every 8 cols. */
4028             line[0] = NUL;
4029             for (i = 0, k = 0; i < width; k++) { /* Character loop... */
4030                 if (!buf[k])            /* No more chars in this line, done. */
4031                   break;
4032                 if (buf[k] != '\t') {   /* If it's not a tab */
4033                     if (i >= LINBUFSIZ) /* Check for overflow */
4034                       break;
4035                     obuf[i++] = buf[k]; /* and then deposit it. */
4036                     obuf[i] = NUL;      /* Keep it null-terminated */
4037                     continue;
4038                 }
4039                 z = 8 - (i % 8);        /* It's a tab, expand it. */
4040                 if (z == 0) z = 8;
4041                 for (j = 0; j < z && i < LINBUFSIZ; j++) {
4042 #ifdef UNICODE
4043                     if (ucs2 && !ucsorder)
4044                       obuf[i++] = NUL;
4045 #endif /* UNICODE */
4046                     obuf[i++] = ' ';
4047 #ifdef UNICODE
4048                     if (ucs2 && ucsorder)
4049                       obuf[i++] = NUL;
4050 #endif /* UNICODE */
4051                 }
4052                 obuf[i++] = NUL;
4053                 obuf[i] = NUL;
4054             }
4055             obuf[width] = NUL;          /* Now truncate at given width. */
4056 #ifdef COMMENT
4057             /* This doesn't work for UCS-2 because it contains NULs */
4058             ckstrncpy(buf,obuf,TYPBUFL); /* and copy it back (again?) */
4059 #else
4060             memcpy((char *)buf,(char *)obuf,i); /* Copy it back */
4061 #endif /* COMMENT */
4062             len = (i > width) ? width : i; /* Spare us another strlen()... */
4063         }
4064         if (tailing) {                  /* If /TAIL:n... */
4065             k = lines % head;           /* save this line in circular buffer */
4066             if (!counting) {
4067                 if (tail[k]) free(tail[k]);
4068                 tail[k] = malloc(len+2);
4069                 if (!tail[k]) {
4070                     printf("?Memory allocation failure\n");
4071                     goto xdotype;
4072                 }
4073                 memcpy(tail[k],buf,len);
4074                 tlen[k] = len;
4075                 continue;
4076             }
4077         }
4078         if (counting)                   /* If only counting */
4079           continue;                     /* we're done with this line */
4080
4081         if (paging) {                   /* Displaying this line... */
4082             int u;
4083             u = len;                    /* Length in BYTES */
4084             if (ucs2)                   /* If outputting in UCS-2 */
4085               u /= 2;                   /* convert length to CHARACTERS */
4086             x = (u / cmd_cols) + 1;     /* Crudely allow for wrap */
4087             if (cmd_rows > 0 && cmd_cols > 0)
4088               n += x;                   /* This assumes terminal will wrap */
4089         }
4090 #ifdef KUI
4091         if ( gui ) {
4092             int i;
4093             unsigned short * uch = (unsigned short *)buf;
4094             for ( i=0; i<len/2; i++)
4095                 gui_text_popup_append(uch[i]);
4096                         gui_text_popup_append(CR);
4097                         gui_text_popup_append(LF);
4098         } 
4099         else
4100 #endif /* KUI */
4101         typeline(buf,len,outcs,ofp);    /* Print line, length based */
4102 #ifdef CK_TTGWSIZ
4103         debug(F101,"dotype n","",n);
4104         if (paging > 0 && ofp == stdout) { /* Pause at end of screen */
4105             if (cmd_rows > 0 && cmd_cols > 0) {
4106                 if (n > cmd_rows - 3) {
4107                     if (!askmore())
4108                       goto xdotype;
4109                     else
4110                       n = 0;
4111                 }
4112             }
4113         }
4114 #endif /* CK_TTGWSIZ */
4115     }
4116
4117   xdotype:
4118     if (counting) {
4119         fprintf(ofp,
4120                 "%s: %d line%s\n",file,typ_lines,typ_lines == 1 ? "" : "s");
4121         if (pat)
4122           fprintf(ofp,
4123                   "%s: %d match%s\n",pat,typ_mtchs,typ_mtchs == 1 ? "" : "es");
4124         goto xxdotype;
4125     }
4126     if (tailing && tail) {              /* Typing tail of file? */
4127         if (lines < head) {             /* Yes, show the lines we saved */
4128             k = 0;                      /* Show all lines */
4129         } else {                        /* More lines than tail number */
4130             lines = k;                  /* Last line to show */
4131             k++;                        /* First line to show */
4132             if (k >= head)
4133               k = 0;
4134         }
4135         n = first;                      /* Output line counter */
4136         for (i = k ;; i++) {            /* Loop thru circular buffer */
4137 #ifndef MAC
4138             if (typ_int) {              /* Interrupted? */
4139                 printf("^C...\n");      /* Print message */
4140                 goto xxdotype;
4141             }
4142 #endif /* MAC */
4143             j = i % head;               /* Index of this line */
4144             s = tail[j];                /* Point to line to display */
4145             if (!s)                     /* (shouldn't happen...) */
4146               break;
4147             if (paging) {               /* Crudely allow for line wrap */
4148                 x = tlen[j];
4149                 if (ucs2) x /= 2;
4150                 x = x / cmd_cols + 1;
4151                 if (cmd_rows > 0 && cmd_cols > 0)
4152                   n += x;
4153             }
4154             typeline(s,tlen[j],outcs,ofp); /* Display this line */
4155             if (paging && ofp == stdout) { /* Pause at end of screen */
4156                 if (cmd_rows > 0 && cmd_cols > 0) {
4157                     if (n > cmd_rows - 3) {
4158                         if (!askmore())
4159                           break;
4160                         else
4161                           n = 0;
4162                     }
4163                 }
4164             }
4165             tail[j] = NULL;
4166             free(s);                    /* Free the line */
4167             if (i % head == lines)      /* When to stop */
4168               break;
4169         }
4170         free((char *)tail);             /* Free the list */
4171         tail = NULL;
4172         if (tlen) free((char *)tlen);
4173         tlen = NULL;
4174     }
4175
4176 /* Come here when finished or on SIGINT */
4177
4178   xxdotype:
4179 #ifndef AMIGA
4180 #ifndef MAC
4181     signal(SIGINT,oldsig);              /* Put old signal action back. */
4182 #endif /* MAC */
4183 #endif /* AMIGA */
4184     if (tailing && tail) {
4185         for (i = 0; i < head; i++) {    /* Free each line. */
4186             if (tail[i])
4187               free(tail[i]);
4188         }
4189         free((char *)tail);             /* Free list pointer */
4190         if (tlen)
4191           free((char *)tlen);
4192     }
4193     x = zclose(ZIFILE);                 /* Done, close the input file */
4194     if (ofp != stdout) {                /* Close any output file */
4195         if (ofp) fclose(ofp);
4196         ofp = stdout;
4197     }
4198     binary = save;                      /* Restore text/binary mode */
4199 #ifdef UNICODE
4200     ucsbom = ucsbom_sav;                /* Restore BOM usage */
4201 #endif /* UNICODE */
4202
4203 #ifdef KUI
4204     if ( gui )
4205         gui_text_popup_wait(-1);        /* Wait for user to close the dialog */
4206 #endif /* KUI */
4207     return(rc);
4208 }
4209
4210 /* GREP command */
4211
4212 #define GREP_CASE  0                    /* /CASE */
4213 #define GREP_COUN  1                    /* /COUNT */
4214 #define GREP_DOTF  2                    /* /DOTFILES */
4215 #define GREP_NAME  3                    /* /NAMEONLY */
4216 #define GREP_NOBK  4                    /* /NOBACKUP */
4217 #define GREP_NODO  5                    /* /NODOTFILES */
4218 #define GREP_NOLI  6                    /* /NOLIST */
4219 #define GREP_NOMA  7                    /* /INVERT = /NOMATCH */
4220 #define GREP_NOPA  8                    /* /NOPAGE */
4221 #define GREP_NUMS  9                    /* /LINENUMBERS */
4222 #define GREP_PAGE 10                    /* /PAGE */
4223 #define GREP_RECU 11                    /* /RECURSIVE */
4224 #define GREP_TYPE 12                    /* /TYPE: */
4225 #define GREP_OUTP 13                    /* /OUTPUTFILE: */
4226 #define GREP_EXCP 14                    /* /EXCEPT: */
4227
4228 static struct keytab greptab[] = {
4229     { "/count",        GREP_COUN, CM_ARG },
4230     { "/dotfiles",     GREP_DOTF, 0 },
4231     { "/except",       GREP_EXCP, CM_ARG },
4232     { "/linenumbers",  GREP_NUMS, 0 },
4233     { "/nameonly",     GREP_NAME, 0 },
4234     { "/nobackupfiles",GREP_NOBK, 0 },
4235     { "/nocase",       GREP_CASE, 0 },
4236     { "/nodotfiles",   GREP_NODO, 0 },
4237     { "/nolist",       GREP_NOLI, 0 },
4238     { "/nomatch",      GREP_NOMA, 0 },
4239     { "/nopage",       GREP_NOPA, 0 },
4240     { "/output",       GREP_OUTP, CM_ARG },
4241     { "/page",         GREP_PAGE, 0 },
4242     { "/quiet",        GREP_NOLI, CM_INV },
4243 #ifdef RECURSIVE
4244     { "/recursive",    GREP_RECU, 0 },
4245 #endif /* RECURSIVE */
4246     { "/type",         GREP_TYPE, CM_ARG },
4247     { "", 0, 0 }
4248 };
4249 static int ngreptab =  sizeof(greptab)/sizeof(struct keytab)-1;
4250
4251 static char * grep_except = NULL;
4252
4253 int
4254 dogrep() {
4255     int match, x, y, fc, getval, mc = 0, count = 0, bigcount = 0;
4256     int fline = 0, sline = 0, wild = 0, len = 0;
4257     int xmode = -1, scan = 0;
4258     char c, name[CKMAXPATH+1], outfile[CKMAXPATH+1], *p, *s, *cv = NULL;
4259     FILE * fp = NULL;
4260
4261     int                                 /* Switch values and defaults */
4262       gr_coun = 0,
4263       gr_name = 0,
4264       gr_nobk = 0,
4265       gr_case = 1,
4266       gr_noli = 0,
4267       gr_noma = 0,
4268       gr_nums = 0,
4269       gr_excp = 0,
4270       gr_page = xaskmore;
4271
4272     struct FDB sw, fl;
4273
4274     g_matchdot = matchdot;              /* Save global matchdot setting */
4275     outfile[0] = NUL;
4276     makestr(&grep_except,NULL);
4277
4278     if (ofp != stdout) {                /* In case of previous interruption */
4279         if (ofp) fclose(ofp);
4280         ofp = stdout;
4281     }
4282     cmfdbi(&sw,                         /* First FDB - command switches */
4283            _CMKEY,                      /* fcode */
4284            "String or pattern to search for, or switch",
4285            "",                          /* default */
4286            "",                          /* addtl string data */
4287            ngreptab,                    /* addtl numeric data 1: tbl size */
4288            4,                           /* addtl numeric data 2: 4 = cmswi */
4289            xxstring,                    /* Processing function */
4290            greptab,                     /* Keyword table */
4291            &fl                          /* Pointer to next FDB */
4292            );
4293     cmfdbi(&fl,                         /* Anything that doesn't match */
4294            _CMFLD,                      /* fcode */
4295            "",                          /* hlpmsg */
4296            "",                          /* default */
4297            "",                          /* addtl string data */
4298            0,                           /* addtl numeric data 1 */
4299            0,                           /* addtl numeric data 2 */
4300            xxstring,                    /* xxstring */
4301            NULL,
4302            NULL
4303            );
4304     while (1) {                         /* Parse 0 or more switches */
4305         x = cmfdb(&sw);                 /* Parse something */
4306         if (x < 0)
4307           return(x);
4308         if (cmresult.fcode != _CMKEY)   /* Break out if not a switch */
4309           break;
4310         c = cmgbrk();
4311         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
4312             printf("?This switch does not take an argument\n");
4313             return(-9);
4314         }
4315         if ((cmresult.nresult != GREP_COUN) && !getval &&
4316             (cmgkwflgs() & CM_ARG)) {
4317             printf("?This switch requires an argument\n");
4318             return(-9);
4319         }
4320         switch (cmresult.nresult) {
4321           case GREP_COUN: {
4322               gr_coun++;
4323               if (getval) {
4324                   if ((x = cmfld("Variable for result","",&s,NULL)) < 0)
4325                     return(x);
4326                   makestr(&cv,s);
4327               }
4328               break;
4329           }
4330           case GREP_CASE: gr_case=0; break;
4331           case GREP_NAME: gr_name++; gr_noli=0; break;
4332           case GREP_NOBK: gr_nobk++; break;
4333           case GREP_NOLI: gr_noli++; gr_name=0; gr_nums=0; break;
4334           case GREP_NOMA: gr_noma++; break;
4335           case GREP_NOPA: gr_page=0; break;
4336           case GREP_NUMS: gr_nums++; gr_noli=0; break;
4337           case GREP_PAGE: gr_page++; gr_noli=0; break;
4338           case GREP_NODO:
4339             matchdot = 0;
4340             break;
4341           case GREP_DOTF:
4342             matchdot = 1;
4343             break;
4344 #ifdef RECURSIVE
4345           case GREP_RECU:
4346             recursive = 1;
4347             break;
4348 #endif /* RECURSIVE */
4349           case GREP_TYPE: {
4350               extern struct keytab txtbin[];
4351               if ((x = cmkey(txtbin,3,"","",xxstring)) < 0)
4352                 return(x);
4353               if (x == 2) {             /* ALL */
4354                   xmode = -1;
4355               } else {                  /* TEXT or BINARY only */
4356                   xmode = x;
4357                   scan = 1;
4358               }
4359               break;
4360           }
4361           case GREP_OUTP:               /* Send output to file */
4362             if ((x = cmofi("File for GREP'd lines","",&s,xxstring)) < 0)
4363               return(x);
4364             ckstrncpy(outfile,s,CKMAXPATH);
4365             break;
4366           case GREP_EXCP:               /* Exception pattern */
4367             if (getval) {
4368                 if ((x = cmfld("Exception pattern",
4369                                "",
4370                                &s,
4371                                xxstring
4372                                )) < 0)
4373                   return(x);
4374                 gr_excp++;
4375                 makestr(&grep_except,s);
4376             }
4377         }
4378     }
4379     if (outfile[0]) {
4380         ofp = fopen(outfile,"w");       /* Open output file */
4381         if (!ofp) {
4382             printf("?Can't open output file %s: %s\n",outfile,ck_errstr());
4383             ofp = stdout;
4384             return(-9);
4385         }
4386         gr_page = 0;
4387     }
4388     s = cmresult.sresult;
4389     s = brstrip(s);                     /* Strip braces from pattern */
4390     if (!*s) {
4391         printf("?Pattern required\n");
4392         return(-9);
4393     }
4394     ckstrncpy(tmpbuf,s,TMPBUFSIZ);      /* Save pattern */
4395     if ((x = cmifi("File(s) to search","",&s,&wild,xxstring)) < 0) {
4396         if (x == -3) {
4397             printf("?File specification required\n");
4398             x = -9;
4399         }
4400         return(x);
4401     }
4402     s = brstrip(s);                     /* Strip braces from filename */
4403 #ifndef ZXREWIND
4404     ckstrncpy(line,s,LINBUFSIZ);
4405 #endif /* ZXREWIND */
4406     if ((y = cmcfm()) < 0)
4407       return(y);
4408
4409     if (gr_page > -1)
4410       xaskmore = gr_page;               /* Paging... */
4411
4412     p = tmpbuf;                         /* Point to pattern */
4413 #ifdef COMMENT
4414 /* Now this is done in ckmatch */
4415     if (*p == '^') {                    /* '^' anchors pattern to beginning */
4416         p++;
4417     } else if (*p != '*') {             /* Otherwise prepend implied '*' */
4418         tmpbuf[0] = '*';
4419         p = tmpbuf;
4420     }
4421     x = strlen(p);                      /* Get length of result */
4422     if (x > 0 && x < TMPBUFSIZ) {       /* '$' at end anchors pattern to end */
4423         if (p[x-1] == '$') {
4424             p[x-1] = NUL;
4425         } else if (p[x-1] != '*') {
4426             p[x] = '*';
4427             p[x+1] = NUL;
4428         }
4429     }
4430 #endif /* COMMENT */
4431     debug(F111,"grep pat",p,x);
4432
4433 #ifdef ZXREWIND
4434     fc = zxrewind();                    /* Rewind the file list */
4435 #else
4436     {
4437         int flags = ZX_FILONLY;         /* Expand file list */
4438         if (matchdot)  flags |= ZX_MATCHDOT;
4439         if (recursive) flags |= ZX_RECURSE;
4440         fc = nzxpand(line,flags);
4441     }
4442 #endif /* ZXREWIND */
4443 #ifdef UNIX
4444     sh_sort(mtchs,NULL,fc,0,0,filecase);
4445 #endif /* UNIX */
4446
4447     debug(F101,"grep cmd_rows","",cmd_rows);
4448     debug(F101,"grep cmd_cols","",cmd_cols);
4449
4450     while (1) {                         /* Loop for each file */
4451         znext(name);                    /* Get next file */
4452         if (!name[0])                   /* No more, done */
4453           break;
4454         if (gr_nobk)                    /* Skipping backup files? */
4455           if (ckmatch("*.~[1-9]*~",name,1,1)) /* Backup file? */
4456             continue;                   /* Yes, skip */
4457         if (scan) {                     /* /TYPE: given? */
4458             switch (scanfile(name,&y,nscanfile)) { /* Yes, scan the file */
4459               case FT_BIN:
4460                 if (xmode != 1)
4461                   continue;
4462                 break;
4463               case FT_TEXT:
4464               case FT_7BIT:
4465               case FT_8BIT:
4466 #ifdef UNICODE
4467               case FT_UTF8:
4468               case FT_UCS2:
4469 #endif /* UNICODE */
4470                 if (xmode != 0)
4471                   continue;
4472             }
4473         }
4474         fp = fopen(name,"r");           /* Open */
4475         if (!fp)                        /* Can't */
4476           continue;                     /* Skip */
4477         count = 0;                      /* Match count, this file */
4478         fline = 0;                      /* Line count, this file */
4479         while (1) {                     /* Loop for each line */
4480             if (fgets(line,LINBUFSIZ,fp) == NULL) { /* Get next line */
4481                 fclose(fp);
4482                 fp = NULL;
4483                 debug(F100,"GREP EOF","",0);
4484                 break;
4485             }
4486             fline++;                    /* Count this line */
4487             line[LINBUFSIZ] = NUL;      /* Make sure it's terminated */
4488             debug(F111,"GREP",line,fline);
4489             len = (int)strlen(line);    /* Get length */
4490             while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r'))
4491               line[--len] = NUL;        /* Chop off terminators */
4492             match = ckmatch(p,line,gr_case,1+4); /* Match against pattern */
4493             if (match && gr_excp) {
4494                 if (ckmatch(grep_except,line,gr_case,1+4))
4495                     match = 0;
4496             }
4497             if (gr_noma)                /* Invert match sense if requested */
4498               match = !match;
4499             if (match) {                /* Have a matching line */
4500                 mc++;                   /* Total match count */
4501                 count++;                /* Match count this file */
4502                 if (gr_name) {          /* Don't care how many lines match */
4503                     fclose(fp);         /* Close the file */
4504                     fp = NULL;          /* and quit the line-reading loop. */
4505                     break;
4506                 }
4507                 if (gr_coun || gr_noli) /* Not listing each line */
4508                   continue;             /* so don't print anything now. */
4509                 if (wild) {             /* If searching multiple files */
4510                     fprintf(ofp,"%s:",name); /* print filename. */
4511                     len += (int)strlen(name) + 1;
4512                 }
4513                 if (gr_nums) {          /* If line numbers wanted */
4514                     char nbuf[32];
4515                     len += ckmakmsg(nbuf,32,ckitoa(fline),":",NULL,NULL);
4516                     fprintf(ofp,"%s",nbuf);
4517                 }
4518                 if (cmd_rows > 0 && cmd_cols > 0)
4519                   sline += (len / cmd_cols) + 1;
4520                 fprintf(ofp,"%s\n",line); /* Print the line. */
4521                 if (sline > cmd_rows - 3) {
4522                     if (!askmore()) goto xgrep; else sline = 0;
4523                 }
4524             }
4525         }
4526         if (!gr_noli) {                 /* If not not listing... */
4527             x = 0;
4528             if (gr_coun) {              /* Show match count only */
4529                 fprintf(ofp,"%s:%d\n",name,count);
4530                 x++;
4531             } else if (gr_name && count > 0) { /* Show name only */
4532                 fprintf(ofp,"%s\n",name);
4533                 x++;
4534             }
4535             if (x > 0) {
4536                 if (++sline > cmd_rows - 3) {
4537                     if (!askmore()) goto xgrep; else sline = 0;
4538                 }
4539             }
4540         }
4541         bigcount += count;              /* Overall count */
4542     }
4543   xgrep:
4544 #ifndef NOSPL
4545     if (gr_coun && cv) {                /* /COUNT:blah */
4546         addmac(cv,ckitoa(bigcount));    /* set the variable */
4547         makestr(&cv,NULL);              /* free this */
4548     }
4549 #endif /* NOSPL */
4550     if (fp) fclose(fp);                 /* close input file if still open */
4551     if (ofp != stdout) {                /* Close any output file */
4552         if (ofp) fclose(ofp);
4553         ofp = stdout;
4554     }
4555     return(success = mc ? 1 : 0);
4556 }
4557
4558 /* System-independent directory */
4559
4560 static char ** dirlist = NULL;
4561 static int ndirlist = 0;
4562
4563 static VOID
4564 freedirlist() {
4565     if (dirlist) {
4566         int i;
4567         for (i = 0; i < ndirlist; i++) {
4568             if (dirlist[i])
4569               free(dirlist[i]);
4570         }
4571         free((char *)dirlist);
4572         dirlist = NULL;
4573     }
4574     ndirlist = 0;
4575 }
4576
4577 static struct keytab dirswtab[] = {     /* DIRECTORY command switches */
4578     { "/after",       DIR_AFT, CM_ARG },
4579     { "/all",         DIR_ALL, 0 },
4580 #ifndef NOSPL
4581     { "/array",       DIR_ARR, CM_ARG },
4582 #endif /* NOSPL */
4583     { "/ascending",   DIR_ASC, 0 },
4584     { "/backup",      DIR_BUP, 0 },
4585     { "/before",      DIR_BEF, CM_ARG },
4586     { "/brief",       DIR_BRF, 0 },
4587     { "/count",       DIR_COU, CM_ARG },
4588     { "/descending",  DIR_DSC, CM_INV },
4589     { "/directories", DIR_DIR, 0 },
4590     { "/dotfiles",    DIR_DOT, 0 },
4591     { "/englishdate", DIR_DAT, 0 },
4592     { "/except",      DIR_EXC, CM_ARG },
4593     { "/files",       DIR_FIL, 0 },
4594     { "/heading",     DIR_HDG, 0 },
4595     { "/isodate",     DIR_ISO, 0 },
4596     { "/larger-than", DIR_LAR, CM_ARG },
4597 #ifdef CKSYMLINK
4598     { "/followlinks", DIR_LNK, 0 },
4599 #endif /* CKSYMLINK */
4600     { "/message",     DIR_MSG, CM_ARG },
4601     { "/nobackupfiles",DIR_NOB, 0 },
4602     { "/nodotfiles",  DIR_NOD, 0 },
4603 #ifdef CKSYMLINK
4604     { "/nofollowlinks",DIR_NLK, 0 },
4605 #endif /* CKSYMLINK */
4606     { "/noheading",   DIR_NOH, 0 },
4607 #ifdef CKSYMLINK
4608     { "/nolinks",     DIR_NOL, 0 },
4609 #endif /* CKSYMLINK */
4610     { "/nomessage",   DIR_NOM, 0 },
4611 #ifdef CK_TTGWSIZ
4612     { "/nopage",      DIR_NOP, 0 },
4613 #endif /* CK_TTGWSIZ */
4614 #ifdef RECURSIVE
4615     { "/norecursive", DIR_NOR, 0 },
4616 #else
4617 #ifdef VMS
4618     { "/norecursive", DIR_NOR, 0 },
4619 #else
4620 #ifdef datageneral
4621     { "/norecursive", DIR_NOR, 0 },
4622 #endif /* datageneral */
4623 #endif /* VMS */
4624 #endif /* RECURSIVE */
4625     { "/nosort",      DIR_NOS, 0 },
4626     { "/not-after",   DIR_NAF, CM_ARG },
4627     { "/not-before",  DIR_NBF, CM_ARG },
4628     { "/not-since",   DIR_NAF, CM_INV|CM_ARG },
4629     { "/noxfermode",  DIR_NOT, 0 },
4630     { "/output",      DIR_OUT, CM_ARG },
4631 #ifdef CK_TTGWSIZ
4632     { "/page",        DIR_PAG, 0 },
4633 #endif /* CK_TTGWSIZ */
4634 #ifdef RECURSIVE
4635     { "/recursive",   DIR_REC, 0 },
4636 #else
4637 #ifdef VMS
4638     { "/recursive",   DIR_REC, 0 },
4639 #else
4640 #ifdef datageneral
4641     { "/recursive",   DIR_REC, 0 },
4642 #endif /* datageneral */
4643 #endif /* VMS */
4644 #endif /* RECURSIVE */
4645     { "/reverse",     DIR_DSC, 0 },
4646     { "/since",       DIR_AFT, CM_ARG|CM_INV },
4647     { "/smaller-than",DIR_SMA, CM_ARG },
4648     { "/sort",        DIR_SRT, CM_ARG },
4649     { "/summary",     DIR_SUM, 0 },
4650     { "/top",         DIR_TOP, CM_ARG },
4651     { "/type",        DIR_BIN, CM_ARG },
4652     { "/xfermode",    DIR_TYP, 0 },
4653     { "/verbose",     DIR_VRB, 0 },
4654     { "",0,0 }
4655 };
4656 static int ndirswtab = (sizeof(dirswtab) / sizeof(struct keytab)) - 1;
4657
4658 static struct keytab dirsort[] = {      /* DIRECTORY /SORT: options */
4659     { "date",         DIRS_DT, 0 },
4660     { "name",         DIRS_NM, 0 },
4661     { "size",         DIRS_SZ, 0 }
4662 };
4663 static int ndirsort = (sizeof(dirsort) / sizeof(struct keytab));
4664
4665 static int dir_date = -1;               /* Option defaults (-1 means none) */
4666 static int dir_page = -1;
4667 static int dir_verb =  1;
4668 static int dir_msg  = -1;
4669 #ifdef VMS
4670 static int dir_sort = -1;               /* Names are already sorted in VMS */
4671 static int dir_rvrs = -1;
4672 #else
4673 static int dir_sort =  1;               /* Sort by default */
4674 static int dir_rvrs =  0;               /* Not in reverse */
4675 #endif /* VMS */
4676 static int dir_skey = DIRS_NM;          /* By name */
4677 #ifdef RECURSIVE
4678 static int dir_recu = -1;
4679 #endif /* RECURSIVE */
4680 static int dir_mode = -1;
4681 static int dir_show = -1;               /* Show all files by default */
4682 int dir_dots =  -1;                     /* Except dot files */
4683 int dir_back =  1;
4684 int dir_head =  0;
4685 static char * dirmsg = NULL;
4686 static int dirmsglen = 0;
4687
4688 #ifndef NOSHOW
4689 VOID
4690 showdiropts() {
4691     int x = 0;
4692     extern int optlines;
4693     prtopt(&optlines,"DIRECTORY");
4694     if (dir_show > 0) {
4695         prtopt(&optlines,(dir_show == 1) ? "/FILES" :
4696                ((dir_show == 2) ? "/DIRECTORIES" : "/ALL"));
4697         x++;
4698     } else {
4699         prtopt(&optlines,"/ALL");
4700         x++;
4701     }
4702     if (dir_verb > -1) {
4703         prtopt(&optlines,dir_verb ? "/VERBOSE" : "/BRIEF");
4704         x++;
4705     }
4706     if (dir_page > -1) {
4707         prtopt(&optlines,dir_page ? "/PAGE" : "/NOPAGE");
4708         x++;
4709     }
4710     if (dir_date > -1) {
4711         prtopt(&optlines,dir_date ? "/ENGLISHDATE" : "/ISODATE");
4712         x++;
4713     }
4714     if (dir_dots > -1) {
4715         prtopt(&optlines,dir_dots ? "/DOTFILES" : "/NODOTFILES");
4716         x++;
4717     }
4718     if (dir_back > -1) {
4719         prtopt(&optlines,dir_back ? "/BACKUP" : "/NOBACKUP");
4720         x++;
4721     }
4722     if (dir_head > -1) {
4723         prtopt(&optlines,dir_head ? "/HEADING" : "/NOHEADING");
4724         x++;
4725     }
4726 #ifdef RECURSIVE
4727     if (dir_recu > -1) {
4728         prtopt(&optlines,dir_recu ? "/RECURSIVE" : "/NORECURSIVE");
4729         x++;
4730     }
4731 #endif /* RECURSIVE */
4732     if (dir_mode > -1) {
4733         prtopt(&optlines,dir_mode ? "/XFERMODE" : "/NOXFERMODE");
4734         x++;
4735     }
4736     if (dir_sort == 0) {
4737         x++;
4738         prtopt(&optlines,"/NOSORT ");
4739     } else if (dir_sort > 0) {
4740         x++;
4741         if (dir_skey == DIRS_NM) s = "/SORT:NAME";
4742         else if (dir_skey == DIRS_SZ) s = "/SORT:SIZE";
4743         else if (dir_skey == DIRS_DT) s = "/SORT:DATE";
4744         prtopt(&optlines,s);
4745     }
4746     if (dir_rvrs > -1) {
4747         prtopt(&optlines,dir_rvrs ? "/REVERSE" : "/ASCENDING");
4748         x++;
4749     }
4750     if (dir_msg > -1) {
4751         if (dir_msg == 0) {
4752             prtopt(&optlines,"/NOMESSAGE");
4753         } else {
4754             ckmakmsg(tmpbuf,TMPBUFSIZ,"/MESSAGE:{",dirmsg,"}",NULL);
4755             prtopt(&optlines,tmpbuf);
4756         }
4757         x++;
4758     }
4759     if (!x) prtopt(&optlines,"(no options set)");
4760     prtopt(&optlines,"");
4761 }
4762 #endif /* NOSHOW */
4763
4764 int
4765 setdiropts() {                          /* Set DIRECTORY option defaults */
4766     int xb = -1, xv = -1, xp = -1, xd = -1, xh = -1, xf = -1;
4767     int xk = -1, xr = -1, xs = -1, xx = -1, xm = -1, xa = -1, xg = -1;
4768     int getval;
4769     char c;
4770     while (1) {
4771         if ((y = cmswi(dirswtab,ndirswtab,"Switch","",xxstring)) < 0) {
4772             if (y == -3)
4773               break;
4774             else
4775               return(y);
4776         }
4777         c = cmgbrk();
4778         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
4779             printf("?This switch does not take an argument\n");
4780             return(-9);
4781         }
4782         if (!getval && (cmgkwflgs() & CM_ARG)) {
4783             printf("?This switch requires an argument\n");
4784             return(-9);
4785         }
4786         switch (y) {
4787           case DIR_BRF: xv = 0; break;
4788           case DIR_VRB: xv = 1; break;
4789           case DIR_PAG: xp = 1; break;
4790           case DIR_NOP: xp = 0; break;
4791           case DIR_ISO: xd = 0; break;
4792           case DIR_DAT: xd = 1; break;
4793           case DIR_HDG: xh = 1; break;
4794           case DIR_NOH: xh = 0; break;
4795           case DIR_DOT: xf = 1; break;
4796           case DIR_NOD: xf = 0; break;
4797           case DIR_ALL: xa = 3; break;
4798           case DIR_DIR: xa = 2; break;
4799           case DIR_FIL: xa = 1; break;
4800           case DIR_SRT:
4801             x = DIRS_NM;
4802             if (getval)
4803               if ((x = cmkey(dirsort,ndirsort,"Sort key","name",xxstring)) < 0)
4804                 return(x);
4805             xk = x;
4806             xs = 1;
4807             break;
4808           case DIR_NOS: xs = 0; break;
4809           case DIR_ASC: xx = 0; break;
4810           case DIR_DSC: xx = 1; break;
4811           case DIR_REC: xr = 1; break;
4812           case DIR_NOR: xr = 0; break;
4813           case DIR_TYP: xm = 1; break;
4814           case DIR_NOT: xm = 0; break;
4815           case DIR_BUP: xb = 1; break;
4816           case DIR_NOB: xb = 0; break;
4817           case DIR_NOM: xg = 0; break;
4818           case DIR_MSG:
4819             if (getval)
4820               if ((x = cmfld("Message to append to each line",
4821                              "",
4822                              &s,
4823                              xxstring
4824                              )) < 0)
4825                 return(x);
4826             xg = 1;
4827             ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ);
4828             break;
4829           default:
4830             printf("?This option can not be set\n");
4831             return(-9);
4832         }
4833     }
4834     if ((x = cmcfm()) < 0)              /* Get confirmation */
4835       return(x);
4836     if (xv > -1) dir_verb = xv;         /* Confirmed, save defaults */
4837     if (xp > -1) dir_page = xp;
4838     if (xd > -1) dir_date = xd;
4839     if (xh > -1) dir_head = xh;
4840     if (xs > -1) dir_sort = xs;
4841     if (xk > -1) dir_skey = xk;
4842     if (xx > -1) dir_rvrs = xx;
4843     if (xf > -1) dir_dots = xf;
4844     if (xa > -1) dir_show = xa;
4845     if (xm > -1) dir_mode = xm;
4846     if (xb > -1) dir_back = xb;
4847 #ifdef RECURSIVE
4848     if (xr > -1) dir_recu = xr;
4849 #endif /* RECURSIVE */
4850     if (xg > -1) dir_msg  = xg;
4851     if (xg > 0)
4852       makestr(&dirmsg,tmpbuf);
4853     return(success = 1);
4854 }
4855
4856 int
4857 domydir(cx) int cx; {                   /* Internal DIRECTORY command */
4858     extern char *months[];
4859 #ifdef VMS
4860     _PROTOTYP( char * zrelname, (char *,char *) );
4861     char * cdp = NULL;
4862 #endif /* VMS */
4863
4864     char name[CKMAXPATH+1], outfile[CKMAXPATH+1], *p = NULL, c = NUL;
4865     char linebuf[CKMAXPATH+256];
4866     char * mstr = NULL, * dstr = NULL, * s2 = NULL, * cv = NULL;
4867     CK_OFF_T len = (CK_OFF_T)0, nbytes = (CK_OFF_T)0;
4868     CK_OFF_T minsize = (CK_OFF_T)-1, maxsize = (CK_OFF_T)-1;
4869     long ndirs = 0, nfiles = 0, nmatches = 0;
4870     int verbose = 0, wild = 0, page = 0, n = 0, engdate = 0, summary = 0;
4871     int heading = 0, xsort = 0, reverse = 0, sortby = 0, msg = 0;
4872     int k, i = 0, x = 0, nx = 0, skey = 0, dlen = 0, itsadir = 0;
4873     int show = 3, xfermod = 0, backup = 1, rc = 0, getval = 0;
4874     int touch = 0;
4875     int fs = 0;
4876     int multiple = 0;
4877     int cmifn1 = 1, cmifn2 = 0;
4878     int dir_top = 0, dir_cou = 0;
4879     int dontshowlinks = 0;
4880     int dontfollowlinks = 0;
4881     int arrayindex = -1;
4882     struct FDB sw, fi, fl;
4883     char dbuf[32], xbuf[32];
4884
4885 #ifndef NOSPL
4886     char array = NUL;
4887     char ** ap = NULL;
4888 #endif /* NOSPL */
4889     char
4890       * dir_aft = NULL,
4891       * dir_bef = NULL,
4892       * dir_naf = NULL,
4893       * dir_nbf = NULL,
4894       * dir_exc = NULL;
4895     char * xlist[16];
4896
4897     debug(F101,"domydir cx","",cx);
4898
4899     g_matchdot = matchdot;              /* Save global matchdot setting */
4900 #ifdef COMMENT
4901     nolinks = 2;                        /* (it should already be 2) */
4902 #endif  /* COMMENT */
4903     outfile[0] = NUL;                   /* No output file yet */
4904
4905     if (ofp != stdout) {                /* In case of previous interruption */
4906         if (ofp) fclose(ofp);
4907         ofp = stdout;
4908     }
4909     for (i = 0; i < 16; i++) xlist[i] = NULL;
4910
4911     dir_top = 0;
4912     name[0] = NUL;
4913     freedirlist();                      /* In case not freed last time */
4914     page      = dir_page > -1 ? dir_page : xaskmore; /* Set option defaults */
4915     engdate   = dir_date > -1 ? dir_date : 0;
4916     verbose   = dir_verb > -1 ? dir_verb : 1;
4917     heading   = dir_head > -1 ? dir_head : 0;
4918     xsort     = dir_sort > -1 ? dir_sort : 0;
4919     sortby    = dir_skey > -1 ? dir_skey : 0;
4920     reverse   = dir_rvrs > -1 ? dir_rvrs : 0;
4921     msg       = dir_msg  > -1 ? dir_msg  : 0;
4922 #ifdef UNIXOROSK
4923     if (dir_dots > -1) matchdot = dir_dots;
4924 #endif /* UNIXOROSK */
4925     xfermod   = dir_mode > -1 ? dir_mode : 0;
4926     backup    = dir_back > -1 ? dir_back : 1;
4927 #ifdef RECURSIVE
4928     recursive = dir_recu > -1 ? dir_recu : 0;
4929 #endif /* RECURSIVE */
4930     show      = dir_show > -1 ? dir_show : 3;
4931
4932     if (cx == XXWDIR) {                 /* WDIRECTORY */
4933         debug(F100,"domydir WDIRECTORY","",0);
4934         reverse = 1;                    /* Reverse chronological order */
4935         xsort = 1;
4936         sortby = DIRS_DT;
4937     } else if (cx == XXHDIR) {          /* HDIRECTORY */
4938         debug(F100,"domydir HDIRECTORY","",0);
4939         reverse = 1;                    /* Reverse order by size */
4940         xsort = 1;
4941         sortby = DIRS_SZ;
4942     } else if (cx == XXTOUC) {
4943         touch = 1;
4944         verbose = 0;
4945     }   
4946
4947 #ifdef CK_TTGWSIZ
4948 #ifdef OS2
4949     ttgcwsz();                          /* Screen length for more-prompting */
4950 #else /* OS2 */
4951     /* Check whether window size changed */
4952     if (ttgwsiz() > 0) {
4953         if (tt_rows > 0 && tt_cols > 0) {
4954             cmd_rows = tt_rows;
4955             cmd_cols = tt_cols;
4956         }
4957     }
4958 #endif /* OS2 */
4959 #endif /* CK_TTGWSIZ */
4960
4961     diractive = 1;
4962     cmifn1 = nolinks | 1;               /* 1 = files or directories */
4963     cmifn2 = 0;                         /* 0 = not directories only */
4964
4965   again:
4966
4967     cmfdbi(&sw,                         /* First FDB - command switches */
4968            _CMKEY,                      /* fcode */
4969            "Enter or Return to confirm the command, or\n\
4970  file specification, or switch",
4971            "",                          /* default */
4972            "",                          /* addtl string data */
4973            ndirswtab,                   /* addtl numeric data 1: tbl size */
4974            4,                           /* addtl numeric data 2: 4 = cmswi */
4975            xxstring,                    /* Processing function */
4976            dirswtab,                    /* Keyword table */
4977            &fi                          /* Pointer to next FDB */
4978            );
4979     cmfdbi(&fi,                         /* 2nd FDB - filespec to match */
4980            _CMIFI,                      /* fcode */
4981            "File specification",        /* hlpmsg */
4982 #ifdef datageneral
4983            "+",                         /* Default filespec is wildcard */
4984 #else                                   /* that matches all files... */
4985 #ifdef VMS
4986            "*.*",
4987 #else
4988            "*",
4989 #endif /* VMS */
4990 #endif /* datageneral */
4991            "",                          /* addtl string data */
4992            cmifn1,
4993            cmifn2,                      /* 1 = only dirs; 0 files or dirs */
4994            xxstring,
4995            NULL,
4996            &fl
4997            );
4998     cmfdbi(&fl,                         /* Anything that doesn't match */
4999            _CMFLD,                      /* fcode */
5000            "",                          /* hlpmsg */
5001            "",                          /* default */
5002            "",                          /* addtl string data */
5003            0,                           /* addtl numeric data 1 */
5004            0,                           /* addtl numeric data 2 */
5005            xxstring,
5006            NULL,
5007            NULL
5008            );
5009     while (1) {                         /* Parse 0 or more switches */
5010         x = cmfdb(&sw);                 /* Parse something */
5011         debug(F101,"domydir cmfdb","",x);
5012         if (x < 0)
5013           return(x); 
5014         if (cmresult.fcode != _CMKEY)   /* Break out if not a switch */
5015           break;
5016         c = cmgbrk();
5017         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
5018             printf("?This switch does not take an argument\n");
5019             return(-9);
5020         }
5021         k = cmresult.nresult;
5022         if (!getval &&
5023             (cmgkwflgs() & CM_ARG) && k != DIR_TOP && k != DIR_COU) {
5024             printf("?This switch requires an argument\n");
5025             return(-9);
5026         }
5027         switch (k) {
5028           case DIR_COU: {
5029               dir_cou++;
5030               if (getval) {
5031                   if ((x = cmfld("Variable for result","",&s,NULL)) < 0)
5032                     return(x);
5033                   makestr(&cv,s);
5034               }
5035               break;
5036           }
5037           case DIR_BRF: verbose = 0; break;
5038           case DIR_VRB: verbose = 1; break;
5039 #ifdef CK_TTGWSIZ
5040           case DIR_PAG: page = 1;    break;
5041           case DIR_NOP: page = 0;    break;
5042 #endif /* CK_TTGWSIZ */
5043           case DIR_ISO: engdate = 0; break;
5044           case DIR_DAT: engdate = 1; break;
5045           case DIR_HDG: heading = 1; break;
5046           case DIR_NOH: heading = 0; break;
5047 #ifdef UNIXOROSK
5048           case DIR_DOT: matchdot = 1; break;
5049           case DIR_NOD: matchdot = 0; break;
5050 #endif /* UNIXOROSK */
5051           case DIR_ALL:
5052             show = 3;
5053             cmifn1 |= 1;
5054             cmifn2 = 0;
5055             goto again;
5056           case DIR_DIR:
5057             show = 2;
5058             cmifn1 |= 1;
5059             cmifn2 = 1;
5060             goto again;
5061           case DIR_FIL:
5062             show = 1;
5063             cmifn1 &= ~(1);
5064             cmifn2 = 0;
5065             goto again;
5066           case DIR_SRT:
5067             x = DIRS_NM;
5068             if (c == ':' || c == '=')
5069               if ((x = cmkey(dirsort,ndirsort,"Sort key","name",xxstring)) < 0)
5070                 return(x);
5071             xsort = 1;
5072             sortby = x;
5073             break;
5074
5075           case DIR_BUP: backup  = 1; fs++;   break;
5076           case DIR_NOB: backup  = 0; fs++;   break;
5077
5078           case DIR_NOS: xsort = 0;     break;
5079           case DIR_ASC: reverse = 0;   break;
5080           case DIR_DSC: reverse = 1;   break;
5081 #ifdef RECURSIVE
5082           case DIR_REC: recursive = 1; diractive = 1; break;
5083           case DIR_NOR: recursive = 0; diractive = 0; break;
5084 #endif /* RECURSIVE */
5085           case DIR_TYP: xfermod = 1;   break;
5086           case DIR_NOT: xfermod = 0;   break;
5087
5088 #ifdef CKSYMLINK
5089           case DIR_LNK:                 /* Follow links */
5090 #ifdef COMMENT
5091             /* A command switch shouldn't be setting a global value! */
5092             nolinks = 0;
5093 #endif  /* COMMENT */
5094             cmifn1 &= ~(2);
5095             dontfollowlinks = 0;
5096             goto again;
5097           case DIR_NLK:                 /* Don't follow links */
5098 #ifdef COMMENT
5099             nolinks = 2;
5100 #endif  /* COMMENT */
5101             cmifn1 &= ~(2);
5102             dontfollowlinks = 1;
5103             goto again;
5104           case DIR_NOL:                 /* Don't show links at all */
5105             dontshowlinks = 1;
5106             goto again;
5107 #endif /* CKSYMLINK */
5108
5109           case DIR_NOM: msg     = 0;   break;
5110           case DIR_MSG:
5111             if (c == ':' || c == '=')
5112               if ((x = cmfld("Message to append to each line",
5113                              "",
5114                              &s,
5115                              xxstring
5116                              )) < 0)
5117                 return(x);
5118             msg = 1;
5119             ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ);
5120             break;
5121
5122           case DIR_SMA:
5123           case DIR_LAR: {
5124               CK_OFF_T y;
5125               if (!getval) break;
5126               if ((x = cmnumw("File size in bytes","0",10,&y,xxstring)) < 0)
5127                 return(x);
5128               fs++;
5129               show = 1;
5130               switch (cmresult.nresult) {
5131                 case DIR_SMA: minsize = y; break;
5132                 case DIR_LAR: maxsize = y; break;
5133               }
5134               break;
5135           }
5136           case DIR_TOP:
5137             dir_top = 10;
5138             if (!getval) break;
5139               if ((x = cmnum("How many lines to show","10",10,&y,xxstring))< 0)
5140                 return(x);
5141               dir_top = y;
5142               break;
5143
5144 #ifndef NOSPL
5145           case DIR_ARR:
5146             if (c != ':' && c != '=') {
5147                 printf("?Array name required\n");
5148                 return(-9);
5149             }
5150             if ((x = cmfld("Array name (a single letter will do)",
5151                            "",
5152                            &s,
5153                            NULL
5154                            )) < 0) {
5155                 if (x == -3) {
5156                     printf("?Array name required\n");
5157                     return(-9);
5158                 } else
5159                   return(x);
5160             }
5161             if (!*s) {
5162                 printf("?Array name required\n");
5163                 return(-9);
5164             }
5165             s2 = s;
5166             if (*s == CMDQ) s++;
5167             if (*s == '&') s++;
5168             if (!isalpha(*s)) {
5169                 printf("?Bad array name - \"%s\"\n",s2);
5170                 return(-9);
5171             }
5172             array = *s++;
5173             if (isupper(array)) array = tolower(array);
5174             if (*s && (*s != '[' || *(s+1) != ']')) {
5175                 printf("?Bad array name - \"%s\"\n",s2);
5176                 return(-9);
5177             }
5178             break;
5179 #endif /* NOSPL */
5180           case DIR_AFT:
5181           case DIR_BEF:
5182           case DIR_NAF:
5183           case DIR_NBF:
5184             if (!getval) break;
5185             if ((x = cmdate("File-time","",&s,0,xxstring)) < 0) {
5186                 if (x == -3) {
5187                     printf("?Date-time required\n");
5188                     rc = -9;
5189                 } else
5190                   rc = x;
5191                 goto xdomydir;
5192             }
5193             fs++;
5194             switch (k) {
5195               case DIR_AFT: makestr(&dir_aft,s); break;
5196               case DIR_BEF: makestr(&dir_bef,s); break;
5197               case DIR_NAF: makestr(&dir_naf,s); break;
5198               case DIR_NBF: makestr(&dir_nbf,s); break;
5199             }
5200             break;
5201           case DIR_EXC:
5202             if (!getval) break;
5203             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
5204                 if (x == -3) {
5205                     printf("?Pattern required\n");
5206                     rc = -9;
5207                 } else
5208                   rc = x;
5209                 goto xdomydir;
5210             }
5211             fs++;
5212             makestr(&dir_exc,s);
5213             break;
5214
5215           case DIR_SUM:
5216             summary = 1; break;
5217
5218           case DIR_BIN: {
5219               extern struct keytab txtbin[];
5220               extern int xfiletype;
5221               if (!getval) break;
5222               if ((x = cmkey(txtbin,3,"","all",xxstring)) < 0) {
5223                   rc = x;
5224                   goto xdomydir;
5225               }
5226               if (x == 2) {
5227                   xfiletype = -1;
5228               } else {
5229                   xfiletype = x;
5230                   fs = 1;
5231               }
5232               break;
5233           }
5234           case DIR_OUT:
5235             if ((x = cmofi("File for directory listing","",&s,xxstring)) < 0)
5236               return(x);
5237             ckstrncpy(outfile,s,CKMAXPATH+1);
5238             break;
5239
5240           default:
5241             printf("?Sorry, not implemented yet - \"%s\"\n", atmbuf);
5242             goto xdomydir;
5243         }
5244     }
5245     ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Safe copy of filespec */
5246
5247 /* ^^^ START MULTIPLE */
5248     
5249     while (!touch) {
5250         x = cmfld("Another filespec or Enter","",&s,xxstring);
5251         if (x == -3)
5252           break;
5253         if (x < 0)
5254           return(x);
5255         ckstrncat(line,",",LINBUFSIZ);
5256         ckstrncat(line,s,LINBUFSIZ);
5257         multiple++;
5258     }
5259     ckmakmsg(tmpbuf,TMPBUFSIZ,"{",line,"}",NULL);
5260     ckstrncpy(line,tmpbuf,LINBUFSIZ);
5261     cmresult.nresult = 1;
5262     cmresult.fcode = _CMIFI;
5263
5264 /* ^^^ END MULTIPLE */
5265
5266     s = line;
5267
5268     if ((x = cmcfm()) < 0)              /* Get confirmation */
5269       return(x);
5270
5271 /*
5272   Command is TOUCH and file doesn't exist.
5273 */
5274     if (touch) {                        /* TOUCH */
5275         if ((cmresult.fcode == _CMIFI && zchki(s) == (CK_OFF_T)-1)) {
5276             FILE * fp;
5277             s = brstrip(s);
5278             if (!iswild(s)) {
5279 #ifdef UNIX
5280                 if (s[0] == '~')
5281                   s = tilde_expand(s);
5282 #endif  /* UNIX */
5283                 fp = fopen(s,"w");      /* Create file */
5284                 if (!fp) {
5285                     printf("?TOUCH %s: %s\n",s,ck_errstr());
5286                     rc = -9;
5287                     goto xdomydir;
5288                 }
5289                 fclose(fp);
5290                 cx = XXDIR;             /* Now maybe list it. */
5291                 multiple++;             /* Force new directory scan */
5292             }
5293         }
5294     } else
5295
5296     if (cmresult.fcode != _CMIFI) {     /* Nothing matched */
5297         /*
5298           Note - this never gets executed because after the "begin
5299           multiple" hack above, the result is always _CMIFI).
5300         */
5301         char * m;
5302         if (*s == '/')
5303 #ifdef UNIXOROSK
5304           m = "does not match switch or name of accessible file";
5305 #else
5306 #ifdef OS2
5307         m = "does not match switch or name of accessible file";
5308 #else
5309         m = "no switches match";
5310 #endif /* OS2 */
5311 #endif /* UNIXOROSX */
5312         else
5313           m = "not found or not accessible";
5314         printf("\"%s\" - %s\n",s,m);
5315         rc = -9;
5316         goto xdomydir;
5317     }
5318 #ifdef COMMENT
5319     /* This can't be right because it's based on _CMCFM */
5320     wild = cmresult.nresult;            /* Wildcard was given? */
5321     debug(F111,"domydir cmifi2",s,wild);
5322 #else
5323     wild = 0;
5324 #endif  /* COMMENT */
5325
5326     if (outfile[0]) {                   /* If an output file was specified */
5327         ofp = fopen(outfile,"w");       /* open it */
5328         if (!ofp) {
5329             printf("?Can't open output file %s: %s\n",outfile,ck_errstr());
5330             ofp = stdout;
5331             return(-9);
5332         }
5333         page = 0;
5334     }
5335
5336 #ifdef OS2
5337     if (!wild) {
5338         if (zchki(s) == -2) {           /* Found a directory */
5339             p = s + (int)strlen(s) - 1; /* Yes */
5340             if (*p == '\\' || *p == '/')
5341               strcat(s, "*");
5342             else if (*p == ':')
5343               strcat(s, "./*");
5344             else
5345               strcat(s, "/*");
5346             wild = 1;                   /* Now it's wild */
5347         }
5348     }
5349 #else
5350     if (!wild) if (isdir(s)) {          /* Is it a directory? */
5351         p = s + (int)strlen(s) - 1;     /* Yes */
5352 #ifdef VMS
5353         {
5354             /* Convert from FOO.DIR;1 to [x.FOO] if necessary */
5355             char buf[CKMAXPATH+1];
5356             debug(F000,"domydir directory 0",s,*p);
5357             if (cvtdir(s,buf,CKMAXPATH) > 0)
5358               ckstrncpy(line,buf,LINBUFSIZ);
5359         }
5360 #endif /* VMS */
5361         debug(F000,"domydir directory 1",s,*p);
5362 #ifdef VMS
5363         if (*p == ']' || *p == '>' || *p == ':')
5364           strcat(s, "*.*");
5365 #else
5366 #ifdef datageneral
5367         if (*p == ':')
5368           strcat(s, "+");
5369         else
5370           strcat(s, ":+");
5371 #else
5372 #ifdef STRATUS
5373         if (*p == '>')
5374           strcat(s, "*");
5375         else
5376           strcat(s, ">*");
5377 #endif /* STRATUS */
5378 #endif /* datageneral */
5379 #endif /* VMS */
5380         wild = 1;                       /* Now it's wild */
5381         debug(F000,"domydir directory 2",s,*p);
5382     }
5383 #endif /* OS2 */
5384
5385 #ifdef ZXREWIND
5386 /* cmifi() already called nzxpand so we can just re-use the same list. */
5387     if (!multiple) {
5388         x = zxrewind();                 /* Rewind the list */
5389         debug(F111,"domydir zxrewind",s,x);
5390     } else {
5391 #endif /* ZXREWIND */
5392 /*
5393   In case we gave multiple filespecs they are now in {a,b,c} list format.
5394   Which is a valid wildcard.  We pass it to nzxpand() to get back the list
5395   of files that match.  This is fine for DIRECTORY but it's not find for
5396   TOUCH because we want TOUCH to see those names so it can create the files.
5397   So for now at least, if TOUCH is to be used to create files -- as opposed
5398   to changing the timestamps of existing files -- it can only do one file
5399   at a time.
5400 */
5401         nzxopts = (show == ZX_DIRONLY) ? ZX_DIRONLY :
5402           (show == ZX_FILONLY ? ZX_FILONLY : 0);
5403         if (matchdot)  nzxopts |= ZX_MATCHDOT;
5404         if (recursive) nzxopts |= ZX_RECURSE;
5405         x = nzxpand(s,nzxopts);             /* Expand file list */
5406         debug(F111,"domydir nzxpand",s,x);
5407 #ifdef ZXREWIND
5408     }
5409 #endif /* ZXREWIND */
5410
5411 #ifndef NOSPL
5412     if (array) {
5413         int n, xx;
5414         n = (x < 0) ? 0 : x;
5415         if ((xx = dclarray(array,n)) < 0) {
5416             printf("?Array declaration failure\n");
5417             rc = -9;
5418             goto xdomydir;
5419         }
5420         arrayindex = xx;
5421         ap = a_ptr[xx];                 /* Pointer to list of elements */
5422         if (ap)                         /* Set element 0 to dimension */
5423           makestr(&(ap[0]),"0");        /* which so far is zero */
5424         if (n < 1) {                    /* No files matched, done. */
5425             rc = 0;
5426             goto xdomydir;
5427         }
5428     } else
5429 #endif /* NOSPL */
5430       if (!touch && x < 1) {
5431 #ifdef CKROOT
5432           extern int ckrooterr;
5433           if (ckrooterr)
5434             printf("?Off limits: %s\n",s);
5435           else
5436 #endif /* CKROOT */
5437             if (x == 0 && isdir(s))
5438               printf("?Empty directory - \"%s\"\n", s);
5439             else
5440               printf("?%s %s match - \"%s\"\n",
5441                      (x == 0) ? "No" : "Too many",
5442                      (show == 2) ? "directories" : "files",
5443                      brstrip(s)
5444                      );
5445           rc = -9;
5446           goto xdomydir;
5447     }
5448     nx = x;                             /* Remember how many files */
5449     if (nx < 2) xsort = 0;              /* Skip sorting if none or one */
5450
5451     if (msg) {
5452         makestr(&dirmsg,tmpbuf);
5453         dirmsglen = strlen(tmpbuf);
5454     }
5455
5456 #ifdef VMS
5457     cdp = zgtdir();                     /* Get current directory */
5458     debug(F110,"domydir VMS zgtdir",cdp,0);
5459 #endif /* VMS */
5460
5461     if (xsort && verbose) {             /* If sorting, allocate space */
5462         if (!(dirlist = (char **) malloc((x + 1) * sizeof(char **)))) {
5463             if (!quiet) {
5464                 printf("* Warning: Failure to allocate memory for sorting.\n");
5465                 printf("* Will proceed without sorting...\n");
5466             }
5467             xsort = 0;
5468         }
5469         debug(F101,"domydir sort malloc","",xsort);
5470     }
5471
5472     /* Display the listing */
5473
5474 #ifndef NOSPL
5475     if (array)                          /* Storing instead of printing */
5476       heading = 0;
5477 #endif /* NOSPL */
5478
5479     if (heading) {                      /* If /HEADING print heading */
5480         zfnqfp(s,TMPBUFSIZ,tmpbuf);
5481         fprintf(ofp,"\nDirectory of %s\n\n",tmpbuf);
5482         n += 3;
5483     }
5484     if (page > -1)                      /* Paging */
5485       xaskmore = page;
5486
5487     if (!verbose && !touch) {           /* /BRIEF */
5488         if (outfile[0]) {               /* To file  */
5489             int k = 0;
5490             znext(name);
5491             while (name[0]) {           /* One line per file */
5492                 k++;
5493                 if (fs) if (fileselect(name,
5494                                        dir_aft,dir_bef,dir_naf,dir_nbf,
5495                                        minsize,maxsize,!backup,16,xlist) < 1) {
5496                     znext(name);
5497                     continue;
5498                 }
5499                 fprintf(ofp,"%s\n",name);
5500                 znext(name);
5501             }
5502             if (heading)
5503               fprintf(ofp,"Files: %d\n\n",k);
5504             rc = 1;
5505             goto xdomydir;
5506         } else {
5507             rc = xfilhelp(x,"","",n,0,1,
5508                           dir_aft,dir_bef,dir_naf,dir_nbf,
5509                           minsize,maxsize,!backup,16,xlist);
5510             if (rc < 0)
5511               goto xdomydir;
5512             if (heading && rc > 0)
5513               fprintf(ofp,"Files: %d\n\n",x); /* (Might scroll a line or 2) */
5514             rc = 1;
5515             goto xdomydir;
5516         }
5517     }
5518     ndirs = nfiles = 0L;                /* Initialize counters */
5519     nbytes = (CK_OFF_T)0;
5520
5521     if (dir_exc)                        /* Have exception list? */
5522       makelist(dir_exc,xlist,16);       /* Yes, convert to array */
5523
5524     diractive = 1;
5525     znext(name);                        /* Get next file */
5526     while (name[0]) {                   /* Loop for each file */
5527         if (fs) if (fileselect(name,
5528                        dir_aft,dir_bef,dir_naf,dir_nbf,
5529                        minsize,maxsize,!backup,16,xlist) < 1) {
5530             znext(name);
5531             continue;
5532         }
5533         len = zgetfs(name);             /* Get file length */
5534         debug(F111,"domydir zgetfs",name,len);
5535 #ifdef VMSORUNIX
5536         itsadir = zgfs_dir;             /* See if it's a directory */
5537 #else
5538         itsadir = (len == (CK_OFF_T)-2 || isdir(name));
5539 #endif /* VMSOUNIX */
5540         debug(F111,"domydir itsadir",name,itsadir);
5541         if ((itsadir && (show == 1)) || (!itsadir && (show == 2))) {
5542             znext(name);
5543             continue;
5544         }
5545         /* Get here when we know we have selected this file */
5546
5547         nmatches++;
5548         if (itsadir) {                  /* Accumulate totals for summary */
5549             ndirs++;
5550         } else {
5551             nfiles++;
5552             nbytes += len;
5553         }
5554         dstr = NULL;
5555         if (cx == XXTOUC) {             /* Command was TOUCH, not DIRECTORY */
5556             char * filename;
5557             struct zattr xx;
5558             dstr = ckcvtdate("",0);
5559             xx.date.val = dstr;
5560             xx.date.len = (int)strlen(xx.date.val);
5561             xx.lprotect.len = 0;
5562             debug(F110,"domydir touch",name,0);
5563             debug(F110,"domydir touch",dstr,0);
5564             if (zstime(name,&xx,0) < 0) {
5565                 printf("?TOUCH %s: %s\n",name,ck_errstr());
5566                 rc = -9;
5567                 goto xdomydir;
5568             }
5569             if (!verbose) {             /* No listing so skip detail */
5570                 znext(name);
5571                 continue;
5572             }
5573         }
5574         if (summary) {                  /* Summary only, no detail */
5575             znext(name);
5576             continue;
5577         }
5578
5579 #ifndef NOSPL
5580         if (array) {
5581             debug(F111,"domydir array",name,nfiles);
5582             if (ap)
5583               makestr(&(ap[nmatches]),name);
5584             znext(name);
5585             continue;
5586         }
5587 #endif /* NOSPL */
5588
5589 /*
5590   NOTE: The sprintf's in this routine should be safe.  They involve
5591   permission strings, date/time strings, and filenames, all of which have
5592   known maximum lengths; none of these items is input from users.  The
5593   destination buffers are large enough to hold maximum sizes for any and
5594   all items.  NOTE 2: If command was TOUCH, dstr was already set just
5595   above.
5596 */
5597         if (!dstr) {                    /* Get file's modification date/time */
5598             dstr = zfcdat(name);
5599             debug(F111,"domydir zcfdat",dstr,0);
5600         }
5601         if (!dstr) dstr = "";
5602         {
5603 /*
5604   Note that zfcdat() always returns "" or yyyymmdd hh:mm:ss, so any warnings
5605   about possible out-of-bounds dstr[] array refs do not apply.  This block of
5606   code is to stifle the warnings and also allows for any out-of-spec
5607   zfcdat() implementations.
5608 */
5609             int x;
5610             char * p = "00000000 00:00:00";
5611             x = ckstrncpy(xbuf,dstr,32);
5612             if (x < 17) ckstrncpy(&xbuf[x],p+x,32-x);
5613             dstr = xbuf;
5614         }
5615         if (engdate) {                  /* English date requested? */
5616             short month, day, year, hour, minute, seconds;
5617             month = (dstr[4]-48)*10 + (dstr[5]-48);
5618             mstr  = (month > 0 && month <= 12) ? months[month-1] : "xxx";
5619             day   = (dstr[6]-48)*10 + (dstr[7]-48);
5620             year  = (((dstr[0]-48)*10 +
5621                       (dstr[1]-48))*10 +
5622                       (dstr[2]-48))*10 +
5623                       (dstr[3]-48);
5624             hour  = (dstr[9]-48)*10 + (dstr[10]-48);
5625             minute = (dstr[12]-48)*10 + (dstr[13]-48);
5626             seconds = (dstr[15]-48)*10 + (dstr[16]-48);
5627             sprintf(dbuf,               /* SAFE */
5628                     "%2d-%s-%4d %02d:%02d:%02d",
5629                     day,mstr,year,hour,minute,seconds
5630                     );
5631             dstr = dbuf;
5632         } else {                        /* ISO date */
5633             dbuf[0] = dstr[0];          /* yyyy */
5634             dbuf[1] = dstr[1];
5635             dbuf[2] = dstr[2];
5636             dbuf[3] = dstr[3];
5637             dbuf[4] = '-';
5638             dbuf[5] = dstr[4];          /* mm (numeric) */
5639             dbuf[6] = dstr[5];
5640             dbuf[7] = '-';
5641             dbuf[8] = dstr[6];          /* dd */
5642             dbuf[9] = dstr[7];
5643             strcpy(dbuf+10,dstr+8);     /* hh:mm:ss */
5644             dstr = dbuf;
5645         }
5646         dlen = strlen(dbuf);            /* Length of date */
5647         name[CKMAXPATH] = NUL;
5648 #ifdef CK_PERMS
5649 #ifdef VMSORUNIX
5650         p = ziperm(name);               /* Get permissions */
5651         debug(F110,"ziperm perms",p,0);
5652 #else
5653         p = zgperm(name);
5654         debug(F110,"zgperm perms",p,0);
5655 #endif /* VMSORUNIX */
5656 #else
5657         p = NULL;
5658         debug(F110,"NULL perms",p,0);
5659 #endif /* CK_PERMS */
5660
5661 #ifdef VMS
5662         /* Get relative name to save space -- VMS fullnames are long... */
5663         ckstrncpy(name,zrelname(name,cdp),CKMAXPATH);
5664 #endif /* VMS */
5665
5666         if (itsadir && len < (CK_OFF_T)0) { /* Directory */
5667 #ifdef VMS
5668             sprintf(linebuf,"%-22s%-10s  %s  %s",p,"<DIR>",dstr,name);
5669 #else
5670             if (p)
5671               sprintf(linebuf,"%10s%-10s  %s  %s",p,"<DIR>",dstr,name);
5672             else
5673               sprintf(linebuf,"%-10s  %s  %s", "<DIR>", dstr, name);
5674 #endif /* VMS */
5675         } else {                        /* Regular file */
5676 #ifdef VMS
5677             sprintf(linebuf,"%-22s%10s  %s  %s", p, ckfstoa(len), dstr, name);
5678 #else
5679             if (p)
5680               sprintf(linebuf,"%10s%10s  %s  %s", p, ckfstoa(len), dstr, name);
5681             else
5682               sprintf(linebuf,"%10s  %s  %s", ckfstoa(len), dstr, name);
5683 #endif /* VMS */
5684         }
5685 #ifdef UNIX
5686 #ifdef CKSYMLINK
5687         if (zgfs_link) {                /* If it's a symlink */
5688             if (dontshowlinks) {        /* If /NOLINKS don't show it */
5689                 znext(name);
5690                 continue;
5691             }
5692         }
5693         if (zgfs_link && !dontfollowlinks) { /* Symlink and following links */
5694             int n, m;                   /* Show what the link points to */
5695             extern char linkname[];
5696             n = strlen(linebuf);
5697             m = strlen(linkname) + n;
5698             if (m < CKMAXPATH + 58)
5699               strcpy(linebuf+n, " -> "); /* safe (checked) */
5700             if (m + 4 < CKMAXPATH - 58)
5701               strcpy(linebuf+n+4, linkname); /* safe (checked) */
5702         } else
5703 #endif /* CKSYMLINK */
5704 #endif /* UNIX */
5705         if (xfermod) {                  /* Show transfer mode */
5706             int i, x, y;
5707             char * s = "";
5708             y = -1;
5709             x = scanfile(name,&y,nscanfile);
5710             switch (x) {
5711               case FT_TEXT: s = " (T)"; break;
5712               case FT_7BIT: s = " (T)(7BIT)"; break;
5713               case FT_8BIT: s = " (T)(8BIT)"; break;
5714 #ifdef UNICODE
5715               case FT_UTF8: s = " (T)(UTF8)"; break;
5716               case FT_UCS2:
5717                 s = y ? " (T)(UCS2LE)" : " (T)(UCS2BE)";
5718                 break;
5719 #endif /* UNICODE */
5720               case FT_BIN:  s = " (B)"; break;
5721             }
5722             if (!*s) {
5723                 s = binary ? " (B)" : " (T)";
5724             }
5725             if (*s) {
5726                 int n;
5727                 n = strlen(linebuf);
5728                 if (n + 4 < CKMAXPATH - 58)
5729                   strcpy(linebuf+n, s); /* safe (checked) */
5730             }
5731         }
5732         if (msg && dirmsg) {
5733             int n;
5734             n = strlen(linebuf);
5735             if (n + dirmsglen + 2 < CKMAXPATH)
5736               sprintf((char *)(linebuf+n)," %s", dirmsg); /* SAFE */
5737         }
5738         if (xsort) {                    /* Sorting - save line */
5739             i = strlen(linebuf);
5740             if ((ndirlist >= nx) ||
5741                 !(dirlist[ndirlist] = (char *)malloc(i+1))) {
5742                 printf("?Memory allocation error - try /NOSORT\n");
5743                 rc = -9;
5744                 goto xdomydir;
5745             }
5746             strcpy(dirlist[ndirlist],linebuf); /* safe */
5747             ndirlist++;
5748         }
5749         znext(name);                    /* Peek ahead to next file */
5750         if (!xsort) {
5751             if (!touch || (touch && verbose))
5752               fprintf(ofp,"%s\n",linebuf);
5753             if (page && (name[0] || heading)) { /* If /PAGE */
5754                 if (cmd_cols > 0) {
5755                     int x = strlen(linebuf);
5756                     int y;
5757                     y = (x % cmd_cols) ? 1 : 0;
5758                     n += x / cmd_cols + y;
5759                 } else {
5760                     n++;
5761                 }
5762 #ifdef CK_TTGWSIZ
5763                 if (n > (cmd_rows - 3)) { /* Do more-prompting */
5764                     if (!askmore()) {
5765                         rc = 0;
5766                         goto xdomydir;
5767                     } else
5768                       n = 0;
5769                 }
5770 #endif /* CK_TTGWSIZ */
5771             }
5772         }
5773     }
5774 #ifndef NOSPL
5775     if (array) {
5776         if (ap)
5777           makestr(&(ap[0]),ckitoa(nmatches));
5778         rc = 1;
5779         goto xdomydir;
5780     }
5781 #endif /* NOSPL */
5782     if (xsort) {
5783         int namepos;
5784         skey = 0;
5785 #ifdef VMS
5786         namepos = dlen + 35;
5787         switch (sortby) {
5788           case DIRS_NM: skey = namepos; break;
5789           case DIRS_DT: skey = 33; break;
5790           case DIRS_SZ: skey = 21;
5791         }
5792 #else
5793         if (p) {
5794             namepos = dlen + 24;
5795             switch (sortby) {
5796               case DIRS_NM: skey = namepos; break;
5797               case DIRS_DT: skey = 22; break;
5798               case DIRS_SZ: skey = 10;
5799             }
5800         } else {
5801             namepos = dlen + 14;
5802             switch (sortby) {
5803               case DIRS_NM: skey = namepos; break;
5804               case DIRS_DT: skey = 12; break;
5805               case DIRS_SZ: skey = 0;
5806             }
5807         }
5808 #endif /* VMS */
5809         sh_sort(dirlist,NULL,ndirlist,skey,reverse,filecase);
5810         if (dir_top > 0 && dir_top < ndirlist)
5811           ndirlist = dir_top;
5812         for (i = 0; i < ndirlist; i++) {
5813             fprintf(ofp,"%s\n",dirlist[i]);
5814             if (page && (i < ndirlist -1 || heading)) { /* If /PAGE */
5815                 if (cmd_cols > 0) {
5816                     int x = strlen(dirlist[i]);
5817                     int y;
5818                     y = (x % cmd_cols) ? 1 : 0;
5819                     n += ((int)strlen(dirlist[i]) / cmd_cols) + y;
5820                 } else {
5821                     n++;
5822                 }
5823 #ifdef CK_TTGWSIZ
5824                 if (n > (cmd_rows - 3)) { /* Do more-prompting */
5825                     if (!askmore()) {
5826                         rc = 0;
5827                         goto xdomydir;
5828                     } else
5829                       n = 0;
5830                 }
5831 #endif /* CK_TTGWSIZ */
5832             }
5833         }
5834     }
5835
5836     if (heading || summary) {
5837 #ifdef CKFLOAT
5838         CKFLOAT gm;
5839 #endif /* CKFLOAT */
5840         fprintf(ofp,"\n%ld director%s, %ld file%s, %s byte%s",
5841                ndirs,
5842                (ndirs == 1) ? "y" : "ies",
5843                nfiles,
5844                (nfiles == 1) ? "" : "s",
5845                ckfstoa(nbytes),
5846                (nbytes == 1) ? "" : "s"
5847                );
5848 #ifdef CKFLOAT
5849         gm = ((CKFLOAT) nbytes ) / 1000000.0;
5850         if (gm > 1000.0)
5851           fprintf(ofp," (%0.2fGB)",(gm / 1000.0));
5852         else if (gm >= 0.01)
5853           fprintf(ofp," (%0.2fMB)",gm);
5854 #endif /* CKFLOAD */
5855         fprintf(ofp,"\n\n");
5856     } else if (dir_cou && !cv) {
5857         fprintf(ofp,"\n Files: %ld\n\n",nfiles);
5858     }
5859   xdomydir:
5860 #ifndef NOSPL
5861     if (dir_cou && cv) {                /* /COUNT:var */
5862         addmac(cv,ckitoa(nfiles));      /* set the variable */
5863         makestr(&cv,NULL);              /* free this */
5864     }
5865     if (ap) {                           /* If we have a result array */
5866         if (a_dim[arrayindex] > nmatches) /* but it was not filled */
5867           a_dim[arrayindex] = nmatches;   /* adjust dimension */
5868     }
5869 #endif  /* NOSPL */
5870     if (g_matchdot > -1) {
5871         matchdot = g_matchdot;          /* Restore these... */
5872         g_matchdot = -1;
5873     }
5874     freedirlist();
5875     if (ofp != stdout) {                /* Close any output file */
5876         if (ofp) fclose(ofp);
5877         ofp = stdout;
5878     }
5879     if (rc > 0)
5880       success = 1;
5881     return(rc);
5882 }
5883
5884 int
5885 dodir(cx) int cx; {                     /* Do the DIRECTORY command */
5886     char *dc , *msg;
5887
5888 #ifdef OS2
5889     return(domydir(cx));
5890 #else /* OS2 */
5891     if (nopush
5892 #ifdef DOMYDIR                          /* Builds that domydir() by default */
5893         || (cx == XXDIR  || cx == XXLDIR || cx == XXWDIR ||
5894             cx == XXHDIR || cx == XXTOUC)
5895 #endif /* DOMYDIR */
5896         )
5897       return(domydir(cx));              /* Built-in directory command */
5898
5899     /* Use the system's directory command. */
5900
5901     msg = (cx == XXLS) ?
5902       "Arguments for ls" :
5903         "Directory and/or file specification";
5904     if ((x = cmtxt(msg,"",&s,xxstring)) < 0)
5905       return(x);
5906
5907     ckstrncpy(tmpbuf,s,TMPBUFSIZ);      /* Copy the filespec */
5908     s = tmpbuf;
5909
5910     if ((y = cmcfm()) < 0) return(y);
5911
5912     lp = line;
5913     if (!(dc = getenv("CK_DIR")))
5914       dc = DIRCMD;
5915     ckmakmsg(lp,LINBUFSIZ,dc," ",s,NULL);
5916     debug(F110,"DIR",line,0);
5917 #ifdef VMS
5918     conres();
5919 #endif /* VMS */
5920     x = zshcmd(line);
5921 #ifdef VMS
5922     concb((char)escape);
5923 #endif /* VMS */
5924     return(success = (x < 1) ? 0 : 1);
5925 #endif /* OS2 */
5926 }
5927
5928 #ifndef NOSERVER
5929 #ifndef NOFRILLS
5930 /* Do the ENABLE and DISABLE commands */
5931
5932 int
5933 doenable(y,x) int y, x; {
5934 #ifdef CK_LOGIN
5935     if (isguest)                        /* IKSD: Don't let guests */
5936       return(0);                        /* enable anything that's disabled */
5937 #endif /* CK_LOGIN */
5938     switch (x) {
5939       case EN_ALL:
5940         en_cwd = en_cpy = en_del = en_dir = en_fin = en_get = y;
5941         en_ren = en_sen = en_set = en_spa = en_typ = en_ret = y;
5942         if (!inserver)
5943           en_who = en_mai = en_pri = y;
5944         en_mkd = en_rmd = y;
5945         en_xit = y;
5946 #ifndef datageneral
5947         en_bye = y;
5948 #endif /* datageneral */
5949 #ifndef NOPUSH
5950         if (!nopush && !inserver)
5951           en_hos = y;
5952 #endif /* NOPUSH */
5953 #ifndef NOSPL
5954         en_asg = en_que = y;
5955 #endif /* NOSPL */
5956         break;
5957
5958       case EN_BYE:
5959 #ifndef datageneral
5960 /*
5961   In Data General AOS/VS Kermit can't log out its superior process.
5962 */
5963         en_bye = y;
5964 #endif /* datageneral */
5965         break;
5966       case EN_CPY:
5967         en_cpy = y;
5968         break;
5969       case EN_CWD:
5970         en_cwd = y;
5971 #ifdef IKSD
5972         if (inserver && y == 0) {
5973             fnrpath = PATH_OFF;
5974             fnspath = PATH_OFF;
5975         }
5976 #endif /* IKSD */
5977         break;
5978       case EN_DEL:                      /* Deleting of files */
5979         en_del = y;
5980         break;
5981       case EN_DIR:
5982         en_dir = y;
5983         break;
5984       case EN_FIN:
5985         en_fin = y;
5986         break;
5987       case EN_GET:
5988         en_get = y;
5989         break;
5990 #ifndef NOPUSH
5991       case EN_HOS:
5992         if (!nopush)
5993          en_hos = y;
5994         break;
5995 #endif /* NOPUSH */
5996       case EN_REN:
5997         en_ren = y;
5998         break;
5999       case EN_SEN:
6000         en_sen = y;
6001         break;
6002       case EN_SET:
6003         en_set = y;
6004         break;
6005       case EN_SPA:
6006         en_spa = y;
6007         break;
6008       case EN_TYP:
6009         en_typ = y;
6010         break;
6011       case EN_WHO:
6012         en_who = y;
6013         break;
6014 #ifndef NOSPL
6015       case EN_ASG:
6016         en_asg = y;
6017         break;
6018       case EN_QUE:
6019         en_que = y;
6020         break;
6021 #endif /* NOSPL */
6022       case EN_RET:
6023         en_del = y;
6024         break;
6025       case EN_MAI:
6026 #ifdef CK_LOGIN
6027         if (isguest && y) {
6028             printf("?Sorry, not valid for guests\n");
6029             return(-9);
6030         }
6031 #endif /* CK_LOGIN */
6032         en_mai = y;
6033         break;
6034       case EN_PRI:
6035 #ifdef CK_LOGIN
6036         if (isguest && y) {
6037             printf("?Sorry, not valid for guests\n");
6038             return(-9);
6039         }
6040 #endif /* CK_LOGIN */
6041         en_pri = y;
6042         break;
6043       case EN_MKD:
6044         en_mkd = y;
6045         break;
6046       case EN_RMD:
6047         en_rmd = y;
6048         break;
6049       case EN_XIT:
6050         en_xit = y;
6051         break;
6052       case EN_ENA:
6053         if (((y & 1) && !(en_ena & 1)) ||
6054             ((y & 2) && !(en_ena & 2))) {
6055             printf("?Sorry, DISABLE ENABLE can not be undone\n");
6056             return(-9);
6057         } else {
6058             en_ena = y;
6059             break;
6060         }
6061       default:
6062         return(-2);
6063     }
6064     return(1);
6065 }
6066 #endif /* NOFRILLS */
6067 #endif /* NOSERVER */
6068
6069 #ifndef NOFRILLS
6070
6071 static int del_lis = 0;
6072 static int del_dot = 0;
6073 static int del_hdg = 0;
6074 static int del_pag = -1;
6075 static int del_ask = 0;
6076
6077 #ifndef NOSHOW
6078 VOID
6079 showdelopts() {
6080     int x = 0;
6081     extern int optlines;
6082     prtopt(&optlines,"");
6083     prtopt(&optlines,"DELETE");
6084     if (del_ask > -1) {
6085         prtopt(&optlines, del_ask ? "/ASK" : "/NOASK");
6086         x++;
6087     }
6088 #ifdef UNIXOROSK
6089     if (del_dot > -1) {
6090         prtopt(&optlines, del_dot ? "/DOTFILES" : "/NODOTFILES");
6091         x++;
6092     }
6093 #endif /* UNIXOROSK */
6094     if (del_lis > -1) {
6095         prtopt(&optlines, del_lis ? "/LIST" : "/NOLIST");
6096         x++;
6097     }
6098     if (del_hdg > -1) {
6099         prtopt(&optlines, del_hdg ? "/HEADING" : "/NOHEADING");
6100         x++;
6101     }
6102 #ifndef CK_TTGWSIZ
6103     if (del_pag > -1) {
6104         prtopt(&optlines, del_pag ? "/PAGE" : "/NOPAGE");
6105         x++;
6106     }
6107 #endif /* CK_TTGWSIZ */
6108     if (!x) prtopt(&optlines,"(no options set)");
6109     prtopt(&optlines,"");
6110 }
6111 #endif /* NOSHOW */
6112
6113
6114 int
6115 setdelopts() {
6116     int x_lis = -1, x_pag = -1, x_dot = -1, x_hdg = -1, x_ask = -1;
6117     int getval = 0;
6118     char c;
6119     while (1) {
6120         if ((y = cmswi(deltab,ndeltab,"Switch","",xxstring)) < 0) {
6121             if (y == -3)
6122               break;
6123             else
6124               return(y);
6125         }
6126         c = cmgbrk();
6127         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
6128             printf("?This switch does not take an argument\n");
6129             return(-9);
6130         }
6131         if (!getval && (cmgkwflgs() & CM_ARG)) {
6132             printf("?This switch requires an argument\n");
6133             return(-9);
6134         }
6135         switch (y) {
6136           case DEL_DOT:
6137             x_dot = 1;
6138             break;
6139           case DEL_NOD:
6140             x_dot = 0;
6141             break;
6142           case DEL_HDG:
6143             x_hdg = 1;
6144             break;
6145           case DEL_LIS:
6146             x_lis = 1;
6147             break;
6148           case DEL_NOL:
6149             x_lis = 0;
6150             break;
6151 #ifndef CK_TTGWSIZ
6152           case DEL_PAG:
6153             x_pag = 1;
6154             break;
6155           case DEL_NOP:
6156             x_pag = 0;
6157             break;
6158 #endif /* CK_TTGWSIZ */
6159           case DEL_QUI:
6160             x_lis = 0;
6161             break;
6162           case DEL_VRB:
6163             x_lis = 1;
6164             break;
6165           case DEL_ASK:
6166             x_ask = 1;
6167             break;
6168           case DEL_NAS:
6169             x_ask = 0;
6170             break;
6171           default:
6172             printf("?Sorry, this option can not be set\n");
6173             return(-9);
6174         }
6175     }
6176     if ((x = cmcfm()) < 0)              /* Get confirmation */
6177       return(x);
6178     if (x_pag > -1) del_pag = x_pag;
6179     if (x_dot > -1) del_dot = x_dot;
6180     if (x_hdg > -1) del_hdg = x_hdg;
6181     if (x_lis > -1) del_lis = x_lis;
6182     if (x_ask > -1) del_ask = x_ask;
6183     return(success = 1);
6184 }
6185
6186 #ifdef OS2
6187 static char ** xmtchs = NULL;
6188 static int xmtchn = 0;
6189 #endif /* OS2 */
6190
6191 int
6192 dodel() {                               /* DELETE */
6193     int i, j, k, x;
6194     int fs = 0;                         /* Need to call fileselect() */
6195     int len = 0;
6196     int bad = 0;
6197     int getval = 0, asking = 0;
6198     int simulate = 0, rc = 0;
6199     CK_OFF_T minsize = -1L, maxsize = -1L;
6200     int havename = 0, confirmed = 0;
6201     int qflag = 0;
6202     int summary = 0;
6203     int deldirs = 0;
6204     int deltree = 0;
6205     int itsadir = 0;
6206     int argisdir = 0;
6207     int xmode = -1, scan = 0, skip = 0;
6208 #ifdef COMMENT
6209     int pass = 0;
6210 #endif /* COMMENT */
6211     char c;
6212     char * deldef = "";
6213     char safebuf[CKMAXPATH+1];
6214     struct FDB sw, fi, fl;
6215     char
6216       * del_aft = NULL,
6217       * del_bef = NULL,
6218       * del_naf = NULL,
6219       * del_nbf = NULL,
6220       * del_exc = NULL;
6221     int
6222       x_lis = 0,
6223       /* x_dot = -1, */
6224       x_hdg = 0;
6225
6226     char * dxlist[8];
6227
6228     for (i = 0; i < 8; i++) dxlist[i] = NULL;
6229
6230     g_matchdot = matchdot;
6231
6232     if (del_lis > -1) x_lis    = del_lis;
6233     if (del_dot > -1) matchdot = del_dot;
6234     if (del_hdg > -1) x_hdg    = del_hdg;
6235     if (del_pag > -1) xaskmore = del_pag;
6236     if (del_ask > -1) asking   = del_ask;
6237
6238     diractive = 1;
6239     nolinks = 2;                        /* By default don't follow links */
6240
6241     cmfdbi(&sw,                         /* First FDB - command switches */
6242            _CMKEY,                      /* fcode */
6243            "File specification;\n or switch",
6244            "",                          /* default */
6245            "",                          /* addtl string data */
6246            ndeltab,                     /* addtl numeric data 1: tbl size */
6247            4,                           /* addtl numeric data 2: 4 = cmswi */
6248            xxstring,                    /* Processing function */
6249            deltab,                      /* Keyword table */
6250            &fi                          /* Pointer to next FDB */
6251            );
6252     cmfdbi(&fl,                         /* Anything that doesn't match */
6253            _CMFLD,                      /* fcode */
6254            "",                          /* hlpmsg */
6255            "",                          /* default */
6256            "",                          /* addtl string data */
6257            0,                           /* addtl numeric data 1 */
6258            0,                           /* addtl numeric data 2 */
6259            xxstring,
6260            NULL,
6261            NULL
6262            );
6263   again:
6264     cmfdbi(&fi,                         /* 2nd FDB - file to delete */
6265            _CMIFI,                      /* fcode */
6266            "File(s) to delete",         /* hlpmsg */
6267            deldef,                      /* default */
6268            "",                          /* addtl string data */
6269            nolinks | deldirs,           /* 0 = files, 1 = files or dirs */
6270            0,                           /* 1 = dirs only */
6271            xxstring,
6272            NULL,
6273            &fl
6274            );
6275     while (!havename && !confirmed) {
6276         x = cmfdb(&sw);                 /* Parse something */
6277         if (x < 0) {                    /* Error */
6278             if (x == -3)
6279               break;
6280             if (x == -2 || x == -9)
6281               printf("?Does not match switch or filename: \"%s\"\n",atmbuf);
6282             return(x);
6283         }
6284         if (cmresult.fcode != _CMKEY)   /* Break out if not a switch */
6285           break;
6286         c = cmgbrk();                   /* Get break character */
6287         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
6288             printf("?This switch does not take an argument\n");
6289             rc = -9;
6290             goto xdelete;
6291         }
6292         if (!getval && (cmgkwflgs() & CM_ARG)) {
6293             printf("?This switch requires an argument\n");
6294             rc = -9;
6295             goto xdelete;
6296         }
6297         switch (k = cmresult.nresult) {
6298           case DEL_AFT:
6299           case DEL_BEF:
6300           case DEL_NAF:
6301           case DEL_NBF:
6302             if (!getval) break;
6303             if ((x = cmdate("File-time","",&s,0,xxstring)) < 0) {
6304                 if (x == -3) {
6305                     printf("?Date-time required\n");
6306                     x = -9;
6307                 } else
6308                   rc = x;
6309                 goto xdelete;
6310             }
6311             fs++;
6312             deltree = 0;
6313             switch (k) {
6314               case DEL_AFT: makestr(&del_aft,s); break;
6315               case DEL_BEF: makestr(&del_bef,s); break;
6316               case DEL_NAF: makestr(&del_naf,s); break;
6317               case DEL_NBF: makestr(&del_nbf,s); break;
6318             }
6319             break;
6320           case DEL_DOT:
6321             matchdot = 1;
6322             break;
6323           case DEL_NOD:
6324             matchdot = 0;
6325             break;
6326           case DEL_ALL:
6327             fs = 0;
6328 #ifdef VMS
6329             deldef = "*.*";             /* UNIX, Windows, OS/2 */
6330 #else
6331 #ifdef datageneral
6332             deldef = "+";               /* AOS/VS */
6333 #else
6334             deldef = "*";               /* UNIX, Windows, OS/2, VMS... */
6335 #endif /* datageneral */
6336 #endif /* VMS */
6337             deltree = 1;
6338             nolinks = 2;
6339             matchdot = 1;
6340             recursive = 1;              /* Fall through purposely... */
6341           case DEL_DIR:
6342             deldirs = 1;
6343             goto again;
6344           case DEL_EXC:
6345             if (!getval) break;
6346             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
6347                 if (x == -3) {
6348                     printf("?Pattern required\n");
6349                     x = -9;
6350                 } else
6351                   rc = x;
6352                 goto xdelete;
6353             }
6354             fs++;
6355             deltree = 0;
6356             makestr(&del_exc,s);
6357             break;
6358           case DEL_HDG:
6359             x_hdg = 1;
6360             break;
6361 #ifdef RECURSIVE
6362           case DEL_REC:
6363             recursive = 1;
6364             break;
6365 #endif /* RECURSIVE */
6366           case DEL_LIS:
6367             x_lis = 1;
6368             break;
6369           case DEL_SUM:
6370             summary = 1;
6371             x_lis = 0;
6372             x_hdg = 1;
6373             break;
6374           case DEL_NOL:
6375             x_lis = 0;
6376             break;
6377 #ifndef CK_TTGWSIZ
6378           case DEL_PAG:
6379             xaskmore = 1;
6380             break;
6381           case DEL_NOP:
6382             xaskmore = 0;
6383             break;
6384 #endif /* CK_TTGWSIZ */
6385           case DEL_QUI:
6386             qflag = 1;
6387             x_lis = 0;
6388             break;
6389           case DEL_VRB:
6390             x_lis = 1;
6391             break;
6392
6393           case DEL_SMA:
6394           case DEL_LAR:
6395             if (!getval) break;
6396             if ((x = cmnum("File size in bytes","0",10,&y,xxstring)) < 0)
6397               return(x);
6398             fs++;
6399             deltree = 0;
6400             switch (cmresult.nresult) {
6401               case DEL_SMA: minsize = y; break;
6402               case DEL_LAR: maxsize = y; break;
6403             }
6404             break;
6405
6406           case DEL_SIM:
6407             simulate = 1;
6408             x_lis = 1;
6409             break;
6410           case DEL_ASK:
6411             asking = 1;
6412             break;
6413           case DEL_NAS:
6414             asking = 0;
6415             break;
6416           case DEL_TYP: {
6417               extern struct keytab txtbin[];
6418               if (!getval) break;
6419               if ((x = cmkey(txtbin,3,"","",xxstring)) < 0)
6420                 return(x);
6421               if (x == 2) {             /* ALL */
6422                   xmode = -1;
6423               } else {                  /* TEXT or BINARY only */
6424                   xmode = x;
6425                   scan = 1;
6426               }
6427               break;
6428           }
6429           default:
6430             printf("?Not implemented yet - \"%s\"\n",atmbuf);
6431             return(-9);
6432         }
6433     }
6434     if (qflag && (cmresult.fcode == _CMFLD)) {
6435         if ((x = cmcfm()) < 0)
6436           return(x);
6437         else
6438           return(success = 0);
6439     }
6440     if (cmresult.fcode != _CMIFI) {
6441         if (*atmbuf) {
6442             int x;
6443             if (iswild(atmbuf) && nzxpand(atmbuf,nzxopts) == 0)
6444               printf("?No files match: %s\n",brstrip(atmbuf));
6445             else if ((x = zchki(atmbuf)) == -1)
6446               printf("?File not found: %s\n",brstrip(atmbuf));
6447             else if (x == -2)
6448               printf("?Not a regular file: %s\n",atmbuf);
6449             else
6450               /* printf("?Not a deletable file: %s\n",atmbuf); */
6451               goto tryanyway;
6452         } else {
6453             printf("?A file specification is required\n");
6454         }
6455         return(-9);
6456     }
6457   tryanyway:
6458     ckstrncpy(tmpbuf,cmresult.sresult,TMPBUFSIZ); /* Safe copy of filespec */
6459     if (deldirs) {
6460         ckstrncpy(safebuf,cmresult.sresult,CKMAXPATH);
6461 #ifdef VMSORUNIX
6462         len = zgetfs(tmpbuf);           /* Is it a directory name? */
6463         argisdir = zgfs_dir;            /* Then because of how zxpand() */
6464         if (argisdir && zgfs_link)      /* works, we have to add it to */
6465           argisdir = 0;                 /* the list. */
6466         if (itsadir)
6467           len = -2;
6468 #else
6469         len = zchki(tmpbuf);
6470         if (len < 0)
6471           argisdir = isdir(tmpbuf);
6472 #endif /* VMSORUNIX */
6473     }
6474     debug(F110,"DELETE file",tmpbuf,0);
6475     if ((x = cmcfm()) < 0)
6476       return(x);
6477
6478 #ifdef IKSD
6479 #ifdef CK_LOGIN
6480     if (inserver && isguest) {
6481         printf("?Sorry, DELETE unavailable to guests\n");
6482         return(-9);
6483     }
6484 #endif /* CK_LOGIN */
6485 #endif /* IKSD */
6486
6487 #ifndef OS2ORUNIX
6488     if (simulate) {
6489         printf("?Sorry, /SIMULATE not implemented on this platform\n");
6490         return(-9);
6491     }
6492 #endif /* OS2ORUNIX */
6493
6494 #ifdef COMMENT
6495     /* (not needed) */
6496     if (!iswild(tmpbuf)) {
6497         char *m;
6498         errno = 0;
6499         x = zchki(tmpbuf);
6500         if (x < 0) {
6501             switch (x) {
6502               case -2: m = "Not a regular file"; break;
6503               case -1: m = "File not found or not accessible"; break;
6504               default: m = errno ? ck_errstr() : "Can't delete";
6505             }
6506             printf("?%s: \"%s\"\n",m,tmpbuf);
6507             return(-9);
6508         }
6509     }
6510 #endif /* COMMENT */
6511
6512     makelist(del_exc,dxlist,8);
6513
6514 /* tmpbuf[] has the name - now do any needed conversions on it */
6515
6516 #ifdef OS2
6517     {   /* Lower level functions change / to \, not good for CMD.EXE. */
6518         char *p = tmpbuf;
6519         while (*p) {                    /* Change them back to \ */
6520             if (*p == '/') *p = '\\';
6521             p++;
6522         }
6523     }
6524 #endif /* OS2 */
6525
6526 #ifdef VMS
6527     if (iswild(tmpbuf)) {
6528 #ifdef COMMENT
6529         /* Does not handle '.' as version separator */
6530         char *p = tmpbuf;
6531         x = 0;
6532         while (*p) {
6533             if (*p == ';') {
6534                 x = 1;
6535                 break;
6536             } else
6537               p++;
6538         }
6539         if (!x) ckstrncat(tmpbuf,";*",TMPBUFSIZ);
6540 #else
6541         j = 0; x = 0;                   /* for end_dot and number of dots */
6542         i = strlen(tmpbuf);
6543         if (tmpbuf[i] == ';') {
6544             ckstrncat(tmpbuf,"0",TMPBUFSIZ);
6545         } else {
6546             if (tmpbuf[i--] == '.')
6547               j++;
6548             for (; i >= 0; i--) {
6549                 if (tmpbuf[i] == ';' || tmpbuf[i] == ':' ||
6550                     tmpbuf[i] == ']' || tmpbuf[i] == '>')
6551                   break;
6552                 else if (tmpbuf[i] == '.')
6553                   x++;
6554             }
6555             if (tmpbuf[i] != ';') {     /* dot may have been used */
6556                 if (j) {                /* last char is dot */
6557                   if (x)                /* second is version separator */
6558                     ckstrncat(tmpbuf,"0",TMPBUFSIZ);
6559                   else                  /* 'foo.' */
6560                     ckstrncat(tmpbuf,";0",TMPBUFSIZ);
6561                 } else if (x == 1)      /* lacking a version separator */
6562                   ckstrncat(tmpbuf,";0",TMPBUFSIZ);
6563                 else if (x == 0)        /* x == 2 has a version */
6564                   ckstrncat(tmpbuf,".*;0",TMPBUFSIZ);
6565             }
6566         }
6567 #endif /* COMMENT */
6568     }
6569 #endif /* VMS */
6570
6571     debug(F110,"dodel tmpbuf",tmpbuf,0); /* Filename */
6572
6573 #ifndef OS2ORUNIX                       /* No built-in DELETE code... */
6574     /* Construct system command. */
6575     ckmakmsg(line,LINBUFSIZ,DELCMD," ",tmpbuf,NULL);
6576 #else
6577 #ifdef VMS
6578     if (asking) {                       /* Maybe overwrite in VMS */
6579         if (x_lis)                      /* if options are needed... */
6580           ckmakmsg(line,LINBUFSIZ,DELCMD," /confirm/log ",tmpbuf,NULL);
6581         else
6582           ckmakmsg(line,LINBUFSIZ,DELCMD," /confirm ",tmpbuf,NULL);
6583     } else if (x_lis)
6584       ckmakmsg(line,LINBUFSIZ,DELCMD," /log ",tmpbuf,NULL);
6585     conres();
6586 #endif /* VMS */
6587
6588     debug(F110,"dodel line",line,0);
6589 #endif /* OS2ORUNIX */
6590
6591 #ifdef MAC
6592     success = (zdelet(tmpbuf) == 0);
6593
6594 #else  /* !MAC ... */
6595
6596 #ifdef OS2ORUNIX
6597     {
6598         int filespace = 0;
6599         int count = 0;
6600         int lines = 0;
6601         int n = 0;
6602
6603         s = tmpbuf;
6604
6605 #ifdef CK_TTGWSIZ
6606 #ifdef OS2
6607         ttgcwsz();
6608 #else /* OS2 */
6609         /* Check whether window size changed */
6610         if (ttgwsiz() > 0) {
6611             if (tt_rows > 0 && tt_cols > 0) {
6612                 cmd_rows = tt_rows;
6613                 cmd_cols = tt_cols;
6614             }
6615         }
6616 #endif /* OS2 */
6617 #endif /* CK_TTGWSIZ */
6618
6619         if (x_hdg > 0 && !summary) {
6620             printf("Deleting %s...%s\n", s, simulate ? " (SIMULATION)" : "");
6621             n += 2;
6622         }
6623 #ifdef ZXREWIND
6624         z = zxrewind();                 /* Rewind file list */
6625 #else
6626         if (!deldirs)
6627           nzxopts = ZX_FILONLY;
6628         if (recursive) nzxopts |= ZX_RECURSE;
6629         if (matchdot)  nzxopts |= ZX_MATCHDOT;
6630         errno = 0;
6631         z = nzxpand(s,nzxopts);         /* Expand file list */
6632 #endif /* ZXREWIND */
6633         debug(F111,"dodel",s,z);
6634
6635         /* If deleting directories, sort in reverse order */
6636         /* so we delete the files first, then the directory. */
6637
6638 #ifdef OS2
6639         /* In K95, we have no mtchs array, nor any control over */
6640         /* the order in which znext() returns filenames, so we  */
6641         /* must copy the array and sort it. */
6642         {
6643             int i;
6644             if (xmtchs) {               /* Free previous list in case */
6645                 debug(F101,"dodel freeing previous list","",xmtchn);
6646                 for (i = 0; i < xmtchn; i++) /* it wasn't freed last time. */
6647                   if (xmtchs[i])
6648                     free(xmtchs[i]);
6649                 free(xmtchs);
6650             }
6651             xmtchn = 0;
6652             xmtchs = (char **)malloc(z * (sizeof(char **))); /* Make new one */
6653             if (!xmtchs) {
6654                 printf("?Memory allocation failure\n");
6655                 return(-9);
6656             }
6657             for (i = 0; i < z; i++) {
6658                 xmtchs[i] = NULL;
6659                 znext(tmpbuf);
6660                 if (!*tmpbuf)
6661                   break;
6662                 makestr(&(xmtchs[i]),tmpbuf);
6663                 if (!xmtchs[i]) {
6664                     printf("?Memory allocation failure\n");
6665                     xmtchn = i - 1;
6666                     rc = -9;
6667                     goto xdelete;
6668                 }
6669                 /* debug(F111,"dodel add",xmtchs[i],i); */
6670             }
6671             xmtchn = i;
6672             debug(F101,"dodel xmtchn","",xmtchn);
6673             sh_sort(xmtchs,NULL,z,0,deldirs,0);
6674         }
6675 #else
6676 #ifdef UNIX
6677         sh_sort(mtchs,NULL,z,0,deldirs,filecase);
6678 #endif /* UNIX */
6679 #endif /* OS2 */
6680
6681         if (z > 0) {
6682             int i;
6683 #ifdef OS2
6684             int ix = 0;
6685 #endif /* OS2 */
6686             success = 1;
6687             if (x_hdg > 0)
6688               printf("\n");
6689
6690             while (
6691 #ifdef OS2
6692                    ix < xmtchn
6693 #else
6694                    1
6695 #endif /* OS2 */
6696                    ) {                  /* Loop for all files */
6697 #ifdef OS2
6698                 ckstrncpy(tmpbuf,xmtchs[ix++],TMPBUFSIZ);
6699 #else
6700                 znext(tmpbuf);          /* Get next file */
6701 #endif /* OS2 */
6702                 if (!*tmpbuf) {         /* No more */
6703                     if (deldirs && recursive && argisdir) {
6704                         ckstrncpy(tmpbuf,safebuf,TMPBUFSIZ);
6705                         argisdir = 0;   /* (only do this once) */
6706                     } else
6707                       break;
6708                 }
6709                 skip = 0;
6710                 if (!deltree) {
6711                     if (fs)
6712                       if (fileselect(tmpbuf,
6713                                      del_aft,del_bef,del_naf,del_nbf,
6714                                      minsize,maxsize,0,8,dxlist) < 1) {
6715                           skip++;
6716                       }
6717                 }
6718                 if (!skip && scan && itsadir) {
6719                     skip++;
6720                 }
6721                 if (!skip && scan) {
6722                       switch (scanfile(tmpbuf,&y,nscanfile)) {
6723                       case FT_BIN:
6724                         if (xmode != 1)
6725                           skip++;
6726                         break;
6727                       case FT_TEXT:
6728                       case FT_7BIT:
6729                       case FT_8BIT:
6730 #ifdef UNICODE
6731                       case FT_UTF8:
6732                       case FT_UCS2:
6733 #endif /* UNICODE */
6734                         if (xmode != 0)
6735                           skip++;
6736                     }
6737                 }
6738                 if (!skip && asking) {
6739                     int x;
6740                     ckmakmsg(line,LINBUFSIZ," Delete ",tmpbuf,"? ",NULL);
6741                     x = getyesno(line,3);
6742                     switch (x) {
6743                       case 0: continue;           /* no */
6744                       case 1: break;              /* yes */
6745                       case 2: goto xdelete;       /* quit */
6746                       case 3: asking = 0; break;  /* go */
6747                     }
6748                 }
6749 #ifdef VMSORUNIX
6750                 len = zgetfs(tmpbuf);   /* Get length and accessibility */
6751                 itsadir = zgfs_dir;
6752                 if (itsadir && zgfs_link) { /* Treat links to directories */
6753                     itsadir = 0;        /* as regular files */
6754                     if (scan)           /* But not if /TYPE: was given */
6755                       skip++;
6756                 }
6757                 if (itsadir)            /* (emulate non-Unix code) */
6758                   len = -2;
6759 #else
6760                 len = zchki(tmpbuf);    /* Get accessibility */
6761                 if (len < 0)            /* See if it's a directory */
6762                   itsadir = isdir(tmpbuf);
6763 #endif /* VMSORUNIX */
6764
6765                 if (skip) {
6766 #ifdef COMMENT                          /* Too verbose */
6767                     if (x_lis > 0) {
6768                         lines++;
6769                         printf(" %s (SKIPPED)\n",tmpbuf);
6770 #ifdef CK_TTGWSIZ
6771                         if (++n > cmd_rows - 3)
6772                           if (!askmore()) goto xdelete; else n = 0;
6773 #endif /* CK_TTGWSIZ */
6774                     }
6775 #endif /* COMMENT */
6776                     continue;
6777                 }
6778
6779                 debug(F111,"DELETE len",tmpbuf,len);
6780                 if (simulate) {
6781                     filespace += len;
6782                     count++;
6783                     if (x_lis > 0) {
6784                         lines++;
6785                         printf(" %s (SELECTED)\n",tmpbuf);
6786                         if (++n > cmd_rows - 3) {
6787                             int xx;
6788                             xx = askmore();
6789                             if (!xx) goto xdelete; else n = 0;
6790                         }
6791                     }
6792                 } else if (len >= 0 || !itsadir) { /* Regular file */
6793                     zdelet(tmpbuf);                /* or symlink, etc... */
6794                     if (zchki(tmpbuf) < 0) {
6795                         filespace += len;
6796                         count++;
6797                         if (x_lis > 0) {
6798                             lines++;
6799                             printf(" %s (OK)\n",tmpbuf);
6800                             if (++n > cmd_rows - 3)
6801                               if (!askmore()) goto xdelete; else n = 0;
6802                         }
6803                     } else {
6804                         bad++;
6805                         success = 0;
6806                         if (x_lis > 0) {
6807                             lines++;
6808                             printf(" %s (FAILED: %s)\n",tmpbuf,ck_errstr());
6809                             if (++n > cmd_rows - 3)
6810                               if (!askmore()) goto xdelete; else n = 0;
6811                         }
6812                     }
6813                 } else if (/* pass > 0 && */ deldirs && itsadir) {
6814                     /* It's a directory */
6815                     if (zrmdir(tmpbuf) > -1) { /* Only works if empty */
6816                         count++;
6817                         if (x_lis > 0) {
6818                             lines++;
6819                             printf(" %s (OK)\n",tmpbuf);
6820                             if (++n > cmd_rows - 3)
6821                               if (!askmore()) goto xdelete; else n = 0;
6822                         }
6823                     } else {
6824                         success = 0;
6825                         if (x_lis > 0) {
6826                             lines++;
6827                             printf(" %s (FAILED: %s)\n",
6828                                    tmpbuf,
6829                                    ck_errstr());
6830                             if (++n > cmd_rows - 3)
6831                               if (!askmore()) goto xdelete; else n = 0;
6832                         }
6833                     }
6834                 } else if (x_lis > 0) {
6835                     lines++;
6836                     if (isdir(tmpbuf))
6837                       printf(" %s (FAILED: directory)\n",tmpbuf);
6838                     else
6839                       printf(" %s (FAILED: not a regular file)\n",tmpbuf);
6840                     if (++n > cmd_rows - 3)
6841                       if (!askmore()) goto xdelete; else n = 0;
6842                 }
6843             }
6844             if (x_hdg > 0) {
6845                 if (lines > 0)
6846                   printf("\n");
6847                 if (++n > cmd_rows - 3)
6848                   if (!askmore()) goto xdelete; else n = 0;
6849                 printf("%d file%s %sdeleted, %d byte%s %sfreed%s\n",
6850                        count,
6851                        count != 1 ? "s" : "",
6852                        simulate ? "would be " : "",
6853                        filespace,
6854                        filespace != 1 ? "s" : "",
6855                        simulate ? "would be " : "",
6856                        simulate ? " (maybe)" : ""
6857                        );
6858             }
6859             if (!x_lis && !success && !quiet) {
6860                 printf("?DELETE failed for %d file%s \
6861 (use DELETE /LIST to see details)\n",
6862                        bad, bad == 1 ? "" : "s"
6863                        );
6864             }
6865         } else if (x_lis > 0) {
6866             if (errno)
6867               printf("?%s: %s\n",ck_errstr(), tmpbuf);
6868             else
6869               printf("?Can't delete: %s\n",tmpbuf);
6870         }
6871     }
6872 #else /* OS2ORUNIX */
6873 #ifndef VMS                             /* Others - let the system do it. */
6874     xsystem(line);
6875     x = nzxpand(tmpbuf,nzxopts);
6876     success = (x > 0) ? 0 : 1;
6877     if (x_hdg > 0)
6878       printf("%s - %sdeleted\n", tmpbuf, success ? "" : "not ");
6879 #else
6880     if (asking)
6881       printf("\n");
6882     x = xsystem(line);                  /* zshcmd returns 1 for success */
6883     success = (x > 0) ? 1 : 0;
6884     if (x_hdg > 0 && !asking)
6885       printf("%s - %sdeleted\n", tmpbuf, success ? "" : "not ");
6886     concb((char)escape);
6887 #endif /* VMS */
6888 #endif /* OS2ORUNIX */
6889 #endif /* MAC */
6890   xdelete:
6891     if (g_matchdot > -1) {
6892         matchdot = g_matchdot;          /* Restore these... */
6893         g_matchdot = -1;
6894     }
6895 #ifdef OS2
6896     if (xmtchs) {
6897         int i;
6898         debug(F101,"dodel freeing list","",xmtchn);
6899         for (i = 0; i < xmtchn; i++)
6900           if (xmtchs[i]) free(xmtchs[i]);
6901         free(xmtchs);
6902         xmtchs = NULL;
6903         xmtchn = 0;
6904     }
6905 #endif /* OS2 */
6906     debug(F101,"dodel result","",rc);
6907     return((rc < 0) ? rc : success);
6908 }
6909 #endif /* NOFRILLS */
6910
6911 #ifndef NOSPL                           /* The ELSE command */
6912 _PROTOTYP( VOID pushqcmd, (char *) );
6913
6914 int
6915 doelse() {
6916     if (!ifcmd[cmdlvl]) {
6917         printf("?ELSE doesn't follow IF\n");
6918         return(-2);
6919     }
6920 #ifdef COMMENT
6921 /*
6922   Wrong.  This prevents IF..ELSE IF...ELSE IF...ELSE IF...ELSE...
6923   from working.
6924 */
6925     ifcmd[cmdlvl] = 0;
6926 #endif /* COMMENT */
6927     if (!iftest[cmdlvl]) {              /* If IF was false do ELSE part */
6928         if (maclvl > -1 || tlevel > -1) { /* In macro or command file */
6929             debug(F100,"doelse pushing","",0);
6930 #ifndef COMMENT
6931             pushcmd(NULL);              /* save rest of command. */
6932 #else
6933             /* This fixes certain obscure problems */
6934             /* but breaks many other constructions that must work. */
6935             pushqcmd(NULL);
6936 #endif /* COMMENT */
6937         } else {                        /* If interactive, */
6938             cmini(ckxech);              /* just start a new command */
6939             printf("\n");               /* (like in MS-DOS Kermit) */
6940             if (pflag) prompt(xxstring);
6941         }
6942     } else {                            /* Condition is false */
6943         if ((y = cmtxt("command to be ignored","",&s,NULL)) < 0)
6944           return(y);                    /* Gobble up rest of line */
6945     }
6946     return(0);
6947 }
6948 #endif /* NOSPL */
6949
6950 #ifndef NOSPL
6951 int
6952 doswitch() {
6953     char *lp, *ap;                      /* Macro argument pointer */
6954     int len = 0, x, y, pp = 0;
6955     char brbuf[3];
6956
6957     /* Get variable name */
6958
6959     tmpbuf[0] = NUL;
6960     brbuf[0] = '{';
6961     brbuf[1] = '}';
6962     brbuf[2] = NUL;
6963
6964     y = cmfld("Variable name","",&s,xxstring);
6965     debug(F111,"doswitch cmfld",s,y);
6966     if (y < 0) {
6967         if (y == -3)                    /* Because brstrip() writes */
6968           s = brbuf;                    /* into its argument. */
6969         else
6970           return(y);
6971     }
6972     debug(F110,"doswitch A",s,0);
6973     if (!strcmp(s,"(")) {
6974         pp++;
6975         if ((y = cmfld("Variable name","",&s,xxstring)) < 0) {
6976             if (y == -3)
6977               s = brbuf;
6978             else
6979               return(y);
6980             debug(F110,"doswitch B",s,0);
6981         }
6982     }
6983     len = ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ);
6984     if (tmpbuf[0] == CMDQ) {
6985         if (chkvar(s) < 1) {
6986             printf("?Variable name required\n");
6987             return(-9);
6988         }
6989     }
6990     if (pp > 0) {                       /* If open paren given parse closing */
6991         if ((y = cmfld("Closing parenthesis","",&s,NULL)) < 0)
6992           return(y);
6993         if (strcmp(atmbuf,")")) {
6994             printf("?Closing parenthesis required\n");
6995             return(-9);
6996         }
6997     }
6998     lp = line;
6999     x = ckstrncpy(lp,"_switx ",LINBUFSIZ); /* _switx + space */
7000     lp += x;
7001     ap = lp;
7002     debug(F010,"SWITCH a",line,0);
7003
7004 #ifdef COMMENT
7005     x = ckmakmsg(lp,LINBUFSIZ-x,tmpbuf," ",NULL,NULL); /* variable name + SP */
7006 #else
7007     {                                   /* variable name + SP */
7008         char * p = tmpbuf;
7009         if (len > 0) {
7010             if (tmpbuf[0] == '(' && tmpbuf[len-1] == ')') {
7011                 tmpbuf[len-1] = NUL;
7012                 p++;
7013             }
7014         }
7015         x = ckmakmsg(lp,LINBUFSIZ-x,"{",brstrip(p),"}"," ");
7016     }
7017 #endif /* COMMENT */
7018     debug(F010,"SWITCH b",line,0);
7019     lp += x;
7020
7021     /* Get body */
7022
7023     if ((y = cmtxt("series of cases","",&s,NULL)) < 0) return(y);
7024     if ((y = (int)strlen(s)) < 1) return(-2);
7025     if (s[0] != '{' && s[y-1] != '}') { /* Supply braces if missing */
7026         ckmakmsg(tmpbuf,TMPBUFSIZ,"{ ",s," }",NULL);
7027         s = tmpbuf;
7028     }
7029     if (litcmd(&s,&lp,(LINBUFSIZ - (lp - (char *)line) - 2)) < 0) {
7030         printf("?Unbalanced braces\n");
7031         return(0);
7032     }
7033     debug(F010,"SWITCH c",line,0);
7034
7035     x = mlook(mactab,"_switx",nmac);    /* Look up SWITCH macro definition */
7036     if (x < 0) {                        /* Not there? */
7037         addmmac("_switx",sw_def);       /* Put it back. */
7038         if ((x = mlook(mactab,"_switx",nmac)) < 0) { /* Look it up again. */
7039             printf("?SWITCH macro definition gone!\n"); /* Shouldn't happen. */
7040             return(success = 0);
7041         }
7042     }
7043     debug(F010,"SWITCH command",line,0); /* Execute the SWITCH macro. */
7044     success = dodo(x,ap,cmdstk[cmdlvl].ccflgs | CF_IMAC);
7045     debug(F101,"SWITCH status","",success);
7046     return(success);
7047 }
7048
7049 int
7050 dofor() {                               /* The FOR command. */
7051     int i, fx, fy, fz;                  /* loop variables */
7052     char *ap, *di;                      /* macro argument pointer */
7053     int pp = 0;                         /* Paren level */
7054     int mustquote = 0;
7055
7056     for (i = 0; i < 2; i++) {
7057         if ((y = cmfld("Variable name","",&s,NULL)) < 0) {
7058             if (y == -3) {
7059                 printf("?Variable name required\n");
7060                 return(-9);
7061             } else
7062               return(y);
7063         }
7064         if (strcmp(s,"("))
7065           break;
7066         pp++;
7067     }
7068 #ifdef COMMENT
7069     if ((y = parsevar(s,&x,&z)) < 0)    /* Check variable. */
7070       return(y);
7071 #else
7072     if (*s == CMDQ)                     /* If loop variable starts with */
7073       mustquote++;                      /* backslash, mustquote is > 0. */
7074 #endif /* COMMENT */
7075
7076     lp = line;                          /* Build a copy of the command */
7077     ckstrncpy(lp,"_forx ",LINBUFSIZ);
7078     lp += (int)strlen(line);            /* "_for" macro. */
7079     ap = lp;                            /* Save pointer to macro args. */
7080
7081     if (*s == CMDQ) s++;                /* Skip past backslash if any. */
7082     while ((*lp++ = *s++)) ;            /* copy it */
7083     lp--; *lp++ = SP;                   /* add a space */
7084
7085     if ((y = cmnum("initial value","",10,&fx,xxstring)) < 0) {
7086         if (y == -3) return(-2);
7087         else return(y);
7088     }
7089     debug(F101,"dofor fx","",fx);
7090     s = atmbuf;                         /* Copy the atom buffer */
7091
7092     if ((int)strlen(s) < 1) goto badfor;
7093 /*
7094   In edit 192, we change the loop variables to be evaluated at loop entry,
7095   not each time through the loop.  This was required in order to allow
7096   \v(argc) to be used as a loop variable, or in a loop-variable expression.
7097   Thus, we can't have FOR loops that modify their own exit conditions by
7098   changing the final value or the increment.  The problem with \v(argc) was
7099   that it is on the macro stack; after entry into the _forx macro, it is at
7100   the wrong place.
7101 */
7102     sprintf(tmpbuf,"%d",fx);            /* (SAFE) Substitute actual value */
7103     s = tmpbuf;
7104     while ((*lp++ = *s++)) ;            /* (what they actually typed) */
7105     lp--; *lp++ = SP;
7106 #ifdef DEBUG
7107     *lp = NUL;
7108     debug(F110,"FOR A",line,0);
7109 #endif /* DEBUG */
7110
7111     if ((y = cmnum("final value","",10,&fy,xxstring)) < 0) {
7112         if (y == -3) return(-2);
7113         else return(y);
7114     }
7115     debug(F101,"dofor fy","",fy);
7116     s = atmbuf;                         /* Same deal */
7117     if ((int)strlen(s) < 1)
7118       goto badfor;
7119
7120     sprintf(tmpbuf,"%d",fy);            /* SAFE */
7121     s = tmpbuf;
7122     while ((*lp++ = *s++)) ;
7123     lp--;
7124     *lp++ = SP;
7125 #ifdef DEBUG
7126     *lp = NUL;
7127     debug(F110,"FOR B",line,0);
7128 #endif /* DEBUG */
7129
7130     x_ifnum = 1;                        /* Increment or parenthesis */
7131     di = (fx < fy) ? "1" : "-1";        /* Default increment */
7132     if ((y = cmnum("increment",di,10,&fz,xxstring)) < 0) {
7133         debug(F111,"dofor increment",atmbuf,y);
7134         x_ifnum = 0;
7135         if (y == -3) {                  /* Premature termination */
7136             return(-2);
7137         } else if (y == -2) {           /* Maybe closing paren */
7138             if (!strcmp(atmbuf,")")) {
7139                 pp--;                   /* Count it */
7140                 s = di;                 /* supply default interval */
7141                 fz = atoi(s);
7142             } else                      /* Not closing paren, invalid */
7143               return(y);
7144         } else                          /* Other error */
7145           return(y);
7146     } else {                            /* Number */
7147         x_ifnum = 0;
7148         debug(F101,"dofor fz","",fz);
7149         s = atmbuf;                     /* Use it */
7150     }
7151     if ((int)strlen(s) < 1)
7152       goto badfor;
7153
7154     sprintf(tmpbuf,"%d",fz);            /* (SAFE) Same deal */
7155     s = tmpbuf;
7156     while ((*lp++ = *s++)) ;
7157     lp--; *lp++ = SP;
7158
7159 #ifdef DEBUG
7160     *lp = NUL;
7161     debug(F110,"FOR C",line,0);
7162 #endif /* DEBUG */
7163
7164     /* Insert the appropriate comparison operator */
7165     if (fz < 0)
7166       *lp++ = '<';
7167     else
7168       *lp++ = '>';
7169     *lp++ = SP;
7170
7171 #ifdef DEBUG
7172     *lp = NUL;
7173     debug(F110,"FOR D",line,0);
7174 #endif /* DEBUG */
7175
7176     if (pp > 0) {                       /* If open paren given parse closing */
7177         if ((y = cmfld("Closing parenthesis","",&s,NULL)) < 0)
7178           return(y);
7179         if (strcmp(atmbuf,")")) {
7180             printf("?Closing parenthesis required\n");
7181             return(-9);
7182         }
7183     }
7184     if ((y = cmtxt("Command to execute","",&s,NULL)) < 0) return(y);
7185     if ((y = (int)strlen(s)) < 1) return(-2);
7186     if (s[0] != '{' && s[y-1] != '}') { /* Supply braces if missing */
7187         ckmakmsg(tmpbuf,TMPBUFSIZ,"{ ",s," }",NULL);
7188         s = tmpbuf;
7189     }
7190     if (litcmd(&s,&lp,(LINBUFSIZ - (lp - (char *)line) - 2)) < 0) {
7191         printf("?Unbalanced braces\n");
7192         return(0);
7193     }
7194 #ifdef DEBUG
7195     *lp = NUL;
7196     debug(F110,"FOR E",line,0);
7197 #endif /* DEBUG */
7198
7199 #ifdef COMMENT
7200 /* Too strict */
7201     if (fz == 0) {
7202         printf("?Zero increment not allowed\n");
7203         return(0);
7204     }
7205 #endif /* COMMENT */
7206 /*
7207   In version 8.0 we decided to allow macro names anyplace a numeric-valed
7208   variable could appear.  But this caused trouble for the FOR loops because
7209   the quoting in for_def[] assumed a \%i-style loop variable.  We account
7210   for this here in the if (mustquote)...else logic by invoking separate
7211   FOR macro definitions in the two cases.
7212 */
7213     if (mustquote) {                    /* \%i-style loop variable */
7214         x = mlook(mactab,"_forx",nmac); /* Look up FOR macro definition */
7215         if (x < 0) {                    /* Not there? */
7216             addmmac("_forx",for_def);   /* Put it back. */
7217             if ((x = mlook(mactab,"_forx",nmac)) < 0) { /* Look it up again. */
7218                 printf("?FOR macro definition gone!\n");
7219                 return(success = 0);
7220             }
7221         }
7222     } else {                            /* Loop variable is a macro */
7223         x = mlook(mactab,"_forz",nmac);
7224         if (x < 0) {
7225             addmmac("_forz",foz_def);
7226             if ((x = mlook(mactab,"_forz",nmac)) < 0) {
7227                 printf("?FOR macro definition gone!\n");
7228                 return(success = 0);
7229             }
7230         }
7231     }
7232     debug(F010,"FOR command",line,0);   /* Execute the FOR macro. */
7233     return(success = dodo(x,ap,cmdstk[cmdlvl].ccflgs | CF_IMAC));
7234
7235 badfor:
7236     printf("?Incomplete FOR command\n");
7237     return(-2);
7238 }
7239 #endif /* NOSPL */
7240
7241
7242 #ifndef NOSPL
7243
7244 /*  T O D 2 S E C  --  Convert time of day as hh:mm:ss to secs since midnite */
7245 /*
7246   Call with a string hh:mm or hh:mm:ss.
7247   Returns a 0 to 86400 on success, or a negative number on failure.
7248 */
7249 long
7250 tod2sec(t) char * t; {
7251     long t2;
7252     long hh = 0L, mm = 0L, ss = 0L;
7253
7254     if (!t) t = "";
7255     if (!*t)
7256       return(-3L);
7257     debug(F110,"tod2sec",t,0);
7258
7259     if (isdigit(*t))                    /* Get hours from argument */
7260       hh = *t++ - '0';
7261     else
7262       return(-1L);
7263     if (isdigit(*t))
7264       hh = hh * 10 + *t++ - '0';
7265 #ifdef COMMENT
7266     if (hh > 24L)
7267       return(-1L);
7268 #endif /* COMMENT */
7269     if (*t == ':')
7270       t++;
7271     else if (!*t)
7272       goto xtod2sec;
7273     else
7274       return(-1L);
7275
7276     if (isdigit(*t))                    /* Minutes */
7277       mm = *t++ - '0';
7278     else
7279       return(-1L);
7280     if (isdigit(*t))
7281       mm = mm * 10 + *t++ - '0';
7282     if (mm > 60L)
7283       return(-1L);
7284     if (*t == ':')
7285       t++;
7286     else if (!*t)
7287       goto xtod2sec;
7288     else
7289       return(-1L);
7290
7291     if (isdigit(*t))                    /* Seconds */
7292       ss = *t++ - '0';
7293     else
7294       return(-1L);
7295     if (isdigit(*t))
7296       ss = ss * 10 + *t++ - '0';
7297     if (ss > 60L)
7298       return(-1L);
7299
7300     if (*t > 32)                        /* No trailing junk allowed */
7301       return(-1L);
7302
7303   xtod2sec:
7304
7305     t2 = hh * 3600L + mm * 60L + ss;    /* Seconds since midnight from arg */
7306     debug(F101,"tod2sec t2","",t2);
7307
7308     return(t2);
7309 }
7310
7311 int waitinterval = 1;
7312
7313 #ifdef OLDWAIT
7314 #undef OLDWAIT
7315 #endif /* OLDWAIT */
7316
7317 int kbchar = NUL;
7318
7319 int
7320 dopaus(cx) int cx; {
7321     long zz;
7322     extern int sleepcan;
7323
7324 #ifdef OLDWAIT
7325     zz = -1L;
7326     x_ifnum = 1;                        /* Turn off internal complaints */
7327     if (cx == XXWAI)
7328       y = cmnum("seconds to wait, or time of day hh:mm:ss","1",10,&x,xxstring);
7329     else if (cx == XXPAU)
7330       y = cmnum("seconds to pause, or time of day hh:mm:ss",
7331                 "1",10,&x,xxstring);
7332     else
7333       y = cmnum("milliseconds to sleep, or time of day hh:mm:ss",
7334                 "100",10,&x,xxstring);
7335     x_ifnum = 0;
7336     if (y < 0) {
7337         if (y == -2) {                  /* Invalid number or expression */
7338             char *p = tmpbuf;           /* Retrieve string from atmbuf */
7339             int n = TMPBUFSIZ;
7340             *p = NUL;
7341             zzstring(atmbuf,&p,&n);     /* Evaluate in case it's a variable */
7342             zz = tod2sec(tmpbuf);       /* Convert to secs since midnight */
7343             if (zz < 0L) {
7344                 printf("?Number, expression, or time of day required\n");
7345                 return(-9);
7346             } else {
7347                 char now[32];           /* Current time */
7348                 char *p;
7349                 long tnow;
7350                 p = now;
7351                 ztime(&p);
7352                 tnow = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
7353                 if (zz < tnow)          /* User's time before now */
7354                   zz += 86400L;         /* So make it tomorrow */
7355                 zz -= tnow;             /* Seconds from now. */
7356             }
7357         } else
7358           return(y);
7359     }
7360     if (x < 0) x = 0;
7361     switch (cx) {
7362       case XXPAU:                       /* PAUSE */
7363       case XXMSL:                       /* MSLEEP */
7364         if ((y = cmcfm()) < 0) return(y);
7365         break;
7366       case XXWAI:                       /* WAIT */
7367         z = 0;                          /* Modem signal mask */
7368         while (1) {                     /* Read zero or more signal names */
7369             y = cmkey(mstab,nms,"modem signal","",xxstring);
7370             if (y == -3) break;         /* -3 means they typed CR */
7371             if (y < 0) return(y);       /* Other negatives are errors */
7372             z |= y;                     /* OR the bit into the signal mask */
7373         }
7374         if ((y = cmcfm()) < 0) return(y);
7375         break;
7376
7377       default:                          /* Shouldn't happen */
7378         return(-2);
7379     }
7380
7381 /* Command is entered, now do it. */
7382
7383     if (zz > -1L) {                     /* Time of day given? */
7384         x = zz;
7385         if (zz != (long) x) {
7386             printf(
7387 "Sorry, arithmetic overflow - hh:mm:ss not usable on this platform.\n"
7388                    );
7389             return(-9);
7390         }
7391     }
7392     if (cx == XXMSL) {                  /* Millisecond sleep */
7393         msleep(zz < 0 ? x : x * 1000);
7394         return(success = 1);
7395     }
7396     if (cx == XXPAU && !sleepcan) {     /* SLEEP CANCELLATION is OFF */
7397         sleep(x);
7398         return(success = 1);
7399     }
7400
7401     /* WAIT, or else SLEEP with cancellation allowed... */
7402
7403     do {                                /* Sleep loop */
7404         int mdmsig;
7405         if (sleepcan) {                 /* Keyboard cancellation allowed? */
7406             if (y = conchk()) {         /* Did they type something? */
7407 #ifdef COMMENT
7408                 while (y--) coninc(0);  /* Yes, gobble it all up */
7409 #else
7410                 /* There is a debate over whether PAUSE should absorb    */
7411                 /* its cancelling character(s).  There are several       */
7412                 /* reasons why it should gobble at least one character:  */
7413                 /* (1) MS-DOS Kermit does it                             */
7414                 /* (2) if not, subsequent PAUSE commands will terminate  */
7415                 /*     immediately                                       */
7416                 /* (3) if not, subsequent ASK commands will use it as    */
7417                 /*     valid input.  If \13, then it will get no input   */
7418                 /* (4) if not, then the character appears on the command */
7419                 /*     line after all enclosing macros are complete.     */
7420                 kbchar = coninc(0);     /* Gobble one up */
7421 #endif /* COMMENT */
7422                 break;                  /* And quit PAUSing or WAITing */
7423             }
7424         }
7425         if (cx == XXWAI) {              /* WAIT (z == modem signal mask) */
7426             debug(F101,"WAIT x","",x);
7427             if (z > 0) {                /* Looking for any modem signals? */
7428                 mdmsig = ttgmdm();      /* Yes, get them */
7429                 if (mdmsig < 0)         /* Failed */
7430                   return(success = 0);
7431                 if ((mdmsig & z) == z)  /* Got what we wanted? */
7432                   return(success = 1);  /* Succeed */
7433             }
7434             if (x == 0)                 /* WAIT 0 and didn't get our signals */
7435               break;
7436         }
7437         sleep(1);                       /* No interrupt, sleep one second */
7438     } while (--x > 0);
7439
7440     if (cx == XXWAI)                    /* If WAIT and loop exhausted */
7441       success = (z == 0);               /* Fail. */
7442     else                                /*  */
7443       success = (x == 0);               /* Set SUCCESS/FAILURE for PAUSE. */
7444     return(success);
7445
7446 #else  /* New code uses chained FDBs and allows FILE waits... */
7447
7448     char * m = "";                      /* Help message */
7449     struct FDB nu, fl;                  /* Parse function descriptor blocks */
7450     int filewait = 0;
7451     int mdmsig = 0, fs = 0;
7452     char filedate[32];
7453
7454     kbchar = 0;
7455
7456     switch (cx) {
7457       case XXWAI: m = "seconds to wait, or time of day hh:mm:ss"; break;
7458       case XXPAU: m = "seconds to pause, or time of day hh:mm:ss"; break;
7459       case XXMSL: m = "milliseconds to sleep, or time of day hh:mm:ss"; break;
7460     }
7461     zz = -1L;
7462     cmfdbi(&nu,
7463            _CMNUM,                      /* Number */
7464            m,                           /* Help message */
7465            (cx == XXMSL) ? "100" : "1", /* Default */
7466            "",                          /* N/A */
7467            0,                           /* N/A */
7468            0,                           /* N/A */
7469            xxstring,                    /* Processing function */
7470            NULL,                        /* N/A */
7471            &fl                          /* Next */
7472            );
7473     cmfdbi(&fl,                         /* Time of day */
7474            _CMFLD,                      /* Field */
7475            "",                          /* hlpmsg */
7476            "",                          /* default */
7477            "",                          /* addtl string data */
7478            0,                           /* addtl numeric data 1 */
7479            0,                           /* addtl numeric data 2 */
7480            xxstring,                    /* processing func */
7481            NULL,                        /* N/A */
7482            NULL                         /* No next */
7483            );
7484     x = cmfdb(&nu);                     /* Parse a number or a field */
7485     if (x < 0) {
7486         if (x == -3)
7487           x = -2;
7488         return(x);
7489     }
7490     switch (cmresult.fcode) {
7491       case _CMNUM:                      /* Number */
7492         x = cmresult.nresult;
7493         break;
7494       case _CMFLD:                      /* Field */
7495         zz = tod2sec(cmresult.sresult); /* Convert to secs since midnight */
7496         if (zz < 0L) {
7497             printf("?Number, expression, or time of day required\n");
7498             return(-9);
7499         } else {
7500             char now[32];               /* Current time */
7501             char *p;
7502             long tnow;
7503             p = now;
7504             ztime(&p);
7505             tnow = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
7506             if (zz < tnow)              /* User's time before now */
7507               zz += 86400L;             /* So make it tomorrow */
7508             zz -= tnow;         /* Seconds from now. */
7509         }
7510     }
7511     debug(F101,"PAUSE/WAIT/MSLEEP zz","",zz);
7512     switch (cx) {
7513       case XXPAU:                       /* PAUSE */
7514       case XXMSL:                       /* MSLEEP */
7515         if ((y = cmcfm()) < 0) return(y);
7516         break;
7517       case XXWAI:                       /* WAIT */
7518         z = 0;                          /* Modem signal mask */
7519         y = cmkey(waittab,nwaittab,"","",xxstring);
7520         if (y < 0) {
7521             if (y == -3) {
7522                 if ((y = cmcfm()) < 0)
7523                   return(y);
7524                 break;
7525             } else
7526               return(y);
7527         }
7528         if (y == WAIT_FIL) {            /* FILE */
7529             int wild = 0;
7530             if ((z = cmkey(wfswi,nwfswi,"event","",xxstring)) < 0)
7531               return(z);
7532             filewait = z;
7533             if (filewait == WF_MOD || filewait == WF_DEL)
7534               z = cmifi("Filename","",&s,&wild,xxstring);
7535             else
7536               z = cmfld("Filename","",&s,xxstring);
7537             if (z < 0)
7538               return(z);
7539             if (wild || ((filewait == WF_CRE) && iswild(s))) {
7540                 printf("?Wildcards not valid here\n");
7541                 return(-9);
7542             }
7543             ckstrncpy(tmpbuf,s,TMPBUFSIZ);
7544             if ((z = cmcfm()) < 0)
7545               return(z);
7546             break;
7547         } else if (y != WAIT_MDM) {     /* A modem signal */
7548             z |= y;                     /* OR the bit into the signal mask */
7549         }
7550         if (!filewait) {                /* Modem signals... */
7551             while (1) {                 /* Get zero or more signal names */
7552                 y = cmkey(mstab,nms,"modem signal","",xxstring);
7553                 if (y == -3) break;     /* -3 means they typed CR */
7554                 if (y < 0) return(y);   /* Other negatives are errors */
7555                 z |= y;                 /* OR the bit into the signal mask */
7556             }
7557             if ((y = cmcfm()) < 0) return(y);
7558             break;
7559         }
7560
7561       default:                          /* Shouldn't happen */
7562         return(-2);
7563     } /* switch (cx) */
7564
7565 /* Command is entered, now do it. */
7566
7567     if (zz > -1L) {                     /* Time of day given? */
7568         x = zz;
7569         if (zz != (long) x) {
7570             printf(
7571 "Sorry, arithmetic overflow - hh:mm:ss not usable on this platform.\n"
7572                    );
7573             return(-9);
7574         }
7575     }
7576     if (sleepcan)
7577       concb((char)escape);              /* Ensure single-char wakeup */
7578
7579     if (cx == XXMSL) {                  /* Millisecond sleep */
7580         msleep(zz < 0 ? x : x * 1000);
7581         return(success = 1);
7582     }
7583     if (cx == XXPAU && !sleepcan) {     /* SLEEP CANCELLATION is OFF */
7584         sleep(x);
7585         return(success = 1);
7586     }
7587     if (filewait) {                     /* FILE... */
7588         fs = zchki(tmpbuf);             /* Check if file exists */
7589         switch (filewait) {
7590           case WF_DEL:
7591             if (fs == -1)
7592               return(success = 1);
7593             break;
7594           case WF_MOD:
7595             if (fs == -1) {
7596                 printf("?File does not exit: %s\n",tmpbuf);
7597                 return(-9);
7598             }
7599             s = zfcdat(tmpbuf);         /* Get current modification date */
7600             if (!s) s = "";
7601             if (ckstrncpy(filedate,s,32) != 17) {
7602                 printf("?Can't get modification time: %s\n",tmpbuf);
7603                 return(-9);
7604             }
7605             break;
7606           case WF_CRE:
7607             if (fs > -1)
7608               return(success = 1);
7609             break;
7610         }
7611     }
7612     do {                                /* Polling loop */
7613         if (sleepcan) {                 /* Keyboard cancellation allowed? */
7614             if ((y = conchk()) > 0) {   /* Did they type something? */
7615                 kbchar = coninc(0);     /* Yes, get first char they typed */
7616                 debug(F000,"WAIT kbchar","",kbchar);
7617 #ifdef COMMENT
7618                 while (--y > 0)         /* Gobble the rest up */
7619                   coninc(0);
7620 #endif /* COMMENT */
7621                 return(success = 0);    /* And quit PAUSing or WAITing */
7622             }
7623         }
7624         if (filewait == 0) {
7625             if (cx == XXWAI) {          /* WAIT for modem signals */
7626                 if (z != 0) {
7627                     mdmsig = ttgmdm();  /* Get them. */
7628                     debug(F101,"WAIT ttgmdm","",mdmsig);
7629                     if (mdmsig < 0)     /* Failure to get them? */
7630                       return(success = 0); /* Fail. */
7631                     if ((mdmsig & z) == z) /* Got desired ones? */
7632                       return(success = 1); /* Succeed. */
7633                 } else if (x == 0)
7634                   return(success = 0);
7635             }
7636         } else {                        /* FILE... */
7637             fs = zchki(tmpbuf);         /* Get file status */
7638             if (filewait == WF_MOD) {   /* Wait for modification */
7639                 if (fs == -1)           /* Failure to get status */
7640                   return(success = 0);  /* so WAIT fails. */
7641                 s = zfcdat(tmpbuf);     /* Get current modification time */
7642                 if (!s) s = "";         /* And compare with the time */
7643                 if (strcmp(s,filedate)) /* when the WAIT started */
7644                   return(success = 1);
7645             } else if (filewait == WF_DEL) { /* Wait for deletion */
7646                 if (fs == -1)           /* If file doesn't exist, */
7647                   return(success = 1);  /* succeed. */
7648             } else if (filewait == WF_CRE) { /* Wait for creation */
7649                 if (fs != -1)           /* If file exists */
7650                   return(success = 1);  /* succeed. */
7651             }
7652         }
7653         if (x < 1)                      /* SLEEP/WAIT/PAUSE 0 */
7654           break;
7655         sleep(waitinterval);            /* No interrupt, sleep */
7656         x -= waitinterval;              /* Deduct sleep time */
7657     } while (x > 0);
7658
7659     if (cx == XXWAI)                    /* WAIT time expired */
7660       success = (z == 0);               /* Succeed if no modem signals */
7661     else                                /* For SLEEP or PAUSE, success */
7662       success = (x == 0);               /* depends on whether it was */
7663     return(success);                    /* interrupted from the keyboard. */
7664 #endif /* OLDWAIT */
7665 }
7666 #endif /* NOSPL */
7667
7668 #ifdef OS2ORUNIX
7669 _PROTOTYP(int zcmpfn,(char *, char *));
7670 #endif /* OS2ORUNIX */
7671
7672 #ifndef NOFRILLS
7673 #ifdef NT
7674 int 
7675 dolink() {
7676     /* Parse a file or a directory name */
7677     int i, x, z, listing = 0, havename = 0, wild = 0, rc = 1;
7678     struct FDB sw, fi;
7679
7680     cmfdbi(&sw,                         /* 2nd FDB - optional /PAGE switch */
7681            _CMKEY,                      /* fcode */
7682            "Filename or switch",        /* hlpmsg */
7683            "",                          /* default */
7684            "",                          /* addtl string data */
7685            nqvswtab,                    /* addtl numeric data 1: tbl size */
7686            4,                           /* addtl numeric data 2: 4 = cmswi */
7687            xxstring,                    /* Processing function */
7688            qvswtab,                     /* Keyword table */
7689            &fi                          /* Pointer to next FDB */
7690            );
7691
7692     cmfdbi(&fi,                         /* 1st FDB - file to type */
7693            _CMIFI,                      /* fcode */
7694            "",                          /* hlpmsg */
7695            "",                          /* default */
7696            "",                          /* addtl string data */
7697            3,                           /* addtl numeric data 1 */
7698            0,                           /* addtl numeric data 2 */
7699            xxstring,
7700            NULL,
7701            NULL
7702            );
7703
7704     while (!havename) {
7705         x = cmfdb(&sw);                 /* Parse something */
7706         if (x < 0)                      /* Error */
7707           return(x);
7708         switch (cmresult.fcode) {
7709           case _CMKEY:
7710             switch (cmresult.nresult) {
7711               case DEL_LIS:
7712               case DEL_VRB:
7713                 listing = 1;
7714                 break;
7715               case DEL_NOL:
7716               case DEL_QUI:
7717                 listing = 0;
7718                 break;
7719             }
7720             break;
7721           case _CMIFI:
7722             s = cmresult.sresult;
7723             havename = 1;
7724             break;
7725           default:
7726             return(-2);
7727         }
7728     }
7729     wild = cmresult.nresult;            /* Source specification wild? */
7730
7731     ckstrncpy(line,s,LINBUFSIZ);        /* Make a safe copy of source name */
7732     s = line;
7733
7734     if (!wild)
7735       wild = iswild(line);
7736
7737     p = tmpbuf;                         /* Place for new name */
7738     if ((x = cmofi(wild ? "Target directory" : "New name",
7739                    "",&s,xxstring)) < 0) { /* Get new name */
7740         if (x == -3) {
7741             printf("?%s required\n", wild ? "Target directory" : "New name");
7742             return(-9);
7743         } else return(x);
7744     }
7745     ckstrncpy(p,s,TMPBUFSIZ);           /* Make a safe copy of the new name */
7746     if ((y = cmcfm()) < 0) return(y);
7747
7748     if (!wild) {                        /* Just one */
7749         if (listing) printf("%s => %s ",line,p);
7750         if (zlink(line,p) < 0) {
7751             if (listing) printf("(FAILED: %s\n",ck_errstr());
7752             rc = 0;
7753         } else {
7754             if (listing) printf("(OK)\n");
7755         }
7756         return(success = rc);
7757     }
7758     if (!isdir(p)) {                    /* Multiple */
7759         printf(                         /* if target is not a directory */
7760 "?Multiple source files not allowed if target is not a directory.\n");
7761         return(-9);
7762     }
7763 #ifdef COMMENT
7764     else {                              /* Show full path of target */
7765         char buf[CKMAXPATH];            /* (too much) */
7766         if (zfnqfp(p,CKMAXPATH,buf))
7767           ckstrncpy(tmpbuf,buf,TMPBUFSIZ);
7768     }
7769 #endif /* COMMENT */
7770
7771 #ifdef VMS
7772     conres();                           /* Let Ctrl-C work. */
7773 #endif /* VMS */
7774     debug(F110,"dolink line",line,0);
7775
7776 #ifdef ZXREWIND
7777     z = zxrewind();                     /* Rewind file list */
7778 #else
7779     z = nzxpand(s,0);                   /* Expand file list */
7780 #endif /* ZXREWIND */
7781     debug(F111,"dolink p",p,z);
7782
7783 #ifdef UNIX
7784     if (wild && z > 1)
7785       sh_sort(mtchs,NULL,z,0,0,filecase); /* Alphabetize the filename list */
7786 #endif /* UNIX */
7787
7788     while (z-- > 0) {
7789         if (!(z == 0 && !wild))
7790           znext(line);
7791         if (!line[0])
7792           break;
7793         if (listing) printf("%s => %s ",line,p);
7794         if (zlink(line,p) < 0) {
7795             if (listing) printf("(FAILED: %s\n",ck_errstr());
7796             rc = 0;
7797         } else {
7798             if (listing) printf("(OK)\n");
7799         }
7800     }
7801 #ifdef VMS
7802     concb((char)escape);
7803 #endif /* VMS */
7804     return(success = rc);
7805 }
7806 #endif /* NT */
7807
7808 #ifdef ZCOPY
7809 int
7810 docopy() {
7811     int i, x, listing = 0, nolist = 0, havename = 0, getval;
7812     char c;
7813     struct FDB sw, fi;
7814     int overwrite = OVW_ALWAYS;
7815     int targetisdir = 0;
7816     int targetlen = 0;
7817     int appending = 0;
7818     int preserve = 0;
7819     int swapping = 0;
7820     int fromb64 = 0;
7821     int tob64 = 0;
7822     int wild = 0;
7823     int rc = 1;
7824
7825     char newname[CKMAXPATH], * nm;
7826     nm = newname;
7827
7828     cmfdbi(&sw,                         /* 1st FDB - switches */
7829            _CMKEY,                      /* fcode */
7830            "Filename or switch",        /* hlpmsg */
7831            "",                          /* default */
7832            "",                          /* addtl string data */
7833            ncopytab,                    /* addtl numeric data 1: tbl size */
7834            4,                           /* addtl numeric data 2: 4 = cmswi */
7835            xxstring,                    /* Processing function */
7836            copytab,                     /* Keyword table */
7837            &fi                          /* Pointer to next FDB */
7838            );
7839     cmfdbi(&fi,                         /* 2nd FDB - file to copy */
7840            _CMIFI,                      /* fcode */
7841            "",                          /* hlpmsg */
7842            "",                          /* default */
7843            "",                          /* addtl string data */
7844            0,                           /* addtl numeric data 1 */
7845            0,                           /* addtl numeric data 2 */
7846            xxstring,
7847            NULL,
7848            NULL
7849            );
7850
7851     while (!havename) {
7852         x = cmfdb(&sw);                 /* Parse something */
7853         if (x < 0)                      /* Error */
7854           return(x);
7855         switch (cmresult.fcode) {
7856           case _CMKEY:
7857             c = cmgbrk();                   /* Get break character */
7858             if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
7859                 printf("?This switch does not take an argument\n");
7860                 rc = -9;
7861                 return(rc);
7862             }
7863             if (!getval && (cmgkwflgs() & CM_ARG)) {
7864                 printf("?This switch requires an argument\n");
7865                 rc = -9;
7866                 return(rc);
7867             }
7868             switch (cmresult.nresult) {
7869               case DEL_LIS:
7870               case DEL_VRB:
7871                 nolist = 0;
7872                 listing = 1;
7873                 break;
7874               case DEL_NOL:
7875               case DEL_QUI:
7876                 nolist = 1;
7877                 listing = 0;
7878                 break;
7879               case 999:
7880                 swapping = 1;
7881                 break;
7882               case 998:
7883                 appending = 1;
7884                 break;
7885               case 995:
7886                 preserve = 1;
7887                 break;
7888               case 994:
7889                 if ((x = cmkey(ovwtab,novwtab,
7890                                "When to overwrite existing destination file",
7891                                "",xxstring)) < 0)
7892                   return(x);
7893                 overwrite = x;
7894                 break;
7895 #ifndef NOSPL
7896               case 997:
7897                 fromb64 = 1;
7898                 break;
7899               case 996:
7900                 tob64 = 1;
7901                 break;
7902 #endif /* NOSPL */
7903             }
7904             break;
7905           case _CMIFI:
7906             s = cmresult.sresult;
7907             havename = 1;
7908             break;
7909           default:
7910             return(-2);
7911         }
7912     }
7913     wild = cmresult.nresult;
7914     ckstrncpy(line,s,LINBUFSIZ);        /* Make a safe copy of source name */
7915     s = line;
7916     p = tmpbuf;                         /* Place for new name */
7917
7918     /* Get destination name */
7919     if ((x = cmofi("destination name and/or directory",
7920 #ifdef UNIX
7921                    "."
7922 #else
7923                    ""
7924 #endif /* UNIX */
7925                    ,&s,xxstring)) < 0) {
7926         if (x == -3) {
7927             printf("?Name for destination file required\n");
7928             return(-9);
7929         } else return(x);
7930     }
7931     ckstrncpy(p,s,TMPBUFSIZ);           /* Safe copy of destination name */
7932     if ((y = cmcfm()) < 0) return(y);
7933     if (appending && swapping) {
7934         printf("?Sorry, /APPEND and /SWAP conflict\n");
7935         return(-9);
7936     }
7937 #ifdef COMMENT
7938 /*
7939   This unreasonably prevented "COPY /APPEND *.* bigfile" from concatenating
7940   a bunch of files into one big file.
7941 */
7942     if (appending && wild) {
7943         printf("?Sorry, /APPEND can be used only with single files\n");
7944         return(-9);
7945     }
7946 #endif /* COMMENT */
7947     targetisdir = isdir(p);
7948     x = strlen(p);
7949     if (targetisdir) {
7950 #ifdef UNIXOROSK
7951         if (p[x-1] != '/') {
7952             ckstrncat(p,"/",TMPBUFSIZ);
7953             x++;
7954         }
7955 #else
7956 #ifdef OS2
7957         if (p[x-1] != '/') {
7958             ckstrncat(p,"/",TMPBUFSIZ);
7959             x++;
7960         }
7961 #else
7962 #ifdef STRATUS
7963         if (p[x-1] != '>') {
7964             ckstrncat(p,">",TMPBUFSIZ);
7965             x++;
7966         }
7967 #else
7968 #ifdef datageneral
7969         if (p[x-1] != ':') {
7970             ckstrncat(p,":",TMPBUFSIZ);
7971             x++;
7972         }
7973 #else
7974         if (p[x-1] != '/') {
7975             ckstrncat(p,"/",TMPBUFSIZ);
7976             x++;
7977         }
7978 #endif /* datageneral */
7979 #endif /* STRATUS */
7980 #endif /* OS2 */
7981 #endif /* UNIXOROSK */
7982     }
7983     targetlen = x;
7984
7985     if (!appending) {                   /* If /APPEND not given */
7986         if (wild && !targetisdir) {     /* No wildcards allowed */
7987             printf(                     /* if target is not a directory */
7988 "?Multiple source files not allowed if target is not a directory.\n");
7989             return(-9);
7990         }
7991     }
7992
7993 #ifdef VMS
7994     conres();                           /* Let Ctrl-C work. */
7995 #endif /* VMS */
7996     debug(F110,"docopy line",line,0);
7997     debug(F110,"docopy p",p,0);
7998     debug(F110,"docopy nm",nm,0);
7999
8000 #ifdef ZXREWIND
8001     z = zxrewind();                     /* Rewind file list */
8002 #else
8003     z = nzxpand(s,0);                   /* Expand file list */
8004 #endif /* ZXREWIND */
8005
8006 #ifdef UNIX
8007     if (wild)
8008       sh_sort(mtchs,NULL,z,0,0,filecase); /* Alphabetize the filename list */
8009 #endif /* UNIX */
8010
8011 #ifdef IKSD
8012     if (!targetisdir && zchki(p) > -1) { /* Destination file exists? */
8013         if (inserver && (!ENABLED(en_del)
8014 #ifdef CK_LOGIN
8015                          || isguest
8016 #endif /* CK_LOGIN */
8017                          )) {
8018             printf("?Sorry, overwriting existing files is disabled\n");
8019             return(-9);
8020         }
8021     }
8022 #endif /* IKSD */
8023
8024     if (tob64 && fromb64) {             /* To and from B64 = no conversion */
8025         tob64 = 0;
8026         fromb64 = 0;
8027     }
8028     debug(F110,"COPY dest",p,0);
8029
8030     while (z > 0) {
8031
8032         znext(line);
8033         if (!line[0])
8034           break;
8035
8036         errno = 0;                      /* Reset errno */
8037
8038         if (targetisdir) {
8039             zstrip(line,&nm);
8040             ckmakmsg(newname,CKMAXPATH,p,nm,NULL,NULL);
8041             nm = newname;
8042         } else {
8043             nm = p;
8044         }
8045         if (overwrite) {                /* Overwrite checking? */
8046             if (zchki(nm) >= (CK_OFF_T)0) { /* Destination file exists? */
8047
8048                 char d1[20], * d2;
8049                 char * n1, * n2;
8050                 int i, skip = 0;
8051
8052                 i = strlen(line);       /* Isolate source filename */
8053                 for (; i >= 0; i--) {
8054                     if (ISDIRSEP(line[i])) {
8055                         n1 = &line[i+1];
8056                         break;
8057                     }
8058                 }
8059                 debug(F110,"COPY n1", n1, 0);
8060                 i = strlen(nm);         /* And destination filename */
8061                 for (; i >= 0; i--) {
8062                     if (ISDIRSEP(nm[i])) {
8063                         n2 = &nm[i+1];
8064                         break;
8065                     }
8066                 }
8067                 debug(F110,"COPY n2", n2, 0);
8068                 if (!strcmp(n1,n2)) {             /* Same name? */
8069                     if (overwrite == OVW_NEVER) { /* Never overwrite? */
8070                         if (listing)              /* Skip */
8071                           if (listing) printf("%s => %s (SKIPPED)\n",line,nm);
8072                         continue;
8073                     }
8074                     ckstrncpy(d1,zfcdat(line),20); /* Source file timestamp */
8075                     d2 = zfcdat(nm);           /* Timestamp of dest file */
8076                     x = strcmp(d1,d2);         /* Compare them */
8077                     if (((overwrite == OVW_NEWER) && (x < 0)) ||
8078                         ((overwrite == OVW_OLDER) && (x > 0))) {
8079                         if (listing)
8080                           if (listing) printf("%s => %s (SKIPPED)\n",line,nm);
8081                         continue;
8082                     }
8083                 }
8084             }
8085         }
8086         if (listing) printf("%s => %s ",line,nm);
8087
8088         /* Straight copy */
8089         if (!swapping && !appending && !fromb64 && !tob64) {
8090             debug(F110,"COPY zcopy",line,0);
8091
8092             if ((x = zcopy(line,p)) < 0) { /* Let zcopy() do it. */
8093                 debug(F111,"COPY not OK",line,x);
8094                 switch (x) {
8095                   case -2:
8096                     if (listing)
8097                       printf("(FAILED: Not a regular file)\n");
8098                     else if (!nolist)
8099                       printf("?Not a regular file - %s\n",line);
8100                     rc = 0;
8101                     break;
8102                   case -3:
8103                     if (listing)
8104                       printf("(FAILED: Not found or not accessible)\n");
8105                     else if (!nolist)
8106                       printf("?Not found or not accessible - %s\n",line);
8107                     rc = 0;
8108                     break;
8109                   case -4:
8110                     if (listing)
8111                       printf("(FAILED: Permission denied)\n");
8112                     else if (!nolist)
8113                       printf("?Permission denied - %s\n",line);
8114                     rc = 0;
8115                     break;
8116                   case -5:
8117                     if (listing)
8118                       printf("(Source and destination are the same file)\n");
8119                     else if (!nolist)
8120                       printf(
8121                           "?Source and destination are the same file - %s\n",
8122                           line
8123                           );
8124                     break;
8125                   case -6:
8126                     if (listing)
8127                       printf("(FAILED: Input/Output error)\n");
8128                     else if (!nolist)
8129                       printf("?Input/Output error - %s\n",line);
8130                     rc = 0;
8131                     break;
8132                   case -7:
8133                     if (listing)
8134                       printf("(FAILED: %s - %s)\n",p,ck_errstr());
8135                     else if (!nolist)
8136                       printf("?%s - %s\n",ck_errstr(),p);
8137                     rc = 0;
8138                     break;
8139                   default:
8140                     if (listing)
8141                       printf("(FAILED: %s)\n",ck_errstr());
8142                     else if (!nolist)
8143                       printf("?%s\n",ck_errstr());
8144                     rc = 0;
8145                 }
8146             } else {                    /* Regular copy succeeded */
8147                 debug(F110,"COPY OK..",newname,0);
8148 #ifndef NOXFER
8149                 if (preserve) {         /* Handle /PRESERVE */
8150                     char * pstr = "";   /* File permissions string */
8151                     struct zattr xx;    /* File attribute structure */
8152                     extern char * cksysid;
8153
8154                     initattr(&xx);      /* Initialize the struct */
8155
8156                     xx.systemid.val = cksysid; /* Set our system ID */
8157                     xx.systemid.len = (int)strlen(cksysid);
8158 #ifdef CK_PERMS
8159                     pstr = zgperm(line); /* Get source file's permissions */
8160 #endif /* CK_PERMS */
8161                     xx.lprotect.val = pstr;
8162                     xx.lprotect.len = (int)strlen(pstr);
8163                     xx.date.val = zfcdat(line); /* Source file's timestamp */
8164                     xx.date.len = (int)strlen(xx.date.val);
8165                     if (zstime(nm,&xx,0) < 0) {
8166                         printf("?COPY /PRESERVE %s: %s\n",nm,ck_errstr());
8167                         rc = -9;
8168                     }
8169                 }
8170 #endif  /* NOXFER */
8171                 if (listing && rc > -1)
8172                   printf("(OK)\n");
8173             }
8174
8175         } else {                        /* Special options */
8176
8177             int prev, y, x = 0;         /* Variables needed for them */
8178             int i, t;
8179             char ibuf[100];
8180             char obuf[200];
8181             FILE * in = NULL;
8182             FILE * out = NULL;
8183
8184             if ((in = fopen(line,"r")) == NULL) { /* Open input file */
8185                 if (listing)
8186                   printf("(FAILED: %s)\n",ck_errstr());
8187                 else if (!nolist)
8188                   printf("?%s - %s)\n",ck_errstr(),line);
8189                 rc = 0;
8190                 continue;
8191             }
8192             if (targetisdir) {          /* Target is directory */
8193                 char * buf = NULL;      /* so append this filename to it */
8194                 zstrip(line,&buf);
8195                 p[targetlen] = NUL;
8196                 if (buf)
8197                   ckstrncat(p,buf,TMPBUFSIZ);
8198             }
8199 #ifdef OS2ORUNIX
8200             if (zcmpfn(line,p)) {       /* Input and output are same file? */
8201                 if (listing)
8202                   printf("(FAILED: Source and destination identical)\n");
8203                 else if (!nolist)
8204                   printf("?Source and destination identical - %s\n", line); 
8205                 rc = 0;
8206                 continue;
8207             }
8208 #endif /* OS2ORUNIX */
8209             if ((out = fopen(p, (appending ? "a" : "w"))) == NULL) {
8210                 fclose(in);
8211                 if (listing)
8212                   printf("(FAILED: %s - %s)\n",p,ck_errstr());
8213                 else if (!nolist)
8214                   printf("?%s - %s\n",p,ck_errstr());
8215                 rc = 0;
8216                 continue;
8217             }
8218 #ifndef NOSPL
8219             if (tob64) {                /* Converting to Base-64 */
8220
8221                 debug(F110,"COPY tob64",line,0);
8222
8223                 while (1) {             /* Loop... */
8224                     prev = x;
8225                     if ((x = fread(ibuf,1,54,in)) < 1) { /* EOF */
8226                         if (listing)
8227                           printf("(OK)\n");
8228                         break;
8229                     }
8230                     if (prev % 3) {
8231                         if (listing)
8232                           printf("(FAILED: Phase error at %d)\n",prev);
8233                         else if (!nolist)
8234                           printf("?Phase error at %d\n",prev);
8235                         rc = 0;
8236                         break;
8237                     }
8238                     if (swapping) {
8239                         if (x & 1) {
8240                             if (listing)
8241                               printf("(FAILED: Swap error)\n");
8242                             else if (!nolist)
8243                               printf("?Swap error\n");
8244                             rc = 0;
8245                             break;
8246                         }
8247                         for (i = 0; i < x; i+=2) {
8248                             t = ibuf[i];
8249                             ibuf[i] = ibuf[i+1];
8250                             ibuf[i+1] = t;
8251                         }
8252                     }
8253                     if ((y = b8tob64(ibuf,x,obuf,180)) < 0) {
8254                         if (listing)
8255                           printf("(FAILED: Encoding error)\n");
8256                         else if (!nolist)
8257                           printf("?Encoding error\n");
8258                         rc = 0;
8259                         break;
8260                     }
8261                     fprintf(out,"%s\n",obuf);
8262                 }
8263
8264             } else if (fromb64) {       /* Converting from Base 64 */
8265
8266                 debug(F110,"COPY fromb64",line,0);
8267
8268                 if ((out = fopen(p,appending ? "a" : "w")) == NULL) {
8269                     fclose(in);
8270                     if (listing)
8271                       printf("(FAILED: %s - %s)\n",p,ck_errstr());
8272                     else if (!nolist)
8273                       printf("?%s - %s\n",p,ck_errstr());
8274                     rc = 0;
8275                     continue;
8276                 }
8277                 x = 1;
8278                 while (x) {
8279                     x = fread(ibuf,1,80,in);
8280                     if ((y = b64tob8(ibuf,x,obuf,80)) < 0) {
8281                         if (listing)
8282                           printf("(FAILED: Decoding error)\n");
8283                         else if (!nolist)
8284                           printf("?Decoding error\n");
8285                         rc = 0;
8286                         break;
8287                     }
8288                     if (swapping) {
8289                         if (x & 1) {
8290                             if (listing)
8291                               printf("(FAILED: Swap error)\n");
8292                             else if (!nolist)
8293                               printf("?Swap error\n");
8294                             rc = 0;
8295                             break;
8296                         }
8297                         for (i = 0; i < y; i+=2) {
8298                             t = obuf[i];
8299                             obuf[i] = obuf[i+1];
8300                             obuf[i+1] = t;
8301                         }
8302                     }
8303                     if (y > 0) {
8304                         if (fwrite(obuf,1,y,out) < 1) {
8305                             if (listing)
8306                               printf("(FAILED: %s - %s)\n",p,ck_errstr());
8307                             else if (!nolist)
8308                               printf("?%s - %s\n",p,ck_errstr());
8309                             rc = 0;
8310                             break;
8311                         }
8312                     }
8313                 }
8314
8315             } else
8316 #endif /* NOSPL */
8317
8318             if (swapping) {             /* Swapping bytes */
8319
8320                 CHAR c[3];
8321                 c[2] = NUL;
8322
8323                 debug(F110,"COPY swapping",line,0);
8324
8325                 while (1) {
8326                     x = fread((char *)c,1,2,in);
8327                     if (x < 1) {
8328                         if (listing)
8329                           printf("(OK)\n");
8330                         break;
8331                     } else if (x == 1) {
8332                         c[1] = c[0];
8333                         c[0] = NUL;
8334                         printf(
8335                             "(WARNING: Odd byte count)");
8336                         if (!listing) printf("\n");
8337                     }
8338                     if (fprintf(out,"%c%c",c[1],c[0]) == EOF) {
8339                         if (listing)
8340                           printf("(FAILED: %s - %s)\n",p,ck_errstr());
8341                         else if (!nolist)
8342                           printf("?%s - %s\n",p,ck_errstr());
8343                         rc = 0;
8344                         break;
8345                     }
8346                 }
8347
8348             } else if (appending) {     /* Appending to target file */
8349
8350                 char c;
8351
8352                 debug(F110,"COPY appending",line,0);
8353
8354                 while (1) {
8355                     x = fread(&c,1,1,in);
8356                     if (x < 1) {
8357                         if (listing)
8358                           printf("(OK)\n");
8359                         break;
8360                     }
8361                     if (fwrite(&c,1,1,out) < 1) {
8362                         if (listing)
8363                           printf("(FAILED: %s - %s)\n",p,ck_errstr());
8364                         else if (!nolist)
8365                           printf("?%s - %s\n",p,ck_errstr());
8366                         rc = 0;
8367                         break;
8368                     }
8369                 }
8370             }
8371             if (out) fclose(out);
8372             if (in) fclose(in);
8373         }
8374 #ifdef VMSORUNIX
8375         concb((char)escape);
8376 #endif /* VMSORUNIX */
8377     }
8378     if (rc > -1) success = rc;
8379     return(rc);
8380 }
8381 #endif /* ZCOPY */
8382 #endif /* NOFRILLS */
8383
8384 #ifndef NOCSETS
8385 #ifndef NOUNICODE
8386 static struct keytab * xfcstab = NULL;  /* For RENAME /CONVERT: */
8387 static char cvtbufin[CKMAXPATH+8] = { NUL, NUL };
8388 static char cvtbufout[CKMAXPATH+8] = { NUL, NUL };
8389 static char * pcvtbufin = NULL;
8390 static char * pcvtbufout = NULL;
8391
8392 static int                              /* Input function xgnbyte() */
8393 cvtfnin() {
8394     CHAR c;
8395     c = *pcvtbufin++;
8396     return(c ? c : -1);
8397 }
8398
8399 _PROTOTYP(int cvtfnout,(char));         /* Output function for xpnbyte() */
8400 int
8401 #ifdef CK_ANSIC
8402 cvtfnout(char c)
8403 #else
8404 cvtfnout(c) char c;
8405 #endif  /* CK_ANSIC */
8406 {
8407     if (pcvtbufout - cvtbufout >= CKMAXPATH)
8408       return(-1);
8409     *pcvtbufout++ = c;
8410     *pcvtbufout = NUL;
8411     return(1);
8412 }
8413
8414 /* Convert a string from any charset to any other charset */
8415
8416 char *
8417 cvtstring(s,csin,csout) char * s; int csin, csout; {
8418     int c;
8419     extern CK_OFF_T ffc;
8420
8421     ckstrncpy(cvtbufin,s,CKMAXPATH);    /* Put it in a public place */
8422     pcvtbufin = cvtbufin;               /* with public pointers */
8423     pcvtbufout = cvtbufout;
8424     *pcvtbufout = NUL;
8425
8426     if (csin == csout)                  /* If the two sets are the same */
8427       return((char *)cvtbufin);         /* don't bother converting */
8428
8429     initxlate(csin,csout);              /* Initialize the translator */
8430
8431     while ((c = xgnbyte(FC_UCS2,csin,cvtfnin)) > -1) { /* Loop thru string */
8432         if (xpnbyte(c,TC_UCS2,csout,cvtfnout) < 0) {
8433             ffc = (CK_OFF_T)0;
8434             return("");
8435         }
8436     }
8437     /* ffc is touched by xgnbyte() but this is not file transfer */
8438     /* so we have to undo it */
8439     ffc = (CK_OFF_T)0;
8440     return((char *)cvtbufout);
8441 }
8442 #endif /* NOUNICODE */
8443 #endif /* NOCSETS */
8444
8445 #ifndef NORENAME
8446 #ifndef NOFRILLS
8447 #ifdef ZRENAME
8448
8449 /* The RENAME command - expanded and improved in 8.0.212 April 2006 */
8450
8451 static char * ren_sub[4] = { NULL,NULL,NULL,NULL }; /* For RENAME /REPLACE */
8452
8453 int ren_list = 0;                       /* Default listing action for RENAME */
8454 int ren_coll = RENX_OVWR;               /* Default collision action */
8455
8456 int
8457 shorename() {
8458     char * s;
8459     switch (ren_coll) {
8460       case RENX_FAIL: s = "fail"; break;
8461       case RENX_OVWR: s = "overwrite"; break;
8462       case RENX_SKIP: s = "proceed"; break;
8463     }
8464     printf(" rename collision: %s\n",s);
8465     printf(" rename list:      %s\n",showoff(ren_list));
8466     return(1);
8467 }
8468
8469 int
8470 setrename() {                           /* Parse SET RENAME options */
8471     int x, y;
8472     if ((x = cmkey(renamset,nrenamset,"","", xxstring)) < 0)
8473       return(x);
8474     switch (x) {
8475       case REN_OVW:                     /* COLLISION */
8476         if ((x = cmkey(r_collision,nr_collision,"","", xxstring)) < 0)
8477           return(x);
8478         if ((y = cmcfm()) < 0)
8479           return(y);
8480         ren_coll = x;
8481         break;
8482       case DEL_LIS:                     /* LIST */
8483         return(seton(&ren_list));
8484     }
8485     return(success = 1);
8486 }
8487
8488 /* Reverse a string - Assumes a single-byte character set */
8489
8490 int
8491 gnirts(s1, s2, len) char * s1, * s2; int len; {
8492     int n, m = 0;
8493     if (!s1)                            /* Null source pointer, fail */
8494       return(0);
8495     n = (int) strlen(s1);
8496     if (n > len-1)                      /* Source longer than dest, fail */
8497       return(0);
8498     s2[n--] = NUL;                      /* Deposit null byte at end of dest */
8499     for (; n >= 0; n--) {               /* Copy the rest backwards */
8500         *s2++ = s1[n];
8501         m++;
8502     }
8503     return(m);
8504 }
8505
8506 /*
8507   r e n a m e o n e
8508
8509   Worker function to rename one file for dorenam() (below).
8510     old        = name of file or directory to be renamed
8511     new        = new name (not required for /UPPER, /LOWER, and /REPLACE)
8512     replacing  = 1 if doing string replacement on the name  
8513     casing     = 1 if converting name to lowercase, 2 if to uppercase
8514     all        = if doing case conversion on all names, not just monocase ones
8515     converting = 1 if converting character sets
8516     cset1      = character set to convert from (File Character Set index)
8517     cset2      = character set to convert to (ditto, see ck?xla.h)
8518     listing    = 1 to show results of rename
8519     nolist     = 1 to be completely silent (don't even print error messages)
8520     op         = 1 means simulate, 2 means check for collision, 0 means rename
8521     size       = length of result buffer.
8522     collision  = action to take if destination file already exists:
8523                  0 = fail
8524                  1 = overwrite and succeed
8525                  2 = skip and succeed
8526   Returns:
8527      0: on failure to rename or when a forbidden collision would have occurred.
8528      1: on success (file was renamed or did not need to be renamed).
8529   Note:
8530      If this code is ever built on any platform that is not Unix, Windows,
8531      VMS, or OS/2, this routine might need some adjustment.
8532 */
8533
8534 /* Opcodes for op... */
8535 #define REN_OP_SIM 1                    /* Simulate */
8536 #define REN_OP_CHK 2                    /* Check for collisions */
8537
8538 static int
8539 renameone(old,new,
8540           replacing,casing,all,converting,cset1,cset2,
8541           listing,nolist,op,size,collision)
8542     char * old, * new;
8543     int replacing,casing,all,converting,cset1,cset2,
8544     listing,nolist,op,size,collision;
8545 {
8546     char buf[CKMAXPATH];                /* Temporary filename buffer */
8547     char out[CKMAXPATH];                /* Buffer for new name */
8548     char dir[CKMAXPATH];                /* Destination directory */
8549     char pat[CKMAXPATH];                /* Path segment on old filename */
8550
8551     char * destdir;                     /* Destination directory, if any */
8552     char * srcpath;                     /* Source path, if any */
8553     int rc = 1, flag = 0, skip = 0;     /* Control */
8554     int honorcase = 0, replaced = 0;
8555     int anchor = 0;                     /* 1 = beginning, 2 = end */
8556     int occur = 0;                      /* Occurrence */
8557     int minus = 0;                      /* Occurrence is negative */
8558     int allbut = 0;                     /* Occurrence is "all but" */
8559     int arg2isfile = 0;                 /* Arg2 ("new") is a filename */
8560     
8561     debug(F110,"RENAMEONE old",old,0);
8562     debug(F110,"RENAMEONE new",new,0);
8563     debug(F110,"RENAMEONE ren_sub[0]",ren_sub[0],0);
8564     debug(F110,"RENAMEONE ren_sub[1]",ren_sub[1],0);
8565     debug(F110,"RENAMEONE ren_sub[2]",ren_sub[2],0);
8566
8567     if (op == REN_OP_SIM && !nolist)    /* For convenience */
8568       listing = 1;
8569 #ifndef NOSPL
8570     honorcase = inpcas[cmdlvl];         /* Inherit SET CASE value */
8571 #else
8572 #ifdef UNIX
8573     honorcase = 1;
8574 #else
8575     honorcase = 0;
8576 #endif  /* UNIX */
8577 #endif  /* NOSPL */
8578
8579     if (!old) old = "";                 /* In case of bad args */
8580     if (!new) new = "";
8581     if (!*old) return(success = 0);
8582     ckstrncpy(out,new,CKMAXPATH);       /* So we don't write into */
8583     new = out;                          /* our argument... */
8584     size = CKMAXPATH;
8585
8586     pat[0] = NUL;                       /* Assume no path in source file.. */
8587     srcpath = pat;
8588     {
8589         int n;                          /* If the old name includes a path */
8590         n = (int)strlen(old) - 1;       /* put it in a separate place.     */
8591         for (; n >= 0; n--) {           /* We are renaming the file only.  */
8592             if (ISDIRSEP(old[n])) {
8593                 ckstrncpy(pat,old,CKMAXPATH);
8594                 pat[n+1] = NUL;
8595                 old = old+n+1;
8596                 break;
8597             }
8598         }
8599     }
8600     debug(F110,"RENAMEONE old 2",old,0);
8601     debug(F110,"RENAMEONE pat 2",pat,0);
8602
8603     dir[0] = NUL;                       /* Assume no destination directory */
8604     destdir = dir;
8605     if (*new) {                         /* If Arg2 given */
8606         if (isdir(new)) {               /* If it's a directory */
8607             ckstrncpy(dir,new,CKMAXPATH); /* put it here */
8608         } else {                        /* otherwise */
8609             arg2isfile++;               /* flag that it's a filename */
8610         }    
8611     }
8612     if (!casing && !replacing && !converting) {
8613         if (!*new)
8614           return(success = 0);
8615         if (!arg2isfile) {              /* Destination is a directory? */
8616             if (!isdir(old)) {          /* and source is not? */
8617 #ifndef VMS
8618                 int n, x = 0;           /* Concatenate them */
8619                 if ((n = strlen(new)) > 0) /* so we can check for */
8620                   if (ISDIRSEP(new[n-1]))  /* collisions. */
8621                     x++;
8622                 ckmakmsg(buf,size,new,x ? "" : "/", old, "");
8623 #else
8624                 ckmakmsg(buf,size,new, old, NULL, NULL);
8625 #endif  /* VMS */
8626                 debug(F110,"RENAMEONE new new",new,0);          
8627                 new = buf;
8628                 size = CKMAXPATH;
8629             }
8630         }
8631     } else if (*new) {                  /* Directory to move file to */
8632         int n, x = 0;                   /* after changing its name */
8633         if (!isdir(new))
8634           return(success = 0);
8635 #ifndef VMS
8636         if ((n = strlen(new)) > 0)
8637           if (ISDIRSEP(new[n-1]))
8638             x++;
8639         ckmakmsg(dir,CKMAXPATH,new,x ? "" : "/", "", "");
8640 #else
8641         ckstrncpy(dir,new,CKMAXPATH);
8642 #endif  /* VMS */
8643     }
8644
8645 #ifndef NOCSETS
8646 #ifndef NOUNICODE
8647     if (converting) {
8648         new = cvtstring(old,cset1,cset2);
8649     }
8650 #endif  /* NOUNICODE */
8651 #endif  /* NOCSETS */
8652
8653     if (replacing) {                    /* Replacing strings */
8654         int todo = 0;
8655         int len0, len1, len2;
8656         char c, *p, *s, *bp[3];
8657
8658         bp[0] = old;                    /* Original name */
8659         bp[1] = ren_sub[0];             /* String to be replaced */
8660         bp[2] = ren_sub[1];             /* What to replace it with */
8661         if (!bp[2]) bp[2] = "";
8662
8663         len0 = (int)strlen(bp[0]);      /* length of original filename */
8664         len1 = (int)strlen(bp[1]);      /* length of target substring */
8665         len2 = (int)strlen(bp[2]);      /* Length of replacement string */
8666
8667         if (ren_sub[2]) {               /* Optional options */
8668             p = ren_sub[2];
8669             while ((c = *p++)) {
8670                 switch (c) {
8671                   case '^':
8672                     anchor = 1; occur = 0; minus = 0; allbut = 0; break;
8673                   case '$':
8674                     anchor = 2; occur = 0; minus = 0; allbut = 0; break;
8675                   case 'A': honorcase = 1; minus = 0; allbut = 0; break;
8676                   case 'a': honorcase = 0; minus = 0; allbut = 0; break;
8677                   case '-': minus = 1; break;
8678                   case '~': allbut = 1; break;
8679                   default:
8680                     if (isdigit(c)) {
8681                         occur = c - '0';
8682                         if (minus) occur = 0 - occur;
8683                         anchor = 0;
8684                     }
8685                     minus = 0;
8686                 }
8687             }
8688         }
8689         if (anchor) {                   /* Anchored replacement... */
8690             y = len0 - len1;
8691             if (y > 0) {
8692                 int x;
8693                 switch (anchor) {
8694                   case 1:               /* Anchored at beginning */
8695                     if (!ckstrcmp(bp[1],bp[0],len1,honorcase)) {
8696                         x = ckstrncpy(new,bp[2],size);
8697                         (VOID) ckstrncpy(new+x,bp[0]+len1,size-x);
8698                         replaced = 1;
8699                     }
8700                     break;
8701                   case 2:               /* Anchored at end */
8702                     if (!ckstrcmp(bp[1],bp[0]+y,len1,honorcase)) {
8703                         x = ckstrncpy(new,bp[0],y+1);
8704                         (VOID) ckstrncpy(new+y,bp[2],size-x);
8705                         replaced = 1;
8706                     }
8707                     break;
8708                 }
8709             }
8710             if (!replaced) {
8711                 ckstrncpy(new,old,size); /* Keep old name */
8712                 replaced = 1;
8713             }
8714         } else {                        /* Replace all occurrences */
8715             int j, n = 0;               /* or a particular occurrence */
8716             char c;
8717             int x = 0;
8718             char * s0 = NULL, * s1 = NULL, * s2 = NULL;
8719             p = new;                    /* Pointer to new name */
8720
8721             if (occur < 0) {            /* nth occurrence from the right */
8722                 occur = 0 - occur;
8723                 s0 = (char *)malloc(len0+1); /* Reverse original string */
8724                 if (s0) {
8725                     (VOID) gnirts(bp[0],s0,len0+1);
8726                     bp[0] = s0;
8727                 } else return(0); 
8728                 s1 = (char *)malloc(len1+1); /* Reverse target string */
8729                 if (s1) {
8730                     (VOID) gnirts(bp[1],s1,len1+1);
8731                     bp[1] = s1;
8732                 } else return(0); 
8733                 s2 = (char *)malloc(len2+1); /* Reverse replacement string */
8734                 if (s2) {
8735                     (VOID) gnirts(bp[2],s2,len2+1);
8736                     bp[2] = s2;
8737                 } else return(0); 
8738                 debug(F111,"RENAMEONE s0",s0,len0);
8739                 debug(F111,"RENAMEONE s1",s1,len1);
8740                 debug(F111,"RENAMEONE s2",s2,len2);
8741             }
8742             s = bp[0];                  /* Pointer to old name */
8743             p = new;                    /* Pointer to new name */
8744             j = len0 - len1 + 1;        /* How much to scan */
8745             while (j-- > 0) {           /* For each character... */
8746                 if (!ckstrcmp(bp[1],s,len1,honorcase)) { /* Match? */
8747                     n++;                /* Occurrence counter */
8748                     todo = (occur == 0) ||
8749                         (!allbut && n == occur) ||
8750                         (allbut && n != occur);
8751                     if (!todo) {        /* Desired occurrence? */
8752                         size -= ckstrncpy(p,bp[1],size); /* No... */
8753                         p += len1;      /* Copy target string */
8754                         s += len1;      /* instead of replacement string */
8755                         continue;
8756                     }
8757                     if (len2) {         /* If replacement string not empty */
8758                         size -= ckstrncpy(p,bp[2],size); /* Copy it */
8759                         p += len2;
8760                     }
8761                     s += len1;          /* Advance source position */
8762                 } else {                /* No match */
8763                     *p++ = *s++;        /* just copy this character */
8764                     size--;
8765                 }
8766             }
8767             while ((*p++ = *s++));      /* Done copy the rest */
8768             replaced = 1;               /* Remember we changed the name */
8769             if (s0) {                   /* Were we doing "all but"? */
8770                 debug(F110,"RENAMEONE new1",new,0);
8771                 x = (int)strlen(new);   /* Unreverse the result */
8772                 if ((p = (char *)malloc(x+2))) {
8773                     (VOID) gnirts(new,p,x+2);
8774                     debug(F110,"RENAMEONE new2",new,0);
8775                     ckstrncpy(new,p,x+2);
8776                     free(p);
8777                 }
8778                 if (s0) free(s0);       /* Free the temporary strings */
8779                 if (s1) free(s1);
8780                 if (s2) free(s2);
8781                 debug(F110,"RENAMEONE new3",new,0);
8782             }
8783         }
8784     }
8785     if (casing) {                       /* Changing case? */
8786         char c, * t;                    /* See if mixed case. */
8787         if (!replaced)
8788           ckstrncpy(new,old,size);      /* Copy old name to new name */
8789         t = new;
8790         while ((c = *t++)) {
8791             if (islower(c)) flag |= 1; /* Have a lowercase letter */
8792             else if (isupper(c)) flag |= 2; /* Have an uppercase letter */
8793             if (flag == 3) break;       /* Have a mixed-case name */
8794         }
8795         if (all || flag < 3) {  /* Not skipping or not mixed case */
8796             if (casing == 1 && flag != 1) /* Change case to lower */
8797               (VOID) cklower(new);
8798             else if (casing == 2 && flag != 2) /* Change case to upper */
8799               (VOID) ckupper(new);
8800         }
8801     }
8802     debug(F110,"XXX 1 new",new,0);
8803     debug(F110,"XXX 1 old",old,0);
8804     debug(F110,"XXX 1 srcpath",srcpath,0);
8805     debug(F110,"XXX 1 destdir",destdir,0);
8806
8807     if (*destdir && !arg2isfile) {      /* Moving without renaming */
8808         ckstrncat(srcpath,old,CKMAXPATH);
8809         old = srcpath;
8810         new = destdir;
8811     } else if (*destdir || *srcpath) {  /* Were there any pathnames? */
8812         char tmp[CKMAXPATH];
8813         ckmakmsg(tmp,CKMAXPATH,srcpath,old,NULL,NULL);
8814         ckstrncpy(old,tmp,CKMAXPATH);
8815         if (*destdir) {                 /* Directory-to-move-to given? */
8816             ckstrncat(destdir,new,CKMAXPATH);
8817             new = destdir;
8818         } else if (*srcpath && !arg2isfile) { /* Or was there a source path? */
8819             ckstrncat(srcpath,new,CKMAXPATH);
8820             new = srcpath;
8821         }
8822     }
8823     debug(F110,"XXX 2",new,0);
8824
8825     skip = 0;                           /* Can we skip this one? */
8826 #ifdef COMMENT
8827     if (casing && !replaced) {
8828         skip = (((all == 0) && (flag == 3)) || (flag == casing)) ? 1 : 0;
8829         if (!skip && destdir) skip = 0;
8830     }
8831 #endif  /* COMMENT */
8832     if (!skip) {
8833         if (!ckstrcmp(old,new,-1,1))
8834           skip = 1;
8835     }
8836     if (op == 0 && !skip && (collision != RENX_OVWR)) {
8837         if (zchki(new) > (CK_OFF_T)-1) { /* New file already exists?  */
8838             switch (collision) {        /* Yes, take specified action */
8839               case RENX_SKIP:           /* Skip this one and proceed  */
8840                 skip = 2;
8841                 break;
8842               case RENX_FAIL:           /* Or fail. */
8843                 skip = 3;
8844                 if (!listing && !nolist)
8845                   printf("?File already exists: %s\n",new);
8846             }
8847         }
8848     }
8849     debug(F110,"RENAMEONE new",new,0);
8850     debug(F101,"RENAMEONE flag","",flag);       
8851     debug(F101,"RENAMEONE skip","",skip);       
8852     debug(F100,"RENAMEONE ----------------","",0);
8853
8854     if (skip == 3) {
8855         if (listing) printf("%s => %s (SKIPPED: %s already exists)\n",
8856                             old,new,new);
8857         rc = 0;
8858     } else if (skip) {                  /* Skipping this one */
8859         if (listing) printf("%s => %s (%s)\n",
8860                             old,new,
8861                             (skip == 2) ? "COLLISION: SKIPPED" : "SKIPPED");
8862     } else {                            /* Have to rename this one */
8863         if (op == REN_OP_CHK) {         /* Checking for collisions */
8864             return((zchki(new) > (CK_OFF_T)-1) ? 0 : 1 );
8865         } else if (op == REN_OP_SIM) {  /* Simulating */
8866             if (listing) printf("%s => %s (SIMULATED)\n",old,new);
8867         } else {                        /* Really renaming */
8868             if (listing) printf("%s => %s ",old,new);
8869             if (zrename(old,new) < 0) {
8870                 rc = 0;
8871                 if (listing)
8872                   printf("(FAILED: %s)\n",ck_errstr());
8873                 else if (!nolist)
8874                   printf("?%s\n",ck_errstr());
8875             } else {
8876                 if (listing) printf("(OK)\n");
8877             }
8878         }
8879     }
8880     return(success = rc);  /* Succeeds also if nothing needed to be renamed */
8881 }
8882
8883 int
8884 dorenam() {
8885 #ifndef NOCSETS
8886 #ifndef NOUNICODE
8887     extern int nfilc;
8888     extern struct keytab fcstab[];
8889     extern struct csinfo fcsinfo[];
8890 #endif  /* NOUNICODE */
8891 #endif  /* NOCSETS */
8892     int cset1 = 0, cset2 = 0;
8893
8894     int i, x, z, fn, listing = 0, havename = 0, wild = 0, rc = 1, noarg = 0;
8895     int nolist = 0, all = 0, casing = 0, replacing = 0, getval = 0, sim = 0;
8896     int converting = 0, collision = 0;
8897
8898     char c;
8899     struct FDB sw, fi;
8900
8901     collision = ren_coll;               /* Inherit SET RENAME COLLISION */
8902     listing = ren_list;                 /* Inhereit SET RENAME LIST */
8903
8904     if (ren_sub[0]) makestr(&(ren_sub[0]),NULL);
8905     if (ren_sub[1]) makestr(&(ren_sub[1]),NULL);
8906     if (ren_sub[2]) makestr(&(ren_sub[2]),NULL);
8907     line[0] = NUL;
8908     tmpbuf[0] = NUL;
8909
8910     cmfdbi(&sw,                         /* 1st FDB - switches */
8911            _CMKEY,                      /* fcode */
8912            "Filename or switch",        /* hlpmsg */
8913            "",                          /* default */
8914            "",                          /* addtl string data */
8915            nrenamsw,                    /* addtl numeric data 1: tbl size */
8916            4,                           /* addtl numeric data 2: 4 = cmswi */
8917            xxstring,                    /* Processing function */
8918            renamsw,                     /* Keyword table */
8919            &fi                          /* Pointer to next FDB */
8920            );
8921
8922     cmfdbi(&fi,                         /* 2nd FDB - file or directory name */
8923            _CMIFI,                      /* fcode */
8924            "",                          /* hlpmsg */
8925            "",                          /* default */
8926            "",                          /* addtl string data */
8927            3,                           /* Flags */
8928            0,                           /* 0 = Parse file or directory names */
8929            xxstring,
8930            NULL,
8931            NULL
8932            );
8933
8934     if (cmflgs == 1) {
8935         printf("?File or directory name required\n");
8936         return(-9);
8937     }
8938     while (!havename) {
8939         noarg = 0;
8940         x = cmfdb(&sw);                 /* Parse something */
8941         if (x == -3) {                  /* They hit Enter prematurely */
8942             printf("?Command incomplete\n");
8943             return(-9);
8944         } else if (x < 0) {             /* Other error */
8945             if (x == -1)
8946               return(x);
8947             if (iswild(atmbuf) && nzxpand(atmbuf,nzxopts) == 0)
8948               printf("?No files match: %s\n",brstrip(atmbuf));
8949             else if (zchki(atmbuf) == -1)
8950               printf("?File not found: %s\n",brstrip(atmbuf));
8951             else
8952               printf("?Error with switch or filename: %s\n",brstrip(atmbuf));
8953             return(-9);
8954         }
8955         fn = cmresult.nresult;          /* For brevity */
8956         switch (cmresult.fcode) {       /* Handle each kind of field */
8957           case _CMKEY:                  /* Keyword (switch) */
8958             c = cmgbrk();
8959             if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
8960                 printf("?This switch does not take an argument\n");
8961                 return(-9);
8962             }
8963             if (!getval && (cmgkwflgs() & CM_ARG))
8964               noarg = 1;                /* Remember arg is missing */
8965             switch (cmresult.nresult) { /* Handle the switch */
8966               case DEL_LIS:             /* /LIST */
8967               case DEL_VRB:             /* /VERBOSE */
8968                 listing = 1;
8969                 break;
8970               case DEL_NOL:             /* /NOLIST */
8971               case DEL_QUI:             /* /QUIET */
8972                 nolist = 1;
8973                 listing = 0;
8974                 break;
8975               case DEL_SIM:             /* /SIMULATE */
8976                 sim = 1;
8977                 break;
8978               case REN_UPP:             /* /UPPER: */
8979               case REN_LOW:             /* /LOWER */
8980                 all = 1;
8981                 if (!noarg) {
8982                     if ((x = cmkey((fn == REN_UPP) ? r_upper : r_lower,
8983                                    2, "","all",xxstring)) < 0) {
8984                         if (x == -3)
8985                           x = 1;
8986                         else
8987                           return(x);
8988                     }
8989                     all = x;
8990                 }
8991                 /* 0 = don't convert; 1 = convert to lower; 2 = to upper */
8992                 casing = (fn == REN_UPP) ? 2 : 1;
8993                 converting = 0;
8994                 break;
8995               case REN_OVW:             /* /COLLISION */
8996                 if (!noarg) {
8997                     if ((x = cmkey(r_collision,
8998                                    nr_collision,"","overwrite",xxstring))<0) {
8999                         if (x == -3)
9000                           x = RENX_OVWR;
9001                         else
9002                           return(x);
9003                     }
9004                     collision = x;
9005                 }
9006                 break;
9007               case REN_RPL:             /* /REPLACE: */
9008                 if (noarg) {
9009                     printf("?This switch requires an argument\n");
9010                     return(-9);
9011                 }
9012                 if ((x = cmfld("String to remove, or {{String1}{String2}}",
9013                                "",&s,xxstring)) < 0) {
9014                     if (x == -3) {
9015                         printf("?Target string required\n");
9016                         x = -9;
9017                     }
9018                     return(x);
9019                 }
9020                 if (s[0]) {
9021                     if (s[0] == '{' && s[1] == '{') /* Get the list */
9022                       makelist(s,ren_sub,3);
9023                     else
9024                       makestr(&(ren_sub[0]),s);
9025                 }
9026                 converting = 0;
9027                 break;
9028
9029               case REN_SPA:             /* /FIXSPACES: */
9030                 if (!noarg)
9031                   if ((x = cmfld("Character or string to replace spaces with",
9032                                  "_",&s,xxstring)) < 0)
9033                     if (x == -3)
9034                       s = "_";
9035                     else
9036                       return(x);
9037                 makestr(&(ren_sub[0])," ");
9038                 makestr(&(ren_sub[1]),noarg ? "_" : brstrip(s));
9039                 makestr(&(ren_sub[3]),NULL);
9040                 converting = 0;
9041                 break;
9042
9043 #ifndef NOCSETS
9044 #ifndef NOUNICODE
9045               case REN_XLA:             /* /CONVERT:cset1:cset2 */
9046                 if (!xfcstab) {         /* Make a copy of the file charset */
9047                     int i, x;           /* table with CM_ARG set for each */
9048                     x = (nfilc + 1) * sizeof(struct keytab);
9049                     xfcstab = (struct keytab *)malloc(x);
9050                     if (!xfcstab) {
9051                         printf("?Memory allocation failure\n");
9052                         return(-9);
9053                     }
9054                     for (i = 0; i < nfilc; i++) {
9055                         xfcstab[i].kwd = fcstab[i].kwd;
9056                         xfcstab[i].kwval = fcstab[i].kwval;
9057                         xfcstab[i].flgs = fcstab[i].flgs | CM_ARG;
9058                     }
9059                 }
9060                 if ((x = cmswi(xfcstab,nfilc,
9061                                "Character-set of old name","",xxstring)) < 0) {
9062                     if (x == -3) {
9063                         printf("?Pair of character-set names required\n");
9064                         return(-9);
9065                     } else
9066                       return(x);
9067                 }
9068                 cset1 = x;
9069                 c = cmgbrk();
9070                 if (!getval) {
9071                     printf("?Secondcharacter-set name required\n");
9072                     return(-9);
9073                 }
9074                 if ((x = cmkey(fcstab,nfilc,
9075                                "Character-set of new name","",xxstring)) < 0) {
9076                     if (x == -3) {
9077                         printf("?Second character-set name required\n");
9078                         return(-9);
9079                     } else
9080                       return(x);
9081                 }
9082                 cset2 = x;
9083                 if (casing)
9084                   casing = 0;
9085                 if (ren_sub[0])
9086                   makestr(&(ren_sub[0]),NULL);
9087                 if (ren_sub[1])
9088                   makestr(&(ren_sub[1]),NULL);
9089                 if (ren_sub[2])
9090                   makestr(&(ren_sub[2]),NULL);
9091                 converting = 1;
9092                 break;
9093 #endif  /* NOUNICODE */
9094 #endif  /* NOCSETS */
9095             }
9096             break;
9097           case _CMIFI:                  /* File or directory name */
9098             s = cmresult.sresult;
9099             havename = 1;
9100             break;
9101           default:
9102             printf("?File or directory name required\n");
9103             return(-9);
9104         }
9105     }
9106     if (havename) {
9107         ckstrncpy(line,s,LINBUFSIZ);    /* Make a safe copy of source name */
9108     } else {
9109         printf("?Internal error\n");
9110         return(-9);                     /* Shouldn't happen */
9111     }
9112     wild = cmresult.nresult;            /* Source specification wild? */
9113     if (!wild)
9114       wild = iswild(line);
9115
9116     debug(F111,"RENAME WILD",line,wild);
9117
9118     p = tmpbuf;                         /* Place for new name */
9119     p[0] = NUL;
9120     replacing = ren_sub[0] ? 1 : 0;
9121
9122 #ifdef COMMENT
9123     if (!(casing || replacing || converting)) {
9124         if ((x = cmofi(wild ? "Target directory" : "New name",
9125                        "",&s,xxstring)) < 0) { /* Get new name */
9126             if (x == -3) {
9127                 printf("?%s required\n",
9128                        wild ? "Target directory" : "New name");
9129                 return(-9);
9130             } else return(x);
9131         }
9132         ckstrncpy(p,s,TMPBUFSIZ);       /* Make a safe copy of the new name */
9133     }
9134 #else
9135     if ((x = cmofi(wild ? "Target directory" : "New name",
9136                    "",&s,xxstring)) < 0) { /* Get new name */
9137         if (x == -3) {
9138             if (casing || replacing || converting) {
9139                 s = "";
9140             } else {
9141                 printf("?%s required\n",
9142                        wild ? "Target directory" : "New name");
9143                 return(-9);
9144             }
9145         } else return(x);
9146     }
9147     ckstrncpy(p,s,TMPBUFSIZ);           /* Make a safe copy of the new name */
9148 #endif  /* COMMENT */
9149
9150     if ((y = cmcfm()) < 0) return(y);   /* Confirm the command */
9151
9152 #ifdef COMMENT
9153 #ifndef NOUNICODE
9154     if (converting) {
9155         printf(" From: %s\n",fcsinfo[cset1].keyword);
9156         printf(" To:   %s\n",fcsinfo[cset2].keyword);
9157     }
9158 #endif  /* NOUNICODE */
9159     if (casing) {
9160         printf("CASING: %s\n", (casing == 1) ? "LOWER" : "UPPER");
9161     }
9162     if (replacing) {
9163         printf("REPLACING: '%s' with '%s'\n",
9164                ren_sub[0],
9165                ren_sub[1] ? ren_sub[1] : "");
9166     }
9167 #endif  /* COMMENT */
9168
9169     s = line;
9170     if (!wild)                          /* Just one */
9171       return(success =
9172              renameone(s,p,
9173                        replacing,casing,all,converting,cset1,cset2,
9174                        listing,nolist,sim,TMPBUFSIZ,collision));
9175
9176     if (!casing && !replacing && !converting) { /* Multiple files */
9177         if (!isdir(p)) {
9178             printf(                     /* if target is not a directory */
9179 "?Multiple source files not allowed if target is not a directory.\n");
9180             return(-9);
9181         }
9182     }
9183 #ifdef COMMENT
9184     else {                              /* Show full path of target */
9185         char buf[CKMAXPATH];            /* (too much) */
9186         if (zfnqfp(p,CKMAXPATH,buf))
9187           ckstrncpy(tmpbuf,buf,TMPBUFSIZ);
9188     }
9189 #endif /* COMMENT */
9190
9191 #ifdef VMS
9192     conres();                           /* Let Ctrl-C work. */
9193 #endif /* VMS */
9194     debug(F110,"dorename line",line,0);
9195
9196 #ifdef ZXREWIND
9197     z = zxrewind();                     /* Rewind file list */
9198 #else
9199     z = nzxpand(s,0);                   /* Expand file list */
9200 #endif /* ZXREWIND */
9201     debug(F111,"dorename p",p,z);
9202
9203 #ifdef UNIX
9204     if (wild && z > 1)
9205       sh_sort(mtchs,NULL,z,0,0,filecase); /* Alphabetize the filename list */
9206 #endif /* UNIX */
9207
9208     /* For /COLLISION:FAIL make a silent pass to see if there would be any */
9209
9210     if (collision == RENX_FAIL) {
9211         int n = 0;
9212         char line[CKMAXPATH+2];
9213         while (z-- > 0) {
9214             if (!(z == 0 && !wild))
9215               znext(line);
9216             if (!line[0])
9217               break;
9218             if (!renameone((char *)line,p,
9219                            replacing,casing,all,converting,cset1,cset2,
9220                            0,1,REN_OP_CHK,TMPBUFSIZ,RENX_FAIL))
9221               n++;
9222         }
9223         if (n > 0) {
9224             printf("?Failed: %d file%s would be overwritten\n",
9225                    n, (n != 1) ? "s" : "");
9226 #ifdef VMS
9227             concb((char)escape);
9228 #endif /* VMS */
9229             return(success = 0);
9230         }
9231         /* Get the file list back. */
9232 #ifdef ZXREWIND
9233         z = zxrewind();
9234 #else
9235         z = nzxpand(s,0);
9236 #endif /* ZXREWIND */
9237     }
9238     while (z-- > 0 && rc > 0) {
9239         char line[CKMAXPATH+2];
9240         if (!(z == 0 && !wild))
9241           znext(line);
9242         if (!line[0])
9243           break;
9244         rc = renameone((char *)line,p, 
9245                        replacing,casing,all,converting,cset1,cset2,
9246                        listing,nolist,sim,TMPBUFSIZ,collision);
9247     }
9248 #ifdef VMS
9249     concb((char)escape);
9250 #endif /* VMS */
9251     return(success = rc);
9252 }
9253 #endif /* ZRENAME */
9254 #endif /* NOFRILLS */
9255 #endif /* NORENAME */
9256
9257 #ifndef NOSPL
9258
9259 /* Do the RETURN command */
9260
9261 int
9262 doreturn(s) char *s; {
9263     int x;
9264     extern int tra_asg;
9265     char * line, * lp;
9266
9267     if (cmdlvl < 1) {
9268         printf("\n?Can't return from level %d\n",maclvl);
9269         return(success = 0);
9270     }
9271     line = malloc(LINBUFSIZ);
9272     if (line == NULL)
9273       return(success = 0);
9274     lp = line;                          /* Expand return value now */
9275     x = LINBUFSIZ-1;
9276     if (!s) s = "";
9277     debug(F110,"RETURN s",s,0);
9278     if (zzstring(s,&lp,&x) > -1) {
9279         s = line;
9280         debug(F110,"RETURN zzstring",s,0);
9281     }
9282
9283     /* Pop from all FOR/WHILE/SWITCH/XIFs */
9284     while ((maclvl > 0) &&
9285            (m_arg[maclvl-1][0]) &&
9286            (cmdstk[cmdlvl].src == CMD_MD) &&
9287            (!strncmp(m_arg[maclvl-1][0],"_xif",4) ||
9288             !strncmp(m_arg[maclvl-1][0],"_for",4) ||
9289             !strncmp(m_arg[maclvl-1][0],"_swi",4) ||
9290             !strncmp(m_arg[maclvl-1][0],"_whi",4))) {
9291         debug(F111,"RETURN IF/FOR/WHI/SWI pop",m_arg[maclvl-1][0],maclvl);
9292         dogta(XXPTA);                   /* Put args back */
9293         popclvl();                      /* Pop up two levels */
9294         popclvl();
9295     }
9296     if (tra_asg) {                      /* If tracing show return value */
9297         if (*s)
9298           printf("<<< %s: \"%s\"\n", m_arg[maclvl][0], s);
9299         else
9300           printf("<<< %s: (null)\n", m_arg[maclvl][0]);
9301     }
9302     popclvl();                          /* Pop from enclosing TAKE or macro */
9303     debug(F111,"RETURN tolevel",s,maclvl);
9304     if (!s) s = "";
9305     if (!*s) s = NULL;
9306     makestr(&(mrval[maclvl+1]),s);      /* Set the RETURN value */
9307     free(line);
9308     return(success = 1);                /* Macro succeeds if we RETURN */
9309 }
9310 #endif /* NOSPL */
9311
9312 #ifndef NOSPL
9313 /* Do the OPEN command */
9314
9315 int
9316 doopen()  {                             /* OPEN { append, read, write } */
9317     int x, y, z = 0; char *s;
9318     static struct filinfo fcb;          /* (must be static) */
9319     if ((x = cmkey(opntab,nopn,"mode","",xxstring)) < 0) {
9320         if (x == -3) {
9321             printf("?Mode required\n");
9322             return(-9);
9323         } else return(x);
9324     }
9325     switch (x) {
9326       case OPN_FI_R:                    /* Old file (READ) */
9327         if (chkfn(ZRFILE) > 0) {
9328             printf("?Read file already open\n");
9329             return(-2);
9330         }
9331         if ((z = cmifi("File to read","",&s,&y,xxstring)) < 0) {
9332             if (z == -3) {
9333                 printf("?Input filename required\n");
9334                 return(-9);
9335             } else return(z);
9336         }
9337         if (y) {                                /* No wildcards allowed */
9338             printf("\n?Please specify a single file\n");
9339             return(-2);
9340         }
9341         ckstrncpy(line,s,LINBUFSIZ);
9342         if ((int)strlen(line) < 1) return(-2);
9343         if ((z = cmnum("buffer size","4096",10,&y,xxstring)) < 0)
9344           return(z);
9345         if (y < 1) {
9346             printf("?Positive number required\n");
9347             return(-9);
9348         }
9349         if ((z = cmcfm()) < 0) return(z);
9350         readblock = y;
9351         if (readbuf)
9352           free((char *)readbuf);
9353         if (!(readbuf = (CHAR *) malloc(readblock+1))) {
9354             printf("?Can't allocate read buffer\n");
9355             return(-9);
9356         }
9357         return(success = zopeni(ZRFILE,line));
9358
9359 #ifndef MAC
9360 #ifndef NOPUSH
9361       case OPN_PI_R:                    /* Pipe/Process (!READ) */
9362         if (nopush) {
9363             printf("?Read from pipe disabled\n");
9364             return(success=0);
9365         }
9366         if (chkfn(ZRFILE) > 0) {
9367             printf("?Read file already open\n");
9368             return(-2);
9369         }
9370         if ((y = cmtxt("System command to read from","",&s,xxstring)) < 0) {
9371             if (y == -3) {
9372                 printf("?Command name required\n");
9373                 return(-9);
9374             } else return(y);
9375         }
9376         ckstrncpy(line,brstrip(s),LINBUFSIZ);
9377         if (!line[0]) return(-2);
9378         if ((y = cmcfm()) < 0) return(y);
9379         if (!readbuf) {
9380             if (!(readbuf = (CHAR *) malloc(readblock+1))) {
9381                 printf("?Can't allocate read buffer\n");
9382                 return(-9);
9383             }
9384         }
9385         return(success = zxcmd(ZRFILE,line));
9386
9387       case OPN_PI_W:                    /* Write to pipe */
9388         if (nopush) {
9389             printf("?Write to pipe disabled\n");
9390             return(success=0);
9391         }
9392         if (chkfn(ZWFILE) > 0) {
9393             printf("?Write file already open\n");
9394             return(-2);
9395         }
9396         if ((y = cmtxt("System command to write to","",&s,xxstring)) < 0) {
9397             if (y == -3) {
9398                 printf("?Command name required\n");
9399                 return(-9);
9400             } else return(y);
9401         }
9402         ckstrncpy(line,brstrip(s),LINBUFSIZ);
9403         if (!line[0]) return(-2);
9404         if ((y = cmcfm()) < 0) return(y);
9405         success = zxcmd(ZWFILE,line);
9406         if (!success && msgflg)
9407           printf("Can't open process for writing: %s\n",line);
9408         return(success);
9409 #endif /* NOPUSH */
9410 #endif /* MAC */
9411
9412       case OPN_FI_W:                    /* New file (WRITE) */
9413       case OPN_FI_A:                    /* (APPEND) */
9414         if ((z = cmofi("Name of local file to create","",&s,xxstring)) < 0) {
9415             if (z == -3) {
9416                 printf("?Filename required\n");
9417                 return(-9);
9418             } else return(z);
9419         }
9420         if (z == 2) {
9421             printf("?Sorry, %s is a directory name\n",s);
9422             return(-9);
9423         }
9424         if (chkfn(ZWFILE) > 0) {
9425             printf("?Write/Append file already open\n");
9426             return(-2);
9427         }
9428         fcb.bs = fcb.cs = fcb.rl = fcb.fmt = fcb.org = fcb.cc = fcb.typ = 0;
9429         fcb.lblopts = 0;
9430         fcb.dsp = (x == OPN_FI_W) ? XYFZ_N : XYFZ_A; /* Create or Append */
9431         ckstrncpy(line,s,LINBUFSIZ);
9432         if ((int)strlen(line) < 1) return(-2);
9433         if ((y = cmcfm()) < 0) return(y);
9434         return(success = zopeno(ZWFILE,line,NULL,&fcb));
9435
9436 #ifndef NOLOCAL
9437       case OPN_SER:                     /* OPEN PORT or LINE */
9438       case OPN_NET: {                   /* OPEN HOST */
9439           extern int didsetlin, ttnproto;
9440           if (x == OPN_NET) {
9441               z = ttnproto;
9442               ttnproto = NP_NONE;
9443           }
9444           if ((y = setlin((x == OPN_SER) ? XYLINE : XYHOST, 1, 0)) < 0) {
9445               if (x == OPN_NET)
9446                 ttnproto = z;
9447               success = 0;
9448           }
9449           didsetlin++;
9450           return(y);
9451       }
9452 #endif /* NOLOCAL */
9453
9454       default:
9455         printf("?Not implemented");
9456         return(-2);
9457     }
9458 }
9459 #endif /* NOSPL */
9460
9461 #ifndef NOXFER
9462 /*  D O X G E T  --  GET command parser with switches  */
9463
9464 #ifdef CK_LABELED
9465 int g_lf_opts = -1;
9466 extern int lf_opts;
9467 #endif /* CK_LABELED */
9468
9469 int
9470 doxget(cx) int cx; {
9471     extern int                          /* External variables we need */
9472 #ifdef RECURSIVE
9473       recursive,
9474 #endif /* RECURSIVE */
9475       xfermode, fdispla, protocol, usepipes,
9476       g_binary, g_xfermode, g_displa, g_rpath, g_usepipes;
9477     extern char * rcv_move;             /* Directory to move new files to */
9478     extern char * rcv_rename;           /* What to rename new files to */
9479     extern char * rcvexcept[];          /* RECEIVE / GET exception list */
9480     int opkt  =  0;                     /* Flag for O-Packet needed */
9481
9482 #ifdef PIPESEND
9483     extern int pipesend;
9484     extern char * rcvfilter;
9485 #endif /* PIPESEND */
9486     extern struct keytab rpathtab[];
9487     extern int nrpathtab;
9488     extern CK_OFF_T calibrate;
9489     int asname = 0;                     /* Flag for have as-name */
9490     int konly = 0;                      /* Kermit-only function */
9491     int c, i, n, confirmed = 0;         /* Workers */
9492     int getval = 0;                     /* Whether to get switch value */
9493     int rcvcmd = 0;                     /* Whether it is the RECEIVE command */
9494     int mget = 0;                       /* Whether it is the MGET command */
9495     struct stringint pv[SND_MAX+1];    /* Temporary array for switch values */
9496     struct FDB sw, fl, cm;              /* FDBs for each parse function */
9497     char * cmdstr = "this command";
9498
9499 #ifdef NEWFTP
9500     if (cx == XXGET || cx == XXREGET || cx == XXMGET || cx == XXRETR) {
9501         extern int ftpget;
9502         extern int ftpisopen();
9503         if ((ftpget == 1) || ((ftpget == 2) && ftpisopen())) {
9504             int x;
9505             x = doftpget(cx,0);
9506             debug(F101,"doftpget return","",x);
9507             if (x > -1)
9508               success = x;
9509             debug(F101,"doftpget success","",success);
9510             return(x);
9511         }
9512     }
9513 #endif /* NEWFTP */
9514
9515     debug(F101,"xget cx","",cx);
9516
9517     oopts = -1;
9518     omode = -1;
9519
9520     for (i = 0; i <= SND_MAX; i++) {    /* Initialize switch values */
9521         pv[i].sval = NULL;
9522         pv[i].ival = -1;
9523         pv[i].wval = (CK_OFF_T)-1;
9524     }
9525     /* Preset switch values based on top-level command that called us */
9526
9527     switch (cx) {
9528       case XXREC:                       /* RECEIVE */
9529         cmdstr = "RECEIVE";
9530         rcvcmd = 1; break;
9531       case XXGET:                       /* GET */
9532         cmdstr = "GET";
9533         konly = 1;
9534         break;
9535 #ifdef CK_RESEND
9536       case XXREGET:                     /* REGET */
9537         cmdstr = "REGET";
9538         konly = 1;
9539         pv[SND_BIN].ival = 1;           /* Implies /BINARY */
9540         pv[SND_RES].ival = 1; break;
9541 #endif /* CK_RESEND */
9542       case XXRETR:                      /* RETRIEVE */
9543         cmdstr = "RETRIEVE";
9544         konly = 1;
9545         pv[SND_DEL].ival = 1; break;
9546 #ifdef PIPESEND
9547       case XXCREC:                      /* CRECEIVE */
9548         cmdstr = "CRECEIVE";
9549         konly = 1;
9550         rcvcmd = 1;
9551         pv[SND_CMD].ival = 1; break;
9552       case XXCGET:                      /* CGET */
9553         cmdstr = "CGET";
9554         konly = 1;
9555         pv[SND_CMD].ival = 1; break;
9556 #endif /* PIPESEND */
9557 #ifndef NOMGET
9558       case XXMGET:                      /* MGET */
9559         cmdstr = "MGET";
9560         konly = 1;
9561         mget = 1; break;
9562 #endif /* NOMGET */
9563     }
9564     debug(F111,"xget rcvcmd",cmdstr,rcvcmd);
9565     debug(F101,"xget konly","",konly);
9566
9567 #ifdef CK_XYZ
9568     if (!rcvcmd && protocol != PROTO_K) {
9569         printf("?Sorry, %s works only with Kermit protocol\n",cmdstr);
9570         return(-9);
9571     }
9572 #endif /* CK_XYZ */
9573
9574     /* Set up chained parse functions... */
9575
9576     cmfdbi(&sw,                         /* First FDB - command switches */
9577            _CMKEY,                      /* fcode */
9578            rcvcmd ?
9579            "Optional name/template to store incoming files under, or switch" :
9580            "Remote filename, or switch", /* hlpmsg */
9581            "",                          /* default */
9582            "",                          /* addtl string data */
9583            rcvcmd ? nrcvtab : ngettab,  /* addtl numeric data 1: tbl size */
9584            4,                           /* addtl numeric data 2: 4 = cmswi */
9585            xxstring,                    /* Processing function */
9586            rcvcmd ? rcvtab : gettab,    /* Keyword table */
9587            &fl                          /* Pointer to next FDB */
9588            );
9589     if (rcvcmd || mget)                 /* RECEIVE or MGET */
9590       cmfdbi(&fl,
9591            _CMTXT,                      /* fcode */
9592            rcvcmd ?                     /* hlpmsg */
9593              "Output filename or Command" : /* Output filename */
9594              "File(s) to GET",              /* Files we are asking for */
9595            "",                          /* default */
9596            "",                          /* addtl string data */
9597            0,                           /* addtl numeric data 1 */
9598            0,                           /* addtl numeric data 2 */
9599 #ifdef COMMENT
9600 #ifdef CK_XYZ
9601            (protocol == PROTO_X || protocol == PROTO_XC) ?
9602              xxstring :
9603              (rcvcmd ? (xx_strp)0  : xxstring)
9604 #else
9605            rcvcmd ? (xx_strp)0  : xxstring /* Processing function */
9606 #endif /* CK_XYZ */
9607 #else  /* COMMENT */
9608            xxstring                     /* Always evaluate - fdc 2006/02/01 */
9609 #endif  /* COMMENT */
9610              ,
9611            NULL,
9612            &cm
9613            );
9614     else
9615       cmfdbi(&fl,                       /* Remote filename or command */
9616            _CMFLD,                      /* fcode */
9617            "Remote filename",           /* hlpmsg */
9618            "",                          /* default */
9619            "",                          /* addtl string data */
9620            0,                           /* addtl numeric data 1 */
9621            0,                           /* addtl numeric data 2 */
9622            xxstring,
9623            NULL,
9624            &cm
9625            );
9626     cmfdbi(&cm,                         /* Confirmation */
9627            _CMCFM,                      /* fcode */
9628            "",                          /* hlpmsg */
9629            "",                          /* default */
9630            "",                          /* addtl string data */
9631            0,                           /* addtl numeric data 1 */
9632            0,                           /* addtl numeric data 2 */
9633            NULL,
9634            NULL,
9635            NULL
9636            );
9637
9638     /* (See doxsend() for fuller commentary) */
9639
9640     while (1) {                         /* Parse 0 or more switches */
9641         x = cmfdb(&sw);                 /* Parse something */
9642         debug(F101,"xget cmfdb","",x);
9643         if (x < 0)                      /* Error */
9644           goto xgetx;                   /* or reparse needed */
9645         if (cmresult.fcode != _CMKEY)   /* Break out if not a switch */
9646           break;
9647         c = cmgbrk();                   /* Get break character */
9648         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
9649             printf("?This switch does not take an argument\n");
9650             x = -9;
9651             goto xgetx;
9652         }
9653         if (!getval && (cmgkwflgs() & CM_ARG)) {
9654             printf("?This switch requires an argument\n");
9655             x = -9;
9656             goto xgetx;
9657         }
9658         n = cmresult.nresult;           /* Numeric result = switch value */
9659         debug(F101,"xget switch","",n);
9660
9661         switch (n) {                    /* Process the switch */
9662 #ifdef PIPESEND
9663           case SND_CMD:                 /* These take no args */
9664             if (nopush) {
9665                 printf("?Sorry, system command access is disabled\n");
9666                 x = -9;
9667                 goto xgetx;
9668             } else if (rcvfilter) {
9669                 printf(
9670 "?Sorry, no GET /COMMAND when RECEIVE FILTER selected\n");
9671                 x = -9;
9672                 goto xgetx;
9673             }
9674             if (rcvcmd)
9675               sw.hlpmsg = "Command, or switch"; /* Change help message */
9676             /* Fall thru... */
9677 #endif /* PIPESEND */
9678
9679           case SND_REC:                 /* /RECURSIVE */
9680             pv[SND_PTH].ival = PATH_REL; /* Implies relative pathnames */
9681             pv[n].ival = 1;             /* Set the recursive flag */
9682             break;
9683
9684           case SND_RES:                 /* /RECOVER */
9685             pv[SND_BIN].ival = 1;       /* Implies /BINARY */
9686             pv[n].ival = 1;             /* Set the resend flag */
9687             break;
9688
9689           case SND_DEL:                 /* /DELETE */
9690           case SND_SHH:                 /* /QUIET */
9691           case SND_CAL:                 /* /CALIBRATE */
9692           case SND_XPA:                 /* /TRANSPARENT */
9693             pv[n].ival = 1;             /* Just set the appropriate flag */
9694             break;
9695
9696           case SND_PIP:                 /* /PIPES:{ON,OFF} */
9697             if (!getval) {
9698                 pv[n].ival = 1;
9699                 break;
9700             }
9701             if ((x = cmkey(onoff,2,"","on",xxstring)) < 0)
9702               goto xgetx;
9703             if (!nopush)
9704               pv[n].ival = x;
9705             break;
9706
9707           /* File transfer modes - each undoes the others */
9708
9709           case SND_BIN:                 /* Binary */
9710           case SND_TXT:                 /* Text */
9711           case SND_IMG:                 /* Image */
9712           case SND_LBL:                 /* Labeled */
9713             pv[SND_BIN].ival = 0;       /* Unset all */
9714             pv[SND_TXT].ival = 0;
9715             pv[SND_IMG].ival = 0;
9716             pv[SND_LBL].ival = 0;
9717             pv[n].ival = 1;             /* Set the requested one */
9718             break;
9719
9720           case SND_EXC:                 /* Excludes */
9721             if (!getval) break;
9722             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
9723                 if (x == -3) {
9724                     printf("?Pattern required\n");
9725                     x = -9;
9726                 }
9727                 goto xgetx;
9728             }
9729             if (pv[n].sval) free(pv[n].sval);
9730             y = strlen(s);
9731             if (y > 256) {
9732                 printf("?Pattern too long - 256 max\n");
9733                 x = -9;
9734                 goto xgetx;
9735             }
9736             pv[n].sval = malloc(y+1);
9737             if (pv[n].sval) {
9738                 strcpy(pv[n].sval,s);   /* safe */
9739                 pv[n].ival = 1;
9740             }
9741             break;
9742
9743 #ifdef COMMENT
9744           /* Not implemented */
9745           case SND_PRI:                 /* GET to printer */
9746             pv[n].ival = 1;
9747             if (!getval) break;
9748             if ((x = cmfld("Print options","",&s,xxstring)) < 0)
9749               goto xgetx;
9750             pv[n].sval = malloc((int)strlen(s)+1);
9751             if (pv[n].sval)
9752               strcpy(pv[n].sval,s);     /* safe */
9753             break;
9754 #endif /* COMMENT */
9755
9756           case SND_MOV:                 /* MOVE after */
9757           case SND_REN:                 /* RENAME after */
9758             if (!getval) break;
9759             if ((x = cmfld(n == SND_MOV ?
9760            "device and/or directory for source file after sending" :
9761            "new name for source file after sending",
9762                            "",
9763                            &s,
9764                            n == SND_MOV ? xxstring : NULL
9765                            )) < 0) {
9766                 if (x == -3) {
9767                     printf("%s\n", n == SND_MOV ?
9768                            "?Destination required" :
9769                            "?New name required"
9770                            );
9771                     x = -9;
9772                 }
9773                 goto xgetx;
9774             }
9775             if (pv[n].sval) {
9776                 free(pv[n].sval);
9777                 pv[n].sval = NULL;
9778             }
9779             s = brstrip(s);
9780             y = strlen(s);
9781             if (y > 0) {
9782                 pv[n].sval = malloc(y+1);
9783                 if (pv[n].sval) {
9784                     strcpy(pv[n].sval,s); /* safe */
9785                     pv[n].ival = 1;
9786                 }
9787             }
9788             break;
9789
9790           case SND_ASN:                 /* As-name */
9791             if (!getval) break;
9792             if (mget) {
9793                 printf("?Sorry, as-name not allowed with MGET\n");
9794                 x = -9;
9795                 goto xgetx;
9796             }
9797             if (
9798 #ifdef COMMENT
9799                 (x = cmfld("Name to store it under","",&s,NULL))
9800 #else
9801                 (x = cmfld("Name to store it under","",&s,xxstring))
9802 #endif  /* COMMENT */
9803                 < 0)
9804               goto xgetx;
9805             s = brstrip(s);
9806             if ((y = strlen(s)) > 0) {
9807                 if (pv[n].sval) free(pv[n].sval);
9808                 pv[n].sval = malloc(y+1);
9809                 if (pv[n].sval) {
9810                     strcpy(pv[n].sval,s); /* safe */
9811                     pv[n].ival = 1;
9812                 }
9813             }
9814             break;
9815
9816 #ifdef PIPESEND
9817           case SND_FLT:                 /* Filter */
9818             debug(F101,"xget /filter getval","",getval);
9819             if (!getval) break;
9820             if ((x = cmfld("Filter program to receive through",
9821                            "",&s,NULL)) < 0) {
9822                 if (x == -3)
9823                   s = "";
9824                 else
9825                   goto xgetx;
9826             }
9827             if (*s) s = brstrip(s);
9828             y = strlen(s);
9829             for (x = 0; x < y; x++) {   /* Make sure they included "\v(...)" */
9830                 if (s[x] != '\\') continue;
9831                 if (s[x+1] == 'v') break;
9832             }
9833             if (x == y) {
9834                 printf(
9835                 "?Filter must contain a replacement variable for filename.\n"
9836                        );
9837                 x = -9;
9838                 goto xgetx;
9839             }
9840             pv[n].ival = 1;
9841             if (pv[n].sval) {
9842                 free(pv[n].sval);
9843                 pv[n].sval = NULL;
9844             }
9845             if ((y = strlen(s)) > 0) {
9846                 if ((pv[n].sval = malloc(y+1)))
9847                   strcpy(pv[n].sval,s); /* safe */
9848             }
9849             break;
9850 #endif /* PIPESEND */
9851
9852           case SND_PTH:                 /* Pathnames */
9853             if (!getval) {
9854                 pv[n].ival = PATH_REL;
9855                 break;
9856             }
9857             if ((x = cmkey(rpathtab,nrpathtab,"","on",xxstring)) < 0)
9858               goto xgetx;
9859             pv[n].ival = x;             /* Ditto */
9860             break;
9861
9862           case SND_NAM:                 /* Filenames */
9863             if (!getval) break;
9864             if ((x = cmkey(fntab,nfntab,"","converted",xxstring)) < 0)
9865               goto xgetx;
9866             pv[n].ival = x;
9867             break;
9868
9869           case SND_PRO:                 /* Protocol to use */
9870             if (!getval) break;
9871             if ((x = cmkey(protos,nprotos,"File-transfer protocol","",
9872                            xxstring)) < 0) {
9873                 if (x == -3)
9874                   x = 0;
9875                 else
9876                   goto xgetx;
9877             }
9878             debug(F111,"xget /proto",atmbuf,x);
9879             pv[n].ival = x;
9880             if (konly && x != PROTO_K) {
9881                 printf(
9882 "?Sorry, this command works only with Kermit protocol\n"
9883                        );
9884                 x = -9;
9885                 goto xgetx;
9886             }
9887             break;
9888
9889           default:
9890             printf("?Unexpected switch value - %d\n",cmresult.nresult);
9891             x = -9;
9892             goto xgetx;
9893         }
9894     }
9895     debug(F101,"xget cmresult fcode","",cmresult.fcode);
9896
9897     cmarg = line;                       /* Initialize string pointers */
9898     cmarg2 = tmpbuf;
9899     asname = 0;
9900     line[0] = NUL;                      /* and buffers. */
9901     tmpbuf[0] = NUL;
9902
9903     switch (cmresult.fcode) {           /* How did we get out of switch loop */
9904       case _CMFLD:                      /* (3) Remote filespec */
9905         ckstrncpy(line,cmresult.sresult,LINBUFSIZ);
9906         break;
9907       case _CMTXT:                      /* (4) As-name */
9908         if (rcvcmd) {
9909             ckstrncpy(tmpbuf,cmresult.sresult,TMPBUFSIZ);
9910             if ((int)strlen(tmpbuf) > 0)
9911               asname = 1;
9912         } else {
9913             ckstrncpy(line,cmresult.sresult,LINBUFSIZ);
9914         }
9915       case _CMCFM:                      /* (6) Confirmation */
9916         confirmed = 1;
9917         break;
9918       default:
9919         printf("?Unexpected function code: %d\n",cmresult.fcode);
9920         x = -9;
9921         goto xgetx;
9922     }
9923     debug(F110,"xget string",cmarg,0);
9924     debug(F101,"xget confirmed","",confirmed);
9925
9926     cmarg = brstrip(cmarg);             /* Strip any braces */
9927
9928     if (!confirmed) {                   /* CR not typed yet, get more fields */
9929         if (pv[SND_CMD].ival > 0) {
9930             debug(F100,"xget calling cmtxt","",0);
9931             x = cmtxt("Local command to pipe into","",&s,NULL);
9932             if (x < 0 && x != -3) goto xgetx;
9933             if (x != -3) {
9934                 ckstrncpy(tmpbuf,s,TMPBUFSIZ);
9935                 asname = 1;
9936             }
9937         } else if (!rcvcmd) {
9938 #ifdef VMS
9939             /* cmofi() fails if you give it a directory name */
9940             x = cmfld("Name or directory for incoming file","",&s,NULL);
9941             debug(F111,"xget cmfld",s,x);
9942 #else
9943             x = cmofi("Name or directory for incoming file","",&s,NULL);
9944             debug(F111,"xget cmofi",s,x);
9945 #endif /* VMS */
9946             if (x < 0 && x != -3) goto xgetx;
9947             if (x != -3) {
9948                 ckstrncpy(tmpbuf,s,TMPBUFSIZ);
9949                 if ((x = cmcfm()) < 0) goto xgetx;
9950                 asname = 1;
9951             }
9952         }
9953     }
9954     /* Arrive here with cmarg and cmarg2 all set */
9955
9956     debug(F111,"xget asname",cmarg2,asname);
9957     if (!asname) {
9958         if (pv[SND_ASN].sval)
9959           ckstrncpy(tmpbuf,pv[SND_ASN].sval,TMPBUFSIZ);
9960         else
9961           tmpbuf[0] = NUL;
9962     }
9963     cmarg2 = brstrip(cmarg2);           /* Strip outer braces if any. */
9964     debug(F110,"xget cmarg",cmarg,0);
9965     debug(F110,"xget cmarg2",cmarg2,0);
9966
9967     if (!*cmarg &&
9968         (cx == XXGET || cx == XXREGET || cx == XXCGET || cx == XXMGET)) {
9969         printf("?A remote file specification is required\n");
9970         x = -9;
9971         goto xgetx;
9972     }
9973 #ifdef PIPESEND
9974     if (pv[SND_CMD].ival > 0) {         /* /COMMAND sets pipesend flag */
9975         x = -9;
9976         if (!*cmarg2) {
9977             printf("?Command required\n");
9978             goto xgetx;
9979         } else if (nopush) {
9980             printf("?Sorry, system command access is disabled\n");
9981             goto xgetx;
9982         } else if (rcvfilter) {
9983             printf("?Sorry, no GET /COMMAND while RECEIVE FILTER selected\n");
9984             goto xgetx;
9985         } else
9986           pipesend = 1;
9987     }
9988     debug(F101,"xget /COMMAND pipesend","",pipesend);
9989 #endif /* PIPESEND */
9990
9991 #ifdef CK_RESEND
9992     if (pv[SND_RES].ival > 0) {         /* REGET or GET /RECOVER */
9993 #ifdef RECURSIVE
9994         if (pv[SND_REC].ival > 0) {     /* RECURSIVE */
9995 #ifdef COMMENT
9996             printf("?Unsupported option combination: /RECOVER /RECURSIVE\n");
9997             x = -9;
9998             goto xgetx;
9999 #else
10000             opkt = 1;
10001 #endif /* COMMENT */
10002         }
10003 #endif /* RECURSIVE */
10004         if (pv[SND_DEL].ival > 0) {     /* /DELETE */
10005 #ifdef COMMENT
10006             printf("?Unsupported option combination: /RECOVER /DELETE\n");
10007             x = -9;
10008             goto xgetx;
10009 #else
10010             opkt = 1;
10011 #endif /* COMMENT */
10012         }
10013     }
10014 #endif /* CK_RESEND */
10015
10016     if (pv[SND_EXC].ival > 0)           /* /EXCEPT */
10017       makelist(pv[SND_EXC].sval,rcvexcept,NSNDEXCEPT);
10018
10019 #ifdef IKS_OPTION
10020     if (!rcvcmd
10021 #ifdef CK_XYZ
10022          && protocol == PROTO_K
10023 #endif /* CK_XYZ */
10024          ) {
10025         if (!iks_wait(KERMIT_REQ_START,1)) {
10026             printf(
10027               "?A Kermit Server is not available to process this command\n");
10028             x = -9;                     /* correct the return code */
10029             goto xgetx;
10030         }
10031     }
10032 #endif /* IKS_OPTION */
10033
10034 #ifdef CK_XYZ
10035     {
10036         int po, pg;                     /* (for clarity) */
10037         po = pv[SND_PRO].ival;          /* /PROTOCOL option */
10038         pg = protocol;                  /* Protocol global  */
10039         if ((rcvcmd && !*cmarg2) &&     /* If no as-name was given */
10040             /* and /PROTOCOL is XMODEM or global protocol is XMODEM... */
10041             ((po <  0 && (pg == PROTO_X || pg == PROTO_XC)) ||
10042              (po > -1 && (po == PROTO_X || po == PROTO_XC)))
10043             ) {
10044             printf(
10045 "Sorry, you must specify a name when receiving a file with XMODEM protocol\n"
10046                    );
10047             x = -9;
10048             goto xgetx;
10049         }
10050     }
10051 #endif /* CK_XYZ */
10052
10053 #ifdef RECURSIVE
10054     if (pv[SND_REC].ival > 0) {         /* RECURSIVE */
10055         recursive = 1;
10056         pv[SND_PTH].ival = PATH_REL;    /* Implies relative pathnames too */
10057     }
10058 #endif /* RECURSIVE */
10059
10060     if (pv[SND_PIP].ival > -1) {
10061         g_usepipes = usepipes;
10062         usepipes = pv[SND_PIP].ival;
10063     }
10064
10065     /* Save global protocol parameters */
10066
10067     g_proto = protocol;
10068 #ifdef CK_LABELED
10069     g_lf_opts = lf_opts;                /* Save labeled transfer options */
10070 #endif /* CK_LABELED */
10071     g_urpsiz = urpsiz;                  /* Receive packet length */
10072     g_spsizf = spsizf;                  /* Send packet length flag */
10073     g_spsiz = spsiz;                    /* Send packet length */
10074     g_spsizr = spsizr;                  /* etc etc */
10075     g_spmax = spmax;
10076     g_wslotr = wslotr;
10077     g_prefixing = prefixing;
10078     g_fncact = fncact;
10079     g_fncnv = fncnv;
10080     g_fnspath = fnspath;
10081     g_fnrpath = fnrpath;
10082     g_xfrxla = xfrxla;
10083
10084     if (pv[SND_PRO].ival > -1) {        /* Change according to switch */
10085         protocol = pv[SND_PRO].ival;
10086         if (ptab[protocol].rpktlen > -1)   /* copied from initproto() */
10087             urpsiz = ptab[protocol].rpktlen;
10088         if (ptab[protocol].spktflg > -1)
10089             spsizf = ptab[protocol].spktflg;
10090         if (ptab[protocol].spktlen > -1) {
10091             spsiz = ptab[protocol].spktlen;
10092             if (spsizf)
10093                 spsizr = spmax = spsiz;
10094         }
10095         if (ptab[protocol].winsize > -1)
10096             wslotr = ptab[protocol].winsize;
10097         if (ptab[protocol].prefix > -1)
10098             prefixing = ptab[protocol].prefix;
10099         if (ptab[protocol].fnca > -1)
10100             fncact  = ptab[protocol].fnca;
10101         if (ptab[protocol].fncn > -1)
10102             fncnv   = ptab[protocol].fncn;
10103         if (ptab[protocol].fnsp > -1)
10104             fnspath = ptab[protocol].fnsp;
10105         if (ptab[protocol].fnrp > -1)
10106             fnrpath = ptab[protocol].fnrp;
10107     }
10108     debug(F101,"xget protocol","",protocol);
10109     debug(F111,"xget cmarg2",cmarg2,xfermode);
10110
10111     g_xfermode = xfermode;
10112     g_binary = binary;
10113     if (pv[SND_BIN].ival > 0) {         /* Change according to switch */
10114         xfermode = XMODE_M;
10115         binary = XYFT_B;                /* FILE TYPE BINARY */
10116         omode = GMOD_BIN;               /* O-Packet mode */
10117         debug(F101,"doxget /BINARY xfermode","",xfermode);
10118     } else if (pv[SND_TXT].ival > 0) {  /* Ditto for /TEXT */
10119         xfermode = XMODE_M;
10120         binary = XYFT_T;
10121         omode = GMOD_TXT;
10122         debug(F101,"doxget /TEXT xfermode","",xfermode);
10123     } else if (pv[SND_IMG].ival > 0) {
10124         xfermode = XMODE_M;
10125 #ifdef VMS
10126         binary = XYFT_I;
10127 #else
10128         binary = XYFT_B;
10129 #endif /* VMS */
10130         omode = GMOD_TXT;
10131         debug(F101,"doxget /IMAGE xfermode","",xfermode);
10132     }
10133 #ifdef CK_LABELED
10134     else if (pv[SND_LBL].ival > 0) {
10135         xfermode = XMODE_M;
10136         binary = XYFT_L;
10137         omode = GMOD_LBL;
10138         debug(F101,"doxget /LABELED xfermode","",xfermode);
10139     }
10140 #endif /* CK_LABELED */
10141     debug(F101,"xget binary","",binary);
10142     debug(F101,"xget omode","",omode);
10143
10144     if (pv[SND_XPA].ival > 0)           /* /TRANSPARENT */
10145       xfrxla = 0;                       /* Don't translate character sets */
10146
10147 #ifdef PIPESEND
10148     if (pv[SND_FLT].ival > 0)
10149       makestr(&rcvfilter,pv[SND_FLT].sval);
10150 #endif /* PIPESEND */
10151
10152 #ifdef CK_TMPDIR
10153     if (pv[SND_MOV].ival > 0) {
10154         int len;
10155         char * p = pv[SND_MOV].sval;
10156 #ifdef CK_LOGIN
10157         if (isguest) {
10158             printf("?Sorry, /MOVE-TO not available to guests\n");
10159             x = -9;
10160             goto xgetx;
10161         }
10162 #endif /* CK_LOGIN */
10163         len = strlen(p);
10164         if (!isdir(p)) {                /* Check directory */
10165 #ifdef CK_MKDIR
10166             char * s = NULL;
10167             s = (char *)malloc(len + 4);
10168             if (s) {
10169                 strcpy(s,p);            /* safe */
10170 #ifdef datageneral
10171                 if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; }
10172 #else
10173                 if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; }
10174 #endif /* datageneral */
10175                 s[len++] = 'X';
10176                 s[len] = NUL;
10177                 x = zmkdir(s);
10178                 free(s);
10179                 if (x < 0) {
10180                     printf("?Can't create \"%s\"\n",p);
10181                     x = -9;
10182                     goto xgetx;
10183                 }
10184             }
10185 #else
10186             printf("?Directory \"%s\" not found\n",p);
10187             x = -9;
10188             goto xgetx;
10189 #endif /* CK_MKDIR */
10190         }
10191         zfnqfp(p,LINBUFSIZ,line);
10192         makestr(&rcv_move,line);
10193     }
10194 #endif /* CK_TMPDIR */
10195
10196     if (pv[SND_REN].ival > 0) {         /* /RENAME-TO:name */
10197         char * p = pv[SND_REN].sval;
10198 #ifdef CK_LOGIN
10199         if (isguest) {
10200             printf("?Sorry, /RENAME-TO not available to guests\n");
10201             x = -9;
10202             goto xgetx;
10203         }
10204 #endif /* CK_LOGIN */
10205         if (!p) p = "";
10206         if (!*p) {
10207             printf("?New name required for /RENAME\n");
10208             x = -9;
10209             goto xgetx;
10210         }
10211         p = brstrip(p);
10212         makestr(&rcv_rename,p);
10213         debug(F110,"xget rcv_rename","",0);
10214     }
10215
10216 #ifdef CALIBRATE
10217     if (pv[SND_CAL].ival > 0)
10218       calibrate = (CK_OFF_T)1;
10219 #endif /* CALIBRATE */
10220     g_displa = fdispla;
10221     if (pv[SND_SHH].ival > 0)
10222       fdispla = 0;
10223     debug(F101,"xget display","",fdispla);
10224
10225     if (pv[SND_NAM].ival > -1) {        /* /FILENAMES */
10226         g_fncnv = fncnv;                /* Save global value */
10227         fncnv = pv[SND_NAM].ival;
10228         debug(F101,"xsend fncnv","",fncnv);
10229         /* We should also handle O packet filename option here */
10230         /* but we don't really need to since WHATAMI already handles it */
10231     }
10232     if (pv[SND_PTH].ival > -1) {        /* PATHNAMES */
10233         g_rpath = fnrpath;              /* Save global values */
10234         fnrpath = pv[SND_PTH].ival;
10235         debug(F101,"xsend fnrpath","",fnrpath);
10236 #ifndef NZLTOR
10237         if (fnrpath != PATH_OFF) {
10238             g_fncnv = fncnv;
10239             fncnv = XYFN_L;
10240             debug(F101,"xsend fncnv","",fncnv);
10241         }
10242         /* We should also handle O packet pathname option here */
10243         /* but we don't really need to since WHATAMI already handles it */
10244 #endif /* NZLTOR */
10245     }
10246
10247     /* Set protocol start state */
10248
10249     if (opkt) {                         /* Extended GET Options*/
10250         sstate = (CHAR) 'o';
10251         oopts = 0;
10252         if (pv[SND_DEL].ival > 0) oopts |= GOPT_DEL; /* GET /DELETE */
10253         if (pv[SND_RES].ival > 0) oopts |= GOPT_RES; /* GET /RECOVER */
10254         if (pv[SND_REC].ival > 0) oopts |= GOPT_REC; /* GET /RECURSIVE */
10255     } else if (rcvcmd)
10256       sstate = (CHAR) 'v';              /* RECEIVE or CRECEIVE */
10257     else if (pv[SND_DEL].ival > 0)
10258       sstate = (CHAR) 'h';              /* GET /DELETE (= RETRIEVE) */
10259     else if (pv[SND_RES].ival > 0)
10260       sstate = (CHAR) 'j';              /* GET /RECOVER (= REGET) */
10261     else
10262       sstate = (CHAR) 'r';              /* Regular GET */
10263     getcmd = 1;
10264     debug(F000,"xget sstate","",sstate);
10265 #ifdef MAC
10266     what = W_RECV;
10267     scrcreate();
10268 #endif /* MAC */
10269     if (local) {
10270         if (pv[SND_SHH].ival != 0)
10271           displa = 1;
10272         if (protocol == PROTO_K)        /* fdc 20070108 */
10273           ttflui();
10274     }
10275     x = 0;
10276 #ifdef PIPESEND
10277     if (pipesend)
10278       goto xgetx;
10279 #endif /* PIPESEND */
10280
10281 #ifdef CK_TMPDIR
10282 /*
10283   cmarg2 is also allowed to be a device or directory name;
10284   even the name of a directory that doesn't exist.
10285 */
10286     y = strlen(cmarg2);
10287     debug(F111,"xget strlen(cmarg2)",cmarg2,y);
10288     if ((y > 0) &&
10289 #ifdef OS2
10290         ((isalpha(cmarg2[0]) &&
10291          cmarg2[1] == ':' &&
10292          cmarg2[2] == NUL) ||
10293         (cmarg[y-1] == '/' || cmarg[y-1] == '\\') ||
10294         isdir(cmarg2))
10295 #else
10296 #ifdef UNIXOROSK
10297         (cmarg2[y-1] == '/' || isdir(cmarg2))
10298 #else
10299 #ifdef VMS
10300         (cmarg2[y-1] == ']' || cmarg2[y-1] == '>' || isdir(cmarg2))
10301 #else
10302 #ifdef STRATUS
10303         (cmarg2[y-1] == '>' || isdir(cmarg2))
10304 #else
10305 #ifdef datageneral
10306         (cmarg2[y-1] == ':' || cmarg[0] == ':' || isdir(cmarg2))
10307 #else
10308         isdir(cmarg2)
10309 #endif /* datageneral */
10310 #endif /* STRATUS */
10311 #endif /* VMS */
10312 #endif /* UNIXOROSK */
10313 #endif /* OS2 */
10314         ) {
10315         debug(F110,"doxget RECEIVE cmarg2 disk or dir",cmarg2,0);
10316         if (!f_tmpdir) {
10317             int x;
10318             s = zgtdir();
10319             if (s) {
10320                 ckstrncpy(savdir,s,TMPDIRLEN); /* remember old disk/dir */
10321                 f_tmpdir = 1;   /* and that we did this */
10322             } else {
10323                 printf("?Can't get current directory\n");
10324                 cmarg2 = "";
10325                 f_tmpdir = 0;
10326                 x = -9;
10327                 goto xgetx;
10328             }
10329 #ifdef CK_MKDIR
10330             x = zchki(cmarg2);          /* Does as-name exist? */
10331             if (x == -1) {              /* Doesn't exist */
10332                 char * p = NULL;        /* Try to create it */
10333                 x = strlen(cmarg2);
10334                 if ((p = (char *)malloc(x+4))) {
10335                     sprintf(p,"%s%s",cmarg2,"x.x"); /* SAFE (prechecked) */
10336                     x = zmkdir(p);
10337                     free(p);
10338                     if (x < 0) {
10339                         printf("?Can't create %s\n",cmarg2);
10340                         x = -9;
10341                         goto xgetx;
10342                     }
10343                 }
10344             }
10345 #endif /* CK_MKDIR */
10346             if (!zchdir(cmarg2)) {      /* change to given disk/directory, */
10347                 printf("?Can't access %s\n",cmarg2);
10348                 x = -9;
10349                 goto xgetx;
10350             }
10351             cmarg2 = "";
10352         }
10353     }
10354 #endif /* CK_TMPDIR */
10355
10356     ckstrncpy(fspec,cmarg,CKMAXPATH);   /* Note - this is a REMOTE filespec */
10357     debug(F111,"xget fspec",fspec,fspeclen);
10358     debug(F110,"xget cmarg2",cmarg2,0);
10359
10360   xgetx:
10361     for (i = 0; i < SND_MAX; i++)
10362       if (pv[i].sval)
10363         free(pv[i].sval);
10364     return(x);
10365 }
10366 #endif /* NOXFER */
10367
10368 #ifndef NOSPL
10369
10370 /*
10371   D O G T A  --  Do _GETARGS or _PUTARGS Command.
10372
10373   Used by XIF, FOR, WHILE, and SWITCH, each of which are implemented as
10374   2-level macros; the first level defines the macro, the second runs it.
10375   This routine hides the fact that they are macros by importing the
10376   macro arguments (if any) from two levels up, to make them available
10377   in the IF, FOR, SWITCH, and WHILE commands themselves; for example as
10378   loop indices, etc, and within the IF/FOR/WHILE/SWITCH body itself.
10379   _PUTARGS is in case we changed any of these variables or used SHIFT
10380   on them, so the new values won't be lost as we pop up the stack.
10381 */
10382 int
10383 dogta(cx) int cx; {
10384     int i, n;
10385     char c, *p,  mbuf[4];
10386     extern int topargc, cmdint;
10387     extern char ** topxarg;
10388
10389     if ((y = cmcfm()) < 0)
10390       return(y);
10391     debug(F101,"dogta cx","",cx);
10392     debug(F101,"dogta maclvl","",maclvl);
10393     if (cx == XXGTA) {
10394         debug(F101,"dogta _GETARGS maclvl","",maclvl);
10395     } else if (cx == XXPTA) {
10396         debug(F101,"dogta _PUTARGS maclvl","",maclvl);
10397     } else {
10398         return(-2);
10399     }
10400     if (maclvl < 1)
10401       return(success = 0);
10402
10403     /* Make new copies of macro arguments /%0..9 */
10404
10405     mbuf[0] = '%'; mbuf[1] = '0'; mbuf[2] = NUL; /* Argument name buf */
10406
10407     if (cx == XXPTA) {                  /* Go NOINT because _PUTARGS */
10408         if (cmdint)                     /* temporarily changes maclvl. */
10409           connoi();                     /* Interrupts OFF. */
10410     }
10411     for (i = 0; i < 10; i++) {          /* For all args */
10412         c = (char) (i + '0');           /* Make name */
10413         mbuf[1] = (char) c;             /* Insert digit */
10414         if (cx == XXGTA) {              /* Get arg from level-minus-2 */
10415             if (maclvl == 1) p = g_var[c]; /* If at level 1 use globals 0..9 */
10416             else p = m_arg[maclvl-2][i];   /* Otherwise they're on the stack */
10417             addmac(mbuf,p);
10418 #ifdef COMMENT
10419             if (maclvl > 1)
10420               makestr(&(m_line[maclvl]),m_line[maclvl-2]);
10421 #endif /* COMMENT */
10422         } else if (cx == XXPTA) {       /* Put args level+2 */
10423             maclvl -= 2;                /* This is gross, it's because we're */
10424             addmac(mbuf,m_arg[maclvl+2][i]); /* adding macros two levels up */
10425             maclvl += 2;                     /* and addmac() uses maclvl. */
10426             count[cmdlvl - 2]  = count[cmdlvl];
10427             intime[cmdlvl - 2] = intime[cmdlvl];
10428             inpcas[cmdlvl - 2] = inpcas[cmdlvl];
10429             takerr[cmdlvl - 2] = takerr[cmdlvl];
10430             merror[cmdlvl - 2] = merror[cmdlvl];
10431             xquiet[cmdlvl - 2] = xquiet[cmdlvl];
10432             xvarev[cmdlvl - 2] = xvarev[cmdlvl];
10433         } else return(success = 0);     /* Bad call to this routine */
10434     }
10435     if (cx == XXPTA) {                  /* Restore interrupts if we */
10436         if (cmdint)                     /* turned them off above. */
10437           conint(trap,stptrap);
10438     }
10439     /* Now take care of the argument vector array \&_[], \v(return), */
10440     /* and \v(argc) by just copying the pointers. */
10441
10442     if (cx == XXGTA) {                  /* GETARGS from 2 levels up */
10443         if (maclvl == 1) {
10444             a_ptr[0] = topxarg;         /* \&_[] array */
10445             a_dim[0] = topargc - 1;     /* Dimension doesn't include [0] */
10446             m_xarg[maclvl] = topxarg;
10447             n_xarg[maclvl] = topargc;   /* But \v(argc) does include \%0 */
10448             macargc[maclvl] = topargc;
10449             makestr(&(mrval[maclvl+1]),mrval[0]); /* (see vnlook()) */
10450         } else {
10451             a_ptr[0] = m_xarg[maclvl-2];
10452             a_dim[0] = n_xarg[maclvl-2];
10453             m_xarg[maclvl] = m_xarg[maclvl-2];
10454             n_xarg[maclvl] = n_xarg[maclvl-2];
10455             macargc[maclvl] = n_xarg[maclvl-2];
10456             makestr(&(mrval[maclvl+1]),mrval[maclvl-1]); /* (see vnlook()) */
10457
10458         }
10459     } else {                            /* PUTARGS 2 levels up */
10460         if (maclvl > 1) {
10461             a_ptr[0] = m_xarg[maclvl];
10462             m_xarg[maclvl-2] = m_xarg[maclvl];
10463             a_dim[0] = n_xarg[maclvl];
10464             n_xarg[maclvl-2] = n_xarg[maclvl];
10465             macargc[maclvl-2] = n_xarg[maclvl];
10466         }
10467     }
10468     return(1);                  /* Internal command - don't change success */
10469 }
10470 #endif /* NOSPL */
10471
10472 #ifndef NOSPL
10473 /*
10474   Do the GOTO and [_]FORWARD commands.
10475   s = Label to search for, cx = function code: XXGOTO, XXFWD, or XXXFWD.
10476 */
10477
10478 int
10479 dogoto(s, cx) char *s; int cx; {
10480     int i, j, x, y, z, bc;
10481     int empty = 0, stopflg = 0;
10482     char * cmd;                         /* Name of this command */
10483     char tmplbl[LBLSIZ+1], *lp;         /* Current label from command stream */
10484     char tmp2[LBLSIZ+1];                /* SWITCH label conversion buffer */
10485     char tmp3[LBLSIZ+1];                /* Target label */
10486
10487     stopflg = (cx == XXXFWD);           /* _FORWARD (used in SWITCH) */
10488     bc = 0;                             /* Brace counter */
10489
10490     cmd = (cx == XXGOTO) ? "GOTO" : ((cx == XXFWD) ? "FORWARD" : "_FORWARD");
10491     if (!s) s = "";
10492     if (!*s) empty = 1;
10493
10494 #ifdef DEBUG
10495     if (deblog) {
10496         debug(F111,"GOTO command",cmd,cx);
10497         debug(F101,"GOTO cmdlvl","",cmdlvl);
10498         debug(F101,"GOTO maclvl","",maclvl);
10499         debug(F101,"GOTO tlevel","",tlevel);
10500         debug(F111,"GOTO target",s,empty);
10501     }
10502 #endif /* DEBUG */
10503     debug(F110,cmd,s,0);
10504     x = ckstrncpy(tmp3+1,s,LBLSIZ);
10505     debug(F101,"GOTO target len","",x);
10506     debug(F101,"GOTO target at x","",s[x]);
10507     if (s[x]) {
10508         debug(F100,"GOTO target overflow","",0);
10509         printf("?GOTO target or SWITCH case label too long\n");
10510         if (stopflg) dostop();          /* If in SWITCH return to prompt */
10511         return(success = 0);
10512     }
10513     s = tmp3+1;
10514     if (*s != ':') {                    /* Make copy of label */
10515         tmp3[0] = ':';                  /* guaranteed to start with ":" */
10516         s--;
10517     }
10518     if (!stopflg && !empty) {
10519         if (s[1] == '.' || s[1] == SP || s[1] == NUL) {
10520             printf("?Bad label syntax - '%s'\n",s);
10521             if (stopflg) dostop();      /* If in SWITCH return to prompt */
10522             return(success = 0);
10523         }
10524     }
10525     if (cmdlvl == 0) {
10526         printf("?Sorry, %s only works in a command file or macro\n",cmd);
10527         return(success = 0);
10528     }
10529     y = strlen(s);                      /* y = length of target label */
10530     debug(F111,cmd,s,y);
10531
10532     while (cmdlvl > 0) {                /* As long as not at top level... */
10533         if (cmdstk[cmdlvl].src == CMD_MD) { /* GOTO inside macro */
10534             int i, m, flag;
10535             char *xp, *tp;
10536
10537             /* GOTO: rewind the macro; FORWARD: start at current position */
10538
10539             lp = (cx == XXGOTO) ? macx[maclvl] : macp[maclvl];
10540             m = (int)strlen(lp);
10541             debug(F111,"GOTO in macro",lp,m);
10542
10543             flag = 1;                   /* flag for valid label position */
10544             for (i = 0; i < m; i++,lp++) { /* search for label in macro body */
10545                 if (*lp == '{')         /* But only at this level */
10546                   bc++;                 /* Anything inside braces is off */
10547                 else if (*lp == '}')    /* limits. */
10548                   bc--;
10549                 if (stopflg && bc > 0)  /* This is good for SWITCH */
10550                   continue;             /* but interferes with WHILE, etc. */
10551                 if (*lp == ',') {
10552                     flag = 1;
10553                     continue;
10554                 }
10555                 if (flag) {             /* If in valid label position */
10556                     if (*lp == SP)      /* eat leading spaces */
10557                       continue;
10558                     if (*lp != ':') {   /* Look for label introducer */
10559                         flag = 0;       /* this isn't it */
10560                         continue;       /* keep looking */
10561                     }
10562                 }
10563                 if (!flag)              /* We don't have a label */
10564                   continue;             /*  so keep looking... */
10565                 xp = lp; tp = tmplbl;   /* Copy the label from the macro */
10566                 j = 0;                  /* to make it null-terminated */
10567                 while ((*tp = *xp)) {
10568                     if (j++ > LBLSIZ-1) { /* j = length of word from macro */
10569                         printf("?GOTO target or SWITCH case label too long\n");
10570                         if (stopflg) dostop(); /* Return to prompt */
10571                         return(success = 0);
10572                     }
10573                     if (!*tp || *tp == ',') /* Look for end of word */
10574                       break;
10575                     else tp++, xp++;    /* Next character */
10576                 }
10577                 *tp = NUL;              /* In case we stopped early */
10578                 /* Now do caseless string comparison, using longest length */
10579                 debug(F111,"macro GOTO label",s,y);
10580                 debug(F111,"macro target label",tmplbl,j);
10581                 if (stopflg) {          /* Allow variables as SWITCH labels */
10582                     int n = LBLSIZ - 1;
10583                     char * p = tmp2;
10584                     zzstring(tmplbl,&p,&n);
10585                     if (n < 0) {
10586                         printf("?GOTO target or SWITCH case label too long\n");
10587                         if (stopflg) dostop(); /* Return to prompt */
10588                         return(0);
10589                     }
10590                     ckstrncpy(tmplbl,tmp2,LBLSIZ);
10591                 }
10592                 debug(F111,"GOTO s",s,y);
10593                 debug(F111,"GOTO tmplbl",tmplbl,j);
10594                 debug(F111,"GOTO empty",ckitoa(stopflg),empty);
10595
10596                 if (empty) {               /* Empty target */
10597                     z = (!strcmp(s,":") && /* String is empty */
10598                          /* and Label is ":" or ":*"... */
10599                          (!strcmp(tmplbl,":") || !strcmp(tmplbl,":*")))
10600                         ? 0 : 1;
10601                     debug(F111,"GOTO","A",z);
10602                 } else if (stopflg) {
10603                     z = ckmatch(tmplbl,s,inpcas[cmdlvl],1) ? 0 : 1;
10604                     debug(F111,"GOTO","B",z);
10605                 } else {
10606                     z = (stopflg && inpcas[cmdlvl]) ?
10607                       strcmp(s,tmplbl) :
10608                         ckstrcmp(s,tmplbl,(y > j) ? y : j, 0);
10609                     debug(F111,"GOTO","C",z);
10610                 }
10611                 if (!z) {
10612                     break;
10613                 } else if (stopflg &&
10614                          !ckstrcmp(":default",tmplbl,(8 > j) ? 8 : j, 0)) {
10615                     debug(F100,"GOTO DEFAULT","",0);
10616                     break;
10617                 } else {
10618                     flag = 0;
10619                 }
10620             }
10621             debug(F111,"GOTO macro i",cmd,i);
10622             debug(F111,"GOTO macro m",cmd,m);
10623             if (i >= m) {               /* Didn't find the label */
10624                 debug(F101,"GOTO failed cmdlvl","",cmdlvl);
10625 #ifdef COMMENT
10626                 /* MOVED TO AFTER POPCLVL ABOUT 20 LINES DOWN 5 AUG 2002 */
10627                    if (stopflg)
10628                   return(0);
10629 #endif /* COMMENT */
10630                 if ((maclvl > 0) &&
10631                        (m_arg[maclvl-1][0]) &&
10632                        (cmdstk[cmdlvl].src == CMD_MD) &&
10633                        (!strncmp(m_arg[maclvl-1][0],"_xif",4) ||
10634                         !strncmp(m_arg[maclvl-1][0],"_for",4) ||
10635                         !strncmp(m_arg[maclvl-1][0],"_swi",4) ||
10636                         !strncmp(m_arg[maclvl-1][0],"_whi",4))) {
10637                     dogta(XXPTA);       /* Restore args */
10638                     debug(F101,"GOTO in XIF/FOR/WHI/SWI popping","",cmdlvl);
10639                     popclvl();          /* Pop an extra level */
10640                 }
10641                 debug(F101,"GOTO popping","",cmdlvl);
10642                 if (!popclvl()) {       /* pop up to next higher level */
10643                     printf("?Label '%s' not found\n",s); /* if none */
10644                     return(0);          /* Quit */
10645                 } else if (stopflg) {   /* SWITCH no case label match */
10646                     return(0);          /* and no DEFAULT lable. */
10647                 } else {
10648                     continue;        /* otherwise look again */
10649                 }
10650             }
10651             debug(F110,"GOTO found macro label",tmplbl,0);
10652             macp[maclvl] = lp;          /* set macro buffer pointer */
10653             return(1);
10654         } else if (cmdstk[cmdlvl].src == CMD_TF) {
10655             x = 0;                      /* GOTO issued in take file */
10656             debug(F111,"GOTO in TAKE file",cmd,cx);
10657             if (cx == XXGOTO) {         /* If GOTO, but not FORWARD, */
10658                 rewind(tfile[tlevel]);  /* search file from beginning */
10659                 tfline[tlevel] = 0;
10660             }
10661             while (! feof(tfile[tlevel])) {
10662 #ifdef COMMENT
10663 /* This is wrong - it lets us jump to labels inside inferior blocks */
10664                 tfline[tlevel]++;
10665                 if (fgets(line,LINBUFSIZ,tfile[tlevel]) == NULL) /* Get line */
10666 #else
10667                 if (getnct(line,LINBUFSIZ,tfile[tlevel],0) < 0)
10668 #endif /* COMMENT */
10669                   break;                /* If no more, done, label not found */
10670                 lp = line;              /* Got line */
10671                 while (*lp == SP || *lp == HT)
10672                   lp++;                 /* Strip leading whitespace */
10673                 if (*lp != ':') continue; /* Check for label introducer */
10674                 while (*(lp+1) == SP) { /* Remove space between : and name */
10675                     *(lp+1) = ':';
10676                     lp++;               /* Strip leading whitespace */
10677                 }
10678                 tp = lp;                /* Get end of word */
10679                 j = 0;
10680                 while (*tp) {           /* And null-terminate it */
10681                     if (*tp < 33) {
10682                         *tp = NUL;
10683                         break;
10684                     } else tp++, j++;
10685                 }
10686                 if (!ckstrcmp(lp,s,(y > j) ? y : j,0)) { /* Caseless compare */
10687                     x = 1;              /* Got it */
10688                     break;              /* done. */
10689                 } else if (stopflg &&
10690                            !ckstrcmp(":default",tmplbl,(8 > j) ? 8 : j,0)) {
10691                     x = 1;
10692                     break;
10693                 }
10694             }
10695             if (x == 0) {               /* If not found, print message */
10696                 debug(F101,"GOTO failed at cmdlvl","",cmdlvl);
10697                 if (stopflg)
10698                   return(0);
10699                 if (!popclvl()) {       /* pop up to next higher level */
10700                     printf("?Label '%s' not found\n",s); /* if none */
10701                     return(0);          /* quit */
10702                 } else continue;        /* otherwise look again */
10703             }
10704             return(x);                  /* Send back return code */
10705         }
10706     }
10707     printf("?Stack problem in GOTO %s\n",s); /* Shouldn't see this */
10708     return(0);
10709 }
10710 #endif /* NOSPL */
10711
10712 /* Finish parsing and do the IF, XIF, and WHILE commands */
10713
10714 #ifndef NOSPL
10715
10716 /*  C H K V A R  --  Check (if it's a) Variable  */
10717
10718
10719 #ifdef OLDCHKVAR
10720 /*
10721   Crude and disgusting, but needed for OS/2, DOS, and Windows, where filenames
10722   have backslashes in them.  How do we know if a backslash in a filename is a
10723   directory separator, or if it's a Kermit backslash?  This routine does a
10724   rough syntax check of the next few characters and if it looks like it MIGHT
10725   be a variable, then it tries to evaluate it, and if the result is not empty,
10726   we say it's a variable, although sometimes it might not be -- some cases are
10727   truly ambiguous.  For example there might a DOS directory called \%a, and
10728   we also have a variable with the same name.  This is all for the sake of not
10729   having to tell PC users that they have to double all backslashes in file
10730   and directory names.
10731 */
10732 #else
10733 /*
10734   Somewhat less crude & disgusting.  The previous method was nondeterministic
10735   and (worse) it interfered with macro argument passing.  So now we only
10736   check the syntax of backslash-items to see if they are variables, but we
10737   do NOT check their values.
10738 */
10739 #endif /* OLDCHKVAR */
10740 /*
10741   Call with a string pointer pointing at the backslash of the purported
10742   variable.  Returns 1 if it has the syntax of a variable, 0 if not.
10743 */
10744 int
10745 chkvar(s) char *s; {
10746     int z = 0;                          /* Return code - assume failure. */
10747     if (!s) s = "";                     /* Watch our for null pointers. */
10748     if (!*s) return(0);                 /* Empty arg so not a variable. */
10749     if (*s == CMDQ) {                   /* Object begins with backslash. */
10750         char c;
10751         c = s[1];                       /* Character following backslash. */
10752         if (c) {
10753             int t = 0;
10754             if (c == CMDQ)              /* Quoted backslash */
10755               return(1);
10756             c = (char) (islower(c) ? toupper(c) : c); /* Otherwise... */
10757             if (c == '%') {             /* Simple variable */
10758 #ifdef OLDCHKVAR
10759                 t = 1;
10760 #else
10761                 return(1);
10762 #endif /* OLDCHKVAR */
10763             } else if (c == '&') {      /* Array */
10764                 if (!s[2]) return(0);
10765                 if (s[3] == '[')
10766                   t = ckindex("]",s,4,0,1);
10767 #ifndef OLDCHKVAR
10768                 return((t > 0) ? 1 : 0);
10769 #endif /* OLDCHKVAR */
10770             } else if (c == '$' ||      /* Environment variable */
10771                        c == 'V' ||      /* Built-in variable */
10772                        c == 'M') {      /* Macro name */
10773                 t = (s[2] == '(');
10774 #ifndef OLDCHKVAR
10775                 return((t > 0) ? 1 : 0);
10776 #endif /* OLDCHKVAR */
10777             } else if (c == 'F') {      /* Function reference */
10778                 /* Don't actually call it - it might have side effects */
10779                 int x;
10780                 if ((x = ckindex("(",s,3,0,1))) /* Just check syntax */
10781                   if ((x = ckindex(")",s,x,0,1)))
10782                     z = 1;
10783                 /* Insert a better syntax check here if necessary */
10784             }
10785 #ifdef OLDCHKVAR
10786             if (t) {
10787                 t = 255;                /* This lets us test \v(xxx) */
10788                 lp = line;              /* and even \f...(xxx) */
10789                 zzstring(s,&lp,&t);     /* Evaluate it, whatever it is. */
10790                 t = strlen(line);       /* Get its length. */
10791                 debug(F111,"chkvar",line,t);
10792                 z = t > 0;              /* If length > 0, it's defined */
10793             }
10794 #endif /* OLDCHKVAR */
10795         }
10796     }
10797     return(z);
10798 }
10799
10800 /*  B O O L E X P  --  Evaluate a Boolean expression  */
10801
10802 #define BOOLLEN 1024
10803 static char boolval[BOOLLEN];
10804
10805 int
10806 boolexp(cx) int cx; {
10807     int x, y, z; char *s, *p;
10808     int parens = 0, pcount = 0, ecount = 0;
10809     char *q, *bx;
10810     struct FDB kw, nu;
10811 #ifdef FNFLOAT
10812     struct FDB fl;
10813     CKFLOAT f1 = 0.0, f2 = 0.0;
10814     int f1flag = 0, f2flag = 0;
10815 #endif /* FNFLOAT */
10816 #ifdef OS2
10817     extern int keymac;
10818 #endif /* OS2 */
10819
10820     not = 0;                            /* Flag for whether "NOT" was seen */
10821     z = 0;                              /* Initial IF condition */
10822     ifargs = 0;                         /* Count of IF condition words */
10823     bx = boolval;                       /* Initialize boolean value */
10824     *bx = NUL;
10825
10826   ifagain:
10827     cmfdbi(&kw,                         /* First FDB - command switches */
10828            _CMKEY,                      /* fcode */
10829            "Number, numeric-valued variable, Boolean expression, or keyword",
10830            "",                          /* default */
10831            "",                          /* addtl string data */
10832            nif,                         /* addtl numeric data 1: tbl size */
10833            0,                           /* addtl numeric data 2: 4 = silent */
10834            xxstring,                    /* Processing function */
10835            iftab,                       /* Keyword table */
10836            &nu                          /* Pointer to next FDB */
10837            );
10838     cmfdbi(&nu,                         /* 2nd FDB - An integer */
10839            _CMNUM,                      /* fcode */
10840            "",                          /* hlpmsg */
10841            "",                          /* Default */
10842            "",                          /* addtl string data */
10843            0,
10844            0,
10845            xxstring,
10846            NULL,
10847 #ifdef FNFLOAT
10848            &fl
10849 #else
10850            NULL
10851 #endif /* FNFLOAT */
10852            );
10853 #ifdef FNFLOAT
10854     cmfdbi(&fl,                         /* A floating-point number */
10855            _CMFLD,                      /* fcode */
10856            "",                          /* hlpmsg */
10857            "",                          /* default */
10858            "",                          /* addtl string data */
10859            0,                           /* addtl numeric data 1 */
10860            0,                           /* addtl numeric data 2 */
10861            xxstring,
10862            NULL,
10863            NULL
10864            );
10865 #endif /* FNFLOAT */
10866     x = cmfdb(&kw);                     /* Parse a keyword or a number */
10867     debug(F111,"boolval cmfdb","",x);
10868     if (x < 0) {
10869         if (x == -3)
10870           x = -2;
10871         return(x);
10872     }
10873     debug(F111,"boolval switch","",cmresult.fcode);
10874     switch (cmresult.fcode) {           /* What did we get? */
10875       case _CMFLD: {                    /* A "field" */
10876           int i;
10877           char * s;
10878           s = cmresult.sresult;
10879 /*
10880   C-Kermit 9.0: This allows a macro name to serve as an
10881   IF condition without having to enclose it in \m(...).
10882 */
10883           if (
10884 #ifdef FNFLOAT    
10885               !isfloat(cmresult.sresult,0) /* Not a number */
10886 #else
10887               !chknum(cmresult.sresult) /* Not a number */
10888 #endif  /* FNFLOAT */
10889               ) {
10890               i = mlook(mactab,cmresult.sresult,nmac); /* Look it up */
10891               if (i > -1)               /* in the macro table */
10892                 s = mactab[x].mval;     /* and get its value */
10893               else                      /* Otherwise if no such macro */
10894                 s = "0";                /* evaluate as FALSE. */
10895           }
10896 #ifdef FNFLOAT
10897         if (isfloat(s,0)) {             /* A floating-point number? */
10898             f1 = floatval;              /* Yes, get its value */
10899             f1flag = 1;                 /* remember we did this */
10900             ifc = 9999;                 /* Set special "if-code" */
10901         }
10902 #else
10903         if (chknum(s)) {
10904             cmresult.nresult = atoi(s);
10905             ifc = 9999;
10906             break;
10907         }
10908 #endif /* FNFLOAT */
10909         else
10910           return(-2);
10911       }
10912       case _CMNUM:                      /* A number... */
10913         ifc = 9999;                     /* Set special "if-code" */
10914         break;
10915       case _CMKEY:                      /* A keyword */
10916         ifc = cmresult.nresult;         /* Get if-code */
10917         break;
10918       default:
10919         return(-2);
10920     }
10921     switch (ifc) {                      /* set z = 1 for true, 0 for false */
10922       case 9999:                        /* Number */
10923 #ifdef FNFLOAT
10924         if (f1flag) {
10925             z = (f1 == 0.0) ? 0 : 1;
10926         } else
10927 #endif /* FNFLOAT */
10928         z = (cmresult.nresult == 0) ? 0 : 1;
10929         break;
10930       case XXIFLP:                      /* Left paren */
10931         if (pcount == 0 && ifargs > 0)
10932           return(-2);
10933         parens = 1;
10934         pcount++;
10935         ifargs++;
10936         *bx++ = '(';
10937         goto ifagain;
10938       case XXIFRP:                      /* Right paren */
10939         if (!parens)
10940           return(-2);
10941         if (--pcount < 0)
10942           return(-2);
10943         ifargs++;
10944         *bx++ = ')';
10945         *bx = NUL;
10946         if (pcount == 0)
10947           goto ifend;
10948         goto ifagain;
10949       case XXIFAN:                      /* AND (&&) */
10950         ifargs++;
10951         if (!ecount)
10952           return(-2);
10953         *bx++ = '&';
10954         goto ifagain;
10955       case XXIFOR:                      /* OR (||) */
10956         ifargs++;
10957         if (!ecount)
10958           return(-2);
10959         *bx++ = '|';
10960         goto ifagain;
10961       case XXIFNO:                      /* IF NOT [ NOT [ NOT ... ] ] */
10962         if (bx > boolval) {             /* evala() doesn't like cascaded */
10963             if (*(bx-1) == '!') {       /* unary operators... */
10964                 *(bx-1) = NUL;          /* So here, two wrongs make a right. */
10965                 bx--;
10966             } else {
10967                 *bx++ = '!';
10968             }
10969         } else {
10970             *bx++ = '!';
10971         }
10972         ifargs++;
10973         goto ifagain;
10974       case XXIFTR:                      /* IF TRUE */
10975         z = 1;
10976         debug(F101,"if true","",z);
10977         ifargs += 1;
10978         break;
10979       case XXIFNT:                      /* IF FALSE */
10980         z = 0;
10981         debug(F101,"if true","",z);
10982         ifargs += 1;
10983         break;
10984       case XXIFSU:                      /* IF SUCCESS */
10985         z = ( success != 0 ) ? 1 : 0;
10986         debug(F101,"if success","",z);
10987         ifargs += 1;
10988         break;
10989       case XXIFFA:                      /* IF FAILURE */
10990         z = ( success == 0 ) ? 1 : 0;
10991         debug(F101,"if failure","",z);
10992         ifargs += 1;
10993         break;
10994
10995       case XXIFDE:                      /* IF DEFINED */
10996         if ((x = cmfld("Macro or variable name","",&s,NULL)) < 0)
10997           return((x == -3) ? -2 : x);
10998
10999         if (*s == CMDQ) {
11000             char * lp;
11001             char line[256];
11002             int t, x;
11003             if (*(s+1) == 'f' || *(s+1) == 'F') { /* Built-in function */
11004                 extern struct keytab fnctab[];
11005                 extern int nfuncs;
11006                 ckstrncpy(line,s+2,256);
11007                 if (line[0]) {
11008                     lp = ckstrchr(line,'(');
11009                     if (lp) *lp = NUL;
11010                     x = lookup(fnctab,line,nfuncs,&t);
11011                     z = x > -1;
11012                 }
11013                 debug(F111,"if defined function",line,z);
11014             } else if (*(s+1) == 'v' || *(s+1) == 'V') { /* 8.0.200 */
11015                 extern struct keytab vartab[];
11016                 extern int nvars;
11017                 z = 0;
11018                 if (*(s+2) == '(') {
11019                     ckstrncpy(line,s+3,256);
11020                     if (line[0]) {
11021                         lp = ckstrchr(line,')');
11022                         if (lp) *lp = NUL;
11023                         x = lookup(vartab,line,nvars,&t);
11024                         z = x > -1;
11025                         if (z) {        /* 8.0.203 */
11026                             int t;      /* It must have a value to succeed */
11027                             t = 255;    /* as in C-Kermit 6.0 and 7.0 */
11028                             lp = line;  /* (this was broken in 8.0.200-201) */
11029                             zzstring(s,&lp,&t);
11030                             t = strlen(line);
11031                             z = line[0] ? 1 : 0;
11032                         }
11033                     }
11034                 }
11035                 debug(F111,"if defined variable",line,z);
11036             } else {
11037                 z = chkvar(s);          /* Starts with backslash */
11038                 if (z > 0) {            /* Yes... */
11039                     t = 255;            /* than buffer so if zzstring fails  */
11040                     lp = line;          /* check for that -- overflow means */
11041                     line[0] = NUL;      /* the quantity is defined. */
11042                     x = zzstring(s,&lp,&t);
11043                     if ((x < 0 && t != 255) || !line[0])
11044                       z = 0;
11045                     debug(F111,"if defined zzstring",line,z);
11046                     debug(F101,"if defined zzstring t","",t);
11047                 }
11048             }
11049         } else {
11050             z = (mxlook(mactab,s,nmac) > -1); /* Look for exact match */
11051         }
11052         debug(F111,"if defined final",s,z);
11053         ifargs += 2;
11054         break;
11055
11056       case XXIFDC: {                    /* IF DECLARED */
11057           char * lp;
11058           char line[32];
11059           int j, k, t, x;
11060           if ((x = cmfld("Array name","",&s,NULL)) < 0)
11061             return((x == -3) ? -2 : x);
11062           if (*s == CMDQ) {
11063               if (*(s+1) != '&') {
11064                   t = 31;
11065                   lp = line;
11066                   line[0] = NUL;
11067                   x = zzstring(s,&lp,&t);
11068                   s = line;
11069               }
11070           }
11071           z = 0;
11072           if ((x = arraybounds(s,&j,&k)) > -1) {
11073               if (a_ptr[x]) {
11074                   if (j < 1)
11075                     z = 1;
11076                   else if (j <= a_dim[x])
11077                     z = 1;
11078                   if (z == 1 && k > a_dim[x])
11079                     z = 0;
11080               }
11081           }
11082           break;
11083       }
11084       case XXIFBG:                      /* IF BACKGROUND */
11085       case XXIFFG:                      /* IF FOREGROUND */
11086         bgchk();                        /* Check background status */
11087         if (ifc == XXIFFG)              /* Foreground */
11088           z = pflag ? 1 : 0;
11089         else z = pflag ? 0 : 1;         /* Background */
11090         ifargs += 1;
11091         break;
11092
11093       case XXIFCO:                      /* IF COUNT */
11094         z = ( --count[cmdlvl] > 0 );
11095         if (cx == XXWHI) count[cmdlvl] += 2; /* Don't ask... */
11096         debug(F101,"if count","",z);
11097         ifargs += 1;
11098         break;
11099
11100       case XXIFEX:                      /* IF EXIST */
11101 #ifdef CK_TMPDIR
11102       case XXIFDI:                      /* IF DIRECTORY */
11103 #endif /* CK_TMPDIR */
11104       case XXIFAB:                      /* IF ABSOLUTE */
11105       case XXIFLN:                      /* IF LINK */
11106         if ((x = cmfld(
11107                        ((ifc == XXIFDI) ? "Directory name" : "File"),
11108                        "",&s,
11109 #ifdef OS2
11110                        NULL             /* This allows \'s in filenames */
11111 #else
11112                        xxstring
11113 #endif /* OS2 */
11114                        )) < 0) {
11115             if (x == -3) {
11116                 extern int cmflgs;
11117                 if (cmflgs == 1) {
11118                     printf("?File or directory name required\n");
11119                     return(-9);
11120                 }
11121             } else return(x);
11122         }
11123         s = brstrip(s);
11124 #ifdef UNIX
11125         if (ifc == XXIFLN) {
11126             z = isalink(s);
11127         } else
11128 #endif  /* UNIX */
11129         if (ifc == XXIFAB) {
11130             z = isabsolute(s);
11131         } else if (ifc == XXIFEX) {
11132             z = (zgetfs(s) > -1L);
11133             debug(F101,"if exist 1","",z);
11134 #ifdef OS2
11135             if (!z) {                   /* File not found. */
11136                 int t;                  /* Try expanding variables */
11137                 t = LINBUFSIZ-1;        /* and looking again. */
11138                 lp = line;
11139                 zzstring(s,&lp,&t);
11140                 s = line;
11141                 z = ( zchki(s) > -1L );
11142                 debug(F101,"if exist 2","",z);
11143             }
11144 #endif /* OS2 */
11145 #ifdef CK_TMPDIR
11146         } else {
11147 #ifdef VMS
11148             z = (zchki(s) == -2)
11149 #else
11150 /* Because this doesn't catch $DISK1:[FOO]BLAH.DIR;1 */
11151             z = isdir(s)
11152 #ifdef OS2
11153               || (isalpha(s[0]) && s[1] == ':' && s[2] == NUL)
11154 #endif /* OS2 */
11155 #endif /* VMS */
11156               ;
11157             debug(F101,"if directory 1","",z);
11158
11159             if (!z) {                   /* File not found. */
11160                 int t;                  /* Try expanding variables */
11161                 t = LINBUFSIZ-1;        /* and looking again. */
11162                 lp = line;
11163                 zzstring(s,&lp,&t);
11164                 s = line;
11165                 z = isdir(s)
11166 #ifdef OS2
11167                   || (isalpha(s[0]) && s[1] == ':' && s[2] == NUL)
11168 #endif /* OS2 */
11169                     ;
11170                 debug(F101,"if directory 2","",z);
11171             }
11172 #endif /* CK_TMPDIR */
11173         }
11174         ifargs += 2;
11175         break;
11176
11177       case XXIFEQ:                      /* IF EQUAL (string comparison) */
11178       case XXIFLL:                      /* IF Lexically Less Than */
11179       case XXIFLG:                      /* If Lexically Greater Than */
11180         if ((x = cmfld("first word or variable name","",&s,xxstring)) < 0) {
11181             if (x == -3) {
11182                 printf("?Text required\n");
11183                 return(-9);
11184             } else return(x);
11185         }
11186         s = brstrip(s);                 /* Strip braces */
11187         x = (int)strlen(s);
11188         if (x > LINBUFSIZ-1) {
11189             printf("?IF: strings too long\n");
11190             return(-2);
11191         }
11192         lp = line;                      /* lp points to first string */
11193         ckstrncpy(line,s,LINBUFSIZ);
11194         if ((y = cmfld("second word or variable name","",&s,xxstring)) < 0) {
11195             if (y == -3) {
11196                 printf("?Text required\n");
11197                 return(-9);
11198             } else return(y);
11199         }
11200         s = brstrip(s);
11201         y = (int)strlen(s);
11202         if (x + y + 2 > LINBUFSIZ) {
11203             printf("?IF: strings too long\n");
11204             return(-2);
11205         }
11206         tp = lp + x + 2;                /* tp points to second string */
11207         strcpy(tp,s);                   /* safe (checked) */
11208         x = ckstrcmp(lp,tp,-1,inpcas[cmdlvl]); /* Use longest length */
11209         switch (ifc) {
11210           case XXIFEQ:                  /* IF EQUAL (string comparison) */
11211             z = (x == 0);
11212             break;
11213           case XXIFLL:                  /* IF Lexically Less Than */
11214             z = (x < 0);
11215             break;
11216           case XXIFLG:                  /* If Lexically Greater Than */
11217             z = (x > 0);
11218             break;
11219         }
11220         debug(F101,"IF EQ result","",z);
11221         ifargs += 3;
11222         break;
11223
11224       case XXIFVE:                      /* IF VERSION */
11225       case XXIFAE:                      /* IF (arithmetically) = */
11226       case XXIFNQ:                      /* IF != (not arithmetically equal) */
11227       case XXIFLT:                      /* IF <  */
11228       case XXIFLE:                      /* IF <= */
11229       case XXIFGE:                      /* IF >= */
11230       case XXIFGT: {                    /* IF >  */
11231
11232           /* July 2006 - converted to use CK_OFF_T rather than int to */
11233           /* allow long integers on platforms that have ck_off_t > 32 bits */
11234           int xx;
11235           CK_OFF_T n1 = (CK_OFF_T)0, n2 = (CK_OFF_T)0;
11236         if (ifc == XXIFVE) {
11237             n1 = (CK_OFF_T) vernum;
11238         } else {
11239             x = cmfld("first number or variable name","",&s,xxstring);
11240             if (x == -3) {
11241                 printf("?Quantity required\n");
11242                 return(-9);
11243             }
11244             if (x < 0) return(x);
11245             debug(F101,"xxifgt cmfld","",x);
11246             ckstrncpy(line,s,LINBUFSIZ);
11247             lp = brstrip(line);
11248             debug(F110,"xxifgt exp1",lp,0);
11249
11250 /* The following bit is for compatibility with old versions of MS-DOS Kermit */
11251
11252             if (!ckstrcmp(lp,"count",5,0)) {
11253                 n1 = (CK_OFF_T)count[cmdlvl];
11254             } else if (!ckstrcmp(lp,"version",7,0)) {
11255                 n1 = (CK_OFF_T) vernum;
11256             } else if (!ckstrcmp(lp,"argc",4,0)) {
11257                 n1 = (CK_OFF_T) macargc[maclvl];
11258             } else {
11259
11260 /* End of compatibility bit */
11261
11262 #ifdef FNFLOAT
11263                 if (isfloat(lp,0) > 1) { /* Allow floating-point comparisons */
11264                     f1 = floatval;
11265                     f1flag = 1;
11266                 } else
11267 #endif /* FNFLOAT */
11268                   if (chknum(lp)) {
11269                       n1 = ckatofs(lp);
11270                   } else {              /* Check for arithmetic expression */
11271                       q = evala(lp);    /* cmnum() does this but ... */
11272                       if (chknum(q)) {  /* we're not using cmnum(). */
11273                           n1 = ckatofs(q);
11274                       } else {
11275                           printf("?Value not numeric - %s", lp);
11276                           return(-9);
11277                       }
11278                   }
11279             }
11280         }
11281         y = cmfld("number or variable name","",&s,xxstring);
11282         if (y == -3) {
11283             printf("?Quantity required\n");
11284             return(-9);
11285         }
11286         if (y < 0) return(y);
11287         s = brstrip(s);
11288         if (!*s) return(-2);
11289         if (ifc == XXIFVE) {
11290             tp = line;
11291         } else {
11292             x = (int)strlen(lp);
11293             tp = line + x + 2;
11294         }
11295         ckstrncpy(tp,s,LINBUFSIZ-x-2);
11296         debug(F110,"xxifgt exp2",tp,0);
11297         if (!ckstrcmp(tp,"count",5,0)) {
11298             n2 = (CK_OFF_T) count[cmdlvl];
11299         } else if (!ckstrcmp(tp,"version",7,0)) {
11300             n2 = (CK_OFF_T) vernum;
11301         } else if (!ckstrcmp(tp,"argc",4,0)) {
11302             n2 = (CK_OFF_T) macargc[maclvl];
11303         } else {
11304 #ifdef FNFLOAT
11305             if (isfloat(tp,0) > 1) {
11306                 f2 = floatval;
11307                 f2flag = 1;
11308             } else
11309 #endif /* FNFLOAT */
11310             if (chknum(tp)) {
11311                 n2 = ckatofs(tp);
11312             } else {
11313                 q = evala(tp);
11314                 if (chknum(q)) {        /* we're not using cmnum(). */
11315                     n2 = ckatofs(q);
11316                 } else {
11317                     printf("?Value not numeric - %s", tp);
11318                     return(-9);
11319                 }
11320             }
11321         }
11322         xx = (ifc == XXIFVE) ? XXIFGE : ifc;
11323
11324 #ifdef FNFLOAT
11325         if (f1flag && !f2flag) {
11326             f2 = (CKFLOAT)n2;
11327             f2flag = 1;
11328         }
11329         if (f2flag && !f1flag)
11330           f1 = (CKFLOAT)n1;
11331         if (f1flag)
11332           z = ((f1 <  f2 && xx == XXIFLT)
11333                || (f1 != f2 && xx == XXIFNQ)
11334                || (f1 <= f2 && xx == XXIFLE)
11335                || (f1 == f2 && xx == XXIFAE)
11336                || (f1 >= f2 && xx == XXIFGE)
11337                || (f1 >  f2 && xx == XXIFGT));
11338         else
11339 #endif /* FNFLOAT */
11340           z = ((n1 <  n2 && xx == XXIFLT)
11341                || (n1 != n2 && xx == XXIFNQ)
11342                || (n1 <= n2 && xx == XXIFLE)
11343                || (n1 == n2 && xx == XXIFAE)
11344                || (n1 >= n2 && xx == XXIFGE)
11345                || (n1 >  n2 && xx == XXIFGT));
11346         debug(F101,"xxifge z","",z);
11347         if (ifc == XXIFVE)
11348           ifargs += 2;
11349         else
11350           ifargs += 3;
11351         break;
11352       }
11353
11354       case XXIFNU:                      /* IF NUMERIC */
11355         x = cmfld("variable name or constant","",&s,NULL);
11356         if (x == -3) {
11357             extern int cmflgs;
11358             if (cmflgs == 1) {
11359                 printf("?Quantity required\n");
11360                 return(-9);
11361             }
11362         } else if (x < 0)
11363           return(x);
11364         x = LINBUFSIZ-1;
11365         lp = line;
11366         zzstring(s,&lp,&x);
11367         lp = line;
11368         debug(F110,"xxifnu quantity",lp,0);
11369         z = chknum(lp);
11370 #ifdef COMMENT
11371 /*
11372   This works, but it's not wise -- IF NUMERIC is mostly used to see if a
11373   string really does contain only numeric characters.  If they want to force
11374   evaluation, they can use \feval() on the argument string.
11375 */
11376         if (!z) {                       /* Not a number */
11377             x_ifnum = 1;                /* Avoid "eval" error messages */
11378             q = evala(lp);              /* Maybe it's an expression */
11379             z = chknum(q);              /* that evaluates to a number */
11380             x_ifnum = 0;                /* Put eval messages back to normal */
11381             if (z) debug(F110,"xxifnu exp",lp,0);
11382         }
11383 #endif /* COMMENT */
11384         debug(F101,"xxifnu chknum","",z);
11385         ifargs += 2;
11386         break;
11387
11388 #ifdef ZFCDAT
11389       case XXIFNE: {                    /* IF NEWER */
11390         char d1[20], * d2;              /* Buffers for 2 dates */
11391         if ((z = cmifi("First file","",&s,&y,xxstring)) < 0)
11392           return(z);
11393         ckstrncpy(d1,zfcdat(s),20);
11394         if ((z = cmifi("Second file","",&s,&y,xxstring)) < 0)
11395           return(z);
11396         d2 = zfcdat(s);
11397         if ((int)strlen(d1) != 17 || (int)strlen(d2) != 17) {
11398             printf("?Failure to get file date\n");
11399             return(-9);
11400         }
11401         debug(F110,"xxifnewer d1",d1,0);
11402         debug(F110,"xxifnewer d2",d2,0);
11403         z = (strcmp(d1,d2) > 0) ? 1 : 0;
11404         debug(F101,"xxifnewer","",z);
11405         ifargs += 2;
11406         break;
11407       }
11408 #endif /* ZFCDAT */
11409
11410 #ifdef CK_IFRO
11411       case XXIFRO:                      /* REMOTE-ONLY advisory */
11412         ifargs++;
11413 #ifdef NOLOCAL
11414         z = 1;
11415 #else
11416         z = remonly;
11417 #endif /* NOLOCAL */
11418         break;
11419 #endif /* CK_IFRO */
11420
11421       case XXIFAL:                      /* ALARM */
11422         ifargs++;
11423         debug(F101,"IF ALARM ck_alarm","",ck_alarm);
11424         debug(F110,"IF ALARM alrm_date",alrm_date,0);
11425         debug(F110,"IF ALARM alrm_time",alrm_time,0);
11426
11427         if (ck_alarm < 1L || alrm_date[0] < '0' || alrm_time[0] < '0') {
11428             z = 0;                      /* ALARM not SET */
11429             break;                      /* so IF ALARM fails */
11430         }
11431         /* Get current date and time */
11432         ckstrncpy(tmpbuf,ckcvtdate("",1),TMPBUFSIZ);
11433         s = tmpbuf;
11434         s[8] = NUL;
11435         z = (int) strncmp(tmpbuf,alrm_date,8); /* Compare dates */
11436         debug(F101,"IF ALARM date z","",z);
11437         if (z == 0) {                   /* Dates are the same */
11438             /* Compare times */
11439             z = (tod2sec(tmpbuf+9) >= atol(alrm_time)) ? 1 : -1;
11440             debug(F101,"IF ALARM time z","",z);
11441         }
11442         tmpbuf[0] = NUL;                /* z >= 0 if alarm is passed */
11443         z = ((z >= 0) ? 1 : 0);         /* z <  0 otherwise */
11444         debug(F101,"IF ALARM final z","",z);
11445         break;
11446
11447       case XXIFOP:                      /* IF OPEN */
11448         if ((x = cmkey(iotab,niot,"file or log","",xxstring)) < 0)
11449           return(x);
11450         if (x == 9999 || x == ZSTDIO) {
11451             bgchk();                    /* Check background status */
11452             z = pflag ? 1 : 0;
11453         } else if (x == 8888) {
11454             z = local ? ttchk() > -1 : 0;
11455 #ifdef CKLOGDIAL
11456         } else if (x == 7777) {
11457             extern int dialog;
11458             z = dialog ? 1 : 0;
11459 #endif /* CKLOGDIAL */
11460         } else
11461           z = (chkfn(x) > 0) ? 1 : 0;
11462         ifargs += 1;
11463         break;
11464
11465       case XXIFSD:                      /* Started-From-Dialer */
11466 #ifdef OS2
11467         z = StartedFromDialer;
11468 #else
11469         z = 0;
11470 #endif /* OS2 */
11471         break;
11472
11473       case XXIFTM:                      /* Terminal-Macro */
11474 #ifdef OS2
11475         z = cmdstk[cmdlvl].ccflgs & CF_KMAC;
11476 #else
11477         z = 0;
11478 #endif /* OS2 */
11479         break;
11480
11481       case XXIFEM:                      /* Emulation is active */
11482 #ifdef OS2
11483         z = 1;
11484 #else
11485         z = 0;
11486 #endif /* OS2 */
11487         break;
11488
11489       case XXIFIK:                      /* Running as IKSD? */
11490         z = inserver;
11491         break;
11492
11493       case XXIFTA:                      /* Connection is TAPI */
11494         z = 0;
11495 #ifndef NODIAL
11496 #ifdef CK_TAPI
11497         if (local && !network && tttapi)
11498           z = 1;
11499 #endif /* CK_TAPI */
11500 #endif /* NODIAL */
11501         break;
11502
11503       case XXIFMA:                      /* IF MATCH */
11504         x = cmfld("String or variable","",&s,xxstring);
11505         if (x == -3) {
11506             extern int cmflgs;
11507             if (cmflgs == 1) {
11508                 printf("?String required\n");
11509                 return(-9);
11510             }
11511         } else if (x < 0)
11512           return(x);
11513         ckstrncpy(line,s,LINBUFSIZ);
11514         s = brstrip(line);
11515         debug(F110,"xxifma string",line,0);
11516         x = cmfld("Pattern","",&p,xxstring);
11517         if (x == -3) {
11518             extern int cmflgs;
11519             if (cmflgs == 1) {
11520                 printf("?Pattern required\n");
11521                 return(-9);
11522             }
11523         } else if (x < 0)
11524           return(x);
11525         ckstrncpy(tmpbuf,p,TMPBUFSIZ);
11526         p = brstrip(tmpbuf);
11527         debug(F110,"xxifma pattern",tmpbuf,0);
11528         z = ckmatch(p,s,inpcas[cmdlvl],1);
11529         break;
11530
11531       case XXIFFL: {                    /* IF FLAG */
11532           extern int ooflag;
11533           z = ooflag;
11534           break;
11535       }
11536       case XXIFAV: {                    /* IF AVAILABLE */
11537           if ((x = cmkey(availtab,availtabn,"","",xxstring)) < 0)
11538             return(x);
11539           switch (x) {
11540             case AV_KRB4:
11541               z = ck_krb4_is_installed();
11542               break;
11543             case AV_KRB5:
11544               z = ck_krb5_is_installed();
11545               break;
11546             case AV_SRP:
11547               z = ck_srp_is_installed();
11548               break;
11549             case AV_SSL:
11550               z = ck_ssleay_is_installed();
11551               break;
11552             case AV_NTLM:
11553               z = ck_ntlm_is_installed();
11554               break;
11555             case AV_CRYPTO:
11556               z = ck_crypt_is_installed();
11557               break;
11558             case AV_SSH:
11559               z = ck_ssh_is_installed();
11560               break;
11561             default:
11562               z = 0;
11563           }
11564           break;
11565       }
11566       case XXIFAT:                      /* IF ASKTIMEOUT */
11567         z = asktimedout;
11568         break;
11569
11570       case XXIFRD:                      /* IF READABLE */
11571       case XXIFWR:                      /* IF WRITEABLE */
11572         if ((x = cmfld("File or directory name",
11573                        "",
11574                        &s,
11575 #ifdef OS2
11576                        NULL             /* This allows \'s in filenames */
11577 #else
11578                        xxstring
11579 #endif /* OS2 */
11580                        )) < 0) {
11581             if (x == -3) {
11582                 extern int cmflgs;
11583                 if (cmflgs == 1) {
11584                     printf("?File or directory name required\n");
11585                     return(-9);
11586                 }
11587             } else return(x);
11588         }
11589         s = brstrip(s);
11590 /*
11591   zchk[io]() do not do what we want here for directories, so we set
11592   a global flag telling it to behave specially in this case.  Othewise
11593   we'd have to change the API and change all ck?fio.c modules accordingly.
11594 */
11595         y = 0;                          /* Try-again control */
11596 #ifdef OS2
11597   ifrdagain:
11598 #endif /* OS2 */
11599         if (ifc == XXIFRD) {            /* IF READABLE */
11600             zchkid = 1;
11601             z = zchki(s) > -1;
11602             zchkid = 0;
11603         } else if (ifc == XXIFWR) {     /* IF WRITEABLE */
11604             zchkod = 1;
11605             z = zchko(s) > -1;
11606             zchkod = 0;
11607         }
11608 #ifdef OS2
11609         if (!z && !y) {                 /* File not found. */
11610             int t;                      /* Try expanding variables */
11611             t = LINBUFSIZ-1;            /* and looking again. */
11612             lp = line;
11613             zzstring(s,&lp,&t);
11614             s = line;
11615             z = zchko(s) > -1;
11616             y++;
11617             goto ifrdagain;
11618         }
11619 #endif /* OS2 */
11620         ifargs += 2;
11621         break;
11622       case XXIFQU:                      /* IF QUIET */
11623         z = quiet ? 1 : 0;
11624         debug(F101,"if quiet","",z);
11625         ifargs += 1;
11626         break;
11627
11628       case XXIFWI:                      /* WILD */
11629         if ((x = cmfld("File specification","",&s,xxstring)) < 0) return(x);
11630         z = iswild(s);
11631         break;
11632
11633       case XXIFCK:                      /* C-KERMIT */
11634 #ifdef OS2
11635         z = 0;
11636 #else
11637         z = 1;
11638 #endif /* OS2 */
11639         break;
11640
11641       case XXIFK9:                      /* K-95 */
11642 #ifdef OS2
11643         z = 1;
11644 #else
11645         z = 0;
11646 #endif /* OS2 */
11647         break;
11648
11649       case XXIFGU:                      /* GUI */
11650 #ifdef KUI
11651         z = 1;
11652 #else
11653         z = 0;
11654 #endif /* KUI */
11655         break;
11656
11657       case XXIFMS:                      /* MS-KERMIT */
11658         z = 0;
11659         break;
11660
11661       case XXIFLO:                      /* IF LOCAL */
11662         z = local ? 1 : 0;
11663         break;
11664
11665       case XXIFCM: {                    /* IF COMMAND */
11666           extern struct keytab cmdtab[];
11667           extern int ncmd;
11668           if ((x = cmfld("Word","",&s,xxstring)) < 0)
11669             return(x);
11670           z = lookup(cmdtab,s,ncmd,&y);
11671           z = (z == -2 || z > -1) ? 1 : 0;
11672           break;
11673       }
11674 #ifdef CKFLOAT
11675       case XXIFFP:                      /* IF FLOAT */
11676         if ((x = cmfld("Number","",&s,xxstring)) < 0)
11677           if (x != -3)                  /* e.g. empty variable */
11678             return(x);
11679         z = isfloat(s,0);
11680         break;
11681 #endif /* CKFLOAT */
11682
11683       case XXIFKB:                      /* KBHIT */
11684         z = conchk();
11685         if (z < 0) z = 0;
11686         if (z > 1) z = 1;
11687         break;
11688
11689       case XXIFKG: {                    /* KERBANG */
11690           extern int cfilef;
11691 #ifdef COMMENT
11692           z = (xcmdsrc == 0) ? 0 : (cfilef && cmdlvl == 1);
11693 #else
11694           z = (xcmdsrc == 0) ? 0 : (cfilef && tlevel == 0);
11695 #endif  /* COMMENT */
11696           break;
11697       }
11698
11699       case XXIFDB: {                    /* IF DEBUG - 2010/03/16 */
11700           extern int debmsg;
11701           z = debmsg;
11702           break;
11703       }
11704
11705       default:                          /* Shouldn't happen */
11706         return(-2);
11707     } /* end of switch */
11708
11709     if (z)
11710       *bx++ = '1';
11711     else
11712       *bx++ = '0';
11713     *bx = NUL;
11714     if (bx > boolval + BOOLLEN - 2) {
11715         printf("?Boolean expression too long");
11716         return(-9);
11717     }
11718     ecount++;                           /* Expression counter */
11719     debug(F101,"boolexp parens","",parens);
11720     debug(F101,"boolexp pcount","",pcount);
11721     if (parens && pcount > 0)
11722       goto ifagain;
11723
11724   ifend:                                /* No more - done */
11725     *bx = NUL;
11726     z = atoi(evalx(boolval));
11727     debug(F111,"boolexp boolval",boolval,z);
11728     return(z);
11729 }
11730
11731 /*  D O I F  --  Do the IF command  */
11732
11733 int
11734 doif(cx) int cx; {
11735     int x, y, z; char *s, *p;
11736     char *q;
11737 #ifdef OS2
11738     extern int keymac;
11739 #endif /* OS2 */
11740
11741     debug(F101,"doif cx","",cx);
11742
11743     z = boolexp(cx);                    /* Evaluate the condition(s) */
11744     debug(F010,"doif cmdbuf",cmdbuf,0);
11745     debug(F101,"doif boolexp","",z);
11746     if (z < 0)
11747       return(z);
11748
11749     if (cx == XXIF) {                   /* Allow IF to have XIF semantics. */
11750         char * p;
11751         p = cmpeek();
11752         if (!p) p = "";
11753         while (*p) {
11754             if (*p == SP || *p == HT)
11755               p++;
11756             else
11757               break;
11758         }
11759         if (*p == '{')
11760           cx = XXIFX;
11761     }
11762     switch (cx) {                       /* Separate handling for IF and XIF */
11763
11764       case XXASSER:                     /* And ASSERT */
11765         if ((x = cmcfm()) < 0)
11766           return(x);
11767         return(success = z);
11768
11769       case XXIF:                        /* This is IF... */
11770         ifcmd[cmdlvl] = 1;              /* We just completed an IF command */
11771         debug(F101,"doif condition","",z);
11772         if (z) {                        /* Condition is true */
11773             iftest[cmdlvl] = 1;         /* Remember that IF succeeded */
11774             if (maclvl > -1) {          /* In macro, */
11775                 pushcmd(NULL);          /* save rest of command. */
11776             } else if (tlevel > -1) {   /* In take file, */
11777                 debug(F100, "doif: pushing command", "", 0);
11778                 pushcmd(NULL);          /* save rest of command. */
11779             } else {                    /* If interactive, */
11780                 cmini(ckxech);          /* just start a new command */
11781                 printf("\n");           /* (like in MS-DOS Kermit) */
11782                 if (pflag) prompt(xxstring);
11783             }
11784         } else {                        /* Condition is false */
11785             iftest[cmdlvl] = 0;         /* Remember command failed. */
11786             if ((y = cmtxt("command to be ignored","",&s,NULL)) < 0)
11787               return(y);                /* Gobble up rest of line */
11788         }
11789         return(0);
11790
11791       case XXIFX: {                     /* This is XIF (Extended IF) */
11792           char *p;
11793           char e[5];
11794           int i;
11795           if ((y = cmtxt("Object command","",&s,NULL)) < 0)
11796             return(y);                  /* Get object command. */
11797           p = s;
11798           lp = line;
11799           debug(F110,"doif THEN part",s,-54);
11800           if (litcmd(&p,&lp,LINBUFSIZ - 1) < 0) { /* Quote THEN-part */
11801               return(-2);
11802           }
11803           debug(F111,"doif THEN part 2",line,z);
11804
11805           while (*p == SP) p++;         /* Strip trailing spaces */
11806           ifcmd[cmdlvl] = 0;            /* Assume ELSE part in same line */
11807           iftest[cmdlvl] = z ? 1 : 0;
11808           if (*p) {                     /* At end? */
11809               if (!z) {                 /* No, use ELSE-part, if any */
11810                   for (i = 0; i < 4; i++) e[i] = *p++;
11811                   if (ckstrcmp(e,"else",4,0)) /* See if we have an ELSE */
11812                     return(-2);         /* Something else - error. */
11813                   debug(F010,"doif ELSE line 1",p,0);
11814                   while (*p == SP) p++; /* Skip spaces */
11815                   if (*p != '{') {      /* Brace ELSE part if necessary */
11816                       ckmakmsg(tmpbuf,TMPBUFSIZ,"{",p," }",NULL);
11817                       p = tmpbuf;
11818                       debug(F010,"doif ELSE line 2",p,0);
11819                   }
11820                   lp = line;            /* Write over THEN part... */
11821                   *lp = NUL;            /* with ELSE part. */
11822                   if (litcmd(&p,&lp,LINBUFSIZ - 2) < 0) {
11823                       return(-2);
11824                   }
11825                   while (*p == SP) p++; /* Strip trailing spaces */
11826                   if (*p) return(-2);   /* Should be nothing here. */
11827                   debug(F010,"doif ELSE line 3",line,0);
11828               }
11829           } else {                      /* At end, treat like an IF command */
11830               if (!z) line[0] = NUL;    /* Condition not true and no ELSE */
11831               ifcmd[cmdlvl] = 1;        /* Allow ELSE on next line */
11832               debug(F101,"IF condition","",z);
11833           }
11834           if (line[0]) {
11835               x = mlook(mactab,"_xif",nmac); /* Get index of "_xif" macro. */
11836               if (x < 0) {                      /* Not there? */
11837                   addmmac("_xif",xif_def);      /* Put it back. */
11838                   if (mlook(mactab,"_xif",nmac) < 0) { /* Look it up again. */
11839                       printf("?XIF macro gone!\n");
11840                       return(success = 0);
11841                   }
11842               }
11843               dodo(x,line,cmdstk[cmdlvl].ccflgs | CF_IMAC);
11844           }
11845           return(0);
11846       }
11847       case XXWHI: {                     /* WHILE Command */
11848           p = cmdbuf;                   /* Capture IF condition */
11849           ifcond[0] = NUL;              /* from command buffer */
11850           while (*p == SP) p++;
11851           while (*p != SP) p++;
11852           ifcp = ifcond;
11853           ifcp += ckstrncpy(ifcp,"{ \\flit(if ( not ",IFCONDLEN);
11854 #ifdef COMMENT
11855 /*
11856   This doesn't work because it breaks on the first left brace, which does
11857   not necessarily start the command list, e.g. "while equal \%a {\35}".
11858 */
11859           while (*p != '{' && *p != NUL) *ifcp++ = *p++;
11860           p = " ) goto _..bot) } ";
11861           while (*ifcp++ = *p++) ;
11862 #else
11863 /*
11864   The command parser sets cmbptr to the spot where it left off parsing in
11865   the command buffer.
11866 */
11867           {
11868               extern char * cmbptr;
11869               if (cmbptr) {
11870                   while (p < cmbptr && *p != NUL)
11871                     *ifcp++ = *p++;
11872                   p = " ) goto _..bot) } ";
11873                   while ((*ifcp++ = *p++)) ;
11874               } else {
11875                   printf("?Internal error parsing WHILE condition\n");
11876                   return(-9);
11877               }
11878           }
11879 #endif /* COMMENT */
11880
11881           debug(F110,"WHILE cmd",ifcond,0);
11882
11883           if ((y = cmtxt("Object command","",&s,NULL)) < 0)
11884             return(y);                  /* Get object command. */
11885           p = s;
11886           lp = line;
11887           if (litcmd(&p,&lp,LINBUFSIZ - 2) < 0) { /* Quote object command */
11888               return(-2);
11889           }
11890           debug(F101,"WHILE body",line,-54);
11891           if (line[0]) {
11892               char *p;
11893               x = mlook(mactab,"_while",nmac); /* index of "_while" macro. */
11894               if (x < 0) {              /* Not there? */
11895                   addmmac("_while",whil_def); /* Put it back. */
11896                   if (mlook(mactab,"_while",nmac) < 0) { /* Look it up again */
11897                       printf("?WHILE macro definition gone!\n");
11898                       return(success = 0);
11899                   }
11900               }
11901               p = malloc((int)strlen(ifcond) + (int)strlen(line) + 2);
11902               if (p) {
11903                   strcpy(p,ifcond);     /* safe (prechecked) */
11904                   strcat(p,line);       /* safe (prechecked) */
11905                   debug(F010,"WHILE dodo",p,0);
11906                   dodo(x,p,cmdstk[cmdlvl].ccflgs | CF_IMAC);
11907                   free(p);
11908                   p = NULL;
11909               } else {
11910                   printf("?Can't allocate storage for WHILE command");
11911                   return(success = 0);
11912               }
11913           }
11914           return(0);
11915       }
11916       default:
11917         return(-2);
11918     }
11919 }
11920 #endif /* NOSPL */
11921
11922 /* Set up a TAKE command file */
11923
11924 int
11925 dotake(s) char *s; {
11926 #ifndef NOSPL
11927     extern int tra_cmd;
11928 #endif /* NOSPL */
11929 #ifndef NOLOCAL
11930 #ifdef OS2
11931     extern int term_io;
11932     int term_io_sav = term_io;
11933 #endif /* OS2 */
11934 #endif /* NOLOCAL */
11935     int slen;
11936
11937     debug(F110,"dotake",s,0);
11938     if (!s) s = "";
11939     if (!*s) return(success = 0);
11940     slen = strlen(s);
11941     debug(F101,"dotake len","",slen);
11942
11943     if ((tfile[++tlevel] = fopen(s,"r")) == NULL) {
11944         perror(s);
11945         debug(F110,"dotake fail",s,0);
11946         tlevel--;
11947         return(success = 0);
11948     } else {
11949         tfline[tlevel] = 0;             /* Line counter */
11950 #ifdef VMS
11951         conres();                       /* So Ctrl-C will work */
11952 #endif /* VMS */
11953 #ifndef NOLOCAL
11954 #ifdef OS2
11955         term_io = 0;                    /* Disable Terminal Emulator I/O */
11956 #endif /* OS2 */
11957 #endif /* NOLOCAL */
11958 #ifndef NOSPL
11959         cmdlvl++;                       /* Entering a new command level */
11960         debug(F111,"CMD +F",s,cmdlvl);
11961         debug(F101,"dotake cmdlvl","",cmdlvl);
11962         debug(F101,"dotake tlevel","",tlevel);
11963         if (cmdlvl > CMDSTKL) {
11964             debug(F100,"dotake stack overflow","",0);
11965             cmdlvl--;
11966             debug(F111,"CMD*-F",s,cmdlvl);
11967             fclose(tfile[tlevel--]);
11968             printf("?TAKE files and/or DO commands nested too deeply\n");
11969             return(success = 0);
11970         }
11971         if (tfnam[tlevel]) {            /* Copy the filename */
11972             free(tfnam[tlevel]);
11973             tfnam[tlevel] = NULL;
11974         }
11975         if ((tfnam[tlevel] = malloc(strlen(s) + 1))) {
11976             strcpy(tfnam[tlevel],s);    /* safe */
11977         } else {
11978             printf("?Memory allocation failure\n");
11979             return(success = 0);
11980         }
11981         ifcmd[cmdlvl] = 0;              /* Set variables for this cmd file */
11982         iftest[cmdlvl] = 0;
11983         count[cmdlvl]  = count[cmdlvl-1];  /* Inherit this */
11984         intime[cmdlvl] = intime[cmdlvl-1]; /* Inherit this */
11985         inpcas[cmdlvl] = inpcas[cmdlvl-1]; /* Inherit this */
11986         takerr[cmdlvl] = takerr[cmdlvl-1]; /* Inherit this */
11987         merror[cmdlvl] = merror[cmdlvl-1]; /* Inherit this */
11988         xquiet[cmdlvl] = quiet;
11989         xvarev[cmdlvl] = vareval;
11990         xcmdsrc = CMD_TF;
11991         cmdstk[cmdlvl].src = CMD_TF;    /* Say we're in a TAKE file */
11992         cmdstk[cmdlvl].lvl = tlevel;    /* nested at this level */
11993         cmdstk[cmdlvl].ccflgs = cmdstk[cmdlvl-1].ccflgs;
11994 #else
11995         takerr[tlevel] = takerr[tlevel-1]; /* Inherit this */
11996 #endif /* NOSPL */
11997     }
11998 #ifndef NOSPL
11999     if (tra_cmd)
12000       printf("[%d] +F: \"%s\"\n",cmdlvl,s);
12001 #endif /* NOSPL */
12002 #ifndef NOLOCAL
12003 #ifdef OS2
12004     term_io = term_io_sav;
12005 #endif /* OS2 */
12006 #endif /* NOLOCAL */
12007     return(1);
12008 }
12009 #endif /* NOICP */