1 #include "ckcsym.h"
2 #ifndef NOICP
3 
4 /*
5   Authors:
6     Frank da Cruz <fdc@columbia.edu>,
7       The Kermit Project, New York City
8     Jeffrey E Altman <jaltman@secure-endpoints.com>
9       Secure Endpoints Inc., New York City
10 
11   Copyright (C) 1985, 2020,
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   Last update:
17     Fri Sep 18 14:55:27 2020
18 */
19 
20 /* Includes */
21 
22 #include "ckcdeb.h"
23 #include "ckcasc.h"
24 #include "ckcker.h"
25 #include "ckuusr.h"
26 #include "ckcxla.h"
27 #include "ckcnet.h"                     /* Network symbols */
28 #include <signal.h>
29 
30 #ifndef NOSTAT
31 #ifdef VMS
32 /* 2010-03-09 SMS.  VAX C needs help to find "sys".  It's easier not to try. */
33 #include <stat.h>
34 #else /* def VMS */
35 #include <sys/stat.h>
36 #endif /* def VMS [else] */
37 #endif /* NOSTAT */
38 
39 #ifdef VMS
40 #ifndef TCPSOCKET
41 #include <errno.h>
42 #endif /* TCPSOCKET */
43 #endif /* VMS */
44 
45 #ifdef datageneral
46 #define fgets(stringbuf,max,fd) dg_fgets(stringbuf,max,fd)
47 #endif /* datageneral */
48 
49 #ifdef QNX6
50 #define readblock kreadblock
51 #endif /* QNX6 */
52 
53 /* External Kermit Variables, see ckmain.c for description. */
54 
55 extern xx_strp xxstring;
56 
57 extern int local, xitsta, binary, parity, escape, flow, cmd_rows, turn,
58   turnch, duplex, ckxech, seslog, dfloc, cnflg, tlevel, pflag, msgflg, mdmtyp,
59   zincnt, quiet, repars, techo, network, nzxopts, what, filepeek, recursive;
60 
61 extern int xaskmore, tt_rows, tt_cols, cmd_cols, g_matchdot, diractive,
62   xcmdsrc, nscanfile, reliable, nolinks, cmflgs;
63 
64 #ifdef VMSORUNIX
65 extern int zgfs_dir, zgfs_link;
66 #endif /* VMSORUNIX */
67 
68 #ifdef CK_IFRO
69   extern int remonly;
70 #endif /* CK_IFRO */
71 
72 #ifdef OS2
73 extern int StartedFromDialer ;
74 extern BYTE vmode;
75 extern int k95stdout;
76 #ifndef NT
77 #define INCL_NOPM
78 #define INCL_VIO                        /* Needed for ckocon.h */
79 #include <os2.h>
80 #undef COMMENT
81 #else
82 #define APIRET ULONG
83 #include <windows.h>
84 #include <tapi.h>
85 #include "ckntap.h"
86 #endif /* NT */
87 #include "ckocon.h"
88 #include "ckodir.h"			/* [jt] 2013/11/21 - for MAXPATHLEN */
89 #endif /* OS2 */
90 
91 extern long vernum, speed;
92 extern char *versio, *protv, *ckxv, *ckzv, *fnsv, *connv, *dftty, *cmdv;
93 extern char *dialv, *loginv, *for_def[], *whil_def[], *xif_def[], *sw_def[];
94 extern char *foz_def[];
95 extern char *ckxsys, *ckzsys;
96 #ifndef OS2
97 extern char *DIRCMD;
98 #ifndef UNIX
99 extern char *DELCMD;
100 #endif /* UNIX */
101 #endif /* OS2 */
102 extern char ttname[], filnam[];
103 extern CHAR sstate, feol;
104 extern char *zinptr;
105 
106 #ifdef UNIX
107 extern char ** mtchs;                   /* zxpand() file list */
108 #endif /* UNIX */
109 
110 #ifndef NOXFER
111 extern int oopts, omode, oname, opath;  /* O-Packet options */
112 
113 extern int stdinf, sndsrc, size, rpsiz, urpsiz, fncnv, fnrpath, displa,
114   stdouf, isguest, pktlog, nfils, keep, maxrps, fblksiz, frecl, frecfm,
115   atcapr, atdiso, spsizf, spsiz, spsizr, spmax, wslotr, prefixing,
116   fncact, fnspath, nprotos, g_proto, g_urpsiz, g_spsizf,
117   g_spsiz, g_spsizr, g_spmax, g_wslotr, g_prefixing, g_fncact, g_fncnv,
118   g_fnspath, g_fnrpath, xfrxla, g_xfrxla;
119 
120 extern char *cmarg, *cmarg2;
121 
122 #ifndef NOMSEND                         /* Multiple SEND */
123 extern char *msfiles[];
124 #endif /* NOMSEND */
125 extern char fspec[];                    /* Most recent filespec */
126 extern int fspeclen;
127 
128 #ifdef CK_TMPDIR
129 extern int f_tmpdir;                    /* Directory changed temporarily */
130 extern char savdir[];                   /* For saving current directory */
131 #endif /* CK_TMPDIR */
132 
133 extern struct keytab protos[];          /* File transfer protocols */
134 extern struct ck_p ptab[NPROTOS];
135 #endif /* NOXFER */
136 
137 #ifdef DCMDBUF                          /* Declarations from cmd package */
138 extern char *cmdbuf, *atmbuf;           /* Command buffers */
139 #else
140 extern char cmdbuf[], atmbuf[];         /* Command buffers */
141 #endif /* DCMDBUF */
142 
143 extern int nopush;
144 
145 #ifndef NOSPL
146 int askflag = 0;                        /* ASK-class command active */
147 int echostars = 0;			/* ASKQ should echo asterisks */
148 extern char **a_ptr[];
149 extern int a_dim[];
150 extern char **m_xarg[];
151 extern int n_xarg[];
152 extern struct mtab *mactab;
153 extern int nmac;
154 extern long ck_alarm;
155 extern char alrm_date[], alrm_time[];
156 extern int x_ifnum;
157 #endif /* NOSPL */
158 
159 extern int inserver;                    /* I am IKSD */
160 extern int backgrd;                     /* Kermit executing in background */
161 extern char psave[];                    /* For saving & restoring prompt */
162 extern char *tp;                        /* Temporary buffer */
163 
164 int readblock = 4096;                   /* READ buffer size */
165 CHAR * readbuf = NULL;                  /* Pointer to read buffer */
166 int readsize = 0;                       /* Number of chars actually read */
167 int getcmd = 0;                         /* GET-class command was given */
168 
169 char chgsourcedir[MAXPATHLEN+1] = { 0,0 }; /* Source directory for CHANGE */
170 char chgdestdir[MAXPATHLEN+1] = { 0,0 }; /* Destination directory for CHANGE */
171 char chgbackupdir[MAXPATHLEN+1] = { 0,0 }; /* Backup directory for CHANGE */
172 
173 extern int zchkod, zchkid;
174 
175 /*  C K U U S 6 --  "User Interface" for Unix Kermit (Part 6)  */
176 
177 struct keytab deltab[] = {              /* DELETE Command Options */
178     { "/all",           DEL_ALL,  CM_INV },
179     { "/after",         DEL_AFT,  CM_ARG },
180     { "/ask",           DEL_ASK,  0 },
181     { "/before",        DEL_BEF,  CM_ARG },
182     { "/directories",   DEL_DIR,  0 },
183     { "/dotfiles",      DEL_DOT,  0 },
184     { "/except",        DEL_EXC,  CM_ARG },
185     { "/heading",       DEL_HDG,  0 },
186     { "/l",             DEL_LIS,  CM_INV|CM_ABR },
187     { "/larger-than",   DEL_LAR,  CM_ARG },
188     { "/list",          DEL_LIS,  0 },
189     { "/log",           DEL_LIS,  CM_INV },
190     { "/noask",         DEL_NAS,  0 },
191     { "/nodotfiles",    DEL_NOD,  0 },
192     { "/noheading",     DEL_NOH,  0 },
193     { "/nol",           DEL_NOL,  CM_INV|CM_ABR },
194     { "/nolist",        DEL_NOL,  0 },
195     { "/nolog",         DEL_NOL,  CM_INV },
196     { "/nopage",        DEL_NOP,  0 },
197     { "/not-after",     DEL_NAF,  CM_ARG },
198     { "/not-before",    DEL_NBF,  CM_ARG },
199     { "/not-since",     DEL_NAF,  CM_INV|CM_ARG },
200     { "/page",          DEL_PAG,  0 },
201     { "/quiet",         DEL_QUI,  CM_INV },
202     { "/recursive",     DEL_REC,  0 },
203     { "/simulate",      DEL_SIM,  0 },
204     { "/since",         DEL_AFT,  CM_ARG|CM_INV },
205     { "/smaller-than",  DEL_SMA,  CM_ARG },
206     { "/summary",       DEL_SUM,  0 },
207     { "/tree",          DEL_ALL,  0 },
208     { "/type",          DEL_TYP,  CM_ARG },
209     { "/verbose",       DEL_VRB,  CM_INV }
210 };
211 int ndeltab = sizeof(deltab)/sizeof(struct keytab);
212 
213 /* /QUIET-/VERBOSE (/LIST-/NOLIST) (/LOG-/NOLOG) table */
214 
215 struct keytab qvswtab[] = {
216     { "/l",           DEL_LIS,  CM_INV|CM_ABR },
217     { "/list",        DEL_LIS,  0 },
218     { "/log",         DEL_LIS,  CM_INV },
219     { "/nol",         DEL_NOL,  CM_INV|CM_ABR },
220     { "/nolist",      DEL_NOL,  0 },
221     { "/nolog",       DEL_NOL,  CM_INV },
222     { "/quiet",       DEL_QUI,  CM_INV },
223     { "/verbose",     DEL_VRB,  CM_INV }
224 };
225 int nqvswtab = sizeof(qvswtab)/sizeof(struct keytab);
226 
227 static struct keytab renamsw[] = {
228     { "/collision",   REN_OVW,  CM_ARG },
229 #ifndef NOUNICODE
230     { "/convert",     REN_XLA,  CM_ARG },
231 #endif	/* NOUNICODE */
232     { "/fixspaces",   REN_SPA,  CM_ARG },
233     { "/l",           DEL_LIS,  CM_INV|CM_ABR },
234     { "/list",        DEL_LIS,  0      },
235     { "/log",         DEL_LIS,  CM_INV },
236     { "/lower",       REN_LOW,  CM_ARG },
237     { "/nol",         DEL_NOL,  CM_INV|CM_ABR },
238     { "/nolist",      DEL_NOL,  0      },
239     { "/nolog",       DEL_NOL,  CM_INV },
240     { "/quiet",       DEL_QUI,  CM_INV },
241     { "/replace",     REN_RPL,  CM_ARG },
242     { "/simulate",    DEL_SIM,  0      },
243     { "/upper",       REN_UPP,  CM_ARG },
244     { "/verbose",     DEL_VRB,  CM_INV }
245 };
246 static int nrenamsw = sizeof(renamsw)/sizeof(struct keytab);
247 
248 static struct keytab renamset[] = {
249     { "collision",    REN_OVW,  0 },
250     { "list",         DEL_LIS,  0 }
251 };
252 static int nrenamset = sizeof(renamset)/sizeof(struct keytab);
253 
254 /* Args for RENAME /LOWER: and /UPPER: */
255 
256 static struct keytab r_upper[] = {
257     { "all",   1, 0 },
258     { "lower", 0, 0 }
259 };
260 
261 static struct keytab r_lower[] = {
262     { "all",   1, 0 },
263     { "upper", 0, 0 }
264 };
265 
266 /* Args for RENAME /COLLISION... */
267 
268 #define RENX_FAIL 0
269 #define RENX_OVWR 1
270 #define RENX_SKIP 2
271 
272 static struct keytab r_collision[] = {
273     { "fail",      RENX_FAIL, 0 },
274     { "overwrite", RENX_OVWR, 0 },
275     { "proceed",   RENX_SKIP, CM_INV },
276     { "skip",      RENX_SKIP, 0 }
277 };
278 static int nr_collision = sizeof(r_collision)/sizeof(struct keytab);
279 
280 struct keytab copytab[] = {
281     { "/append",      998,      0 },
282 #ifndef NOSPL
283     { "/fromb64",     997,      0 },
284 #endif /* NOSPL */
285     { "/l",           DEL_LIS,  CM_INV|CM_ABR },
286     { "/list",        DEL_LIS,  0 },
287     { "/log",         DEL_LIS,  CM_INV },
288     { "/nol",         DEL_NOL,  CM_INV|CM_ABR },
289     { "/nolist",      DEL_NOL,  0 },
290     { "/nolog",       DEL_NOL,  CM_INV },
291     { "/overwrite",   994,      CM_ARG },
292 #ifndef NOXFER
293     { "/preserve",    995,      0 },
294 #endif	/* NOXFER */
295     { "/quiet",       DEL_QUI,  CM_INV },
296     { "/swap-bytes",  999,      0 },
297 #ifndef NOSPL
298     { "/tob64",       996,      0 },
299 #endif /* NOSPL */
300     { "/verbose",     DEL_VRB,  CM_INV }
301 };
302 int ncopytab = sizeof(copytab)/sizeof(struct keytab);
303 
304 #define OVW_ALWAYS 0
305 #define OVW_NEVER  1
306 #define OVW_OLDER  2
307 #define OVW_NEWER  3
308 
309 static struct keytab ovwtab[] = {
310     { "always", OVW_ALWAYS, 0 },
311     { "never",  OVW_NEVER, 0 },
312     { "newer",  OVW_NEWER, 0 },
313     { "older",  OVW_OLDER, 0 }
314 };
315 static int novwtab = 4;
316 
317 #ifndef NOXFER
318 static struct keytab gettab[] = {       /* GET options */
319     { "/as-name",         SND_ASN, CM_ARG },
320     { "/binary",          SND_BIN, 0 },
321 #ifdef CALIBRATE
322     { "/calibrate",       SND_CAL, CM_INV },
323 #endif /* CALIBRATE */
324 #ifdef PIPESEND
325     { "/command",         SND_CMD, CM_PSH },
326 #endif /* PIPESEND */
327     { "/delete",          SND_DEL, 0 },
328     { "/except",          SND_EXC, CM_ARG },
329     { "/filenames",       SND_NAM, CM_ARG },
330 #ifdef PIPESEND
331     { "/filter",          SND_FLT, CM_ARG|CM_PSH },
332 #endif /* PIPESEND */
333 #ifdef VMS
334     { "/image",           SND_IMG, 0 },
335     { "/labeled",         SND_LBL, 0 },
336 #else
337     { "/image",           SND_BIN, CM_INV },
338 #endif /* VMS */
339 #ifdef CK_TMPDIR
340     { "/move-to",         SND_MOV, CM_ARG },
341 #endif /* CK_TMPDIR */
342     { "/pathnames",       SND_PTH, CM_ARG },
343     { "/pipes",           SND_PIP, CM_ARG|CM_PSH },
344     { "/quiet",           SND_SHH, 0 },
345 #ifdef CK_RESEND
346     { "/recover",         SND_RES, 0 },
347 #endif /* CK_RESEND */
348     { "/recursive",       SND_REC, 0 },
349     { "/rename-to",       SND_REN, CM_ARG },
350 #ifdef COMMENT
351     { "/smaller-than",    SND_SMA, CM_ARG },
352 #endif /* COMMENT */
353     { "/subdirectories",  SND_REC, CM_INV },
354     { "/text",            SND_TXT, 0 },
355     { "/transparent",     SND_XPA, 0 }
356 };
357 #define NGETTAB sizeof(gettab)/sizeof(struct keytab)
358 static int ngettab = NGETTAB;
359 
360 static struct keytab rcvtab[] = {       /* RECEIVE options */
361     { "/as-name",         SND_ASN, CM_ARG },
362     { "/binary",          SND_BIN, 0 },
363 #ifdef CALIBRATE
364     { "/calibrate",       SND_CAL, CM_INV },
365 #endif /* CALIBRATE */
366 #ifdef PIPESEND
367     { "/command",         SND_CMD, CM_PSH },
368 #endif /* PIPESEND */
369     { "/except",          SND_EXC, CM_ARG },
370     { "/filenames",       SND_NAM, CM_ARG },
371 #ifdef PIPESEND
372     { "/filter",          SND_FLT, CM_ARG|CM_PSH },
373 #endif /* PIPESEND */
374 #ifdef VMS
375     { "/image",           SND_IMG, 0 },
376     { "/labeled",         SND_LBL, 0 },
377 #else
378     { "/image",           SND_BIN, CM_INV },
379 #endif /* VMS */
380 #ifdef CK_TMPDIR
381     { "/move-to",         SND_MOV, CM_ARG },
382 #endif /* CK_TMPDIR */
383     { "/pathnames",       SND_PTH, CM_ARG },
384     { "/pipes",           SND_PIP, CM_ARG|CM_PSH },
385 #ifdef CK_XYZ
386     { "/protocol",        SND_PRO, CM_ARG },
387 #else
388     { "/protocol",        SND_PRO, CM_ARG|CM_INV },
389 #endif /* CK_XYZ */
390     { "/quiet",           SND_SHH, 0 },
391     { "/recursive",       SND_REC, 0 },
392     { "/rename-to",       SND_REN, CM_ARG },
393     { "/text",            SND_TXT, 0 },
394     { "/transparent",     SND_XPA, 0 }
395 };
396 #define NRCVTAB sizeof(rcvtab)/sizeof(struct keytab)
397 static int nrcvtab = NRCVTAB;
398 #endif /* NOXFER */
399 
400 /* WAIT table */
401 
402 #define WAIT_FIL 997
403 #define WAIT_MDM 998
404 
405 struct keytab waittab[] = {
406     { "cd",            BM_DCD,   CM_INV }, /* (Carrier Detect) */
407     { "cts",           BM_CTS,   CM_INV }, /* (Clear To Send)  */
408     { "dsr",           BM_DSR,   CM_INV }, /* (Data Set Ready) */
409     { "file",          WAIT_FIL, 0 },      /* New category selector keywords */
410     { "modem-signals", WAIT_MDM, 0 },      /* ... */
411     { "ri",            BM_RNG,   CM_INV }  /* (Ring Indicator) */
412 };
413 int nwaittab = (sizeof(waittab) / sizeof(struct keytab));
414 
415 /* Modem signal table */
416 
417 struct keytab mstab[] = {
418     { "cd",    BM_DCD, 0 },             /* Carrier Detect */
419     { "cts",   BM_CTS, 0 },             /* Clear To Send  */
420     { "dsr",   BM_DSR, 0 },             /* Data Set Ready */
421     { "ri",    BM_RNG, 0 }              /* Ring Indicator */
422 };
423 int nms = (sizeof(mstab) / sizeof(struct keytab));
424 
425 #define WF_MOD 1
426 #define WF_DEL 2
427 #define WF_CRE 3
428 
429 struct keytab wfswi[] = {               /* WAIT FILE switches */
430     { "creation",     WF_CRE, 0 },      /* Wait for file to be created */
431     { "deletion",     WF_DEL, 0 },      /* Wait for file to be deleted */
432     { "modification", WF_MOD, 0 }       /* Wait for file to be modified */
433 };
434 int nwfswi = (sizeof(wfswi) / sizeof(struct keytab));
435 
436 #ifndef NOSPL
437 struct keytab asgtab[] = {              /* Assignment operators for "." */
438     { "::=", 2, 0 },                    /* ASSIGN and EVALUATE */
439     { ":=",  1, 0 },                    /* ASSIGN */
440     { "=",   0, 0 }                     /* DEFINE */
441 };
442 int nasgtab = (sizeof(asgtab) / sizeof(struct keytab));
443 
444 struct keytab opntab[] = {
445 #ifndef NOPUSH
446     { "!read",  OPN_PI_R, CM_INV },
447     { "!write", OPN_PI_W, CM_INV },
448 #endif /* NOPUSH */
449     { "append", OPN_FI_A, 0 },
450     { "host",   OPN_NET,  0 },
451 #ifdef OS2
452     { "line",   OPN_SER,  CM_INV },
453     { "port",   OPN_SER,  0 },
454 #else
455     { "line",   OPN_SER,  0 },
456     { "port",   OPN_SER,  CM_INV },
457 #endif /* OS2 */
458     { "read",   OPN_FI_R, 0 },
459     { "write",  OPN_FI_W, 0 }
460 };
461 int nopn = (sizeof(opntab) / sizeof(struct keytab));
462 
463 /* IF conditions */
464 
465 #define  XXIFCO 0       /* IF COUNT */
466 #define  XXIFER 1       /* IF ERRORLEVEL */
467 #define  XXIFEX 2       /* IF EXIST */
468 #define  XXIFFA 3       /* IF FAILURE */
469 #define  XXIFSU 4       /* IF SUCCESS */
470 #define  XXIFNO 5       /* IF NOT */
471 #define  XXIFDE 6       /* IF DEFINED */
472 #define  XXIFEQ 7       /* IF EQUAL (strings) */
473 #define  XXIFAE 8       /* IF = (numbers) */
474 #define  XXIFLT 9       /* IF < (numbers) */
475 #define  XXIFGT 10      /* IF > (numbers) */
476 #define  XXIFLL 11      /* IF Lexically Less Than (strings) */
477 #define  XXIFLG 12      /* IF Lexically Greater Than (strings) */
478 #define  XXIFEO 13      /* IF EOF (READ file) */
479 #define  XXIFBG 14      /* IF BACKGROUND */
480 #define  XXIFNU 15      /* IF NUMERIC */
481 #define  XXIFFG 16      /* IF FOREGROUND */
482 #define  XXIFDI 17      /* IF DIRECTORY */
483 #define  XXIFNE 18      /* IF NEWER */
484 #define  XXIFRO 19      /* IF REMOTE-ONLY */
485 #define  XXIFAL 20      /* IF ALARM */
486 #define  XXIFSD 21      /* IF STARTED-FROM-DIALER */
487 #define  XXIFTR 22      /* IF TRUE */
488 #define  XXIFNT 23      /* IF FALSE */
489 #define  XXIFTM 24      /* IF TERMINAL-MACRO */
490 #define  XXIFEM 25      /* IF EMULATION */
491 #define  XXIFOP 26      /* IF OPEN */
492 #define  XXIFLE 27      /* IF <= */
493 #define  XXIFGE 28      /* IF >= */
494 #define  XXIFIP 29      /* IF INPATH */
495 #define  XXIFTA 30      /* IF TAPI */
496 #define  XXIFMA 31      /* IF MATCH */
497 #define  XXIFFL 32      /* IF FLAG */
498 #define  XXIFAB 33      /* IF ABSOLUTE */
499 #define  XXIFAV 34      /* IF AVAILABLE */
500 #define  XXIFAT 35      /* IF ASKTIMEOUT */
501 #define  XXIFRD 36      /* IF READABLE */
502 #define  XXIFWR 37      /* IF WRITEABLE */
503 #define  XXIFAN 38      /* IF ... AND ... */
504 #define  XXIFOR 39      /* IF ... OR ... */
505 #define  XXIFLP 40      /* IF left parenthesis */
506 #define  XXIFRP 41      /* IF right parenthesis */
507 #define  XXIFNQ 42      /* IF != (== "NOT =") */
508 #define  XXIFQU 43      /* IF QUIET */
509 #define  XXIFCK 44      /* IF C-KERMIT */
510 #define  XXIFK9 45      /* IF K-95 */
511 #define  XXIFMS 46      /* IF MS-KERMIT */
512 #define  XXIFWI 47      /* IF WILD */
513 #define  XXIFLO 48      /* IF LOCAL */
514 #define  XXIFCM 49      /* IF COMMAND */
515 #define  XXIFFP 50      /* IF FLOAT */
516 #define  XXIFIK 51      /* IF IKS */
517 #define  XXIFKB 52      /* IF KBHIT */
518 #define  XXIFKG 53      /* IF KERBANG */
519 #define  XXIFVE 54      /* IF VERSION */
520 #define  XXIFDC 55      /* IF DECLARED */
521 #define  XXIFGU 56      /* IF GUI */
522 #define  XXIFLN 57	/* IF LINK */
523 #define  XXIFDB 58	/* IF DEBUG */
524 #define  XXIFFU 59	/* IF FUNCTION */
525 #define  XXIFNN 60	/* IF NEQ (lexically not equal) */
526 #define  XXIFLLE 61	/* IF LLE (lexically less than or equal) */
527 #define  XXIFLGE 62	/* IF LLE (lexically less than or equal) */
528 #define  XXIFTXT 63	/* IF TEXT (file) */
529 #define  XXIFBIN 64	/* IF BINARY (file) */
530 
531 struct keytab iftab[] = {               /* IF commands */
532     { "!",          XXIFNO, 0 },
533     { "!=",         XXIFNQ, 0 },
534     { "&&",         XXIFAN, 0 },
535     { "(",          XXIFLP, 0 },
536     { ")",          XXIFRP, 0 },
537     { "<",          XXIFLT, 0 },
538     { "<=",         XXIFLE, 0 },
539     { "=",          XXIFAE, 0 },
540     { "==",         XXIFAE, CM_INV },
541     { ">",          XXIFGT, 0 },
542     { ">=",         XXIFGE, 0 },
543     { "absolute",   XXIFAB, 0 },
544     { "alarm",      XXIFAL, 0 },
545     { "and",        XXIFAN, 0 },
546     { "asktimeout", XXIFAT, 0 },
547     { "available",  XXIFAV, 0 },
548     { "background", XXIFBG, 0 },
549     { "binary",     XXIFBIN,0 },
550     { "c-kermit",   XXIFCK, 0 },
551     { "command",    XXIFCM, 0 },
552     { "count",      XXIFCO, 0 },
553     { "dcl",        XXIFDC, CM_INV },
554     { "debug",      XXIFDB, 0 },
555     { "declared",   XXIFDC, 0 },
556     { "defined",    XXIFDE, 0 },
557 #ifdef CK_TMPDIR
558     { "directory",  XXIFDI, 0 },
559 #endif /* CK_TMPDIR */
560     { "emulation",  XXIFEM, 0 },
561 #ifdef COMMENT
562     { "eof",        XXIFEO, 0 },
563 #endif /* COMMENT */
564     { "equal",      XXIFEQ, 0 },
565     { "error",      XXIFFA, CM_INV },
566     { "exist",      XXIFEX, 0 },
567     { "failure",    XXIFFA, 0 },
568     { "false",      XXIFNT, 0 },
569     { "flag",       XXIFFL, 0 },
570 #ifdef CKFLOAT
571     { "float",      XXIFFP, 0 },
572 #endif /* CKFLOAT */
573     { "foreground", XXIFFG, 0 },
574     { "function",   XXIFFU, 0 },
575 #ifdef OS2
576     { "gui",        XXIFGU, 0 },
577 #else
578     { "gui",        XXIFGU, CM_INV },
579 #endif /* OS2 */
580 #ifdef IKSD
581     { "iksd",       XXIFIK, 0 },
582 #else
583     { "iksd",       XXIFIK, CM_INV },
584 #endif /* IKSD */
585     { "integer",    XXIFNU, CM_INV },
586     { "k-95",       XXIFK9, CM_INV },
587     { "kbhit",      XXIFKB, 0 },
588 #ifdef UNIX
589     { "kerbang",    XXIFKG, 0 },
590 #else
591     { "kerbang",    XXIFKG, CM_INV },
592 #endif /* UNIX */
593     { "lge",        XXIFLGE,0 },
594     { "lgt",        XXIFLG, 0 },
595 #ifdef UNIX
596     { "link",       XXIFLN, 0 },
597 #endif /* UNIX */
598     { "lle",        XXIFLLE,0 },
599     { "llt",        XXIFLL, 0 },
600     { "local",      XXIFLO, 0 },
601     { "match",      XXIFMA, 0 },
602     { "ms-kermit",  XXIFMS, CM_INV },
603     { "neq",        XXIFNN, 0 },
604 #ifdef ZFCDAT
605     { "newer",      XXIFNE, 0 },
606 #endif /* ZFCDAT */
607     { "not",        XXIFNO, 0 },
608     { "numeric",    XXIFNU, 0 },
609 /*  { "ok",         XXIFSU, CM_INV }, */
610     { "open",       XXIFOP, 0 },
611     { "or",         XXIFOR, 0 },
612     { "quiet",      XXIFQU, 0 },
613     { "readable",   XXIFRD, 0 },
614     { "remote-only",XXIFRO, 0 },
615     { "started-from-dialer",XXIFSD, CM_INV },
616     { "success",    XXIFSU, 0 },
617     { "tapi",       XXIFTA, 0 },
618 #ifdef OS2
619     { "terminal-macro", XXIFTM, 0 },
620 #else
621     { "terminal-macro", XXIFTM, CM_INV },
622 #endif /* OS2 */
623     { "text",       XXIFTXT,0 },
624     { "true",       XXIFTR, 0 },
625     { "version",    XXIFVE, 0 },
626     { "wild",       XXIFWI, 0 },
627     { "windows",    XXIFK9, 0 },
628     { "writeable",  XXIFWR, 0 },
629     { "||",         XXIFOR, 0 },
630     { "", 0, 0 }
631 };
632 int nif = (sizeof(iftab) / sizeof(struct keytab)) - 1;
633 
634 struct keytab iotab[] = {               /* Keywords for IF OPEN */
635     { "!read-file",      ZRFILE, CM_INV },
636     { "!write-file",     ZWFILE, CM_INV },
637     { "append-file",     ZWFILE, CM_INV },
638     { "connection",      8888,   0 },
639 #ifdef CKLOGDIAL
640     { "cx-log",          7777,   0 },
641 #endif /* CKLOGDIAL */
642     { "debug-log",       ZDFILE, 0 },
643     { "error",           9999,   0 },
644     { "packet-log",      ZPFILE, 0 },
645     { "read-file",       ZRFILE, 0 },
646     { "screen",          ZSTDIO, 0 },
647     { "session-log",     ZSFILE, 0 },
648     { "transaction-log", ZTFILE, 0 },
649     { "write-file",      ZWFILE, 0 }
650 };
651 int niot = (sizeof(iotab) / sizeof(struct keytab));
652 #endif /* NOSPL */
653 
654 /* Variables and prototypes */
655 
656 _PROTOTYP(static int doymdir,(int));
657 _PROTOTYP(static int renameone,(char *,char *,
658 				int,int,int,int,int,int,int,int,int,int,int));
659 
660 #ifdef NETCONN
661 extern int nnetdir;                     /* How many network directories */
662 #endif /* NETCONN */
663 #ifdef CK_SECURITY
664 _PROTOTYP(int ck_krb4_is_installed,(void));
665 _PROTOTYP(int ck_krb5_is_installed,(void));
666 _PROTOTYP(int ck_ntlm_is_installed,(void));
667 _PROTOTYP(int ck_srp_is_installed,(void));
668 _PROTOTYP(int ck_ssleay_is_installed,(void));
669 _PROTOTYP(int ck_ssh_is_installed,(void));
670 _PROTOTYP(int ck_crypt_is_installed,(void));
671 #else
672 #define ck_krb4_is_installed() (0)
673 #define ck_krb5_is_installed() (0)
674 #define ck_ntlm_is_installed() (0)
675 #define ck_srp_is_installed() (0)
676 #define ck_ssleay_is_installed() (0)
677 #define ck_ssh_is_installed() (0)
678 #define ck_crypt_is_installed() (0)
679 #endif /* CK_SECURITY */
680 
681 #define AV_KRB4   1
682 #define AV_KRB5   2
683 #define AV_NTLM   3
684 #define AV_SRP    4
685 #define AV_SSL    5
686 #define AV_CRYPTO 6
687 #define AV_SSH    7
688 
689 struct keytab availtab[] = {             /* Available authentication types */
690     { "crypto",     AV_CRYPTO, CM_INV }, /* and encryption */
691     { "encryption", AV_CRYPTO, 0 },
692     { "k4",         AV_KRB4,   CM_INV },
693     { "k5",         AV_KRB5,   CM_INV },
694     { "kerberos4",  AV_KRB4,   0 },
695     { "kerberos5",  AV_KRB5,   0 },
696     { "krb4",       AV_KRB4,   CM_INV },
697     { "krb5",       AV_KRB5,   CM_INV },
698     { "ntlm",       AV_NTLM,   0 },
699     { "srp",        AV_SRP,    0 },
700     { "ssh",        AV_SSH,    0 },
701     { "ssl",        AV_SSL,    0 },
702     { "tls",        AV_SSL,    0 },
703     { "",           0,         0 }
704 };
705 int availtabn = sizeof(availtab)/sizeof(struct keytab)-1;
706 
707 #ifndef NODIAL
708 _PROTOTYP(static int ddcvt, (char *, FILE *, int) );
709 _PROTOTYP(static int dncvt, (int, int, int, int) );
710 _PROTOTYP(char * getdname, (void) );
711 
712 static int partial  = 0;                /* For partial dial */
713 static char *dscopy = NULL;
714 int dialtype = -1;
715 
716 char *dialnum = (char *)0;              /* Remember DIAL number for REDIAL */
717 int dirline = 0;                        /* Dial directory line number */
718 extern char * dialdir[];                /* Dial directory file names */
719 extern int dialdpy;                     /* DIAL DISPLAY on/off */
720 extern int ndialdir;                    /* How many dial directories */
721 extern int ntollfree;                   /* Toll-free call info */
722 extern int ndialpxx;                    /* List of PBX exchanges */
723 extern char *dialtfc[];
724 char * matchpxx = NULL;                 /* PBX exchange that matched */
725 extern int nlocalac;                    /* Local area-code list */
726 extern char * diallcac[];
727 extern int tttapi;
728 #ifdef CK_TAPI
729 extern int tapiconv;                    /* TAPI Conversions */
730 extern int tapipass;                    /* TAPI Passthrough */
731 #endif /* CK_TAPI */
732 extern int dialatmo;
733 extern char * dialnpr, * dialsfx;
734 extern char * dialixp, * dialixs, * dialmac;
735 extern char * dialldp, * diallds, * dialtfp;
736 extern char * dialpxi, * dialpxo, * diallac;
737 extern char * diallcp, * diallcs, * diallcc;
738 extern char * dialpxx[];
739 
740 extern int dialcnf;                     /* DIAL CONFIRMATION */
741 int dialfld = 0;                        /* DIAL FORCE-LONG-DISTANCE */
742 int dialsrt = 1;                        /* DIAL SORT ON */
743 int dialrstr = 6;                       /* DIAL RESTRICTION */
744 int dialtest = 0;                       /* DIAL TEST */
745 int dialcount = 0;                      /* \v(dialcount) */
746 
747 extern int dialsta;                     /* Dial status */
748 int dialrtr = -1,                       /* Dial retries */
749     dialint = 10;                       /* Dial retry interval */
750 extern long dialcapas;                  /* Modem capabilities */
751 extern int dialcvt;                     /* DIAL CONVERT-DIRECTORY */
752 #endif /* NODIAL */
753 
754 #ifndef NOSPL
755 #define IFCONDLEN 256
756 int ifc,                                /* IF case */
757     not = 0,                            /* Flag for IF NOT */
758     ifargs = 0;                         /* Count of IF condition words */
759 char ifcond[IFCONDLEN];                 /* IF condition text */
760 char *ifcp;                             /* Pointer to IF condition text */
761 extern int vareval;
762 #ifdef DCMDBUF
763 extern int
764  *ifcmd,  *count,  *iftest, *intime,
765     *inpcas, *takerr, *merror, *xquiet, *xvarev;
766 #else
767 extern int ifcmd[];                     /* Last command was IF */
768 extern int iftest[];                    /* Last IF was true */
769 extern int count[];                     /* For IF COUNT, one for each cmdlvl */
770 extern int intime[];                    /* Ditto for other stackables... */
771 extern int inpcas[];
772 extern int takerr[];
773 extern int merror[];
774 extern int xquiet[];
775 extern int xvarev[];
776 #endif /* DCMDBUF */
777 #else
778 extern int takerr[];
779 #endif /* NOSPL */
780 
781 #ifdef DCMDBUF
782 extern char *line;                      /* Character buffer for anything */
783 extern char *tmpbuf;
784 #else
785 extern char line[], tmpbuf[];
786 #endif /* DCMDBUF */
787 extern char *lp;                        /* Pointer to line buffer */
788 
789 int cwdf = 0;                           /* CWD has been done */
790 
791 /* Flags for ENABLE/DISABLE */
792 extern int en_cwd, en_cpy, en_del, en_dir, en_fin,
793    en_get, en_hos, en_ren, en_sen, en_set, en_spa, en_typ, en_who, en_bye,
794    en_asg, en_que, en_ret, en_mai, en_pri, en_mkd, en_rmd, en_xit, en_ena;
795 
796 extern FILE *tfile[];                   /* File pointers for TAKE command */
797 extern char *tfnam[];                   /* Names of TAKE files */
798 extern int tfline[];                    /* TAKE-file line number */
799 
800 extern int success;                     /* Command success/failure flag */
801 extern int cmdlvl;                      /* Current position in command stack */
802 
803 #ifndef NOSPL
804 extern int maclvl;                      /* Macro to execute */
805 extern char *macx[];                    /* Index of current macro */
806 extern char *mrval[];                   /* Macro return value */
807 extern char *macp[];                    /* Pointer to macro */
808 extern int macargc[];                   /* ARGC from macro invocation */
809 
810 #ifdef COMMENT
811 extern char *m_line[];
812 #endif /* COMMENT */
813 
814 extern char *m_arg[MACLEVEL][NARGS];    /* Stack of macro arguments */
815 extern char *g_var[];                   /* Global variables %a, %b, etc */
816 
817 #ifdef DCMDBUF
818 extern struct cmdptr *cmdstk;           /* The command stack itself */
819 #else
820 extern struct cmdptr cmdstk[];          /* The command stack itself */
821 #endif /* DCMDBUF */
822 #endif /* NOSPL */
823 
824 #define xsystem(s) zsyscmd(s)
825 
826 static int x, y, z = 0;
827 static char *s, *p;
828 
829 #ifdef OS2
830 _PROTOTYP( int os2settitle, (char *, int) );
831 #endif /* OS2 */
832 
833 extern struct keytab yesno[], onoff[], fntab[];
834 extern int nyesno, nfntab;
835 
836 #ifndef NOSPL
837 
838 /* Do the ASK, ASKQ, GETOK, and READ commands */
839 
840 int asktimedout = 0;
841 
842 #define ASK_PUP 1
843 #define ASK_TMO 2
844 #define ASK_GUI 3
845 #define ASK_QUI 4
846 #define ASK_DEF 5
847 #define ASK_ECH 6
848 
849 #define GETC_CHK 1
850 #define GETC_TMO 2
851 #define GETC_QUI 3
852 
853 static struct keytab asktab[] = {
854     {  "/default", ASK_DEF, CM_ARG },
855     {  "/gui",     ASK_GUI,
856 #ifdef KUI
857            0
858 #else /* KUI */
859            CM_INV
860 #endif /* KUI */
861     },
862     { "/popup",    ASK_PUP,
863 #ifdef OS2
864            0
865 #else /* OS2 */
866            CM_INV
867 #endif /* OS2 */
868     },
869     { "/quiet",    ASK_QUI, 0 },
870     { "/timeout",  ASK_TMO, CM_ARG },
871     { "", 0, 0 }
872 };
873 static int nasktab = sizeof(asktab)/sizeof(struct keytab)-1;
874 
875 static struct keytab askqtab[] = {
876     { "/default",  ASK_DEF, CM_ARG },
877     { "/echo",     ASK_ECH, CM_ARG },
878     { "/gui",      ASK_GUI,
879 #ifdef KUI
880            0
881 #else /* KUI */
882            CM_INV
883 #endif /* KUI */
884     },
885     { "/noecho",   ASK_QUI, CM_INV },
886     { "/popup",    ASK_PUP,
887 #ifdef OS2
888            0
889 #else /* OS2 */
890            CM_INV
891 #endif /* OS2 */
892     },
893     { "/quiet",    ASK_QUI, 0 },
894     { "/timeout",  ASK_TMO, CM_ARG },
895     { "", 0, 0 }
896 };
897 static int naskqtab = sizeof(askqtab)/sizeof(struct keytab)-1;
898 
899 static struct keytab getctab[] = {
900     { "/check",    GETC_CHK, 0 },
901     { "/quiet",    GETC_QUI, 0 },
902     { "/timeout",  GETC_TMO, CM_ARG },
903     { "", 0, 0 }
904 };
905 static int ngetctab = sizeof(getctab)/sizeof(struct keytab)-1;
906 
907 int
doask(cx)908 doask(cx) int cx; {
909     extern int asktimer, timelimit;
910 #ifdef CK_RECALL
911     extern int on_recall;
912 #endif /* CK_RECALL */
913     int echochar = 0;
914     int popupflg = 0;
915     int guiflg = 0;
916     int nomsg = 0;
917     int mytimer = 0;
918     int peek = 0;
919 #ifdef CK_APC
920     extern int apcactive, apcstatus;
921 #endif /* CK_APC */
922 
923     char dfbuf[1024];			/* Buffer for default answer */
924     char * dfanswer = NULL;		/* Pointer to it */
925 
926     char vnambuf[VNAML+1];              /* Buffer for variable names */
927     char *vnp = NULL;                   /* Pointer to same */
928 
929     dfbuf[0] = NUL;
930     vnambuf[0] = NUL;
931 
932 #ifdef CK_APC
933     if ( apcactive != APC_INACTIVE && (apcstatus & APC_NOINP) ) {
934         return(success = 0);
935     }
936 #endif /* CK_APC */
937 
938     mytimer = asktimer;                 /* Inherit global ASK timer */
939     echostars = 0;			/* For ASKQ */
940 
941     if (cx == XXASK || cx == XXASKQ) {
942         struct FDB sw, fl;
943         int getval;
944         char c;
945         if (cx == XXASKQ)               /* Don't log ASKQ response */
946           debok = 0;
947         cmfdbi(&sw,                     /* First FDB - command switches */
948                _CMKEY,                  /* fcode */
949                "Variable name or switch",
950                "",                      /* default */
951                "",                      /* addtl string data */
952 	       ((cx == XXASK) ? nasktab : naskqtab), /* Table size */
953                4,                       /* addtl numeric data 2: 4 = cmswi */
954                xxstring,                /* Processing function */
955 	       ((cx == XXASK) ? asktab : askqtab), /* Keyword table */
956                &fl                      /* Pointer to next FDB */
957                );
958         cmfdbi(&fl,                     /* Anything that doesn't match */
959                _CMFLD,                  /* fcode */
960                "",                      /* hlpmsg */
961                "",                      /* default */
962                "",                      /* addtl string data */
963                0,                       /* addtl numeric data 1 */
964                0,                       /* addtl numeric data 2 */
965                NULL,
966                NULL,
967                NULL
968                );
969         while (1) {                     /* Parse 0 or more switches */
970             x = cmfdb(&sw);             /* Parse something */
971             if (x < 0)
972               return(x);
973             if (cmresult.fcode != _CMKEY) /* Break out if not a switch */
974               break;
975             c = cmgbrk();
976             if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
977                 printf("?This switch does not take an argument\n");
978                 return(-9);
979             }
980             if (!getval && (cmgkwflgs() & CM_ARG)) {
981                 printf("?This switch requires an argument\n");
982                 return(-9);
983             }
984             switch (cmresult.nresult) {
985 	      case ASK_QUI:
986 		nomsg = 1;
987 		if (cx == XXASKQ)
988 		  echostars = 0;
989 		break;
990               case ASK_PUP:
991                 popupflg = 1;
992                 break;
993 	      case ASK_GUI:
994 		guiflg = 1;
995 		break;
996               case ASK_TMO: {
997                   if ((y = cmnum("seconds","1",10,&x,xxstring)) < 0)
998                     return(y);
999                   if (x < 0)
1000                     x = 0;
1001                   mytimer = x;
1002                   break;
1003               }
1004               case ASK_ECH: {
1005                   if ((y = cmfld("Character to echo","*",&s,xxstring)) < 0)
1006                     return(y);
1007 		  echochar = *s;
1008                   break;
1009               }
1010               case ASK_DEF: {
1011                   if ((y = cmfld("Text to supply if reply is empty",
1012 				 "",&s,xxstring)) < 0)
1013                     return(y);
1014 		  ckstrncpy(dfbuf,s,1024);
1015 		  dfanswer = dfbuf;
1016                   break;
1017               }
1018               default: return(-2);
1019             }
1020         }
1021         /* Have variable name, make copy. */
1022         ckstrncpy(vnambuf,cmresult.sresult,VNAML);
1023         vnp = vnambuf;
1024         if (vnambuf[0] == CMDQ &&
1025             (vnambuf[1] == '%' || vnambuf[1] == '&'))
1026           vnp++;
1027         y = 0;
1028         if (*vnp == '%' || *vnp == '&') {
1029             if ((y = parsevar(vnp,&x,&z)) < 0)
1030 	      return(y);
1031         }
1032     } else if (cx == XXGETC) {          /* GETC */
1033         struct FDB sw, fl;
1034         int getval;
1035         char c;
1036         cmfdbi(&sw,                     /* First FDB - command switches */
1037                _CMKEY,                  /* fcode */
1038                "Variable name or switch",
1039                "",                      /* default */
1040                "",                      /* addtl string data */
1041                ngetctab,                /* Table size */
1042                4,                       /* addtl numeric data 2: 4 = cmswi */
1043                xxstring,                /* Processing function */
1044 	       getctab,                 /* Keyword table */
1045                &fl                      /* Pointer to next FDB */
1046                );
1047         cmfdbi(&fl,                     /* Anything that doesn't match */
1048                _CMFLD,                  /* fcode */
1049                "",                      /* hlpmsg */
1050                "",                      /* default */
1051                "",                      /* addtl string data */
1052                0,                       /* addtl numeric data 1 */
1053                0,                       /* addtl numeric data 2 */
1054                NULL,
1055                NULL,
1056                NULL
1057                );
1058         while (1 && !peek) {            /* Parse 0 or more switches */
1059             x = cmfdb(&sw);             /* Parse something */
1060             if (x < 0)
1061               return(x);
1062             if (cmresult.fcode != _CMKEY) /* Break out if not a switch */
1063               break;
1064             c = cmgbrk();
1065             if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
1066                 printf("?This switch does not take an argument\n");
1067                 return(-9);
1068             }
1069             if (!getval && (cmgkwflgs() & CM_ARG)) {
1070                 printf("?This switch requires an argument\n");
1071                 return(-9);
1072             }
1073             switch (cmresult.nresult) {
1074 	      case GETC_CHK:            /* GETC /CHECK */
1075 		peek = 1;
1076 		break;
1077 	      case GETC_QUI:            /* GETC /QUIET */
1078 		nomsg = 1;
1079 		break;
1080               case GETC_TMO: {          /* GETC /TIMEOUT:sec */
1081                   if ((y = cmnum("seconds","1",10,&x,xxstring)) < 0)
1082                     return(y);
1083                   if (x < 0)
1084                     x = 0;
1085                   mytimer = x;
1086                   break;
1087               }
1088               default: return(-2);
1089             }
1090         }
1091         if (peek) {                     /* GETC /CHECK */
1092 /*
1093   This was intended to mean "check how many characters are waiting to be
1094   read from standard input".  Conchk() was supposed to do that but it
1095   doesn't when stdin is redirected.  The best I can do is ask isatty(0)
1096   whether stdin is a terminal.  If not we'll assume it's redirected stdin.
1097   Btw, even if stdin really is a terminal conchk() returns 0, even if
1098   there is typeahead.  - fdc, 21 Apr 2017.
1099 */
1100             int itsatty = -1;
1101             if ((y = cmcfm()) < 0)      /* Get confirmation */
1102               return(y);
1103             itsatty = isatty(0);        /* Is stdin a tty? */
1104             debug(F101,"GETC peek","",peek);
1105             debug(F101,"GETC itsatty","",itsatty);
1106             return(success = (itsatty > 0) ? 0 : 1);
1107         }
1108 
1109         /* Regular GETC... Have variable name, make copy. */
1110         ckstrncpy(vnambuf,cmresult.sresult,VNAML);
1111         vnp = vnambuf;
1112         if (vnambuf[0] == CMDQ &&
1113             (vnambuf[1] == '%' || vnambuf[1] == '&'))
1114           vnp++;
1115         y = 0;
1116         if (*vnp == '%' || *vnp == '&') {
1117             if ((y = parsevar(vnp,&x,&z)) < 0)
1118               return(y);
1119         }
1120     } else if (cx != XXGOK && cx != XXRDBL && !peek) { /* Get variable name */
1121         if ((y = cmfld("Variable name","",&s,NULL)) < 0) {
1122             if (y == -3) {
1123                 printf("?Variable name required\n");
1124                 return(-9);
1125             } else return(y);
1126         }
1127         ckstrncpy(vnambuf,s,VNAML);     /* Make a copy. */
1128         vnp = vnambuf;
1129         if (vnambuf[0] == CMDQ &&
1130             (vnambuf[1] == '%' || vnambuf[1] == '&'))
1131           vnp++;
1132         y = 0;
1133         if (*vnp == '%' || *vnp == '&') {
1134             if ((y = parsevar(vnp,&x,&z)) < 0)
1135               return(y);
1136         }
1137     }
1138     if (cx == XXREA || cx == XXRDBL) {  /* READ or READBLOCK command */
1139         if ((y = cmcfm()) < 0)          /* Get confirmation */
1140           return(y);
1141         if (chkfn(ZRFILE) < 1) {        /* File open? */
1142             printf("?Read file not open\n");
1143             return(success = 0);
1144         }
1145         if (!(s = (char *)readbuf)) {           /* Where to read into. */
1146             printf("?Oops, no READ buffer!\n");
1147             return(success = 0);
1148         }
1149         y = zsinl(ZRFILE, s, readblock); /* Read a line. */
1150         debug(F111,"read zsinl",s,y);
1151         if (y < 0) {                    /* On EOF or other error, */
1152             zclose(ZRFILE);             /* close the file, */
1153             delmac(vnp,0);              /* delete the variable, */
1154             return(success = 0);        /* and return failure. */
1155         } else {                        /* Read was OK. */
1156             readsize = (int) strlen(s);
1157             success = (addmac(vnp,s) < 0 ? 0 : 1); /* Define variable */
1158             debug(F111,"read addmac",vnp,success);
1159             return(success);            /* Return success. */
1160         }
1161     }
1162 
1163     /* ASK, ASKQ, GETOK */
1164 
1165     if (cx == XXGOK) {			/* GETOK can take switches */
1166         struct FDB sw, fl;
1167         int getval;
1168         char c;
1169         cmfdbi(&sw,                     /* First FDB - command switches */
1170                _CMKEY,                  /* fcode */
1171                "Variable name or question prompt",
1172                "",                      /* default */
1173                "",                      /* addtl string data */
1174                nasktab,                 /* addtl numeric data 1: tbl size */
1175                4,                       /* addtl numeric data 2: 4 = cmswi */
1176                xxstring,                /* Processing function */
1177                asktab,                  /* Keyword table */
1178                &fl                      /* Pointer to next FDB */
1179                );
1180         cmfdbi(&fl,                     /* Anything that doesn't match */
1181                _CMTXT,                  /* fcode */
1182                "",                      /* hlpmsg */
1183                "",                      /* default */
1184                "",                      /* addtl string data */
1185                0,                       /* addtl numeric data 1 */
1186                0,                       /* addtl numeric data 2 */
1187                NULL,
1188                NULL,
1189                NULL
1190                );
1191         while (1) {                     /* Parse 0 or more switches */
1192             x = cmfdb(&sw);             /* Parse something */
1193             if (x < 0)
1194               return(x);
1195             if (cmresult.fcode != _CMKEY) /* Break out if not a switch */
1196               break;
1197             c = cmgbrk();
1198             if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
1199                 printf("?This switch does not take an argument\n");
1200                 return(-9);
1201             }
1202             if (!getval && (cmgkwflgs() & CM_ARG)) {
1203                 printf("?This switch requires an argument\n");
1204                 return(-9);
1205             }
1206             switch (cmresult.nresult) {
1207               case ASK_PUP:
1208                 popupflg = 1;
1209                 break;
1210 	      case ASK_GUI:
1211 		guiflg = 1;
1212 		break;
1213               case ASK_TMO: {
1214                   if ((y = cmnum("seconds","1",10,&x,xxstring)) < 0)
1215                     return(y);
1216                   if (x < 0)
1217                     x = 0;
1218                   mytimer = x;
1219                   break;
1220               }
1221               case ASK_DEF: {
1222                   if ((y = cmfld("Text to supply if reply is empty",
1223 				 "",&s,xxstring)) < 0)
1224                     return(y);
1225 		  ckstrncpy(dfbuf,s,1024);
1226 		  dfanswer = dfbuf;
1227                   break;
1228               }
1229 	      case ASK_QUI:
1230 		nomsg = 1;
1231 		break;
1232               default: return(-2);
1233             }
1234 	}
1235 	p = cmresult.sresult;
1236     } else
1237       if ((y = cmtxt(
1238 "Prompt,\n\
1239  enclose in { braces } or \" quotes \" to preserve leading and trailing\n\
1240  spaces, precede question mark with backslash (\\).",
1241                    "",&p,xxstring)) < 0)
1242         return(y);
1243 
1244     if (!p) p = "";
1245 #ifndef NOLOCAL
1246 #ifdef OS2
1247     if (popupflg) {                     /* Popup requested */
1248         int len = -1;
1249         ckstrncpy(tmpbuf,brstrip(p),TMPBUFSIZ);
1250         p = tmpbuf;
1251         if (cx == XXASK || cx == XXASKQ) {
1252             if (cx == XXASK)
1253               len = popup_readtext(vmode,NULL,p,line,LINBUFSIZ,mytimer);
1254             else
1255               len = popup_readpass(vmode,NULL,p,line,LINBUFSIZ,mytimer);
1256             asktimedout = ( len < 0 && mytimer );
1257         } else if (cx == XXGOK) {
1258 	    printf("?Sorry, GETOK /POPUP not implemented yet\n");
1259 	    timelimit = 0;
1260 	    return(-9);
1261 	}
1262         if (len >= 0) {
1263 	    y = addmac(vnp,(char *)line); /* Add it to the macro table. */
1264         } else if ( asktimedout && dfanswer ) {
1265             y = addmac(vnp,dfanswer);	    /* Add it to the macro table. */
1266             asktimedout = 0;
1267             len = 0;
1268         }
1269         timelimit = 0;
1270         return(success = ((len >= 0) && (y >= 0)) && !asktimedout);
1271     }
1272 #ifdef KUI
1273     if (guiflg) {                       /* GUI requested */
1274         int rc, n;
1275 	char * s1;
1276 	s1 = tmpbuf;
1277 	n = TMPBUFSIZ-1;
1278 	zzstring(brstrip(p),&s1,&n);
1279         p = tmpbuf;
1280         if (cx == XXASK || cx == XXASKQ) {
1281             rc = gui_txt_dialog(NULL,p,(cx == XXASK),
1282                                 line,LINBUFSIZ,dfanswer,mytimer);
1283             asktimedout = (rc == -1 && mytimer);
1284             if (rc == 1) {
1285                 y = addmac(vnp,(char *)line); /* Add it to the macro table. */
1286             } else if ( asktimedout && dfanswer ) {
1287                 y = addmac(vnp,dfanswer); /* Add default to macro table. */
1288                 asktimedout = 0;
1289                 rc = 1;
1290             }
1291 	    timelimit = 0;
1292 	    return(success = (rc == 1 && (y >= 0)) && !asktimedout);
1293 	} else if (cx == XXGOK) {
1294 	    int x;
1295 	    x = lookup(yesno,dfanswer,nyesno,NULL);
1296 	    if (x != 1) x = 2;
1297 	    rc = uq_ok(NULL, p, 3, NULL, x);
1298 	    return(success = (rc == 1));
1299 	}
1300     }
1301 #endif /* KUI */
1302 #endif /* OS2 */
1303 #endif /* NOLOCAL */
1304 
1305     concb((char)escape);                /* Enter CBREAK mode */
1306     cmsavp(psave,PROMPTL);              /* Save old prompt */
1307     cmsetp(brstrip(p));                 /* Make new prompt */
1308 
1309 reprompt:
1310     if (cx == XXASKQ) {                 /* For ASKQ, */
1311         cmini(0);                       /* no-echo mode. */
1312 	if (echochar)
1313 	  echostars = echochar;
1314     } else {                            /* For others, regular echoing. */
1315         cmini(ckxech);
1316 	echostars = 0;
1317     }
1318     askflag = 1;
1319     x = -1;                             /* This means to reparse. */
1320     cmflgs = 0;
1321     if (pflag)
1322       prompt(xxstring);                 /* Issue prompt. */
1323 
1324     asktimedout = 0;                    /* Handle timed responses. */
1325     timelimit = mytimer;
1326 reparse:
1327     cmres();
1328     if (cx == XXGOK) {                  /* GETOK */
1329 #ifdef CK_RECALL
1330         on_recall = 0;
1331 #endif /* CK_RECALL */
1332         askflag = 0;
1333 	/* GETOK uses keyword table */
1334         x = cmkey(yesno,nyesno,"",dfanswer,xxstring);
1335         if (x < 0) {                    /* Parse error */
1336             if (x == -10) {
1337 		char * ds;
1338 		ds = dfanswer ? dfanswer : "No";
1339 		if (!nomsg)
1340 		  printf("?Timed out, assuming \"%s\"",ds);
1341 		printf("\n");
1342                 asktimedout = 1;
1343 		x = lookup(yesno,ds,nyesno,NULL);
1344 		if (x != 1) x = 0;
1345                 goto gokdone;
1346             } else if (x == -3) {       /* No answer? */
1347                 printf("Please respond Yes or No\n"); /* Make them answer */
1348                 cmini(ckxech);
1349                 goto reprompt;
1350             } else if (x == -1) {
1351                 goto reparse;
1352             } else
1353               goto reprompt;
1354         }
1355         if (cmcfm() < 0)                /* Get confirmation */
1356           goto reparse;
1357   gokdone:
1358         askflag = 0;
1359         cmsetp(psave);                  /* Restore prompt */
1360 #ifdef VMS
1361         if (cmdlvl > 0)                 /* In VMS and not at top level, */
1362           conres();                     /*  restore console again. */
1363 #endif /* VMS */
1364         timelimit = 0;
1365         return(x);                      /* Return success or failure */
1366     } else if (cx == XXGETC             /* GETC */
1367 #ifdef OS2
1368                || cx == XXGETK          /* or GETKEYCODE */
1369 #endif /* OS2 */
1370                ) {                      /* GETC */
1371         char tmp[16];
1372         conbin((char)escape);           /* Put keyboard in raw mode */
1373 #ifndef NOSETKEY
1374 #ifdef OS2
1375         if (cx == XXGETK) {             /* GETKEYCODE */
1376             extern int os2gks;
1377             int t;
1378             t = os2gks;                 /* Turn off kverb recognition */
1379             os2gks = 0;
1380             x = congks(timelimit);      /* Read a key event, blocking */
1381             os2gks = t;                 /* Put back kverb recognition */
1382         } else                          /* GETC */
1383 #endif /* OS2 */
1384 #endif /* NOSETKEY */
1385         {
1386             x = coninc(timelimit);      /* Read one character */
1387             debug(F101,"GETC coninc","",x);
1388         }
1389         concb((char)escape);            /* Put keyboard back in cbreak mode */
1390         if (x > -1) {
1391             if (xcmdsrc == 0)
1392               printf("\r\n");
1393 #ifdef OS2
1394             if (cx == XXGETK) {         /* GETKEYCODE */
1395                 sprintf(tmp,"%d",x);    /* SAFE */
1396             } else {
1397 #endif /* OS2 */
1398                 tmp[0] = (char) (x & 0xff);
1399                 tmp[1] = NUL;
1400 #ifdef OS2
1401             }
1402 #endif /* OS2 */
1403             y = addmac(vnp,tmp);        /* Add it to the macro table. */
1404             debug(F111,"getc/getk addmac",vnp,y);
1405         } else y = -1;
1406         cmsetp(psave);                  /* Restore old prompt. */
1407         if (x < -1) {
1408             asktimedout = 1;
1409             if (!quiet && !nomsg)
1410               printf("?Timed out");
1411 	    printf("\n");
1412         }
1413         timelimit = 0;
1414         return(success = ((y < 0 ? 0 : 1) && (asktimedout == 0)));
1415     } else {                            /* ASK or ASKQ */
1416 #ifdef CK_RECALL
1417         on_recall = 0;			/* Don't put response in recall buf */
1418 #endif /* CK_RECALL */
1419 	askflag = 1;			/* ASK[Q] always goes to terminal */
1420         y = cmdgquo();                  /* Get current quoting */
1421         cmdsquo(0);                     /* Turn off quoting */
1422         while (x == -1) {               /* Prompt till they answer */
1423             x = cmtxt("Please respond.",dfanswer,&s,NULL);
1424             debug(F111,"ASK cmtxt",s,x);
1425             cmres();
1426         }
1427         cmdsquo(y);                     /* Restore previous quoting */
1428         if (cx == XXASKQ)               /* ASKQ must echo CRLF here */
1429           printf("\r\n");
1430 	if (x == -10 && dfanswer) {	/* Don't fail on timeout if */
1431 	    s = dfanswer;		/* a default was specified */
1432 	    asktimedout = 0;		/* and don't fail */
1433 	    x = 0;
1434 	}
1435         if (x < 0) {                    /* If cmtxt parse error, */
1436             cmsetp(psave);              /* restore original prompt */
1437 #ifdef VMS
1438             if (cmdlvl > 0)             /* In VMS and not at top level, */
1439               conres();                 /*  restore console again. */
1440 #endif /* VMS */
1441             if (x == -10) {		/* Timed out with no response */
1442 		if (!nomsg)
1443 		  printf("?Timed out");
1444 		printf("\n");
1445                 asktimedout = 1;
1446 		if (dfanswer)		/* Supply default answer if any */
1447 		  s = dfanswer;
1448                 success = x = 0;	/* (was "x = -9;") */
1449             }
1450             timelimit = 0;
1451             return(x);                  /* and return cmtxt's error code. */
1452         }
1453         if (!s || *s == NUL) {		/* If user typed a bare CR, */
1454             cmsetp(psave);              /* Restore old prompt, */
1455             delmac(vnp,0);              /* delete variable if it exists, */
1456 #ifdef VMS
1457             if (cmdlvl > 0)             /* In VMS and not at top level, */
1458               conres();                 /*  restore console again. */
1459 #endif /* VMS */
1460             timelimit = 0;
1461             return(success = 1);        /* and return. */
1462         }
1463         y = addmac(vnp,s);              /* Add it to the macro table. */
1464         debug(F111,"ask addmac",vnp,y);
1465         cmsetp(psave);                  /* Restore old prompt. */
1466 #ifdef VMS
1467         if (cmdlvl > 0)                 /* In VMS and not at top level, */
1468           conres();                     /*  restore console again. */
1469 #endif /* VMS */
1470         timelimit = 0;
1471         return(success = (y < 0 ? 0 : 1) && (asktimedout == 0));
1472     }
1473 }
1474 #endif /* NOSPL */
1475 
1476 #ifndef NOSPL
1477 int
doincr(cx)1478 doincr(cx) int cx; {                    /* INCREMENT, DECREMENT */
1479     char vnambuf[VNAML+1];              /* Buffer for variable names */
1480     int eval = 0;
1481     CK_OFF_T x;
1482     eval = (cx == XX_DECR || cx == XX_INCR);
1483 
1484     if ((y = cmfld("Variable name","",&s, eval ? xxstring : NULL)) < 0) {
1485         if (y == -3) {
1486             printf("?Variable name required\n");
1487             return(-9);
1488         } else return(y);
1489     }
1490     ckstrncpy(vnambuf,s,VNAML);
1491     if ((y = cmnumw("by amount","1",10,&x,xxstring)) < 0)
1492       return(y);
1493     if ((y = cmcfm()) < 0)
1494       return(y);
1495 
1496     z = (cx == XX_INCR || cx == XXINC) ? 1 : 0; /* Increment or decrement? */
1497 
1498     if (incvar(vnambuf,x,z) < 0) {
1499         printf("?Variable %s not defined or not numeric\n",vnambuf);
1500         return(success = 0);
1501     }
1502     return(success = 1);
1503 }
1504 
1505 /* Used by doundef() */
1506 static int
xxundef(s,verbose,simulate)1507 xxundef(s,verbose,simulate) char * s; int verbose, simulate; {
1508     int rc = 0;
1509     if (!s) return(0);
1510     if (*s == CMDQ && *(s+1) == '%') {
1511         char c = *(s+2), * p = NULL;
1512         if (c >= '0' && c <= '9') {
1513             if (maclvl < 0)
1514               p = g_var[c];
1515             else
1516               p = m_arg[maclvl][c - '0'];
1517         } else {
1518             if (isupper(c)) c += ('a'-'A');
1519             if (c >= 'a' && c <= 'z')
1520               p = g_var[c];
1521         }
1522         if (!p) return(-1);
1523     }
1524     if (verbose)
1525       printf(" %s ",s);
1526     if (simulate) {
1527         printf("(SELECTED)\n");
1528     } else if ((x = delmac(s,1)) > -1) { /* Full name required */
1529         rc = 1;
1530         if (verbose) printf("(OK)\n");
1531     } else if (verbose)
1532       printf("(FAILED)\n");
1533     return(rc);
1534 }
1535 
1536 /* Do the (_)DEFINE, (_)ASSIGN, and UNDEFINE commands */
1537 
1538 #define UND_MAT 1
1539 #define UND_VRB 2
1540 #define UND_EXC 3
1541 #define UND_SIM 3
1542 
1543 static struct keytab undefswi[] = {
1544     { "/list",     UND_VRB, 0 },
1545 #ifdef COMMENT
1546     { "/except",   UND_EXC, CM_ARG },
1547 #endif /* COMMENT */
1548     { "/matching", UND_MAT, 0 },
1549     { "/simulate", UND_SIM, 0 },
1550     { "/verbose",  UND_VRB, CM_INV }
1551 };
1552 static int nundefswi = sizeof(undefswi) / sizeof(struct keytab);
1553 
1554 #define UNDEFMAX 64
1555 static char ** undeflist = NULL;
1556 int
doundef(cx)1557 doundef(cx) int cx; {                   /* UNDEF, _UNDEF */
1558     int i, j, n, rc = 0, arraymsg = 0;
1559     int domatch = 0, verbose = 0, errors = 0, simulate = 0, flag = 0;
1560     char *vnp, vnbuf[4];
1561 #ifdef COMMENT
1562     char *except = NULL;
1563 #endif /* COMMENT */
1564     struct FDB sw, fl;
1565     int getval;
1566     char c;
1567 
1568     if (!undeflist) {                   /* Allocate list if necessary */
1569         undeflist = (char **)malloc(UNDEFMAX * sizeof(char *));
1570         if (!undeflist) {
1571             printf("?Memory allocation failure\n");
1572             return(-9);
1573         }
1574         for (i = 0; i < UNDEFMAX; i++)
1575           undeflist[i] = NULL;
1576     }
1577     cmfdbi(&sw,                         /* First FDB - command switches */
1578            _CMKEY,                      /* fcode */
1579            "Variable name or switch",
1580            "",                          /* default */
1581            "",                          /* addtl string data */
1582            nundefswi,                   /* addtl numeric data 1: tbl size */
1583            4,                           /* addtl numeric data 2: 4 = cmswi */
1584            xxstring,                    /* Processing function */
1585            undefswi,                    /* Keyword table */
1586            &fl                          /* Pointer to next FDB */
1587            );
1588     cmfdbi(&fl,                         /* Anything that doesn't match */
1589            _CMFLD,                      /* fcode */
1590            "",                          /* hlpmsg */
1591            "",                          /* default */
1592            "",                          /* addtl string data */
1593            0,                           /* addtl numeric data 1 */
1594            0,                           /* addtl numeric data 2 */
1595            (cx == XXUNDEF) ? NULL : xxstring,
1596            NULL,
1597            NULL
1598            );
1599     while (1) {                         /* Parse 0 or more switches */
1600         x = cmfdb(&sw);                 /* Parse something */
1601         if (x < 0)
1602           return(x);
1603         if (cmresult.fcode != _CMKEY)   /* Break out if not a switch */
1604           break;
1605         c = cmgbrk();
1606         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
1607             printf("?This switch does not take an argument\n");
1608             return(-9);
1609         }
1610         switch (cmresult.nresult) {
1611           case UND_MAT: domatch  = 1; break;
1612           case UND_SIM: simulate = 1; /* fall thru on purpose */
1613           case UND_VRB: verbose  = 1; break;
1614 
1615 #ifdef COMMENT
1616           case UND_EXC:
1617             if (!getval) break;
1618             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
1619                 if (x == -3) {
1620                     printf("?Pattern required\n");
1621                     x = -9;
1622                 }
1623                 goto xgetx;
1624             }
1625             makestr(&except,cmresult.sresult);
1626             break;
1627 #endif /* COMMENT */
1628 
1629           default:
1630             return(-2);
1631         }
1632     }
1633     n = 0;
1634     makestr(&(undeflist[n++]),cmresult.sresult);
1635     for (i = 1; i < UNDEFMAX; i++) {
1636         x = cmfld("Macro or variable name","",&s,
1637                   ((cx == XXUNDEF) ? NULL : xxstring)
1638                   );
1639         if (x == -3) {
1640             if ((y = cmcfm()) < 0)
1641               return(y);
1642             break;
1643         } else if (y < 0) {
1644             return(y);
1645         }
1646         makestr(&(undeflist[n++]),s);
1647     }
1648     /* Now we have a list of n variables or patterns to undefine */
1649 
1650     for (i = 0; i < n; i++) {
1651         flag = 0;
1652         if (!(vnp = undeflist[i]))
1653           continue;
1654         if (vnp[0] == CMDQ && (vnp[1] == '%' || vnp[1] == '&')) {
1655             flag++;
1656             vnp++;
1657         }
1658         if (!domatch) {                 /* Pattern match not requested */
1659             if (flag) {
1660                 if ((y = parsevar(vnp,&x,&z)) < 0) {
1661                     vnp--;
1662                     if (verbose) printf(" %s...error\n",vnp);
1663                     continue;
1664                 }
1665                 vnp--;
1666             }
1667             x = xxundef(vnp,verbose,simulate);
1668             if (x > -1) {
1669                 if (!x && !simulate) errors++;
1670                 rc += x;
1671             }
1672             continue;
1673         }
1674         /* Pattern match requested */
1675 
1676         if (!flag) {                    /* It's a macro */
1677             for (j = 0; j < nmac; j++) {
1678                 if (ckmatch(vnp,mactab[j].kwd,0,1)) {
1679                     x = xxundef(mactab[j].kwd,verbose,simulate);
1680                     if (x > -1) {
1681                         rc += x;
1682                         if (!x) errors++;
1683                     }
1684                     if (!simulate)
1685                       j--;              /* Because mactab shifted up */
1686                 }
1687             }
1688         } else if (vnp[0] == '%') {     /* It's a \%x variable */
1689             vnbuf[0] = CMDQ;
1690             vnbuf[1] = '%';
1691             vnbuf[3] = NUL;
1692             for (j = '0'; j <= 'z'; j++) { /* 0..9 a..z */
1693                 vnbuf[2] = j;
1694                 if (ckmatch(vnp,&vnbuf[1],0,1)) {
1695                     x = xxundef(vnbuf,verbose,simulate);
1696                     if (x > -1) {
1697                         if (!x) errors++;
1698                         rc += x;
1699                     }
1700                 }
1701                 if (j == '9') j = (int)'a' - 1; /* 9 -> a */
1702             }
1703         } else if (vnp[0] == '&') {
1704             if (!arraymsg && !quiet) {
1705                 printf("?UNDEFINE /MATCH can't be used with arrays.\n");
1706                 printf("(Type HELP ARRAY to see other methods.)\n");
1707             }
1708             arraymsg++;
1709             errors++;
1710         }
1711     }
1712     if (verbose)
1713       printf("undefined: %d, errors: %d\n",rc,errors);
1714 
1715     for (i = 0; i < UNDEFMAX; i++) {    /* Check them all */
1716         if (undeflist[i]) {             /* in case we were interrupted */
1717             free(undeflist[i]);         /* previously... */
1718             undeflist[i] = NULL;
1719         }
1720     }
1721     return(success = (errors == 0) ? 1 : 0);
1722 }
1723 
1724 int
dodef(cx)1725 dodef(cx) int cx; {
1726     extern int xxdot;
1727     extern char ppvnambuf[];
1728     int doeval = 0;
1729     char vnambuf[VNAML+1];              /* Buffer for variable names */
1730     char *vnp;                          /* Pointer to same */
1731     int k, mydot;
1732     mydot = xxdot;                      /* Copy */
1733     xxdot = 0;                          /* and reset */
1734 /*
1735   In case we got here from a command that begins like ".\%a", cmkey() has
1736   already evaluated \%a, but we don't want that, so we retrieve the variable
1737   name from a special pre-evaluation buffer in the command module, and we
1738   undo the "unget word" that would be done because of the token, because if
1739   the variable was defined, it will unget its value rather than its name.
1740 */
1741     s = NULL;
1742 
1743     if (mydot && ppvnambuf[0] == '.' && ppvnambuf[1]) {
1744         s = ppvnambuf+1;
1745         unungw();
1746     }
1747     if (!s) {
1748         if (cx == XXDFX || cx == XXASX)
1749           /* Evaluate variable name */
1750           y = cmfld("Macro or variable name","",&s,xxstring);
1751         else
1752           /* Don't evaluate the variable name */
1753           y = cmfld("Macro or variable name","",&s,NULL);
1754         if (y < 0) {
1755             if (y == -3) {
1756                 printf("?Variable name required\n");
1757                 return(-9);
1758             } else return(y);
1759         }
1760     }
1761     k = strlen(s);
1762     if (k > VNAML) {
1763         printf("?Name too long: \"%s\"\n",s);
1764         return(-9);
1765     }
1766     ckstrncpy(vnambuf,s,VNAML);
1767     vnambuf[VNAML] = NUL;
1768     vnp = vnambuf;
1769     if (vnambuf[0] == CMDQ && (vnambuf[1] == '%' || vnambuf[1] == '&')) vnp++;
1770     if (*vnp == '%' || *vnp == '&') {
1771         if ((y = parsevar(vnp,&x,&z)) < 0) return(y);
1772 #ifdef COMMENT
1773         if (cx == XXUNDEF) {            /* Undefine */
1774             if ((y = cmtxt("Text to be ignored","",&s,NULL)) < 0) return(y);
1775             delmac(vnp,0);
1776             return(success = 1);
1777         }
1778 #endif /* COMMENT */
1779         debug(F101,"dodef parsevar x","",x);
1780         if (mydot) {
1781             if ((doeval = cmkey(asgtab,nasgtab,"operator","=",NULL)) < 0)
1782               return(doeval);
1783             if (doeval > 0)             /* Type of assignment */
1784               cx = XXASS;
1785         }
1786         if (y == 1) {                   /* Simple variable */
1787             if ((y = cmtxt("Definition of variable","",&s,NULL)) < 0)
1788               return(y);
1789             s = brstrip(s);
1790             debug(F110,"xxdef var name",vnp,0);
1791             debug(F110,"xxdef var def",s,0);
1792         } else if (y == 2) {            /* Array element */
1793             if ((y = arraynam(vnp,&x,&z)) < 0) return(y);
1794             if (x == 96) {
1795                 printf("?Argument vector array is read-only\n");
1796                 return(-9);
1797             }
1798             if (chkarray(x,z) < 0) return(-2);
1799             if ((y = cmtxt("Definition of array element","",&s,NULL)) < 0)
1800               return(y);
1801             debug(F110,"xxdef array ref",vnp,0);
1802             debug(F110,"xxdef array def",s,0);
1803         }
1804     } else {                            /* Macro */
1805 #ifdef COMMENT
1806         if (cx == XXUNDEF) {            /* Undefine */
1807             if ((y = cmtxt("Text to be ignored","",&s,NULL)) < 0) return(y);
1808             delmac(vnp,0);
1809             return(success = 1);
1810         }
1811 #endif /* COMMENT */
1812         if (mydot) {
1813             if ((doeval = cmkey(asgtab,nasgtab,"operator","=",NULL)) < 0)
1814               return(doeval);
1815             if (doeval > 0)
1816               cx = XXASS;
1817         }
1818         if ((y = cmtxt("Definition of macro","",&s,NULL)) < 0) return(y);
1819 #ifdef DEBUG
1820         if (deblog) {
1821             debug(F110,"xxdef macro name",vnp,0);
1822             debug(F010,"xxdef macro def",s,0);
1823         }
1824 #endif /* DEBUG */
1825         s = brstrip(s);
1826     }
1827     if (*s == NUL) {                    /* No arg given, undefine */
1828         delmac(vnp,1);                  /* silently... */
1829         return(success = 1);            /* even if it doesn't exist... */
1830     }
1831     /* Defining a new macro or variable */
1832 
1833     if (cx == XXASS || cx == XXASX) {   /* ASSIGN rather than DEFINE? */
1834         int t;
1835         t = LINBUFSIZ-1;
1836         lp = line;                      /* If so, expand its value now */
1837         zzstring(s,&lp,&t);
1838         s = line;
1839     }
1840     if (doeval == 2) {                  /* Arithmetic evaluation wanted too? */
1841         ckstrncpy(line,evala(s),LINBUFSIZ);
1842         line[LINBUFSIZ] = NUL;
1843     }
1844     /* debug(F111,"calling addmac",s,(int)strlen(s)); */
1845 
1846     y = addmac(vnp,s);                  /* Add it to the appropriate table. */
1847     if (y < 0) {
1848         printf("?%s failed\n",(cx == XXASS || cx == XXASX) ?
1849                "ASSIGN" : "DEFINE");
1850         return(success = 0);
1851     } else if (cx == XXASX || cx == XXDFX) /* For _ASG or _DEF, */
1852       return(1);                           /* don't change success variable */
1853     else
1854       return(success = 1);
1855 }
1856 #endif /* NOSPL */
1857 
1858 
1859 #ifndef NODIAL
1860 /*
1861    L U D I A L  --  Lookup up dialing directory entry.
1862 
1863    Call with string to look up and file descriptor of open dialing directory
1864    file.  On success, returns number of matches found, with numbers stored
1865    in an array accessible via getdnum().
1866 */
1867 static char *dn_p[MAXDNUMS + 1];        /* Dial Number pointers */
1868 static char *dn_p2[MAXDNUMS + 1];       /* Converted dial number pointers */
1869 static int dn_x[MAXDNUMS + 1];          /* Type of call */
1870 static int dncount = 0;
1871 char * d_name = NULL;                   /* Dial name pointer */
1872 
1873 char *                                  /* Get dial directory entry name */
getdname()1874 getdname() {
1875     return(d_name ? d_name : "");
1876 }
1877 
1878 char *
getdnum(n)1879 getdnum(n) int n; {                     /* Get dial number n from directory */
1880     if (n < 0 || n > dncount || n > MAXDNUMS)
1881       return("");
1882     else
1883       return(dn_p[n]);
1884 }
1885 
1886 char *                  /* Check area code for spurious leading digit */
chk_ac(i,buf)1887 chk_ac(i,buf) int i; char buf[]; {
1888     char *p;
1889     if (!buf)
1890       return("");
1891     p = (char *) buf;                   /* Country we are calling: */
1892     if (i ==  44 ||                     /* UK */
1893         i ==  49 ||                     /* Germany */
1894         i ==  39 ||                     /* Italy */
1895         i ==  31 ||                     /* Netherlands */
1896         i == 351 ||                     /* Portugal */
1897         i ==  55 ||                     /* Brazil */
1898         i == 972 ||                     /* Israel */
1899         i ==  41 ||                     /* Switzerland */
1900         i ==  43 ||                     /* Austria */
1901         i ==  42 ||                     /* Czech Republic */
1902         i ==  36 ||                     /* Hungary */
1903         i ==  30 ||                     /* Greece */
1904         i == 352 ||                     /* Luxembourg */
1905         i ==  48 ||                     /* Poland */
1906         i ==  27 ||                     /* South Africa */
1907         i ==  33 ||                     /* France (as of 1997) */
1908         i ==  358                       /* Finland (ditto) */
1909         ) {
1910         if (buf[0] == '0')
1911           p++;
1912     }
1913     return(p);
1914 }
1915 
1916 /* Call Is Long Distance -- Expand this to cover 10-digit local dialing etc */
1917 /*
1918    src  = area code of caller
1919    dest = area code of callee
1920    Returns:
1921      0 if call is local
1922      1 if call is long distance
1923      2 if call is local but area code must be dialed anyway
1924 */
1925 static int
callisld(src,dest)1926 callisld(src, dest) char * src, * dest; {
1927     int i;
1928     if (dialfld)                        /* Force long distance? */
1929       return(1);
1930     if (!strcmp(src,dest)) {            /* Area codes are the same */
1931         for (i = 0; i < nlocalac; i++)  /* Is AC in the lc-area-codes list? */
1932           if (!strcmp(src,diallcac[i]))
1933             return(2);                  /* Yes so must be dialed */
1934         return(0);                      /* No so don't dial it. */
1935     }
1936     for (i = 0; i < nlocalac; i++)      /* ACs not the same so look in list */
1937       if (!strcmp(dest,diallcac[i]))    /* Match */
1938         return(2);                      /* So local call with area code */
1939     return(1);                          /* Not local so long-distance */
1940 }
1941 
1942 char pdsfx[64] = { NUL, NUL };
1943 
1944 #ifndef NOSPL
1945 static char *
xdial(s)1946 xdial(s) char *s; {                     /* Run dial string thru macro */
1947     int x, m;
1948     char * s2;
1949     s2 = NULL;
1950     makestr(&s2,s);			/* Copy the argument */
1951     if (!dialmac)                       /* Dial macro name given? */
1952       return(NULL);
1953     if ((x = mxlook(mactab,dialmac,nmac)) < 0) /* Is the macro defined? */
1954       return(NULL);
1955     m = maclvl;
1956     x = dodo(x,s2,0);			/* Set up the macro */
1957     if (s2) free(s2);
1958     if (x > 0) {
1959         while (maclvl > m)              /* Execute the parser */
1960           parser(1);
1961         return(mrval[maclvl+1]);        /* Return the result */
1962     }
1963     return(NULL);
1964 }
1965 #endif /* NOSPL */
1966 
1967 static int
dncvt(k,cx,prefix,suffix)1968 dncvt(k,cx, prefix, suffix)
1969     int k, cx, prefix, suffix; {        /* Dial Number Convert */
1970     int i, j, n, what;                  /* cx is top-level command index */
1971     char *ss;                           /* prefix - add prefixes? */
1972     char *p, *p2, *pxo;                 /* suffix - add suffixes? */
1973     char *lac;
1974     char *npr;
1975     char *sfx;
1976     /* char *psfx; */
1977     char ccbuf[128];
1978     int cc;
1979     char acbuf[24];
1980     char *acptr;
1981     char outbuf[256];
1982 /*
1983   First pass for strict (punctuation-based) interpretation.
1984   If it fails, we try the looser (length-based) one.
1985 */
1986     dialtype = -1;
1987     what = 0;                           /* Type of call */
1988     s = dn_p[k];                        /* Number to be converted. */
1989     debug(F111,"dncvt",s,k);
1990     if (dn_p2[k]) {
1991         free(dn_p2[k]);
1992         dn_p2[k] = NULL;
1993     }
1994     if (!s) {
1995         printf("Error - No phone number to convert\n");
1996         return(-1);
1997     }
1998     if ((int)strlen(s) > 200) {
1999         ckstrncpy(outbuf,s,40);
2000         printf("?Too long: \"%s...\"\n",outbuf);
2001         return(-1);
2002     }
2003     npr = (prefix && dialnpr) ? dialnpr : "";
2004     sfx = (suffix && dialsfx) ? dialsfx : "";
2005     /* if (partial) psfx = dialsfx ? dialsfx : ""; */
2006     pxo = (prefix && dialpxo) ? dialpxo : "";
2007     lac = diallac ? diallac : "";       /* Local area code */
2008 
2009     outbuf[0] = NUL;                    /* Initialize conversion buffer */
2010     ss = s;                             /* Remember original string */
2011 
2012     if (*s != '+') {                    /* Literal number */
2013         dn_x[k] = DN_UNK;               /* Sort key is "unknown". */
2014         ckmakmsg(outbuf,256,            /* Sandwich it between */
2015                  pxo,npr,s,sfx          /* DIAL PREFIX and SUFFIX */
2016                 );
2017 #ifdef CK_TAPI
2018         if (tttapi &&                   /* TAPI does its own conversions */
2019             !tapipass &&                /* if not in passthru mode */
2020             tapiconv == CK_AUTO ||      /* and TAPI conversions are AUTO */
2021             tapiconv == CK_ON           /* OR if TAPI conversions are ON */
2022             ) {
2023             char * p = NULL;
2024             dialtype = -2;
2025             if (!cktapiConvertPhoneNumber(dn_p[k], &p))
2026               return(-1);
2027             makestr(&dn_p2[k], p);
2028             if (p) free(p);
2029             return(0);
2030         } else
2031 #endif /* CK_TAPI */
2032           makestr(&dn_p2[k], outbuf);   /* Not TAPI */
2033         dialtype = what;
2034         return(0);                      /* Done. */
2035     }
2036     i = 0;                              /* Portable number */
2037     s++;                                /* Tiptoe past the plus sign */
2038     ccbuf[0] = NUL;                     /* Do country code first */
2039 
2040     if (!diallcc) {                     /* Do we know our own? */
2041         if (cx != XXLOOK)
2042           printf("Error - prior SET DIAL COUNTRY-CODE command required\n");
2043         return(-1);
2044     }
2045 
2046     /* Parse the number */
2047 
2048     while (1) {                         /* Get the country code */
2049         while (*s == HT || *s == SP)
2050           s++;
2051         if (!s)                         /* Not in standard format */
2052           break;
2053         if (*s == '(') {                /* Beginning of area code  */
2054             s++;                        /* Skip past parenthesis   */
2055             ccbuf[i] = NUL;             /* End of country code */
2056             if (!s) {                   /* Check for end of string */
2057                 printf("Error - phone number ends prematurely: \"%s\"\n",ss);
2058                 return(-1);
2059             }
2060             break;
2061         } else {                        /* Collect country code */
2062             if (isdigit(*s))
2063               ccbuf[i++] = *s;          /* copy this character */
2064             s++;
2065             if (!*s || i > 127)         /* watch out for memory leak */
2066               break;
2067         }
2068     }
2069     cc = atoi(ccbuf);                   /* Numeric version of country code */
2070 
2071     i = 0;                              /* Now get area code */
2072     acbuf[0] = NUL;                     /* Initialize area-code buffer */
2073     acptr = acbuf;                      /* and pointer. */
2074     while (1) {
2075         while (*s == HT || *s == SP)    /* Ignore whitespace */
2076           s++;
2077         if (!s)                         /* String finished */
2078           break;
2079         if (*s == ')') {                /* End of area code  */
2080             s++;                        /* Skip past parenthesis   */
2081             acbuf[i] = NUL;             /* Terminate area-code buffer */
2082             break;
2083         } else {                        /* Part of area code */
2084             if (isdigit(*s))            /* If it's a digit, */
2085               acbuf[i++] = *s;          /* copy this character */
2086             s++;                        /* Point to next */
2087             if (!*s || i > 23)          /* Watch out for overflow */
2088               break;
2089         }
2090     }
2091 
2092 /*
2093    Here we strip any leading 0 for countries that we know have
2094    0 as a long-distance prefix and do not have any area codes that
2095    start with 0 (formerly also ditto for "9" in Finland...)
2096 */
2097     i = atoi(ccbuf);
2098     acptr = chk_ac(i,acbuf);
2099 
2100     while (*s == HT || *s == SP)        /* Skip whitespace */
2101       s++;
2102 
2103 /* printf("S=[%s], ACPTR=[%s]\n",s,acptr); */
2104 
2105     if (*s && *acptr) {                 /* Area code was delimited */
2106 
2107         while (*s == '-' || *s == '.')  /* Skip past gratuitious punctuation */
2108           s++;
2109         if (!*s) s--;                   /* But not to end of string */
2110 
2111         if (strcmp(diallcc,ccbuf)) {    /* Out of country? */
2112             if (!dialixp) {             /* Need intl-prefix */
2113                 if (cx != XXLOOK)
2114                   printf("Error - No international dialing prefix defined\n");
2115                 return(-1);
2116             }
2117             what = dn_x[k] = DN_INTL;
2118             p  = (prefix && dialixp) ? dialixp : ""; /* Intl-prefix */
2119             p2 = (suffix && dialixs) ? dialixs : ""; /* Intl-suffix */
2120 
2121             /* Form the final phone number */
2122 #ifdef COMMENT
2123             sprintf(pdsfx,"%s%s",p2,sfx); /* UNSAFE */
2124             sprintf(outbuf,
2125                     "%s%s%s%s%s%s%s%s",
2126                     pxo,npr,p,ccbuf,acptr,s,p2,sfx
2127                     );
2128 #else
2129             ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL);
2130             ckmakxmsg(outbuf,256,pxo,npr,p,ccbuf,acptr,s,p2,sfx,
2131                       NULL,NULL,NULL,NULL);
2132 #endif /* COMMENT */
2133 
2134         } else if ((x = callisld(lac,acptr)) >= 1) { /* In-country LD */
2135             if (!diallac && cx != XXLOOK) { /* Don't know my own area code */
2136                 if (cc == 1)
2137                   printf("WARNING - Prior SET DIAL AREA-CODE needed\n");
2138             }
2139             if (x == 2) {               /* Local call with area code */
2140                 what = dn_x[k] = DN_LOCAL;      /* Local-call */
2141                 p  = (prefix && diallcp) ? diallcp : ""; /* local-prefix */
2142                 p2 = (suffix && diallcs) ? diallcs : ""; /* local-suffix */
2143             } else {
2144                 what = dn_x[k] = DN_LONG;       /* Long-distance */
2145                 for (i = 0; i < ntollfree; i++) { /* But toll-free too? */
2146                     if (!strcmp(acptr,dialtfc[i])) {
2147                         what = dn_x[k] = DN_FREE;
2148                         break;
2149                     }
2150                 }
2151                 if (what == DN_FREE) {  /* Toll-free call */
2152                     p = (prefix && dialtfp) ? dialtfp :
2153                         ((prefix && dialldp) ? dialldp : "");
2154                     p2 = "";            /* no suffix */
2155                 } else {                        /* normal long distance */
2156                     p  = (prefix && dialldp) ? dialldp : ""; /* ld-prefix */
2157                     p2 = (suffix && diallds) ? diallds : ""; /* ld-suffix */
2158                 }
2159             }
2160             /* Form the number to be dialed */
2161 #ifdef COMMENT
2162             sprintf(outbuf,"%s%s%s%s%s%s%s",
2163                     pxo,npr,p,acptr,s,p2,sfx
2164                     );
2165             sprintf(pdsfx,"%s%s",p2,sfx);
2166 #else
2167             ckmakxmsg(outbuf,256,
2168                       pxo,npr,p,acptr,s,p2,sfx,
2169                       NULL,NULL,NULL,NULL,NULL);
2170             ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL);
2171 #endif /* COMMENT */
2172         } else {                        /* Same country, same area code */
2173             what = dn_x[k] = DN_LOCAL;  /* So it's a local call. */
2174             if (!prefix || !(dialpxo || ndialpxx)) { /* Not dialing from PBX */
2175                 p  = (prefix && diallcp) ? diallcp : ""; /* local-prefix */
2176                 p2 = (suffix && diallcs) ? diallcs : ""; /* local-suffix */
2177 #ifdef COMMENT
2178                 if (x == 2)
2179                   sprintf(outbuf,"%s%s%s%s%s%s",npr,p,acptr,s,p2,sfx);
2180                 else
2181                   sprintf(outbuf,"%s%s%s%s%s",npr,p,s,p2,sfx);
2182                 sprintf(pdsfx,"%s%s",p2,sfx);
2183 #else
2184                 if (x == 2)
2185                   ckmakxmsg(outbuf,256,
2186                             npr,p,acptr,s,p2,sfx,
2187                             NULL,NULL,NULL,NULL,NULL,NULL);
2188                 else
2189                   ckmakxmsg(outbuf,256,
2190                             npr,p,s,p2,sfx,
2191                             NULL,NULL,NULL,NULL,NULL,NULL,NULL);
2192                 ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL);
2193 #endif /* COMMENT */
2194 
2195             } else {                    /* Dialing from a PBX and not TAPI */
2196                 if (ndialpxx) {         /* Is it internal? */
2197 #ifdef COMMENT
2198                     i = (int) strlen(dialpxx);
2199                     j = (int) strlen(s);
2200                     x = -1;
2201                     if (j > i)
2202                       x = ckstrcmp(dialpxx,s,i,0);
2203 #else
2204                     int kx;
2205                     x = -1;
2206                     j = (int) strlen(s);
2207                     for (kx = 0; kx < ndialpxx; kx++) {
2208                         i = (int) strlen(dialpxx[kx]);
2209                         if (j > i)
2210                           if (!(x = ckstrcmp(dialpxx[kx],s,i,0)))
2211                             break;
2212                     }
2213 #endif /* COMMENT */
2214                     if (!x) {
2215                         char * icp, buf[32];
2216                         makestr(&matchpxx,dialpxx[kx]);
2217                         debug(F111,"dncvt matchpxx",matchpxx,kx);
2218                         what = dn_x[kx] = DN_INTERN;   /* Internal call. */
2219                         s += i;
2220                         /* Internal-call prefix */
2221                         icp = dialpxi;
2222 #ifndef NOSPL
2223                         if (icp) {
2224                             if (*icp == '\\') {
2225                                 char c, *bp;
2226                                 int n;
2227                                 c = *(icp+1);
2228                                 if (isupper(c)) c = tolower(c);
2229                                 if (c == 'v' || c == 'f') {
2230                                     n = 32;
2231                                     bp = buf;
2232                                     zzstring(icp,&bp,&n);
2233                                     icp = buf;
2234                                 }
2235                             }
2236                         }
2237 #endif /* NOSPL */
2238                         p = (prefix && icp) ? icp : "";
2239 #ifdef COMMENT
2240                         sprintf(outbuf,"%s%s%s%s",npr,p,s,sfx);
2241 #else
2242                         ckmakmsg(outbuf,256,npr,p,s,sfx);
2243 #endif /* COMMENT */
2244                     } else {            /* External local call */
2245                         /* local-prefix */
2246                         p  = (prefix && diallcp) ? diallcp : "";
2247                         /* local-suffix */
2248                         p2 = (prefix && diallcs) ? diallcs : "";
2249 #ifdef COMMENT
2250                         if (x == 2)
2251                           sprintf(outbuf,"%s%s%s%s%s%s%s",
2252                                   dialpxo ? dialpxo : "",
2253                                   npr,p,acptr,s,p2,sfx);
2254                         else
2255                           sprintf(outbuf,
2256                                   "%s%s%s%s%s%s",
2257                                   dialpxo ? dialpxo : "",
2258                                   npr,p,s,p2,sfx
2259                                   );
2260 #else
2261                         if (x == 2)
2262                           ckmakxmsg(outbuf, 256,
2263                                    dialpxo ? dialpxo : "",
2264                                    npr,p,acptr,s,p2,sfx,
2265                                    NULL,NULL,NULL,NULL,NULL);
2266                         else
2267                           ckmakxmsg(outbuf, 256,
2268                                     dialpxo ? dialpxo : "",
2269                                     npr,p,s,p2,sfx,
2270                                     NULL,NULL,NULL,NULL,NULL,NULL);
2271 #endif /* COMMENT */
2272                     }
2273                 }
2274             }
2275         }
2276 
2277     } else {                            /* Area code was not delimited */
2278 
2279         char xbuf[256];                 /* Comparison based only on length */
2280         char ybuf[256];
2281         int x, j;
2282 
2283         s = ss;
2284 
2285         for (i = 0; i < 255; i++) {
2286             if (!*s) break;
2287             while (!isdigit(*s)) {      /* Pay attention only to digits */
2288                 s++;
2289                 if (!*s) break;
2290             }
2291             xbuf[i] = *s++;
2292         }
2293         xbuf[i] = NUL;
2294 
2295         x = 1;                          /* Assume LD */
2296         n = 0;
2297         if (!dialfld) {                 /* If LD not forced */
2298             for (j = 0; j < nlocalac; j++) { /* check local AC list? */
2299                 ckmakmsg(ybuf,256,diallcc,diallcac[j],NULL,NULL);
2300                 n = (int) strlen(ybuf);
2301                 if (n > 0 && !ckstrcmp(xbuf,ybuf,n,0)) {
2302                     x = 2;
2303                     break;
2304                 }
2305             }
2306             if (x == 1) {               /* Or exact match with local CC+AC? */
2307                 ckmakmsg(ybuf,256,diallcc,lac,NULL,NULL);
2308                 n = (int) strlen(ybuf);
2309                 if (n > 0 && !ckstrcmp(xbuf,ybuf,n,0))
2310                   x = 0;
2311             }
2312         }
2313         if (x == 0 || x == 2) {         /* Local call */
2314             int xx,kx;                  /* Begin 1 Dec 2001... */
2315             /* Account for PBX internal calls */
2316             if (ndialpxx) {
2317                 xx = -1;
2318                 j = (int) strlen(ybuf);
2319                 for (kx = 0; kx < ndialpxx; kx++) {
2320                     i = (int) strlen(dialpxx[kx]);
2321                     if (j >= i)
2322                       if (!(xx = ckstrcmp(dialpxx[kx],&xbuf[j],i,0)))
2323                         break;
2324                 }
2325             }
2326             if (!xx) {
2327                 char * icp, buf[32];
2328                 makestr(&matchpxx,dialpxx[kx]);
2329                 debug(F111,"dncvt matchpxx",matchpxx,kx);
2330                 what = dn_x[kx] = DN_INTERN; /* Internal call. */
2331                 s = xbuf + j + i;
2332                 icp = dialpxi;          /* Internal-call prefix */
2333 #ifndef NOSPL
2334                 if (icp) {
2335                     if (*icp == '\\') {
2336                         char c, *bp;
2337                         int n;
2338                         c = *(icp+1);
2339                         if (isupper(c)) c = tolower(c);
2340                         if (c == 'v' || c == 'f') {
2341                             n = 32;
2342                             bp = buf;
2343                             zzstring(icp,&bp,&n);
2344                             icp = buf;
2345                         }
2346                     }
2347                 }
2348 #endif /* NOSPL */
2349                 p = (prefix && icp) ? icp : "";
2350                 ckmakmsg(outbuf,256,npr,p,s,sfx);
2351                 /* End 1 Dec 2001... */
2352 
2353             } else {                    /* Not PBX internal */
2354 
2355                 dn_x[k] = DN_LOCAL;
2356                 p = (prefix && diallcp) ? diallcp : "";
2357                 p2 = (suffix && diallcs) ? diallcs : "";
2358                 s = (char *) (xbuf + ((x == 0) ? n : (int)strlen(diallcc)));
2359                 ckmakxmsg(outbuf,256,
2360                           pxo,npr,p,s,p2,sfx,
2361                           NULL,NULL,NULL,NULL,NULL,NULL);
2362                 ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL);
2363             }
2364         } else {                        /* Not local */
2365             n = ckstrncpy(ybuf,diallcc,256);
2366             if (n > 0 && !ckstrcmp(xbuf,ybuf,n,0)) { /* Long distance */
2367                 dn_x[k] = DN_LONG;
2368                 p = (prefix && dialldp) ? dialldp : "";
2369                 p2 = (suffix && diallds) ? diallds : "";
2370                 s = xbuf + n;
2371                 while (*s == '-' || *s == '.')
2372                   s++;
2373 #ifdef COMMENT
2374                 sprintf(outbuf,"%s%s%s%s%s%s",pxo,npr,p,s,p2,sfx);
2375                 sprintf(pdsfx,"%s%s",p2,sfx);
2376 #else
2377                 ckmakxmsg(outbuf,256,
2378                           pxo,npr,p,s,p2,sfx,
2379                          NULL,NULL,NULL,NULL,NULL,NULL);
2380                 ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL);
2381 #endif /* COMMENT */
2382             } else {
2383                 dn_x[k] = DN_INTL;      /* International */
2384                 if (!dialixp) {
2385                     if (cx != XXLOOK) {
2386                         printf(
2387                           "Error - No international dialing prefix defined\n"
2388                                );
2389                         return(-1);
2390                     }
2391                 }
2392                 p = (prefix && dialixp) ? dialixp : "";
2393                 p2 = (suffix && dialixs) ? dialixs : "";
2394 #ifdef COMMENT
2395                 sprintf(outbuf,"%s%s%s%s%s%s",pxo,npr,p,xbuf,p2,sfx);
2396                 sprintf(pdsfx,"%s%s",p2,sfx);
2397 #else
2398                 ckmakxmsg(outbuf,256,
2399                           pxo,npr,p,xbuf,p2,sfx,
2400                           NULL,NULL,NULL,NULL,NULL,NULL);
2401                 ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL);
2402 #endif /* COMMENT */
2403             }
2404         }
2405     }
2406 #ifdef CK_TAPI
2407     if (tttapi &&                       /* TAPI performs the conversions */
2408         !tapipass &&
2409         tapiconv == CK_AUTO ||
2410         tapiconv == CK_ON
2411         ) {
2412         p = NULL;
2413         dialtype = -2;
2414         if (!cktapiConvertPhoneNumber(dn_p[k],&p))
2415           return(-1);
2416         makestr(&dn_p2[k], p);
2417         if (p) free(p);
2418         return(0);
2419     } else {
2420 #endif /* CK_TAPI */
2421         makestr(&dn_p2[k], outbuf);
2422 #ifdef CK_TAPI
2423     }
2424 #endif /* CK_TAPI */
2425     dialtype = what;
2426     return(0);
2427 }
2428 
2429 static int
ddcvt(s,f,n)2430 ddcvt(s, f, n) char * s; FILE * f; int n; { /* Dial Directory Convert */
2431     char linebuf[1024], *s2;            /* Buffers and pointers */
2432 #ifdef VMS
2433     char * temp = NULL;
2434 #endif /* VMS */
2435     char *info[8];                      /* Pointers to words from entry */
2436     FILE * f2 = NULL;
2437     int x, rc;
2438     rc = -1;
2439 
2440     debug(F110,"ddcvt file",s,0);
2441 
2442     if (!s || !f)                       /* No filename or file */
2443       return(-1);
2444     if (!*s)
2445 
2446     fclose(f);
2447     znewn(s,&s2);                       /* s2 = address of static buffer */
2448     debug(F110,"ddcvt newname",s2,0);
2449 
2450 #ifdef VMS
2451     /* In VMS, znewn() returns the same file name with a new version number */
2452     makestr(&temp,s);                   /* Swap - otherwise the new */
2453     s = s2;                             /* version has the older version */
2454     s2 = temp;                          /* number... */
2455     debug(F110,"ddcvt after swap s",s,0);
2456     debug(F110,"ddcvt after swap s2",s2,0);
2457     makestr(&(dialdir[n]),s);           /* New file gets new version number */
2458     debug(F110,"ddcvt after makestr s2",s2,0);
2459     debug(F111,"ddcvt dialdir[n]",dialdir[n],n);
2460 #else
2461     if (zrename(s,s2) < 0) {            /* Not VMS - rename old file */
2462         perror(s2);                     /* to new (wierd) name. */
2463         goto ddexit;
2464     }
2465 #endif /* VMS */
2466     debug(F110,"ddcvt s2 (old)",s2,0);
2467     if ((f = fopen(s2,"r")) == NULL) {  /* Reopen old file with wierd name */
2468         debug(F110,"ddcvt s2 open error",ck_errstr(),0);
2469         dirline = 0;                    /* (or in VMS, old version) */
2470         perror(s2);
2471         goto ddexit;
2472     }
2473     debug(F110,"ddcvt fopen(s2) OK",s2,0);
2474 
2475     debug(F110,"ddcvt s (new)",s,0);
2476     if ((f2 = fopen(s,"w")) == NULL) {  /* Create new file with old name */
2477         debug(F110,"ddcvt s open error",ck_errstr(),0);
2478         perror(s);                      /* (or in VMS, new version) */
2479         goto ddexit;
2480     }
2481     debug(F110,"ddcvt fopen(s) OK",s,0);
2482 
2483     printf("\nSaving old directory as %s.\nConverting %s...",s2,s);
2484     fprintf(f2,"; %s - Kermit dialing directory\n", s);
2485     fprintf(f2,"%-16s %-20s ; %5s %-6s ; %s\n",
2486                "; Name","Number","Speed","Parity","Comment"
2487                );
2488 
2489     while (1) {
2490         linebuf[0] = NUL;               /* Read a line */
2491         if (fgets(linebuf,1023,f) == NULL)
2492           break;
2493         debug(F110,"ddcvt linebuf",linebuf,0);
2494         if (!linebuf[0]) {              /* Empty line */
2495             fprintf(f2,"\n");
2496             continue;
2497         }
2498         x = (int) strlen(linebuf);      /* Strip line terminator, */
2499         while (x-- > 0) {               /* if any. */
2500             if (linebuf[x] <= SP)
2501               linebuf[x] = NUL;
2502             else
2503               break;
2504         }
2505         xwords(linebuf,5,info,1);       /* Parse it the old way */
2506         for (x = 1; x < 6; x++)
2507           if (!info[x]) info[x] = "";
2508         fprintf(f2,"%-16s %-20s ; %5s %-6s %s\n",
2509                info[1],info[2],info[3],info[4],info[5]
2510                );
2511     }
2512     printf(" OK\n\n");
2513     rc = 0;                             /* Success */
2514   ddexit:
2515     if (f) fclose(f);
2516     if (f2) fclose(f2);
2517 #ifdef VMS
2518     if (temp) free(temp);
2519 #endif /* VMS */
2520     return(rc);
2521 }
2522 
2523 int                                     /* s = name to look up   */
2524 #ifdef CK_ANSIC                         /* cx = index of command */
ludial(char * s,int cx)2525 ludial(char *s, int cx)                 /* (DIAL, LOOKUP, etc)   */
2526 #else
2527 ludial(s, cx) char *s; int cx;
2528 #endif /* CK_ANSIC */
2529 /* ludial */ {
2530 
2531     int dd, n1, n2, n3, i, j, t;        /* Workers */
2532     int olddir, newdir, oldentry, newentry;
2533     int pass = 0;
2534     int oldflg = 0;
2535     int ambiguous = 0;                  /* Flag for lookup was ambiguous */
2536     char *info[7];                      /* Pointers to words from entry */
2537     char *pp;                           /* Pointer to element of array */
2538     FILE * f;
2539     char *line;                         /* File input buffer */
2540 
2541 /* #define LUDEBUG */
2542 
2543 #ifdef LUDEBUG
2544 int zz = 1;
2545 #endif /* LUDEBUG */
2546 
2547     if (!s || ndialdir < 1)             /* Validate arguments */
2548       return(-1);
2549 
2550     if ((n1 = (int) strlen(s)) < 1)     /* Length of string to look up */
2551       return(-1);
2552 
2553     if (!(line = malloc(1024)))         /* Allocate input buffer */
2554       return(-1);
2555 
2556 #ifdef LUDEBUG
2557 if (zz) printf("LUDIAL 1 s[%s], n1=%d\n",s,n1);
2558 #endif /* LUDEBUG */
2559 
2560     pass = 0;
2561   lu_again:
2562     f = NULL;                           /* Dial directory file descriptor */
2563     t = dncount = 0;                    /* Dial-number match count */
2564     dd = 0;                             /* Directory counter */
2565     olddir = 0;
2566     newdir = 0;
2567 /*
2568   We need to recognize both old- and new-style directories.
2569   But we can't allow old-style and new-style entries in the same
2570   directory because there is no way to tell for sure the difference between
2571   an old-style entry like this:
2572 
2573     foo  5551212  9600
2574 
2575   and a new-style literal entry like this:
2576 
2577     foo  555 9600
2578 
2579   I.e. is the "9600" a speed, or part of the phone number?
2580 */
2581     while (1) {                         /* We make one pass */
2582         if (!f) {                       /* Directory not open */
2583             if (dd >= ndialdir)         /* No directories left? */
2584               break;                    /* Done. */
2585             debug(F111,"ludial dialdir[dd]",dialdir[dd],dd);
2586             if ((f = fopen(dialdir[dd],"r")) == NULL) { /* Open it */
2587                 perror(dialdir[dd]);    /* Can't, print message saying why */
2588                 if (line) {
2589                     free(line);
2590                     line = NULL;
2591                 }
2592                 dd++;                   /* Go on to next one, if any... */
2593                 continue;
2594             }
2595             dirline = 0;                /* Directory file line number */
2596             if (dialdpy && !pass)
2597               printf("Opening: %s...\n",dialdir[dd]);
2598             dd++;
2599             if (!oldflg) olddir = 0;
2600             newdir = 0;
2601         }
2602         oldentry = 0;
2603         newentry = 0;
2604         line[0] = NUL;
2605         if (getnct(line,1023,f,1) < 0) { /* Read a line */
2606             if (f) {                    /* f can be clobbered! */
2607                 fclose(f);              /* Close the file */
2608                 f = NULL;               /* Indicate next one needs opening */
2609                 oldflg = 0;
2610             }
2611             continue;
2612         }
2613         if (!line[0])                   /* Empty line */
2614           continue;
2615 #ifdef LUDEBUG
2616 if (zz) printf("LUDIAL 2 s[%s]\n",s);
2617 #endif /* LUDEBUG */
2618 
2619         /* Make a copy and parse it the old way */
2620         /* A copy is needed because xwords() pokes NULs into the string */
2621 
2622         if ((pp = malloc((int)strlen(line) + 1))) {
2623             strcpy(pp,line);            /* safe */
2624             xwords(pp,5,info,0);        /* Parse it the old way */
2625 
2626 #ifdef LUDEBUG
2627 if (zz) printf("LUDIAL 3 s[%s]\n",s);
2628 #endif /* LUDEBUG */
2629 
2630             if (!info[1])
2631               continue;
2632             if (*info[1] == ';') {      /* If full-line comment, */
2633                 newdir = 1;             /* (only new directories have them) */
2634                 continue;               /* keep reading. */
2635             }
2636             if (!info[2])
2637               continue;
2638             if (*info[2] == '+')
2639               newentry = 1;
2640             if (info[4]) {
2641                 if ((*info[4] == '=') ||
2642                     !ckstrcmp(info[4],"none", 4,0) ||
2643                     !ckstrcmp(info[4],"even", 4,0) ||
2644                     !ckstrcmp(info[4],"space",5,0) ||
2645                     !ckstrcmp(info[4],"mark", 4,0) ||
2646                     !ckstrcmp(info[4],"odd",  3,0)
2647                     )
2648                   oldentry = 1;
2649             }
2650         }
2651         if (pp) {
2652             free(pp);
2653             pp = NULL;
2654         }
2655 
2656         /* Check consistency */
2657 
2658         if ((oldentry || olddir) && (newentry || newdir)) {
2659             printf(
2660 "\nERROR: You seem to have old- and new-format entries mixed in your\n");
2661             printf(
2662 "dialing directory.  You'll have to edit it by hand to convert it to the\n");
2663 #ifndef NOHELP
2664             printf("new format.  Type HELP DIAL for further information.\n\n");
2665 #else
2666             printf("new format.\n\n");
2667 #endif /* NOHELP */
2668             if (line) {
2669                 free(line);
2670                 line = NULL;
2671             }
2672             return(-1);
2673         }
2674         if (!olddir && oldentry) {
2675             int convert = 0;
2676             olddir = 1;
2677             if (dialcvt == 2) {         /* 2 == ASK */
2678                 sprintf(tmpbuf,
2679 "WARNING: Old-style dialing directory detected:\n%s", line);
2680 		convert = uq_ok(tmpbuf,
2681 				"Shall I convert it for you? ",3,NULL,0);
2682             } else
2683               convert = dialcvt;
2684             if (convert) {
2685                 debug(F111,"ludial calling ddcvt",dialdir[dd-1],dd);
2686                 if (ddcvt(dialdir[dd-1],f,dd-1) < 0) {
2687                     debug(F111,"ludial ddcvt failed",dialdir[dd-1],dd);
2688                     oldflg = 1;
2689                     printf(
2690 "  Sorry, can't convert.");
2691                     printf(
2692 "  Will ignore speed and parity fields, continuing...\n\n");
2693                 } else {
2694                     olddir = newdir = 0;
2695                     debug(F111,"ludial ddcvt ok",dialdir[dd-1],dd);
2696                 }
2697                 dd--;
2698                 f = NULL;
2699                 continue;
2700             } else {
2701                 if (dialcvt == 2)
2702                   printf(
2703 "  OK, will ignore speed and parity fields, continuing...\n\n");
2704                 olddir = 1;
2705             }
2706         }
2707 
2708 #ifdef LUDEBUG
2709 if (zz) printf("LUDIAL XX s[%s], n1=%d\n",s,n1);
2710 #endif /* LUDEBUG */
2711 
2712         /* Now parse again for real */
2713 
2714         if (oldentry)                   /* Parse it the old way */
2715           xwords(line,5,info,0);
2716         else                            /* Parse it the new way */
2717           xwords(line,2,info,1);
2718 
2719 #ifdef LUDEBUG
2720 if (zz) printf("LUDIAL YY s[%s], n1=%d\n",s,n1);
2721 if (zz) printf("%s [%s]\n",info[1],info[2]);
2722 #endif /* LUDEBUG */
2723 
2724         if (info[1]) {                  /* First word is entry name */
2725             if ((n3 = (int) strlen(info[1])) < 1) /* Its length */
2726               continue;                 /* If no first word, keep reading. */
2727             if (n3 < n1)                /* Search name is longer */
2728               continue;                 /* Can't possibly match */
2729             if (ambiguous && n3 != n1)
2730               continue;
2731 
2732 #ifdef LUDEBUG
2733 if (zz) printf("MATCHING: [%s] [%s], n1=%d\n",s,info[1],n1);
2734 #endif /* LUDEBUG */
2735 
2736             if (ckstrcmp(s,info[1],n1,0)) /* Caseless string comparison */
2737               continue;
2738 
2739 #ifdef LUDEBUG
2740 if (zz) printf("MATCH OK: [%s] [%s], n1=%d\n",s,info[1],n1);
2741 #endif /* LUDEBUG */
2742 
2743             if (!info[2])               /* No phone number given */
2744               continue;
2745             if ((n2 = (int) strlen(info[2])) < 1) /* Length of phone number */
2746               continue;                 /* Ignore empty phone numbers */
2747 
2748             /* Got one */
2749 
2750             if (!(pp = (char *)malloc(n2 + 1))) { /* Allocate storage for it */
2751                 printf("?internal error - ludial malloc 1\n");
2752                 if (line) {
2753                     free(line);
2754                     line = NULL;
2755                 }
2756                 dncount = 0;
2757                 return(-1);
2758             }
2759             strcpy(pp,info[2]);         /* safe */
2760 
2761             if (dncount > MAXDNUMS) {
2762                 printf("Warning: %d matches found, %d max\n",
2763                        dncount,
2764                        MAXDNUMS
2765                        );
2766                 dncount = MAXDNUMS;
2767                 break;
2768             }
2769             dn_p[dncount++] = pp;       /* Add pointer to array. */
2770             if (dncount == 1) {         /* First one... */
2771                 if (d_name) free(d_name);
2772                 if (!(d_name = (char *)malloc(n3 + 1))) { /* Save its name */
2773                     printf("?internal error - ludial malloc 2\n");
2774                     if (line) {
2775                         free(line);
2776                         line = NULL;
2777                     }
2778                     dncount = 0;
2779                     return(-1);
2780                 }
2781                 t = n3;                 /* And its length */
2782                 strcpy(d_name,info[1]); /* safe */
2783             } else {                    /* Second or subsequent one */
2784 
2785 #ifdef LUDEBUG
2786                 if (zz)
2787                   printf("d_name=[%s],info[1]=%s,t=[%d]\n",d_name,info[1],t);
2788 #endif /* LUDEBUG */
2789 
2790                 if ((int) strlen(info[1]) == t) /* Lengths compare */
2791                   if (!ckstrcmp(d_name,info[1],t,0)) /* Caseless compare OK */
2792                     continue;
2793 
2794                 /* Name given by user matches entries with different names */
2795 
2796                 if (ambiguous)          /* Been here before */
2797                   break;
2798 
2799                 ambiguous = 1;          /* Now an exact match is required */
2800                 for (j = 0; j < dncount; j++) { /* Clean out previous list */
2801                     if (dn_p[j]) {
2802                         free(dn_p[j]);
2803                         dn_p[j] = NULL;
2804                     }
2805                 }
2806                 pass++;                 /* Second pass... */
2807                 goto lu_again;          /* Do it all over again. */
2808             }
2809         }
2810     }
2811     if (line) free(line);
2812     if (dncount == 0 && ambiguous) {
2813         printf(" Lookup: \"%s\" - ambiguous%s\n",
2814                s,
2815                cx == XXLOOK ? "" : " - dialing skipped"
2816                );
2817         return(-2);
2818     }
2819     return(dncount);
2820 }
2821 
2822 char *
pncvt(s)2823 pncvt(s) char *s; {                     /* Phone number conversion */
2824     char *p = NULL;                     /* (just a wrapper for dncvt() */
2825     char *q = NULL;
2826     static char pnbuf[128];
2827     makestr(&p,dn_p[0]);                /* Save these in case they are */
2828     makestr(&q,dn_p2[0]);               /* being used */
2829     makestr(&dn_p[0],s);                /* Copy the argument string to here */
2830     dncvt(0,XXLOOK,1,1);                /* Convert it */
2831     if (!dn_p2[0])                      /* Put result where can return it */
2832       pnbuf[0] = NUL;
2833     else
2834       ckstrncpy(pnbuf,dn_p2[0],127);
2835     makestr(&dn_p[0],p);                /* Restore these */
2836     makestr(&dn_p2[0],q);
2837     makestr(&p,NULL);                   /* Free these */
2838     makestr(&q,NULL);
2839     return((char *)pnbuf);
2840 }
2841 
2842 int
dodial(cx)2843 dodial(cx) int cx; {                    /* DIAL or REDIAL */
2844     int i = 0, x = 0;                   /* Workers */
2845     int sparity = -1;                   /* For saving global parity value */
2846     int previous = 0;
2847     int len = 0;
2848     int literal = 0;
2849     int flowsave;
2850     int lufound = 0;                    /* Did any lookup succeed? */
2851     int prefix = 1;
2852     int postfix = 1;
2853     int wasalpha = 0;
2854     int xredial = 0;
2855     int braces = 0;
2856 
2857     char *p = NULL, *s3 = NULL, * sav = NULL;
2858     int j = 0, t = 0, n = 0;
2859     int xretries, xlcc;
2860 
2861 #ifdef COMMENT
2862     debug(F101,"dodial cx","",cx);
2863     debug(F111,"dodial diallcc",diallcc,diallcc);
2864 #endif	/* COMMENT */
2865 
2866     xretries = dialrtr;                 /* If retries not set, */
2867     if (diallcc) {                      /* choose default based on */
2868         xlcc = atoi(diallcc);           /* local country code. */
2869         if (xretries < 0) {
2870             switch (xlcc) {
2871               case 1: xretries = 10; break; /* No restrictions in NANP */
2872                 /* Add other country codes here */
2873                 /* that are known to have no restrictions on redialing. */
2874               default: xretries = 1;
2875             }
2876         }
2877     }
2878     if (cx == XXPDIA) {                 /* Shortcut... */
2879         cx = XXDIAL;
2880         partial = 1;
2881         debug(F100,"PDIAL sets partial=1","",0);
2882         postfix = 0;                    /* Do not add postfix */
2883     } else {
2884         partial = 0;
2885         debug(F100,"DIAL sets partial=0","",0);
2886     }
2887     previous = dialsta;                 /* Status of previous call, if any */
2888     if (previous == DIA_PART) {
2889         prefix = 0;                     /* do not add prefix */
2890     }
2891     s = NULL;                           /* Initialize user's dial string */
2892     if (cx == XXRED) {                  /* REDIAL or... */
2893         if ((y = cmcfm()) < 0)
2894           return(y);
2895     } else if (cx == XXANSW) {          /* ANSWER or ... */
2896         if ((y = cmnum("timeout (seconds)","0",10,&x,xxstring)) < 0)
2897           return(y);
2898         dialatmo = x;
2899         if ((y = cmcfm()) < 0)
2900           return(y);
2901     } else {                            /* DIAL or LOOKUP */
2902         if (ndialdir > 0)
2903           s3 = "Number to dial or entry from dial directory";
2904         else
2905           s3 = "Number to dial";
2906         if ((x = cmtxt(s3, dialnum ? dialnum : "",&s,xxstring)) < 0)
2907           return(x);
2908         if (s) {
2909             len = (int) strlen(s);
2910             ckstrncpy(tmpbuf,s,TMPBUFSIZ); /* Save literal copy */
2911 #ifdef COMMENT
2912             if (len > 1) {              /* Strip outer braces if given */
2913                 if (*s == '{') {
2914                     if (s[len-1] == '}') {
2915                         s[len-1] = NUL;
2916                         s++;
2917                         len -= 2;
2918                     }
2919                 }
2920             }
2921 #else
2922             s = brstrip(s);             /* Strip outer braces or quotes */
2923 #endif /* COMMENT */
2924         }
2925     }
2926 
2927     if (cx != XXLOOK) {                 /* Not LOOKUP */
2928 #ifdef IKSD
2929         if (inserver) {
2930             printf("Sorry, dialing is disabled.\r\n");
2931             return(success = 0);
2932         }
2933 #endif /* IKSD */
2934 #ifdef CK_TAPI
2935         if (tttapi && !tapipass) {
2936           ;                             /* Skip the modem test if TAPI */
2937         } else
2938 #endif /* CK_TAPI */
2939         if (mdmtyp < 1 && !dialtest) {
2940             if (network
2941 #ifdef TN_COMPORT
2942                  && !istncomport()
2943 #endif /* TN_COMPORT */
2944                  )
2945               printf("Please SET HOST first, and then SET MODEM TYPE\n");
2946             else
2947               printf("Sorry, you must SET MODEM TYPE first\n");
2948             dialsta = DIA_NOMO;
2949             return(success = 0);
2950         }
2951         if (!local && !dialtest) {
2952             printf("Sorry, you must SET %s or SET HOST first\n",
2953 #ifdef OS2
2954                    "PORT"
2955 #else
2956                    "LINE"
2957 #endif /* OS2 */
2958                    );
2959             dialsta = DIA_NOLI;
2960             return(success = 0);
2961         }
2962         if ((!network
2963 #ifdef TN_COMPORT
2964               || istncomport()
2965 #endif /* TN_COMPORT */
2966               ) && !dialtest &&
2967 #ifdef CK_TAPI
2968              !tttapi &&
2969 #endif /* CK_TAPI */
2970             (speed < 0L)
2971 #ifdef UNIX
2972             && (strcmp(ttname,"/dev/null"))
2973 #else
2974 #ifdef OSK
2975             && (strcmp(ttname,"/nil"))
2976 #endif /* OSK */
2977 #endif /* UNIX */
2978             ) {
2979             printf("\nSorry, you must SET SPEED first\n");
2980             dialsta = DIA_NOSP;
2981             return(success = 0);
2982         }
2983     }
2984     if (cx != XXANSW) {
2985         for (j = 0; j < MAXDNUMS; j++) { /* Initialize dial-number list */
2986             if (!dialnum) {             /* First time dialing */
2987                 dn_p[j] = NULL;         /* initialize all pointers. */
2988                 dn_p2[j] = NULL;
2989             } else if (dn_p[j]) {       /* Not the first time, */
2990                 free(dn_p[j]);          /* free previous, if any, */
2991                 dn_p[j] = NULL;         /* then set to NULL. */
2992                 if (dn_p2[j])
2993                   free(dn_p2[j]);
2994                 dn_p2[j] = NULL;
2995             } else break;               /* Already NULL */
2996         }
2997         if (len == 0)
2998           s = NULL;
2999         if (!s)
3000           s = dialnum;
3001         if (!s) {
3002             if (cx == XXLOOK)
3003               printf("?Lookup what?\n");
3004             else
3005               printf("%s\n", (cx == XXRED) ?
3006                    "?No DIAL command given yet" :
3007                    "?You must specify a number to dial"
3008                    );
3009             return(-9);
3010         }
3011 
3012     /* Now we have the "raw" dial or lookup string and s is not NULL */
3013 
3014         makestr(&dscopy,s);             /* Put it in a safe place */
3015         s = dscopy;
3016         n = 0;
3017 
3018         debug(F111,"dodial",s,ndialdir);
3019 
3020         wasalpha = 0;
3021         if (isalpha(*s)) {
3022             wasalpha = 1;
3023             if (ndialdir > 0) {         /* Do we have a dialing directory? */
3024                 n = ludial(s,cx);       /* Look up what the user typed */
3025                 if (n == 0)
3026                   printf(" Lookup: \"%s\" - not found%s\n",
3027                          s,
3028                          cx == XXLOOK ? "" : " - dialing as given\n"
3029                          );
3030             }
3031             debug(F101,"dodial",s,n);
3032             if (n < 0 && cx != XXLOOK) { /* Error out if they wanted to dial */
3033                 if (n == -1)            /* -2 means ludial already gave msg */
3034                   printf(" Lookup: fatal error - dialing skipped\n");
3035                 dialsta = DIA_DIR;
3036                 return(-9);
3037             }
3038             if (n > 0)                  /* A successful lookup */
3039               lufound = 1;
3040         } else if (*s == '=') {         /* If number starts with = sign */
3041             s++;                        /* strip it */
3042             literal = 1;                /* remember this */
3043             while (*s == SP) s++;       /* and then also any leading spaces */
3044         } else if (tmpbuf[0] == '{' && tmpbuf[1] == '{') {
3045             makelist(tmpbuf,dn_p,MAXDNUMS);
3046             makestr(&dscopy,tmpbuf);
3047             s = tmpbuf;
3048             for (n = 0; n < MAXDNUMS; n++) /* (have to count how many) */
3049               if (!dn_p[n]) break;
3050             braces = 1;
3051         }
3052         if (cx == XXLOOK && !wasalpha && !braces) {
3053             /* We've been told to lookup a number or a quoted name */
3054             char *p;
3055             n = 0;
3056             p = literal ? s : pncvt(dscopy);
3057             if (!p) p = "";
3058             if (*p) {
3059                 printf("%s  => %s\n", dscopy, p);
3060                 return(success = 1);
3061             } else {
3062                 printf("?Bad phone number\n");
3063                 return(success = 0);
3064             }
3065         }
3066         /* Save DIAL or successful LOOKUP string for future DIAL or REDIAL */
3067         /* But don't save pieces of partial dial ... */
3068 
3069         debug(F101,"DIAL save dialnum partial","",partial);
3070         debug(F101,"DIAL save dialnum previous","",previous);
3071         if ((cx == XXDIAL && partial == 0 && previous != DIA_PART) ||
3072             (cx == XXLOOK && n > 0)) {
3073             makestr(&dialnum,dscopy);
3074             if (!quiet && dscopy && !dialnum)
3075               printf("WARNING - memory allocation failure: redial number\n");
3076         }
3077         if (n > 0) {
3078             if (!quiet && !backgrd && !braces /* && dialdpy */ ) {
3079                 if (!strcmp(d_name,s))
3080                   printf(" Lookup: \"%s\" - exact match\n",s);
3081                 else
3082                   printf(" Lookup: \"%s\" - uniquely matches \"%s\"\n",
3083                          s,
3084                          d_name
3085                          );
3086             }
3087             if ((cx == XXLOOK) ||
3088                 ((n > 1) && !quiet && !backgrd /* && dialdpy */ )) {
3089                 printf(" %d telephone number%sfound for \"%s\"%s\n",
3090                        n,
3091                        (n == 1) ? " " : "s ",
3092                        s,
3093                        (n > 0) ? ":" : "."
3094                        );
3095                 s3 = getdname();
3096             }
3097             for (i = 0; i < n; i++) {   /* Convert */
3098                 dn_x[i] = -1;
3099                 if (dncvt(i,cx,prefix,postfix) < 0) {
3100                     if (cx != XXLOOK) {
3101                         dialsta = DIA_DIR;
3102                         return(-9);
3103                     }
3104                 }
3105             }
3106             if (dialsrt && n > 1) {     /* Sort into optimal order */
3107                 for (i = 0; i < n-1; i++) {
3108                     for (j = i+1; j < n; j++) {
3109                         if (dn_x[j] < dn_x[i]) {
3110                             t = dn_x[j];
3111                             dn_x[j] = dn_x[i];
3112                             dn_x[i] = t;
3113                             p = dn_p[j];
3114                             dn_p[j] = dn_p[i];
3115                             dn_p[i] = p;
3116                             p = dn_p2[j];
3117                             dn_p2[j] = dn_p2[i];
3118                             dn_p2[i] = p;
3119                         }
3120                     }
3121                 }
3122             }
3123             if ((cx == XXLOOK) ||
3124                 ((n > 1) && !quiet && !backgrd /* && dialdpy */ )) {
3125                 int nn = n;
3126 #ifndef NOSPL
3127                 char * p;
3128 #endif /* NOSPL */
3129                 if (cx != XXLOOK)
3130                   if (n > 12) nn = 12;
3131                 for (i = 0; i < nn; i++) {
3132                     printf("%3d. %-12s  %-20s =>  %-20s  (%d)\n",i+1,
3133                            s3, dn_p[i],
3134                            dn_p2[i] ? dn_p2[i] : "(processing failed)",
3135                            dn_x[i]
3136                            );
3137                 }
3138                 if (cx != XXLOOK && n != nn)
3139                   printf("And %d more...\n", n - nn);
3140             }
3141         } else if (n == 0) {            /* Not found in directory */
3142             makestr(&(dn_p[0]),literal ? s : dscopy);
3143             makestr(&d_name,literal ? s : dscopy);
3144             dncount = 1;
3145             n = 1;
3146             if (dncvt(0,cx,prefix,postfix) < 0) { /* In case they typed a */
3147                 dialsta = DIA_DIR;      /* portable-format number ... */
3148                 return(-9);
3149             }
3150         }
3151 
3152 #ifndef NONET
3153 #ifdef NETCONN
3154         /* It's not good that the networks directory depends on NOT-NODIAL.. */
3155         if (cx == XXLOOK && dscopy) {   /* Networks here too... */
3156             extern char *nh_p[], *nh_p2[], *n_name;
3157             extern char *nh_px[4][MAXDNUMS+1];
3158             n = -1;
3159             if (nnetdir > 0) {          /* Do we have a network directory? */
3160                 dirline = 0;
3161                 n = lunet(dscopy);      /* Look up what the user typed */
3162             }
3163             if (n > -1) {
3164                 int k;
3165                 if (n > 0)              /* A successful lookup */
3166                   lufound = 1;
3167                 if (cx == XXLOOK && n == 0)
3168                   printf(" Lookup: \"%s\" - not found\n",dscopy);
3169                 else
3170                   printf("%s %d network entr%s found for \"%s\"%s\n",
3171                          cx == XXLOOK ? " Lookup:" : "",
3172                          n,
3173                          (n == 1) ? "y" : "ies",
3174                          dscopy,
3175                          (n > 0) ? ":" : "."
3176                          );
3177 
3178                 for (i = 0; i < n; i++) {
3179 
3180                     printf("%3d. %-12s => %-9s %s",
3181                            i+1,n_name,nh_p2[i],nh_p[i]);
3182                     for (k = 0; k < 4; k++) {
3183                         if (nh_px[k][i]) {
3184                             printf(" %s",nh_px[k][i]);
3185                         } else
3186                           break;
3187                     }
3188                     printf("\n");
3189                 }
3190             }
3191         }
3192 #endif /* NETCONN */
3193 #endif /* NONET */
3194         if (cx == XXLOOK)
3195           return(success = lufound);
3196     } /* cx != XXANSW */
3197 
3198 #ifdef VMS
3199     conres();                   /* So Ctrl-C/Y will work */
3200 #endif /* VMS */
3201 /*
3202   Some modems do not react well to parity.  Also, if we are dialing through a
3203   TCP/IP TELNET modem server, parity can be fatally misinterpreted as TELNET
3204   negotiations.
3205 
3206   This should work even if the user interrupts the DIAL command, because the
3207   DIAL module has its own interrupt handler.  BUT... if, for some reason, a
3208   dialing device actually *requires* parity (e.g. CCITT V.25bis says that even
3209   parity should be used), this might prevent successful dialing.  For that
3210   reason, we don't do this for V.25bis modems.
3211 */
3212     sparity = parity;                   /* Save current parity */
3213     if ((dialcapas & CKD_V25) == 0)     /* If not V.25bis...  */
3214       parity = 0;                       /* Set parity to NONE */
3215 
3216     flowsave = flow;
3217 /*
3218   These modems use some kind of screwy flow control while in command mode,
3219   and do not present CTS as they should.  So if RTS/CTS is set (or even if
3220   it isn't) disable flow control during dialing.
3221 */
3222 #ifndef MINIDIAL
3223     if (mdmtyp == n_ATT1910 || mdmtyp == n_ATT1900) {
3224         flow = FLO_NONE;                /* This is not enough */
3225 #ifdef CK_TTSETFLOW
3226         ttsetflow(FLO_NONE);            /* Really turn it off */
3227 #endif /* CK_TTSETFLOW */
3228     }
3229 #endif /* MINIDIAL */
3230     if (!network
3231 #ifdef TN_COMPORT
3232         || istncomport()
3233 #endif /* TN_COMPORT */
3234          ) {
3235         int x;
3236         if ((x = ttgmdm()) > -1) {
3237             if (!x && msgflg) {
3238                 printf(
3239 "WARNING - No modem signals detected.  Is your modem turned on?  If not,\n\
3240 use Ctrl-C to interrupt dialing, turn on your modem, then %s.\n",
3241                        cx == XXANSW ?
3242                        "ANSWER again" :
3243                        "REDIAL"
3244                        );
3245             }
3246             if (flow == FLO_RTSC) {
3247                 if (!(x & BM_CTS)) {
3248                     if (msgflg)
3249                       printf(
3250 "WARNING - SET FLOW RTS/CTS is in effect but modem's CTS signal is off.\n\
3251 Disabling flow control temporarily %s...\n",
3252                              cx == XXANSW ?
3253                              "while waiting for call" :
3254                              "during dialing"
3255                              );
3256                     flow = FLO_NONE;
3257                 }
3258             }
3259         }
3260     }
3261     if (cx == XXANSW) {                 /* ANSWER */
3262         success = ckdial("",0,0,1,0);
3263         goto dialfin;
3264     }
3265 
3266 /* Edit 192 adds the ability to dial repeatedly. */
3267 
3268     i = 0;
3269     dialcount = 0;
3270     do {
3271         if (i > 0) printf("\nDial attempt %d of %d...\n", i+1, xretries);
3272         dialcount = i+1;
3273         success = 0;
3274         /* And the ability to dial alternate numbers. */
3275         /* Loop to dial each in a list of numbers for the same name... */
3276         for (j = 0; j < n && !success; j++) { /* until one answers. */
3277             s = dn_p2[j];               /* Next number in list */
3278             if (dn_x[j] >= dialrstr) {  /* Dial restriction */
3279                 printf("Restricted: %s, skipping...\n",dn_p[j]);
3280                 continue;
3281             }
3282             xredial = (i == 0 && j == 0) ? 0 : 1;
3283             if (!s) s = dn_p[j];
3284 
3285 #ifndef NOSPL
3286             sav = s;
3287             p = xdial(s);               /* Apply DIAL macro now */
3288             if (p) if (*p) s = p;
3289 #endif /* NOSPL */
3290 
3291 	    /* Dial confirmation */
3292 	    /* NOTE: the uq_xxx() calls allow for a GUI dialog */
3293 
3294             if (i == 0 && dialcnf) {
3295 		char msgbuf[128];
3296 		ckmakmsg(msgbuf,128,"Dialing ",s,NULL,NULL);
3297 		x = uq_ok(msgbuf,"Is this number correct? ",3,NULL,0);
3298                 if (!x) {
3299 
3300 #ifndef COMMENT
3301 		    x = uq_txt(		/* Allow GUI dialog */
3302 #ifdef OS2
3303 " Please enter the correct number,\r\n or press Enter to skip.",
3304 #else
3305 " Please enter the correct number,\r\n or press Return to skip.",
3306 #endif /* OS2 */
3307                               "Corrected phone number: ",
3308                                1,
3309 			       NULL,
3310 			       atmbuf,
3311 			       ATMBL,
3312                                s,
3313                                DEFAULT_UQ_TIMEOUT
3314                                );
3315 		    if (x && atmbuf[0]) { /* They gave a new one */
3316 			s = atmbuf;
3317 			makestr(&(dn_p2[j]), s);
3318 		    }
3319 
3320 #else  /* COMMENT */
3321 
3322 #ifdef CK_RECALL
3323                     extern int on_recall;
3324 #endif /* CK_RECALL */
3325                     cmsavp(psave,PROMPTL);
3326                     cmsetp(
3327 #ifdef OS2
3328 " Please enter the correct number,\r\n or press Enter to skip: "
3329 #else
3330 " Please enter the correct number,\r\n or press Return to skip: "
3331 #endif /* OS2 */
3332                            );
3333                     cmini(ckxech);
3334                     x = -1;
3335                     if (pflag) prompt(NULL);
3336 #ifdef CK_RECALL
3337                     on_recall = 0;
3338 #endif /* CK_RECALL */
3339                     y = cmdgquo();
3340                     cmdsquo(0);
3341                     while (x < 0) {
3342                         x = cmtxt("Corrected phone number","",&s,NULL);
3343                         cmres();
3344                     }
3345                     if ((int) strlen(s) < 1) {
3346                         cmsetp(psave);
3347                         continue;
3348                     }
3349                     makestr(&(dn_p2[j]), s);
3350                     cmdsquo(y);
3351                     cmsetp(psave);
3352 #endif /* COMMENT */
3353                 }
3354             }
3355             if (dialtest) {             /* Just testing */
3356                 if (i + j == 0)
3357                   printf("\nTESTING...\n");
3358                 if (dialmac)
3359                   printf(" Number: \"%s\" => \"%s\"\n",sav,s);
3360                 else
3361                   printf(" Number: \"%s\"\n",s);
3362                 dialsta = DIA_BUSY;
3363                 success = 0;
3364             } else {
3365                 what |= W_DIALING;
3366                 success = ckdial(s,i,j,partial ? 3 : 0, xredial); /* Dial it */
3367                 what &= ~(W_DIALING);
3368                 if (!success) {
3369                     if (dialsta < 8 ||  /* Break out if unrecoverable error */
3370                         dialsta  == DIA_INTR ||
3371                         dialsta  == DIA_ERR  ||
3372                         previous == DIA_PART
3373                         )
3374                       break;
3375                 }
3376             }
3377         }
3378         if (success)                    /* Succeeded, leave the outer loop */
3379           break;
3380         if (dialsta < 8 ||              /* Break out if unrecoverable error */
3381             dialsta == DIA_INTR ||      /* Interrupted */
3382             dialsta == DIA_NODT ||      /* No dialtone */
3383             dialsta == DIA_NOAC ||      /* Access forbidden */
3384             dialsta == DIA_BLCK ||      /* Blacklisted */
3385             dialsta == DIA_DIR  ||      /* Dialing directory error */
3386             dialsta == DIA_ERR  ||      /* Modem command error */
3387             previous == DIA_PART)
3388           break;
3389         if (++i >= xretries)            /* Break out if too many tries */
3390           break;
3391         if (!backgrd && !quiet) {
3392             if (dialint > 5)
3393               printf(
3394 "\nWill redial in %d second%s- press any key to redial immediately.\n",
3395                      dialint,
3396                      dialint == 1 ? " " : "s "
3397                      );
3398             printf("Ctrl-C to cancel...\n");
3399         }
3400         x = dialint;                    /* Redial interval */
3401         while (x-- > 0) {
3402             if ((y = conchk()) > 0) {   /* Did they type something? */
3403                 while (y--) coninc(0);  /* Yes, absorb it */
3404                 break;                  /* And wake up */
3405             }
3406             sleep(1);                   /* No interrupt, sleep a sec */
3407         }
3408     } while (!success);
3409 
3410   dialfin:
3411 
3412     if (cx != XXLOOK) {
3413         if (!success)
3414           bleep((short) BP_FAIL);
3415         else if (!quiet)
3416           bleep((short) BP_NOTE);
3417 #ifdef OS2
3418         setint();                       /* Fix OS/2 interrupts */
3419 #endif /* OS2 */
3420         if (sparity > -1)
3421           parity = sparity;             /* Restore parity if we saved it */
3422         flow = flowsave;
3423 #ifdef OS2
3424         ttres();                        /* Restore DIAL device */
3425 #endif /* OS2 */
3426 #ifdef VMS
3427         concb((char)escape);            /* Restore console */
3428 #endif /* VMS */
3429 #ifdef OS2
3430         {                               /* Set session title */
3431             char * p, name[72];         /* in window list. */
3432             char * q;
3433             if (cx == XXANSW) {
3434                 q = "Incoming call";
3435             } else {
3436                 if (d_name)
3437                   q = d_name;
3438                 else if (dialnum)
3439                   q = dialnum;
3440                 else if (ttname[0])
3441                   q = ttname;
3442                 else q = "";
3443             }
3444             p = name;
3445             if (success) {
3446                 strncpy(name,q,48);
3447                 while (*p) {            /* Uppercase it for emphasis. */
3448                     if (islower(*p))
3449                       *p = toupper(*p);
3450                     p++;
3451                 }
3452             } else
3453               name[0] = NUL ;
3454             os2settitle((char *) name, TRUE);
3455         }
3456 #endif /* OS2 */
3457     }
3458     if (cx != XXLOOK) {
3459         if (success) {
3460             if (reliable == SET_AUTO) { /* It's not a reliable connection. */
3461                 reliable = SET_OFF;
3462                 debug(F101,"dodial reliable","",reliable);
3463             }
3464         } else {
3465 #ifndef NOHINTS
3466             extern int hints;
3467             if (hints && !quiet && dialsta != 9) { /* 9 == User interrupted */
3468                 extern int dialmhu, dialhng, dialdpy;
3469                 extern char * dialmsg[];
3470                 printf("\n*************************\n");
3471                 printf("DIAL-class command failed.\n");
3472                 printf("Modem type:  %s\n", gmdmtyp());
3473                 printf("Device:      %s\n", ttname);
3474                 printf("Speed:       %ld\n", speed);
3475                 printf("Dial status: %d",dialsta);
3476                 if (dialsta < 35 && dialmsg[dialsta])
3477                   printf(" [%s]",dialmsg[dialsta]);
3478                 printf("\n");
3479                 if (dialsta == DIA_TIMO ||
3480                     dialsta == DIA_NRDY ||
3481                    (dialsta > 13 && dialsta != DIA_BUSY && dialsta != DIA_NOAN)
3482                     ) {
3483                     switch (dialsta) {
3484                       case DIA_TIMO:
3485                         printf(
3486 " . SET DIAL TIMEOUT to a greater value and try again.\n"
3487                                );
3488                         break;
3489                       case DIA_NRSP:
3490                       case DIA_NRDY:
3491                       case DIA_NOIN:
3492                         printf(
3493 " . Is the modem turned on?\n"
3494                                );
3495                         printf(
3496 " . Are you using the right communication port?\n"
3497                                );
3498                         break;
3499                       case DIA_NODT:
3500                         printf(
3501 " . Is the modem connected to the telephone line?\n"
3502                                );
3503                     }
3504                     if (mdmtyp == n_GENERIC) {
3505                         printf(
3506 " . Please choose a specific modem type with SET MODEM TYPE and try again.\n"
3507                                );
3508                         printf(
3509 "    SET MODEM TYPE ? to see the list of known modem types.\n"
3510                                );
3511                     } else {
3512                         printf(
3513 " . Are you sure you have chosen the appropriate modem type?\n"
3514                                );
3515                     }
3516                     if (speed > 19200L) {
3517                         printf(
3518 " . Maybe the interface speed (%ld) is too fast:\n", speed
3519                                );
3520                         printf(
3521 "    SET SPEED to a lower speed and try again.\n"
3522                                );
3523                         printf(
3524 "    SET SPEED ? to see the list of valid speeds.\n"
3525                                );
3526                     }
3527                     if (dialhng) {
3528                         if (dialmhu)
3529                           printf(
3530 " . SET MODEM HANGUP-METHOD RS232 and try again.\n"
3531                                  );
3532                         else
3533                           printf(
3534 " . SET MODEM HANGUP-METHOD MODEM-COMMAND and try again.\n"
3535                                  );
3536                         printf(
3537 " . If that doesn't work, try again with SET DIAL HANGUP OFF.\n"
3538                                );
3539                     } else {
3540                         printf(
3541 " . Give a HANGUP or SET DIAL HANGUP ON command and try again.\n"
3542                                );
3543                     }
3544                     if (!dialdpy)
3545                       printf(
3546 " . Use SET DIAL DISPLAY ON to watch the dialog between Kermit and modem.\n"
3547                              );
3548                 }
3549 #ifndef NOSHOW
3550                 printf(
3551 " . SHOW COMMUNICATIONS, SHOW MODEM, SHOW DIAL to see current settings.\n"
3552                        );
3553 #endif /* NOSHOW */
3554 
3555 #ifndef NOHELP
3556                 printf(
3557 " . HELP SET MODEM, HELP SET DIAL, and HELP DIAL for more information.\n"
3558                        );
3559 #endif /* NOHELP */
3560                 printf("(Use SET HINTS OFF to suppress future hints.)\n");
3561                 printf("*************************\n\n");
3562             }
3563 #endif /* NOHINTS */
3564         }
3565     }
3566     return(success);
3567 }
3568 #endif /* NODIAL */
3569 
3570 /*  D O T Y P E  --  Type (display) a file with various options...  */
3571 
3572 #ifdef BIGBUFOK
3573 #define TYPBUFL 16384
3574 #else
3575 #define TYPBUFL 256
3576 #endif /* BIGBUFOK */
3577 
3578 int typ_lines = 0;                      /* \v(ty_ln) */
3579 int typ_mtchs = 0;                      /* \v(ty_lm) */
3580 static int typ_int = 0;                 /* Flag if TYPE interrupted */
3581 
3582 #ifdef UNICODE
3583 extern int fcharset, fileorder, byteorder, ucsorder;
3584 #define TYPXBUFL TYPBUFL+TYPBUFL+TYPBUFL+4
3585 static char * mp = NULL;
3586 static char * mbuf = NULL;
3587 static long xn = 0L;
3588 
3589 static int
3590 #ifdef CK_ANSIC
storechar(char c)3591 storechar(char c)
3592 #else
3593 storechar(c) char c;
3594 #endif /* CK_ANSIC */
3595 {
3596     if (!mp) return(-1);
3597     if (++xn > TYPXBUFL)
3598       return(-1);
3599     debug(F111,"storechar xn",ckitoa((int)c),xn);
3600     *mp++ = c;
3601     return(0);
3602 }
3603 #endif /* UNICODE */
3604 
3605 static FILE * ofp = NULL;               /* For /OUTPUT: file */
3606 
3607 static int
typeline(buf,len,outcs,ofp)3608 typeline(buf,len,outcs,ofp) char * buf; int len, outcs; FILE * ofp; {
3609     register int i;
3610 
3611     debug(F011,"typeline buf",buf,len);
3612     /* debug(F101,"typeline outcs","",outcs); */
3613 
3614 #ifdef OS2
3615 #ifndef NOLOCAL
3616 #ifdef UNICODE
3617     /* In K95 only, the buffer is guaranteed to be in UCS-2 if outcs >= 0. */
3618     /* Len is its length in bytes.  There is no line terminator. */
3619     /* outcs is the file character-set number (FC_xxx) of the target set */
3620     /* that was requested by the user. */
3621     if (!inserver && !k95stdout) {
3622         extern int wherex[], wherey[];
3623         extern unsigned char colorcmd;
3624 
3625         VscrnWrtUCS2StrAtt( VCMD, (unsigned short *)buf, len/2,
3626                            wherey[VCMD], wherex[VCMD], &colorcmd);
3627         printf("\r\n");
3628         return(0);
3629     }
3630 #endif /* UNICODE */
3631 #endif /* NOLOCAL */
3632 #endif /* OS2 */
3633 
3634 /* In Unix, VMS, etc, the line has already been converted to the desired  */
3635 /* character-set, if one was given.  OR... on all platforms, including in */
3636 /* K95, we don't know the character set.  In either case we dump the line */
3637 /* byte by byte in case it contains NULs (printf() would truncate). */
3638 
3639 #ifdef COMMENT
3640     for (i = 0; i < len; i++)
3641       putchar(buf[i]);
3642 #else
3643     for (i = 0; i < len; i++) {
3644         if (ofp == stdout) {
3645             putchar(buf[i]);
3646         } else {
3647             putc(buf[i],ofp);
3648         }
3649     }
3650 #endif /* COMMENT */
3651 
3652 #ifdef IKSD
3653     if (inserver) {
3654 #ifdef UNICODE
3655         if (outcs == FC_UCS2) {
3656             if (ofp == stdout) {
3657                 putchar(NUL);
3658             } else {
3659                 putc(NUL,ofp);
3660             }
3661         }
3662 #endif /* UNICODE */
3663         if (ofp == stdout) {
3664             putchar('\r');
3665         } else {
3666             putc('\r',ofp);
3667         }
3668     }
3669 #endif /* IKSD */
3670 #ifdef UNICODE
3671     if (outcs == FC_UCS2) {
3672         if (ofp == stdout) {
3673             putchar(NUL);
3674         } else {
3675             putc(NUL,ofp);
3676         }
3677     }
3678 #endif /* UNICODE */
3679     if (ofp == stdout) {
3680         putchar('\n');
3681     } else {
3682         putc('\n',ofp);
3683     }
3684     fflush(stdout);
3685     return(0);
3686 }
3687 
3688 static int                              /* Get translated line */
typegetline(incs,outcs,buf,n)3689 typegetline(incs, outcs, buf, n) int incs, outcs, n; char * buf; {
3690     int x = 0, c0, c1, len = 0, count = 0, eof = 0, xlate = 0;
3691 #ifdef UNICODE
3692     int xxn = -1;
3693     int yyn = -9;
3694     xn = 0L;
3695 
3696 #ifdef DEBUG
3697     if (deblog && typ_lines == 0) {
3698         debug(F101,"typegetline incs","",incs);
3699         debug(F101,"typegetline outcs","",outcs);
3700         debug(F101,"typegetline feol","",feol);
3701         debug(F101,"typegetline byteorder","",byteorder);
3702         debug(F101,"typegetline ucsorder ","",ucsorder);
3703         debug(F111,"typegetline fileorder","1",fileorder);
3704     }
3705 #endif /* DEBUG */
3706 
3707     if (incs < 0)                       /* Shouldn't happen */
3708       return(-2);
3709 
3710     if (outcs == -1)                    /* Can happen */
3711       outcs = incs;
3712 
3713     if (incs != outcs || incs == FC_UCS2) { /* See if we should translate */
3714         xlate = 1;
3715         if (!mbuf) {                    /* Allocate buffer if not allocated */
3716             mbuf = (char *)malloc(TYPXBUFL+1); /* yet */
3717             if (!mbuf) {
3718                 printf("WARNING: Translation buffer allocation failure.\n");
3719                 printf("Translation will be skipped...\n");
3720                 xlate = 0;
3721             }
3722         }
3723     }
3724     if (xlate) {                        /* Translating... */
3725         mp = mbuf;                      /* Reset working buffer pointer */
3726 /*
3727   Here we call xgnbyte() in a loop, having it return UCS-2 bytes.  In K95, we
3728   use UCS-2 directly.  Elsewhere, we feed the UCS-2 bytes into xpnbyte() to
3729   convert them to the desired target character set.  But since we are using
3730   UCS-2, we have several sources for confusion: (1) xgnbyte() might return in
3731   LE or BE byte order, with no explicit indication of what the order is; but
3732   (2) xpnbyte() wants BE; but (3) Windows wants LE.
3733 */
3734         while (1) {
3735             if (typ_int)                /* Quit if interrupted */
3736               return(0);
3737             c0 = xgnbyte(FC_UCS2,incs,NULL); /* Convert to UCS-2 */
3738             debug(F000,"typegetline c0","",c0);
3739             if (c0 < 0) {               /* EOF */
3740                 eof++;
3741                 break;
3742             }
3743             c1 = xgnbyte(FC_UCS2,incs,NULL); /* Convert to UCS-2 */
3744             debug(F000,"typegetline c1","",c1);
3745             if (c1 < 0) {               /* EOF */
3746                 eof++;
3747                 break;
3748             }
3749 #ifdef DEBUG
3750             if (deblog && typ_lines == 0) {
3751                 if (count == 0) /* Check fileorder after BOM */
3752                   debug(F111,"typegetline fileorder","2",fileorder);
3753             }
3754 #endif /* DEBUG */
3755 
3756 #ifdef COMMENT
3757 /* Now we have the two UCS-2 bytes.  Which order are they in? */
3758 
3759             if (fileorder > 0) {        /* Little Endian */
3760                 int t;                  /* So swap them */
3761                 debug(F100,"typegetline swapping","",0);
3762                 t = c1;
3763                 c1 = c0;
3764                 c0 = t;
3765             }
3766 #endif /* COMMENT */
3767             if (c0 == 0 && c1 == 0x0D)  /* Now see if we have EOL */
3768               yyn = xn;
3769 
3770             if (c0 == 0 && c1 == 0x0A)  /* Now see if we have EOL */
3771               xxn = xn;
3772 
3773             count++;                    /* Count byte */
3774 
3775 /* Give the two bytes to xpnbyte() in BE order */
3776 
3777             if ((x = xpnbyte(c0,TC_UCS2,outcs,storechar)) < 0) return(-1);
3778             if ((x = xpnbyte(c1,TC_UCS2,outcs,storechar)) < 0) return(-1);
3779 
3780             if (xxn > -1) {             /* Have end of line? */
3781                 xn = xxn;
3782                 if (yyn == xxn - 2)     /* Adjust for CRLF */
3783                   xn = yyn;
3784                 break;                  /* And break out of loop. */
3785             }
3786         }
3787         mbuf[xn] = NUL;
3788         if (xn > n)                     /* Can truncate here... */
3789           xn = n;
3790         memcpy(buf,mbuf,xn);
3791         debug(F011,"typegetline xlate",buf,xn);
3792         return((eof && (xn == 0)) ? -1 : xn);
3793     }
3794 #endif /* UNICODE */
3795 #ifdef COMMENT
3796     /* We can't use this because, stupidly, zsinl() doesn't return a length. */
3797     /* It could be changed but then we'd have to change all ck?fio.c modules */
3798     x = zsinl(ZIFILE,buf,n);
3799 #else
3800     /* So instead, we copy zsinl() to here... */
3801     /* But note: This does not necessarily handle UCS-2 alignment properly;  */
3802     /* that's what the code in the first section of this routine is for. */
3803     /* But it does tolerate files that contain NULs. */
3804     {
3805         int a;
3806         char *s;
3807 
3808         s = buf;
3809         a = -1;                         /* Current character, none yet. */
3810         debug(F101,"typegetline zsinl simulation","",n);
3811         while (n--) {                   /* Up to given length */
3812 #ifdef COMMENT
3813             int old = 0;
3814             if (feol)                   /* Previous character */
3815               old = a;
3816 #endif /* COMMENT */
3817             if (zchin(ZIFILE,&a) < 0) { /* Read a character from the file */
3818                 debug(F101,"typegetline zchin fail","",count);
3819                 if (count == 0)
3820                   x = -1;               /* EOF or other error */
3821                 break;
3822             } else
3823               count++;
3824             if (feol) {                 /* Single-character line terminator */
3825                 if (a == feol)
3826                   break;
3827             } else {                    /* CRLF line terminator */
3828 #ifdef COMMENT
3829 /* Debug log shows that in Windows, <CR><LF> is returned as <LF>. */
3830 /* Apparently we're not reading the file in binary mode. */
3831 
3832                 if (a == '\015')        /* CR, get next character */
3833                   continue;
3834                 if (old == '\015') {    /* Previous character was CR */
3835                     if (a == '\012') {  /* This one is LF, so we have a line */
3836                         break;
3837                     } else {            /* Not LF, deposit CR */
3838                         *s++ = '\015';
3839                         n--;
3840                         len++;
3841                     }
3842                 }
3843 #else
3844                 if (a == LF) {
3845                     if (s[len] == CR) { /* This probably won't happen */
3846                         s[len] = NUL;
3847                         s--;
3848                         len--;
3849                     }
3850                     break;
3851                 }
3852 #endif /* COMMENT */
3853             }
3854             *s = a;                     /* Deposit character */
3855             s++;
3856             len++;
3857         }
3858         *s = '\0';                      /* Terminate the string */
3859     }
3860 #endif /* COMMENT */
3861     return(x < 0 ? -1 : len);
3862 }
3863 
3864 
3865 #ifndef MAC
3866 SIGTYP
3867 #ifdef CK_ANSIC
tytrap(int foo)3868 tytrap(int foo)                         /* TYPE interrupt trap */
3869 #else
3870 tytrap(foo) int foo;
3871 #endif /* CK_ANSIC */
3872 /* tytrap */ {
3873 #ifdef __EMX__
3874     signal(SIGINT, SIG_ACK);
3875 #endif
3876     debug(F100,"type tytrap SIGINT","",0);
3877     typ_int = 1;                        /* (Need arg for ANSI C) */
3878     SIGRETURN;
3879 }
3880 #endif /* MAC */
3881 
3882 int
dotype(file,paging,first,head,pat,width,prefix,incs,outcs,outfile,z)3883 dotype(file, paging, first, head, pat, width, prefix, incs, outcs, outfile, z)
3884     char * file, * pat, * prefix; int paging, first, head, width, incs, outcs;
3885     char * outfile; int z;
3886 /* dotype */ {
3887     extern CK_OFF_T ffc;
3888     char buf[TYPBUFL+2];
3889     char * s = NULL;
3890     int rc = 1, lines = 0, ucs2 = 0;
3891     char ** tail = NULL;
3892     int * tlen = NULL;
3893     int tailing = 0, counting = 0;
3894     int x, c, n, i, j, k = 0;
3895     int number = 0, save, len, pfxlen = 0, evalpfx = 1;
3896 #ifdef UNICODE
3897     int ucsbom_sav;
3898     extern int ucsbom;
3899 #endif /* UNICODE */
3900 #ifdef NT
3901     int gui = 0;
3902 #endif /* NT */
3903 
3904 #ifndef MAC
3905 #ifdef OS2
3906 #ifdef NT
3907     SIGTYP (* oldsig)(int);             /* For saving old interrupt trap. */
3908 #else /* NT */
3909     SIGTYP (* volatile oldsig)(int);
3910 #endif /* NT */
3911 #else /* OS2 */
3912     SIGTYP (* oldsig)();
3913 #endif /* OS2 */
3914 #endif /* MAC */
3915 
3916 #ifdef KUI
3917     if (outfile == (char *)1) {
3918         gui = 1;
3919         outfile = "";
3920     }
3921 #endif /* KUI */
3922 
3923     if (!file) file = "";
3924     if (!*file) return(-2);
3925 
3926     if (ofp != stdout) {                /* In case of previous interruption */
3927         if (ofp) fclose(ofp);
3928         ofp = stdout;
3929     }
3930     if (!outfile) outfile = "";
3931     if (outfile[0]) {
3932         ofp = fopen(outfile,"w");       /* Open output file */
3933         if (!ofp) {
3934             printf("?Can't open output file %s: %s\n",outfile,ck_errstr());
3935             ofp = stdout;
3936             return(-9);
3937         }
3938     }
3939     number = z;
3940     if (number && prefix) prefix = NULL;
3941 
3942 #ifdef UNICODE
3943     ucsbom_sav = ucsbom;                /* We are not creating a file */
3944     ucsbom = 0;                         /* Do not use BOM bytes */
3945 #endif /* UNICODE */
3946 
3947     typ_int = 0;
3948 
3949     save = binary;                      /* Save file type */
3950 
3951     debug(F101,"dotype incs","",incs);
3952     debug(F101,"dotype outcs","",outcs);
3953 
3954 #ifdef UNICODE
3955     debug(F111,"dotype fileorder","A",fileorder);
3956 #ifdef OS2
3957     if (!inserver && !k95stdout)
3958       outcs = FC_UCS2;
3959 #endif /* OS2 */
3960 
3961     if (outcs == FC_UCS2)               /* Output is UCS-2? */
3962       ucs2 = 1;
3963     if (fileorder < 0)
3964       fileorder = ucsorder;
3965     debug(F111,"dotype fileorder","B",fileorder);
3966 #endif /* UNICODE */
3967 
3968 #ifdef CK_TTGWSIZ
3969 #ifdef OS2
3970     ttgcwsz();
3971 #else /* OS2 */
3972     /* Check whether window size changed */
3973     if (ttgwsiz() > 0) {
3974         if (tt_rows > 0 && tt_cols > 0) {
3975             cmd_rows = tt_rows;
3976             cmd_cols = tt_cols;
3977             debug(F101,"dotype cmd_rows","",cmd_rows);
3978             debug(F101,"dotype cmd_cols","",cmd_cols);
3979         }
3980     }
3981 #endif /* OS2 */
3982 #endif /* CK_TTGWSIZ */
3983 
3984     if (prefix)
3985       pfxlen = strlen(prefix);
3986 
3987     if (paging < 0) {                   /* Count only, don't print */
3988         counting = 1;
3989         prefix = NULL;
3990         width = 0;
3991         paging = 0;
3992     }
3993     if (ucs2)                           /* Crude... */
3994       width *= 2;
3995 
3996 #ifdef OS2
3997     if (*file) {
3998         ckstrncpy(buf, file, TYPBUFL);  /* Change / to \. */
3999         p = buf;
4000         while (*p) {
4001             if (*p == '/') *p = '\\';
4002             p++;
4003         }
4004         file = buf;
4005     } else {
4006         rc = 0;
4007         goto xdotype;
4008     }
4009 #endif /* OS2 */
4010 
4011     if (zchki(file) == -2) {            /* It's a directory */
4012         debug(F111,"dotype zchki failure",file,-2);
4013         if (xcmdsrc == 0) {
4014             printf("?Not a regular file: \"%s\"\n",file);
4015             rc = -9;
4016         } else
4017           rc = 0;
4018         goto xdotype;
4019     }
4020     if (!zopeni(ZIFILE, file)) {        /* Not a directory, open it */
4021         debug(F111,"dotype zopeni failure",file,0);
4022         if (xcmdsrc == 0) {
4023             printf("?Can't open file: \"%s\"\n",file);
4024             rc = -9;
4025         } else
4026           rc = 0;
4027         goto xdotype;
4028     }
4029 
4030 #ifndef AMIGA
4031 #ifndef MAC
4032     errno = 0;
4033     oldsig = signal(SIGINT, tytrap);    /* Save current interrupt trap. */
4034     /* debug(F111,"type SIGINT trap set",ckitoa(errno),oldsig); */
4035 #endif /* MAC */
4036 #endif /* AMIGA */
4037 
4038     if (paging > -1)                    /* More-prompting */
4039       xaskmore = paging;
4040 
4041     binary = 0;
4042 
4043     if (head < 0) {                     /* "tail" was requested */
4044         tailing = 1;                    /* Set flag */
4045         head = 0 - head;                /* Get absolute number of lines */
4046         if (!counting) {
4047             tail = (char **) malloc(head * sizeof(char *)); /* Allocate list */
4048             if (!tail) {
4049                 printf("?Memory allocation failure\n");
4050                 goto xdotype;
4051 
4052             }
4053             tlen = (int *) malloc(head * sizeof(int));
4054             if (!tlen) {
4055                 printf("?Memory allocation failure\n");
4056                 goto xdotype;
4057 
4058             }
4059             for (i = 0; i < head; i++) { /* Initialize each pointer in list. */
4060                 tail[i] = NULL;
4061                 tlen[i] = 0;
4062             }
4063         }
4064     }
4065     typ_lines = 0;
4066     typ_mtchs = 0;
4067 
4068 #ifdef UNICODE
4069     if (outcs > -1 && (incs != outcs || incs == FC_UCS2)) { /* Translating? */
4070         ffc = (CK_OFF_T)0;
4071         initxlate(incs,outcs);          /* Set up translation functions */
4072     } else
4073 #endif /* UNICODE */
4074       outcs = -1;                       /* Means we don't know the charset */
4075 
4076     debug(F101,"dotype ffc","",ffc);
4077     debug(F101,"dotype outcs 2","",outcs);
4078 #ifdef UNICODE
4079     debug(F111,"dotype fileorder","C",fileorder);
4080 #endif /* UNICODE */
4081 
4082     /* Allow the buffer to contain NULs */
4083 
4084     for (n = first;
4085          (len = typegetline(incs,outcs,buf,TYPBUFL)) > -1;
4086          lines++
4087          ) {
4088         debug(F011,"dotype line",buf,len);
4089 #ifndef MAC
4090         if (typ_int) {                  /* Interrupted? */
4091             typ_int = 0;
4092             debug(F101,"type interrupted line","",lines);
4093             printf("^C...\n");          /* Print message */
4094             if (ofp != stdout) {        /* Close any output file */
4095                 if (ofp) fclose(ofp);
4096                 ofp = stdout;
4097             }
4098             goto xxdotype;
4099         }
4100 #endif /* MAC */
4101         typ_lines++;                    /* For \v(ty_ln) */
4102         if (pat)                        /* Matching? */
4103           if (!ckmatch(pat,buf,1,1+4))  /* Line matches pattern? */
4104             continue;                   /* No, skip it */
4105         typ_mtchs++;
4106 
4107         if (head > 0 && !tailing && lines == head) /* Handle /HEAD:n */
4108           break;
4109 
4110         buf[TYPBUFL+1] = NUL;           /* Just in case... */
4111         if (prefix) {                   /* Add specified prefix to each line */
4112             char pbuf[64];
4113             char * pp;
4114             pp = prefix;
4115 #ifndef NOSPL
4116             if (evalpfx) {              /* Prefix is a variable? */
4117                 int n = 63;             /* Maybe - evaluate it and see */
4118                 char * p = pbuf;
4119                 zzstring(prefix,&p,&n); /* If there is no change */
4120                 if (!strcmp(prefix,pbuf)) { /* it's not a variable */
4121                     evalpfx = 0;        /* So don't do this again. */
4122                 } else {                /* It was a variable */
4123                     pp = pbuf;          /* So substitute its value */
4124                     pfxlen = 63 - n;    /* and get its new length */
4125                 }
4126             }
4127 #endif /* NOSPL */
4128             if (len + pfxlen + 2 < TYPBUFL) {
4129                 /* Shift right to make room for prefix */
4130                 memcpy((char *)line+pfxlen,(char *)buf,len);
4131                 lset((char *)line,pp,pfxlen,SP);
4132                 debug(F110,"dotype prefix",line,pfxlen);
4133                 len += pfxlen;
4134                 memcpy((char *)buf,(char *)line,len);
4135             }
4136         } else if (number) {            /* Line numbers */
4137             int x;
4138             sprintf(line,"%4d. ",typ_lines);
4139             x = strlen(line);
4140             len += x;
4141             if (len < LINBUFSIZ) {
4142                 memcpy((char *)&line[x],(char *)buf,len);
4143                 memcpy((char *)buf,(char *)line,len);
4144             }
4145         }
4146         if (width > 0 && width <= TYPBUFL) { /* Truncate at given width. */
4147             char * obuf = line;         /* But to do that first we must */
4148             int i,k,z;                  /* expand tabs; assume every 8 cols. */
4149             line[0] = NUL;
4150             for (i = 0, k = 0; i < width; k++) { /* Character loop... */
4151                 if (!buf[k])            /* No more chars in this line, done. */
4152                   break;
4153                 if (buf[k] != '\t') {   /* If it's not a tab */
4154                     if (i >= LINBUFSIZ) /* Check for overflow */
4155                       break;
4156                     obuf[i++] = buf[k]; /* and then deposit it. */
4157                     obuf[i] = NUL;      /* Keep it null-terminated */
4158                     continue;
4159                 }
4160                 z = 8 - (i % 8);        /* It's a tab, expand it. */
4161                 if (z == 0) z = 8;
4162                 for (j = 0; j < z && i < LINBUFSIZ; j++) {
4163 #ifdef UNICODE
4164                     if (ucs2 && !ucsorder)
4165                       obuf[i++] = NUL;
4166 #endif /* UNICODE */
4167                     obuf[i++] = ' ';
4168 #ifdef UNICODE
4169                     if (ucs2 && ucsorder)
4170                       obuf[i++] = NUL;
4171 #endif /* UNICODE */
4172                 }
4173                 obuf[i++] = NUL;
4174                 obuf[i] = NUL;
4175             }
4176             obuf[width] = NUL;          /* Now truncate at given width. */
4177 #ifdef COMMENT
4178             /* This doesn't work for UCS-2 because it contains NULs */
4179             ckstrncpy(buf,obuf,TYPBUFL); /* and copy it back (again?) */
4180 #else
4181             memcpy((char *)buf,(char *)obuf,i); /* Copy it back */
4182 #endif /* COMMENT */
4183             len = (i > width) ? width : i; /* Spare us another strlen()... */
4184         }
4185         if (tailing) {                  /* If /TAIL:n... */
4186             k = lines % head;           /* save this line in circular buffer */
4187             if (!counting) {
4188                 if (tail[k]) free(tail[k]);
4189                 tail[k] = malloc(len+2);
4190                 if (!tail[k]) {
4191                     printf("?Memory allocation failure\n");
4192                     goto xdotype;
4193                 }
4194                 memcpy(tail[k],buf,len);
4195                 tlen[k] = len;
4196                 continue;
4197             }
4198         }
4199         if (counting)                   /* If only counting */
4200           continue;                     /* we're done with this line */
4201 
4202         if (paging) {                   /* Displaying this line... */
4203             int u;
4204             u = len;                    /* Length in BYTES */
4205             if (ucs2)                   /* If outputting in UCS-2 */
4206               u /= 2;                   /* convert length to CHARACTERS */
4207             x = (u / cmd_cols) + 1;     /* Crudely allow for wrap */
4208             if (cmd_rows > 0 && cmd_cols > 0)
4209               n += x;                   /* This assumes terminal will wrap */
4210         }
4211 #ifdef KUI
4212         if ( gui ) {
4213             int i;
4214             unsigned short * uch = (unsigned short *)buf;
4215             for ( i=0; i<len/2; i++)
4216                 gui_text_popup_append(uch[i]);
4217 			gui_text_popup_append(CR);
4218 			gui_text_popup_append(LF);
4219         }
4220         else
4221 #endif /* KUI */
4222         typeline(buf,len,outcs,ofp);    /* Print line, length based */
4223 #ifdef CK_TTGWSIZ
4224         debug(F101,"dotype n","",n);
4225         if (paging > 0 && ofp == stdout) { /* Pause at end of screen */
4226             if (cmd_rows > 0 && cmd_cols > 0) {
4227                 if (n > cmd_rows - 3) {
4228                     if (!askmore())
4229                       goto xdotype;
4230                     else
4231                       n = 0;
4232                 }
4233             }
4234         }
4235 #endif /* CK_TTGWSIZ */
4236     }
4237 
4238   xdotype:
4239     if (counting) {
4240         fprintf(ofp,
4241                 "%s: %d line%s\n",file,typ_lines,typ_lines == 1 ? "" : "s");
4242         if (pat)
4243           fprintf(ofp,
4244                   "%s: %d match%s\n",pat,typ_mtchs,typ_mtchs == 1 ? "" : "es");
4245         goto xxdotype;
4246     }
4247     if (tailing && tail) {              /* Typing tail of file? */
4248         if (lines < head) {             /* Yes, show the lines we saved */
4249             k = 0;                      /* Show all lines */
4250         } else {                        /* More lines than tail number */
4251             lines = k;                  /* Last line to show */
4252             k++;                        /* First line to show */
4253             if (k >= head)
4254               k = 0;
4255         }
4256         n = first;                      /* Output line counter */
4257         for (i = k ;; i++) {            /* Loop thru circular buffer */
4258 #ifndef MAC
4259             if (typ_int) {              /* Interrupted? */
4260                 printf("^C...\n");      /* Print message */
4261                 goto xxdotype;
4262             }
4263 #endif /* MAC */
4264             j = i % head;               /* Index of this line */
4265             s = tail[j];                /* Point to line to display */
4266             if (!s)                     /* (shouldn't happen...) */
4267               break;
4268             if (paging) {               /* Crudely allow for line wrap */
4269                 x = tlen[j];
4270                 if (ucs2) x /= 2;
4271                 x = x / cmd_cols + 1;
4272                 if (cmd_rows > 0 && cmd_cols > 0)
4273                   n += x;
4274             }
4275             typeline(s,tlen[j],outcs,ofp); /* Display this line */
4276             if (paging && ofp == stdout) { /* Pause at end of screen */
4277                 if (cmd_rows > 0 && cmd_cols > 0) {
4278                     if (n > cmd_rows - 3) {
4279                         if (!askmore())
4280                           break;
4281                         else
4282                           n = 0;
4283                     }
4284                 }
4285             }
4286             tail[j] = NULL;
4287             free(s);                    /* Free the line */
4288             if (i % head == lines)      /* When to stop */
4289               break;
4290         }
4291         free((char *)tail);             /* Free the list */
4292         tail = NULL;
4293         if (tlen) free((char *)tlen);
4294         tlen = NULL;
4295     }
4296 
4297 /* Come here when finished or on SIGINT */
4298 
4299   xxdotype:
4300 #ifndef AMIGA
4301 #ifndef MAC
4302     signal(SIGINT,oldsig);              /* Put old signal action back. */
4303 #endif /* MAC */
4304 #endif /* AMIGA */
4305     if (tailing && tail) {
4306         for (i = 0; i < head; i++) {    /* Free each line. */
4307             if (tail[i])
4308               free(tail[i]);
4309         }
4310         free((char *)tail);             /* Free list pointer */
4311         if (tlen)
4312           free((char *)tlen);
4313     }
4314     x = zclose(ZIFILE);                 /* Done, close the input file */
4315     if (ofp != stdout) {                /* Close any output file */
4316         if (ofp) fclose(ofp);
4317         ofp = stdout;
4318     }
4319     binary = save;                      /* Restore text/binary mode */
4320 #ifdef UNICODE
4321     ucsbom = ucsbom_sav;                /* Restore BOM usage */
4322 #endif /* UNICODE */
4323 
4324 #ifdef KUI
4325     if ( gui )
4326         gui_text_popup_wait(-1);        /* Wait for user to close the dialog */
4327 #endif /* KUI */
4328     return(rc);
4329 }
4330 
4331 /* GREP command */
4332 
4333 #define GREP_CASE  0                    /* /CASE */
4334 #define GREP_COUN  1                    /* /COUNT */
4335 #define GREP_DOTF  2                    /* /DOTFILES */
4336 #define GREP_NAME  3                    /* /NAMEONLY */
4337 #define GREP_NOBK  4                    /* /NOBACKUP */
4338 #define GREP_NODO  5                    /* /NODOTFILES */
4339 #define GREP_NOLI  6                    /* /NOLIST */
4340 #define GREP_NOMA  7                    /* /INVERT = /NOMATCH */
4341 #define GREP_NOPA  8                    /* /NOPAGE */
4342 #define GREP_NUMS  9                    /* /LINENUMBERS */
4343 #define GREP_PAGE 10                    /* /PAGE */
4344 #define GREP_RECU 11                    /* /RECURSIVE */
4345 #define GREP_TYPE 12                    /* /TYPE: */
4346 #define GREP_OUTP 13                    /* /OUTPUTFILE: */
4347 #define GREP_EXCP 14			/* /EXCEPT: */
4348 #define GREP_ARRA 15			/* /ARRAY: */
4349 
4350 static struct keytab greptab[] = {
4351     { "/array",        GREP_ARRA, CM_ARG },
4352     { "/count",        GREP_COUN, CM_ARG },
4353     { "/dotfiles",     GREP_DOTF, 0 },
4354     { "/except",       GREP_EXCP, CM_ARG },
4355     { "/linenumbers",  GREP_NUMS, 0 },
4356     { "/nameonly",     GREP_NAME, 0 },
4357     { "/nobackupfiles",GREP_NOBK, 0 },
4358     { "/nocase",       GREP_CASE, 0 },
4359     { "/nodotfiles",   GREP_NODO, 0 },
4360     { "/nolist",       GREP_NOLI, 0 },
4361     { "/nomatch",      GREP_NOMA, 0 },
4362     { "/nopage",       GREP_NOPA, 0 },
4363     { "/output",       GREP_OUTP, CM_ARG },
4364     { "/page",         GREP_PAGE, 0 },
4365     { "/quiet",        GREP_NOLI, CM_INV },
4366 #ifdef RECURSIVE
4367     { "/recursive",    GREP_RECU, 0 },
4368 #endif /* RECURSIVE */
4369     { "/type",         GREP_TYPE, CM_ARG },
4370     { "", 0, 0 }
4371 };
4372 static int ngreptab =  sizeof(greptab)/sizeof(struct keytab)-1;
4373 
4374 static char * grep_except = NULL;
4375 
4376 int
dogrep()4377 dogrep() {
4378     int match, x, y, fc, getval, mc = 0, count = 0, bigcount = 0;
4379     int fline = 0, sline = 0, wild = 0, len = 0;
4380     int xmode = -1, scan = 0;
4381     char c, name[CKMAXPATH+1], outfile[CKMAXPATH+1], *p, *s, *cv = NULL;
4382     FILE * fp = NULL;
4383 #ifndef NOSPL
4384     char array = NUL;
4385     char ** ap = NULL;
4386     int arrayindex = 0;
4387 #endif /* NOSPL */
4388 
4389     int                                 /* Switch values and defaults */
4390       gr_coun = 0,
4391       gr_name = 0,
4392       gr_nobk = 0,
4393       gr_case = 1,
4394       gr_noli = 0,
4395       gr_noma = 0,
4396       gr_nums = 0,
4397       gr_excp = 0,
4398       gr_page = xaskmore;
4399 
4400     struct FDB sw, fl;
4401 
4402     g_matchdot = matchdot;              /* Save global matchdot setting */
4403     outfile[0] = NUL;
4404     makestr(&grep_except,NULL);
4405 
4406     if (ofp != stdout) {                /* In case of previous interruption */
4407         if (ofp) fclose(ofp);
4408         ofp = stdout;
4409     }
4410     cmfdbi(&sw,                         /* First FDB - command switches */
4411            _CMKEY,                      /* fcode */
4412            "String or pattern to search for, or switch",
4413            "",                          /* default */
4414            "",                          /* addtl string data */
4415            ngreptab,                    /* addtl numeric data 1: tbl size */
4416            4,                           /* addtl numeric data 2: 4 = cmswi */
4417            xxstring,                    /* Processing function */
4418            greptab,                     /* Keyword table */
4419            &fl                          /* Pointer to next FDB */
4420            );
4421     cmfdbi(&fl,                         /* Anything that doesn't match */
4422            _CMFLD,                      /* fcode */
4423            "",                          /* hlpmsg */
4424            "",                          /* default */
4425            "",                          /* addtl string data */
4426            0,                           /* addtl numeric data 1 */
4427            0,                           /* addtl numeric data 2 */
4428            xxstring,			/* xxstring */
4429            NULL,
4430            NULL
4431            );
4432     while (1) {                         /* Parse 0 or more switches */
4433         x = cmfdb(&sw);                 /* Parse something */
4434         if (x < 0)
4435           return(x);
4436         if (cmresult.fcode != _CMKEY)   /* Break out if not a switch */
4437           break;
4438         c = cmgbrk();
4439         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
4440             printf("?This switch does not take an argument\n");
4441             return(-9);
4442         }
4443         if ((cmresult.nresult != GREP_COUN) && !getval &&
4444             (cmgkwflgs() & CM_ARG)) {
4445             printf("?This switch requires an argument\n");
4446             return(-9);
4447         }
4448         switch (cmresult.nresult) {
4449           case GREP_COUN: {
4450               gr_coun++;
4451               if (getval) {
4452                   if ((x = cmfld("Variable for result","",&s,NULL)) < 0)
4453                     return(x);
4454                   makestr(&cv,s);
4455               }
4456               break;
4457           }
4458           case GREP_CASE: gr_case=0; break;
4459           case GREP_NAME: gr_name++; gr_noli=0; break;
4460           case GREP_NOBK: gr_nobk++; break;
4461           case GREP_NOLI: gr_noli++; gr_name=0; gr_nums=0; break;
4462           case GREP_NOMA: gr_noma++; break;
4463           case GREP_NOPA: gr_page=0; break;
4464           case GREP_NUMS: gr_nums++; gr_noli=0; break;
4465           case GREP_PAGE: gr_page++; gr_noli=0; break;
4466           case GREP_NODO:
4467             matchdot = 0;
4468             break;
4469           case GREP_DOTF:
4470             matchdot = 1;
4471             break;
4472           case GREP_ARRA: {
4473 	      char * s2;
4474             if (c != ':' && c != '=') {
4475                 printf("?Array name required\n");
4476                 return(-9);
4477             }
4478             if ((x = cmfld("Array name (a single letter will do)",
4479                            "",
4480                            &s,
4481                            NULL
4482                            )) < 0) {
4483                 if (x == -3) {
4484                     printf("?Array name required\n");
4485                     return(-9);
4486                 } else
4487                   return(x);
4488             }
4489             if (!*s) {
4490                 printf("?Array name required\n");
4491                 return(-9);
4492             }
4493             s2 = s;
4494             if (*s == CMDQ) s++;
4495             if (*s == '&') s++;
4496             if (!isalpha(*s)) {
4497                 printf("?Bad array name - \"%s\"\n",s2);
4498                 return(-9);
4499             }
4500             array = *s++;
4501             if (isupper(array)) array = tolower(array);
4502             if (*s && (*s != '[' || *(s+1) != ']')) {
4503                 printf("?Bad array name - \"%s\"\n",s2);
4504                 return(-9);
4505             }
4506 	    gr_name = 1;
4507             break;
4508 	  }
4509 #ifdef RECURSIVE
4510           case GREP_RECU:
4511             recursive = 1;
4512             break;
4513 #endif /* RECURSIVE */
4514           case GREP_TYPE: {
4515               extern struct keytab txtbin[];
4516               if ((x = cmkey(txtbin,3,"","",xxstring)) < 0)
4517                 return(x);
4518               if (x == 2) {             /* ALL */
4519                   xmode = -1;
4520               } else {                  /* TEXT or BINARY only */
4521                   xmode = x;
4522                   scan = 1;
4523               }
4524               break;
4525           }
4526           case GREP_OUTP:               /* Send output to file */
4527             if ((x = cmofi("File for GREP'd lines","",&s,xxstring)) < 0)
4528               return(x);
4529             ckstrncpy(outfile,s,CKMAXPATH);
4530             break;
4531 	  case GREP_EXCP:		/* Exception pattern */
4532 	    if (getval) {
4533 		if ((x = cmfld("Exception pattern",
4534 			       "",
4535 			       &s,
4536 			       xxstring
4537 			       )) < 0)
4538 		  return(x);
4539 		gr_excp++;
4540 		makestr(&grep_except,s);
4541 	    }
4542         }
4543     }
4544     if (outfile[0]) {
4545         ofp = fopen(outfile,"w");       /* Open output file */
4546         if (!ofp) {
4547             printf("?Can't open output file %s: %s\n",outfile,ck_errstr());
4548             ofp = stdout;
4549             return(-9);
4550         }
4551         gr_page = 0;
4552     }
4553     s = cmresult.sresult;
4554     s = brstrip(s);                     /* Strip braces from pattern */
4555     if (!*s) {
4556         printf("?Pattern required\n");
4557         return(-9);
4558     }
4559     ckstrncpy(tmpbuf,s,TMPBUFSIZ);      /* Save pattern */
4560     if ((x = cmifi("File(s) to search","",&s,&wild,xxstring)) < 0) {
4561         if (x == -3) {
4562             printf("?File specification required\n");
4563             x = -9;
4564         }
4565         return(x);
4566     }
4567     s = brstrip(s);                     /* Strip braces from filename */
4568 #ifndef ZXREWIND
4569     ckstrncpy(line,s,LINBUFSIZ);
4570 #endif /* ZXREWIND */
4571     if ((y = cmcfm()) < 0)
4572       return(y);
4573 
4574     if (gr_page > -1)
4575       xaskmore = gr_page;               /* Paging... */
4576 
4577     p = tmpbuf;                         /* Point to pattern */
4578 #ifdef COMMENT
4579 /* Now this is done in ckmatch */
4580     if (*p == '^') {                    /* '^' anchors pattern to beginning */
4581         p++;
4582     } else if (*p != '*') {             /* Otherwise prepend implied '*' */
4583         tmpbuf[0] = '*';
4584         p = tmpbuf;
4585     }
4586     x = strlen(p);                      /* Get length of result */
4587     if (x > 0 && x < TMPBUFSIZ) {       /* '$' at end anchors pattern to end */
4588         if (p[x-1] == '$') {
4589             p[x-1] = NUL;
4590         } else if (p[x-1] != '*') {
4591             p[x] = '*';
4592             p[x+1] = NUL;
4593         }
4594     }
4595 #endif /* COMMENT */
4596     debug(F111,"grep pat",p,x);
4597 
4598 #ifdef ZXREWIND
4599     fc = zxrewind();                    /* Rewind the file list */
4600 #else
4601     {
4602         int flags = ZX_FILONLY;         /* Expand file list */
4603         if (matchdot)  flags |= ZX_MATCHDOT;
4604         if (recursive) flags |= ZX_RECURSE;
4605         fc = nzxpand(line,flags);
4606     }
4607 #endif /* ZXREWIND */
4608 
4609 #ifndef NOSPL
4610     if (array) {
4611         int n, xx;
4612         n = (fc < 0) ? 0 : fc;
4613         if ((xx = dclarray(array,n)) < 0) {
4614             printf("?Array declaration failure\n");
4615             mc = 0;
4616             goto xgrep;
4617         }
4618 	arrayindex = 0;
4619         ap = a_ptr[xx];			/* Pointer to list of elements */
4620         if (ap)				/* Set element 0 to dimension */
4621           makestr(&(ap[0]),"0");	/* which so far is zero */
4622         if (n < 1) {			/* No files matched, done. */
4623             mc = 0;
4624             goto xgrep;
4625         }
4626     }
4627 #endif /* NOSPL */
4628 
4629 #ifdef UNIX
4630     sh_sort(mtchs,NULL,fc,0,0,filecase);
4631 #endif /* UNIX */
4632 
4633     debug(F101,"grep cmd_rows","",cmd_rows);
4634     debug(F101,"grep cmd_cols","",cmd_cols);
4635 
4636     while (1) {                         /* Loop for each file */
4637         znext(name);                    /* Get next file */
4638         if (!name[0])                   /* No more, done */
4639           break;
4640         if (gr_nobk)                    /* Skipping backup files? */
4641           if (ckmatch("*.~[1-9]*~",name,1,1)) /* Backup file? */
4642             continue;                   /* Yes, skip */
4643         if (scan) {                     /* /TYPE: given? */
4644             switch (scanfile(name,&y,nscanfile)) { /* Yes, scan the file */
4645               case FT_BIN:
4646                 if (xmode != 1)
4647                   continue;
4648                 break;
4649               case FT_TEXT:
4650               case FT_7BIT:
4651               case FT_8BIT:
4652 #ifdef UNICODE
4653               case FT_UTF8:
4654               case FT_UCS2:
4655 #endif /* UNICODE */
4656                 if (xmode != 0)
4657                   continue;
4658             }
4659         }
4660         fp = fopen(name,"r");           /* Open */
4661         if (!fp)                        /* Can't */
4662           continue;                     /* Skip */
4663         count = 0;                      /* Match count, this file */
4664         fline = 0;                      /* Line count, this file */
4665         while (1) {                     /* Loop for each line */
4666             if (fgets(line,LINBUFSIZ,fp) == NULL) { /* Get next line */
4667                 fclose(fp);
4668                 fp = NULL;
4669 		debug(F100,"GREP EOF","",0);
4670                 break;
4671             }
4672             fline++;                    /* Count this line */
4673             line[LINBUFSIZ] = NUL;      /* Make sure it's terminated */
4674 	    debug(F111,"GREP",line,fline);
4675             len = (int)strlen(line);    /* Get length */
4676             while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r'))
4677               line[--len] = NUL;        /* Chop off terminators */
4678             match = ckmatch(p,line,gr_case,1+4); /* Match against pattern */
4679 	    if (match && gr_excp) {
4680 		if (ckmatch(grep_except,line,gr_case,1+4))
4681 		    match = 0;
4682 	    }
4683             if (gr_noma)                /* Invert match sense if requested */
4684               match = !match;
4685             if (match) {                /* Have a matching line */
4686                 mc++;                   /* Total match count */
4687                 count++;                /* Match count this file */
4688                 if (gr_name) {          /* Don't care how many lines match */
4689                     fclose(fp);         /* Close the file */
4690                     fp = NULL;          /* and quit the line-reading loop. */
4691                     break;
4692                 }
4693                 if (gr_coun || gr_noli) /* Not listing each line */
4694                   continue;             /* so don't print anything now. */
4695                 if (wild) {		/* If searching multiple files */
4696                     fprintf(ofp,"%s:",name); /* print filename. */
4697                     len += (int)strlen(name) + 1;
4698                 }
4699                 if (gr_nums) {          /* If line numbers wanted */
4700                     char nbuf[32];
4701                     len += ckmakmsg(nbuf,32,ckitoa(fline),":",NULL,NULL);
4702                     fprintf(ofp,"%s",nbuf);
4703                 }
4704                 if (cmd_rows > 0 && cmd_cols > 0)
4705                   sline += (len / cmd_cols) + 1;
4706                 fprintf(ofp,"%s\n",line); /* Print the line. */
4707                 if (sline > cmd_rows - 3) {
4708                     if (!askmore()) goto xgrep; else sline = 0;
4709                 }
4710             }
4711         }
4712         if (!gr_noli) {			/* If not not listing... */
4713             x = 0;
4714             if (gr_coun) {              /* Show match count only */
4715                 fprintf(ofp,"%s:%d\n",name,count);
4716                 x++;
4717             } else if (gr_name && count > 0) { /* Show name only */
4718 		if (array) {
4719 		    if (ap) {
4720 			makestr(&(ap[arrayindex++]),name);
4721 		    }
4722 		} else {
4723 		    fprintf(ofp,"%s\n",name);
4724 		    x++;
4725 		}
4726             }
4727             if (x > 0) {
4728                 if (++sline > cmd_rows - 3) {
4729                     if (!askmore()) goto xgrep; else sline = 0;
4730                 }
4731             }
4732         }
4733         bigcount += count;              /* Overall count */
4734     }
4735   xgrep:
4736 #ifndef NOSPL
4737     if (array) if (ap) makestr(&(ap[0]),ckitoa(arrayindex));
4738     if (gr_coun && cv) {                /* /COUNT:blah */
4739         addmac(cv,ckitoa(bigcount));    /* set the variable */
4740         makestr(&cv,NULL);              /* free this */
4741     }
4742 #endif /* NOSPL */
4743     if (fp) fclose(fp);                 /* close input file if still open */
4744     if (ofp != stdout) {                /* Close any output file */
4745         if (ofp) fclose(ofp);
4746         ofp = stdout;
4747     }
4748     return(success = mc ? 1 : 0);
4749 }
4750 
4751 /* System-independent directory */
4752 
4753 static char ** dirlist = NULL;
4754 static int ndirlist = 0;
4755 
4756 static VOID
freedirlist()4757 freedirlist() {
4758     if (dirlist) {
4759         int i;
4760         for (i = 0; i < ndirlist; i++) {
4761             if (dirlist[i])
4762               free(dirlist[i]);
4763         }
4764         free((char *)dirlist);
4765         dirlist = NULL;
4766     }
4767     ndirlist = 0;
4768 }
4769 
4770 static struct keytab dirswtab[] = {     /* DIRECTORY command switches */
4771     { "/after",       DIR_AFT, CM_ARG },
4772     { "/all",         DIR_ALL, 0 },
4773 #ifndef NOSPL
4774     { "/array",       DIR_ARR, CM_ARG },
4775 #endif /* NOSPL */
4776     { "/ascending",   DIR_ASC, 0 },
4777     { "/backup",      DIR_BUP, 0 },
4778     { "/before",      DIR_BEF, CM_ARG },
4779     { "/brief",       DIR_BRF, 0 },
4780     { "/count",       DIR_COU, CM_ARG },
4781     { "/descending",  DIR_DSC, CM_INV },
4782     { "/directories", DIR_DIR, 0 },
4783     { "/dotfiles",    DIR_DOT, 0 },
4784     { "/englishdate", DIR_DAT, 0 },
4785     { "/except",      DIR_EXC, CM_ARG },
4786     { "/files",       DIR_FIL, 0 },
4787 #ifdef CKSYMLINK
4788     { "/followlinks", DIR_LNK, 0 },
4789 #endif /* CKSYMLINK */
4790     { "/heading",     DIR_HDG, 0 },
4791     { "/isodate",     DIR_ISO, 0 },
4792     { "/larger-than", DIR_LAR, CM_ARG },
4793     { "/message",     DIR_MSG, CM_ARG },
4794     { "/nobackupfiles",DIR_NOB, 0 },
4795     { "/nodotfiles",  DIR_NOD, 0 },
4796 #ifdef CKSYMLINK
4797     { "/nofollowlinks",DIR_NLK, 0 },
4798 #endif /* CKSYMLINK */
4799     { "/noheading",   DIR_NOH, 0 },
4800 #ifdef CKSYMLINK
4801     { "/nolinks",     DIR_NOL, 0 },
4802 #endif /* CKSYMLINK */
4803     { "/nomessage",   DIR_NOM, 0 },
4804 #ifdef CK_TTGWSIZ
4805     { "/nopage",      DIR_NOP, 0 },
4806 #endif /* CK_TTGWSIZ */
4807 #ifdef RECURSIVE
4808     { "/norecursive", DIR_NOR, 0 },
4809 #else
4810 #ifdef VMS
4811     { "/norecursive", DIR_NOR, 0 },
4812 #else
4813 #ifdef datageneral
4814     { "/norecursive", DIR_NOR, 0 },
4815 #endif /* datageneral */
4816 #endif /* VMS */
4817 #endif /* RECURSIVE */
4818     { "/nosort",      DIR_NOS, 0 },
4819     { "/not-after",   DIR_NAF, CM_ARG },
4820     { "/not-before",  DIR_NBF, CM_ARG },
4821     { "/not-since",   DIR_NAF, CM_INV|CM_ARG },
4822     { "/noxfermode",  DIR_NOT, 0 },
4823     { "/output",      DIR_OUT, CM_ARG },
4824 #ifdef CK_TTGWSIZ
4825     { "/page",        DIR_PAG, 0 },
4826 #endif /* CK_TTGWSIZ */
4827 #ifdef RECURSIVE
4828     { "/recursive",   DIR_REC, 0 },
4829 #else
4830 #ifdef VMS
4831     { "/recursive",   DIR_REC, 0 },
4832 #else
4833 #ifdef datageneral
4834     { "/recursive",   DIR_REC, 0 },
4835 #endif /* datageneral */
4836 #endif /* VMS */
4837 #endif /* RECURSIVE */
4838     { "/reverse",     DIR_DSC, 0 },
4839     { "/since",       DIR_AFT, CM_ARG|CM_INV },
4840     { "/smaller-than",DIR_SMA, CM_ARG },
4841     { "/sort",        DIR_SRT, CM_ARG },
4842     { "/summary",     DIR_SUM, 0 },
4843     { "/top",         DIR_TOP, CM_ARG },
4844     { "/type",        DIR_BIN, CM_ARG },
4845     { "/xfermode",    DIR_TYP, 0 },
4846     { "/verbose",     DIR_VRB, 0 },
4847     { "",0,0 }
4848 };
4849 static int ndirswtab = (sizeof(dirswtab) / sizeof(struct keytab)) - 1;
4850 
4851 static struct keytab dirsort[] = {      /* DIRECTORY /SORT: options */
4852     { "date",         DIRS_DT, 0 },
4853     { "name",         DIRS_NM, 0 },
4854     { "size",         DIRS_SZ, 0 }
4855 };
4856 static int ndirsort = (sizeof(dirsort) / sizeof(struct keytab));
4857 
4858 static struct keytab touchswtab[] = {	/* TOUCH command switches */
4859     { "/after",       DIR_AFT, CM_ARG },
4860     { "/all",         DIR_ALL, 0 },
4861     { "/backup",      DIR_BUP, 0 },
4862     { "/before",      DIR_BEF, CM_ARG },
4863     { "/count",       DIR_COU, CM_ARG },
4864     { "/directories", DIR_DIR, 0 },
4865     { "/dotfiles",    DIR_DOT, 0 },
4866     { "/except",      DIR_EXC, CM_ARG },
4867     { "/files",       DIR_FIL, 0 },
4868 #ifdef CKSYMLINK
4869     { "/followlinks", DIR_LNK, 0 },
4870 #endif /* CKSYMLINK */
4871     { "/larger-than", DIR_LAR, CM_ARG },
4872     { "/list",        DIR_VRB, 0 },
4873     { "/modtime",     DIR_MOD, CM_ARG },
4874     { "/nobackupfiles",DIR_NOB, 0 },
4875     { "/nodotfiles",  DIR_NOD, 0 },
4876 #ifdef CKSYMLINK
4877     { "/nofollowlinks",DIR_NLK, 0 },
4878 #endif /* CKSYMLINK */
4879 #ifdef CKSYMLINK
4880     { "/nolinks",     DIR_NOL, 0 },
4881 #endif /* CKSYMLINK */
4882 #ifdef CK_TTGWSIZ
4883 #endif /* CK_TTGWSIZ */
4884 #ifdef RECURSIVE
4885     { "/norecursive", DIR_NOR, 0 },
4886 #else
4887 #ifdef VMS
4888     { "/norecursive", DIR_NOR, 0 },
4889 #else
4890 #ifdef datageneral
4891     { "/norecursive", DIR_NOR, 0 },
4892 #endif /* datageneral */
4893 #endif /* VMS */
4894 #endif /* RECURSIVE */
4895     { "/not-after",   DIR_NAF, CM_ARG },
4896     { "/not-before",  DIR_NBF, CM_ARG },
4897     { "/not-since",   DIR_NAF, CM_INV|CM_ARG },
4898 #ifdef RECURSIVE
4899     { "/recursive",   DIR_REC, 0 },
4900 #else
4901 #ifdef VMS
4902     { "/recursive",   DIR_REC, 0 },
4903 #else
4904 #ifdef datageneral
4905     { "/recursive",   DIR_REC, 0 },
4906 #endif /* datageneral */
4907 #endif /* VMS */
4908 #endif /* RECURSIVE */
4909     { "/simulate",    DIR_SIM, 0 },
4910     { "/since",       DIR_AFT, CM_ARG|CM_INV },
4911     { "/smaller-than",DIR_SMA, CM_ARG },
4912     { "/type",        DIR_BIN, CM_ARG },
4913     { "/verbose",     DIR_VRB, CM_INV },
4914     { "",0,0 }
4915 };
4916 static int ntouchswtab = (sizeof(touchswtab) / sizeof(struct keytab)) - 1;
4917 
4918 static struct keytab changeswtab[] = {	/* CHANGE command switches */
4919     { "/after",       DIR_AFT, CM_ARG },
4920     { "/backup",      DIR_BAK, CM_ARG },
4921     { "/before",      DIR_BEF, CM_ARG },
4922     { "/case",           7777, CM_ARG },
4923     { "/count",       DIR_COU, CM_ARG },
4924     { "/destination", DIR_DES, CM_ARG },
4925     { "/dotfiles",    DIR_DOT, 0 },
4926     { "/except",      DIR_EXC, CM_ARG },
4927     { "/larger-than", DIR_LAR, CM_ARG },
4928     { "/list",        DIR_VRB, 0 },
4929     { "/modtime",     DIR_MOD, CM_ARG },
4930     { "/nodotfiles",  DIR_NOD, 0 },
4931 #ifdef RECURSIVE
4932     { "/norecursive", DIR_NOR, 0 },
4933 #endif /* RECURSIVE */
4934     { "/not-after",   DIR_NAF, CM_ARG },
4935     { "/not-before",  DIR_NBF, CM_ARG },
4936     { "/not-since",   DIR_NAF, CM_INV|CM_ARG },
4937 #ifdef RECURSIVE
4938     { "/recursive",   DIR_REC, 0 },
4939 #endif /* RECURSIVE */
4940     { "/simulate",    DIR_SIM, 0 },
4941     { "/since",       DIR_AFT, CM_ARG|CM_INV },
4942     { "/smaller-than",DIR_SMA, CM_ARG },
4943     { "/verbose",     DIR_VRB, CM_INV },
4944     { "",0,0 }
4945 };
4946 static int nchangeswtab = (sizeof(changeswtab) / sizeof(struct keytab)) - 1;
4947 
4948 #define CHMT_U 0			/* CHANGE command modtime options */
4949 #define CHMT_P 1
4950 
4951 static struct keytab chmttab[] = {
4952     { "preserve",     CHMT_P, 0 },
4953     { "update",       CHMT_U, 0 }
4954 };
4955 static int nchmttab = 2;
4956 
4957 static int dir_date = -1;               /* Option defaults (-1 means none) */
4958 static int dir_page = -1;
4959 static int dir_verb =  1;
4960 static int dir_msg  = -1;
4961 #ifdef VMS
4962 static int dir_sort = -1;               /* Names are already sorted in VMS */
4963 static int dir_rvrs = -1;
4964 #else
4965 static int dir_sort =  1;               /* Sort by default */
4966 static int dir_rvrs =  0;               /* Not in reverse */
4967 #endif /* VMS */
4968 static int dir_skey = DIRS_NM;          /* By name */
4969 #ifdef RECURSIVE
4970 static int dir_recu = -1;
4971 #endif /* RECURSIVE */
4972 static int dir_mode = -1;
4973 static int dir_show = -1;               /* Show all files by default */
4974 int dir_dots =  -1;			/* Except dot files */
4975 int dir_back =  1;
4976 int dir_head =  0;
4977 static char * dirmsg = NULL;
4978 static int dirmsglen = 0;
4979 
4980 #ifndef NOSHOW
4981 VOID
showdiropts()4982 showdiropts() {
4983     int x = 0;
4984     extern int optlines;
4985     prtopt(&optlines,"DIRECTORY");
4986     if (dir_show > 0) {
4987         prtopt(&optlines,(dir_show == 1) ? "/FILES" :
4988                ((dir_show == 2) ? "/DIRECTORIES" : "/ALL"));
4989         x++;
4990     } else {
4991         prtopt(&optlines,"/ALL");
4992         x++;
4993     }
4994     if (dir_verb > -1) {
4995         prtopt(&optlines,dir_verb ? "/VERBOSE" : "/BRIEF");
4996         x++;
4997     }
4998     if (dir_page > -1) {
4999         prtopt(&optlines,dir_page ? "/PAGE" : "/NOPAGE");
5000         x++;
5001     }
5002     if (dir_date > -1) {
5003         prtopt(&optlines,dir_date ? "/ENGLISHDATE" : "/ISODATE");
5004         x++;
5005     }
5006     if (dir_dots > -1) {
5007         prtopt(&optlines,dir_dots ? "/DOTFILES" : "/NODOTFILES");
5008         x++;
5009     }
5010     if (dir_back > -1) {
5011         prtopt(&optlines,dir_back ? "/BACKUP" : "/NOBACKUP");
5012         x++;
5013     }
5014     if (dir_head > -1) {
5015         prtopt(&optlines,dir_head ? "/HEADING" : "/NOHEADING");
5016         x++;
5017     }
5018 #ifdef RECURSIVE
5019     if (dir_recu > -1) {
5020         prtopt(&optlines,dir_recu ? "/RECURSIVE" : "/NORECURSIVE");
5021         x++;
5022     }
5023 #endif /* RECURSIVE */
5024     if (dir_mode > -1) {
5025         prtopt(&optlines,dir_mode ? "/XFERMODE" : "/NOXFERMODE");
5026         x++;
5027     }
5028     if (dir_sort == 0) {
5029         x++;
5030         prtopt(&optlines,"/NOSORT ");
5031     } else if (dir_sort > 0) {
5032         x++;
5033         if (dir_skey == DIRS_NM) s = "/SORT:NAME";
5034         else if (dir_skey == DIRS_SZ) s = "/SORT:SIZE";
5035         else if (dir_skey == DIRS_DT) s = "/SORT:DATE";
5036         prtopt(&optlines,s);
5037     }
5038     if (dir_rvrs > -1) {
5039         prtopt(&optlines,dir_rvrs ? "/REVERSE" : "/ASCENDING");
5040         x++;
5041     }
5042     if (dir_msg > -1) {
5043         if (dir_msg == 0) {
5044             prtopt(&optlines,"/NOMESSAGE");
5045         } else {
5046             ckmakmsg(tmpbuf,TMPBUFSIZ,"/MESSAGE:{",dirmsg,"}",NULL);
5047             prtopt(&optlines,tmpbuf);
5048         }
5049         x++;
5050     }
5051     if (!x) prtopt(&optlines,"(no options set)");
5052     prtopt(&optlines,"");
5053 }
5054 #endif /* NOSHOW */
5055 
5056 int
setdiropts()5057 setdiropts() {                          /* Set DIRECTORY option defaults */
5058     int xb = -1, xv = -1, xp = -1, xd = -1, xh = -1, xf = -1;
5059     int xk = -1, xr = -1, xs = -1, xx = -1, xm = -1, xa = -1, xg = -1;
5060     int getval;
5061     char c;
5062     while (1) {
5063         if ((y = cmswi(dirswtab,ndirswtab,"Switch","",xxstring)) < 0) {
5064             if (y == -3)
5065               break;
5066             else
5067               return(y);
5068         }
5069         c = cmgbrk();
5070         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
5071             printf("?This switch does not take an argument\n");
5072             return(-9);
5073         }
5074         if (!getval && (cmgkwflgs() & CM_ARG)) {
5075             printf("?This switch requires an argument\n");
5076             return(-9);
5077         }
5078         switch (y) {
5079           case DIR_BRF: xv = 0; break;
5080           case DIR_VRB: xv = 1; break;
5081           case DIR_PAG: xp = 1; break;
5082           case DIR_NOP: xp = 0; break;
5083           case DIR_ISO: xd = 0; break;
5084           case DIR_DAT: xd = 1; break;
5085           case DIR_HDG: xh = 1; break;
5086           case DIR_NOH: xh = 0; break;
5087           case DIR_DOT: xf = 1; break;
5088           case DIR_NOD: xf = 0; break;
5089           case DIR_ALL: xa = 3; break;
5090           case DIR_DIR: xa = 2; break;
5091           case DIR_FIL: xa = 1; break;
5092           case DIR_SRT:
5093             x = DIRS_NM;
5094             if (getval)
5095               if ((x = cmkey(dirsort,ndirsort,"Sort key","name",xxstring)) < 0)
5096                 return(x);
5097             xk = x;
5098             xs = 1;
5099             break;
5100           case DIR_NOS: xs = 0; break;
5101           case DIR_ASC: xx = 0; break;
5102           case DIR_DSC: xx = 1; break;
5103           case DIR_REC: xr = 1; break;
5104           case DIR_NOR: xr = 0; break;
5105           case DIR_TYP: xm = 1; break;
5106           case DIR_NOT: xm = 0; break;
5107           case DIR_BUP: xb = 1; break;
5108           case DIR_NOB: xb = 0; break;
5109           case DIR_NOM: xg = 0; break;
5110           case DIR_MSG:
5111             if (getval)
5112               if ((x = cmfld("Message to append to each line",
5113                              "",
5114                              &s,
5115                              xxstring
5116                              )) < 0)
5117                 return(x);
5118             xg = 1;
5119             ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ);
5120             break;
5121           default:
5122             printf("?This option can not be set\n");
5123             return(-9);
5124         }
5125     }
5126     if ((x = cmcfm()) < 0)              /* Get confirmation */
5127       return(x);
5128     if (xv > -1) dir_verb = xv;         /* Confirmed, save defaults */
5129     if (xp > -1) dir_page = xp;
5130     if (xd > -1) dir_date = xd;
5131     if (xh > -1) dir_head = xh;
5132     if (xs > -1) dir_sort = xs;
5133     if (xk > -1) dir_skey = xk;
5134     if (xx > -1) dir_rvrs = xx;
5135     if (xf > -1) dir_dots = xf;
5136     if (xa > -1) dir_show = xa;
5137     if (xm > -1) dir_mode = xm;
5138     if (xb > -1) dir_back = xb;
5139 #ifdef RECURSIVE
5140     if (xr > -1) dir_recu = xr;
5141 #endif /* RECURSIVE */
5142     if (xg > -1) dir_msg  = xg;
5143     if (xg > 0)
5144       makestr(&dirmsg,tmpbuf);
5145     return(success = 1);
5146 }
5147 
5148 int
domydir(cx)5149 domydir(cx) int cx; {			/* Internal DIRECTORY command */
5150     extern char *months[], *tempdir;
5151 #ifdef VMS
5152     _PROTOTYP( char * zrelname, (char *,char *) );
5153     char * cdp = NULL;
5154 #endif /* VMS */
5155     struct zattr xxstruct;
5156 
5157     int chmtopt = CHMT_U;
5158     char name[CKMAXPATH+1], outfile[CKMAXPATH+1], *p = NULL, c = NUL;
5159     char linebuf[CKMAXPATH+CKMAXPATH+256];
5160     char string1[1024], string2[1024]; 	/* For CHANGE */
5161     char modtime[100];
5162     char * mstr = NULL, * dstr = NULL, * s2 = NULL, * cv = NULL;
5163     CK_OFF_T len = (CK_OFF_T)0, nbytes = (CK_OFF_T)0;
5164     CK_OFF_T minsize = (CK_OFF_T)-1, maxsize = (CK_OFF_T)-1;
5165     long ndirs = 0, nfiles = 0, nmatches = 0;
5166     int verbose = 0, wild = 0, page = 0, n = 0, engdate = 0, summary = 0;
5167     int heading = 0, xsort = 0, reverse = 0, sortby = 0, msg = 0;
5168     int k, i = 0, x = 0, nx = 0, skey = 0, dlen = 0, itsadir = 0;
5169     int show = 3, xfermod = 0, backup = 1, rc = 0, getval = 0;
5170     int touch = 0;
5171     int change = 0;			/* Doing CHANGE command */
5172     int chcase = 0;			/* CHANGE case dependence */
5173     int s1len = 0;			/* CHANGE string1 length */
5174     int s2len = 0;			/* CHANGE string2 length */
5175     int fs = 0;
5176     int multiple = 0;
5177     int cmifn1 = 1, cmifn2 = 0;
5178     int dir_top = 0, dir_cou = 0;
5179     int dontshowlinks = 0;
5180     int dontfollowlinks = 0;
5181     int arrayindex = -1;
5182     int simulate = 0;
5183     struct FDB sw, fi, fl;
5184     char dbuf[256], xbuf[32];
5185     int reallysort = 0;
5186     int changeinplace = 0;
5187     int changebackup = 0;
5188     int changes = 0;                    /* Change counter per file */
5189     int totalchanges = 0;               /* Change counter all files */
5190 
5191 #ifndef NOSPL
5192     char array = NUL;
5193     char ** ap = NULL;
5194 #endif /* NOSPL */
5195     char
5196       * dir_aft = NULL,
5197       * dir_bef = NULL,
5198       * dir_naf = NULL,
5199       * dir_nbf = NULL,
5200       * dir_exc = NULL;
5201     char * xlist[16];
5202 
5203     debug(F101,"domydir cx","",cx);
5204 
5205     chgsourcedir[0] = NUL;              /* CHANGE source directory */
5206     chgdestdir[0] = NUL;                /* CHANGE destination directory */
5207     chgbackupdir[0] = NUL;              /* CHANGE backup directory */
5208     changeinplace = 1;                  /* CHANGE'ing files in place */
5209     changebackup = 0;                   /* Backing up CHANGEd files */
5210     changes = 0;
5211     totalchanges = 0;
5212 
5213     g_matchdot = matchdot;              /* Save global matchdot setting */
5214 #ifdef COMMENT
5215     nolinks = 2;                        /* (it should already be 2) */
5216 #endif	/* COMMENT */
5217     outfile[0] = NUL;                   /* No output file yet */
5218 
5219     modtime[0] = '\0';			/* Initialize TOUCH /MODTIME */
5220 
5221     if (ofp != stdout) {                /* In case of previous interruption */
5222         if (ofp) fclose(ofp);
5223         ofp = stdout;
5224     }
5225     for (i = 0; i < 16; i++) xlist[i] = NULL;
5226 
5227     dir_top = 0;
5228     name[0] = NUL;
5229     freedirlist();                      /* In case not freed last time */
5230     page      = dir_page > -1 ? dir_page : xaskmore; /* Set option defaults */
5231     engdate   = dir_date > -1 ? dir_date : 0;
5232     verbose   = dir_verb > -1 ? dir_verb : 1;
5233     heading   = dir_head > -1 ? dir_head : 0;
5234     xsort     = dir_sort > -1 ? dir_sort : 0;
5235     sortby    = dir_skey > -1 ? dir_skey : 0;
5236     reverse   = dir_rvrs > -1 ? dir_rvrs : 0;
5237     msg       = dir_msg  > -1 ? dir_msg  : 0;
5238 #ifdef UNIXOROSK
5239     if (dir_dots > -1) matchdot = dir_dots;
5240 #endif /* UNIXOROSK */
5241     xfermod   = dir_mode > -1 ? dir_mode : 0;
5242     backup    = dir_back > -1 ? dir_back : 1;
5243 #ifdef RECURSIVE
5244     recursive = dir_recu > -1 ? dir_recu : 0;
5245 #endif /* RECURSIVE */
5246     show      = dir_show > -1 ? dir_show : 3;
5247 
5248     diractive = 1;                      /* This is a DIRECTORY command */
5249 
5250     switch (cx) {
5251       case XXWDIR: 			/* WDIRECTORY */
5252 	debug(F100,"domydir WDIRECTORY","",0);
5253 	reverse = 1;			/* Reverse chronological order */
5254 	xsort = 1;
5255 	sortby = DIRS_DT;
5256 	break;
5257       case XXHDIR:			/* HDIRECTORY */
5258 	debug(F100,"domydir HDIRECTORY","",0);
5259 	reverse = 1;			/* Reverse order by size */
5260 	xsort = 1;
5261 	sortby = DIRS_SZ;
5262 	break;
5263       case XXTOUC:
5264         diractive = 0;                  /* This is NOT a DIRECTORY command */
5265 	touch = 1;
5266 	verbose = 0;
5267 	break;
5268       case XXCHG:			/* CHANGE 2013-04-18 */
5269         diractive = 0;                  /* This is NOT a DIRECTORY command */
5270 	change = 1;
5271 	verbose = 0;
5272     }
5273 
5274 #ifdef CK_TTGWSIZ
5275 #ifdef OS2
5276     ttgcwsz();                          /* Screen length for more-prompting */
5277 #else /* OS2 */
5278     /* Check whether window size changed */
5279     if (ttgwsiz() > 0) {
5280         if (tt_rows > 0 && tt_cols > 0) {
5281             cmd_rows = tt_rows;
5282             cmd_cols = tt_cols;
5283         }
5284     }
5285 #endif /* OS2 */
5286 #endif /* CK_TTGWSIZ */
5287 
5288     cmifn1 = nolinks | 1;               /* 1 = files or directories */
5289     cmifn2 = 0;                         /* 0 = not directories only */
5290 
5291   again:
5292 
5293     cmfdbi(&sw,                         /* First FDB - command switches */
5294            _CMKEY,                      /* fcode */
5295            "Enter or Return to confirm the command, or\n\
5296  file specification, or switch",
5297            "",                          /* default */
5298            "",                          /* addtl string data */
5299            touch ? ntouchswtab : (change ? nchangeswtab : ndirswtab),
5300            4,                           /* addtl numeric data 2: 4 = cmswi */
5301            xxstring,                    /* Processing function */
5302            touch ? touchswtab : (change ? changeswtab : dirswtab),
5303            &fi                          /* Pointer to next FDB */
5304            );
5305     cmfdbi(&fi,                         /* 2nd FDB - filespec to match */
5306            _CMIFI,                      /* fcode */
5307            "File specification",        /* hlpmsg */
5308 #ifdef datageneral
5309            "+",                         /* Default filespec is wildcard */
5310 #else                                   /* that matches all files... */
5311 #ifdef VMS
5312            "*.*",
5313 #else
5314            "*",
5315 #endif /* VMS */
5316 #endif /* datageneral */
5317            "",                          /* addtl string data */
5318            cmifn1,
5319            cmifn2,                      /* 1 = only dirs; 0 files or dirs */
5320            xxstring,
5321            NULL,
5322            &fl
5323            );
5324     cmfdbi(&fl,                         /* Anything that doesn't match */
5325            _CMFLD,                      /* fcode */
5326            "",                          /* hlpmsg */
5327            "",                          /* default */
5328            "",                          /* addtl string data */
5329            0,                           /* addtl numeric data 1 */
5330            0,                           /* addtl numeric data 2 */
5331            xxstring,
5332            NULL,
5333            NULL
5334            );
5335     while (1) {                         /* Parse 0 or more switches */
5336         x = cmfdb(&sw);                 /* Parse something */
5337         debug(F101,"domydir cmfdb","",x);
5338         if (x < 0)
5339           return(x);
5340         if (cmresult.fcode != _CMKEY)   /* Break out if not a switch */
5341           break;
5342         c = cmgbrk();
5343         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
5344             printf("?This switch does not take an argument\n");
5345             return(-9);
5346         }
5347 	k = cmresult.nresult;
5348         if (!getval &&
5349 	    (cmgkwflgs() & CM_ARG) && k != DIR_TOP && k != DIR_COU) {
5350             printf("?This switch requires an argument\n");
5351             return(-9);
5352         }
5353         switch (k) {
5354           case DIR_COU: {
5355               dir_cou++;
5356               if (getval) {
5357                   if ((x = cmfld("Variable for result","",&s,NULL)) < 0)
5358                     return(x);
5359                   makestr(&cv,s);
5360               }
5361               break;
5362           }
5363           case DIR_BRF: verbose = 0; break;
5364           case DIR_VRB: verbose = 1; break;
5365 #ifdef CK_TTGWSIZ
5366           case DIR_PAG: page = 1;    break;
5367           case DIR_NOP: page = 0;    break;
5368 #endif /* CK_TTGWSIZ */
5369           case DIR_ISO: engdate = 0; break;
5370           case DIR_DAT: engdate = 1; break;
5371           case DIR_HDG: heading = 1; break;
5372           case DIR_NOH: heading = 0; break;
5373 #ifdef UNIXOROSK
5374           case DIR_DOT: matchdot = 1; break;
5375           case DIR_NOD: matchdot = 0; break;
5376 #endif /* UNIXOROSK */
5377           case DIR_ALL:
5378             show = 3;
5379             cmifn1 |= 1;
5380             cmifn2 = 0;
5381             goto again;
5382           case DIR_DIR:
5383             show = 2;
5384             cmifn1 |= 1;
5385             cmifn2 = 1;
5386             goto again;
5387           case DIR_FIL:
5388             show = 1;
5389             cmifn1 &= ~(1);
5390             cmifn2 = 0;
5391             goto again;
5392           case DIR_SRT:
5393             x = DIRS_NM;
5394             if (c == ':' || c == '=')
5395               if ((x = cmkey(dirsort,ndirsort,"Sort key","name",xxstring)) < 0)
5396                 return(x);
5397             xsort = 1;
5398             sortby = x;
5399             break;
5400 
5401           case DIR_BUP: backup  = 1; fs++;   break;
5402           case DIR_NOB: backup  = 0; fs++;   break;
5403 
5404           case DIR_NOS: xsort = 0;     break;
5405           case DIR_ASC: reverse = 0;   break;
5406           case DIR_DSC: reverse = 1;   break;
5407 #ifdef RECURSIVE
5408           case DIR_REC: recursive = 1; diractive = 1; break;
5409           case DIR_NOR: recursive = 0; diractive = 0; break;
5410 #endif /* RECURSIVE */
5411           case DIR_TYP: xfermod = 1;   break;
5412           case DIR_NOT: xfermod = 0;   break;
5413 
5414 #ifdef CKSYMLINK
5415           case DIR_LNK:                 /* Follow links */
5416 #ifdef COMMENT
5417 	    /* A command switch shouldn't be setting a global value! */
5418             nolinks = 0;
5419 #endif	/* COMMENT */
5420             cmifn1 &= ~(2);
5421 	    dontfollowlinks = 0;
5422             goto again;
5423           case DIR_NLK:                 /* Don't follow links */
5424 #ifdef COMMENT
5425             nolinks = 2;
5426 #endif	/* COMMENT */
5427             cmifn1 &= ~(2);
5428 	    dontfollowlinks = 1;
5429             goto again;
5430 	  case DIR_NOL:			/* Don't show links at all */
5431 	    dontshowlinks = 1;
5432 	    goto again;
5433 #endif /* CKSYMLINK */
5434 
5435           case DIR_NOM: msg     = 0;   break;
5436           case DIR_MSG:
5437             if (c == ':' || c == '=')
5438               if ((x = cmfld("Message to append to each line",
5439                              "",
5440                              &s,
5441                              xxstring
5442                              )) < 0)
5443                 return(x);
5444             msg = 1;
5445             ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ);
5446             break;
5447 
5448           case DIR_SMA:
5449           case DIR_LAR: {
5450 	      CK_OFF_T y;
5451 	      if (!getval) break;
5452 	      if ((x = cmnumw("File size in bytes","0",10,&y,xxstring)) < 0)
5453 		return(x);
5454 	      fs++;
5455 	      show = 1;
5456 	      switch (cmresult.nresult) {
5457 		case DIR_SMA: minsize = y; break;
5458 		case DIR_LAR: maxsize = y; break;
5459 	      }
5460 	      break;
5461 	  }
5462 	  case DIR_TOP:
5463 	    dir_top = 10;
5464 	    if (!getval) break;
5465 	      if ((x = cmnum("How many lines to show","10",10,&y,xxstring))< 0)
5466 		return(x);
5467 	      dir_top = y;
5468 	      break;
5469 
5470 #ifndef NOSPL
5471           case DIR_ARR:
5472             if (c != ':' && c != '=') {
5473                 printf("?Array name required\n");
5474                 return(-9);
5475             }
5476             if ((x = cmfld("Array name (a single letter will do)",
5477                            "",
5478                            &s,
5479                            NULL
5480                            )) < 0) {
5481                 if (x == -3) {
5482                     printf("?Array name required\n");
5483                     return(-9);
5484                 } else
5485                   return(x);
5486             }
5487             if (!*s) {
5488                 printf("?Array name required\n");
5489                 return(-9);
5490             }
5491             s2 = s;
5492             if (*s == CMDQ) s++;
5493             if (*s == '&') s++;
5494             if (!isalpha(*s)) {
5495                 printf("?Bad array name - \"%s\"\n",s2);
5496                 return(-9);
5497             }
5498             array = *s++;
5499             if (isupper(array)) array = tolower(array);
5500             if (*s && (*s != '[' || *(s+1) != ']')) {
5501                 printf("?Bad array name - \"%s\"\n",s2);
5502                 return(-9);
5503             }
5504             break;
5505 #endif /* NOSPL */
5506           case DIR_AFT:
5507           case DIR_BEF:
5508           case DIR_NAF:
5509           case DIR_NBF:
5510             if (!getval) break;
5511             if ((x = cmdate("File-time","",&s,0,xxstring)) < 0) {
5512                 if (x == -3) {
5513                     printf("?Date-time required\n");
5514                     rc = -9;
5515                 } else
5516                   rc = x;
5517                 goto xdomydir;
5518             }
5519             fs++;
5520             switch (k) {
5521               case DIR_AFT: makestr(&dir_aft,s); break;
5522               case DIR_BEF: makestr(&dir_bef,s); break;
5523               case DIR_NAF: makestr(&dir_naf,s); break;
5524               case DIR_NBF: makestr(&dir_nbf,s); break;
5525             }
5526             break;
5527           case DIR_EXC:
5528             if (!getval) break;
5529             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
5530                 if (x == -3) {
5531                     printf("?Pattern required\n");
5532                     rc = -9;
5533                 } else
5534                   rc = x;
5535                 goto xdomydir;
5536             }
5537             fs++;
5538             makestr(&dir_exc,s);
5539             break;
5540 
5541           case DIR_SUM:
5542             summary = 1;
5543 	    break;
5544 
5545           case DIR_BIN: {
5546               extern struct keytab txtbin[];
5547               extern int xfiletype;
5548               if (!getval) break;
5549               if ((x = cmkey(txtbin,3,"","all",xxstring)) < 0) {
5550                   rc = x;
5551                   goto xdomydir;
5552               }
5553               if (x == 2) {
5554                   xfiletype = -1;
5555               } else {
5556                   xfiletype = x;
5557                   fs = 1;
5558               }
5559               break;
5560           }
5561           case DIR_OUT:
5562             if ((x = cmofi("File for directory listing","",&s,xxstring)) < 0)
5563               return(x);
5564             ckstrncpy(outfile,s,CKMAXPATH+1);
5565             break;
5566 
5567           case DIR_SIM:			/* TOUCH or CHANGE /SIMULATE */
5568 	    simulate = 1;
5569 	    break;
5570 
5571 	  case 7777:			/* CASE: (CHANGE only) */
5572 	    if (change) {
5573 		if ((x = cmkey(onoff,2,"","on",xxstring)) < 0)
5574 		  return(x);
5575 		chcase = x;
5576 	    }
5577 	    break;
5578 
5579           case DIR_MOD:			/* TOUCH or CHANGE /MODTIME: */
5580 	    if (change) {
5581 		if ((x = cmkey(chmttab,nchmttab,"","update",xxstring)) < 0)
5582 		  return(x);
5583 		chmtopt = x;
5584 		break;
5585 	    }
5586             if ((x = cmdate("File modification date-time",
5587 			    "now",&s,0,xxstring)) < 0)
5588 	      return(x);
5589 	    ckstrncpy(modtime,brstrip(s),100);
5590 	    break;
5591 
5592           case DIR_DES:                 /* CHANGE /DESTINATION:dirname */
5593           case DIR_BAK:                 /* CHANGE /BACKUP:dirname */
5594             if (change) {
5595                 int x;
5596                 char * whatdir = chgdestdir;
5597                 char * hmsg = "Directory for changed files";
5598 
5599                 if (k == DIR_BAK) {
5600                     hmsg = "Directory for backing up original files";
5601                     whatdir = chgbackupdir;
5602                 }
5603                 x = cmdir(hmsg,"",&s,xxstring);
5604                 if (x < 0) {
5605                       if (x == -3) {
5606                         printf("?Parse error\n");
5607                         return(-9);
5608                     }
5609                     return(x);
5610                 }
5611                 x = isdir(s);           /* this is overkill but... */
5612                 if (x < 0) {
5613                       if (x == -3) {
5614                         printf("?Directory name required\n");
5615                         return(-9);
5616                     }
5617                     return(x);
5618                 }
5619                 ckstrncpy(whatdir,s,MAXPATHLEN);
5620                 if (!isdir(whatdir)) { /* Double overkill */
5621                     printf("?%s is not a directory name\n",whatdir);
5622                     return(-9);
5623                 }
5624                 switch (k) {
5625                   case DIR_DES:         /* DESTINATION switch given */
5626                     changeinplace = 0;  /* Making new files */
5627                     break;
5628                   case DIR_BAK:         /* BACKUP switch given */
5629                     changebackup = 1;   /* Backup up original files */
5630                     break;
5631                 }
5632             }
5633             break;
5634 
5635           default:
5636             printf("?Sorry, not implemented yet - \"%s\"\n", atmbuf);
5637             goto xdomydir;
5638         }
5639     }
5640     ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Safe copy of filespec */
5641 
5642 /* ^^^ START MULTIPLE */
5643 
5644     while (!touch && !change) {		/* Multiple filespecs only for DIR */
5645 	x = cmfld("Another filespec or Enter","",&s,xxstring);
5646 	if (x == -3)
5647 	  break;
5648 	if (x < 0)
5649 	  return(x);
5650 	ckstrncat(line,",",LINBUFSIZ);
5651 	ckstrncat(line,s,LINBUFSIZ);
5652 	multiple++;
5653     }
5654     ckmakmsg(tmpbuf,TMPBUFSIZ,"{",line,"}",NULL);
5655     ckstrncpy(line,tmpbuf,LINBUFSIZ);
5656     cmresult.nresult = 1;
5657     cmresult.fcode = _CMIFI;
5658 
5659 /* ^^^ END MULTIPLE */
5660 
5661     ckstrncpy(name,line,MAXPATHLEN);
5662 
5663     if (change) {			/* Finish parsing CHANGE command */
5664         debug(F110,"CHANGE source file",line,0);
5665 	x = cmfld("Text to be changed","",&s,xxstring);
5666 	if (x < 0) {
5667 	    if (x == -3) {
5668 		printf("?You must specify the text to be changed\n");
5669 		return(-9);
5670 	    } else {
5671 		return(x);
5672 	    }
5673 	}
5674 	s = brstrip(s);
5675 	s1len = ckstrncpy(string1,s,1024);
5676         debug(F110,"CHANGE string1",string1,0);
5677 
5678 	x = cmfld("Text to change it to","",&s2,xxstring);
5679 	if (x < 0 && x != -3) return(x);
5680 	s2 = brstrip(s2);
5681 	s2len = ckstrncpy(string2,s2,1024);
5682         debug(F110,"CHANGE string2",string2,0);
5683     }
5684     if ((x = cmcfm()) < 0)              /* Get confirmation */
5685       return(x);
5686 /*
5687   Command is TOUCH and file doesn't exist.
5688 */
5689     if (touch) {			/* TOUCH */
5690 	if ((cmresult.fcode == _CMIFI && zchki(s) == (CK_OFF_T)-1)) {
5691 	    FILE * fp;
5692 	    s = brstrip(s);
5693 	    if (!iswild(s)) {
5694 		/* Given date-time, if any, else current date-time */
5695 		dstr = ckcvtdate(modtime[0] ? modtime : "",0);
5696 		xxstruct.date.val = dstr;
5697 		xxstruct.date.len = (int)strlen(xxstruct.date.val);
5698 		xxstruct.lprotect.len = 0;
5699 		xxstruct.gprotect.len = 0;
5700 #ifdef UNIX
5701 		if (s[0] == '~')
5702 		  s = tilde_expand(s);
5703 #endif	/* UNIX */
5704 		/* the IF condition was added 2013-04-15 */
5705 
5706 		if (zchki(s) < 0) {	/* If file doesn't already exist... */
5707 		    fp = fopen(s,"w");	/* Create it */
5708 		    if (!fp) {
5709 			printf("?TOUCH %s: %s\n",s,ck_errstr());
5710 			rc = -9;
5711 			goto xdomydir;
5712 		    }
5713 		    fclose(fp);
5714 		}
5715 		debug(F110,"TOUCH CREATE NONEXISTENT",s,0);
5716 		if (zstime(s,&xxstruct,0) < 0) {
5717 		    debug(F110,"TOUCH ZSTIME FAILED",s,0);
5718 		    printf("?TOUCH %s: %s\n",name,ck_errstr());
5719 		    rc = -9;
5720 		    goto xdomydir;
5721 		}
5722 		debug(F110,"TOUCH ZSTIME OK",xxstruct.date.val,0);
5723 		multiple++;		/* Force new directory scan */
5724 	    }
5725 	}
5726     } else
5727 
5728     if (cmresult.fcode != _CMIFI) {     /* Nothing matched */
5729 	/*
5730 	  Note - this never gets executed because after the "begin
5731 	  multiple" hack above, the result is always _CMIFI).
5732 	*/
5733         char * m;
5734 	if (*s == '/')
5735 #ifdef UNIXOROSK
5736 	  m = "does not match switch or name of accessible file";
5737 #else
5738 #ifdef OS2
5739 	m = "does not match switch or name of accessible file";
5740 #else
5741 	m = "no switches match";
5742 #endif /* OS2 */
5743 #endif /* UNIXOROSX */
5744 	else
5745 	  m = "not found or not accessible";
5746 	printf("\"%s\" - %s\n",s,m);
5747 	rc = -9;
5748 	goto xdomydir;
5749     }
5750 #ifdef COMMENT
5751     /* This can't be right because it's based on _CMCFM */
5752     wild = cmresult.nresult;            /* Wildcard was given? */
5753     debug(F111,"domydir cmifi2",s,wild);
5754 #else
5755     wild = 0;
5756 #endif	/* COMMENT */
5757 
5758     if (outfile[0]) {			/* If an output file was specified */
5759         ofp = fopen(outfile,"w");       /* open it */
5760         if (!ofp) {
5761             printf("?Can't open output file %s: %s\n",outfile,ck_errstr());
5762             ofp = stdout;
5763             return(-9);
5764         }
5765         page = 0;
5766     }
5767 
5768 #ifdef OS2
5769     if (!wild) {
5770         if (zchki(s) == -2) {           /* Found a directory */
5771             p = s + (int)strlen(s) - 1; /* Yes */
5772             if (*p == '\\' || *p == '/')
5773               strcat(s, "*");
5774             else if (*p == ':')
5775               strcat(s, "./*");
5776             else
5777               strcat(s, "/*");
5778             wild = 1;                   /* Now it's wild */
5779         }
5780     }
5781 #else
5782     if (!wild) if (isdir(s)) {          /* Is it a directory? */
5783         p = s + (int)strlen(s) - 1;     /* Yes */
5784 #ifdef VMS
5785         {
5786             /* Convert from FOO.DIR;1 to [x.FOO] if necessary */
5787             char buf[CKMAXPATH+1];
5788             debug(F000,"domydir directory 0",s,*p);
5789             if (cvtdir(s,buf,CKMAXPATH) > 0)
5790               ckstrncpy(line,buf,LINBUFSIZ);
5791         }
5792 #endif /* VMS */
5793         debug(F000,"domydir directory 1",s,*p);
5794 #ifdef VMS
5795         if (*p == ']' || *p == '>' || *p == ':')
5796           strcat(s, "*.*");
5797 #else
5798 #ifdef datageneral
5799         if (*p == ':')
5800           strcat(s, "+");
5801         else
5802           strcat(s, ":+");
5803 #else
5804 #ifdef STRATUS
5805         if (*p == '>')
5806           strcat(s, "*");
5807         else
5808           strcat(s, ">*");
5809 #endif /* STRATUS */
5810 #endif /* datageneral */
5811 #endif /* VMS */
5812         wild = 1;                       /* Now it's wild */
5813         debug(F000,"domydir directory 2",s,*p);
5814     }
5815 #endif /* OS2 */
5816 
5817 #ifdef ZXREWIND
5818 /* cmifi() already called nzxpand so we can just re-use the same list. */
5819     if (!multiple) {
5820 	x = zxrewind();			/* Rewind the list */
5821 	debug(F111,"domydir zxrewind",s,x);
5822     } else {
5823 #endif /* ZXREWIND */
5824 /*
5825   In case we gave multiple filespecs they are now in {a,b,c} list format.
5826   Which is a valid wildcard.  We pass it to nzxpand() to get back the list
5827   of files that match.  This is fine for DIRECTORY but it's not fine for
5828   TOUCH because we want TOUCH to see those names so it can create the files.
5829   So for now at least, if TOUCH is to be used to create files -- as opposed
5830   to changing the timestamps of existing files -- it can only do one file
5831   at a time.
5832 */
5833 	nzxopts = (show == ZX_DIRONLY) ? ZX_DIRONLY :
5834 	  (show == ZX_FILONLY ? ZX_FILONLY : 0);
5835 	if (matchdot)  nzxopts |= ZX_MATCHDOT;
5836 	if (recursive) nzxopts |= ZX_RECURSE;
5837 	x = nzxpand(s,nzxopts);             /* Expand file list */
5838 	debug(F111,"domydir nzxpand",s,x);
5839 #ifdef ZXREWIND
5840     }
5841 #endif /* ZXREWIND */
5842 
5843 #ifndef NOSPL
5844     if (array) {
5845         int n, xx;
5846         n = (x < 0) ? 0 : x;
5847         if ((xx = dclarray(array,n)) < 0) {
5848             printf("?Array declaration failure\n");
5849             rc = -9;
5850             goto xdomydir;
5851         }
5852 	arrayindex = xx;
5853         ap = a_ptr[xx];			/* Pointer to list of elements */
5854         if (ap)				/* Set element 0 to dimension */
5855           makestr(&(ap[0]),"0");	/* which so far is zero */
5856         if (n < 1) {			/* No files matched, done. */
5857             rc = 0;
5858             goto xdomydir;
5859         }
5860     } else
5861 #endif /* NOSPL */
5862       if (!touch && x < 1) {
5863 #ifdef CKROOT
5864           extern int ckrooterr;
5865           if (ckrooterr)
5866             printf("?Off limits: %s\n",s);
5867           else
5868 #endif /* CKROOT */
5869             if (x == 0 && isdir(s))
5870               printf("?Empty directory - \"%s\"\n", s);
5871             else
5872               printf("?%s %s match - \"%s\"\n",
5873                      (x == 0) ? "No" : "Too many",
5874                      (show == 2) ? "directories" : "files",
5875                      brstrip(s)
5876                      );
5877           rc = -9;
5878           goto xdomydir;
5879     }
5880     nx = x;                             /* Remember how many files */
5881     reallysort = xsort;
5882     if (nx < 2) reallysort = 0;		/* Skip sorting if none or one */
5883     xsort = 1;				/* 2013-12-06 but do everything else */
5884 
5885     if (msg) {
5886         makestr(&dirmsg,tmpbuf);
5887         dirmsglen = strlen(tmpbuf);
5888     }
5889 
5890 #ifdef VMS
5891     cdp = zgtdir();                     /* Get current directory */
5892     debug(F110,"domydir VMS zgtdir",cdp,0);
5893 #endif /* VMS */
5894 
5895     if (xsort && verbose) {             /* If sorting, allocate space */
5896         if (!(dirlist = (char **) malloc((x + 1) * sizeof(char **)))) {
5897             if (!quiet) {
5898                 printf("* Warning: Failure to allocate memory for sorting.\n");
5899                 printf("* Will proceed without sorting...\n");
5900             }
5901             xsort = 0;
5902         }
5903         debug(F101,"domydir sort malloc","",xsort);
5904     }
5905 
5906     /* Display the listing */
5907 
5908 #ifndef NOSPL
5909     if (array)                          /* Storing instead of printing */
5910       heading = 0;
5911 #endif /* NOSPL */
5912 
5913     if (heading) {                      /* If /HEADING print heading */
5914         zfnqfp(s,TMPBUFSIZ,tmpbuf);
5915         fprintf(ofp,"\nDirectory of %s\n\n",tmpbuf);
5916         n += 3;
5917     }
5918     if (page > -1)                      /* Paging */
5919       xaskmore = page;
5920 
5921     if (dir_exc)                        /* Have exception list? */
5922       makelist(dir_exc,xlist,16);	/* Yes, convert to array */
5923 
5924     if (!verbose && !touch && !change) { /* /BRIEF */
5925         if (outfile[0]) {               /* To file  */
5926             int k = 0;
5927             znext(name);
5928             while (name[0]) {           /* One line per file */
5929                 k++;
5930                 if (fs) if (fileselect(name,
5931                                        dir_aft,dir_bef,dir_naf,dir_nbf,
5932                                        minsize,maxsize,!backup,16,xlist) < 1) {
5933                     znext(name);
5934                     continue;
5935                 }
5936                 fprintf(ofp,"%s\n",name);
5937                 znext(name);
5938             }
5939             if (heading)
5940               fprintf(ofp,"Files: %d\n\n",k);
5941             rc = 1;
5942             goto xdomydir;
5943         } else {
5944             rc = xfilhelp(x,"","",n,0,1,
5945 			  dir_aft,dir_bef,dir_naf,dir_nbf,
5946 			  minsize,maxsize,!backup,16,xlist);
5947             if (rc < 0)
5948               goto xdomydir;
5949             if (heading && rc > 0)
5950               fprintf(ofp,"Files: %d\n\n",x); /* (Might scroll a line or 2) */
5951             rc = 1;
5952             goto xdomydir;
5953         }
5954     }
5955     ndirs = nfiles = 0L;		/* Initialize counters */
5956     nbytes = (CK_OFF_T)0;
5957 
5958     if (change) {                       /* CHANGE - check for conflicts */
5959         struct zfnfp * fp;
5960         char dbuf[MAXPATHLEN+1];
5961         char bbuf[MAXPATHLEN+1];
5962 
5963         fp = zfnqfp(name,TMPBUFSIZ,chgsourcedir); /* Source directory path */
5964         if (fp) {
5965             chgsourcedir[fp->fname - fp->fpath] = NUL;
5966             debug(F110,"CHANGE source directory",chgsourcedir,0);
5967             if (chgdestdir[0]) {
5968                 debug(F110,"CHANGE destination directory",chgdestdir,0);
5969                 zfnqfp(chgdestdir,TMPBUFSIZ,dbuf);
5970                 debug(F110,"CHANGE destination directory",dbuf,0);
5971                 if (!strcmp(dbuf,chgsourcedir)) {
5972                     printf(
5973                         "?Destination and source directories are the same\n");
5974                     success = 0;
5975                     goto xdomydir;
5976                 }
5977             }
5978             if (chgbackupdir[0]) {
5979                 debug(F110,"CHANGE backup directory",chgbackupdir,0);
5980                 zfnqfp(chgbackupdir,TMPBUFSIZ,bbuf);
5981                 debug(F110,"CHANGE backup directory",bbuf,0);
5982                 if (!strcmp(bbuf,chgsourcedir)) {
5983                     printf("?Backup and source directories are the same\n");
5984                     success = 0;
5985                     goto xdomydir;
5986                 }
5987             }
5988             if (chgbackupdir[0] && chgdestdir[0]) {
5989                 if (!strcmp(bbuf,dbuf)) {
5990                     printf(
5991                         "?Backup and destination directories are the same\n");
5992                     success = 0;
5993                     goto xdomydir;
5994                 }
5995             }
5996         }
5997     }
5998     diractive = 1;                      /* DIRECTORY command is active */
5999     znext(name);                        /* Get next file */
6000     while (name[0]) {                   /* Loop for each file */
6001         if (fs) if (fileselect(name,
6002                        dir_aft,dir_bef,dir_naf,dir_nbf,
6003                        minsize,maxsize,!backup,16,xlist) < 1) {
6004             znext(name);
6005             continue;
6006         }
6007         len = zgetfs(name);             /* Get file length */
6008         debug(F111,"domydir loop zgetfs",name,len);
6009 #ifdef VMSORUNIX
6010         itsadir = zgfs_dir;             /* See if it's a directory */
6011 #else
6012         itsadir = (len == (CK_OFF_T)-2 || isdir(name));
6013 #endif /* VMSOUNIX */
6014         debug(F111,"domydir itsadir",name,itsadir);
6015         changes = 0;
6016         if ((itsadir && (show == 1)) || (!itsadir && (show == 2))) {
6017             znext(name);
6018             continue;
6019         }
6020         /* Get here when we know we have selected this file */
6021 
6022         nmatches++;
6023         if (itsadir) {                  /* Accumulate totals for summary */
6024             ndirs++;
6025         } else {
6026             nfiles++;
6027             nbytes += len;
6028         }
6029 	dstr = NULL;			/* File date-time string */
6030 
6031 /* BEGIN CHANGE command */
6032 
6033 	if (cx == XXCHG) {		/* Command was CHANGE, not DIRECTORY */
6034             FILE * ifp = NULL;		/* Input file pointer */
6035             FILE * ofp = NULL;		/* Output (temporary) file pointer */
6036             FILE * bfp = NULL;          /* Backup file pointer */
6037             char backupfile[MAXPATHLEN+1]; /* Backup file */
6038 	    char tmpfile[MAXPATHLEN];	/* Buffer for filename */
6039 	    char * tdp = tmpfile;	/* Temporary directory path */
6040 	    int linebufsiz = 24575;	/* Buf size for reading file lines */
6041 	    char * linebuf = NULL;	/* Input file buffer */
6042 	    char * lbp = NULL;		/* and pointer to it */
6043 	    char * newbuf = NULL;	/* Output file buffer */
6044 	    char * nbp = NULL;		/* and pointer */
6045 	    int bufleft = 0;		/* Space left in newbuf */
6046 	    int i, j, k, x, y;		/* Workers */
6047 	    int failed = 0;		/* Search string not found */
6048 	    char c1, c2;		/* Char for quick compare */
6049 
6050             changes = 0;                /* Change counter */
6051             k = 0;
6052             x = scanfile(name,NULL,nscanfile)	    ;
6053             debug(F111,"domydir CHANGE scanfile",name,x);
6054 	    switch (x) {                /* Is it a text file? */
6055 	      case FT_7BIT: k++; break;
6056 	      case FT_UTF8: k++; break;
6057 	      case FT_UCS2: k++; break;
6058 	      case FT_8BIT: k++; break;
6059 	      case FT_TEXT: k++; break;
6060 	    }
6061 	    if (!k) {
6062 		if (verbose)
6063 		  printf("%s: Skipped (not a text file)\n", name);
6064 		znext(name);
6065 		continue;
6066 	    }
6067             debug(F101,"CHANGE changeinplace","",changeinplace);
6068 
6069             if (changeinplace) {        /* CHANGing in place? */
6070                 int x = 0;
6071                 if (!tempdir) {         /* Need a temporary directory */
6072                     x++;
6073                 } else if (!*tempdir) {
6074                     x++;
6075                 }
6076 /*
6077   It might make more sense to fall back on the current directory, or the
6078   directory specified in the filespec, because that one has to be writeable or
6079   the files could not be changed.
6080 */
6081                 if (x) {
6082                     printf(
6083     "?Temporary directory not defined, use SET TEMP-DIRECTORY to define one.\n"
6084                     );
6085                     success = 0;
6086                     goto xdomydir;
6087                 }
6088                 ckstrncpy(tmpfile,tempdir,MAXPATHLEN); /* Temp directory */
6089                 ckstrncat(tmpfile,"__x",MAXPATHLEN); /* Temp filespec */
6090                 if (simulate) {
6091                     /* Too much */
6092                     /* printf("Would create temp file %s\n",tmpfile); */
6093                 } else {
6094                     ofp = fopen(tmpfile,"w"); /* Open temporary file */
6095                     debug(F110,"CHANGE in place tmpfile",tmpfile,0);
6096                     if (!ofp) {
6097                         printf("?Can't open temporary file %s: %s\n",
6098                                tmpfile,ck_errstr());
6099                         success = 0;
6100                         goto xdomydir;
6101                     }
6102                 }
6103             } else {                    /* Making a new copy of the file */
6104                 char * p = name, * p2 = NULL;
6105                 debug(F110,"CHANGE chgdestdir",chgdestdir,0);
6106                 ckstrncpy(tmpfile,chgdestdir,MAXPATHLEN);
6107                 debug(F110,"CHANGE tmpfile",tmpfile,0);
6108                 while (*p++) { if (ISDIRSEP(*p)) p2 = p; } /* Just the name */
6109                 if (!p2) {              /* name had no slashes in it */
6110                     p2 = name;
6111                     ckstrncat(tmpfile,STRDIRSEP,MAXPATHLEN);
6112                 }
6113                 debug(F110,"CHANGE name",p2,0);
6114                 ckstrncat(tmpfile,p2,MAXPATHLEN);
6115                 debug(F110,"CHANGE final tmpfile",tmpfile,0);
6116                 if (simulate) {
6117                     printf("Would create new file %s\n",tmpfile);
6118                 } else {
6119                     debug(F110,"CHANGE /dest tmpfile",tmpfile,0);
6120                     ofp = fopen(tmpfile,"w"); /* Open temporary file */
6121                     if (!ofp) {
6122                         printf("?Can't open destination file %s: %s\n",
6123                                tmpfile,ck_errstr());
6124                         success = 0;
6125                         goto xdomydir;
6126                     }
6127                 }
6128             }
6129             if (changebackup) {         /* Backing up original file? */
6130                 char * p = name, * p2 = NULL;
6131                 ckstrncpy(backupfile,chgbackupdir,MAXPATHLEN);
6132                 debug(F111,"CHANGE backupfile",backupfile,1);
6133                 while (*p++) { if (ISDIRSEP(*p)) p2 = p; } /* Just the name */
6134                 if (!p2) {              /* name had no slashes in it */
6135                     p2 = name;
6136                     ckstrncat(backupfile,STRDIRSEP,MAXPATHLEN);
6137                 }
6138                 debug(F111,"CHANGE backupfile",backupfile,2);
6139                 ckstrncat(backupfile,p2,MAXPATHLEN);
6140                 debug(F111,"CHANGE backupfile",backupfile,3);
6141                 if (simulate) {
6142                     printf("Would back up original file to %s\n",
6143                            backupfile);
6144                 } else {
6145                     bfp = fopen(backupfile,"w"); /* Open temporary file */
6146                     if (!bfp) {
6147                         printf("?Can't open backup file %s: %s\n",
6148                                backupfile,ck_errstr());
6149                         success = 0;
6150                         goto xdomydir;
6151                     }
6152                 }
6153             }
6154             if ((ifp = fopen(name,"r")) == NULL) { /* Open input file */
6155                 printf("?Can't open file %s: %s\n",s,ck_errstr());
6156                 fclose(ofp);
6157                 success = 0;
6158                 goto xdomydir;
6159             }
6160             /* Get timestamp of original file */
6161             debug(F101,"CHANGE timestamp changebackup","",changebackup);
6162             if (chmtopt == CHMT_P || changebackup) {
6163                 debug(F110,"CHANGE file timestamp name",name,0);
6164                 dstr = zfcdat(name);
6165                 if (!dstr) dstr = "";
6166                 if (!*dstr) printf("WARNING: can't get date for %s\n",name);
6167                 debug(F110,"CHANGE file timestamp dstr",dstr,0);
6168                 xxstruct.date.val = dstr; /* change file's modtime */
6169                 xxstruct.date.len = (int)strlen(xxstruct.date.val);
6170                 xxstruct.lprotect.len = 0;
6171                 xxstruct.gprotect.len = 0;
6172             }
6173             linebuf = (char *) malloc(linebufsiz+1); /* Malloc a line buffer */
6174             if (!linebuf) {
6175                 printf("?Memory allocation failure\n");
6176                 fclose(ofp);
6177                 fclose(ifp);
6178                 if (bfp) fclose(bfp);
6179                 success = 0;
6180                 goto xdomydir;
6181             }
6182             newbuf = (char *) malloc(linebufsiz+1); /* Buffer for copy */
6183             if (!newbuf) {
6184                 free(linebuf);
6185                 printf("?Memory allocation failure\n");
6186                 fclose(ofp);
6187                 fclose(ifp);
6188                 if (bfp) fclose(bfp);
6189                 success = 0;
6190                 goto xdomydir;
6191             }
6192             /* Loop through lines of each original file... */
6193 
6194             while (fgets(linebuf, linebufsiz, ifp)) { /* Read a line */
6195                 if (changebackup && !simulate) {
6196                     if (fputs(linebuf, bfp) == EOF) { /* Backing up */
6197                         printf("?%s: Write failed - %s\n",
6198                                backupfile,ck_errstr());
6199                         failed++;
6200                         break;
6201                     }
6202                 }
6203                 nbp = newbuf;
6204                 lbp = linebuf;
6205                 bufleft = linebufsiz;	/* Space left in newbuf */
6206                 x = ckindex(string1,lbp,0,0,chcase);
6207                 if (x == 0) {		/* Nothing to replace */
6208                     if (!simulate) {
6209                         if (fputs(lbp, ofp) == EOF) {
6210                             printf("?%s: Write failed - %s\n",
6211                                    tmpfile,ck_errstr());
6212                             failed++;
6213                             break;
6214                         }
6215                     }
6216                 } else while (1) {      /* One or maybe more occurrences */
6217                     changes++;		/* Count this change */
6218                     totalchanges++;     /* Increment total changes */
6219                     j = x + s2len - 1;	/* Size of addition to newbuf */
6220                     bufleft -= j;       /* Remaining space in newbuf after */
6221                     if (bufleft > j) {  /* If space enough */
6222                         char c;
6223                         c = lbp[x];
6224                         lbp[x] = NUL;   /* Terminate for strncpy */
6225                         strncpy(nbp,lbp,bufleft); /* Copy this piece */
6226                         lbp[x] = c;
6227                         nbp += (x - 1);	/* adjust destination pointer */
6228                         strncpy(nbp,string2,bufleft); /* replacement string */
6229                         nbp += s2len;	/* and adjust destination pointer */
6230                     } else {		/* Otherwise fail. */
6231                         failed++;
6232                         printf("?%s: Write failed - %s\n",tmpfile,ck_errstr());
6233                         break;
6234                     }
6235                     lbp += x + s1len - 1; /* Adjust source pointer */
6236                     x = ckindex(string1,lbp,0,0,chcase); /* Get next */
6237                     if (!x) {	    /* No more string1's found in this line */
6238                         if (!simulate) { /* Write changes */
6239                             if (fputs(newbuf, ofp) == EOF) {
6240                                 printf("?%s: Write failed - %s\n",
6241                                        tmpfile,ck_errstr());
6242                                 failed++;
6243                                 break;
6244                             }
6245                             if (*lbp) {   /* And write out last chunk if any */
6246                                 if (fputs(lbp, ofp) == EOF) {
6247                                     printf("?%s: Write failed - %s\n",
6248                                            tmpfile,ck_errstr());
6249                                     failed++;
6250                                     break;
6251                                 }
6252                             }
6253                         }
6254                         break;
6255                     }
6256                 }
6257             }
6258             fclose(ifp);                /* End... close files */
6259             if (!simulate) {
6260                 if (bfp) fclose(bfp);
6261                 fclose(ofp);
6262             }
6263             bfp = ifp = ofp = NULL;
6264             free(linebuf);              /* and free buffers */
6265             free(newbuf);
6266             if (simulate) {             /* Simulation run */
6267                 if (failed) {
6268                     printf("Would fail: %s\n",name);
6269                 } else if (changes) {
6270                     printf("Would change: %s\n",name);
6271                 } else if (verbose) {
6272                     printf("Would not change: %s\n",name);
6273                 }
6274                 zdelet(tmpfile);
6275             } else if (!failed) {       /* Really changing */
6276                 char * result = name;
6277                 if (changes) {		/* If changes were made */
6278                     if (changeinplace) { /* Changing in place... */
6279                         x = zrename(tmpfile,name); /* Replace original file */
6280                         if (x < 0) {
6281                             printf("?Rename temporary file %s to %s failed",
6282                                    tmpfile, name);
6283                             zdelet(tmpfile); /* delete temporary file */
6284                             success = 0;
6285                             goto xdomydir;
6286                         }
6287                     } else {            /* Making new file... */
6288                         result = tmpfile;
6289                     }
6290                     if (chmtopt == CHMT_P) { /* If preserving file dates */
6291                         debug(F110,"Setting modtime",result,0);
6292                         if (zstime(result,&xxstruct,0) < 0) {
6293                             printf("?Error \
6294 preserving original modtime: %s %s\n",
6295                                    result,
6296                                    ck_errstr()
6297                                    );
6298                             rc = -9;
6299                             goto xdomydir;
6300                         }
6301                     }
6302                     /* Change modtime of backup file unconditionally */
6303                     debug(F111,"CHANGE modtime",backupfile,changebackup);
6304                     if (changebackup) {
6305                         if (zstime(backupfile,&xxstruct,0) < 0) {
6306                             printf("?Modtime error on backup file: %s\n",
6307                                    backupfile,
6308                                    ck_errstr()
6309                                    );
6310                             rc = -9;
6311                             goto xdomydir;
6312                         }
6313                     }
6314                     if (verbose)
6315                       printf("Changed %s: %s -> %s\n",result,string1,string2);
6316                 } else if (changeinplace) {
6317                     zdelet(tmpfile);	/* Delete temporary file */
6318                     if (changebackup) zdelet(backupfile); /* and backup */
6319                 }
6320             }
6321             if (znext(name))		/* Get next file */
6322               continue;
6323             success = 1;                /* If none we're finished */
6324             goto xdomydir;
6325         }
6326 
6327 /* TOUCH command... */
6328 
6329 	if (cx == XXTOUC) {		/* Command was TOUCH, not DIRECTORY */
6330 	    /* Given date-time, if any, else current date-time */
6331 	    debug(F110,"TOUCH dstr before",dstr,0);
6332 	    dstr = ckcvtdate(modtime[0] ? modtime : "",0);
6333 	    debug(F110,"TOUCH dstr after",dstr,0);
6334 	    xxstruct.date.val = dstr;
6335 	    xxstruct.date.len = (int)strlen(xxstruct.date.val);
6336 	    xxstruct.lprotect.len = 0;
6337 	    xxstruct.gprotect.len = 0;
6338 	    if (simulate) {
6339 		printf(" %s (%s)\n",name,dstr);
6340 	    } else {
6341 		if (zstime(name,&xxstruct,0) < 0) {
6342 		    printf("?TOUCH %s: %s\n",name,ck_errstr());
6343 		    rc = -9;
6344 		    goto xdomydir;
6345 		}
6346 	    }
6347 	    if (!verbose || simulate) {	/* No listing so just go back */
6348 		znext(name);		/* and do next file. */
6349 		continue;
6350 	    }
6351 	}
6352         if (summary) {			/* Summary only, no detail */
6353             znext(name);
6354             continue;
6355         }
6356 /*
6357   NOTE: The sprintf's in this routine should be safe.  They involve
6358   permission strings, date/time strings, and filenames, all of which have
6359   known maximum lengths; none of these items is input from users.  The
6360   destination buffers are large enough to hold maximum sizes for any and
6361   all items.  NOTE 2: If command was TOUCH, dstr was already set just
6362   above.
6363 */
6364 	if (!dstr) {			/* Get file's modification date/time */
6365 	    dstr = zfcdat(name);
6366 	    debug(F111,"domydir zcfdat",dstr,0);
6367 	}
6368         if (!dstr) dstr = "";
6369         {
6370 /*
6371   Note that zfcdat() always returns "" or yyyymmdd hh:mm:ss, so any warnings
6372   about possible out-of-bounds dstr[] array refs do not apply.  This block of
6373   code is to stifle the warnings and also allows for any out-of-spec
6374   zfcdat() implementations.
6375 */
6376             int x;
6377             char * p = "00000000 00:00:00";
6378             x = ckstrncpy(xbuf,dstr,32);
6379             if (x < 17) ckstrncpy(&xbuf[x],p+x,32-x);
6380             dstr = xbuf;
6381         }
6382         if (engdate) {                  /* English date requested? */
6383             short month, day, year, hour, minute, seconds;
6384             month = (dstr[4]-48)*10 + (dstr[5]-48);
6385             mstr  = (month > 0 && month <= 12) ? months[month-1] : "xxx";
6386             day   = (dstr[6]-48)*10 + (dstr[7]-48);
6387             year  = (((dstr[0]-48)*10 +
6388                       (dstr[1]-48))*10 +
6389                       (dstr[2]-48))*10 +
6390                       (dstr[3]-48);
6391             hour  = (dstr[9]-48)*10 + (dstr[10]-48);
6392             minute = (dstr[12]-48)*10 + (dstr[13]-48);
6393             seconds = (dstr[15]-48)*10 + (dstr[16]-48);
6394             sprintf(dbuf,               /* SAFE */
6395                     "%2d-%s-%4d %02d:%02d:%02d",
6396                     day,mstr,year,hour,minute,seconds
6397                     );
6398             dstr = dbuf;
6399         } else {                        /* ISO date */
6400             dbuf[0] = dstr[0];          /* yyyy */
6401             dbuf[1] = dstr[1];
6402             dbuf[2] = dstr[2];
6403             dbuf[3] = dstr[3];
6404             dbuf[4] = '-';
6405             dbuf[5] = dstr[4];          /* mm (numeric) */
6406             dbuf[6] = dstr[5];
6407             dbuf[7] = '-';
6408             dbuf[8] = dstr[6];          /* dd */
6409             dbuf[9] = dstr[7];
6410             strcpy(dbuf+10,dstr+8);     /* hh:mm:ss */
6411             dstr = dbuf;
6412         }
6413         dlen = strlen(dbuf);            /* Length of date */
6414         name[CKMAXPATH] = NUL;
6415 #ifdef CK_PERMS
6416 #ifdef VMSORUNIX
6417         p = ziperm(name);               /* Get permissions */
6418         debug(F110,"ziperm perms",p,0);
6419 #else
6420         p = zgperm(name);
6421         debug(F110,"zgperm perms",p,0);
6422 #endif /* VMSORUNIX */
6423 #else
6424         p = NULL;
6425         debug(F110,"NULL perms",p,0);
6426 #endif /* CK_PERMS */
6427 
6428 #ifdef VMS
6429         /* Get relative name to save space -- VMS fullnames are long... */
6430         ckstrncpy(name,zrelname(name,cdp),CKMAXPATH);
6431 #endif /* VMS */
6432 
6433         if (itsadir && len < (CK_OFF_T)0) { /* Directory */
6434 #ifdef VMS
6435             sprintf(linebuf,"%-22s%-10s  %s  %s",p,"<DIR>",dstr,name);
6436 #else
6437             if (p)
6438               sprintf(linebuf,"%10s%-10s  %s  %s",p,"<DIR>",dstr,name);
6439             else
6440               sprintf(linebuf,"%-10s  %s  %s", "<DIR>", dstr, name);
6441 #endif /* VMS */
6442         } else {                        /* Regular file */
6443 #ifdef VMS
6444             sprintf(linebuf,"%-22s%10s  %s  %s", p, ckfstoa(len), dstr, name);
6445 #else
6446             if (p)
6447               sprintf(linebuf,"%10s%10s  %s  %s", p, ckfstoa(len), dstr, name);
6448             else
6449               sprintf(linebuf,"%10s  %s  %s", ckfstoa(len), dstr, name);
6450 #endif /* VMS */
6451         }
6452 #ifdef UNIX
6453 #ifdef CKSYMLINK
6454         if (zgfs_link) {		/* If it's a symlink */
6455 	    if (dontshowlinks) {	/* If /NOLINKS don't show it */
6456 		znext(name);
6457 		continue;
6458 	    }
6459 	}
6460         if (zgfs_link && !dontfollowlinks) { /* Symlink and following links */
6461             int n, m;			/* Show what the link points to */
6462             extern char linkname[];
6463             n = strlen(linebuf);
6464             m = strlen(linkname) + n;
6465             if (m < CKMAXPATH + 58)
6466               strcpy(linebuf+n, " -> "); /* safe (checked) */
6467             if (m + 4 < CKMAXPATH - 58)
6468               strcpy(linebuf+n+4, linkname); /* safe (checked) */
6469         } else
6470 #endif /* CKSYMLINK */
6471 #endif /* UNIX */
6472         if (xfermod) {                  /* Show transfer mode */
6473             int i, x, y;
6474             char * s = "";
6475             y = -1;
6476             x = scanfile(name,&y,nscanfile);
6477             switch (x) {
6478               case FT_TEXT: s = " (T)"; break;
6479               case FT_7BIT: s = " (T)(7BIT)"; break;
6480               case FT_8BIT: s = " (T)(8BIT)"; break;
6481 #ifdef UNICODE
6482               case FT_UTF8: s = " (T)(UTF8)"; break;
6483               case FT_UCS2:
6484                 s = y ? " (T)(UCS2LE)" : " (T)(UCS2BE)";
6485                 break;
6486 #endif /* UNICODE */
6487               case FT_BIN:  s = " (B)"; break;
6488             }
6489             if (!*s) {
6490                 s = binary ? " (B)" : " (T)";
6491             }
6492             if (*s) {
6493                 int n;
6494                 n = strlen(linebuf);
6495                 if (n + 4 < CKMAXPATH - 58)
6496                   strcpy(linebuf+n, s); /* safe (checked) */
6497             }
6498         }
6499         if (msg && dirmsg) {
6500             int n;
6501             n = strlen(linebuf);
6502             if (n + dirmsglen + 2 < CKMAXPATH)
6503               sprintf((char *)(linebuf+n)," %s", dirmsg); /* SAFE */
6504         }
6505         if (xsort) {		/* Sorting - save line */
6506             i = strlen(linebuf);
6507             if ((ndirlist >= nx) ||
6508                 !(dirlist[ndirlist] = (char *)malloc(i+1))) {
6509                 printf("?Memory allocation error - try /NOSORT\n");
6510                 rc = -9;
6511                 goto xdomydir;
6512             }
6513             strcpy(dirlist[ndirlist],linebuf); /* safe */
6514             ndirlist++;
6515         }
6516         znext(name);                    /* Peek ahead to next file */
6517         if (!xsort) {
6518 	    if (!touch || (touch && verbose))
6519 	      fprintf(ofp,"%s\n",linebuf);
6520             if (page && (name[0] || heading)) { /* If /PAGE */
6521                 if (cmd_cols > 0) {
6522                     int x = strlen(linebuf);
6523                     int y;
6524                     y = (x % cmd_cols) ? 1 : 0;
6525                     n += x / cmd_cols + y;
6526                 } else {
6527                     n++;
6528                 }
6529 #ifdef CK_TTGWSIZ
6530                 if (n > (cmd_rows - 3)) { /* Do more-prompting */
6531                     if (!askmore()) {
6532                         rc = 0;
6533                         goto xdomydir;
6534                     } else
6535                       n = 0;
6536                 }
6537 #endif /* CK_TTGWSIZ */
6538             }
6539         }
6540     }
6541     if (xsort) {
6542 	int namepos;
6543         skey = 0;
6544 #ifdef VMS
6545 	namepos = dlen + 35;
6546         switch (sortby) {
6547           case DIRS_NM: skey = namepos; break;
6548           case DIRS_DT: skey = 33; break;
6549           case DIRS_SZ: skey = 21;
6550         }
6551 #else
6552         if (p) {
6553 	    namepos = dlen + 24;
6554             switch (sortby) {
6555               case DIRS_NM: skey = namepos; break;
6556               case DIRS_DT: skey = 22; break;
6557               case DIRS_SZ: skey = 10;
6558             }
6559         } else {
6560 	    namepos = dlen + 14;
6561             switch (sortby) {
6562               case DIRS_NM: skey = namepos; break;
6563               case DIRS_DT: skey = 12; break;
6564               case DIRS_SZ: skey = 0;
6565             }
6566         }
6567 #endif /* VMS */
6568         if (reallysort) sh_sort(dirlist,NULL,ndirlist,skey,reverse,filecase);
6569 	if (dir_top > 0 && dir_top < ndirlist)
6570 	  ndirlist = dir_top;
6571         for (i = 0; i < ndirlist; i++) {
6572 #ifndef NOSPL
6573 	    /* Storing result filenames in an array... */
6574 	    if (array) {
6575 		char * name;
6576 		name = dirlist[i] + namepos;
6577 		debug(F111,"domydir array",name,nfiles);
6578 		if (ap)
6579 		  makestr(&(ap[i+1]),name);
6580 		continue;
6581 	    }
6582 #endif /* NOSPL */
6583 	    /* Printing the result filenames, size, date, etc... */
6584             fprintf(ofp,"%s\n",dirlist[i]);
6585             if (page && (i < ndirlist -1 || heading)) { /* If /PAGE */
6586                 if (cmd_cols > 0) {
6587                     int x = strlen(dirlist[i]);
6588                     int y;
6589                     y = (x % cmd_cols) ? 1 : 0;
6590                     n += ((int)strlen(dirlist[i]) / cmd_cols) + y;
6591                 } else {
6592                     n++;
6593                 }
6594 #ifdef CK_TTGWSIZ
6595                 if (n > (cmd_rows - 3)) { /* Do more-prompting */
6596                     if (!askmore()) {
6597                         rc = 0;
6598                         goto xdomydir;
6599                     } else
6600                       n = 0;
6601                 }
6602 #endif /* CK_TTGWSIZ */
6603             }
6604         }
6605 #ifndef NOSPL
6606 	if (array) {
6607 	    if (ap)
6608 	      makestr(&(ap[0]),ckitoa(ndirlist));
6609 	    rc = 1;
6610 	    goto xdomydir;
6611 	}
6612 #endif /* NOSPL */
6613     }
6614     if (heading || summary) {
6615 #ifdef CKFLOAT
6616         CKFLOAT gm;
6617 #endif /* CKFLOAT */
6618         fprintf(ofp,"\n%ld director%s, %ld file%s, %s byte%s",
6619                ndirs,
6620                (ndirs == 1) ? "y" : "ies",
6621                nfiles,
6622                (nfiles == 1) ? "" : "s",
6623 	       ckfstoa(nbytes),
6624                (nbytes == 1) ? "" : "s"
6625                );
6626 #ifdef CKFLOAT
6627         gm = ((CKFLOAT) nbytes ) / 1000000.0;
6628         if (gm > 1000.0)
6629           fprintf(ofp," (%0.2fGB)",(gm / 1000.0));
6630         else if (gm >= 0.01)
6631           fprintf(ofp," (%0.2fMB)",gm);
6632 #endif /* CKFLOAD */
6633         fprintf(ofp,"\n\n");
6634     } else if (dir_cou && !cv) {
6635 	fprintf(ofp,"\n Files: %ld\n\n",nfiles);
6636     }
6637   xdomydir:
6638 #ifndef NOSPL
6639     if (dir_cou && cv) {                /* /COUNT:var */
6640         int n;
6641         n = totalchanges;               /* Number of changes for CHANGE */
6642         if (cx != XXCHG)                /* Number for files for DIRECTORY */
6643           n = nfiles;
6644         addmac(cv,ckitoa(n));           /* set the variable */
6645         makestr(&cv,NULL);              /* free this */
6646     }
6647     if (ap) {				/* If we have a result array */
6648 	if (a_dim[arrayindex] > nmatches) /* but it was not filled */
6649 	  a_dim[arrayindex] = nmatches;   /* adjust dimension */
6650     }
6651 #endif	/* NOSPL */
6652     if (g_matchdot > -1) {
6653         matchdot = g_matchdot;          /* Restore these... */
6654         g_matchdot = -1;
6655     }
6656     freedirlist();
6657     if (ofp != stdout) {                /* Close any output file */
6658         if (ofp) fclose(ofp);
6659         ofp = stdout;
6660     }
6661     if (rc > 0)
6662       success = 1;
6663     return(rc);
6664 }
6665 
6666 int
dodir(cx)6667 dodir(cx) int cx; {                     /* Do the DIRECTORY command */
6668     char *dc , *msg;
6669 
6670 #ifdef OS2
6671     return(domydir(cx));
6672 #else /* OS2 */
6673     if (nopush
6674 #ifdef DOMYDIR                          /* Builds that domydir() by default */
6675         || (cx == XXDIR  || cx == XXLDIR || cx == XXWDIR ||
6676 	    cx == XXHDIR || cx == XXTOUC || cx == XXCHG )
6677 #endif /* DOMYDIR */
6678         )
6679       return(domydir(cx));		/* Built-in directory command */
6680 
6681     /* Use the system's directory command. */
6682 
6683     msg = (cx == XXLS) ?
6684       "Arguments for ls" :
6685         "Directory and/or file specification";
6686     if ((x = cmtxt(msg,"",&s,xxstring)) < 0)
6687       return(x);
6688 
6689     ckstrncpy(tmpbuf,s,TMPBUFSIZ);      /* Copy the filespec */
6690     s = tmpbuf;
6691 
6692     if ((y = cmcfm()) < 0) return(y);
6693 
6694     lp = line;
6695     if (!(dc = getenv("CK_DIR")))
6696       dc = DIRCMD;
6697     ckmakmsg(lp,LINBUFSIZ,dc," ",s,NULL);
6698     debug(F110,"DIR",line,0);
6699 #ifdef VMS
6700     conres();
6701 #endif /* VMS */
6702     x = zshcmd(line);
6703 #ifdef VMS
6704     concb((char)escape);
6705 #endif /* VMS */
6706     return(success = (x < 1) ? 0 : 1);
6707 #endif /* OS2 */
6708 }
6709 
6710 #ifndef NOSERVER
6711 #ifndef NOFRILLS
6712 /* Do the ENABLE and DISABLE commands */
6713 
6714 int
doenable(y,x)6715 doenable(y,x) int y, x; {
6716 #ifdef CK_LOGIN
6717     if (isguest)                        /* IKSD: Don't let guests */
6718       return(0);                        /* enable anything that's disabled */
6719 #endif /* CK_LOGIN */
6720     switch (x) {
6721       case EN_ALL:
6722         en_cwd = en_cpy = en_del = en_dir = en_fin = en_get = y;
6723         en_ren = en_sen = en_set = en_spa = en_typ = en_ret = y;
6724         if (!inserver)
6725           en_who = en_mai = en_pri = y;
6726         en_mkd = en_rmd = y;
6727         en_xit = y;
6728 #ifndef datageneral
6729         en_bye = y;
6730 #endif /* datageneral */
6731 #ifndef NOPUSH
6732         if (!nopush && !inserver)
6733           en_hos = y;
6734 #endif /* NOPUSH */
6735 #ifndef NOSPL
6736         en_asg = en_que = y;
6737 #endif /* NOSPL */
6738         break;
6739 
6740       case EN_BYE:
6741 #ifndef datageneral
6742 /*
6743   In Data General AOS/VS Kermit can't log out its superior process.
6744 */
6745         en_bye = y;
6746 #endif /* datageneral */
6747         break;
6748       case EN_CPY:
6749         en_cpy = y;
6750         break;
6751       case EN_CWD:
6752         en_cwd = y;
6753 #ifdef IKSD
6754         if (inserver && y == 0) {
6755             fnrpath = PATH_OFF;
6756             fnspath = PATH_OFF;
6757         }
6758 #endif /* IKSD */
6759         break;
6760       case EN_DEL:                      /* Deleting of files */
6761         en_del = y;
6762         break;
6763       case EN_DIR:
6764         en_dir = y;
6765         break;
6766       case EN_FIN:
6767         en_fin = y;
6768         break;
6769       case EN_GET:
6770         en_get = y;
6771         break;
6772 #ifndef NOPUSH
6773       case EN_HOS:
6774         if (!nopush)
6775          en_hos = y;
6776         break;
6777 #endif /* NOPUSH */
6778       case EN_REN:
6779         en_ren = y;
6780         break;
6781       case EN_SEN:
6782         en_sen = y;
6783         break;
6784       case EN_SET:
6785         en_set = y;
6786         break;
6787       case EN_SPA:
6788         en_spa = y;
6789         break;
6790       case EN_TYP:
6791         en_typ = y;
6792         break;
6793       case EN_WHO:
6794         en_who = y;
6795         break;
6796 #ifndef NOSPL
6797       case EN_ASG:
6798         en_asg = y;
6799         break;
6800       case EN_QUE:
6801         en_que = y;
6802         break;
6803 #endif /* NOSPL */
6804       case EN_RET:
6805         en_del = y;
6806         break;
6807       case EN_MAI:
6808 #ifdef CK_LOGIN
6809         if (isguest && y) {
6810             printf("?Sorry, not valid for guests\n");
6811             return(-9);
6812         }
6813 #endif /* CK_LOGIN */
6814         en_mai = y;
6815         break;
6816       case EN_PRI:
6817 #ifdef CK_LOGIN
6818         if (isguest && y) {
6819             printf("?Sorry, not valid for guests\n");
6820             return(-9);
6821         }
6822 #endif /* CK_LOGIN */
6823         en_pri = y;
6824         break;
6825       case EN_MKD:
6826         en_mkd = y;
6827         break;
6828       case EN_RMD:
6829         en_rmd = y;
6830         break;
6831       case EN_XIT:
6832         en_xit = y;
6833         break;
6834       case EN_ENA:
6835         if (((y & 1) && !(en_ena & 1)) ||
6836             ((y & 2) && !(en_ena & 2))) {
6837             printf("?Sorry, DISABLE ENABLE can not be undone\n");
6838             return(-9);
6839         } else {
6840             en_ena = y;
6841             break;
6842         }
6843       default:
6844         return(-2);
6845     }
6846     return(1);
6847 }
6848 #endif /* NOFRILLS */
6849 #endif /* NOSERVER */
6850 
6851 #ifndef NOFRILLS
6852 
6853 static int del_lis = 0;
6854 static int del_dot = 0;
6855 static int del_hdg = 0;
6856 static int del_pag = -1;
6857 static int del_ask = 0;
6858 
6859 #ifndef NOSHOW
6860 VOID
showdelopts()6861 showdelopts() {
6862     int x = 0;
6863     extern int optlines;
6864     prtopt(&optlines,"");
6865     prtopt(&optlines,"DELETE");
6866     if (del_ask > -1) {
6867         prtopt(&optlines, del_ask ? "/ASK" : "/NOASK");
6868         x++;
6869     }
6870 #ifdef UNIXOROSK
6871     if (del_dot > -1) {
6872         prtopt(&optlines, del_dot ? "/DOTFILES" : "/NODOTFILES");
6873         x++;
6874     }
6875 #endif /* UNIXOROSK */
6876     if (del_lis > -1) {
6877         prtopt(&optlines, del_lis ? "/LIST" : "/NOLIST");
6878         x++;
6879     }
6880     if (del_hdg > -1) {
6881         prtopt(&optlines, del_hdg ? "/HEADING" : "/NOHEADING");
6882         x++;
6883     }
6884 #ifndef CK_TTGWSIZ
6885     if (del_pag > -1) {
6886         prtopt(&optlines, del_pag ? "/PAGE" : "/NOPAGE");
6887         x++;
6888     }
6889 #endif /* CK_TTGWSIZ */
6890     if (!x) prtopt(&optlines,"(no options set)");
6891     prtopt(&optlines,"");
6892 }
6893 #endif /* NOSHOW */
6894 
6895 
6896 int
setdelopts()6897 setdelopts() {
6898     int x_lis = -1, x_pag = -1, x_dot = -1, x_hdg = -1, x_ask = -1;
6899     int getval = 0;
6900     char c;
6901     while (1) {
6902         if ((y = cmswi(deltab,ndeltab,"Switch","",xxstring)) < 0) {
6903             if (y == -3)
6904               break;
6905             else
6906               return(y);
6907         }
6908         c = cmgbrk();
6909         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
6910             printf("?This switch does not take an argument\n");
6911             return(-9);
6912         }
6913         if (!getval && (cmgkwflgs() & CM_ARG)) {
6914             printf("?This switch requires an argument\n");
6915             return(-9);
6916         }
6917         switch (y) {
6918           case DEL_DOT:
6919             x_dot = 1;
6920             break;
6921           case DEL_NOD:
6922             x_dot = 0;
6923             break;
6924           case DEL_HDG:
6925             x_hdg = 1;
6926             break;
6927           case DEL_LIS:
6928             x_lis = 1;
6929             break;
6930           case DEL_NOL:
6931             x_lis = 0;
6932             break;
6933 #ifndef CK_TTGWSIZ
6934           case DEL_PAG:
6935             x_pag = 1;
6936             break;
6937           case DEL_NOP:
6938             x_pag = 0;
6939             break;
6940 #endif /* CK_TTGWSIZ */
6941           case DEL_QUI:
6942             x_lis = 0;
6943             break;
6944           case DEL_VRB:
6945             x_lis = 1;
6946             break;
6947           case DEL_ASK:
6948             x_ask = 1;
6949             break;
6950           case DEL_NAS:
6951             x_ask = 0;
6952             break;
6953           default:
6954             printf("?Sorry, this option can not be set\n");
6955             return(-9);
6956         }
6957     }
6958     if ((x = cmcfm()) < 0)              /* Get confirmation */
6959       return(x);
6960     if (x_pag > -1) del_pag = x_pag;
6961     if (x_dot > -1) del_dot = x_dot;
6962     if (x_hdg > -1) del_hdg = x_hdg;
6963     if (x_lis > -1) del_lis = x_lis;
6964     if (x_ask > -1) del_ask = x_ask;
6965     return(success = 1);
6966 }
6967 
6968 #ifdef OS2
6969 static char ** xmtchs = NULL;
6970 static int xmtchn = 0;
6971 #endif /* OS2 */
6972 
6973 int
dodel()6974 dodel() {                               /* DELETE */
6975     int i, j, k, x;
6976     int fs = 0;                         /* Need to call fileselect() */
6977     int len = 0;
6978     int bad = 0;
6979     int getval = 0, asking = 0;
6980     int simulate = 0, rc = 0;
6981     CK_OFF_T minsize = -1L, maxsize = -1L;
6982     int havename = 0, confirmed = 0;
6983     int qflag = 0;
6984     int summary = 0;
6985     int deldirs = 0;
6986     int deltree = 0;
6987     int itsadir = 0;
6988     int argisdir = 0;
6989     int xmode = -1, scan = 0, skip = 0;
6990 #ifdef COMMENT
6991     int pass = 0;
6992 #endif /* COMMENT */
6993     char c;
6994     char * deldef = "";
6995     char safebuf[CKMAXPATH+1];
6996     struct FDB sw, fi, fl;
6997     char
6998       * del_aft = NULL,
6999       * del_bef = NULL,
7000       * del_naf = NULL,
7001       * del_nbf = NULL,
7002       * del_exc = NULL;
7003     int
7004       x_lis = 0,
7005       /* x_dot = -1, */
7006       x_hdg = 0;
7007 
7008     char * dxlist[8];
7009 
7010     for (i = 0; i < 8; i++) dxlist[i] = NULL;
7011 
7012     g_matchdot = matchdot;
7013 
7014     if (del_lis > -1) x_lis    = del_lis;
7015     if (del_dot > -1) matchdot = del_dot;
7016     if (del_hdg > -1) x_hdg    = del_hdg;
7017     if (del_pag > -1) xaskmore = del_pag;
7018     if (del_ask > -1) asking   = del_ask;
7019 
7020     diractive = 1;
7021     nolinks = 2;                        /* By default don't follow links */
7022 
7023     cmfdbi(&sw,                         /* First FDB - command switches */
7024            _CMKEY,                      /* fcode */
7025            "File specification;\n or switch",
7026            "",                          /* default */
7027            "",                          /* addtl string data */
7028            ndeltab,                     /* addtl numeric data 1: tbl size */
7029            4,                           /* addtl numeric data 2: 4 = cmswi */
7030            xxstring,                    /* Processing function */
7031            deltab,                      /* Keyword table */
7032            &fi                          /* Pointer to next FDB */
7033            );
7034     cmfdbi(&fl,                         /* Anything that doesn't match */
7035            _CMFLD,                      /* fcode */
7036            "",                          /* hlpmsg */
7037            "",                          /* default */
7038            "",                          /* addtl string data */
7039            0,                           /* addtl numeric data 1 */
7040            0,                           /* addtl numeric data 2 */
7041            xxstring,
7042            NULL,
7043            NULL
7044            );
7045   again:
7046     cmfdbi(&fi,                         /* 2nd FDB - file to delete */
7047            _CMIFI,                      /* fcode */
7048            "File(s) to delete",         /* hlpmsg */
7049            deldef,                      /* default */
7050            "",                          /* addtl string data */
7051            nolinks | deldirs,           /* 0 = files, 1 = files or dirs */
7052            0,                           /* 1 = dirs only */
7053            xxstring,
7054            NULL,
7055            &fl
7056            );
7057     while (!havename && !confirmed) {
7058         x = cmfdb(&sw);                 /* Parse something */
7059         if (x < 0) {                    /* Error */
7060             if (x == -3)
7061               break;
7062             if (x == -2 || x == -9)
7063               printf("?Does not match switch or filename: \"%s\"\n",atmbuf);
7064             return(x);
7065         }
7066         if (cmresult.fcode != _CMKEY)   /* Break out if not a switch */
7067           break;
7068         c = cmgbrk();                   /* Get break character */
7069         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
7070             printf("?This switch does not take an argument\n");
7071             rc = -9;
7072             goto xdelete;
7073         }
7074         if (!getval && (cmgkwflgs() & CM_ARG)) {
7075             printf("?This switch requires an argument\n");
7076             rc = -9;
7077             goto xdelete;
7078         }
7079         switch (k = cmresult.nresult) {
7080           case DEL_AFT:
7081           case DEL_BEF:
7082           case DEL_NAF:
7083           case DEL_NBF:
7084             if (!getval) break;
7085             if ((x = cmdate("File-time","",&s,0,xxstring)) < 0) {
7086                 if (x == -3) {
7087                     printf("?Date-time required\n");
7088                     x = -9;
7089                 } else
7090                   rc = x;
7091                 goto xdelete;
7092             }
7093             fs++;
7094             deltree = 0;
7095             switch (k) {
7096               case DEL_AFT: makestr(&del_aft,s); break;
7097               case DEL_BEF: makestr(&del_bef,s); break;
7098               case DEL_NAF: makestr(&del_naf,s); break;
7099               case DEL_NBF: makestr(&del_nbf,s); break;
7100             }
7101             break;
7102           case DEL_DOT:
7103             matchdot = 1;
7104             break;
7105           case DEL_NOD:
7106             matchdot = 0;
7107             break;
7108           case DEL_ALL:
7109             fs = 0;
7110 #ifdef VMS
7111             deldef = "*.*";             /* UNIX, Windows, OS/2 */
7112 #else
7113 #ifdef datageneral
7114             deldef = "+";               /* AOS/VS */
7115 #else
7116             deldef = "*";               /* UNIX, Windows, OS/2, VMS... */
7117 #endif /* datageneral */
7118 #endif /* VMS */
7119             deltree = 1;
7120             nolinks = 2;
7121             matchdot = 1;
7122             recursive = 1;              /* Fall through purposely... */
7123           case DEL_DIR:
7124             deldirs = 1;
7125             goto again;
7126           case DEL_EXC:
7127             if (!getval) break;
7128             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
7129                 if (x == -3) {
7130                     printf("?Pattern required\n");
7131                     x = -9;
7132                 } else
7133                   rc = x;
7134                 goto xdelete;
7135             }
7136             fs++;
7137             deltree = 0;
7138             makestr(&del_exc,s);
7139             break;
7140           case DEL_HDG:
7141             x_hdg = 1;
7142             break;
7143 #ifdef RECURSIVE
7144           case DEL_REC:
7145             recursive = 1;
7146             break;
7147 #endif /* RECURSIVE */
7148           case DEL_LIS:
7149             x_lis = 1;
7150             break;
7151           case DEL_SUM:
7152             summary = 1;
7153             x_lis = 0;
7154             x_hdg = 1;
7155             break;
7156           case DEL_NOL:
7157             x_lis = 0;
7158             break;
7159 #ifndef CK_TTGWSIZ
7160           case DEL_PAG:
7161             xaskmore = 1;
7162             break;
7163           case DEL_NOP:
7164             xaskmore = 0;
7165             break;
7166 #endif /* CK_TTGWSIZ */
7167           case DEL_QUI:
7168             qflag = 1;
7169             x_lis = 0;
7170             break;
7171           case DEL_VRB:
7172             x_lis = 1;
7173             break;
7174 
7175           case DEL_SMA:
7176           case DEL_LAR:
7177             if (!getval) break;
7178             if ((x = cmnum("File size in bytes","0",10,&y,xxstring)) < 0)
7179               return(x);
7180             fs++;
7181             deltree = 0;
7182             switch (cmresult.nresult) {
7183               case DEL_SMA: minsize = y; break;
7184               case DEL_LAR: maxsize = y; break;
7185             }
7186             break;
7187 
7188           case DEL_SIM:
7189             simulate = 1;
7190             x_lis = 1;
7191             break;
7192           case DEL_ASK:
7193             asking = 1;
7194             break;
7195           case DEL_NAS:
7196             asking = 0;
7197             break;
7198           case DEL_TYP: {
7199               extern struct keytab txtbin[];
7200               if (!getval) break;
7201               if ((x = cmkey(txtbin,3,"","",xxstring)) < 0)
7202                 return(x);
7203               if (x == 2) {             /* ALL */
7204                   xmode = -1;
7205               } else {                  /* TEXT or BINARY only */
7206                   xmode = x;
7207                   scan = 1;
7208               }
7209               break;
7210           }
7211           default:
7212             printf("?Not implemented yet - \"%s\"\n",atmbuf);
7213             return(-9);
7214         }
7215     }
7216     if (qflag && (cmresult.fcode == _CMFLD)) {
7217         if ((x = cmcfm()) < 0)
7218           return(x);
7219         else
7220           return(success = 0);
7221     }
7222     if (cmresult.fcode != _CMIFI) {
7223         if (*atmbuf) {
7224 	    int x;
7225             if (iswild(atmbuf) && nzxpand(atmbuf,nzxopts) == 0)
7226               printf("?No files match: %s\n",brstrip(atmbuf));
7227             else if ((x = zchki(atmbuf)) == -1)
7228 	      printf("?File not found: %s\n",brstrip(atmbuf));
7229 	    else if (x == -2)
7230 	      printf("?Not a regular file: %s\n",atmbuf);
7231 	    else
7232               /* printf("?Not a deletable file: %s\n",atmbuf); */
7233               goto tryanyway;
7234         } else {
7235             printf("?A file specification is required\n");
7236         }
7237         return(-9);
7238     }
7239   tryanyway:
7240     ckstrncpy(tmpbuf,cmresult.sresult,TMPBUFSIZ); /* Safe copy of filespec */
7241     if (deldirs) {
7242         ckstrncpy(safebuf,cmresult.sresult,CKMAXPATH);
7243 #ifdef VMSORUNIX
7244         len = zgetfs(tmpbuf);           /* Is it a directory name? */
7245         argisdir = zgfs_dir;            /* Then because of how zxpand() */
7246         if (argisdir && zgfs_link)      /* works, we have to add it to */
7247           argisdir = 0;                 /* the list. */
7248         if (itsadir)
7249           len = -2;
7250 #else
7251         len = zchki(tmpbuf);
7252         if (len < 0)
7253           argisdir = isdir(tmpbuf);
7254 #endif /* VMSORUNIX */
7255     }
7256     debug(F110,"DELETE file",tmpbuf,0);
7257     if ((x = cmcfm()) < 0)
7258       return(x);
7259 
7260 #ifdef IKSD
7261 #ifdef CK_LOGIN
7262     if (inserver && isguest) {
7263         printf("?Sorry, DELETE unavailable to guests\n");
7264         return(-9);
7265     }
7266 #endif /* CK_LOGIN */
7267 #endif /* IKSD */
7268 
7269 #ifndef OS2ORUNIX
7270     if (simulate) {
7271         printf("?Sorry, /SIMULATE not implemented on this platform\n");
7272         return(-9);
7273     }
7274 #endif /* OS2ORUNIX */
7275 
7276 #ifdef COMMENT
7277     /* (not needed) */
7278     if (!iswild(tmpbuf)) {
7279         char *m;
7280         errno = 0;
7281         x = zchki(tmpbuf);
7282         if (x < 0) {
7283             switch (x) {
7284               case -2: m = "Not a regular file"; break;
7285               case -1: m = "File not found or not accessible"; break;
7286               default: m = errno ? ck_errstr() : "Can't delete";
7287             }
7288             printf("?%s: \"%s\"\n",m,tmpbuf);
7289             return(-9);
7290         }
7291     }
7292 #endif /* COMMENT */
7293 
7294     makelist(del_exc,dxlist,8);
7295 
7296 /* tmpbuf[] has the name - now do any needed conversions on it */
7297 
7298 #ifdef OS2
7299     {   /* Lower level functions change / to \, not good for CMD.EXE. */
7300         char *p = tmpbuf;
7301         while (*p) {                    /* Change them back to \ */
7302             if (*p == '/') *p = '\\';
7303             p++;
7304         }
7305     }
7306 #endif /* OS2 */
7307 
7308 #ifdef VMS
7309     if (iswild(tmpbuf)) {
7310 #ifdef COMMENT
7311         /* Does not handle '.' as version separator */
7312         char *p = tmpbuf;
7313         x = 0;
7314         while (*p) {
7315             if (*p == ';') {
7316                 x = 1;
7317                 break;
7318             } else
7319               p++;
7320         }
7321         if (!x) ckstrncat(tmpbuf,";*",TMPBUFSIZ);
7322 #else
7323         j = 0; x = 0;                   /* for end_dot and number of dots */
7324         i = strlen(tmpbuf);
7325         if (tmpbuf[i] == ';') {
7326             ckstrncat(tmpbuf,"0",TMPBUFSIZ);
7327         } else {
7328             if (tmpbuf[i--] == '.')
7329               j++;
7330             for (; i >= 0; i--) {
7331                 if (tmpbuf[i] == ';' || tmpbuf[i] == ':' ||
7332                     tmpbuf[i] == ']' || tmpbuf[i] == '>')
7333                   break;
7334                 else if (tmpbuf[i] == '.')
7335                   x++;
7336             }
7337             if (tmpbuf[i] != ';') {     /* dot may have been used */
7338                 if (j) {                /* last char is dot */
7339                   if (x)                /* second is version separator */
7340                     ckstrncat(tmpbuf,"0",TMPBUFSIZ);
7341                   else                  /* 'foo.' */
7342                     ckstrncat(tmpbuf,";0",TMPBUFSIZ);
7343                 } else if (x == 1)      /* lacking a version separator */
7344                   ckstrncat(tmpbuf,";0",TMPBUFSIZ);
7345                 else if (x == 0)        /* x == 2 has a version */
7346                   ckstrncat(tmpbuf,".*;0",TMPBUFSIZ);
7347             }
7348         }
7349 #endif /* COMMENT */
7350     }
7351 #endif /* VMS */
7352 
7353     debug(F110,"dodel tmpbuf",tmpbuf,0); /* Filename */
7354 
7355 #ifndef OS2ORUNIX                       /* No built-in DELETE code... */
7356     /* Construct system command. */
7357     ckmakmsg(line,LINBUFSIZ,DELCMD," ",tmpbuf,NULL);
7358 #else
7359 #ifdef VMS
7360     if (asking) {                       /* Maybe overwrite in VMS */
7361         if (x_lis)                      /* if options are needed... */
7362           ckmakmsg(line,LINBUFSIZ,DELCMD," /confirm/log ",tmpbuf,NULL);
7363         else
7364           ckmakmsg(line,LINBUFSIZ,DELCMD," /confirm ",tmpbuf,NULL);
7365     } else if (x_lis)
7366       ckmakmsg(line,LINBUFSIZ,DELCMD," /log ",tmpbuf,NULL);
7367     conres();
7368 #endif /* VMS */
7369 
7370     debug(F110,"dodel line",line,0);
7371 #endif /* OS2ORUNIX */
7372 
7373 #ifdef MAC
7374     success = (zdelet(tmpbuf) == 0);
7375 
7376 #else  /* !MAC ... */
7377 
7378 #ifdef OS2ORUNIX
7379     {
7380         int filespace = 0;
7381         int count = 0;
7382         int lines = 0;
7383         int n = 0;
7384 
7385         s = tmpbuf;
7386 
7387 #ifdef CK_TTGWSIZ
7388 #ifdef OS2
7389         ttgcwsz();
7390 #else /* OS2 */
7391         /* Check whether window size changed */
7392         if (ttgwsiz() > 0) {
7393             if (tt_rows > 0 && tt_cols > 0) {
7394                 cmd_rows = tt_rows;
7395                 cmd_cols = tt_cols;
7396             }
7397         }
7398 #endif /* OS2 */
7399 #endif /* CK_TTGWSIZ */
7400 
7401         if (x_hdg > 0 && !summary) {
7402             printf("Deleting %s...%s\n", s, simulate ? " (SIMULATION)" : "");
7403             n += 2;
7404         }
7405 #ifdef ZXREWIND
7406         z = zxrewind();                 /* Rewind file list */
7407 #else
7408         if (!deldirs)
7409           nzxopts = ZX_FILONLY;
7410         if (recursive) nzxopts |= ZX_RECURSE;
7411         if (matchdot)  nzxopts |= ZX_MATCHDOT;
7412         errno = 0;
7413         z = nzxpand(s,nzxopts);         /* Expand file list */
7414 #endif /* ZXREWIND */
7415         debug(F111,"dodel",s,z);
7416 
7417         /* If deleting directories, sort in reverse order */
7418         /* so we delete the files first, then the directory. */
7419 
7420 #ifdef OS2
7421         /* In K95, we have no mtchs array, nor any control over */
7422         /* the order in which znext() returns filenames, so we  */
7423         /* must copy the array and sort it. */
7424         {
7425             int i;
7426             if (xmtchs) {               /* Free previous list in case */
7427                 debug(F101,"dodel freeing previous list","",xmtchn);
7428                 for (i = 0; i < xmtchn; i++) /* it wasn't freed last time. */
7429                   if (xmtchs[i])
7430                     free(xmtchs[i]);
7431                 free(xmtchs);
7432             }
7433             xmtchn = 0;
7434             xmtchs = (char **)malloc(z * (sizeof(char **))); /* Make new one */
7435             if (!xmtchs) {
7436                 printf("?Memory allocation failure\n");
7437                 return(-9);
7438             }
7439             for (i = 0; i < z; i++) {
7440                 xmtchs[i] = NULL;
7441                 znext(tmpbuf);
7442                 if (!*tmpbuf)
7443                   break;
7444                 makestr(&(xmtchs[i]),tmpbuf);
7445                 if (!xmtchs[i]) {
7446                     printf("?Memory allocation failure\n");
7447                     xmtchn = i - 1;
7448                     rc = -9;
7449                     goto xdelete;
7450                 }
7451                 /* debug(F111,"dodel add",xmtchs[i],i); */
7452             }
7453             xmtchn = i;
7454             debug(F101,"dodel xmtchn","",xmtchn);
7455             sh_sort(xmtchs,NULL,z,0,deldirs,0);
7456         }
7457 #else
7458 #ifdef UNIX
7459         sh_sort(mtchs,NULL,z,0,deldirs,filecase);
7460 #endif /* UNIX */
7461 #endif /* OS2 */
7462 
7463         if (z > 0) {
7464             int i;
7465 #ifdef OS2
7466             int ix = 0;
7467 #endif /* OS2 */
7468             success = 1;
7469             if (x_hdg > 0)
7470               printf("\n");
7471 
7472             while (
7473 #ifdef OS2
7474                    ix < xmtchn
7475 #else
7476                    1
7477 #endif /* OS2 */
7478                    ) {                  /* Loop for all files */
7479 #ifdef OS2
7480                 ckstrncpy(tmpbuf,xmtchs[ix++],TMPBUFSIZ);
7481 #else
7482                 znext(tmpbuf);          /* Get next file */
7483 #endif /* OS2 */
7484                 if (!*tmpbuf) {         /* No more */
7485                     if (deldirs && recursive && argisdir) {
7486                         ckstrncpy(tmpbuf,safebuf,TMPBUFSIZ);
7487                         argisdir = 0;   /* (only do this once) */
7488                     } else
7489                       break;
7490                 }
7491                 skip = 0;
7492                 if (!deltree) {
7493                     if (fs)
7494                       if (fileselect(tmpbuf,
7495                                      del_aft,del_bef,del_naf,del_nbf,
7496                                      minsize,maxsize,0,8,dxlist) < 1) {
7497                           skip++;
7498                       }
7499                 }
7500                 if (!skip && scan && itsadir) {
7501                     skip++;
7502                 }
7503                 if (!skip && scan) {
7504                       switch (scanfile(tmpbuf,&y,nscanfile)) {
7505                       case FT_BIN:
7506                         if (xmode != 1)
7507                           skip++;
7508                         break;
7509                       case FT_TEXT:
7510                       case FT_7BIT:
7511                       case FT_8BIT:
7512 #ifdef UNICODE
7513                       case FT_UTF8:
7514                       case FT_UCS2:
7515 #endif /* UNICODE */
7516                         if (xmode != 0)
7517                           skip++;
7518                     }
7519                 }
7520                 if (!skip && asking) {
7521                     int x;
7522                     ckmakmsg(line,LINBUFSIZ," Delete ",tmpbuf,"? ",NULL);
7523                     x = getyesno(line,3);
7524                     switch (x) {
7525                       case 0: continue;           /* no */
7526                       case 1: break;              /* yes */
7527                       case 2: goto xdelete;       /* quit */
7528                       case 3: asking = 0; break;  /* go */
7529                     }
7530                 }
7531 #ifdef VMSORUNIX
7532                 len = zgetfs(tmpbuf);   /* Get length and accessibility */
7533                 itsadir = zgfs_dir;
7534                 if (itsadir && zgfs_link) { /* Treat links to directories */
7535                     itsadir = 0;        /* as regular files */
7536                     if (scan)           /* But not if /TYPE: was given */
7537                       skip++;
7538                 }
7539                 if (itsadir)            /* (emulate non-Unix code) */
7540                   len = -2;
7541 #else
7542                 len = zchki(tmpbuf);    /* Get accessibility */
7543                 if (len < 0)            /* See if it's a directory */
7544                   itsadir = isdir(tmpbuf);
7545 #endif /* VMSORUNIX */
7546 
7547                 if (skip) {
7548 #ifdef COMMENT                          /* Too verbose */
7549                     if (x_lis > 0) {
7550                         lines++;
7551                         printf(" %s (SKIPPED)\n",tmpbuf);
7552 #ifdef CK_TTGWSIZ
7553                         if (++n > cmd_rows - 3)
7554                           if (!askmore()) goto xdelete; else n = 0;
7555 #endif /* CK_TTGWSIZ */
7556                     }
7557 #endif /* COMMENT */
7558                     continue;
7559                 }
7560 
7561                 debug(F111,"DELETE len",tmpbuf,len);
7562                 if (simulate) {
7563                     filespace += len;
7564                     count++;
7565                     if (x_lis > 0) {
7566                         lines++;
7567                         printf(" %s (SELECTED)\n",tmpbuf);
7568                         if (++n > cmd_rows - 3) {
7569                             int xx;
7570                             xx = askmore();
7571                             if (!xx) goto xdelete; else n = 0;
7572                         }
7573                     }
7574                 } else if (len >= 0 || !itsadir) { /* Regular file */
7575                     zdelet(tmpbuf);                /* or symlink, etc... */
7576                     if (zchki(tmpbuf) < 0) {
7577                         filespace += len;
7578                         count++;
7579                         if (x_lis > 0) {
7580                             lines++;
7581                             printf(" %s (OK)\n",tmpbuf);
7582                             if (++n > cmd_rows - 3)
7583                               if (!askmore()) goto xdelete; else n = 0;
7584                         }
7585                     } else {
7586                         bad++;
7587                         success = 0;
7588                         if (x_lis > 0) {
7589                             lines++;
7590                             printf(" %s (FAILED: %s)\n",tmpbuf,ck_errstr());
7591                             if (++n > cmd_rows - 3)
7592                               if (!askmore()) goto xdelete; else n = 0;
7593                         }
7594                     }
7595                 } else if (/* pass > 0 && */ deldirs && itsadir) {
7596                     /* It's a directory */
7597                     if (zrmdir(tmpbuf) > -1) { /* Only works if empty */
7598                         count++;
7599                         if (x_lis > 0) {
7600                             lines++;
7601                             printf(" %s (OK)\n",tmpbuf);
7602                             if (++n > cmd_rows - 3)
7603                               if (!askmore()) goto xdelete; else n = 0;
7604                         }
7605                     } else {
7606                         success = 0;
7607                         if (x_lis > 0) {
7608                             lines++;
7609                             printf(" %s (FAILED: %s)\n",
7610                                    tmpbuf,
7611                                    ck_errstr());
7612                             if (++n > cmd_rows - 3)
7613                               if (!askmore()) goto xdelete; else n = 0;
7614                         }
7615                     }
7616                 } else if (x_lis > 0) {
7617                     lines++;
7618                     if (isdir(tmpbuf))
7619                       printf(" %s (FAILED: directory)\n",tmpbuf);
7620                     else
7621                       printf(" %s (FAILED: not a regular file)\n",tmpbuf);
7622                     if (++n > cmd_rows - 3)
7623                       if (!askmore()) goto xdelete; else n = 0;
7624                 }
7625             }
7626             if (x_hdg > 0) {
7627                 if (lines > 0)
7628                   printf("\n");
7629                 if (++n > cmd_rows - 3)
7630                   if (!askmore()) goto xdelete; else n = 0;
7631                 printf("%d file%s %sdeleted, %d byte%s %sfreed%s\n",
7632                        count,
7633                        count != 1 ? "s" : "",
7634                        simulate ? "would be " : "",
7635                        filespace,
7636                        filespace != 1 ? "s" : "",
7637                        simulate ? "would be " : "",
7638                        simulate ? " (maybe)" : ""
7639                        );
7640             }
7641             if (!x_lis && !success && !quiet) {
7642                 printf("?DELETE failed for %d file%s \
7643 (use DELETE /LIST to see details)\n",
7644                        bad, bad == 1 ? "" : "s"
7645                        );
7646             }
7647         } else if (x_lis > 0) {
7648             if (errno)
7649               printf("?%s: %s\n",ck_errstr(), tmpbuf);
7650             else
7651               printf("?Can't delete: %s\n",tmpbuf);
7652         }
7653     }
7654 #else /* OS2ORUNIX */
7655 #ifndef VMS                             /* Others - let the system do it. */
7656     xsystem(line);
7657     x = nzxpand(tmpbuf,nzxopts);
7658     success = (x > 0) ? 0 : 1;
7659     if (x_hdg > 0)
7660       printf("%s - %sdeleted\n", tmpbuf, success ? "" : "not ");
7661 #else
7662     if (asking)
7663       printf("\n");
7664     x = xsystem(line);                  /* zshcmd returns 1 for success */
7665     success = (x > 0) ? 1 : 0;
7666     if (x_hdg > 0 && !asking)
7667       printf("%s - %sdeleted\n", tmpbuf, success ? "" : "not ");
7668     concb((char)escape);
7669 #endif /* VMS */
7670 #endif /* OS2ORUNIX */
7671 #endif /* MAC */
7672   xdelete:
7673     if (g_matchdot > -1) {
7674         matchdot = g_matchdot;          /* Restore these... */
7675         g_matchdot = -1;
7676     }
7677 #ifdef OS2
7678     if (xmtchs) {
7679         int i;
7680         debug(F101,"dodel freeing list","",xmtchn);
7681         for (i = 0; i < xmtchn; i++)
7682           if (xmtchs[i]) free(xmtchs[i]);
7683         free(xmtchs);
7684         xmtchs = NULL;
7685         xmtchn = 0;
7686     }
7687 #endif /* OS2 */
7688     debug(F101,"dodel result","",rc);
7689     return((rc < 0) ? rc : success);
7690 }
7691 #endif /* NOFRILLS */
7692 
7693 #ifndef NOSPL                           /* The ELSE command */
7694 _PROTOTYP( VOID pushqcmd, (char *) );
7695 
7696 int
doelse()7697 doelse() {
7698     if (!ifcmd[cmdlvl]) {
7699         printf("?ELSE doesn't follow IF\n");
7700         return(-2);
7701     }
7702 #ifdef COMMENT
7703 /*
7704   Wrong.  This prevents IF..ELSE IF...ELSE IF...ELSE IF...ELSE...
7705   from working.
7706 */
7707     ifcmd[cmdlvl] = 0;
7708 #endif /* COMMENT */
7709     if (!iftest[cmdlvl]) {              /* If IF was false do ELSE part */
7710         if (maclvl > -1 || tlevel > -1) { /* In macro or command file */
7711             debug(F100,"doelse pushing","",0);
7712 #ifndef COMMENT
7713             pushcmd(NULL);              /* save rest of command. */
7714 #else
7715             /* This fixes certain obscure problems */
7716             /* but breaks many other constructions that must work. */
7717             pushqcmd(NULL);
7718 #endif /* COMMENT */
7719         } else {                        /* If interactive, */
7720             cmini(ckxech);              /* just start a new command */
7721             printf("\n");               /* (like in MS-DOS Kermit) */
7722             if (pflag) prompt(xxstring);
7723         }
7724     } else {                            /* Condition is false */
7725         if ((y = cmtxt("command to be ignored","",&s,NULL)) < 0)
7726           return(y);                    /* Gobble up rest of line */
7727     }
7728     return(0);
7729 }
7730 #endif /* NOSPL */
7731 
7732 #ifndef NOSPL
7733 int
doswitch()7734 doswitch() {
7735     char *lp, *ap;                      /* Macro argument pointer */
7736     int len = 0, x, y, pp = 0;
7737     char brbuf[3];
7738 
7739     /* Get variable name */
7740 
7741     tmpbuf[0] = NUL;
7742     brbuf[0] = '{';
7743     brbuf[1] = '}';
7744     brbuf[2] = NUL;
7745 
7746     y = cmfld("Variable name","",&s,xxstring);
7747     debug(F111,"doswitch cmfld",s,y);
7748     if (y < 0) {
7749         if (y == -3)			/* Because brstrip() writes */
7750           s = brbuf;			/* into its argument. */
7751         else
7752           return(y);
7753     }
7754     debug(F110,"doswitch A",s,0);
7755     if (!strcmp(s,"(")) {
7756         pp++;
7757         if ((y = cmfld("Variable name","",&s,xxstring)) < 0) {
7758             if (y == -3)
7759               s = brbuf;
7760             else
7761               return(y);
7762 	    debug(F110,"doswitch B",s,0);
7763         }
7764     }
7765     len = ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ);
7766     if (tmpbuf[0] == CMDQ) {
7767         if (chkvar(s) < 1) {
7768             printf("?Variable name required\n");
7769             return(-9);
7770         }
7771     }
7772     if (pp > 0) {                       /* If open paren given parse closing */
7773         if ((y = cmfld("Closing parenthesis","",&s,NULL)) < 0)
7774           return(y);
7775         if (strcmp(atmbuf,")")) {
7776             printf("?Closing parenthesis required\n");
7777             return(-9);
7778         }
7779     }
7780     lp = line;
7781     x = ckstrncpy(lp,"_switx ",LINBUFSIZ); /* _switx + space */
7782     lp += x;
7783     ap = lp;
7784     debug(F010,"SWITCH a",line,0);
7785 
7786 #ifdef COMMENT
7787     x = ckmakmsg(lp,LINBUFSIZ-x,tmpbuf," ",NULL,NULL); /* variable name + SP */
7788 #else
7789     {                                   /* variable name + SP */
7790         char * p = tmpbuf;
7791 	if (len > 0) {
7792 	    if (tmpbuf[0] == '(' && tmpbuf[len-1] == ')') {
7793 		tmpbuf[len-1] = NUL;
7794 		p++;
7795 	    }
7796         }
7797         x = ckmakmsg(lp,LINBUFSIZ-x,"{",brstrip(p),"}"," ");
7798     }
7799 #endif /* COMMENT */
7800     debug(F010,"SWITCH b",line,0);
7801     lp += x;
7802 
7803     /* Get body */
7804 
7805     if ((y = cmtxt("series of cases","",&s,NULL)) < 0) return(y);
7806     if ((y = (int)strlen(s)) < 1) return(-2);
7807     if (s[0] != '{' && s[y-1] != '}') { /* Supply braces if missing */
7808         ckmakmsg(tmpbuf,TMPBUFSIZ,"{ ",s," }",NULL);
7809         s = tmpbuf;
7810     }
7811     if (litcmd(&s,&lp,(LINBUFSIZ - (lp - (char *)line) - 2)) < 0) {
7812         printf("?Unbalanced braces\n");
7813         return(0);
7814     }
7815     debug(F010,"SWITCH c",line,0);
7816 
7817     x = mlook(mactab,"_switx",nmac);    /* Look up SWITCH macro definition */
7818     if (x < 0) {                        /* Not there? */
7819         addmmac("_switx",sw_def);       /* Put it back. */
7820         if ((x = mlook(mactab,"_switx",nmac)) < 0) { /* Look it up again. */
7821             printf("?SWITCH macro definition gone!\n"); /* Shouldn't happen. */
7822             return(success = 0);
7823         }
7824     }
7825     debug(F010,"SWITCH command",line,0); /* Execute the SWITCH macro. */
7826     success = dodo(x,ap,cmdstk[cmdlvl].ccflgs | CF_IMAC);
7827     debug(F101,"SWITCH status","",success);
7828     return(success);
7829 }
7830 
7831 int
dofor()7832 dofor() {                               /* The FOR command. */
7833     int i, fx, fy, fz;                  /* loop variables */
7834     char *ap, *di;                      /* macro argument pointer */
7835     int pp = 0;                         /* Paren level */
7836     int mustquote = 0;
7837     char loopvar[8], loopvar2[8];       /* \%x-style loop variable */
7838 
7839     debug(F100,"dofor entry","",0);
7840     for (i = 0; i < 2; i++) {
7841         if ((y = cmfld("Variable name","",&s,NULL)) < 0) {
7842             if (y == -3) {
7843                 printf("?Variable name required\n");
7844                 return(-9);
7845             } else
7846               return(y);
7847         }
7848         if (strcmp(s,"("))
7849           break;
7850         pp++;
7851     }
7852 #ifdef COMMENT
7853     if ((y = parsevar(s,&x,&z)) < 0)    /* Check variable. */
7854       return(y);
7855 #else
7856     if (*s == CMDQ)                     /* If loop variable starts with */
7857       mustquote++;                      /* backslash, mustquote is > 0. */
7858 #endif /* COMMENT */
7859     debug(F111," dofor loop variable mustquote",s,mustquote);
7860 
7861     lp = line;                          /* Build a copy of the command */
7862     ckstrncpy(lp,"_forx ",LINBUFSIZ);
7863     lp += (int)strlen(line);            /* "_for" macro. */
7864     ap = lp;                            /* Save pointer to macro args. */
7865 
7866     if (*s == CMDQ) s++;                /* Skip past backslash if any. */
7867     while ((*lp++ = *s++)) ;            /* copy it */
7868     lp--; *lp++ = SP;                   /* add a space */
7869 
7870     if ((y = cmnum("initial value","",10,&fx,xxstring)) < 0) {
7871         if (y == -3) return(-2);
7872         else return(y);
7873     }
7874     debug(F101," dofor fx","",fx);
7875     s = atmbuf;                         /* Copy the atom buffer */
7876 
7877     if ((int)strlen(s) < 1) goto badfor;
7878 /*
7879   In edit 192, we change the loop variables to be evaluated at loop entry,
7880   not each time through the loop.  This was required in order to allow
7881   \v(argc) to be used as a loop variable, or in a loop-variable expression.
7882   Thus, we can't have FOR loops that modify their own exit conditions by
7883   changing the final value or the increment.  The problem with \v(argc) was
7884   that it is on the macro stack; after entry into the _forx macro, it is at
7885   the wrong place.
7886 */
7887     sprintf(tmpbuf,"%d",fx);            /* (SAFE) Substitute actual value */
7888     s = tmpbuf;
7889     while ((*lp++ = *s++)) ;            /* (what they actually typed) */
7890     lp--; *lp++ = SP;
7891 #ifdef DEBUG
7892     *lp = NUL;
7893     debug(F110," dofor line A",line,0);
7894 #endif /* DEBUG */
7895 
7896     if ((y = cmnum("final value","",10,&fy,xxstring)) < 0) {
7897         if (y == -3) return(-2);
7898         else return(y);
7899     }
7900     debug(F101," dofor loop exit value","",fy);
7901     s = atmbuf;                         /* Same deal */
7902     if ((int)strlen(s) < 1)
7903       goto badfor;
7904 
7905     sprintf(tmpbuf,"%d",fy);            /* SAFE */
7906     s = tmpbuf;
7907     while ((*lp++ = *s++)) ;
7908     lp--;
7909     *lp++ = SP;
7910 #ifdef DEBUG
7911     *lp = NUL;
7912     debug(F110," dofor line B",line,0);
7913 #endif /* DEBUG */
7914 
7915     x_ifnum = 1;                        /* Increment or parenthesis */
7916     di = (fx < fy) ? "1" : "-1";        /* Default increment */
7917     debug(F110," dofor default increment",di,0);
7918     if ((y = cmnum("increment",di,10,&fz,xxstring)) < 0) {
7919         debug(F111," dofor increment parse failed",atmbuf,y);
7920         x_ifnum = 0;
7921         if (y == -3) {                  /* Premature termination */
7922             return(-2);
7923         } else if (y == -2) {           /* Maybe closing paren */
7924             if (!strcmp(atmbuf,")")) {
7925                 pp--;                   /* Count it */
7926                 s = di;                 /* supply default interval */
7927                 fz = atoi(s);
7928             } else                      /* Not closing paren, invalid */
7929               return(y);
7930         } else                          /* Other error */
7931           return(y);
7932         debug(F101," dofor default increment supplied","",fz);
7933     } else {                            /* Number */
7934         x_ifnum = 0;
7935         debug(F101," dofor parsed increment ok","",fz);
7936         s = atmbuf;                     /* Use it */
7937     }
7938     if ((int)strlen(s) < 1)
7939       goto badfor;
7940 
7941     sprintf(tmpbuf,"%d",fz);            /* (SAFE) Same deal */
7942     s = tmpbuf;
7943     while ((*lp++ = *s++)) ;
7944     lp--; *lp++ = SP;
7945 
7946 #ifdef DEBUG
7947     *lp = NUL;
7948     debug(F110," dofor FOR command C",line,0);
7949 #endif /* DEBUG */
7950 
7951     /* Insert the appropriate comparison operator */
7952     if (fz < 0)
7953       *lp++ = '<';
7954     else
7955       *lp++ = '>';
7956     *lp++ = SP;
7957 
7958 #ifdef DEBUG
7959     *lp = NUL;
7960     debug(F110," dofor FOR command D",line,0);
7961 #endif /* DEBUG */
7962 
7963     if (pp > 0) {                       /* If open paren given parse closing */
7964         if ((y = cmfld("Closing parenthesis","",&s,NULL)) < 0)
7965           return(y);
7966         if (strcmp(atmbuf,")")) {
7967             printf("?Closing parenthesis required\n");
7968             return(-9);
7969         }
7970     }
7971     if ((y = cmtxt("Command(s) to execute","",&s,NULL)) < 0) return(y);
7972     if ((y = (int)strlen(s)) < 1) return(-2);
7973     debug(F110," doif FOR body A",s,0);
7974     if (s[0] != '{' && s[y-1] != '}') { /* Supply braces if missing */
7975         ckmakmsg(tmpbuf,TMPBUFSIZ,"{ ",s," }",NULL);
7976         s = tmpbuf;
7977     }
7978     debug(F110," doif FOR body B",s,0);
7979     if (litcmd(&s,&lp,(LINBUFSIZ - (lp - (char *)line) - 2)) < 0) {
7980         printf("?Unbalanced braces\n");
7981         return(0);
7982     }
7983 
7984 #ifdef DEBUG
7985     *lp = NUL;
7986     debug(F110," doif FOR body C",s,0);
7987 #endif /* DEBUG */
7988 
7989 #ifdef COMMENT
7990 /* Too strict */
7991     if (fz == 0) {
7992         printf("?Zero increment not allowed\n");
7993         return(0);
7994     }
7995 #endif /* COMMENT */
7996 /*
7997   In C-Kermit 8.0 we allow bare macro names anywhere a numeric-valed variable
7998   could appear.  But this caused trouble for the FOR loops because the quoting
7999   in for_def[] assumed a \%i-style loop variable.  We account for this here in
8000   the if (mustquote)...else logic by invoking separate FOR macro definitions
8001   in the two cases.
8002 */
8003     debug(F100," dofor choosing FOR macro definition","",0);
8004     if (mustquote) {                    /* \%i-style loop variable */
8005         debug(F101," dofor choosing _forx because mustquote","",mustquote);
8006         x = mlook(mactab,"_forx",nmac); /* Look up FOR macro definition */
8007         if (x < 0) {                    /* Not there? */
8008             addmmac("_forx",for_def);   /* Put it back. */
8009             if ((x = mlook(mactab,"_forx",nmac)) < 0) { /* Look it up again. */
8010                 printf("?FOR macro definition gone!\n");
8011                 return(success = 0);
8012             }
8013             debug(F110," dofor loop var is \\%x",for_def[0],0);
8014         }
8015     } else {                            /* Loop variable is a macro */
8016         debug(F101," dofor choosing _forz because mustquote","",mustquote);
8017         x = mlook(mactab,"_forz",nmac);
8018         if (x < 0) {
8019             addmmac("_forz",foz_def);
8020             if ((x = mlook(mactab,"_forz",nmac)) < 0) {
8021                 printf("?FOR macro definition gone!\n");
8022                 return(success = 0);
8023             }
8024         }
8025         debug(F110," dofor loop var is macro",foz_def[0],0);
8026     }
8027     debug(F010," dofor final FOR body",line,0); /* Execute the FOR macro. */
8028     debug(F100," dofor done, chaining to dodo()...","",0);
8029     return(success = dodo(x,ap,cmdstk[cmdlvl].ccflgs | CF_IMAC));
8030 
8031 badfor:
8032     printf("?Incomplete FOR command\n");
8033     debug(F100," dofoar parse failure","",0);
8034     return(-2);
8035 }
8036 #endif /* NOSPL */
8037 
8038 #ifndef NOSPL
8039 
8040 /*  T O D 2 S E C  --  Convert time of day as hh:mm:ss to secs since midnite */
8041 /*
8042   Call with a string hh:mm or hh:mm:ss.
8043   Returns a 0 to 86400 on success, or a negative number on failure.
8044 */
8045 long
tod2sec(t)8046 tod2sec(t) char * t; {
8047     long t2;
8048     long hh = 0L, mm = 0L, ss = 0L;
8049 
8050     if (!t) t = "";
8051     if (!*t)
8052       return(-3L);
8053     debug(F110,"tod2sec",t,0);
8054 
8055     if (isdigit(*t))                    /* Get hours from argument */
8056       hh = *t++ - '0';
8057     else
8058       return(-1L);
8059     if (isdigit(*t))
8060       hh = hh * 10 + *t++ - '0';
8061 #ifdef COMMENT
8062     if (hh > 24L)
8063       return(-1L);
8064 #endif /* COMMENT */
8065     if (*t == ':')
8066       t++;
8067     else if (!*t)
8068       goto xtod2sec;
8069     else
8070       return(-1L);
8071 
8072     if (isdigit(*t))                    /* Minutes */
8073       mm = *t++ - '0';
8074     else
8075       return(-1L);
8076     if (isdigit(*t))
8077       mm = mm * 10 + *t++ - '0';
8078     if (mm > 60L)
8079       return(-1L);
8080     if (*t == ':')
8081       t++;
8082     else if (!*t)
8083       goto xtod2sec;
8084     else
8085       return(-1L);
8086 
8087     if (isdigit(*t))                    /* Seconds */
8088       ss = *t++ - '0';
8089     else
8090       return(-1L);
8091     if (isdigit(*t))
8092       ss = ss * 10 + *t++ - '0';
8093     if (ss > 60L)
8094       return(-1L);
8095 
8096     if (*t > 32)                        /* No trailing junk allowed */
8097       return(-1L);
8098 
8099   xtod2sec:
8100 
8101     t2 = hh * 3600L + mm * 60L + ss;    /* Seconds since midnight from arg */
8102     debug(F101,"tod2sec t2","",t2);
8103 
8104     return(t2);
8105 }
8106 
8107 int waitinterval = 1;
8108 
8109 #ifdef OLDWAIT
8110 #undef OLDWAIT
8111 #endif /* OLDWAIT */
8112 
8113 int kbchar = NUL;
8114 
8115 int
dopaus(cx)8116 dopaus(cx) int cx; {
8117     long zz;
8118     extern int sleepcan;
8119 
8120 #ifdef OLDWAIT
8121     zz = -1L;
8122     x_ifnum = 1;                        /* Turn off internal complaints */
8123     if (cx == XXWAI)
8124       y = cmnum("seconds to wait, or time of day hh:mm:ss","1",10,&x,xxstring);
8125     else if (cx == XXPAU)
8126       y = cmnum("seconds to pause, or time of day hh:mm:ss",
8127                 "1",10,&x,xxstring);
8128     else
8129       y = cmnum("milliseconds to sleep, or time of day hh:mm:ss",
8130                 "100",10,&x,xxstring);
8131     x_ifnum = 0;
8132     if (y < 0) {
8133         if (y == -2) {                  /* Invalid number or expression */
8134             char *p = tmpbuf;           /* Retrieve string from atmbuf */
8135             int n = TMPBUFSIZ;
8136             *p = NUL;
8137             zzstring(atmbuf,&p,&n);     /* Evaluate in case it's a variable */
8138             zz = tod2sec(tmpbuf);       /* Convert to secs since midnight */
8139             if (zz < 0L) {
8140                 printf("?Number, expression, or time of day required\n");
8141                 return(-9);
8142             } else {
8143                 char now[32];           /* Current time */
8144                 char *p;
8145                 long tnow;
8146                 p = now;
8147                 ztime(&p);
8148                 tnow = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
8149                 if (zz < tnow)          /* User's time before now */
8150                   zz += 86400L;         /* So make it tomorrow */
8151                 zz -= tnow;             /* Seconds from now. */
8152             }
8153         } else
8154           return(y);
8155     }
8156     if (x < 0) x = 0;
8157     switch (cx) {
8158       case XXPAU:                       /* PAUSE */
8159       case XXMSL:                       /* MSLEEP */
8160         if ((y = cmcfm()) < 0) return(y);
8161         break;
8162       case XXWAI:                       /* WAIT */
8163         z = 0;                          /* Modem signal mask */
8164         while (1) {                     /* Read zero or more signal names */
8165             y = cmkey(mstab,nms,"modem signal","",xxstring);
8166             if (y == -3) break;         /* -3 means they typed CR */
8167             if (y < 0) return(y);       /* Other negatives are errors */
8168             z |= y;                     /* OR the bit into the signal mask */
8169         }
8170         if ((y = cmcfm()) < 0) return(y);
8171         break;
8172 
8173       default:                          /* Shouldn't happen */
8174         return(-2);
8175     }
8176 
8177 /* Command is entered, now do it. */
8178 
8179     if (zz > -1L) {                     /* Time of day given? */
8180         x = zz;
8181         if (zz != (long) x) {
8182             printf(
8183 "Sorry, arithmetic overflow - hh:mm:ss not usable on this platform.\n"
8184                    );
8185             return(-9);
8186         }
8187     }
8188     if (cx == XXMSL) {                  /* Millisecond sleep */
8189         msleep(zz < 0 ? x : x * 1000);
8190         return(success = 1);
8191     }
8192     if (cx == XXPAU && !sleepcan) {     /* SLEEP CANCELLATION is OFF */
8193         sleep(x);
8194         return(success = 1);
8195     }
8196 
8197     /* WAIT, or else SLEEP with cancellation allowed... */
8198 
8199     do {                                /* Sleep loop */
8200         int mdmsig;
8201         if (sleepcan) {                 /* Keyboard cancellation allowed? */
8202             if (y = conchk()) {         /* Did they type something? */
8203 #ifdef COMMENT
8204                 while (y--) coninc(0);  /* Yes, gobble it all up */
8205 #else
8206                 /* There is a debate over whether PAUSE should absorb    */
8207                 /* its cancelling character(s).  There are several       */
8208                 /* reasons why it should gobble at least one character:  */
8209                 /* (1) MS-DOS Kermit does it                             */
8210                 /* (2) if not, subsequent PAUSE commands will terminate  */
8211                 /*     immediately                                       */
8212                 /* (3) if not, subsequent ASK commands will use it as    */
8213                 /*     valid input.  If \13, then it will get no input   */
8214                 /* (4) if not, then the character appears on the command */
8215                 /*     line after all enclosing macros are complete.     */
8216                 kbchar = coninc(0);     /* Gobble one up */
8217 #endif /* COMMENT */
8218                 break;                  /* And quit PAUSing or WAITing */
8219             }
8220         }
8221         if (cx == XXWAI) {              /* WAIT (z == modem signal mask) */
8222             debug(F101,"WAIT x","",x);
8223             if (z > 0) {                /* Looking for any modem signals? */
8224                 mdmsig = ttgmdm();      /* Yes, get them */
8225                 if (mdmsig < 0)         /* Failed */
8226                   return(success = 0);
8227                 if ((mdmsig & z) == z)  /* Got what we wanted? */
8228                   return(success = 1);  /* Succeed */
8229             }
8230             if (x == 0)                 /* WAIT 0 and didn't get our signals */
8231               break;
8232         }
8233         sleep(1);                       /* No interrupt, sleep one second */
8234     } while (--x > 0);
8235 
8236     if (cx == XXWAI)                    /* If WAIT and loop exhausted */
8237       success = (z == 0);               /* Fail. */
8238     else                                /*  */
8239       success = (x == 0);               /* Set SUCCESS/FAILURE for PAUSE. */
8240     return(success);
8241 
8242 #else  /* New code uses chained FDBs and allows FILE waits... */
8243 
8244     char * m = "";                      /* Help message */
8245     struct FDB nu, fl;                  /* Parse function descriptor blocks */
8246     int filewait = 0;
8247     int mdmsig = 0, fs = 0;
8248     char filedate[32];
8249 
8250     kbchar = 0;
8251 
8252     switch (cx) {
8253       case XXWAI: m = "seconds to wait, or time of day hh:mm:ss"; break;
8254       case XXPAU: m = "seconds to pause, or time of day hh:mm:ss"; break;
8255       case XXMSL: m = "milliseconds to sleep, or time of day hh:mm:ss"; break;
8256     }
8257     zz = -1L;
8258     cmfdbi(&nu,
8259            _CMNUM,                      /* Number */
8260            m,                           /* Help message */
8261            (cx == XXMSL) ? "100" : "1", /* Default */
8262            "",                          /* N/A */
8263            0,                           /* N/A */
8264            0,                           /* N/A */
8265            xxstring,                    /* Processing function */
8266            NULL,                        /* N/A */
8267            &fl                          /* Next */
8268            );
8269     cmfdbi(&fl,                         /* Time of day */
8270            _CMFLD,                      /* Field */
8271            "",                          /* hlpmsg */
8272            "",                          /* default */
8273            "",                          /* addtl string data */
8274            0,                           /* addtl numeric data 1 */
8275            0,                           /* addtl numeric data 2 */
8276            xxstring,                    /* processing func */
8277            NULL,                        /* N/A */
8278            NULL                         /* No next */
8279            );
8280     x = cmfdb(&nu);                     /* Parse a number or a field */
8281     if (x < 0) {
8282         if (x == -3)
8283           x = -2;
8284         return(x);
8285     }
8286     switch (cmresult.fcode) {
8287       case _CMNUM:                      /* Number */
8288         x = cmresult.nresult;
8289         break;
8290       case _CMFLD:                      /* Field */
8291         zz = tod2sec(cmresult.sresult); /* Convert to secs since midnight */
8292         if (zz < 0L) {
8293             printf("?Number, expression, or time of day required\n");
8294             return(-9);
8295         } else {
8296             char now[32];               /* Current time */
8297             char *p;
8298             long tnow;
8299             p = now;
8300             ztime(&p);
8301             tnow = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
8302             if (zz < tnow)              /* User's time before now */
8303               zz += 86400L;             /* So make it tomorrow */
8304             zz -= tnow;         /* Seconds from now. */
8305         }
8306     }
8307     debug(F101,"PAUSE/WAIT/MSLEEP zz","",zz);
8308     switch (cx) {
8309       case XXPAU:                       /* PAUSE */
8310       case XXMSL:                       /* MSLEEP */
8311         if ((y = cmcfm()) < 0) return(y);
8312         break;
8313       case XXWAI:                       /* WAIT */
8314         z = 0;                          /* Modem signal mask */
8315         y = cmkey(waittab,nwaittab,"","",xxstring);
8316         if (y < 0) {
8317             if (y == -3) {
8318                 if ((y = cmcfm()) < 0)
8319                   return(y);
8320                 break;
8321             } else
8322               return(y);
8323         }
8324         if (y == WAIT_FIL) {            /* FILE */
8325             int wild = 0;
8326             if ((z = cmkey(wfswi,nwfswi,"event","",xxstring)) < 0)
8327               return(z);
8328             filewait = z;
8329             if (filewait == WF_MOD || filewait == WF_DEL)
8330               z = cmifi("Filename","",&s,&wild,xxstring);
8331             else
8332               z = cmfld("Filename","",&s,xxstring);
8333             if (z < 0)
8334               return(z);
8335             if (wild || ((filewait == WF_CRE) && iswild(s))) {
8336                 printf("?Wildcards not valid here\n");
8337                 return(-9);
8338             }
8339             ckstrncpy(tmpbuf,s,TMPBUFSIZ);
8340             if ((z = cmcfm()) < 0)
8341               return(z);
8342             break;
8343         } else if (y != WAIT_MDM) {     /* A modem signal */
8344             z |= y;                     /* OR the bit into the signal mask */
8345         }
8346         if (!filewait) {                /* Modem signals... */
8347             while (1) {                 /* Get zero or more signal names */
8348                 y = cmkey(mstab,nms,"modem signal","",xxstring);
8349                 if (y == -3) break;     /* -3 means they typed CR */
8350                 if (y < 0) return(y);   /* Other negatives are errors */
8351                 z |= y;                 /* OR the bit into the signal mask */
8352             }
8353             if ((y = cmcfm()) < 0) return(y);
8354             break;
8355         }
8356 
8357       default:                          /* Shouldn't happen */
8358         return(-2);
8359     } /* switch (cx) */
8360 
8361 /* Command is entered, now do it. */
8362 
8363     if (zz > -1L) {                     /* Time of day given? */
8364         x = zz;
8365         if (zz != (long) x) {
8366             printf(
8367 "Sorry, arithmetic overflow - hh:mm:ss not usable on this platform.\n"
8368                    );
8369             return(-9);
8370         }
8371     }
8372     if (sleepcan)
8373       concb((char)escape);              /* Ensure single-char wakeup */
8374 
8375     if (cx == XXMSL) {                  /* Millisecond sleep */
8376         msleep(zz < 0 ? x : x * 1000);
8377         return(success = 1);
8378     }
8379     if (cx == XXPAU && !sleepcan) {     /* SLEEP CANCELLATION is OFF */
8380         sleep(x);
8381         return(success = 1);
8382     }
8383     if (filewait) {                     /* FILE... */
8384         fs = zchki(tmpbuf);             /* Check if file exists */
8385         switch (filewait) {
8386           case WF_DEL:
8387             if (fs == -1)
8388               return(success = 1);
8389             break;
8390           case WF_MOD:
8391             if (fs == -1) {
8392                 printf("?File does not exit: %s\n",tmpbuf);
8393                 return(-9);
8394             }
8395             s = zfcdat(tmpbuf);         /* Get current modification date */
8396             if (!s) s = "";
8397             if (ckstrncpy(filedate,s,32) != 17) {
8398                 printf("?Can't get modification time: %s\n",tmpbuf);
8399                 return(-9);
8400             }
8401             break;
8402           case WF_CRE:
8403             if (fs > -1)
8404               return(success = 1);
8405             break;
8406         }
8407     }
8408     do {                                /* Polling loop */
8409         if (sleepcan) {                 /* Keyboard cancellation allowed? */
8410             if ((y = conchk()) > 0) {   /* Did they type something? */
8411                 kbchar = coninc(0);     /* Yes, get first char they typed */
8412                 debug(F000,"WAIT kbchar","",kbchar);
8413 #ifdef COMMENT
8414                 while (--y > 0)         /* Gobble the rest up */
8415                   coninc(0);
8416 #endif /* COMMENT */
8417                 return(success = 0);    /* And quit PAUSing or WAITing */
8418             }
8419         }
8420         if (filewait == 0) {
8421             if (cx == XXWAI) {          /* WAIT for modem signals */
8422                 if (z != 0) {
8423                     mdmsig = ttgmdm();  /* Get them. */
8424                     debug(F101,"WAIT ttgmdm","",mdmsig);
8425                     if (mdmsig < 0)     /* Failure to get them? */
8426                       return(success = 0); /* Fail. */
8427                     if ((mdmsig & z) == z) /* Got desired ones? */
8428                       return(success = 1); /* Succeed. */
8429                 } else if (x == 0)
8430                   return(success = 0);
8431             }
8432         } else {                        /* FILE... */
8433             fs = zchki(tmpbuf);         /* Get file status */
8434             if (filewait == WF_MOD) {   /* Wait for modification */
8435                 if (fs == -1)           /* Failure to get status */
8436                   return(success = 0);  /* so WAIT fails. */
8437                 s = zfcdat(tmpbuf);     /* Get current modification time */
8438                 if (!s) s = "";         /* And compare with the time */
8439                 if (strcmp(s,filedate)) /* when the WAIT started */
8440                   return(success = 1);
8441             } else if (filewait == WF_DEL) { /* Wait for deletion */
8442                 if (fs == -1)           /* If file doesn't exist, */
8443                   return(success = 1);  /* succeed. */
8444             } else if (filewait == WF_CRE) { /* Wait for creation */
8445                 if (fs != -1)           /* If file exists */
8446                   return(success = 1);  /* succeed. */
8447             }
8448         }
8449         if (x < 1)                      /* SLEEP/WAIT/PAUSE 0 */
8450           break;
8451         sleep(waitinterval);            /* No interrupt, sleep */
8452         x -= waitinterval;              /* Deduct sleep time */
8453     } while (x > 0);
8454 
8455     if (cx == XXWAI)                    /* WAIT time expired */
8456       success = (z == 0);               /* Succeed if no modem signals */
8457     else                                /* For SLEEP or PAUSE, success */
8458       success = (x == 0);               /* depends on whether it was */
8459     return(success);                    /* interrupted from the keyboard. */
8460 #endif /* OLDWAIT */
8461 }
8462 #endif /* NOSPL */
8463 
8464 #ifdef OS2ORUNIX
8465 _PROTOTYP(int zcmpfn,(char *, char *));
8466 #endif /* OS2ORUNIX */
8467 
8468 #ifndef NOFRILLS
8469 #ifdef NT
8470 int
dolink()8471 dolink() {
8472     /* Parse a file or a directory name */
8473     int i, x, z, listing = 0, havename = 0, wild = 0, rc = 1;
8474     struct FDB sw, fi;
8475 
8476     cmfdbi(&sw,                         /* 2nd FDB - optional /PAGE switch */
8477            _CMKEY,                      /* fcode */
8478            "Filename or switch",        /* hlpmsg */
8479            "",                          /* default */
8480            "",                          /* addtl string data */
8481            nqvswtab,                    /* addtl numeric data 1: tbl size */
8482            4,                           /* addtl numeric data 2: 4 = cmswi */
8483            xxstring,                    /* Processing function */
8484            qvswtab,                     /* Keyword table */
8485            &fi                          /* Pointer to next FDB */
8486            );
8487 
8488     cmfdbi(&fi,                         /* 1st FDB - file to type */
8489            _CMIFI,                      /* fcode */
8490            "",                          /* hlpmsg */
8491            "",                          /* default */
8492            "",                          /* addtl string data */
8493            3,                           /* addtl numeric data 1 */
8494            0,                           /* addtl numeric data 2 */
8495            xxstring,
8496            NULL,
8497            NULL
8498            );
8499 
8500     while (!havename) {
8501         x = cmfdb(&sw);                 /* Parse something */
8502         if (x < 0)                      /* Error */
8503           return(x);
8504         switch (cmresult.fcode) {
8505           case _CMKEY:
8506             switch (cmresult.nresult) {
8507               case DEL_LIS:
8508               case DEL_VRB:
8509                 listing = 1;
8510                 break;
8511               case DEL_NOL:
8512               case DEL_QUI:
8513                 listing = 0;
8514                 break;
8515             }
8516             break;
8517           case _CMIFI:
8518             s = cmresult.sresult;
8519             havename = 1;
8520             break;
8521           default:
8522             return(-2);
8523         }
8524     }
8525     wild = cmresult.nresult;            /* Source specification wild? */
8526 
8527     ckstrncpy(line,s,LINBUFSIZ);        /* Make a safe copy of source name */
8528     s = line;
8529 
8530     if (!wild)
8531       wild = iswild(line);
8532 
8533     p = tmpbuf;                         /* Place for new name */
8534     if ((x = cmofi(wild ? "Target directory" : "New name",
8535                    "",&s,xxstring)) < 0) { /* Get new name */
8536         if (x == -3) {
8537             printf("?%s required\n", wild ? "Target directory" : "New name");
8538             return(-9);
8539         } else return(x);
8540     }
8541     ckstrncpy(p,s,TMPBUFSIZ);           /* Make a safe copy of the new name */
8542     if ((y = cmcfm()) < 0) return(y);
8543 
8544     if (!wild) {                        /* Just one */
8545         if (listing) printf("%s => %s ",line,p);
8546         if (zlink(line,p) < 0) {
8547             if (listing) printf("(FAILED: %s\n",ck_errstr());
8548             rc = 0;
8549         } else {
8550             if (listing) printf("(OK)\n");
8551         }
8552         return(success = rc);
8553     }
8554     if (!isdir(p)) {                    /* Multiple */
8555         printf(                         /* if target is not a directory */
8556 "?Multiple source files not allowed if target is not a directory.\n");
8557         return(-9);
8558     }
8559 #ifdef COMMENT
8560     else {                              /* Show full path of target */
8561         char buf[CKMAXPATH];            /* (too much) */
8562         if (zfnqfp(p,CKMAXPATH,buf))
8563           ckstrncpy(tmpbuf,buf,TMPBUFSIZ);
8564     }
8565 #endif /* COMMENT */
8566 
8567 #ifdef VMS
8568     conres();                           /* Let Ctrl-C work. */
8569 #endif /* VMS */
8570     debug(F110,"dolink line",line,0);
8571 
8572 #ifdef ZXREWIND
8573     z = zxrewind();                     /* Rewind file list */
8574 #else
8575     z = nzxpand(s,0);                   /* Expand file list */
8576 #endif /* ZXREWIND */
8577     debug(F111,"dolink p",p,z);
8578 
8579 #ifdef UNIX
8580     if (wild && z > 1)
8581       sh_sort(mtchs,NULL,z,0,0,filecase); /* Alphabetize the filename list */
8582 #endif /* UNIX */
8583 
8584     while (z-- > 0) {
8585         if (!(z == 0 && !wild))
8586           znext(line);
8587         if (!line[0])
8588           break;
8589         if (listing) printf("%s => %s ",line,p);
8590         if (zlink(line,p) < 0) {
8591             if (listing) printf("(FAILED: %s\n",ck_errstr());
8592             rc = 0;
8593         } else {
8594             if (listing) printf("(OK)\n");
8595         }
8596     }
8597 #ifdef VMS
8598     concb((char)escape);
8599 #endif /* VMS */
8600     return(success = rc);
8601 }
8602 #endif /* NT */
8603 
8604 #ifdef ZCOPY
8605 int
docopy()8606 docopy() {
8607     int i, x, listing = 0, nolist = 0, havename = 0, getval;
8608     char c;
8609     struct FDB sw, fi;
8610     int overwrite = OVW_ALWAYS;
8611     int targetisdir = 0;
8612     int targetlen = 0;
8613     int appending = 0;
8614     int preserve = 0;
8615     int swapping = 0;
8616     int fromb64 = 0;
8617     int tob64 = 0;
8618     int wild = 0;
8619     int rc = 1;
8620 
8621     char newname[CKMAXPATH], * nm;
8622     nm = newname;
8623 
8624     cmfdbi(&sw,                         /* 1st FDB - switches */
8625            _CMKEY,                      /* fcode */
8626            "Filename or switch",        /* hlpmsg */
8627            "",                          /* default */
8628            "",                          /* addtl string data */
8629            ncopytab,                    /* addtl numeric data 1: tbl size */
8630            4,                           /* addtl numeric data 2: 4 = cmswi */
8631            xxstring,                    /* Processing function */
8632            copytab,                     /* Keyword table */
8633            &fi                          /* Pointer to next FDB */
8634            );
8635     cmfdbi(&fi,                         /* 2nd FDB - file to copy */
8636            _CMIFI,                      /* fcode */
8637            "",                          /* hlpmsg */
8638            "",                          /* default */
8639            "",                          /* addtl string data */
8640            0,                           /* addtl numeric data 1 */
8641            0,                           /* addtl numeric data 2 */
8642            xxstring,
8643            NULL,
8644            NULL
8645            );
8646 
8647     while (!havename) {
8648         x = cmfdb(&sw);                 /* Parse something */
8649         if (x < 0)                      /* Error */
8650           return(x);
8651         switch (cmresult.fcode) {
8652           case _CMKEY:
8653 	    c = cmgbrk();                   /* Get break character */
8654 	    if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
8655 		printf("?This switch does not take an argument\n");
8656 		rc = -9;
8657 		return(rc);
8658 	    }
8659 	    if (!getval && (cmgkwflgs() & CM_ARG)) {
8660 		printf("?This switch requires an argument\n");
8661 		rc = -9;
8662 		return(rc);
8663 	    }
8664             switch (cmresult.nresult) {
8665               case DEL_LIS:
8666               case DEL_VRB:
8667 		nolist = 0;
8668                 listing = 1;
8669                 break;
8670               case DEL_NOL:
8671               case DEL_QUI:
8672 		nolist = 1;
8673                 listing = 0;
8674                 break;
8675               case 999:
8676                 swapping = 1;
8677                 break;
8678               case 998:
8679                 appending = 1;
8680                 break;
8681 	      case 995:
8682 		preserve = 1;
8683                 break;
8684 	      case 994:
8685 		if ((x = cmkey(ovwtab,novwtab,
8686 			       "When to overwrite existing destination file",
8687 			       "",xxstring)) < 0)
8688 		  return(x);
8689 		overwrite = x;
8690                 break;
8691 #ifndef NOSPL
8692               case 997:
8693                 fromb64 = 1;
8694                 break;
8695               case 996:
8696                 tob64 = 1;
8697                 break;
8698 #endif /* NOSPL */
8699             }
8700             break;
8701           case _CMIFI:
8702             s = cmresult.sresult;
8703             havename = 1;
8704             break;
8705           default:
8706             return(-2);
8707         }
8708     }
8709     wild = cmresult.nresult;
8710     ckstrncpy(line,s,LINBUFSIZ);        /* Make a safe copy of source name */
8711     s = line;
8712     p = tmpbuf;                         /* Place for new name */
8713 
8714     /* Get destination name */
8715     if ((x = cmofi("destination name and/or directory",
8716 #ifdef UNIX
8717                    "."
8718 #else
8719                    ""
8720 #endif /* UNIX */
8721                    ,&s,xxstring)) < 0) {
8722         if (x == -3) {
8723             printf("?Name for destination file required\n");
8724             return(-9);
8725         } else return(x);
8726     }
8727     ckstrncpy(p,s,TMPBUFSIZ);           /* Safe copy of destination name */
8728     if ((y = cmcfm()) < 0) return(y);
8729     if (appending && swapping) {
8730         printf("?Sorry, /APPEND and /SWAP conflict\n");
8731         return(-9);
8732     }
8733 #ifdef COMMENT
8734 /*
8735   This unreasonably prevented "COPY /APPEND *.* bigfile" from concatenating
8736   a bunch of files into one big file.
8737 */
8738     if (appending && wild) {
8739         printf("?Sorry, /APPEND can be used only with single files\n");
8740         return(-9);
8741     }
8742 #endif /* COMMENT */
8743     targetisdir = isdir(p);
8744     x = strlen(p);
8745     if (targetisdir) {
8746 #ifdef UNIXOROSK
8747         if (p[x-1] != '/') {
8748             ckstrncat(p,"/",TMPBUFSIZ);
8749             x++;
8750         }
8751 #else
8752 #ifdef OS2
8753         if (p[x-1] != '/') {
8754             ckstrncat(p,"/",TMPBUFSIZ);
8755             x++;
8756         }
8757 #else
8758 #ifdef STRATUS
8759         if (p[x-1] != '>') {
8760             ckstrncat(p,">",TMPBUFSIZ);
8761             x++;
8762         }
8763 #else
8764 #ifdef datageneral
8765         if (p[x-1] != ':') {
8766             ckstrncat(p,":",TMPBUFSIZ);
8767             x++;
8768         }
8769 #else
8770         if (p[x-1] != '/') {
8771             ckstrncat(p,"/",TMPBUFSIZ);
8772             x++;
8773         }
8774 #endif /* datageneral */
8775 #endif /* STRATUS */
8776 #endif /* OS2 */
8777 #endif /* UNIXOROSK */
8778     }
8779     targetlen = x;
8780 
8781     if (!appending) {			/* If /APPEND not given */
8782 	if (wild && !targetisdir) {	/* No wildcards allowed */
8783 	    printf(			/* if target is not a directory */
8784 "?Multiple source files not allowed if target is not a directory.\n");
8785 	    return(-9);
8786 	}
8787     }
8788 
8789 #ifdef VMS
8790     conres();                           /* Let Ctrl-C work. */
8791 #endif /* VMS */
8792     debug(F110,"docopy line",line,0);
8793     debug(F110,"docopy p",p,0);
8794     debug(F110,"docopy nm",nm,0);
8795 
8796 #ifdef ZXREWIND
8797     z = zxrewind();                     /* Rewind file list */
8798 #else
8799     z = nzxpand(s,0);                   /* Expand file list */
8800 #endif /* ZXREWIND */
8801 
8802 #ifdef UNIX
8803     if (wild)
8804       sh_sort(mtchs,NULL,z,0,0,filecase); /* Alphabetize the filename list */
8805 #endif /* UNIX */
8806 
8807 #ifdef IKSD
8808     if (!targetisdir && zchki(p) > -1) { /* Destination file exists? */
8809         if (inserver && (!ENABLED(en_del)
8810 #ifdef CK_LOGIN
8811                          || isguest
8812 #endif /* CK_LOGIN */
8813                          )) {
8814             printf("?Sorry, overwriting existing files is disabled\n");
8815             return(-9);
8816         }
8817     }
8818 #endif /* IKSD */
8819 
8820     if (tob64 && fromb64) {             /* To and from B64 = no conversion */
8821         tob64 = 0;
8822         fromb64 = 0;
8823     }
8824     debug(F110,"COPY dest",p,0);
8825 
8826     while (z > 0) {
8827 
8828         znext(line);
8829         if (!line[0])
8830           break;
8831 
8832         errno = 0;                      /* Reset errno */
8833 
8834 	if (targetisdir) {
8835 	    zstrip(line,&nm);
8836 	    ckmakmsg(newname,CKMAXPATH,p,nm,NULL,NULL);
8837 	    nm = newname;
8838 	} else {
8839 	    nm = p;
8840 	}
8841 	if (overwrite) {		/* Overwrite checking? */
8842 	    if (zchki(nm) >= (CK_OFF_T)0) { /* Destination file exists? */
8843 
8844 		char d1[20], * d2;
8845 		char * n1, * n2;
8846 		int i, skip = 0;
8847 
8848 		i = strlen(line);	/* Isolate source filename */
8849 		for (; i >= 0; i--) {
8850 		    if (ISDIRSEP(line[i])) {
8851 			n1 = &line[i+1];
8852 			break;
8853 		    }
8854 		}
8855 		debug(F110,"COPY n1", n1, 0);
8856 		i = strlen(nm);		/* And destination filename */
8857 		for (; i >= 0; i--) {
8858 		    if (ISDIRSEP(nm[i])) {
8859 			n2 = &nm[i+1];
8860 			break;
8861 		    }
8862 		}
8863 		debug(F110,"COPY n2", n2, 0);
8864 		if (!strcmp(n1,n2)) {          	  /* Same name? */
8865 		    if (overwrite == OVW_NEVER) { /* Never overwrite? */
8866 			if (listing)	          /* Skip */
8867 			  if (listing) printf("%s => %s (SKIPPED)\n",line,nm);
8868 			continue;
8869 		    }
8870 		    ckstrncpy(d1,zfcdat(line),20); /* Source file timestamp */
8871 		    d2 = zfcdat(nm);	       /* Timestamp of dest file */
8872 		    x = strcmp(d1,d2);	       /* Compare them */
8873 		    if (((overwrite == OVW_NEWER) && (x < 0)) ||
8874 			((overwrite == OVW_OLDER) && (x > 0))) {
8875 			if (listing)
8876 			  if (listing) printf("%s => %s (SKIPPED)\n",line,nm);
8877 			continue;
8878 		    }
8879 		}
8880 	    }
8881 	}
8882         if (listing) printf("%s => %s ",line,nm);
8883 
8884         /* Straight copy */
8885         if (!swapping && !appending && !fromb64 && !tob64) {
8886             debug(F110,"COPY zcopy",line,0);
8887 
8888             if ((x = zcopy(line,p)) < 0) { /* Let zcopy() do it. */
8889 		debug(F111,"COPY not OK",line,x);
8890                 switch (x) {
8891                   case -2:
8892 		    if (listing)
8893 		      printf("(FAILED: Not a regular file)\n");
8894 		    else if (!nolist)
8895 		      printf("?Not a regular file - %s\n",line);
8896                     rc = 0;
8897                     break;
8898                   case -3:
8899 		    if (listing)
8900 		      printf("(FAILED: Not found or not accessible)\n");
8901 		    else if (!nolist)
8902 		      printf("?Not found or not accessible - %s\n",line);
8903                     rc = 0;
8904                     break;
8905                   case -4:
8906 		    if (listing)
8907 		      printf("(FAILED: Permission denied)\n");
8908 		    else if (!nolist)
8909 		      printf("?Permission denied - %s\n",line);
8910                     rc = 0;
8911                     break;
8912                   case -5:
8913 		    if (listing)
8914 		      printf("(Source and destination are the same file)\n");
8915 		    else if (!nolist)
8916 		      printf(
8917 			  "?Source and destination are the same file - %s\n",
8918 			  line
8919 			  );
8920                     break;
8921                   case -6:
8922 		    if (listing)
8923 		      printf("(FAILED: Input/Output error)\n");
8924 		    else if (!nolist)
8925 		      printf("?Input/Output error - %s\n",line);
8926                     rc = 0;
8927                     break;
8928                   case -7:
8929 		    if (listing)
8930 		      printf("(FAILED: %s - %s)\n",p,ck_errstr());
8931 		    else if (!nolist)
8932 		      printf("?%s - %s\n",ck_errstr(),p);
8933                     rc = 0;
8934                     break;
8935                   default:
8936 		    if (listing)
8937 		      printf("(FAILED: %s)\n",ck_errstr());
8938 		    else if (!nolist)
8939 		      printf("?%s\n",ck_errstr());
8940                     rc = 0;
8941                 }
8942             } else {			/* Regular copy succeeded */
8943 		debug(F110,"COPY OK..",newname,0);
8944 #ifndef NOXFER
8945 		if (preserve) {		/* Handle /PRESERVE */
8946 		    char * pstr = "";	/* File permissions string */
8947 		    struct zattr xx;	/* File attribute structure */
8948 		    extern char * cksysid;
8949 
8950 		    initattr(&xx);	/* Initialize the struct */
8951 
8952 		    xx.systemid.val = cksysid; /* Set our system ID */
8953 		    xx.systemid.len = (int)strlen(cksysid);
8954 #ifdef CK_PERMS
8955 		    pstr = zgperm(line); /* Get source file's permissions */
8956 #endif /* CK_PERMS */
8957 		    xx.lprotect.val = pstr;
8958 		    xx.lprotect.len = (int)strlen(pstr);
8959                     xx.gprotect.len = 0;
8960 		    xx.date.val = zfcdat(line);	/* Source file's timestamp */
8961 		    xx.date.len = (int)strlen(xx.date.val);
8962 		    if (zstime(nm,&xx,0) < 0) {
8963 			printf("?COPY /PRESERVE %s: %s\n",nm,ck_errstr());
8964 			rc = -9;
8965 		    }
8966 		}
8967 #endif	/* NOXFER */
8968 		if (listing && rc > -1)
8969 		  printf("(OK)\n");
8970             }
8971 
8972         } else {                        /* Special options */
8973 
8974             int prev, y, x = 0;         /* Variables needed for them */
8975             int i, t;
8976             char ibuf[100];
8977             char obuf[200];
8978             FILE * in = NULL;
8979             FILE * out = NULL;
8980 
8981             if ((in = fopen(line,"r")) == NULL) { /* Open input file */
8982                 if (listing)
8983 		  printf("(FAILED: %s)\n",ck_errstr());
8984 		else if (!nolist)
8985 		  printf("?%s - %s)\n",ck_errstr(),line);
8986                 rc = 0;
8987                 continue;
8988             }
8989             if (targetisdir) {          /* Target is directory */
8990                 char * buf = NULL;      /* so append this filename to it */
8991                 zstrip(line,&buf);
8992                 p[targetlen] = NUL;
8993                 if (buf)
8994                   ckstrncat(p,buf,TMPBUFSIZ);
8995             }
8996 #ifdef OS2ORUNIX
8997             if (zcmpfn(line,p)) {       /* Input and output are same file? */
8998                 if (listing)
8999                   printf("(FAILED: Source and destination identical)\n");
9000 		else if (!nolist)
9001                   printf("?Source and destination identical - %s\n", line);
9002                 rc = 0;
9003                 continue;
9004             }
9005 #endif /* OS2ORUNIX */
9006             if ((out = fopen(p, (appending ? "a" : "w"))) == NULL) {
9007                 fclose(in);
9008                 if (listing)
9009 		  printf("(FAILED: %s - %s)\n",p,ck_errstr());
9010 		else if (!nolist)
9011 		  printf("?%s - %s\n",p,ck_errstr());
9012                 rc = 0;
9013                 continue;
9014             }
9015 #ifndef NOSPL
9016             if (tob64) {                /* Converting to Base-64 */
9017 
9018                 debug(F110,"COPY tob64",line,0);
9019 
9020                 while (1) {             /* Loop... */
9021                     prev = x;
9022                     if ((x = fread(ibuf,1,54,in)) < 1) { /* EOF */
9023                         if (listing)
9024                           printf("(OK)\n");
9025                         break;
9026                     }
9027                     if (prev % 3) {
9028                         if (listing)
9029                           printf("(FAILED: Phase error at %d)\n",prev);
9030 			else if (!nolist)
9031                           printf("?Phase error at %d\n",prev);
9032                         rc = 0;
9033                         break;
9034                     }
9035                     if (swapping) {
9036                         if (x & 1) {
9037                             if (listing)
9038                               printf("(FAILED: Swap error)\n");
9039 			    else if (!nolist)
9040 			      printf("?Swap error\n");
9041                             rc = 0;
9042                             break;
9043                         }
9044                         for (i = 0; i < x; i+=2) {
9045                             t = ibuf[i];
9046                             ibuf[i] = ibuf[i+1];
9047                             ibuf[i+1] = t;
9048                         }
9049                     }
9050                     if ((y = b8tob64(ibuf,x,obuf,180)) < 0) {
9051                         if (listing)
9052                           printf("(FAILED: Encoding error)\n");
9053 			else if (!nolist)
9054 			  printf("?Encoding error\n");
9055                         rc = 0;
9056                         break;
9057                     }
9058                     fprintf(out,"%s\n",obuf);
9059                 }
9060 
9061             } else if (fromb64) {       /* Converting from Base 64 */
9062 
9063                 debug(F110,"COPY fromb64",line,0);
9064 
9065                 if ((out = fopen(p,appending ? "a" : "w")) == NULL) {
9066                     fclose(in);
9067                     if (listing)
9068 		      printf("(FAILED: %s - %s)\n",p,ck_errstr());
9069 		    else if (!nolist)
9070 		      printf("?%s - %s\n",p,ck_errstr());
9071                     rc = 0;
9072                     continue;
9073                 }
9074                 x = 1;
9075                 while (x) {
9076                     x = fread(ibuf,1,80,in);
9077                     if ((y = b64tob8(ibuf,x,obuf,80)) < 0) {
9078                         if (listing)
9079                           printf("(FAILED: Decoding error)\n");
9080 			else if (!nolist)
9081 			  printf("?Decoding error\n");
9082                         rc = 0;
9083                         break;
9084                     }
9085                     if (swapping) {
9086                         if (x & 1) {
9087                             if (listing)
9088                               printf("(FAILED: Swap error)\n");
9089 			    else if (!nolist)
9090 			      printf("?Swap error\n");
9091                             rc = 0;
9092                             break;
9093                         }
9094                         for (i = 0; i < y; i+=2) {
9095                             t = obuf[i];
9096                             obuf[i] = obuf[i+1];
9097                             obuf[i+1] = t;
9098                         }
9099                     }
9100                     if (y > 0) {
9101                         if (fwrite(obuf,1,y,out) < 1) {
9102                             if (listing)
9103                               printf("(FAILED: %s - %s)\n",p,ck_errstr());
9104 			    else if (!nolist)
9105 			      printf("?%s - %s\n",p,ck_errstr());
9106                             rc = 0;
9107                             break;
9108                         }
9109                     }
9110                 }
9111 
9112             } else
9113 #endif /* NOSPL */
9114 
9115             if (swapping) {             /* Swapping bytes */
9116 
9117                 CHAR c[3];
9118                 c[2] = NUL;
9119 
9120                 debug(F110,"COPY swapping",line,0);
9121 
9122                 while (1) {
9123                     x = fread((char *)c,1,2,in);
9124                     if (x < 1) {
9125                         if (listing)
9126                           printf("(OK)\n");
9127                         break;
9128                     } else if (x == 1) {
9129                         c[1] = c[0];
9130                         c[0] = NUL;
9131                         printf(
9132                             "(WARNING: Odd byte count)");
9133                         if (!listing) printf("\n");
9134                     }
9135                     if (fprintf(out,"%c%c",c[1],c[0]) == EOF) {
9136                         if (listing)
9137                           printf("(FAILED: %s - %s)\n",p,ck_errstr());
9138 			else if (!nolist)
9139                           printf("?%s - %s\n",p,ck_errstr());
9140                         rc = 0;
9141                         break;
9142                     }
9143                 }
9144 
9145             } else if (appending) {     /* Appending to target file */
9146 
9147                 char c;
9148 
9149                 debug(F110,"COPY appending",line,0);
9150 
9151                 while (1) {
9152                     x = fread(&c,1,1,in);
9153                     if (x < 1) {
9154                         if (listing)
9155                           printf("(OK)\n");
9156                         break;
9157                     }
9158                     if (fwrite(&c,1,1,out) < 1) {
9159                         if (listing)
9160                           printf("(FAILED: %s - %s)\n",p,ck_errstr());
9161 			else if (!nolist)
9162                           printf("?%s - %s\n",p,ck_errstr());
9163                         rc = 0;
9164                         break;
9165                     }
9166                 }
9167             }
9168             if (out) fclose(out);
9169             if (in) fclose(in);
9170         }
9171 #ifdef VMSORUNIX
9172         concb((char)escape);
9173 #endif /* VMSORUNIX */
9174     }
9175     if (rc > -1) success = rc;
9176     return(rc);
9177 }
9178 #endif /* ZCOPY */
9179 #endif /* NOFRILLS */
9180 
9181 #ifndef NOCSETS
9182 #ifndef NOUNICODE
9183 static struct keytab * xfcstab = NULL;	/* For RENAME /CONVERT: */
9184 static char cvtbufin[CKMAXPATH+8] = { NUL, NUL };
9185 static char cvtbufout[CKMAXPATH+8] = { NUL, NUL };
9186 static char * pcvtbufin = NULL;
9187 static char * pcvtbufout = NULL;
9188 
9189 static int				/* Input function xgnbyte() */
cvtfnin()9190 cvtfnin() {
9191     CHAR c;
9192     c = *pcvtbufin++;
9193     return(c ? c : -1);
9194 }
9195 
9196 _PROTOTYP(int cvtfnout,(char));		/* Output function for xpnbyte() */
9197 int
9198 #ifdef CK_ANSIC
cvtfnout(char c)9199 cvtfnout(char c)
9200 #else
9201 cvtfnout(c) char c;
9202 #endif	/* CK_ANSIC */
9203 {
9204     if (pcvtbufout - cvtbufout >= CKMAXPATH)
9205       return(-1);
9206     *pcvtbufout++ = c;
9207     *pcvtbufout = NUL;
9208     return(1);
9209 }
9210 
9211 /* Convert a string from any charset to any other charset */
9212 
9213 char *
cvtstring(s,csin,csout)9214 cvtstring(s,csin,csout) char * s; int csin, csout; {
9215     int c;
9216     extern CK_OFF_T ffc;
9217 
9218     ckstrncpy(cvtbufin,s,CKMAXPATH);	/* Put it in a public place */
9219     pcvtbufin = cvtbufin;		/* with public pointers */
9220     pcvtbufout = cvtbufout;
9221     *pcvtbufout = NUL;
9222 
9223     if (csin == csout)			/* If the two sets are the same */
9224       return((char *)cvtbufin);		/* don't bother converting */
9225 
9226     initxlate(csin,csout);		/* Initialize the translator */
9227 
9228     while ((c = xgnbyte(FC_UCS2,csin,cvtfnin)) > -1) { /* Loop thru string */
9229 	if (xpnbyte(c,TC_UCS2,csout,cvtfnout) < 0) {
9230 	    ffc = (CK_OFF_T)0;
9231 	    return("");
9232 	}
9233     }
9234     /* ffc is touched by xgnbyte() but this is not file transfer */
9235     /* so we have to undo it */
9236     ffc = (CK_OFF_T)0;
9237     return((char *)cvtbufout);
9238 }
9239 #endif /* NOUNICODE */
9240 #endif /* NOCSETS */
9241 
9242 #ifndef NORENAME
9243 #ifndef NOFRILLS
9244 #ifdef ZRENAME
9245 
9246 /* The RENAME command - expanded and improved in 8.0.212 April 2006 */
9247 
9248 static char * ren_sub[4] = { NULL,NULL,NULL,NULL }; /* For RENAME /REPLACE */
9249 
9250 int ren_list = 0;		        /* Default listing action for RENAME */
9251 int ren_coll = RENX_OVWR;		/* Default collision action */
9252 
9253 int
shorename()9254 shorename() {
9255     char * s;
9256     switch (ren_coll) {
9257       case RENX_FAIL: s = "fail"; break;
9258       case RENX_OVWR: s = "overwrite"; break;
9259       case RENX_SKIP: s = "proceed"; break;
9260     }
9261     printf(" rename collision: %s\n",s);
9262     printf(" rename list:      %s\n",showoff(ren_list));
9263     return(1);
9264 }
9265 
9266 int
setrename()9267 setrename() {				/* Parse SET RENAME options */
9268     int x, y;
9269     if ((x = cmkey(renamset,nrenamset,"","", xxstring)) < 0)
9270       return(x);
9271     switch (x) {
9272       case REN_OVW:			/* COLLISION */
9273 	if ((x = cmkey(r_collision,nr_collision,"","", xxstring)) < 0)
9274 	  return(x);
9275 	if ((y = cmcfm()) < 0)
9276 	  return(y);
9277 	ren_coll = x;
9278 	break;
9279       case DEL_LIS:			/* LIST */
9280 	return(seton(&ren_list));
9281     }
9282     return(success = 1);
9283 }
9284 
9285 /* Reverse a string - Assumes a single-byte character set */
9286 
9287 int
gnirts(s1,s2,len)9288 gnirts(s1, s2, len) char * s1, * s2; int len; {
9289     int n, m = 0;
9290     if (!s1)				/* Null source pointer, fail */
9291       return(0);
9292     n = (int) strlen(s1);
9293     if (n > len-1)			/* Source longer than dest, fail */
9294       return(0);
9295     s2[n--] = NUL;			/* Deposit null byte at end of dest */
9296     for (; n >= 0; n--) {		/* Copy the rest backwards */
9297 	*s2++ = s1[n];
9298 	m++;
9299     }
9300     return(m);
9301 }
9302 
9303 /*
9304   r e n a m e o n e
9305 
9306   Worker function to rename one file for dorenam() (below).
9307     old        = name of file or directory to be renamed
9308     new        = new name (not required for /UPPER, /LOWER, and /REPLACE)
9309     replacing  = 1 if doing string replacement on the name
9310     casing     = 1 if converting name to lowercase, 2 if to uppercase
9311     all        = if doing case conversion on all names, not just monocase ones
9312     converting = 1 if converting character sets
9313     cset1      = character set to convert from (File Character Set index)
9314     cset2      = character set to convert to (ditto, see ck?xla.h)
9315     listing    = 1 to show results of rename
9316     nolist     = 1 to be completely silent (don't even print error messages)
9317     op         = 1 means simulate, 2 means check for collision, 0 means rename
9318     size       = length of result buffer.
9319     collision  = action to take if destination file already exists:
9320                  0 = fail
9321                  1 = overwrite and succeed
9322                  2 = skip and succeed
9323   Returns:
9324      0: on failure to rename or when a forbidden collision would have occurred.
9325      1: on success (file was renamed or did not need to be renamed).
9326   Note:
9327      If this code is ever built on any platform that is not Unix, Windows,
9328      VMS, or OS/2, this routine might need some adjustment.
9329 */
9330 
9331 /* Opcodes for op... */
9332 #define REN_OP_SIM 1			/* Simulate */
9333 #define REN_OP_CHK 2			/* Check for collisions */
9334 
9335 static int
renameone(old,new,replacing,casing,all,converting,cset1,cset2,listing,nolist,op,size,collision)9336 renameone(old,new,
9337 	  replacing,casing,all,converting,cset1,cset2,
9338 	  listing,nolist,op,size,collision)
9339     char * old, * new;
9340     int replacing,casing,all,converting,cset1,cset2,
9341     listing,nolist,op,size,collision;
9342 {
9343     char buf[CKMAXPATH];		/* Temporary filename buffer */
9344     char out[CKMAXPATH];		/* Buffer for new name */
9345     char dir[CKMAXPATH];		/* Destination directory */
9346     char pat[CKMAXPATH];		/* Path segment on old filename */
9347 
9348     char * destdir;			/* Destination directory, if any */
9349     char * srcpath;			/* Source path, if any */
9350     int rc = 1, flag = 0, skip = 0;	/* Control */
9351     int honorcase = 0, replaced = 0;
9352     int anchor = 0;			/* 1 = beginning, 2 = end */
9353     int occur = 0;			/* Occurrence */
9354     int minus = 0;			/* Occurrence is negative */
9355     int allbut = 0;			/* Occurrence is "all but" */
9356     int arg2isfile = 0;			/* Arg2 ("new") is a filename */
9357 
9358     debug(F110,"RENAMEONE old",old,0);
9359     debug(F110,"RENAMEONE new",new,0);
9360     debug(F110,"RENAMEONE ren_sub[0]",ren_sub[0],0);
9361     debug(F110,"RENAMEONE ren_sub[1]",ren_sub[1],0);
9362     debug(F110,"RENAMEONE ren_sub[2]",ren_sub[2],0);
9363 
9364     if (op == REN_OP_SIM && !nolist)	/* For convenience */
9365       listing = 1;
9366 #ifndef NOSPL
9367     honorcase = inpcas[cmdlvl];		/* Inherit SET CASE value */
9368 #else
9369 #ifdef UNIX
9370     honorcase = 1;
9371 #else
9372     honorcase = 0;
9373 #endif	/* UNIX */
9374 #endif	/* NOSPL */
9375 
9376     if (!old) old = "";			/* In case of bad args */
9377     if (!new) new = "";
9378     if (!*old) return(success = 0);
9379     ckstrncpy(out,new,CKMAXPATH);	/* So we don't write into */
9380     new = out;				/* our argument... */
9381     size = CKMAXPATH;
9382 
9383     pat[0] = NUL;			/* Assume no path in source file.. */
9384     srcpath = pat;
9385     {
9386 	int n;				/* If the old name includes a path */
9387 	n = (int)strlen(old) - 1;	/* put it in a separate place.     */
9388 	for (; n >= 0; n--) {		/* We are renaming the file only.  */
9389 	    if (ISDIRSEP(old[n])) {
9390 		ckstrncpy(pat,old,CKMAXPATH);
9391 		pat[n+1] = NUL;
9392 		old = old+n+1;
9393 		break;
9394 	    }
9395 	}
9396     }
9397     debug(F110,"RENAMEONE old 2",old,0);
9398     debug(F110,"RENAMEONE pat 2",pat,0);
9399 
9400     dir[0] = NUL;			/* Assume no destination directory */
9401     destdir = dir;
9402     if (*new) {				/* If Arg2 given */
9403 	if (isdir(new)) {		/* If it's a directory */
9404 	    ckstrncpy(dir,new,CKMAXPATH); /* put it here */
9405 	} else {			/* otherwise */
9406 	    arg2isfile++;		/* flag that it's a filename */
9407 	}
9408     }
9409     if (!casing && !replacing && !converting) {
9410 	if (!*new)
9411 	  return(success = 0);
9412 	if (!arg2isfile) {		/* Destination is a directory? */
9413 	    if (!isdir(old)) {		/* and source is not? */
9414 #ifndef VMS
9415 		int n, x = 0;		/* Concatenate them */
9416 		if ((n = strlen(new)) > 0) /* so we can check for */
9417 		  if (ISDIRSEP(new[n-1]))  /* collisions. */
9418 		    x++;
9419 		ckmakmsg(buf,size,new,x ? "" : "/", old, "");
9420 #else
9421 		ckmakmsg(buf,size,new, old, NULL, NULL);
9422 #endif	/* VMS */
9423 		debug(F110,"RENAMEONE new new",new,0);
9424 		new = buf;
9425 		size = CKMAXPATH;
9426 	    }
9427 	}
9428     } else if (*new) {			/* Directory to move file to */
9429 	int n, x = 0;			/* after changing its name */
9430 	if (!isdir(new))
9431 	  return(success = 0);
9432 #ifndef VMS
9433 	if ((n = strlen(new)) > 0)
9434 	  if (ISDIRSEP(new[n-1]))
9435 	    x++;
9436 	ckmakmsg(dir,CKMAXPATH,new,x ? "" : "/", "", "");
9437 #else
9438 	ckstrncpy(dir,new,CKMAXPATH);
9439 #endif	/* VMS */
9440     }
9441 
9442 #ifndef NOCSETS
9443 #ifndef NOUNICODE
9444     if (converting) {
9445 	new = cvtstring(old,cset1,cset2);
9446     }
9447 #endif	/* NOUNICODE */
9448 #endif	/* NOCSETS */
9449 
9450     if (replacing) {			/* Replacing strings */
9451 	int todo = 0;
9452 	int len0, len1, len2;
9453 	char c, *p, *s, *bp[3];
9454 
9455 	bp[0] = old;			/* Original name */
9456 	bp[1] = ren_sub[0];		/* String to be replaced */
9457 	bp[2] = ren_sub[1];		/* What to replace it with */
9458 	if (!bp[2]) bp[2] = "";
9459 
9460 	len0 = (int)strlen(bp[0]);	/* length of original filename */
9461 	len1 = (int)strlen(bp[1]);	/* length of target substring */
9462 	len2 = (int)strlen(bp[2]);	/* Length of replacement string */
9463 
9464 	if (ren_sub[2]) {		/* Optional options */
9465 	    p = ren_sub[2];
9466 	    while ((c = *p++)) {
9467 		switch (c) {
9468 		  case '^':
9469 		    anchor = 1; occur = 0; minus = 0; allbut = 0; break;
9470 		  case '$':
9471 		    anchor = 2; occur = 0; minus = 0; allbut = 0; break;
9472 		  case 'A': honorcase = 1; minus = 0; allbut = 0; break;
9473 		  case 'a': honorcase = 0; minus = 0; allbut = 0; break;
9474 		  case '-': minus = 1; break;
9475 		  case '~': allbut = 1; break;
9476 		  default:
9477 		    if (isdigit(c)) {
9478 			occur = c - '0';
9479 			if (minus) occur = 0 - occur;
9480 			anchor = 0;
9481 		    }
9482 		    minus = 0;
9483 		}
9484 	    }
9485 	}
9486 	if (anchor) {			/* Anchored replacement... */
9487 	    y = len0 - len1;
9488 	    if (y > 0) {
9489 		int x;
9490 		switch (anchor) {
9491 		  case 1:		/* Anchored at beginning */
9492 		    if (!ckstrcmp(bp[1],bp[0],len1,honorcase)) {
9493 			x = ckstrncpy(new,bp[2],size);
9494 			(VOID) ckstrncpy(new+x,bp[0]+len1,size-x);
9495 			replaced = 1;
9496 		    }
9497 		    break;
9498 		  case 2:		/* Anchored at end */
9499 		    if (!ckstrcmp(bp[1],bp[0]+y,len1,honorcase)) {
9500 			x = ckstrncpy(new,bp[0],y+1);
9501 			(VOID) ckstrncpy(new+y,bp[2],size-x);
9502 			replaced = 1;
9503 		    }
9504 		    break;
9505 		}
9506 	    }
9507 	    if (!replaced) {
9508 		ckstrncpy(new,old,size); /* Keep old name */
9509 		replaced = 1;
9510 	    }
9511 	} else {			/* Replace all occurrences */
9512 	    int j, n = 0;		/* or a particular occurrence */
9513 	    char c;
9514 	    int x = 0;
9515 	    char * s0 = NULL, * s1 = NULL, * s2 = NULL;
9516 	    p = new;			/* Pointer to new name */
9517 
9518 	    if (occur < 0) {		/* nth occurrence from the right */
9519 		occur = 0 - occur;
9520 		s0 = (char *)malloc(len0+1); /* Reverse original string */
9521 		if (s0) {
9522 		    (VOID) gnirts(bp[0],s0,len0+1);
9523 		    bp[0] = s0;
9524 		} else return(0);
9525 		s1 = (char *)malloc(len1+1); /* Reverse target string */
9526 		if (s1) {
9527 		    (VOID) gnirts(bp[1],s1,len1+1);
9528 		    bp[1] = s1;
9529 		} else return(0);
9530 		s2 = (char *)malloc(len2+1); /* Reverse replacement string */
9531 		if (s2) {
9532 		    (VOID) gnirts(bp[2],s2,len2+1);
9533 		    bp[2] = s2;
9534 		} else return(0);
9535 		debug(F111,"RENAMEONE s0",s0,len0);
9536 		debug(F111,"RENAMEONE s1",s1,len1);
9537 		debug(F111,"RENAMEONE s2",s2,len2);
9538 	    }
9539 	    s = bp[0];			/* Pointer to old name */
9540 	    p = new;			/* Pointer to new name */
9541 	    j = len0 - len1 + 1;	/* How much to scan */
9542 	    while (j-- > 0) {		/* For each character... */
9543 		if (!ckstrcmp(bp[1],s,len1,honorcase)) { /* Match? */
9544 		    n++;		/* Occurrence counter */
9545 		    todo = (occur == 0) ||
9546 			(!allbut && n == occur) ||
9547 			(allbut && n != occur);
9548 		    if (!todo) {	/* Desired occurrence? */
9549 			size -= ckstrncpy(p,bp[1],size); /* No... */
9550 			p += len1;	/* Copy target string */
9551 			s += len1;	/* instead of replacement string */
9552 			continue;
9553 		    }
9554 		    if (len2) {		/* If replacement string not empty */
9555 			size -= ckstrncpy(p,bp[2],size); /* Copy it */
9556 			p += len2;
9557 		    }
9558 		    s += len1;		/* Advance source position */
9559 		} else {		/* No match */
9560 		    *p++ = *s++;	/* just copy this character */
9561 		    size--;
9562 		}
9563 	    }
9564 	    while ((*p++ = *s++));	/* Done copy the rest */
9565 	    replaced = 1;		/* Remember we changed the name */
9566 	    if (s0) {			/* Were we doing "all but"? */
9567 		debug(F110,"RENAMEONE new1",new,0);
9568 		x = (int)strlen(new);	/* Unreverse the result */
9569 		if ((p = (char *)malloc(x+2))) {
9570 		    (VOID) gnirts(new,p,x+2);
9571 		    debug(F110,"RENAMEONE new2",new,0);
9572 		    ckstrncpy(new,p,x+2);
9573 		    free(p);
9574 		}
9575 		if (s0) free(s0);	/* Free the temporary strings */
9576 		if (s1) free(s1);
9577 		if (s2) free(s2);
9578 		debug(F110,"RENAMEONE new3",new,0);
9579 	    }
9580 	}
9581     }
9582     if (casing) {			/* Changing case? */
9583 	char c, * t;			/* See if mixed case. */
9584 	if (!replaced)
9585 	  ckstrncpy(new,old,size);	/* Copy old name to new name */
9586 	t = new;
9587 	while ((c = *t++)) {
9588 	    if (islower(c)) flag |= 1; /* Have a lowercase letter */
9589 	    else if (isupper(c)) flag |= 2; /* Have an uppercase letter */
9590 	    if (flag == 3) break;	/* Have a mixed-case name */
9591 	}
9592 	if (all || flag < 3) {	/* Not skipping or not mixed case */
9593 	    if (casing == 1 && flag != 1) /* Change case to lower */
9594 	      (VOID) cklower(new);
9595 	    else if (casing == 2 && flag != 2) /* Change case to upper */
9596 	      (VOID) ckupper(new);
9597 	}
9598     }
9599     if (*destdir && !arg2isfile) {	/* Moving without renaming */
9600 	ckstrncat(srcpath,old,CKMAXPATH);
9601 	old = srcpath;
9602 	new = destdir;
9603     } else if (*destdir || *srcpath) {	/* Were there any pathnames? */
9604 	char tmp[CKMAXPATH];
9605 	ckmakmsg(tmp,CKMAXPATH,srcpath,old,NULL,NULL);
9606 	ckstrncpy(old,tmp,CKMAXPATH);
9607 	if (*destdir) {			/* Directory-to-move-to given? */
9608 	    ckstrncat(destdir,new,CKMAXPATH);
9609 	    new = destdir;
9610 	} else if (*srcpath && !arg2isfile) { /* Or was there a source path? */
9611 	    ckstrncat(srcpath,new,CKMAXPATH);
9612 	    new = srcpath;
9613 	}
9614     }
9615     skip = 0;				/* Can we skip this one? */
9616 #ifdef COMMENT
9617     if (casing && !replaced) {
9618 	skip = (((all == 0) && (flag == 3)) || (flag == casing)) ? 1 : 0;
9619 	if (!skip && destdir) skip = 0;
9620     }
9621 #endif	/* COMMENT */
9622     if (!skip) {
9623 	if (!ckstrcmp(old,new,-1,1))
9624 	  skip = 1;
9625     }
9626     if (op == 0 && !skip && (collision != RENX_OVWR)) {
9627 	if (zchki(new) > (CK_OFF_T)-1) { /* New file already exists?  */
9628 	    switch (collision) {	/* Yes, take specified action */
9629 	      case RENX_SKIP:		/* Skip this one and proceed  */
9630 		skip = 2;
9631 		break;
9632 	      case RENX_FAIL:		/* Or fail. */
9633 		skip = 3;
9634 		if (!listing && !nolist)
9635 		  printf("?File already exists: %s\n",new);
9636 	    }
9637 	}
9638     }
9639     debug(F110,"RENAMEONE new",new,0);
9640     debug(F101,"RENAMEONE flag","",flag);
9641     debug(F101,"RENAMEONE skip","",skip);
9642     debug(F100,"RENAMEONE ----------------","",0);
9643 
9644     if (skip == 3) {
9645 	if (listing) printf("%s => %s (SKIPPED: %s already exists)\n",
9646 			    old,new,new);
9647 	rc = 0;
9648     } else if (skip) {			/* Skipping this one */
9649 	if (listing) printf("%s => %s (%s)\n",
9650 			    old,new,
9651 			    (skip == 2) ? "COLLISION: SKIPPED" : "SKIPPED");
9652     } else {				/* Have to rename this one */
9653 	if (op == REN_OP_CHK) {		/* Checking for collisions */
9654 	    return((zchki(new) > (CK_OFF_T)-1) ? 0 : 1 );
9655 	} else if (op == REN_OP_SIM) {	/* Simulating */
9656 	    if (listing) printf("%s => %s (SIMULATED)\n",old,new);
9657 	} else {			/* Really renaming */
9658 	    if (listing) printf("%s => %s ",old,new);
9659 	    if (zrename(old,new) < 0) {
9660 		rc = 0;
9661 		if (listing)
9662 		  printf("(FAILED: %s)\n",ck_errstr());
9663 		else if (!nolist)
9664 		  printf("?%s\n",ck_errstr());
9665 	    } else {
9666 		if (listing) printf("(OK)\n");
9667 	    }
9668 	}
9669     }
9670     return(success = rc);  /* Succeeds also if nothing needed to be renamed */
9671 }
9672 
9673 int
dorenam()9674 dorenam() {
9675 #ifndef NOCSETS
9676 #ifndef NOUNICODE
9677     extern int nfilc;
9678     extern struct keytab fcstab[];
9679     extern struct csinfo fcsinfo[];
9680 #endif	/* NOUNICODE */
9681 #endif	/* NOCSETS */
9682     int cset1 = 0, cset2 = 0;
9683 
9684     int i, x, z, fn, listing = 0, havename = 0, wild = 0, rc = 1, noarg = 0;
9685     int nolist = 0, all = 0, casing = 0, replacing = 0, getval = 0, sim = 0;
9686     int converting = 0, collision = 0;
9687 
9688     char c;
9689     struct FDB sw, fi;
9690 
9691     collision = ren_coll;		/* Inherit SET RENAME COLLISION */
9692     listing = ren_list;			/* Inhereit SET RENAME LIST */
9693 
9694     if (ren_sub[0]) makestr(&(ren_sub[0]),NULL);
9695     if (ren_sub[1]) makestr(&(ren_sub[1]),NULL);
9696     if (ren_sub[2]) makestr(&(ren_sub[2]),NULL);
9697     line[0] = NUL;
9698     tmpbuf[0] = NUL;
9699 
9700     cmfdbi(&sw,                         /* 1st FDB - switches */
9701            _CMKEY,                      /* fcode */
9702            "Filename or switch",        /* hlpmsg */
9703            "",                          /* default */
9704            "",                          /* addtl string data */
9705            nrenamsw,			/* addtl numeric data 1: tbl size */
9706            4,                           /* addtl numeric data 2: 4 = cmswi */
9707            xxstring,                    /* Processing function */
9708            renamsw,                     /* Keyword table */
9709            &fi                          /* Pointer to next FDB */
9710            );
9711 
9712     cmfdbi(&fi,                         /* 2nd FDB - file or directory name */
9713            _CMIFI,                      /* fcode */
9714            "",                          /* hlpmsg */
9715            "",                          /* default */
9716            "",                          /* addtl string data */
9717            3,                           /* Flags */
9718            0,                           /* 0 = Parse file or directory names */
9719            xxstring,
9720            NULL,
9721            NULL
9722            );
9723 
9724     if (cmflgs == 1) {
9725 	printf("?File or directory name required\n");
9726 	return(-9);
9727     }
9728     while (!havename) {
9729 	noarg = 0;
9730         x = cmfdb(&sw);                 /* Parse something */
9731 	if (x == -3) {			/* They hit Enter prematurely */
9732 	    printf("?Command incomplete\n");
9733 	    return(-9);
9734 	} else if (x < 0) {		/* Other error */
9735 	    if (x == -1)
9736 	      return(x);
9737             if (iswild(atmbuf) && nzxpand(atmbuf,nzxopts) == 0)
9738               printf("?No files match: %s\n",brstrip(atmbuf));
9739             else if (zchki(atmbuf) == -1)
9740 	      printf("?File not found: %s\n",brstrip(atmbuf));
9741 	    else
9742 	      printf("?Error with switch or filename: %s\n",brstrip(atmbuf));
9743 	    return(-9);
9744 	}
9745 	fn = cmresult.nresult;		/* For brevity */
9746         switch (cmresult.fcode) {	/* Handle each kind of field */
9747           case _CMKEY:			/* Keyword (switch) */
9748             c = cmgbrk();
9749             if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
9750                 printf("?This switch does not take an argument\n");
9751                 return(-9);
9752             }
9753             if (!getval && (cmgkwflgs() & CM_ARG))
9754 	      noarg = 1;		/* Remember arg is missing */
9755             switch (cmresult.nresult) {	/* Handle the switch */
9756               case DEL_LIS:		/* /LIST */
9757               case DEL_VRB:		/* /VERBOSE */
9758                 listing = 1;
9759                 break;
9760               case DEL_NOL:		/* /NOLIST */
9761               case DEL_QUI:		/* /QUIET */
9762 		nolist = 1;
9763                 listing = 0;
9764                 break;
9765               case DEL_SIM:		/* /SIMULATE */
9766                 sim = 1;
9767                 break;
9768 	      case REN_UPP:		/* /UPPER: */
9769 	      case REN_LOW:		/* /LOWER */
9770 		all = 1;
9771 		if (!noarg) {
9772 		    if ((x = cmkey((fn == REN_UPP) ? r_upper : r_lower,
9773 				   2, "","all",xxstring)) < 0) {
9774 			if (x == -3)
9775 			  x = 1;
9776 			else
9777 			  return(x);
9778 		    }
9779 		    all = x;
9780 		}
9781 		/* 0 = don't convert; 1 = convert to lower; 2 = to upper */
9782 		casing = (fn == REN_UPP) ? 2 : 1;
9783 		converting = 0;
9784 		break;
9785 	      case REN_OVW:		/* /COLLISION */
9786 		if (!noarg) {
9787 		    if ((x = cmkey(r_collision,
9788 				   nr_collision,"","overwrite",xxstring))<0) {
9789 			if (x == -3)
9790 			  x = RENX_OVWR;
9791 			else
9792 			  return(x);
9793 		    }
9794 		    collision = x;
9795 		}
9796 		break;
9797 	      case REN_RPL:		/* /REPLACE: */
9798 		if (noarg) {
9799 		    printf("?This switch requires an argument\n");
9800 		    return(-9);
9801 		}
9802 		if ((x = cmfld("String to remove, or {{String1}{String2}}",
9803 			       "",&s,xxstring)) < 0) {
9804 		    if (x == -3) {
9805 			printf("?Target string required\n");
9806 			x = -9;
9807 		    }
9808 		    return(x);
9809 		}
9810 		if (s[0]) {
9811 		    if (s[0] == '{' && s[1] == '{') /* Get the list */
9812 		      makelist(s,ren_sub,3);
9813 		    else
9814 		      makestr(&(ren_sub[0]),s);
9815 		}
9816 		converting = 0;
9817 		break;
9818 
9819 	      case REN_SPA:		/* /FIXSPACES: */
9820 		if (!noarg)
9821 		  if ((x = cmfld("Character or string to replace spaces with",
9822 				 "_",&s,xxstring)) < 0)
9823 		    if (x == -3)
9824 		      s = "_";
9825 		    else
9826 		      return(x);
9827 		makestr(&(ren_sub[0])," ");
9828 		makestr(&(ren_sub[1]),noarg ? "_" : brstrip(s));
9829 		makestr(&(ren_sub[3]),NULL);
9830 		converting = 0;
9831 		break;
9832 
9833 #ifndef NOCSETS
9834 #ifndef NOUNICODE
9835 	      case REN_XLA:		/* /CONVERT:cset1:cset2 */
9836 		if (!xfcstab) {		/* Make a copy of the file charset */
9837 		    int i, x;		/* table with CM_ARG set for each */
9838 		    x = (nfilc + 1) * sizeof(struct keytab);
9839 		    xfcstab = (struct keytab *)malloc(x);
9840 		    if (!xfcstab) {
9841 			printf("?Memory allocation failure\n");
9842 			return(-9);
9843 		    }
9844 		    for (i = 0; i < nfilc; i++) {
9845 			xfcstab[i].kwd = fcstab[i].kwd;
9846 			xfcstab[i].kwval = fcstab[i].kwval;
9847 			xfcstab[i].flgs = fcstab[i].flgs | CM_ARG;
9848 		    }
9849 		}
9850 		if ((x = cmswi(xfcstab,nfilc,
9851 			       "Character-set of old name","",xxstring)) < 0) {
9852 		    if (x == -3) {
9853 			printf("?Pair of character-set names required\n");
9854 			return(-9);
9855 		    } else
9856 		      return(x);
9857 		}
9858 		cset1 = x;
9859 		c = cmgbrk();
9860 		if (!getval) {
9861 		    printf("?Secondcharacter-set name required\n");
9862 		    return(-9);
9863 		}
9864 		if ((x = cmkey(fcstab,nfilc,
9865 			       "Character-set of new name","",xxstring)) < 0) {
9866 		    if (x == -3) {
9867 			printf("?Second character-set name required\n");
9868 			return(-9);
9869 		    } else
9870 		      return(x);
9871 		}
9872 		cset2 = x;
9873 		if (casing)
9874 		  casing = 0;
9875 		if (ren_sub[0])
9876 		  makestr(&(ren_sub[0]),NULL);
9877 		if (ren_sub[1])
9878 		  makestr(&(ren_sub[1]),NULL);
9879 		if (ren_sub[2])
9880 		  makestr(&(ren_sub[2]),NULL);
9881 		converting = 1;
9882 		break;
9883 #endif	/* NOUNICODE */
9884 #endif	/* NOCSETS */
9885             }
9886             break;
9887           case _CMIFI:			/* File or directory name */
9888             s = cmresult.sresult;
9889             havename = 1;
9890             break;
9891           default:
9892 	    printf("?File or directory name required\n");
9893 	    return(-9);
9894         }
9895     }
9896     if (havename) {
9897 	ckstrncpy(line,s,LINBUFSIZ);	/* Make a safe copy of source name */
9898     } else {
9899 	printf("?Internal error\n");
9900 	return(-9);			/* Shouldn't happen */
9901     }
9902     wild = cmresult.nresult;            /* Source specification wild? */
9903     if (!wild)
9904       wild = iswild(line);
9905 
9906     debug(F111,"RENAME WILD",line,wild);
9907 
9908     p = tmpbuf;                         /* Place for new name */
9909     p[0] = NUL;
9910     replacing = ren_sub[0] ? 1 : 0;
9911 
9912 #ifdef COMMENT
9913     if (!(casing || replacing || converting)) {
9914 	if ((x = cmofi(wild ? "Target directory" : "New name",
9915 		       "",&s,xxstring)) < 0) { /* Get new name */
9916 	    if (x == -3) {
9917 		printf("?%s required\n",
9918 		       wild ? "Target directory" : "New name");
9919 		return(-9);
9920 	    } else return(x);
9921 	}
9922 	ckstrncpy(p,s,TMPBUFSIZ);	/* Make a safe copy of the new name */
9923     }
9924 #else
9925     if ((x = cmofi(wild ? "Target directory" : "New name",
9926 		   "",&s,xxstring)) < 0) { /* Get new name */
9927 	if (x == -3) {
9928 	    if (casing || replacing || converting) {
9929 		s = "";
9930 	    } else {
9931 		printf("?%s required\n",
9932 		       wild ? "Target directory" : "New name");
9933 		return(-9);
9934 	    }
9935 	} else return(x);
9936     }
9937     ckstrncpy(p,s,TMPBUFSIZ);		/* Make a safe copy of the new name */
9938 #endif	/* COMMENT */
9939 
9940     if ((y = cmcfm()) < 0) return(y);	/* Confirm the command */
9941 
9942 #ifdef COMMENT
9943 #ifndef NOUNICODE
9944     if (converting) {
9945 	printf(" From: %s\n",fcsinfo[cset1].keyword);
9946 	printf(" To:   %s\n",fcsinfo[cset2].keyword);
9947     }
9948 #endif	/* NOUNICODE */
9949     if (casing) {
9950 	printf("CASING: %s\n", (casing == 1) ? "LOWER" : "UPPER");
9951     }
9952     if (replacing) {
9953 	printf("REPLACING: '%s' with '%s'\n",
9954 	       ren_sub[0],
9955 	       ren_sub[1] ? ren_sub[1] : "");
9956     }
9957 #endif	/* COMMENT */
9958 
9959     s = line;
9960     if (!wild)				/* Just one */
9961       return(success =
9962 	     renameone(s,p,
9963 		       replacing,casing,all,converting,cset1,cset2,
9964 		       listing,nolist,sim,TMPBUFSIZ,collision));
9965 
9966     if (!casing && !replacing && !converting) {	/* Multiple files */
9967 	if (!isdir(p)) {
9968 	    printf(			/* if target is not a directory */
9969 "?Multiple source files not allowed if target is not a directory.\n");
9970 	    return(-9);
9971 	}
9972     }
9973 #ifdef COMMENT
9974     else {                              /* Show full path of target */
9975         char buf[CKMAXPATH];            /* (too much) */
9976         if (zfnqfp(p,CKMAXPATH,buf))
9977           ckstrncpy(tmpbuf,buf,TMPBUFSIZ);
9978     }
9979 #endif /* COMMENT */
9980 
9981 #ifdef VMS
9982     conres();                           /* Let Ctrl-C work. */
9983 #endif /* VMS */
9984     debug(F110,"dorename line",line,0);
9985 
9986 #ifdef ZXREWIND
9987     z = zxrewind();                     /* Rewind file list */
9988 #else
9989     z = nzxpand(s,0);                   /* Expand file list */
9990 #endif /* ZXREWIND */
9991     debug(F111,"dorename p",p,z);
9992 
9993 #ifdef UNIX
9994     if (wild && z > 1)
9995       sh_sort(mtchs,NULL,z,0,0,filecase); /* Alphabetize the filename list */
9996 #endif /* UNIX */
9997 
9998     /* For /COLLISION:FAIL make a silent pass to see if there would be any */
9999 
10000     if (collision == RENX_FAIL) {
10001 	int n = 0;
10002 	char line[CKMAXPATH+2];
10003 	while (z-- > 0) {
10004 	    if (!(z == 0 && !wild))
10005 	      znext(line);
10006 	    if (!line[0])
10007 	      break;
10008 	    if (!renameone((char *)line,p,
10009 			   replacing,casing,all,converting,cset1,cset2,
10010 			   0,1,REN_OP_CHK,TMPBUFSIZ,RENX_FAIL))
10011 	      n++;
10012 	}
10013 	if (n > 0) {
10014 	    printf("?Failed: %d file%s would be overwritten\n",
10015 		   n, (n != 1) ? "s" : "");
10016 #ifdef VMS
10017 	    concb((char)escape);
10018 #endif /* VMS */
10019 	    return(success = 0);
10020 	}
10021 	/* Get the file list back. */
10022 #ifdef ZXREWIND
10023 	z = zxrewind();
10024 #else
10025 	z = nzxpand(s,0);
10026 #endif /* ZXREWIND */
10027     }
10028     while (z-- > 0 && rc > 0) {
10029 	char line[CKMAXPATH+2];
10030         if (!(z == 0 && !wild))
10031           znext(line);
10032         if (!line[0])
10033           break;
10034 	rc = renameone((char *)line,p,
10035 		       replacing,casing,all,converting,cset1,cset2,
10036 		       listing,nolist,sim,TMPBUFSIZ,collision);
10037     }
10038 #ifdef VMS
10039     concb((char)escape);
10040 #endif /* VMS */
10041     return(success = rc);
10042 }
10043 #endif /* ZRENAME */
10044 #endif /* NOFRILLS */
10045 #endif /* NORENAME */
10046 
10047 #ifndef NOSPL
10048 
10049 /* Do the RETURN command */
10050 
10051 int
doreturn(s)10052 doreturn(s) char *s; {
10053     int x;
10054     extern int tra_asg;
10055     char * line, * lp;
10056 
10057     if (cmdlvl < 1) {
10058         printf("\n?Can't return from level %d\n",maclvl);
10059         return(success = 0);
10060     }
10061     line = malloc(LINBUFSIZ);
10062     if (line == NULL)
10063       return(success = 0);
10064     lp = line;                          /* Expand return value now */
10065     x = LINBUFSIZ-1;
10066     if (!s) s = "";
10067     debug(F110,"RETURN s",s,0);
10068     if (zzstring(s,&lp,&x) > -1) {
10069         s = line;
10070         debug(F110,"RETURN zzstring",s,0);
10071     }
10072 
10073     /* Pop from all FOR/WHILE/SWITCH/XIFs */
10074     while ((maclvl > 0) &&
10075            (m_arg[maclvl-1][0]) &&
10076            (cmdstk[cmdlvl].src == CMD_MD) &&
10077            (!strncmp(m_arg[maclvl-1][0],"_xif",4) ||
10078             !strncmp(m_arg[maclvl-1][0],"_for",4) ||
10079             !strncmp(m_arg[maclvl-1][0],"_swi",4) ||
10080             !strncmp(m_arg[maclvl-1][0],"_whi",4))) {
10081         debug(F111,"RETURN IF/FOR/WHI/SWI pop",m_arg[maclvl-1][0],maclvl);
10082         dogta(XXPTA);                   /* Put args back */
10083         popclvl();                      /* Pop up two levels */
10084         popclvl();
10085     }
10086     if (tra_asg) {                      /* If tracing show return value */
10087         if (*s)
10088           printf("<<< %s: \"%s\"\n", m_arg[maclvl][0], s);
10089         else
10090           printf("<<< %s: (null)\n", m_arg[maclvl][0]);
10091     }
10092     popclvl();                          /* Pop from enclosing TAKE or macro */
10093     debug(F111,"RETURN tolevel",s,maclvl);
10094     if (!s) s = "";
10095     if (!*s) s = NULL;
10096     makestr(&(mrval[maclvl+1]),s);      /* Set the RETURN value */
10097     free(line);
10098     return(success = 1);                /* Macro succeeds if we RETURN */
10099 }
10100 #endif /* NOSPL */
10101 
10102 #ifndef NOSPL
10103 /* Do the OPEN command */
10104 
10105 int
doopen()10106 doopen()  {                             /* OPEN { append, read, write } */
10107     int x, y, z = 0; char *s;
10108     static struct filinfo fcb;          /* (must be static) */
10109     if ((x = cmkey(opntab,nopn,"mode","",xxstring)) < 0) {
10110         if (x == -3) {
10111             printf("?Mode required\n");
10112             return(-9);
10113         } else return(x);
10114     }
10115     switch (x) {
10116       case OPN_FI_R:                    /* Old file (READ) */
10117         if (chkfn(ZRFILE) > 0) {
10118             printf("?Read file already open\n");
10119             return(-2);
10120         }
10121         if ((z = cmifi("File to read","",&s,&y,xxstring)) < 0) {
10122             if (z == -3) {
10123                 printf("?Input filename required\n");
10124                 return(-9);
10125             } else return(z);
10126         }
10127         if (y) {                                /* No wildcards allowed */
10128             printf("\n?Please specify a single file\n");
10129             return(-2);
10130         }
10131         ckstrncpy(line,s,LINBUFSIZ);
10132         if ((int)strlen(line) < 1) return(-2);
10133         if ((z = cmnum("buffer size","4096",10,&y,xxstring)) < 0)
10134           return(z);
10135         if (y < 1) {
10136             printf("?Positive number required\n");
10137             return(-9);
10138         }
10139         if ((z = cmcfm()) < 0) return(z);
10140         readblock = y;
10141         if (readbuf)
10142           free((char *)readbuf);
10143         if (!(readbuf = (CHAR *) malloc(readblock+1))) {
10144             printf("?Can't allocate read buffer\n");
10145             return(-9);
10146         }
10147         return(success = zopeni(ZRFILE,line));
10148 
10149 #ifndef MAC
10150 #ifndef NOPUSH
10151       case OPN_PI_R:                    /* Pipe/Process (!READ) */
10152         if (nopush) {
10153             printf("?Read from pipe disabled\n");
10154             return(success=0);
10155         }
10156         if (chkfn(ZRFILE) > 0) {
10157             printf("?Read file already open\n");
10158             return(-2);
10159         }
10160         if ((y = cmtxt("System command to read from","",&s,xxstring)) < 0) {
10161             if (y == -3) {
10162                 printf("?Command name required\n");
10163                 return(-9);
10164             } else return(y);
10165         }
10166         ckstrncpy(line,brstrip(s),LINBUFSIZ);
10167         if (!line[0]) return(-2);
10168         if ((y = cmcfm()) < 0) return(y);
10169         if (!readbuf) {
10170             if (!(readbuf = (CHAR *) malloc(readblock+1))) {
10171                 printf("?Can't allocate read buffer\n");
10172                 return(-9);
10173             }
10174         }
10175         return(success = zxcmd(ZRFILE,line));
10176 
10177       case OPN_PI_W:                    /* Write to pipe */
10178         if (nopush) {
10179             printf("?Write to pipe disabled\n");
10180             return(success=0);
10181         }
10182         if (chkfn(ZWFILE) > 0) {
10183             printf("?Write file already open\n");
10184             return(-2);
10185         }
10186         if ((y = cmtxt("System command to write to","",&s,xxstring)) < 0) {
10187             if (y == -3) {
10188                 printf("?Command name required\n");
10189                 return(-9);
10190             } else return(y);
10191         }
10192         ckstrncpy(line,brstrip(s),LINBUFSIZ);
10193         if (!line[0]) return(-2);
10194         if ((y = cmcfm()) < 0) return(y);
10195         success = zxcmd(ZWFILE,line);
10196         if (!success && msgflg)
10197           printf("Can't open process for writing: %s\n",line);
10198         return(success);
10199 #endif /* NOPUSH */
10200 #endif /* MAC */
10201 
10202       case OPN_FI_W:                    /* New file (WRITE) */
10203       case OPN_FI_A:                    /* (APPEND) */
10204         if ((z = cmofi("Name of local file to create","",&s,xxstring)) < 0) {
10205             if (z == -3) {
10206                 printf("?Filename required\n");
10207                 return(-9);
10208             } else return(z);
10209         }
10210         if (z == 2) {
10211             printf("?Sorry, %s is a directory name\n",s);
10212             return(-9);
10213         }
10214         if (chkfn(ZWFILE) > 0) {
10215             printf("?Write/Append file already open\n");
10216             return(-2);
10217         }
10218         fcb.bs = fcb.cs = fcb.rl = fcb.fmt = fcb.org = fcb.cc = fcb.typ = 0;
10219         fcb.lblopts = 0;
10220         fcb.dsp = (x == OPN_FI_W) ? XYFZ_N : XYFZ_A; /* Create or Append */
10221         ckstrncpy(line,s,LINBUFSIZ);
10222         if ((int)strlen(line) < 1) return(-2);
10223         if ((y = cmcfm()) < 0) return(y);
10224         return(success = zopeno(ZWFILE,line,NULL,&fcb));
10225 
10226 #ifndef NOLOCAL
10227       case OPN_SER:                     /* OPEN PORT or LINE */
10228       case OPN_NET: {                   /* OPEN HOST */
10229           extern int didsetlin, ttnproto;
10230           if (x == OPN_NET) {
10231               z = ttnproto;
10232               ttnproto = NP_NONE;
10233           }
10234           if ((y = setlin((x == OPN_SER) ? XYLINE : XYHOST, 1, 0)) < 0) {
10235               if (x == OPN_NET)
10236                 ttnproto = z;
10237               success = 0;
10238           }
10239           didsetlin++;
10240           return(y);
10241       }
10242 #endif /* NOLOCAL */
10243 
10244       default:
10245         printf("?Not implemented");
10246         return(-2);
10247     }
10248 }
10249 #endif /* NOSPL */
10250 
10251 #ifndef NOXFER
10252 /*  D O X G E T  --  GET command parser with switches  */
10253 
10254 #ifdef CK_LABELED
10255 int g_lf_opts = -1;
10256 extern int lf_opts;
10257 #endif /* CK_LABELED */
10258 
10259 int
doxget(cx)10260 doxget(cx) int cx; {
10261     extern int                          /* External variables we need */
10262 #ifdef RECURSIVE
10263       recursive,
10264 #endif /* RECURSIVE */
10265       xfermode, fdispla, protocol, usepipes,
10266       g_binary, g_xfermode, g_displa, g_rpath, g_usepipes;
10267     extern char * rcv_move;             /* Directory to move new files to */
10268     extern char * rcv_rename;           /* What to rename new files to */
10269     extern char * rcvexcept[];          /* RECEIVE / GET exception list */
10270     int opkt  =  0;                     /* Flag for O-Packet needed */
10271 
10272 #ifdef PIPESEND
10273     extern int pipesend;
10274     extern char * rcvfilter;
10275 #endif /* PIPESEND */
10276     extern struct keytab rpathtab[];
10277     extern int nrpathtab;
10278     extern CK_OFF_T calibrate;
10279     int asname = 0;                     /* Flag for have as-name */
10280     int konly = 0;                      /* Kermit-only function */
10281     int c, i, n, confirmed = 0;         /* Workers */
10282     int getval = 0;                     /* Whether to get switch value */
10283     int rcvcmd = 0;                     /* Whether it is the RECEIVE command */
10284     int mget = 0;                       /* Whether it is the MGET command */
10285     struct stringint pv[SND_MAX+1];    /* Temporary array for switch values */
10286     struct FDB sw, fl, cm;              /* FDBs for each parse function */
10287     char * cmdstr = "this command";
10288 
10289 #ifdef NEWFTP
10290     if (cx == XXGET || cx == XXREGET || cx == XXMGET || cx == XXRETR) {
10291         extern int ftpget;
10292         extern int ftpisopen();
10293         if ((ftpget == 1) || ((ftpget == 2) && ftpisopen())) {
10294 	    int x;
10295 	    x = doftpget(cx,0);
10296 	    debug(F101,"doftpget return","",x);
10297 	    if (x > -1)
10298 	      success = x;
10299 	    debug(F101,"doftpget success","",success);
10300 	    return(x);
10301 	}
10302     }
10303 #endif /* NEWFTP */
10304 
10305     debug(F101,"xget cx","",cx);
10306 
10307     oopts = -1;
10308     omode = -1;
10309 
10310     for (i = 0; i <= SND_MAX; i++) {    /* Initialize switch values */
10311         pv[i].sval = NULL;
10312         pv[i].ival = -1;
10313         pv[i].wval = (CK_OFF_T)-1;
10314     }
10315     /* Preset switch values based on top-level command that called us */
10316 
10317     switch (cx) {
10318       case XXREC:                       /* RECEIVE */
10319         cmdstr = "RECEIVE";
10320         rcvcmd = 1; break;
10321       case XXGET:                       /* GET */
10322         cmdstr = "GET";
10323         konly = 1;
10324         break;
10325 #ifdef CK_RESEND
10326       case XXREGET:                     /* REGET */
10327         cmdstr = "REGET";
10328         konly = 1;
10329         pv[SND_BIN].ival = 1;           /* Implies /BINARY */
10330         pv[SND_RES].ival = 1; break;
10331 #endif /* CK_RESEND */
10332       case XXRETR:                      /* RETRIEVE */
10333         cmdstr = "RETRIEVE";
10334         konly = 1;
10335         pv[SND_DEL].ival = 1; break;
10336 #ifdef PIPESEND
10337       case XXCREC:                      /* CRECEIVE */
10338         cmdstr = "CRECEIVE";
10339         konly = 1;
10340         rcvcmd = 1;
10341         pv[SND_CMD].ival = 1; break;
10342       case XXCGET:                      /* CGET */
10343         cmdstr = "CGET";
10344         konly = 1;
10345         pv[SND_CMD].ival = 1; break;
10346 #endif /* PIPESEND */
10347 #ifndef NOMGET
10348       case XXMGET:                      /* MGET */
10349         cmdstr = "MGET";
10350         konly = 1;
10351         mget = 1; break;
10352 #endif /* NOMGET */
10353     }
10354     debug(F111,"xget rcvcmd",cmdstr,rcvcmd);
10355     debug(F101,"xget konly","",konly);
10356 
10357 #ifdef CK_XYZ
10358     if (!rcvcmd && protocol != PROTO_K) {
10359         printf("?Sorry, %s works only with Kermit protocol\n",cmdstr);
10360         return(-9);
10361     }
10362 #endif /* CK_XYZ */
10363 
10364     /* Set up chained parse functions... */
10365 
10366     cmfdbi(&sw,                         /* First FDB - command switches */
10367            _CMKEY,                      /* fcode */
10368            rcvcmd ?
10369            "Optional name/template to store incoming files under, or switch" :
10370            "Remote filename, or switch", /* hlpmsg */
10371            "",                          /* default */
10372            "",                          /* addtl string data */
10373            rcvcmd ? nrcvtab : ngettab,  /* addtl numeric data 1: tbl size */
10374            4,                           /* addtl numeric data 2: 4 = cmswi */
10375            xxstring,                    /* Processing function */
10376            rcvcmd ? rcvtab : gettab,    /* Keyword table */
10377            &fl                          /* Pointer to next FDB */
10378            );
10379     if (rcvcmd || mget)                 /* RECEIVE or MGET */
10380       cmfdbi(&fl,
10381            _CMTXT,                      /* fcode */
10382            rcvcmd ?                     /* hlpmsg */
10383              "Output filename or Command" : /* Output filename */
10384              "File(s) to GET",              /* Files we are asking for */
10385            "",                          /* default */
10386            "",                          /* addtl string data */
10387            0,                           /* addtl numeric data 1 */
10388            0,                           /* addtl numeric data 2 */
10389 #ifdef COMMENT
10390 #ifdef CK_XYZ
10391            (protocol == PROTO_X || protocol == PROTO_XC) ?
10392              xxstring :
10393              (rcvcmd ? (xx_strp)0  : xxstring)
10394 #else
10395            rcvcmd ? (xx_strp)0  : xxstring /* Processing function */
10396 #endif /* CK_XYZ */
10397 #else  /* COMMENT */
10398 	   xxstring			/* Always evaluate - fdc 2006/02/01 */
10399 #endif	/* COMMENT */
10400              ,
10401            NULL,
10402            &cm
10403            );
10404     else
10405       cmfdbi(&fl,                       /* Remote filename or command */
10406            _CMFLD,                      /* fcode */
10407            "Remote filename",           /* hlpmsg */
10408            "",                          /* default */
10409            "",                          /* addtl string data */
10410            0,                           /* addtl numeric data 1 */
10411            0,                           /* addtl numeric data 2 */
10412            xxstring,
10413            NULL,
10414            &cm
10415            );
10416     cmfdbi(&cm,                         /* Confirmation */
10417            _CMCFM,                      /* fcode */
10418            "",                          /* hlpmsg */
10419            "",                          /* default */
10420            "",                          /* addtl string data */
10421            0,                           /* addtl numeric data 1 */
10422            0,                           /* addtl numeric data 2 */
10423            NULL,
10424            NULL,
10425            NULL
10426            );
10427 
10428     /* (See doxsend() for fuller commentary) */
10429 
10430     while (1) {                         /* Parse 0 or more switches */
10431         x = cmfdb(&sw);                 /* Parse something */
10432         debug(F101,"xget cmfdb","",x);
10433         if (x < 0)                      /* Error */
10434           goto xgetx;                   /* or reparse needed */
10435         if (cmresult.fcode != _CMKEY)   /* Break out if not a switch */
10436           break;
10437         c = cmgbrk();                   /* Get break character */
10438         if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) {
10439             printf("?This switch does not take an argument\n");
10440             x = -9;
10441             goto xgetx;
10442         }
10443         if (!getval && (cmgkwflgs() & CM_ARG)) {
10444             printf("?This switch requires an argument\n");
10445             x = -9;
10446             goto xgetx;
10447         }
10448         n = cmresult.nresult;           /* Numeric result = switch value */
10449         debug(F101,"xget switch","",n);
10450 
10451         switch (n) {                    /* Process the switch */
10452 #ifdef PIPESEND
10453           case SND_CMD:                 /* These take no args */
10454             if (nopush) {
10455                 printf("?Sorry, system command access is disabled\n");
10456                 x = -9;
10457                 goto xgetx;
10458             } else if (rcvfilter) {
10459                 printf(
10460 "?Sorry, no GET /COMMAND when RECEIVE FILTER selected\n");
10461                 x = -9;
10462                 goto xgetx;
10463             }
10464             if (rcvcmd)
10465               sw.hlpmsg = "Command, or switch"; /* Change help message */
10466             /* Fall thru... */
10467 #endif /* PIPESEND */
10468 
10469           case SND_REC:                 /* /RECURSIVE */
10470             pv[SND_PTH].ival = PATH_REL; /* Implies relative pathnames */
10471             pv[n].ival = 1;             /* Set the recursive flag */
10472             break;
10473 
10474           case SND_RES:                 /* /RECOVER */
10475             pv[SND_BIN].ival = 1;       /* Implies /BINARY */
10476             pv[n].ival = 1;             /* Set the resend flag */
10477             break;
10478 
10479           case SND_DEL:                 /* /DELETE */
10480           case SND_SHH:                 /* /QUIET */
10481           case SND_CAL:                 /* /CALIBRATE */
10482           case SND_XPA:                 /* /TRANSPARENT */
10483             pv[n].ival = 1;             /* Just set the appropriate flag */
10484             break;
10485 
10486           case SND_PIP:                 /* /PIPES:{ON,OFF} */
10487             if (!getval) {
10488                 pv[n].ival = 1;
10489                 break;
10490             }
10491             if ((x = cmkey(onoff,2,"","on",xxstring)) < 0)
10492               goto xgetx;
10493             if (!nopush)
10494               pv[n].ival = x;
10495             break;
10496 
10497           /* File transfer modes - each undoes the others */
10498 
10499           case SND_BIN:                 /* Binary */
10500           case SND_TXT:                 /* Text */
10501           case SND_IMG:                 /* Image */
10502           case SND_LBL:                 /* Labeled */
10503             pv[SND_BIN].ival = 0;       /* Unset all */
10504             pv[SND_TXT].ival = 0;
10505             pv[SND_IMG].ival = 0;
10506             pv[SND_LBL].ival = 0;
10507             pv[n].ival = 1;             /* Set the requested one */
10508             break;
10509 
10510           case SND_EXC:                 /* Excludes */
10511             if (!getval) break;
10512             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
10513                 if (x == -3) {
10514                     printf("?Pattern required\n");
10515                     x = -9;
10516                 }
10517                 goto xgetx;
10518             }
10519             if (pv[n].sval) free(pv[n].sval);
10520             y = strlen(s);
10521             if (y > 256) {
10522                 printf("?Pattern too long - 256 max\n");
10523                 x = -9;
10524                 goto xgetx;
10525             }
10526             pv[n].sval = malloc(y+1);
10527             if (pv[n].sval) {
10528                 strcpy(pv[n].sval,s);   /* safe */
10529                 pv[n].ival = 1;
10530             }
10531             break;
10532 
10533 #ifdef COMMENT
10534           /* Not implemented */
10535           case SND_PRI:                 /* GET to printer */
10536             pv[n].ival = 1;
10537             if (!getval) break;
10538             if ((x = cmfld("Print options","",&s,xxstring)) < 0)
10539               goto xgetx;
10540             pv[n].sval = malloc((int)strlen(s)+1);
10541             if (pv[n].sval)
10542               strcpy(pv[n].sval,s);     /* safe */
10543             break;
10544 #endif /* COMMENT */
10545 
10546           case SND_MOV:                 /* MOVE after */
10547           case SND_REN:                 /* RENAME after */
10548             if (!getval) break;
10549             if ((x = cmfld(n == SND_MOV ?
10550            "device and/or directory for source file after sending" :
10551            "new name for source file after sending",
10552                            "",
10553                            &s,
10554                            n == SND_MOV ? xxstring : NULL
10555                            )) < 0) {
10556                 if (x == -3) {
10557                     printf("%s\n", n == SND_MOV ?
10558                            "?Destination required" :
10559                            "?New name required"
10560                            );
10561                     x = -9;
10562                 }
10563                 goto xgetx;
10564             }
10565             if (pv[n].sval) {
10566                 free(pv[n].sval);
10567                 pv[n].sval = NULL;
10568             }
10569             s = brstrip(s);
10570             y = strlen(s);
10571             if (y > 0) {
10572                 pv[n].sval = malloc(y+1);
10573                 if (pv[n].sval) {
10574                     strcpy(pv[n].sval,s); /* safe */
10575                     pv[n].ival = 1;
10576                 }
10577             }
10578             break;
10579 
10580           case SND_ASN:                 /* As-name */
10581             if (!getval) break;
10582             if (mget) {
10583                 printf("?Sorry, as-name not allowed with MGET\n");
10584                 x = -9;
10585                 goto xgetx;
10586             }
10587             if (
10588 #ifdef COMMENT
10589 		(x = cmfld("Name to store it under","",&s,NULL))
10590 #else
10591 		(x = cmfld("Name to store it under","",&s,xxstring))
10592 #endif	/* COMMENT */
10593 		< 0)
10594               goto xgetx;
10595             s = brstrip(s);
10596             if ((y = strlen(s)) > 0) {
10597                 if (pv[n].sval) free(pv[n].sval);
10598                 pv[n].sval = malloc(y+1);
10599                 if (pv[n].sval) {
10600                     strcpy(pv[n].sval,s); /* safe */
10601                     pv[n].ival = 1;
10602                 }
10603             }
10604             break;
10605 
10606 #ifdef PIPESEND
10607           case SND_FLT:                 /* Filter */
10608             debug(F101,"xget /filter getval","",getval);
10609             if (!getval) break;
10610             if ((x = cmfld("Filter program to receive through",
10611                            "",&s,NULL)) < 0) {
10612                 if (x == -3)
10613                   s = "";
10614                 else
10615                   goto xgetx;
10616             }
10617             if (*s) s = brstrip(s);
10618             y = strlen(s);
10619             for (x = 0; x < y; x++) {   /* Make sure they included "\v(...)" */
10620                 if (s[x] != '\\') continue;
10621                 if (s[x+1] == 'v') break;
10622             }
10623             if (x == y) {
10624                 printf(
10625                 "?Filter must contain a replacement variable for filename.\n"
10626                        );
10627                 x = -9;
10628                 goto xgetx;
10629             }
10630             pv[n].ival = 1;
10631             if (pv[n].sval) {
10632                 free(pv[n].sval);
10633                 pv[n].sval = NULL;
10634             }
10635             if ((y = strlen(s)) > 0) {
10636                 if ((pv[n].sval = malloc(y+1)))
10637                   strcpy(pv[n].sval,s); /* safe */
10638             }
10639             break;
10640 #endif /* PIPESEND */
10641 
10642           case SND_PTH:                 /* Pathnames */
10643             if (!getval) {
10644                 pv[n].ival = PATH_REL;
10645                 break;
10646             }
10647             if ((x = cmkey(rpathtab,nrpathtab,"","on",xxstring)) < 0)
10648               goto xgetx;
10649             pv[n].ival = x;             /* Ditto */
10650             break;
10651 
10652           case SND_NAM:                 /* Filenames */
10653             if (!getval) break;
10654             if ((x = cmkey(fntab,nfntab,"","converted",xxstring)) < 0)
10655               goto xgetx;
10656             pv[n].ival = x;
10657             break;
10658 
10659           case SND_PRO:                 /* Protocol to use */
10660             if (!getval) break;
10661             if ((x = cmkey(protos,nprotos,"File-transfer protocol","",
10662                            xxstring)) < 0) {
10663                 if (x == -3)
10664                   x = 0;
10665                 else
10666                   goto xgetx;
10667             }
10668             debug(F111,"xget /proto",atmbuf,x);
10669             pv[n].ival = x;
10670             if (konly && x != PROTO_K) {
10671                 printf(
10672 "?Sorry, this command works only with Kermit protocol\n"
10673                        );
10674                 x = -9;
10675                 goto xgetx;
10676             }
10677             break;
10678 
10679           default:
10680             printf("?Unexpected switch value - %d\n",cmresult.nresult);
10681             x = -9;
10682             goto xgetx;
10683         }
10684     }
10685     debug(F101,"xget cmresult fcode","",cmresult.fcode);
10686 
10687     cmarg = line;                       /* Initialize string pointers */
10688     cmarg2 = tmpbuf;
10689     asname = 0;
10690     line[0] = NUL;                      /* and buffers. */
10691     tmpbuf[0] = NUL;
10692 
10693     switch (cmresult.fcode) {           /* How did we get out of switch loop */
10694       case _CMFLD:                      /* (3) Remote filespec */
10695         ckstrncpy(line,cmresult.sresult,LINBUFSIZ);
10696         break;
10697       case _CMTXT:                      /* (4) As-name */
10698         if (rcvcmd) {
10699             ckstrncpy(tmpbuf,cmresult.sresult,TMPBUFSIZ);
10700             if ((int)strlen(tmpbuf) > 0)
10701               asname = 1;
10702         } else {
10703             ckstrncpy(line,cmresult.sresult,LINBUFSIZ);
10704         }
10705       case _CMCFM:                      /* (6) Confirmation */
10706         confirmed = 1;
10707         break;
10708       default:
10709         printf("?Unexpected function code: %d\n",cmresult.fcode);
10710         x = -9;
10711         goto xgetx;
10712     }
10713     debug(F110,"xget string",cmarg,0);
10714     debug(F101,"xget confirmed","",confirmed);
10715 
10716     cmarg = brstrip(cmarg);             /* Strip any braces */
10717 
10718     if (!confirmed) {                   /* CR not typed yet, get more fields */
10719         if (pv[SND_CMD].ival > 0) {
10720             debug(F100,"xget calling cmtxt","",0);
10721             x = cmtxt("Local command to pipe into","",&s,NULL);
10722             if (x < 0 && x != -3) goto xgetx;
10723             if (x != -3) {
10724                 ckstrncpy(tmpbuf,s,TMPBUFSIZ);
10725                 asname = 1;
10726             }
10727         } else if (!rcvcmd) {
10728 #ifdef VMS
10729             /* cmofi() fails if you give it a directory name */
10730             x = cmfld("Name or directory for incoming file","",&s,NULL);
10731             debug(F111,"xget cmfld",s,x);
10732 #else
10733             x = cmofi("Name or directory for incoming file","",&s,NULL);
10734             debug(F111,"xget cmofi",s,x);
10735 #endif /* VMS */
10736             if (x < 0 && x != -3) goto xgetx;
10737             if (x != -3) {
10738                 ckstrncpy(tmpbuf,s,TMPBUFSIZ);
10739                 if ((x = cmcfm()) < 0) goto xgetx;
10740                 asname = 1;
10741             }
10742         }
10743     }
10744     /* Arrive here with cmarg and cmarg2 all set */
10745 
10746     debug(F111,"xget asname",cmarg2,asname);
10747     if (!asname) {
10748         if (pv[SND_ASN].sval)
10749           ckstrncpy(tmpbuf,pv[SND_ASN].sval,TMPBUFSIZ);
10750         else
10751           tmpbuf[0] = NUL;
10752     }
10753     cmarg2 = brstrip(cmarg2);           /* Strip outer braces if any. */
10754     debug(F110,"xget cmarg",cmarg,0);
10755     debug(F110,"xget cmarg2",cmarg2,0);
10756 
10757     if (!*cmarg &&
10758         (cx == XXGET || cx == XXREGET || cx == XXCGET || cx == XXMGET)) {
10759         printf("?A remote file specification is required\n");
10760         x = -9;
10761         goto xgetx;
10762     }
10763 #ifdef PIPESEND
10764     if (pv[SND_CMD].ival > 0) {         /* /COMMAND sets pipesend flag */
10765         x = -9;
10766         if (!*cmarg2) {
10767             printf("?Command required\n");
10768             goto xgetx;
10769         } else if (nopush) {
10770             printf("?Sorry, system command access is disabled\n");
10771             goto xgetx;
10772         } else if (rcvfilter) {
10773             printf("?Sorry, no GET /COMMAND while RECEIVE FILTER selected\n");
10774             goto xgetx;
10775         } else
10776           pipesend = 1;
10777     }
10778     debug(F101,"xget /COMMAND pipesend","",pipesend);
10779 #endif /* PIPESEND */
10780 
10781 #ifdef CK_RESEND
10782     if (pv[SND_RES].ival > 0) {         /* REGET or GET /RECOVER */
10783 #ifdef RECURSIVE
10784         if (pv[SND_REC].ival > 0) {     /* RECURSIVE */
10785 #ifdef COMMENT
10786             printf("?Unsupported option combination: /RECOVER /RECURSIVE\n");
10787             x = -9;
10788             goto xgetx;
10789 #else
10790             opkt = 1;
10791 #endif /* COMMENT */
10792         }
10793 #endif /* RECURSIVE */
10794         if (pv[SND_DEL].ival > 0) {     /* /DELETE */
10795 #ifdef COMMENT
10796             printf("?Unsupported option combination: /RECOVER /DELETE\n");
10797             x = -9;
10798             goto xgetx;
10799 #else
10800             opkt = 1;
10801 #endif /* COMMENT */
10802         }
10803     }
10804 #endif /* CK_RESEND */
10805 
10806     if (pv[SND_EXC].ival > 0)           /* /EXCEPT */
10807       makelist(pv[SND_EXC].sval,rcvexcept,NSNDEXCEPT);
10808 
10809 #ifdef IKS_OPTION
10810     if (!rcvcmd
10811 #ifdef CK_XYZ
10812          && protocol == PROTO_K
10813 #endif /* CK_XYZ */
10814          ) {
10815         if (!iks_wait(KERMIT_REQ_START,1)) {
10816             printf(
10817               "?A Kermit Server is not available to process this command\n");
10818             x = -9;                     /* correct the return code */
10819             goto xgetx;
10820         }
10821     }
10822 #endif /* IKS_OPTION */
10823 
10824 #ifdef CK_XYZ
10825     {
10826         int po, pg;                     /* (for clarity) */
10827         po = pv[SND_PRO].ival;          /* /PROTOCOL option */
10828         pg = protocol;                  /* Protocol global  */
10829         if ((rcvcmd && !*cmarg2) &&     /* If no as-name was given */
10830             /* and /PROTOCOL is XMODEM or global protocol is XMODEM... */
10831             ((po <  0 && (pg == PROTO_X || pg == PROTO_XC)) ||
10832              (po > -1 && (po == PROTO_X || po == PROTO_XC)))
10833             ) {
10834             printf(
10835 "Sorry, you must specify a name when receiving a file with XMODEM protocol\n"
10836                    );
10837             x = -9;
10838             goto xgetx;
10839         }
10840     }
10841 #endif /* CK_XYZ */
10842 
10843 #ifdef RECURSIVE
10844     if (pv[SND_REC].ival > 0) {         /* RECURSIVE */
10845         recursive = 1;
10846         pv[SND_PTH].ival = PATH_REL;    /* Implies relative pathnames too */
10847     }
10848 #endif /* RECURSIVE */
10849 
10850     if (pv[SND_PIP].ival > -1) {
10851         g_usepipes = usepipes;
10852         usepipes = pv[SND_PIP].ival;
10853     }
10854 
10855     /* Save global protocol parameters */
10856 
10857     g_proto = protocol;
10858 #ifdef CK_LABELED
10859     g_lf_opts = lf_opts;                /* Save labeled transfer options */
10860 #endif /* CK_LABELED */
10861     g_urpsiz = urpsiz;                  /* Receive packet length */
10862     g_spsizf = spsizf;                  /* Send packet length flag */
10863     g_spsiz = spsiz;                    /* Send packet length */
10864     g_spsizr = spsizr;                  /* etc etc */
10865     g_spmax = spmax;
10866     g_wslotr = wslotr;
10867     g_prefixing = prefixing;
10868     g_fncact = fncact;
10869     g_fncnv = fncnv;
10870     g_fnspath = fnspath;
10871     g_fnrpath = fnrpath;
10872     g_xfrxla = xfrxla;
10873 
10874     if (pv[SND_PRO].ival > -1) {        /* Change according to switch */
10875         protocol = pv[SND_PRO].ival;
10876         if (ptab[protocol].rpktlen > -1)   /* copied from initproto() */
10877             urpsiz = ptab[protocol].rpktlen;
10878         if (ptab[protocol].spktflg > -1)
10879             spsizf = ptab[protocol].spktflg;
10880         if (ptab[protocol].spktlen > -1) {
10881             spsiz = ptab[protocol].spktlen;
10882             if (spsizf)
10883                 spsizr = spmax = spsiz;
10884         }
10885         if (ptab[protocol].winsize > -1)
10886             wslotr = ptab[protocol].winsize;
10887         if (ptab[protocol].prefix > -1)
10888             prefixing = ptab[protocol].prefix;
10889         if (ptab[protocol].fnca > -1)
10890             fncact  = ptab[protocol].fnca;
10891         if (ptab[protocol].fncn > -1)
10892             fncnv   = ptab[protocol].fncn;
10893         if (ptab[protocol].fnsp > -1)
10894             fnspath = ptab[protocol].fnsp;
10895         if (ptab[protocol].fnrp > -1)
10896             fnrpath = ptab[protocol].fnrp;
10897     }
10898     debug(F101,"xget protocol","",protocol);
10899     debug(F111,"xget cmarg2",cmarg2,xfermode);
10900 
10901     g_xfermode = xfermode;
10902     g_binary = binary;
10903     if (pv[SND_BIN].ival > 0) {         /* Change according to switch */
10904         xfermode = XMODE_M;
10905         binary = XYFT_B;                /* FILE TYPE BINARY */
10906         omode = GMOD_BIN;               /* O-Packet mode */
10907         debug(F101,"doxget /BINARY xfermode","",xfermode);
10908     } else if (pv[SND_TXT].ival > 0) {  /* Ditto for /TEXT */
10909         xfermode = XMODE_M;
10910         binary = XYFT_T;
10911         omode = GMOD_TXT;
10912         debug(F101,"doxget /TEXT xfermode","",xfermode);
10913     } else if (pv[SND_IMG].ival > 0) {
10914         xfermode = XMODE_M;
10915 #ifdef VMS
10916         binary = XYFT_I;
10917 #else
10918         binary = XYFT_B;
10919 #endif /* VMS */
10920         omode = GMOD_TXT;
10921         debug(F101,"doxget /IMAGE xfermode","",xfermode);
10922     }
10923 #ifdef CK_LABELED
10924     else if (pv[SND_LBL].ival > 0) {
10925         xfermode = XMODE_M;
10926         binary = XYFT_L;
10927         omode = GMOD_LBL;
10928         debug(F101,"doxget /LABELED xfermode","",xfermode);
10929     }
10930 #endif /* CK_LABELED */
10931     debug(F101,"xget binary","",binary);
10932     debug(F101,"xget omode","",omode);
10933 
10934     if (pv[SND_XPA].ival > 0)           /* /TRANSPARENT */
10935       xfrxla = 0;                       /* Don't translate character sets */
10936 
10937 #ifdef PIPESEND
10938     if (pv[SND_FLT].ival > 0)
10939       makestr(&rcvfilter,pv[SND_FLT].sval);
10940 #endif /* PIPESEND */
10941 
10942 #ifdef CK_TMPDIR
10943     if (pv[SND_MOV].ival > 0) {
10944         int len;
10945         char * p = pv[SND_MOV].sval;
10946 #ifdef CK_LOGIN
10947         if (isguest) {
10948             printf("?Sorry, /MOVE-TO not available to guests\n");
10949             x = -9;
10950             goto xgetx;
10951         }
10952 #endif /* CK_LOGIN */
10953         len = strlen(p);
10954         if (!isdir(p)) {                /* Check directory */
10955 #ifdef CK_MKDIR
10956             char * s = NULL;
10957             s = (char *)malloc(len + 4);
10958             if (s) {
10959                 strcpy(s,p);            /* safe */
10960 #ifdef datageneral
10961                 if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; }
10962 #else
10963                 if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; }
10964 #endif /* datageneral */
10965                 s[len++] = 'X';
10966                 s[len] = NUL;
10967                 x = zmkdir(s);
10968                 free(s);
10969                 if (x < 0) {
10970                     printf("?Can't create \"%s\"\n",p);
10971                     x = -9;
10972                     goto xgetx;
10973                 }
10974             }
10975 #else
10976             printf("?Directory \"%s\" not found\n",p);
10977             x = -9;
10978             goto xgetx;
10979 #endif /* CK_MKDIR */
10980         }
10981         zfnqfp(p,LINBUFSIZ,line);
10982         makestr(&rcv_move,line);
10983     }
10984 #endif /* CK_TMPDIR */
10985 
10986     if (pv[SND_REN].ival > 0) {         /* /RENAME-TO:name */
10987         char * p = pv[SND_REN].sval;
10988 #ifdef CK_LOGIN
10989         if (isguest) {
10990             printf("?Sorry, /RENAME-TO not available to guests\n");
10991             x = -9;
10992             goto xgetx;
10993         }
10994 #endif /* CK_LOGIN */
10995         if (!p) p = "";
10996         if (!*p) {
10997             printf("?New name required for /RENAME\n");
10998             x = -9;
10999             goto xgetx;
11000         }
11001         p = brstrip(p);
11002         makestr(&rcv_rename,p);
11003         debug(F110,"xget rcv_rename","",0);
11004     }
11005 
11006 #ifdef CALIBRATE
11007     if (pv[SND_CAL].ival > 0)
11008       calibrate = (CK_OFF_T)1;
11009 #endif /* CALIBRATE */
11010     g_displa = fdispla;
11011     if (pv[SND_SHH].ival > 0)
11012       fdispla = 0;
11013     debug(F101,"xget display","",fdispla);
11014 
11015     if (pv[SND_NAM].ival > -1) {        /* /FILENAMES */
11016         g_fncnv = fncnv;                /* Save global value */
11017         fncnv = pv[SND_NAM].ival;
11018         debug(F101,"xsend fncnv","",fncnv);
11019         /* We should also handle O packet filename option here */
11020         /* but we don't really need to since WHATAMI already handles it */
11021     }
11022     if (pv[SND_PTH].ival > -1) {        /* PATHNAMES */
11023         g_rpath = fnrpath;              /* Save global values */
11024         fnrpath = pv[SND_PTH].ival;
11025         debug(F101,"xsend fnrpath","",fnrpath);
11026 #ifndef NZLTOR
11027         if (fnrpath != PATH_OFF) {
11028             g_fncnv = fncnv;
11029             fncnv = XYFN_L;
11030             debug(F101,"xsend fncnv","",fncnv);
11031         }
11032         /* We should also handle O packet pathname option here */
11033         /* but we don't really need to since WHATAMI already handles it */
11034 #endif /* NZLTOR */
11035     }
11036 
11037     /* Set protocol start state */
11038 
11039     if (opkt) {                         /* Extended GET Options*/
11040         sstate = (CHAR) 'o';
11041         oopts = 0;
11042         if (pv[SND_DEL].ival > 0) oopts |= GOPT_DEL; /* GET /DELETE */
11043         if (pv[SND_RES].ival > 0) oopts |= GOPT_RES; /* GET /RECOVER */
11044         if (pv[SND_REC].ival > 0) oopts |= GOPT_REC; /* GET /RECURSIVE */
11045     } else if (rcvcmd)
11046       sstate = (CHAR) 'v';              /* RECEIVE or CRECEIVE */
11047     else if (pv[SND_DEL].ival > 0)
11048       sstate = (CHAR) 'h';              /* GET /DELETE (= RETRIEVE) */
11049     else if (pv[SND_RES].ival > 0)
11050       sstate = (CHAR) 'j';              /* GET /RECOVER (= REGET) */
11051     else
11052       sstate = (CHAR) 'r';              /* Regular GET */
11053     getcmd = 1;
11054     debug(F000,"xget sstate","",sstate);
11055 #ifdef MAC
11056     what = W_RECV;
11057     scrcreate();
11058 #endif /* MAC */
11059     if (local) {
11060         if (pv[SND_SHH].ival != 0)
11061           displa = 1;
11062 	if (protocol == PROTO_K)	/* fdc 20070108 */
11063 	  ttflui();
11064     }
11065     x = 0;
11066 #ifdef PIPESEND
11067     if (pipesend)
11068       goto xgetx;
11069 #endif /* PIPESEND */
11070 
11071 #ifdef CK_TMPDIR
11072 /*
11073   cmarg2 is also allowed to be a device or directory name;
11074   even the name of a directory that doesn't exist.
11075 */
11076     y = strlen(cmarg2);
11077     debug(F111,"xget strlen(cmarg2)",cmarg2,y);
11078     if ((y > 0) &&
11079 #ifdef OS2
11080         ((isalpha(cmarg2[0]) &&
11081          cmarg2[1] == ':' &&
11082          cmarg2[2] == NUL) ||
11083         (cmarg[y-1] == '/' || cmarg[y-1] == '\\') ||
11084         isdir(cmarg2))
11085 #else
11086 #ifdef UNIXOROSK
11087         (cmarg2[y-1] == '/' || isdir(cmarg2))
11088 #else
11089 #ifdef VMS
11090         (cmarg2[y-1] == ']' || cmarg2[y-1] == '>' || isdir(cmarg2))
11091 #else
11092 #ifdef STRATUS
11093         (cmarg2[y-1] == '>' || isdir(cmarg2))
11094 #else
11095 #ifdef datageneral
11096         (cmarg2[y-1] == ':' || cmarg[0] == ':' || isdir(cmarg2))
11097 #else
11098         isdir(cmarg2)
11099 #endif /* datageneral */
11100 #endif /* STRATUS */
11101 #endif /* VMS */
11102 #endif /* UNIXOROSK */
11103 #endif /* OS2 */
11104         ) {
11105         debug(F110,"doxget RECEIVE cmarg2 disk or dir",cmarg2,0);
11106         if (!f_tmpdir) {
11107             int x;
11108             s = zgtdir();
11109             if (s) {
11110                 ckstrncpy(savdir,s,TMPDIRLEN); /* remember old disk/dir */
11111                 f_tmpdir = 1;   /* and that we did this */
11112             } else {
11113                 printf("?Can't get current directory\n");
11114                 cmarg2 = "";
11115                 f_tmpdir = 0;
11116                 x = -9;
11117                 goto xgetx;
11118             }
11119 #ifdef CK_MKDIR
11120             x = zchki(cmarg2);          /* Does as-name exist? */
11121             if (x == -1) {              /* Doesn't exist */
11122                 char * p = NULL;        /* Try to create it */
11123                 x = strlen(cmarg2);
11124                 if ((p = (char *)malloc(x+4))) {
11125                     sprintf(p,"%s%s",cmarg2,"x.x"); /* SAFE (prechecked) */
11126                     x = zmkdir(p);
11127                     free(p);
11128                     if (x < 0) {
11129                         printf("?Can't create %s\n",cmarg2);
11130                         x = -9;
11131                         goto xgetx;
11132                     }
11133                 }
11134             }
11135 #endif /* CK_MKDIR */
11136             if (!zchdir(cmarg2)) {      /* change to given disk/directory, */
11137                 printf("?Can't access %s\n",cmarg2);
11138                 x = -9;
11139                 goto xgetx;
11140             }
11141             cmarg2 = "";
11142         }
11143     }
11144 #endif /* CK_TMPDIR */
11145 
11146     ckstrncpy(fspec,cmarg,CKMAXPATH);   /* Note - this is a REMOTE filespec */
11147     debug(F111,"xget fspec",fspec,fspeclen);
11148     debug(F110,"xget cmarg2",cmarg2,0);
11149 
11150   xgetx:
11151     for (i = 0; i < SND_MAX; i++)
11152       if (pv[i].sval)
11153         free(pv[i].sval);
11154     return(x);
11155 }
11156 #endif /* NOXFER */
11157 
11158 #ifndef NOSPL
11159 
11160 /*
11161   D O G T A  --  Do _GETARGS or _PUTARGS Command.
11162 
11163   Used by IF, FOR, WHILE, and SWITCH, each of which are implemented as
11164   2-level macros; the first level defines the macro, the second runs it.
11165   This routine hides the fact that they are macros by importing the
11166   macro arguments (if any) from two levels up, to make them available
11167   in the IF, FOR, SWITCH, and WHILE commands themselves; for example as
11168   loop indices, etc, and within the IF/FOR/WHILE/SWITCH body itself.
11169   _PUTARGS is in case we changed any of these variables or used SHIFT
11170   on them, so the new values won't be lost as we pop up the stack.
11171 */
11172 int
dogta(cx)11173 dogta(cx) int cx; {
11174     int i, n;
11175     char c, *p,  mbuf[4];
11176     extern int topargc, cmdint;
11177     extern char ** topxarg;
11178 
11179     debug(F100,"GETARGS: dogta entry cx","",cx);
11180     if ((y = cmcfm()) < 0)
11181       return(y);
11182     debug(F101," dogta maclvl","",maclvl);
11183     if (cx == XXGTA) {
11184         debug(F101," dogta _GETARGS maclvl","",maclvl);
11185     } else if (cx == XXPTA) {
11186         debug(F101," dogta _PUTARGS maclvl","",maclvl);
11187     } else {
11188         return(-2);
11189     }
11190     if (maclvl < 1)
11191       return(success = 0);
11192 
11193     /* Make new copies of macro arguments /%0..9 */
11194 
11195     mbuf[0] = '%'; mbuf[1] = '0'; mbuf[2] = NUL; /* Argument name buf */
11196 
11197     if (cx == XXPTA) {                  /* Go NOINT because _PUTARGS */
11198         if (cmdint)                     /* temporarily changes maclvl. */
11199           connoi();                     /* Interrupts OFF. */
11200     }
11201     for (i = 0; i < 10; i++) {          /* For all args */
11202         c = (char) (i + '0');           /* Make name */
11203         mbuf[1] = (char) c;             /* Insert digit */
11204         if (cx == XXGTA) {              /* Get arg from level-minus-2 */
11205             if (maclvl == 1) p = g_var[c]; /* If at level 1 use globals 0..9 */
11206             else p = m_arg[maclvl-2][i];   /* Otherwise they're on the stack */
11207             debug(F111," dogta _GETARGS m_arg addmac var i",mbuf,i);
11208             debug(F111," dogta _GETARGS m_arg addmac def i",p,i);
11209             addmac(mbuf,p);
11210 #ifdef COMMENT
11211             if (maclvl > 1)
11212               makestr(&(m_line[maclvl]),m_line[maclvl-2]);
11213 #endif /* COMMENT */
11214         } else if (cx == XXPTA) {       /* Put args level+2 */
11215             maclvl -= 2;                /* This is gross, it's because we're */
11216             addmac(mbuf,m_arg[maclvl+2][i]); /* adding macros two levels up */
11217             maclvl += 2;                     /* and addmac() uses maclvl. */
11218             count[cmdlvl - 2]  = count[cmdlvl];
11219             intime[cmdlvl - 2] = intime[cmdlvl];
11220             inpcas[cmdlvl - 2] = inpcas[cmdlvl];
11221             takerr[cmdlvl - 2] = takerr[cmdlvl];
11222             merror[cmdlvl - 2] = merror[cmdlvl];
11223             xquiet[cmdlvl - 2] = xquiet[cmdlvl];
11224             xvarev[cmdlvl - 2] = xvarev[cmdlvl];
11225         } else return(success = 0);     /* Bad call to this routine */
11226     }
11227     if (cx == XXPTA) {                  /* Restore interrupts if we */
11228         if (cmdint)                     /* turned them off above. */
11229           conint(trap,stptrap);
11230     }
11231     /* Now take care of the argument vector array \&_[], \v(return), */
11232     /* and \v(argc) by just copying the pointers. */
11233 
11234     if (cx == XXGTA) {                  /* GETARGS from 2 levels up */
11235         debug(F101," dogta _GETARGS m_xarg maclvl","",maclvl);
11236         if (maclvl == 1) {
11237             debug(F100," dogta _GETARGS m_xarg top level","",0);
11238             a_ptr[0] = topxarg;         /* \&_[] array */
11239             a_dim[0] = topargc - 1;     /* Dimension doesn't include [0] */
11240             m_xarg[maclvl] = topxarg;
11241             n_xarg[maclvl] = topargc;   /* But \v(argc) does include \%0 */
11242             macargc[maclvl] = topargc;
11243             makestr(&(mrval[maclvl+1]),mrval[0]); /* (see vnlook()) */
11244         } else {
11245             debug(F100," dogta _GETARGS m_xarg in macro","",0);
11246             a_ptr[0] = m_xarg[maclvl-2];
11247             a_dim[0] = n_xarg[maclvl-2];
11248             m_xarg[maclvl] = m_xarg[maclvl-2];
11249             n_xarg[maclvl] = n_xarg[maclvl-2];
11250             macargc[maclvl] = n_xarg[maclvl-2];
11251             makestr(&(mrval[maclvl+1]),mrval[maclvl-1]); /* (see vnlook()) */
11252         }
11253     } else {                            /* PUTARGS 2 levels up */
11254         if (maclvl > 1) {
11255             a_ptr[0] = m_xarg[maclvl];
11256             m_xarg[maclvl-2] = m_xarg[maclvl];
11257             a_dim[0] = n_xarg[maclvl];
11258             n_xarg[maclvl-2] = n_xarg[maclvl];
11259             macargc[maclvl-2] = n_xarg[maclvl];
11260         }
11261     }
11262     return(1);                  /* Internal command - don't change success */
11263 }
11264 #endif /* NOSPL */
11265 
11266 #ifndef NOSPL
11267 /*
11268   Do the GOTO and [_]FORWARD commands.
11269   s = Label to search for, cx = function code: XXGOTO, XXFWD, or XXXFWD.
11270 */
11271 
11272 int
dogoto(s,cx)11273 dogoto(s, cx) char *s; int cx; {
11274     int i, j, x, y, z, bc;
11275     int empty = 0, stopflg = 0;
11276     char * cmd;                         /* Name of this command */
11277     char tmplbl[LBLSIZ+1], *lp;	        /* Current label from command stream */
11278     char tmp2[LBLSIZ+1];		/* SWITCH label conversion buffer */
11279     char tmp3[LBLSIZ+1];		/* Target label */
11280 
11281     stopflg = (cx == XXXFWD);           /* _FORWARD (used in SWITCH) */
11282     bc = 0;                             /* Brace counter */
11283 
11284     cmd = (cx == XXGOTO) ? "GOTO" : ((cx == XXFWD) ? "FORWARD" : "_FORWARD");
11285     if (!s) s = "";
11286     if (!*s) empty = 1;
11287 
11288 #ifdef DEBUG
11289     if (deblog) {
11290         debug(F111,"GOTO command",cmd,cx);
11291         debug(F101,"GOTO cmdlvl","",cmdlvl);
11292         debug(F101,"GOTO maclvl","",maclvl);
11293         debug(F101,"GOTO tlevel","",tlevel);
11294         debug(F111,"GOTO target",s,empty);
11295     }
11296 #endif /* DEBUG */
11297     debug(F110,cmd,s,0);
11298     x = ckstrncpy(tmp3+1,s,LBLSIZ);
11299     debug(F101,"GOTO target len","",x);
11300     debug(F101,"GOTO target at x","",s[x]);
11301     if (s[x]) {
11302 	debug(F100,"GOTO target overflow","",0);
11303 	printf("?GOTO target or SWITCH case label too long\n");
11304 	if (stopflg) dostop();		/* If in SWITCH return to prompt */
11305 	return(success = 0);
11306     }
11307     s = tmp3+1;
11308     if (*s != ':') {                    /* Make copy of label */
11309         tmp3[0] = ':';                  /* guaranteed to start with ":" */
11310         s--;
11311     }
11312     if (!stopflg && !empty) {
11313         if (s[1] == '.' || s[1] == SP || s[1] == NUL) {
11314             printf("?Bad label syntax - '%s'\n",s);
11315 	    if (stopflg) dostop();	/* If in SWITCH return to prompt */
11316             return(success = 0);
11317         }
11318     }
11319     if (cmdlvl == 0) {
11320         printf("?Sorry, %s only works in a command file or macro\n",cmd);
11321         return(success = 0);
11322     }
11323     y = strlen(s);                      /* y = length of target label */
11324     debug(F111,cmd,s,y);
11325 
11326     while (cmdlvl > 0) {                /* As long as not at top level... */
11327         if (cmdstk[cmdlvl].src == CMD_MD) { /* GOTO inside macro */
11328             int i, m, flag;
11329             char *xp, *tp;
11330 
11331             /* GOTO: rewind the macro; FORWARD: start at current position */
11332 
11333             lp = (cx == XXGOTO) ? macx[maclvl] : macp[maclvl];
11334             m = (int)strlen(lp);
11335             debug(F111,"GOTO in macro",lp,m);
11336 
11337             flag = 1;                   /* flag for valid label position */
11338             for (i = 0; i < m; i++,lp++) { /* search for label in macro body */
11339                 if (*lp == '{')         /* But only at this level */
11340                   bc++;                 /* Anything inside braces is off */
11341                 else if (*lp == '}')    /* limits. */
11342                   bc--;
11343                 if (stopflg && bc > 0)  /* This is good for SWITCH */
11344                   continue;             /* but interferes with WHILE, etc. */
11345                 if (*lp == ',') {
11346                     flag = 1;
11347                     continue;
11348                 }
11349                 if (flag) {             /* If in valid label position */
11350                     if (*lp == SP)      /* eat leading spaces */
11351                       continue;
11352                     if (*lp != ':') {   /* Look for label introducer */
11353                         flag = 0;       /* this isn't it */
11354                         continue;       /* keep looking */
11355                     }
11356                 }
11357                 if (!flag)              /* We don't have a label */
11358                   continue;             /*  so keep looking... */
11359                 xp = lp; tp = tmplbl;   /* Copy the label from the macro */
11360                 j = 0;                  /* to make it null-terminated */
11361                 while ((*tp = *xp)) {
11362                     if (j++ > LBLSIZ-1) { /* j = length of word from macro */
11363 			printf("?GOTO target or SWITCH case label too long\n");
11364 			if (stopflg) dostop(); /* Return to prompt */
11365 			return(success = 0);
11366 		    }
11367                     if (!*tp || *tp == ',') /* Look for end of word */
11368                       break;
11369                     else tp++, xp++;    /* Next character */
11370                 }
11371                 *tp = NUL;              /* In case we stopped early */
11372                 /* Now do caseless string comparison, using longest length */
11373                 debug(F111,"macro GOTO label",s,y);
11374                 debug(F111,"macro target label",tmplbl,j);
11375                 if (stopflg) {          /* Allow variables as SWITCH labels */
11376                     int n = LBLSIZ - 1;
11377                     char * p = tmp2;
11378                     zzstring(tmplbl,&p,&n);
11379 		    if (n < 0) {
11380 			printf("?GOTO target or SWITCH case label too long\n");
11381 			if (stopflg) dostop(); /* Return to prompt */
11382 			return(0);
11383 		    }
11384                     ckstrncpy(tmplbl,tmp2,LBLSIZ);
11385                 }
11386                 debug(F111,"GOTO s",s,y);
11387                 debug(F111,"GOTO tmplbl",tmplbl,j);
11388                 debug(F111,"GOTO empty",ckitoa(stopflg),empty);
11389 
11390                 if (empty) {		   /* Empty target */
11391 		    z = (!strcmp(s,":") && /* String is empty */
11392 			 /* and Label is ":" or ":*"... */
11393 			 (!strcmp(tmplbl,":") || !strcmp(tmplbl,":*")))
11394 			? 0 : 1;
11395 		    debug(F111,"GOTO","A",z);
11396                 } else if (stopflg) {
11397                     z = ckmatch(tmplbl,s,inpcas[cmdlvl],1) ? 0 : 1;
11398 		    debug(F111,"GOTO","B",z);
11399                 } else {
11400                     z = (stopflg && inpcas[cmdlvl]) ?
11401                       strcmp(s,tmplbl) :
11402                         ckstrcmp(s,tmplbl,(y > j) ? y : j, 0);
11403 		    debug(F111,"GOTO","C",z);
11404                 }
11405                 if (!z) {
11406                     break;
11407                 } else if (stopflg &&
11408                          !ckstrcmp(":default",tmplbl,(8 > j) ? 8 : j, 0)) {
11409                     debug(F100,"GOTO DEFAULT","",0);
11410                     break;
11411                 } else {
11412                     flag = 0;
11413                 }
11414             }
11415             debug(F111,"GOTO macro i",cmd,i);
11416             debug(F111,"GOTO macro m",cmd,m);
11417             if (i >= m) {               /* Didn't find the label */
11418                 debug(F101,"GOTO failed cmdlvl","",cmdlvl);
11419 #ifdef COMMENT
11420 		/* MOVED TO AFTER POPCLVL ABOUT 20 LINES DOWN 5 AUG 2002 */
11421 		   if (stopflg)
11422                   return(0);
11423 #endif /* COMMENT */
11424                 if ((maclvl > 0) &&
11425                        (m_arg[maclvl-1][0]) &&
11426                        (cmdstk[cmdlvl].src == CMD_MD) &&
11427                        (!strncmp(m_arg[maclvl-1][0],"_xif",4) ||
11428                         !strncmp(m_arg[maclvl-1][0],"_for",4) ||
11429                         !strncmp(m_arg[maclvl-1][0],"_swi",4) ||
11430                         !strncmp(m_arg[maclvl-1][0],"_whi",4))) {
11431                     dogta(XXPTA);       /* Restore args */
11432                     debug(F101,"GOTO in XIF/FOR/WHI/SWI popping","",cmdlvl);
11433                     popclvl();          /* Pop an extra level */
11434                 }
11435                 debug(F101,"GOTO popping","",cmdlvl);
11436                 if (!popclvl()) {       /* pop up to next higher level */
11437                     printf("?Label '%s' not found\n",s); /* if none */
11438                     return(0);          /* Quit */
11439                 } else if (stopflg) {	/* SWITCH no case label match */
11440 		    return(0);		/* and no DEFAULT lable. */
11441 		} else {
11442 		    continue;        /* otherwise look again */
11443 		}
11444             }
11445             debug(F110,"GOTO found macro label",tmplbl,0);
11446             macp[maclvl] = lp;          /* set macro buffer pointer */
11447             return(1);
11448         } else if (cmdstk[cmdlvl].src == CMD_TF) {
11449             x = 0;                      /* GOTO issued in take file */
11450             debug(F111,"GOTO in TAKE file",cmd,cx);
11451             if (cx == XXGOTO) {         /* If GOTO, but not FORWARD, */
11452                 rewind(tfile[tlevel]);  /* search file from beginning */
11453                 tfline[tlevel] = 0;
11454             }
11455             while (! feof(tfile[tlevel])) {
11456 #ifdef COMMENT
11457 /* This is wrong - it lets us jump to labels inside inferior blocks */
11458                 tfline[tlevel]++;
11459                 if (fgets(line,LINBUFSIZ,tfile[tlevel]) == NULL) /* Get line */
11460 #else
11461                 if (getnct(line,LINBUFSIZ,tfile[tlevel],0) < 0)
11462 #endif /* COMMENT */
11463                   break;                /* If no more, done, label not found */
11464                 lp = line;              /* Got line */
11465                 while (*lp == SP || *lp == HT)
11466                   lp++;                 /* Strip leading whitespace */
11467                 if (*lp != ':') continue; /* Check for label introducer */
11468                 while (*(lp+1) == SP) { /* Remove space between : and name */
11469                     *(lp+1) = ':';
11470                     lp++;               /* Strip leading whitespace */
11471                 }
11472                 tp = lp;                /* Get end of word */
11473                 j = 0;
11474                 while (*tp) {           /* And null-terminate it */
11475                     if (*tp < 33) {
11476                         *tp = NUL;
11477                         break;
11478                     } else tp++, j++;
11479                 }
11480                 if (!ckstrcmp(lp,s,(y > j) ? y : j,0)) { /* Caseless compare */
11481                     x = 1;              /* Got it */
11482                     break;              /* done. */
11483                 } else if (stopflg &&
11484                            !ckstrcmp(":default",tmplbl,(8 > j) ? 8 : j,0)) {
11485                     x = 1;
11486                     break;
11487                 }
11488             }
11489             if (x == 0) {               /* If not found, print message */
11490                 debug(F101,"GOTO failed at cmdlvl","",cmdlvl);
11491                 if (stopflg)
11492                   return(0);
11493                 if (!popclvl()) {       /* pop up to next higher level */
11494                     printf("?Label '%s' not found\n",s); /* if none */
11495                     return(0);          /* quit */
11496                 } else continue;        /* otherwise look again */
11497             }
11498             return(x);                  /* Send back return code */
11499         }
11500     }
11501     printf("?Stack problem in GOTO %s\n",s); /* Shouldn't see this */
11502     return(0);
11503 }
11504 #endif /* NOSPL */
11505 
11506 /* Finish parsing and do the IF, XIF, and WHILE commands */
11507 
11508 #ifndef NOSPL
11509 
11510 /*  C H K V A R  --  Check (if it's a) Variable  */
11511 
11512 
11513 #ifdef OLDCHKVAR
11514 /*
11515   Crude and disgusting, but needed for OS/2, DOS, and Windows, where filenames
11516   have backslashes in them.  How do we know if a backslash in a filename is a
11517   directory separator, or if it's a Kermit backslash?  This routine does a
11518   rough syntax check of the next few characters and if it looks like it MIGHT
11519   be a variable, then it tries to evaluate it, and if the result is not empty,
11520   we say it's a variable, although sometimes it might not be -- some cases are
11521   truly ambiguous.  For example there might a DOS directory called \%a, and
11522   we also have a variable with the same name.  This is all for the sake of not
11523   having to tell PC users that they have to double all backslashes in file
11524   and directory names.
11525 */
11526 #else
11527 /*
11528   Somewhat less crude & disgusting.  The previous method was nondeterministic
11529   and (worse) it interfered with macro argument passing.  So now we only
11530   check the syntax of backslash-items to see if they are variables, but we
11531   do NOT check their values.
11532 */
11533 #endif /* OLDCHKVAR */
11534 /*
11535   Call with a string pointer pointing at the backslash of the purported
11536   variable.  Returns 1 if it has the syntax of a variable, 0 if not.
11537 */
11538 int
chkvar(s)11539 chkvar(s) char *s; {
11540     int z = 0;                          /* Return code - assume failure. */
11541     if (!s) s = "";                     /* Watch our for null pointers. */
11542     if (!*s) return(0);                 /* Empty arg so not a variable. */
11543     if (*s == CMDQ) {                   /* Object begins with backslash. */
11544         char c;
11545         c = s[1];                       /* Character following backslash. */
11546         if (c) {
11547             int t = 0;
11548             if (c == CMDQ)              /* Quoted backslash */
11549               return(1);
11550             c = (char) (islower(c) ? toupper(c) : c); /* Otherwise... */
11551             if (c == '%') {             /* Simple variable */
11552 #ifdef OLDCHKVAR
11553                 t = 1;
11554 #else
11555                 return(1);
11556 #endif /* OLDCHKVAR */
11557             } else if (c == '&') {      /* Array */
11558                 if (!s[2]) return(0);
11559                 if (s[3] == '[')
11560                   t = ckindex("]",s,4,0,1);
11561 #ifndef OLDCHKVAR
11562                 return((t > 0) ? 1 : 0);
11563 #endif /* OLDCHKVAR */
11564             } else if (c == '$' ||      /* Environment variable */
11565                        c == 'V' ||      /* Built-in variable */
11566                        c == 'M') {      /* Macro name */
11567                 t = (s[2] == '(');
11568 #ifndef OLDCHKVAR
11569                 return((t > 0) ? 1 : 0);
11570 #endif /* OLDCHKVAR */
11571             } else if (c == 'F') {      /* Function reference */
11572                 /* Don't actually call it - it might have side effects */
11573                 int x;
11574                 if ((x = ckindex("(",s,3,0,1))) /* Just check syntax */
11575                   if ((x = ckindex(")",s,x,0,1)))
11576                     z = 1;
11577                 /* Insert a better syntax check here if necessary */
11578             }
11579 #ifdef OLDCHKVAR
11580             if (t) {
11581                 t = 255;                /* This lets us test \v(xxx) */
11582                 lp = line;              /* and even \f...(xxx) */
11583                 zzstring(s,&lp,&t);     /* Evaluate it, whatever it is. */
11584                 t = strlen(line);       /* Get its length. */
11585                 debug(F111,"chkvar",line,t);
11586                 z = t > 0;              /* If length > 0, it's defined */
11587             }
11588 #endif /* OLDCHKVAR */
11589         }
11590     }
11591     return(z);
11592 }
11593 
11594 /*  B O O L E X P  --  Evaluate a Boolean expression  */
11595 
11596 #define BOOLLEN 1024
11597 static char boolval[BOOLLEN];
11598 
11599 int
boolexp(cx)11600 boolexp(cx) int cx; {
11601     int x, y, z; char *s, *p;
11602     int parens = 0, pcount = 0, ecount = 0;
11603     char *q, *bx;
11604     struct FDB kw, nu;
11605 #ifdef FNFLOAT
11606     struct FDB fl;
11607     CKFLOAT f1 = 0.0, f2 = 0.0;
11608     int f1flag = 0, f2flag = 0;
11609 #endif /* FNFLOAT */
11610 #ifdef OS2
11611     extern int keymac;
11612 #endif /* OS2 */
11613     char varnam[VNAML+1];
11614 
11615     not = 0;                            /* Flag for whether "NOT" was seen */
11616     z = 0;                              /* Initial IF condition */
11617     ifargs = 0;                         /* Count of IF condition words */
11618     bx = boolval;                       /* Initialize boolean value */
11619     *bx = NUL;
11620 
11621   ifagain:
11622     cmfdbi(&kw,                         /* First FDB - command switches */
11623            _CMKEY,                      /* fcode */
11624            "Number, numeric-valued variable, Boolean expression, or keyword",
11625            "",                          /* default */
11626            "",                          /* addtl string data */
11627            nif,                         /* addtl numeric data 1: tbl size */
11628            0,                           /* addtl numeric data 2: 4 = silent */
11629            xxstring,                    /* Processing function */
11630            iftab,                       /* Keyword table */
11631            &nu                          /* Pointer to next FDB */
11632            );
11633     cmfdbi(&nu,                         /* 2nd FDB - An integer */
11634            _CMNUM,                      /* fcode */
11635            "",                          /* hlpmsg */
11636            "",                          /* Default */
11637            "",                          /* addtl string data */
11638            0,
11639            0,
11640            xxstring,
11641            NULL,
11642 #ifdef FNFLOAT
11643            &fl
11644 #else
11645            NULL
11646 #endif /* FNFLOAT */
11647            );
11648 #ifdef FNFLOAT
11649     cmfdbi(&fl,                         /* A floating-point number */
11650            _CMFLD,                      /* fcode */
11651            "",                          /* hlpmsg */
11652            "",                          /* default */
11653            "",                          /* addtl string data */
11654            0,                           /* addtl numeric data 1 */
11655            0,                           /* addtl numeric data 2 */
11656            xxstring,
11657            NULL,
11658            NULL
11659            );
11660 #endif /* FNFLOAT */
11661     x = cmfdb(&kw);                     /* Parse a keyword or a number */
11662     debug(F111,"boolval cmfdb","",x);
11663     if (x < 0) {
11664         if (x == -3)
11665           x = -2;
11666         return(x);
11667     }
11668     debug(F111,"boolval switch","",cmresult.fcode);
11669     switch (cmresult.fcode) {           /* What did we get? */
11670       case _CMFLD: {			/* A "field" */
11671 	  int i;
11672 	  char * s;
11673 	  s = cmresult.sresult;
11674 
11675 /* fdc 2013/12/06 - Remember the variable name for error messages */
11676 	  varnam[0] = NUL;
11677 	  i = ckindex("if ",cmdbuf,0,0,0);
11678           if (i) {
11679 	      i += 2;
11680 	      while (cmdbuf[i] == ' ') i++;
11681 	      if (cmdbuf[i]) {
11682 		  int j, k;
11683 		  j = i;
11684 		  k = 0;
11685 		  while (cmdbuf[j] && (cmdbuf[j] != ' ')) {
11686 		      varnam[k++] = cmdbuf[j++];
11687 		  }
11688 		  varnam[k] = NUL;
11689 	      }
11690           }
11691 /*
11692   C-Kermit 9.0: This allows a macro name to serve as an
11693   IF condition without having to enclose it in \m(...).
11694 */
11695 	  if (
11696 #ifdef FNFLOAT
11697 	      !isfloat(cmresult.sresult,0) /* Not a number */
11698 #else
11699 	      !chknum(cmresult.sresult) /* Not a number */
11700 #endif	/* FNFLOAT */
11701 	      ) {
11702 	      i = mlook(mactab,cmresult.sresult,nmac); /* Look it up */
11703 	      if (i > -1)		/* in the macro table */
11704 		s = mactab[x].mval;	/* and get its value */
11705 	      else			/* Otherwise if no such macro */
11706 #ifdef COMMENT
11707 		s = "0";		/* evaluate as FALSE (bad idea) */
11708 #else
11709 	      {
11710 		  if (varnam[0])
11711 		    printf("?Variable %s does not have a numeric value\n",
11712 			   varnam);
11713 		  else
11714 		    printf("?Not an IF condition, macro name or number: %s\n",
11715 			   cmresult.sresult);
11716 		  return(-9);
11717 	      }
11718 #endif /* COMMENT */
11719 	  }
11720 #ifdef FNFLOAT
11721         if (isfloat(s,0)) {		/* A floating-point number? */
11722             f1 = floatval;              /* Yes, get its value */
11723             f1flag = 1;                 /* remember we did this */
11724             ifc = 9999;                 /* Set special "if-code" */
11725         }
11726 #else
11727 	if (chknum(s)) {
11728 	    cmresult.nresult = atoi(s);
11729 	    ifc = 9999;
11730 	    break;
11731 	}
11732 #endif /* FNFLOAT */
11733 	else
11734 	  return(-2);
11735       }
11736       case _CMNUM:                      /* A number... */
11737         ifc = 9999;                     /* Set special "if-code" */
11738         break;
11739       case _CMKEY:                      /* A keyword */
11740         ifc = cmresult.nresult;         /* Get if-code */
11741         break;
11742       default:
11743         return(-2);
11744     }
11745     switch (ifc) {                      /* set z = 1 for true, 0 for false */
11746       case 9999:                        /* Number */
11747 #ifdef FNFLOAT
11748         if (f1flag) {
11749             z = (f1 == 0.0) ? 0 : 1;
11750         } else
11751 #endif /* FNFLOAT */
11752         z = (cmresult.nresult == 0) ? 0 : 1;
11753         break;
11754       case XXIFLP:                      /* Left paren */
11755         if (pcount == 0 && ifargs > 0)
11756           return(-2);
11757         parens = 1;
11758         pcount++;
11759         ifargs++;
11760         *bx++ = '(';
11761         goto ifagain;
11762       case XXIFRP:                      /* Right paren */
11763         if (!parens)
11764           return(-2);
11765         if (--pcount < 0)
11766           return(-2);
11767         ifargs++;
11768         *bx++ = ')';
11769         *bx = NUL;
11770         if (pcount == 0)
11771           goto ifend;
11772         goto ifagain;
11773       case XXIFAN:                      /* AND (&&) */
11774         ifargs++;
11775         if (!ecount)
11776           return(-2);
11777         *bx++ = '&';
11778         goto ifagain;
11779       case XXIFOR:                      /* OR (||) */
11780         ifargs++;
11781         if (!ecount)
11782           return(-2);
11783         *bx++ = '|';
11784         goto ifagain;
11785       case XXIFNO:                      /* IF NOT [ NOT [ NOT ... ] ] */
11786         if (bx > boolval) {             /* evala() doesn't like cascaded */
11787             if (*(bx-1) == '!') {       /* unary operators... */
11788                 *(bx-1) = NUL;          /* So here, two wrongs make a right. */
11789                 bx--;
11790             } else {
11791                 *bx++ = '!';
11792             }
11793         } else {
11794             *bx++ = '!';
11795         }
11796         ifargs++;
11797         goto ifagain;
11798       case XXIFTR:                      /* IF TRUE */
11799         z = 1;
11800         debug(F101,"if true","",z);
11801         ifargs += 1;
11802         break;
11803       case XXIFNT:                      /* IF FALSE */
11804         z = 0;
11805         debug(F101,"if true","",z);
11806         ifargs += 1;
11807         break;
11808       case XXIFSU:                      /* IF SUCCESS */
11809         z = ( success != 0 ) ? 1 : 0;
11810         debug(F101,"if success","",z);
11811         ifargs += 1;
11812         break;
11813       case XXIFFA:                      /* IF FAILURE */
11814         z = ( success == 0 ) ? 1 : 0;
11815         debug(F101,"if failure","",z);
11816         ifargs += 1;
11817         break;
11818 
11819       case XXIFDE:                      /* IF DEFINED */
11820         if ((x = cmfld("Macro or variable name","",&s,NULL)) < 0)
11821           return((x == -3) ? -2 : x);
11822 
11823         if (*s == CMDQ) {
11824             char * lp;
11825             char line[256];
11826             int t, x;
11827             if (*(s+1) == 'f' || *(s+1) == 'F') { /* Built-in function */
11828                 extern struct keytab fnctab[];
11829                 extern int nfuncs;
11830                 ckstrncpy(line,s+2,256);
11831                 if (line[0]) {
11832                     lp = ckstrchr(line,'(');
11833                     if (lp) *lp = NUL;
11834                     x = lookup(fnctab,line,nfuncs,&t);
11835                     z = x > -1;
11836                 }
11837                 debug(F111,"if defined function",line,z);
11838             } else if (*(s+1) == 'v' || *(s+1) == 'V') { /* 8.0.200 */
11839                 extern struct keytab vartab[];
11840                 extern int nvars;
11841                 z = 0;
11842                 if (*(s+2) == '(') {
11843                     ckstrncpy(line,s+3,256);
11844                     if (line[0]) {
11845                         lp = ckstrchr(line,')');
11846                         if (lp) *lp = NUL;
11847                         x = lookup(vartab,line,nvars,&t);
11848                         z = x > -1;
11849 			if (z) {	/* 8.0.203 */
11850 			    int t;	/* It must have a value to succeed */
11851 			    t = 255;	/* as in C-Kermit 6.0 and 7.0 */
11852 			    lp = line;	/* (this was broken in 8.0.200-201) */
11853 			    zzstring(s,&lp,&t);
11854 			    t = strlen(line);
11855 			    z = line[0] ? 1 : 0;
11856 			}
11857                     }
11858                 }
11859                 debug(F111,"if defined variable",line,z);
11860             } else {
11861                 z = chkvar(s);          /* Starts with backslash */
11862                 if (z > 0) {            /* Yes... */
11863                     t = 255;            /* than buffer so if zzstring fails  */
11864                     lp = line;          /* check for that -- overflow means */
11865                     line[0] = NUL;      /* the quantity is defined. */
11866                     x = zzstring(s,&lp,&t);
11867                     if ((x < 0 && t != 255) || !line[0])
11868                       z = 0;
11869                     debug(F111,"if defined zzstring",line,z);
11870                     debug(F101,"if defined zzstring t","",t);
11871                 }
11872             }
11873         } else {
11874             z = (mxlook(mactab,s,nmac) > -1); /* Look for exact match */
11875         }
11876         debug(F111,"if defined final",s,z);
11877         ifargs += 2;
11878         break;
11879 
11880       case XXIFDC: {                    /* IF DECLARED */
11881           char * lp;
11882           char line[32];
11883           int j, k, t, x;
11884           if ((x = cmfld("Array name","",&s,NULL)) < 0)
11885             return((x == -3) ? -2 : x);
11886           if (*s == CMDQ) {
11887               if (*(s+1) != '&') {
11888                   t = 31;
11889                   lp = line;
11890                   line[0] = NUL;
11891                   x = zzstring(s,&lp,&t);
11892                   s = line;
11893               }
11894           }
11895           z = 0;
11896           if ((x = arraybounds(s,&j,&k)) > -1) {
11897               if (a_ptr[x]) {
11898                   if (j < 1)
11899                     z = 1;
11900                   else if (j <= a_dim[x])
11901                     z = 1;
11902                   if (z == 1 && k > a_dim[x])
11903                     z = 0;
11904               }
11905           }
11906           break;
11907       }
11908       case XXIFBG:                      /* IF BACKGROUND */
11909       case XXIFFG:                      /* IF FOREGROUND */
11910         bgchk();                        /* Check background status */
11911         if (ifc == XXIFFG)              /* Foreground */
11912           z = pflag ? 1 : 0;
11913         else z = pflag ? 0 : 1;         /* Background */
11914         ifargs += 1;
11915         break;
11916 
11917       case XXIFCO:                      /* IF COUNT */
11918         z = ( --count[cmdlvl] > 0 );
11919         if (cx == XXWHI) count[cmdlvl] += 2; /* Don't ask... */
11920         debug(F101,"if count","",z);
11921         ifargs += 1;
11922         break;
11923 
11924       case XXIFEX:                      /* IF EXIST */
11925 #ifdef CK_TMPDIR
11926       case XXIFDI:                      /* IF DIRECTORY */
11927 #endif /* CK_TMPDIR */
11928       case XXIFAB:                      /* IF ABSOLUTE */
11929       case XXIFLN:			/* IF LINK */
11930         if ((x = cmfld(
11931                        ((ifc == XXIFDI) ? "Directory name" : "File"),
11932                        "",&s,
11933 #ifdef OS2
11934                        NULL             /* This allows \'s in filenames */
11935 #else
11936                        xxstring
11937 #endif /* OS2 */
11938                        )) < 0) {
11939             if (x == -3) {
11940                 extern int cmflgs;
11941                 if (cmflgs == 1) {
11942                     printf("?File or directory name required\n");
11943                     return(-9);
11944                 }
11945             } else return(x);
11946         }
11947         s = brstrip(s);
11948 #ifdef UNIX
11949 	if (ifc == XXIFLN) {
11950 	    z = isalink(s);
11951 	} else
11952 #endif	/* UNIX */
11953         if (ifc == XXIFAB) {
11954             z = isabsolute(s);
11955         } else if (ifc == XXIFEX) {
11956             z = (zgetfs(s) > -1L);
11957             debug(F101,"if exist 1","",z);
11958 #ifdef OS2
11959             if (!z) {                   /* File not found. */
11960                 int t;                  /* Try expanding variables */
11961                 t = LINBUFSIZ-1;        /* and looking again. */
11962                 lp = line;
11963                 zzstring(s,&lp,&t);
11964                 s = line;
11965                 z = ( zchki(s) > -1L );
11966                 debug(F101,"if exist 2","",z);
11967             }
11968 #endif /* OS2 */
11969 #ifdef CK_TMPDIR
11970         } else {
11971 #ifdef VMS
11972             z = (zchki(s) == -2)
11973 #else
11974 /* Because this doesn't catch $DISK1:[FOO]BLAH.DIR;1 */
11975             z = isdir(s)
11976 #ifdef OS2
11977               || (isalpha(s[0]) && s[1] == ':' && s[2] == NUL)
11978 #endif /* OS2 */
11979 #endif /* VMS */
11980               ;
11981             debug(F101,"if directory 1","",z);
11982 
11983             if (!z) {			/* File not found. */
11984                 int t;                  /* Try expanding variables */
11985                 t = LINBUFSIZ-1;        /* and looking again. */
11986                 lp = line;
11987                 zzstring(s,&lp,&t);
11988                 s = line;
11989                 z = isdir(s)
11990 #ifdef OS2
11991                   || (isalpha(s[0]) && s[1] == ':' && s[2] == NUL)
11992 #endif /* OS2 */
11993                     ;
11994                 debug(F101,"if directory 2","",z);
11995             }
11996 #endif /* CK_TMPDIR */
11997         }
11998         ifargs += 2;
11999         break;
12000 
12001       case XXIFEQ:                      /* IF EQUAL (string comparison) */
12002       case XXIFLL:                      /* IF Lexically Less Than */
12003       case XXIFLLE:			/* IF Lexically Less Than or Equal */
12004       case XXIFLG:                      /* If Lexically Greater Than */
12005       case XXIFLGE:			/* If Lexically Greater Than or Equal*/
12006       case XXIFNN:			/* If Lexically No Equal */
12007         if ((x = cmfld("first word or variable name","",&s,xxstring)) < 0) {
12008             if (x == -3) {
12009                 printf("?Text required\n");
12010                 return(-9);
12011             } else return(x);
12012         }
12013         s = brstrip(s);                 /* Strip braces */
12014         x = (int)strlen(s);
12015         if (x > LINBUFSIZ-1) {
12016             printf("?IF: strings too long\n");
12017             return(-2);
12018         }
12019         lp = line;                      /* lp points to first string */
12020         ckstrncpy(line,s,LINBUFSIZ);
12021         if ((y = cmfld("second word or variable name","",&s,xxstring)) < 0) {
12022             if (y == -3) {
12023                 printf("?Text required\n");
12024                 return(-9);
12025             } else return(y);
12026         }
12027         s = brstrip(s);
12028         y = (int)strlen(s);
12029         if (x + y + 2 > LINBUFSIZ) {
12030             printf("?IF: strings too long\n");
12031             return(-2);
12032         }
12033 #ifdef COMMENT
12034         tp = lp + x + 2;                /* tp points to second string */
12035         strncpy(tp,s,LINBUFSIZ-x-3);
12036 #else
12037 	tp = s;
12038 #endif /* COMMENT */
12039       lexical:
12040         x = ckstrcmp(lp,tp,-1,inpcas[cmdlvl]); /* Use longest length */
12041         switch (ifc) {
12042           case XXIFEQ:                  /* EQUAL (string comparison) */
12043             z = (x == 0);
12044             break;
12045           case XXIFLL:                  /* Lexically Less Than */
12046             z = (x < 0);
12047             break;
12048           case XXIFLLE:			/* Lexically Less Than or Equal */
12049             z = (x <= 0);
12050             break;
12051           case XXIFLG:                  /* Lexically Greater Than */
12052             z = (x > 0);
12053             break;
12054           case XXIFLGE:			/* Lexically Greater Than or Equal */
12055             z = (x >= 0);
12056             break;
12057 	  case XXIFNN:			/* Lexically Not Equal */
12058 	    z = (x != 0);
12059             break;
12060         }
12061         debug(F101,"IF EQ result","",z);
12062         ifargs += 3;
12063         break;
12064 
12065       case XXIFVE:                      /* IF VERSION */
12066       case XXIFAE:                      /* IF (arithmetically) = */
12067       case XXIFNQ:                      /* IF != (not arithmetically equal) */
12068       case XXIFLT:                      /* IF <  */
12069       case XXIFLE:                      /* IF <= */
12070       case XXIFGE:                      /* IF >= */
12071       case XXIFGT: {                    /* IF >  */
12072 	  /* July 2006 - converted to use CK_OFF_T rather than int to */
12073           /* allow long integers on platforms that have ck_off_t > 32 bits */
12074 	  int xx;
12075 	  CK_OFF_T n1 = (CK_OFF_T)0, n2 = (CK_OFF_T)0;
12076         if (ifc == XXIFVE) {
12077             n1 = (CK_OFF_T) vernum;
12078         } else {
12079             x = cmfld("first number or variable name","",&s,xxstring);
12080             if (x == -3) {
12081                 printf("?Quantity required\n");
12082                 return(-9);
12083             }
12084             if (x < 0) return(x);
12085             debug(F101,"xxifgt cmfld","",x);
12086             ckstrncpy(line,s,LINBUFSIZ);
12087             lp = brstrip(line);
12088             debug(F110,"xxifgt exp1",lp,0);
12089 
12090 /* The following bit is for compatibility with old versions of MS-DOS Kermit */
12091 
12092             if (!ckstrcmp(lp,"count",5,0)) {
12093                 n1 = (CK_OFF_T)count[cmdlvl];
12094             } else if (!ckstrcmp(lp,"version",7,0)) {
12095                 n1 = (CK_OFF_T) vernum;
12096             } else if (!ckstrcmp(lp,"argc",4,0)) {
12097                 n1 = (CK_OFF_T) macargc[maclvl];
12098             } else {
12099 
12100 /* End of compatibility bit */
12101 
12102 #ifdef FNFLOAT
12103                 if (isfloat(lp,0) > 1) { /* Allow floating-point comparisons */
12104                     f1 = floatval;
12105                     f1flag = 1;
12106                 } else
12107 #endif /* FNFLOAT */
12108                   if (chknum(lp)) {
12109                       n1 = ckatofs(lp);
12110                   } else {              /* Check for arithmetic expression */
12111                       q = evala(lp);    /* cmnum() does this but ... */
12112                       if (chknum(q)) {	/* we're not using cmnum(). */
12113 			  n1 = ckatofs(q);
12114 		      } else {
12115 			  printf("?Value not numeric - %s", lp);
12116 			  return(-9);
12117 		      }
12118                   }
12119             }
12120         }
12121         y = cmfld("number or variable name","",&s,xxstring);
12122         if (y == -3) {
12123             printf("?Quantity required\n");
12124             return(-9);
12125         }
12126         if (y < 0) return(y);
12127         s = brstrip(s);
12128         if (!*s) return(-2);
12129         if (ifc == XXIFVE) {
12130             tp = line;
12131         } else {
12132             x = (int)strlen(lp);
12133             tp = line + x + 2;
12134         }
12135         ckstrncpy(tp,s,LINBUFSIZ-x-2);
12136         debug(F110,"xxifgt exp2",tp,0);
12137 
12138         if (!ckstrcmp(tp,"count",5,0)) {
12139             n2 = (CK_OFF_T) count[cmdlvl];
12140         } else if (!ckstrcmp(tp,"version",7,0)) {
12141             n2 = (CK_OFF_T) vernum;
12142         } else if (!ckstrcmp(tp,"argc",4,0)) {
12143             n2 = (CK_OFF_T) macargc[maclvl];
12144         } else {
12145 #ifdef FNFLOAT
12146             if (isfloat(tp,0) > 1) {
12147                 f2 = floatval;
12148                 f2flag = 1;
12149             } else
12150 #endif /* FNFLOAT */
12151             if (chknum(tp)) {
12152                 n2 = ckatofs(tp);
12153             } else {
12154                 q = evala(tp);
12155 		if (chknum(q)) {	/* we're not using cmnum(). */
12156 		    n2 = ckatofs(q);
12157 		} else {
12158 		    printf("?Value not numeric - %s", tp);
12159 		    return(-9);
12160 		}
12161             }
12162         }
12163         xx = (ifc == XXIFVE) ? XXIFGE : ifc;
12164 
12165 #ifdef FNFLOAT
12166         if (f1flag && !f2flag) {
12167             f2 = (CKFLOAT)n2;
12168             f2flag = 1;
12169         }
12170         if (f2flag && !f1flag)
12171           f1 = (CKFLOAT)n1;
12172         if (f1flag)
12173           z = ((f1 <  f2 && xx == XXIFLT)
12174                || (f1 != f2 && xx == XXIFNQ)
12175                || (f1 <= f2 && xx == XXIFLE)
12176                || (f1 == f2 && xx == XXIFAE)
12177                || (f1 >= f2 && xx == XXIFGE)
12178                || (f1 >  f2 && xx == XXIFGT));
12179         else
12180 #endif /* FNFLOAT */
12181           z = ((n1 <  n2 && xx == XXIFLT)
12182                || (n1 != n2 && xx == XXIFNQ)
12183                || (n1 <= n2 && xx == XXIFLE)
12184                || (n1 == n2 && xx == XXIFAE)
12185                || (n1 >= n2 && xx == XXIFGE)
12186                || (n1 >  n2 && xx == XXIFGT));
12187         debug(F101,"xxifge z","",z);
12188         if (ifc == XXIFVE)
12189           ifargs += 2;
12190         else
12191           ifargs += 3;
12192         break;
12193       }
12194 
12195       case XXIFNU:                      /* IF NUMERIC */
12196         x = cmfld("variable name or constant","",&s,NULL);
12197         if (x == -3) {
12198             extern int cmflgs;
12199             if (cmflgs == 1) {
12200                 printf("?Quantity required\n");
12201                 return(-9);
12202             }
12203         } else if (x < 0)
12204           return(x);
12205         x = LINBUFSIZ-1;
12206         lp = line;
12207         zzstring(s,&lp,&x);
12208         lp = line;
12209         debug(F110,"xxifnu quantity",lp,0);
12210         z = chknum(lp);
12211 #ifdef COMMENT
12212 /*
12213   This works, but it's not wise -- IF NUMERIC is mostly used to see if a
12214   string really does contain only numeric characters.  If they want to force
12215   evaluation, they can use \feval() on the argument string.
12216 */
12217         if (!z) {                       /* Not a number */
12218             x_ifnum = 1;                /* Avoid "eval" error messages */
12219             q = evala(lp);              /* Maybe it's an expression */
12220             z = chknum(q);              /* that evaluates to a number */
12221             x_ifnum = 0;                /* Put eval messages back to normal */
12222             if (z) debug(F110,"xxifnu exp",lp,0);
12223         }
12224 #endif /* COMMENT */
12225         debug(F101,"xxifnu chknum","",z);
12226         ifargs += 2;
12227         break;
12228 
12229 #ifdef ZFCDAT
12230       case XXIFNE: {                    /* IF NEWER */
12231         char d1[20], * d2;              /* Buffers for 2 dates */
12232         if ((z = cmifi("First file","",&s,&y,xxstring)) < 0)
12233           return(z);
12234         ckstrncpy(d1,zfcdat(s),20);
12235         if ((z = cmifi("Second file","",&s,&y,xxstring)) < 0)
12236           return(z);
12237         d2 = zfcdat(s);
12238         if ((int)strlen(d1) != 17 || (int)strlen(d2) != 17) {
12239             printf("?Failure to get file date\n");
12240             return(-9);
12241         }
12242         debug(F110,"xxifnewer d1",d1,0);
12243         debug(F110,"xxifnewer d2",d2,0);
12244         z = (strcmp(d1,d2) > 0) ? 1 : 0;
12245         debug(F101,"xxifnewer","",z);
12246         ifargs += 2;
12247         break;
12248       }
12249 #endif /* ZFCDAT */
12250 
12251 #ifdef CK_IFRO
12252       case XXIFRO:                      /* REMOTE-ONLY advisory */
12253         ifargs++;
12254 #ifdef NOLOCAL
12255         z = 1;
12256 #else
12257         z = remonly;
12258 #endif /* NOLOCAL */
12259         break;
12260 #endif /* CK_IFRO */
12261 
12262       case XXIFAL:                      /* ALARM */
12263         ifargs++;
12264         debug(F101,"IF ALARM ck_alarm","",ck_alarm);
12265         debug(F110,"IF ALARM alrm_date",alrm_date,0);
12266         debug(F110,"IF ALARM alrm_time",alrm_time,0);
12267 
12268         if (ck_alarm < 1L || alrm_date[0] < '0' || alrm_time[0] < '0') {
12269             z = 0;                      /* ALARM not SET */
12270             break;                      /* so IF ALARM fails */
12271         }
12272         /* Get current date and time */
12273         ckstrncpy(tmpbuf,ckcvtdate("",1),TMPBUFSIZ);
12274         s = tmpbuf;
12275         s[8] = NUL;
12276         z = (int) strncmp(tmpbuf,alrm_date,8); /* Compare dates */
12277         debug(F101,"IF ALARM date z","",z);
12278         if (z == 0) {                   /* Dates are the same */
12279             /* Compare times */
12280             z = (tod2sec(tmpbuf+9) >= atol(alrm_time)) ? 1 : -1;
12281             debug(F101,"IF ALARM time z","",z);
12282         }
12283         tmpbuf[0] = NUL;                /* z >= 0 if alarm is passed */
12284         z = ((z >= 0) ? 1 : 0);         /* z <  0 otherwise */
12285         debug(F101,"IF ALARM final z","",z);
12286         break;
12287 
12288       case XXIFOP:                      /* IF OPEN */
12289         if ((x = cmkey(iotab,niot,"file or log","",xxstring)) < 0)
12290           return(x);
12291         if (x == 9999 || x == ZSTDIO) {
12292             bgchk();                    /* Check background status */
12293             z = pflag ? 1 : 0;
12294         } else if (x == 8888) {
12295             z = local ? ttchk() > -1 : 0;
12296 #ifdef CKLOGDIAL
12297         } else if (x == 7777) {
12298             extern int dialog;
12299             z = dialog ? 1 : 0;
12300 #endif /* CKLOGDIAL */
12301         } else
12302           z = (chkfn(x) > 0) ? 1 : 0;
12303         ifargs += 1;
12304         break;
12305 
12306       case XXIFSD:                      /* Started-From-Dialer */
12307 #ifdef OS2
12308         z = StartedFromDialer;
12309 #else
12310         z = 0;
12311 #endif /* OS2 */
12312         break;
12313 
12314       case XXIFTM:                      /* Terminal-Macro */
12315 #ifdef OS2
12316         z = cmdstk[cmdlvl].ccflgs & CF_KMAC;
12317 #else
12318         z = 0;
12319 #endif /* OS2 */
12320         break;
12321 
12322       case XXIFEM:                      /* Emulation is active */
12323 #ifdef OS2
12324         z = 1;
12325 #else
12326         z = 0;
12327 #endif /* OS2 */
12328         break;
12329 
12330       case XXIFIK:                      /* Running as IKSD? */
12331         z = inserver;
12332         break;
12333 
12334       case XXIFTA:                      /* Connection is TAPI */
12335         z = 0;
12336 #ifndef NODIAL
12337 #ifdef CK_TAPI
12338         if (local && !network && tttapi)
12339           z = 1;
12340 #endif /* CK_TAPI */
12341 #endif /* NODIAL */
12342         break;
12343 
12344       case XXIFMA:                      /* IF MATCH */
12345         x = cmfld("String or variable","",&s,xxstring);
12346         if (x == -3) {
12347             extern int cmflgs;
12348             if (cmflgs == 1) {
12349                 printf("?String required\n");
12350                 return(-9);
12351             }
12352         } else if (x < 0)
12353           return(x);
12354         ckstrncpy(line,s,LINBUFSIZ);
12355         s = brstrip(line);
12356         debug(F110,"xxifma string",line,0);
12357         x = cmfld("Pattern","",&p,xxstring);
12358         if (x == -3) {
12359             extern int cmflgs;
12360             if (cmflgs == 1) {
12361                 printf("?Pattern required\n");
12362                 return(-9);
12363             }
12364         } else if (x < 0)
12365           return(x);
12366         ckstrncpy(tmpbuf,p,TMPBUFSIZ);
12367         p = brstrip(tmpbuf);
12368         debug(F110,"xxifma pattern",tmpbuf,0);
12369         z = ckmatch(p,s,inpcas[cmdlvl],1);
12370         break;
12371 
12372       case XXIFFL: {                    /* IF FLAG */
12373           extern int ooflag;
12374           z = ooflag;
12375           break;
12376       }
12377       case XXIFAV: {                    /* IF AVAILABLE */
12378           if ((x = cmkey(availtab,availtabn,"","",xxstring)) < 0)
12379             return(x);
12380           switch (x) {
12381             case AV_KRB4:
12382               z = ck_krb4_is_installed();
12383               break;
12384             case AV_KRB5:
12385               z = ck_krb5_is_installed();
12386               break;
12387             case AV_SRP:
12388               z = ck_srp_is_installed();
12389               break;
12390             case AV_SSL:
12391               z = ck_ssleay_is_installed();
12392               break;
12393             case AV_NTLM:
12394               z = ck_ntlm_is_installed();
12395               break;
12396             case AV_CRYPTO:
12397               z = ck_crypt_is_installed();
12398               break;
12399             case AV_SSH:
12400               z = ck_ssh_is_installed();
12401               break;
12402             default:
12403               z = 0;
12404           }
12405           break;
12406       }
12407       case XXIFAT:                      /* IF ASKTIMEOUT */
12408         z = asktimedout;
12409         break;
12410 
12411       case XXIFRD:                      /* IF READABLE */
12412       case XXIFWR:                      /* IF WRITEABLE */
12413         if ((x = cmfld("File or directory name",
12414                        "",
12415                        &s,
12416 #ifdef OS2
12417                        NULL             /* This allows \'s in filenames */
12418 #else
12419                        xxstring
12420 #endif /* OS2 */
12421                        )) < 0) {
12422             if (x == -3) {
12423                 extern int cmflgs;
12424                 if (cmflgs == 1) {
12425                     printf("?File or directory name required\n");
12426                     return(-9);
12427                 }
12428             } else return(x);
12429         }
12430         s = brstrip(s);
12431 /*
12432   zchk[io]() do not do what we want here for directories, so we set
12433   a global flag telling it to behave specially in this case.  Othewise
12434   we'd have to change the API and change all ck?fio.c modules accordingly.
12435 */
12436         y = 0;                          /* Try-again control */
12437 #ifdef OS2
12438   ifrdagain:
12439 #endif /* OS2 */
12440         if (ifc == XXIFRD) {            /* IF READABLE */
12441             zchkid = 1;
12442             z = zchki(s) > -1;
12443             zchkid = 0;
12444         } else if (ifc == XXIFWR) {     /* IF WRITEABLE */
12445             zchkod = 1;
12446             z = zchko(s) > -1;
12447             zchkod = 0;
12448         }
12449 #ifdef OS2
12450         if (!z && !y) {                 /* File not found. */
12451             int t;                      /* Try expanding variables */
12452             t = LINBUFSIZ-1;            /* and looking again. */
12453             lp = line;
12454             zzstring(s,&lp,&t);
12455             s = line;
12456             z = zchko(s) > -1;
12457             y++;
12458             goto ifrdagain;
12459         }
12460 #endif /* OS2 */
12461         ifargs += 2;
12462         break;
12463       case XXIFQU:                      /* IF QUIET */
12464         z = quiet ? 1 : 0;
12465         debug(F101,"if quiet","",z);
12466         ifargs += 1;
12467         break;
12468 
12469       case XXIFWI:                      /* WILD */
12470         if ((x = cmfld("File specification","",&s,xxstring)) < 0) return(x);
12471         z = iswild(s);
12472         break;
12473 
12474       case XXIFCK:                      /* C-KERMIT */
12475 #ifdef OS2
12476         z = 0;
12477 #else
12478         z = 1;
12479 #endif /* OS2 */
12480         break;
12481 
12482       case XXIFK9:                      /* K-95 */
12483 #ifdef OS2
12484         z = 1;
12485 #else
12486         z = 0;
12487 #endif /* OS2 */
12488         break;
12489 
12490       case XXIFGU:                      /* GUI */
12491 #ifdef KUI
12492         z = 1;
12493 #else
12494         z = 0;
12495 #endif /* KUI */
12496         break;
12497 
12498       case XXIFMS:                      /* MS-KERMIT */
12499         z = 0;
12500         break;
12501 
12502       case XXIFLO:                      /* IF LOCAL */
12503         z = local ? 1 : 0;
12504         break;
12505 
12506       case XXIFCM: {                    /* IF COMMAND */
12507           extern struct keytab cmdtab[];
12508           extern int ncmd;
12509           if ((x = cmfld("Word","",&s,xxstring)) < 0)
12510             return(x);
12511           z = lookup(cmdtab,s,ncmd,&y);
12512           z = (z == -2 || z > -1) ? 1 : 0;
12513           break;
12514       }
12515 #ifdef CKFLOAT
12516       case XXIFFP:                      /* IF FLOAT */
12517         if ((x = cmfld("Number","",&s,xxstring)) < 0)
12518           if (x != -3)                  /* e.g. empty variable */
12519             return(x);
12520         z = isfloat(s,0);
12521         break;
12522 #endif /* CKFLOAT */
12523 
12524       case XXIFKB:                      /* KBHIT */
12525         z = conchk();
12526         if (z < 0) z = 0;
12527         if (z > 1) z = 1;
12528         break;
12529 
12530       case XXIFKG: {                    /* KERBANG */
12531           extern int cfilef;
12532 #ifdef COMMENT
12533           z = (xcmdsrc == 0) ? 0 : (cfilef && cmdlvl == 1);
12534 #else
12535           z = (xcmdsrc == 0) ? 0 : (cfilef && tlevel == 0);
12536 #endif	/* COMMENT */
12537           break;
12538       }
12539 
12540       case XXIFDB: {			/* IF DEBUG - 2010/03/16 */
12541           extern int debmsg;
12542           z = debmsg;
12543 	  break;
12544       }
12545       case XXIFFU: {			/* IF FUNCTION - 2013/04/15 */
12546 	  extern struct keytab fnctab[];
12547 	  extern int nfuncs;
12548 	  int x, y;
12549 
12550 	  y = cmkeyx(fnctab,nfuncs,"Name of function","",NULL);
12551 	  if (y == -1)			/* Reparse needed */
12552 	    return(y);
12553 	  if (y < 0) {			/* Something given but didn't match */
12554 	      int dummy;
12555 	      char * p;
12556 	      for (p = atmbuf; *p; p++) { /* Chop off trailing parens if any */
12557 		  if (*p == '(') {
12558 		      *p = NUL;
12559 		      break;
12560 		  }
12561 	      }
12562 	      /* Chop off leading "\\f" or "\f" or "f" */
12563 	      p = atmbuf;
12564 	      if (*p == CMDQ)		/* Allow for \\f... */
12565 		p++;
12566 	      if (*p == CMDQ && (*(p+1) == 'f' || *(p+1) == 'F')) { /* or \f */
12567 		  p += 2;
12568 	      } else if (*p == 'f' || *p == 'F') { /* or just f */
12569 		  p++;
12570 	      }
12571 	      y = lookup(fnctab,p,nfuncs,&dummy); /* Look up the result */
12572 	  }
12573 	  z = (y < 1) ? 0 : 1;
12574 	  break;
12575       }
12576       case XXIFTXT:			/* IF TEXT - 2013/04/17 */
12577       case XXIFBIN: {			/* IF BINARY - 2013/04/17 */
12578 	  if ((x = cmifi("Filename","",&s,&y,xxstring)) < 0)
12579 	    return(x);
12580 	  if (y) {
12581 	      printf("?Please enter the name of a single file\n");
12582 	      return(-9);
12583 	  }
12584 	  if (isdir(s)) {		/* Is the file a directory? */
12585 	      z = 0;
12586 	  } else {
12587 	      x = scanfile(s,NULL,nscanfile);
12588 	      if (x < 0) {
12589 		  printf("?Problem scanning %s\n",s);
12590 		  return(-9);
12591 	      }
12592 	      switch (x) {
12593 		case FT_BIN:		/* Binary */
12594 		  z = (ifc == XXIFBIN) ? 1 : 0;
12595 		  break;
12596 		case FT_7BIT:		/* Different text encodings */
12597 		case FT_8BIT:
12598 		case FT_UTF8:
12599 		case FT_UCS2:
12600 		case FT_TEXT:
12601 		  z = (ifc == XXIFTXT) ? 1 : 0;
12602 		  break;
12603 		default:
12604 		  printf("?Problem scanning %s\n",s);
12605 		  return(-9);
12606 	      }
12607 	  }
12608 	  break;
12609       }
12610       default:                          /* Shouldn't happen */
12611         return(-2);
12612     } /* end of switch */
12613 
12614     if (z)
12615       *bx++ = '1';
12616     else
12617       *bx++ = '0';
12618     *bx = NUL;
12619     if (bx > boolval + BOOLLEN - 2) {
12620         printf("?Boolean expression too long");
12621         return(-9);
12622     }
12623     ecount++;                           /* Expression counter */
12624     debug(F101,"boolexp parens","",parens);
12625     debug(F101,"boolexp pcount","",pcount);
12626     if (parens && pcount > 0)
12627       goto ifagain;
12628 
12629   ifend:                                /* No more - done */
12630     *bx = NUL;
12631     z = atoi(evalx(boolval));
12632     debug(F111,"boolexp boolval",boolval,z);
12633     return(z);
12634 }
12635 
12636 /*  D O I F  --  Do the IF command  */
12637 
12638 int
doif(cx)12639 doif(cx) int cx; {
12640     int x, y, z; char *s, *p;
12641     char *q;
12642 #ifdef OS2
12643     extern int keymac;
12644 #endif /* OS2 */
12645 
12646     debug(F101,"doif cx","",cx);
12647 
12648 /* Boolexp() calls the parsing functions: cmkey, cmnum, cmfld */
12649 
12650     z = boolexp(cx);                    /* Evaluate the condition(s) */
12651     debug(F010,"doif cmdbuf",cmdbuf,0);
12652     debug(F101,"doif boolexp","",z);
12653     if (z < 0)
12654       return(z);
12655 
12656     if (cx == XXIF) {                   /* Allow IF to have XIF semantics. */
12657         char * p;
12658         p = cmpeek();
12659         if (!p) p = "";
12660         while (*p) {
12661             if (*p == SP || *p == HT)
12662               p++;
12663             else
12664               break;
12665         }
12666         if (*p == '{')
12667           cx = XXIFX;
12668     }
12669     switch (cx) {                       /* Separate handling for IF and XIF */
12670 
12671       case XXASSER:                     /* And ASSERT */
12672         if ((x = cmcfm()) < 0)
12673           return(x);
12674         return(success = z);
12675 
12676       case XXIF:                        /* This is IF... */
12677         ifcmd[cmdlvl] = 1;              /* We just completed an IF command */
12678         debug(F101,"doif condition","",z);
12679         if (z) {                        /* Condition is true */
12680             iftest[cmdlvl] = 1;         /* Remember that IF succeeded */
12681             if (maclvl > -1) {          /* In macro, */
12682                 pushcmd(NULL);          /* save rest of command. */
12683             } else if (tlevel > -1) {   /* In take file, */
12684                 debug(F100, "doif: pushing command", "", 0);
12685                 pushcmd(NULL);          /* save rest of command. */
12686             } else {                    /* If interactive, */
12687                 cmini(ckxech);          /* just start a new command */
12688                 printf("\n");           /* (like in MS-DOS Kermit) */
12689                 if (pflag) prompt(xxstring);
12690             }
12691         } else {                        /* Condition is false */
12692             iftest[cmdlvl] = 0;         /* Remember command failed. */
12693             if ((y = cmtxt("command to be ignored","",&s,NULL)) < 0)
12694               return(y);                /* Gobble up rest of line */
12695         }
12696         return(0);
12697 
12698       case XXIFX: {                     /* This is XIF (Extended IF) */
12699           char *p;
12700           char e[5];
12701           int i;
12702           if ((y = cmtxt("Object command","",&s,NULL)) < 0)
12703             return(y);                  /* Get object command. */
12704           p = s;
12705           lp = line;
12706           debug(F110,"doif THEN part",s,-54);
12707           if (litcmd(&p,&lp,LINBUFSIZ - 1) < 0) { /* Quote THEN-part */
12708               return(-2);
12709           }
12710           debug(F111,"doif THEN part 2",line,z);
12711 
12712           while (*p == SP) p++;         /* Strip trailing spaces */
12713           ifcmd[cmdlvl] = 0;            /* Assume ELSE part in same line */
12714           iftest[cmdlvl] = z ? 1 : 0;
12715           if (*p) {                     /* At end? */
12716               if (!z) {                 /* No, use ELSE-part, if any */
12717                   for (i = 0; i < 4; i++) e[i] = *p++;
12718                   if (ckstrcmp(e,"else",4,0)) /* See if we have an ELSE */
12719                     return(-2);         /* Something else - error. */
12720                   debug(F010,"doif ELSE line 1",p,0);
12721                   while (*p == SP) p++; /* Skip spaces */
12722                   if (*p != '{') {      /* Brace ELSE part if necessary */
12723                       ckmakmsg(tmpbuf,TMPBUFSIZ,"{",p," }",NULL);
12724                       p = tmpbuf;
12725                       debug(F010,"doif ELSE line 2",p,0);
12726                   }
12727                   lp = line;            /* Write over THEN part... */
12728                   *lp = NUL;            /* with ELSE part. */
12729                   if (litcmd(&p,&lp,LINBUFSIZ - 2) < 0) {
12730                       return(-2);
12731                   }
12732                   while (*p == SP) p++; /* Strip trailing spaces */
12733                   if (*p) return(-2);   /* Should be nothing here. */
12734                   debug(F010,"doif ELSE line 3",line,0);
12735               }
12736           } else {                      /* At end, treat like an IF command */
12737               if (!z) line[0] = NUL;    /* Condition not true and no ELSE */
12738               ifcmd[cmdlvl] = 1;        /* Allow ELSE on next line */
12739               debug(F101,"IF condition","",z);
12740           }
12741           if (line[0]) {
12742               x = mlook(mactab,"_xif",nmac); /* Get index of "_xif" macro. */
12743               if (x < 0) {                      /* Not there? */
12744                   addmmac("_xif",xif_def);      /* Put it back. */
12745                   if (mlook(mactab,"_xif",nmac) < 0) { /* Look it up again. */
12746                       printf("?XIF macro gone!\n");
12747                       return(success = 0);
12748                   }
12749               }
12750               dodo(x,line,cmdstk[cmdlvl].ccflgs | CF_IMAC);
12751           }
12752           return(0);
12753       }
12754       case XXWHI: {                     /* WHILE Command */
12755           p = cmdbuf;                   /* Capture IF condition */
12756           ifcond[0] = NUL;              /* from command buffer */
12757           while (*p == SP) p++;
12758           while (*p != SP) p++;
12759           ifcp = ifcond;
12760           ifcp += ckstrncpy(ifcp,"{ \\flit(if ( not ",IFCONDLEN);
12761 #ifdef COMMENT
12762 /*
12763   This doesn't work because it breaks on the first left brace, which does
12764   not necessarily start the command list, e.g. "while equal \%a {\35}".
12765 */
12766           while (*p != '{' && *p != NUL) *ifcp++ = *p++;
12767           p = " ) goto _..bot) } ";
12768           while (*ifcp++ = *p++) ;
12769 #else
12770 /*
12771   The command parser sets cmbptr to the spot where it left off parsing in
12772   the command buffer.
12773 */
12774           {
12775               extern char * cmbptr;
12776               if (cmbptr) {
12777                   while (p < cmbptr && *p != NUL)
12778                     *ifcp++ = *p++;
12779                   p = " ) goto _..bot) } ";
12780                   while ((*ifcp++ = *p++)) ;
12781               } else {
12782                   printf("?Internal error parsing WHILE condition\n");
12783                   return(-9);
12784               }
12785           }
12786 #endif /* COMMENT */
12787 
12788           debug(F110,"WHILE cmd",ifcond,0);
12789 
12790           if ((y = cmtxt("Object command","",&s,NULL)) < 0)
12791             return(y);                  /* Get object command. */
12792           p = s;
12793           lp = line;
12794           if (litcmd(&p,&lp,LINBUFSIZ - 2) < 0) { /* Quote object command */
12795               return(-2);
12796           }
12797           debug(F111,"WHILE body",line,-54);
12798           if (line[0]) {
12799               char *p;
12800               x = mlook(mactab,"_while",nmac); /* index of "_while" macro. */
12801               if (x < 0) {              /* Not there? */
12802                   addmmac("_while",whil_def); /* Put it back. */
12803                   /* Look it up again */
12804                   if ((x = mlook(mactab,"_while",nmac)) < 0) {
12805                       printf("?WHILE macro definition gone!\n");
12806                       return(success = 0);
12807                   }
12808               }
12809               p = malloc((int)strlen(ifcond) + (int)strlen(line) + 2);
12810               if (p) {
12811                   strcpy(p,ifcond);     /* safe (prechecked) */
12812                   strcat(p,line);       /* safe (prechecked) */
12813                   debug(F110,"WHILE dodo",p,0);
12814                   dodo(x,p,cmdstk[cmdlvl].ccflgs | CF_IMAC);
12815                   free(p);
12816                   p = NULL;
12817               } else {
12818                   printf("?Can't allocate storage for WHILE command");
12819                   return(success = 0);
12820               }
12821           }
12822           return(0);
12823       }
12824       default:
12825         return(-2);
12826     }
12827 }
12828 #endif /* NOSPL */
12829 
12830 /* Set up a TAKE command file */
12831 
12832 int
dotake(s)12833 dotake(s) char *s; {
12834 #ifndef NOSPL
12835     extern char lasttakeline[];         /* Last TAKE-file line */
12836     extern int tra_cmd;
12837 #endif /* NOSPL */
12838 #ifndef NOLOCAL
12839 #ifdef OS2
12840     extern int term_io;
12841     int term_io_sav = term_io;
12842 #endif /* OS2 */
12843 #endif /* NOLOCAL */
12844     int slen;
12845 
12846     debug(F110,"dotake",s,0);
12847     if (!s) s = "";
12848     if (!*s) return(success = 0);
12849     slen = strlen(s);
12850     debug(F101,"dotake len","",slen);
12851 
12852     if ((tfile[++tlevel] = fopen(s,"r")) == NULL) {
12853         perror(s);
12854         debug(F110,"dotake fail",s,0);
12855         tlevel--;
12856         return(success = 0);
12857     } else {
12858         lasttakeline[0] = NUL;
12859         tfline[tlevel] = 0;             /* Line counter */
12860 #ifdef VMS
12861         conres();                       /* So Ctrl-C will work */
12862 #endif /* VMS */
12863 #ifndef NOLOCAL
12864 #ifdef OS2
12865         term_io = 0;                    /* Disable Terminal Emulator I/O */
12866 #endif /* OS2 */
12867 #endif /* NOLOCAL */
12868 #ifndef NOSPL
12869         cmdlvl++;                       /* Entering a new command level */
12870         debug(F111,"CMD +F",s,cmdlvl);
12871         debug(F101,"dotake cmdlvl","",cmdlvl);
12872         debug(F101,"dotake tlevel","",tlevel);
12873         if (cmdlvl > CMDSTKL) {
12874             debug(F100,"dotake stack overflow","",0);
12875             cmdlvl--;
12876             debug(F111,"CMD*-F",s,cmdlvl);
12877             fclose(tfile[tlevel--]);
12878             printf("?TAKE files and/or DO commands nested too deeply\n");
12879             return(success = 0);
12880         }
12881         if (tfnam[tlevel]) {            /* Copy the filename */
12882             free(tfnam[tlevel]);
12883             tfnam[tlevel] = NULL;
12884         }
12885         if ((tfnam[tlevel] = malloc(strlen(s) + 1))) {
12886             strcpy(tfnam[tlevel],s);    /* safe */
12887         } else {
12888             printf("?Memory allocation failure\n");
12889             return(success = 0);
12890         }
12891         ifcmd[cmdlvl] = 0;              /* Set variables for this cmd file */
12892         iftest[cmdlvl] = 0;
12893         count[cmdlvl]  = count[cmdlvl-1];  /* Inherit this */
12894         intime[cmdlvl] = intime[cmdlvl-1]; /* Inherit this */
12895         inpcas[cmdlvl] = inpcas[cmdlvl-1]; /* Inherit this */
12896         takerr[cmdlvl] = takerr[cmdlvl-1]; /* Inherit this */
12897         merror[cmdlvl] = merror[cmdlvl-1]; /* Inherit this */
12898         xquiet[cmdlvl] = quiet;
12899         xvarev[cmdlvl] = vareval;
12900         xcmdsrc = CMD_TF;
12901         cmdstk[cmdlvl].src = CMD_TF;    /* Say we're in a TAKE file */
12902         cmdstk[cmdlvl].lvl = tlevel;    /* nested at this level */
12903         cmdstk[cmdlvl].ccflgs = cmdstk[cmdlvl-1].ccflgs;
12904 #else
12905         takerr[tlevel] = takerr[tlevel-1]; /* Inherit this */
12906 #endif /* NOSPL */
12907     }
12908 #ifndef NOSPL
12909     if (tra_cmd)
12910       printf("[%d] +F: \"%s\"\n",cmdlvl,s);
12911 #endif /* NOSPL */
12912 #ifndef NOLOCAL
12913 #ifdef OS2
12914     term_io = term_io_sav;
12915 #endif /* OS2 */
12916 #endif /* NOLOCAL */
12917     return(1);
12918 }
12919 #endif /* NOICP */
12920