1 #include "ckcsym.h"
2 
3 /*  C K U U S 4 --  "User Interface" for C-Kermit, part 4  */
4 
5 /*
6   Authors:
7     Frank da Cruz <fdc@columbia.edu>,
8       The Kermit Project, New York City
9     Jeffrey E Altman <jaltman@secure-endpoints.com>
10       Secure Endpoints Inc., New York City
11 
12   Copyright (C) 1985, 2021,
13     Trustees of Columbia University in the City of New York.
14     All rights reserved.  See the C-Kermit COPYING.TXT file or the
15     copyright text in the ckcmai.c module for disclaimer and permissions.
16     Last update:
17     Tue Sep 14 15:09:25 2021
18 */
19 
20 /*
21   File ckuus4.c -- Functions moved from other ckuus*.c modules to even
22   out their sizes.
23 */
24 #include "ckcdeb.h"
25 #include "ckcasc.h"
26 #include "ckcker.h"
27 #include "ckcnet.h"                     /* Network symbols */
28 #include "ckuusr.h"
29 #include "ckuver.h"
30 #include "ckcxla.h"                     /* Character sets */
31 
32 #ifdef HAVE_LOCALE
33 #include <locale.h>
34 #endif /* HAVE_LOCALE */
35 
36 #ifdef CK_AUTHENTICATION
37 #include "ckuath.h"
38 #endif /* CK_AUTHENTICATION */
39 #ifdef CK_SSL
40 #include "ck_ssl.h"
41 #endif /* CK_SSL */
42 
43 #ifdef VMS
44 #include <errno.h>                      /* For \v(errno) */
45 extern char * ckvmserrstr(unsigned long);
46 #ifndef OLD_VMS
47 #include <lib$routines.h>               /* Not for VAX C 2.4 */
48 #else
49 #include <libdef.h>
50 #endif /* OLD_VMS */
51 _PROTOTYP(int vmsttyfd, (void) );
52 #endif /* VMS */
53 
54 #ifdef OS2
55 #ifndef NT
56 #define INCL_NOPM
57 #define INCL_VIO                        /* Needed for ckocon.h */
58 #include <os2.h>
59 #undef COMMENT
60 #else
61 #include <windows.h>
62 #include <tapi.h>
63 #include "ckntap.h"
64 #define APIRET ULONG
65 #endif /* NT */
66 #include "ckocon.h"
67 #include "ckodir.h" /* [jt] 2013/11/21 - for MAXPATHLEN */
68 #include "ckoetc.h"
69 int StartedFromDialer = 0;
70 HWND hwndDialer = 0;
71 LONG KermitDialerID = 0;
72 #ifdef putchar
73 #undef putchar
74 #endif /* putchar */
75 #define putchar(x) conoc(x)
76 #ifdef CK_PID
77 #include <process.h>
78 #endif /* CK_PID */
79 #endif /* OS2 */
80 
81 #ifdef KUI
82 extern struct keytab * term_font;
83 extern int ntermfont, tt_font, tt_font_size;
84 #endif /* KUI */
85 
86 extern xx_strp xxstring;
87 
88 #ifdef DEC_TCPIP
89 #include <descrip>
90 #include <dvidef>
91 #include <dcdef>
92 #endif /* DEC_TCPIP */
93 
94 #ifndef NOICP
95 extern char *tfnam[];
96 extern int tlevel;
97 #endif  /* NOICP */
98 
99 #ifdef FNFLOAT
100 #include <math.h>                       /* Floating-point functions */
101 #endif /* FNFLOAT */
102 
103 int fp_rounding = 0;                  /* Nonzero if printf("%f") rounds */
104 int fp_digits = 0;		      /* Digits of floating point precision */
105 
106 extern int quiet, network, xitsta, escape, nopush, xferstat,
107   exitonclose, tn_exit, ttnproto, autodl, flow, byteorder, what, lastxfer;
108 
109 extern int filepeek, nscanfile, makestrlen;
110 extern char * k_info_dir;
111 
112 #ifndef MAC
113 #ifndef AMIGA
114 extern int ttyfd;
115 #endif /* MAC */
116 #endif /* AMIGA */
117 
118 #ifdef TNCODE
119 extern int tn_nlm, tn_b_nlm, tn_b_xfer, tn_sb_bug;
120 extern int tn_rem_echo;
121 extern int tn_b_meu, tn_b_ume, tn_auth_krb5_des_bug;
122 #endif /* TNCODE */
123 
124 static char * lastkwval = NULL;
125 
126 char * xferfile = NULL;
127 int xferlog = 0;
128 
129 extern int local, xargc, stayflg, rcflag, bgset, backgrd, cfilef,
130   inserver, srvcdmsg, success;
131 
132 #ifdef VMS
133 extern int batch;
134 #endif /* VMS */
135 
136 extern char cmdfil[], *versio, *ckxsys, **xargv;
137 extern char lasttakeline[];
138 #ifdef DEBUG
139 extern char debfil[];                   /* Debug log file name */
140 extern int debtim;
141 #endif /* DEBUG */
142 
143 extern int noinit;
144 
145 static char ndatbuf[10];
146 
147 char *months[] = {
148     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
149     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
150 };
151 
152 char *
zzndate()153 zzndate() {                             /* Returns today's date as yyyymmdd */
154     char * p = NULL;
155     int x;
156 
157 /* WARNING - This will fail if asctime() returns non-English month names */
158 
159     ztime(&p);                          /* Get "asctime" string */
160     if (p == NULL || *p == NUL) return("");
161     for (x = 20; x < 24; x++)           /* yyyy */
162       ndatbuf[x - 20] = p[x];
163     ndatbuf[6] = (char) ((p[8] == ' ') ? '0' : p[8]);
164     ndatbuf[7] = p[9];                  /* dd */
165     for (x = 0; x < 12; x++)            /* mm */
166       if (!strncmp(p+4,months[x],3)) break;
167     if (x == 12) {
168         ndatbuf[4] = ndatbuf[5] = '?';
169     } else {
170         x++;
171         ndatbuf[4] = (char) ((x < 10) ? '0' : '1');
172         ndatbuf[5] = (char) ((x % 10) + 48);
173     }
174     ndatbuf[8] = NUL;
175     debug(F110,"zzndate return",ndatbuf,0);
176     return((char *)ndatbuf);
177 }
178 
179 #ifdef DCMDBUF
180 extern struct cmdptr *cmdstk;
181 extern char *line, *tmpbuf;
182 #else
183 extern struct cmdptr cmdstk[];
184 extern char line[], tmpbuf[];
185 #endif /* DCMDBUF */
186 
187 #ifdef OS2
188 extern char exedir[];
189 #else
190 extern char * exedir;
191 #endif /* OS2 */
192 
193 extern int nettype;
194 
195 #ifndef NOICP                           /* Most of this file... */
196 #ifdef CKLOGDIAL
197 extern char diafil[];
198 #endif /* CKLOGDIAL */
199 
200 #ifndef AMIGA
201 #ifndef MAC
202 #include <signal.h>
203 #endif /* MAC */
204 #endif /* AMIGA */
205 
206 #ifdef SV68				/* July 2006 believe it or not */
207 #ifndef SEEK_CUR
208 #include <unistd.h>
209 #endif	/* SEEK_CUR */
210 #endif	/* SV68 */
211 
212 #ifdef SCO32				/* June 2011 believe it or not... */
213 #ifdef XENIX
214 #ifndef SEEK_CUR
215 #include <unistd.h>
216 #endif	/* SEEK_CUR */
217 #endif	/* XENIX */
218 #endif	/* SCO32 */
219 
220 #ifdef STRATUS                          /* Stratus Computer, Inc.  VOS */
221 #ifdef putchar
222 #undef putchar
223 #endif /* putchar */
224 #define putchar(x) conoc(x)
225 #ifdef getchar
226 #undef getchar
227 #endif /* getchar */
228 #define getchar(x) coninc(0)
229 #endif /* STRATUS */
230 
231 
232 #ifdef ANYX25
233 extern int revcall, closgr, cudata;
234 int x25ver;
235 extern char udata[];
236 #ifndef IBMX25
237 extern int npadx3;
238 extern CHAR padparms[];
239 extern struct keytab padx3tab[];
240 #endif /* !IBMX25 */
241 #ifdef IBMX25
242 /* global variables only available for IBM X.25 - possibly interesting for
243  * other implementations
244  */
245 extern x25addr_t local_nua;
246 extern x25addr_t remote_nua;
247 #endif /* IBMX25 */
248 #endif /* ANYX25 */
249 
250 #ifdef NETCONN
251 #ifndef NODIAL
252 extern int nnetdir;
253 extern char *netdir[];
254 #endif /* NODIAL */
255 extern char ipaddr[];
256 
257 #ifdef CK_NETBIOS
258 extern unsigned short netbiosAvail;
259 extern unsigned long NetbeuiAPI;
260 extern unsigned char NetBiosName[];
261 extern unsigned char NetBiosAdapter;
262 extern unsigned char NetBiosLSN;
263 #endif /* CK_NETBIOS */
264 
265 #ifdef TCPSOCKET
266 extern char myipaddr[];
267 extern int tcp_rdns;
268 #ifdef CK_DNS_SRV
269 extern int tcp_dns_srv;
270 #endif /* CK_DNS_SRV */
271 extern char * tcp_address;
272 #ifndef NOHTTP
273 extern char * tcp_http_proxy;
274 #endif /* NOHTTP */
275 #ifdef NT
276 #ifdef CK_SOCKS
277 extern char * tcp_socks_svr;
278 #ifdef CK_SOCKS_NS
279 extern char * tcp_socks_ns;
280 #endif /* CK_SOCKS_NS */
281 #endif /* CK_SOCKS */
282 #endif /* NT */
283 
284 #ifndef NOTCPOPTS
285 #ifdef SOL_SOCKET
286 #ifdef SO_LINGER
287 extern int tcp_linger;
288 extern int tcp_linger_tmo;
289 #endif /* SO_LINGER */
290 #ifdef SO_DONTROUTE
291 extern int tcp_dontroute;
292 #endif /* SO_DONTROUTE */
293 #ifdef TCP_NODELAY
294 extern int tcp_nodelay;
295 #endif /* TCP_NODELAY */
296 #ifdef SO_SNDBUF
297 extern int tcp_sendbuf;
298 #endif /* SO_SNDBUF */
299 #ifdef SO_RCVBUF
300 extern int tcp_recvbuf;
301 #endif /* SO_RCVBUF */
302 #ifdef SO_KEEPALIVE
303 extern int tcp_keepalive;
304 #endif /* SO_KEEPALIVE */
305 #endif /* SOL_SOCKET */
306 #endif /* NOTCPOPTS */
307 #endif /* TCPSOCKET */
308 #endif /* NETCONN */
309 
310 extern char * floname[];
311 
312 #ifndef NOSPL
313 extern int vareval;			/* Variable evaluation method */
314 extern int fndiags;                     /* Function diagnostics on/off */
315 extern int divbyzero;
316 int itsapattern = 0;
317 int isinbuflen = 0;
318 int isjoin = 0;
319 #ifdef CK_APC
320 extern int apcactive;                   /* Nonzero = APC command was rec'd */
321 extern int apcstatus;                   /* Are APC commands being processed? */
322 #ifdef DCMDBUF
323 extern char *apcbuf;                    /* APC command buffer */
324 #else
325 extern char apcbuf[];
326 #endif /* DCMDBUF */
327 #endif /* CK_APC */
328 
329 extern char evalbuf[];                  /* EVALUATE result */
330 extern char uidbuf[], pwbuf[], prmbuf[];
331 _PROTOTYP( static char * fneval, (char *, char * [], int, char * ) );
332 _PROTOTYP( static VOID myflsh, (void) );
333 _PROTOTYP( static char * getip, (char *) );
334 _PROTOTYP( int delta2sec, (char *, long *) );
335 
336 #ifdef NEWFTP
337 _PROTOTYP( char * ftp_cpl_mode, (void) );
338 _PROTOTYP( char * ftp_dpl_mode, (void) );
339 _PROTOTYP( char * ftp_authtype, (void) );
340 #endif /* NEWFTP */
341 
342 #ifndef NOHTTP
343 _PROTOTYP( char * http_host, (void) );
344 _PROTOTYP( int http_isconnected, (void) );
345 _PROTOTYP( char * http_security, (void) );
346 #endif /* NOHTTP */
347 
348 #ifndef NOSEXP
349 _PROTOTYP( char * dosexp, (char *) );
350 int fsexpflag = 0;
351 #endif /* NOSEXP */
352 
353 static char hexdigits[16] = {
354     '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
355 };
356 extern char * tempdir;
357 
358 #ifdef CK_REXX
359 extern char rexxbuf[];
360 #endif /* CK_REXX */
361 
362 extern int tfline[];
363 
364 char *wkdays[] = {
365     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
366 };
367 #endif /* NOSPL */
368 
369 #ifdef OS2
370 extern char startupdir[], inidir[];
371 #else
372 #ifdef VMSORUNIX
373 extern char startupdir[];
374 #endif /* VMSORUNIX */
375 #endif /* OS2 */
376 
377 #ifdef OS2
378 _PROTOTYP (int os2getcp, (void) );
379 #ifdef TCPSOCKET
380 extern char tcpname[];
381 #endif /* TCPSOCKET */
382 extern int tcp_avail;
383 #ifdef DECNET
384 extern int dnet_avail;
385 #endif /* DECNET */
386 #ifdef SUPERLAT
387 extern int slat_avail;
388 #endif /* SUPERLAT */
389 
390 #ifndef NOTERM
391 extern int tt_type, max_tt;
392 extern struct tt_info_rec tt_info[];
393 #endif /* NOTERM */
394 extern int tt_rows[], tt_cols[];
395 #else /* OS2 */
396 extern int tt_rows, tt_cols;
397 #endif /* OS2 */
398 
399 #ifdef CK_TAPI
400 extern int tttapi;
401 extern int tapipass;
402 extern struct keytab * tapilinetab;
403 extern struct keytab * _tapilinetab;
404 extern int ntapiline;
405 #endif /* CK_TAPI */
406 
407 extern struct keytab colxtab[];
408 extern int ncolx;
409 
410 extern char ttname[], *zinptr, *kermrc;
411 extern char inidir[];
412 
413 extern int activecmd, remonly, cmd_rows, cmd_cols, parity, seslog,
414   sessft, sosi, hwparity, tsecs, xargs, zincnt, tlevel, insilence, cmdmsk,
415   timint, timef, inbufsize, dialog, binary, carrier, cdtimo, cmask, duplex,
416   fmask, inecho, nmac, turnch, turn, kbchar;
417 
418 #ifndef NOXFER
419 extern CHAR eol,  mypadc, mystch, padch, seol, stchr, * epktmsg, feol;
420 extern char *cksysid;
421 extern struct ck_p ptab[];
422 extern int
423   protocol, prefixing, xfrbel, xfrcan, xfrint, xfrchr, xfrnum, pktpaus,
424   lscapr, lscapu, xfermode, dest, slostart, maxrps, maxsps, maxtry, mypadn,
425   npad, pkttim, bigrbsiz, bigsbsiz, keep, atcapr, autopar, bctr, bctu,
426   crunched, ckdelay, ebq, ebqflg, pktlog, retrans, rpackets, rptflg, rptq,
427   rtimo, spackets, spsiz, spsizf, spsizr, timeouts, fncact, fncnv, urpsiz,
428   wmax, wslotn, wslotr, fdispla, spmax, fnrpath, fnspath;
429 extern long crc16;
430 #endif /* NOXFER */
431 
432 #ifdef OS2
433 extern int zxpn;
434 extern int viewonly;
435 #endif /* OS2 */
436 
437 #ifndef NOXFER
438 #ifdef GFTIMER
439 extern CKFLOAT fptsecs, fpxfsecs;
440 #endif /* GFTIMER */
441 extern long xfsecs, tfcps;
442 
443 #ifdef CK_TMPDIR
444 extern char *dldir;
445 #endif /* CK_TMPDIR */
446 #endif /* NOXFER */
447 
448 #ifdef RECURSIVE
449 extern int recursive;
450 #endif /* RECURSIVE */
451 
452 #ifdef VMS
453 extern int frecl;
454 #endif /* VMS */
455 
456 extern CK_OFF_T ffc, tfc, tlci, tlco;
457 extern long filcnt, rptn, speed,  ccu, ccp, vernum, xvernum;
458 
459 #ifndef NOSPL
460 extern char fspec[], myhost[];
461 #endif /* NOSPL */
462 
463 extern char *tfnam[];                   /* Command file names */
464 
465 extern char pktfil[],                   /* Packet log file name */
466 #ifdef TLOG
467   trafil[],                             /* Transaction log file name */
468 #endif /* TLOG */
469   sesfil[];                             /* Session log file name */
470 
471 #ifndef NOXMIT                          /* TRANSMIT command variables */
472 extern char xmitbuf[];
473 extern int xmitf, xmitl, xmitx, xmits, xmitw, xmitt;
474 #endif /* NOXMIT */
475 
476 extern int cmdlvl;
477 
478 #ifndef NOSPL
479 /* Script programming language items */
480 extern char **a_ptr[];                  /* Arrays */
481 extern int a_dim[];
482 static char * inpmatch = NULL;
483 #ifdef CKFLOAT
484 char * inpscale = NULL;
485 #endif	/* CKFLOAT */
486 extern char * inpbuf, inchar[];         /* Buffers for INPUT and REINPUT */
487 extern char *inpbp;                     /* And pointer to same */
488 static char *r3 = (char *)0;
489 extern int incount;                     /* INPUT character count */
490 extern int m_found;                     /* MINPUT result */
491 extern int maclvl;                      /* Macro invocation level */
492 extern struct mtab *mactab;             /* Macro table */
493 extern char *mrval[];
494 extern int macargc[], topargc;
495 
496 #ifdef COMMENT
497 extern char *m_line[];
498 extern char *topline;
499 #endif /* COMMENT */
500 
501 extern char *m_arg[MACLEVEL][10]; /* You have to put in the dimensions */
502 extern char *g_var[GVARS];        /* for external 2-dimensional arrays. */
503 #ifdef DCMDBUF
504 extern int *count, *inpcas;
505 #else
506 extern int count[], inpcas[];
507 #endif /* DCMDBUF */
508 #endif /* NOSPL */
509 
510 #ifdef UNIX
511 extern int haslock;                     /* For UUCP locks */
512 extern char flfnam[];
513 #ifndef USETTYLOCK
514 extern char lock2[];
515 #endif /* USETTYLOCK */
516 #endif /* UNIX */
517 
518 #ifdef OS2ORUNIX
519 extern int maxnam, maxpath;             /* Longest name, path length */
520 #endif /* OS2ORUNIX */
521 
522 extern int mdmtyp, mdmsav;
523 
524 #ifndef NODIAL
525 /* DIAL-related variables */
526 extern char modemmsg[];
527 extern MDMINF *modemp[];                /* Pointers to modem info structs */
528 extern int nmdm, dialhng, dialtmo, dialksp, dialdpy, dialsrt, dialsta;
529 extern int dialrtr, dialint, dialrstr, dialcon, dialcq, dialfld;
530 extern int mdmspd, dialec, dialdc, dialmth, dialmauto, dialesc;
531 extern char *dialnum,   *dialini,  *dialdir[], *dialcmd,  *dialnpr,
532  *dialdcon, *dialdcoff, *dialecon, *dialecoff, *dialhcmd, *diallac,
533  *dialhwfc, *dialswfc,  *dialnofc, *dialpulse, *dialtone, *dialname,
534  *dialaaon, *dialaaoff, *dialmac;
535 extern char *diallcc,   *dialixp,  *dialixs,   *dialldp,  *diallds,
536  *dialpxi,  *dialpxo,   *dialsfx,  *dialtfp;
537 extern char *diallcp,   *diallcs;
538 extern int ntollfree, ndialpxx, nlocalac;
539 extern char *dialtfc[], *diallcac[], *dialpxx[], *matchpxx;
540 extern int ndialpucc, ndialtocc;
541 extern char *dialtocc[], *dialpucc[];
542 extern int ndialdir, dialcnf, dialcvt, dialidt, dialpace;
543 extern long dialmax, dialcapas;
544 
545 extern struct keytab mdmtab[];
546 
547 #ifdef BIGBUFOK
548 #define ARGBUFSIZ 8191
549 #else
550 #define ARGBUFSIZ 1023
551 #endif /* BIGBUFOK */
552 
553 #ifdef BIGBUFOK
554 extern char * dialmsg[];
555 #endif /* BIGBUFOK */
556 
557 #endif /* NODIAL */
558 
559 #ifndef NOCSETS
560 /* Translation stuff */
561 extern int nfilc;
562 extern struct keytab fcstab[];
563 extern int fcharset, tcharset, tslevel, language, nlng, tcsr, tcsl;
564 extern int dcset7, dcset8;
565 extern struct keytab lngtab[];
566 extern struct csinfo fcsinfo[], tcsinfo[];
567 extern struct langinfo langs[];
568 #ifdef CK_ANSIC
569 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Character set */
570 extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* translation functions */
571 #else
572 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); /* Character set */
573 extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(); /* translation functions. */
574 #endif /* CK_ANSIC */
575 #ifdef UNICODE
576     extern int ucsbom, ucsorder;
577 #endif /* UNICODE */
578 #endif /* NOCSETS */
579 
580 #ifndef NOSPL
581 /* Built-in variable names, maximum length VNAML (20 characters) */
582 
583 struct keytab vartab[] = {
584     { "_line",     VN_TFLN,  CM_INV},   /* 192 */
585     { "apcactive", VN_APC,   CM_INV},   /* 192 */
586 #ifdef NT
587     { "appdata",   VN_APPDATA, 0},      /* 201 */
588 #endif /* NT */
589     { "argc",      VN_ARGC,  0},
590     { "args",      VN_ARGS,  0},
591     { "authname",  VN_AUTHN, 0},        /* 196 */
592     { "authstate", VN_AUTHS, 0},        /* 195 */
593     { "authtype",  VN_AUTHT, 0},        /* 195 */
594     { "bits",      VN_BITS,  0},        /* 212 */
595     { "blockcheck",VN_BLK,   0},        /* 195 */
596 #ifdef BROWSER
597     { "browser",   VN_BROWSR,0},        /* 193 */
598     { "browsopts", VN_BROPT, 0},        /* 193 */
599     { "browsurl",  VN_URL,   0},        /* 193 */
600     { "buildid",   VN_BUILD, 0},        /* 199 */
601 #endif /* BROWSER */
602     { "byteorder", VN_BYTE,  0},        /* 195 */
603 #ifndef NOCSETS
604     { "charset",   VN_CSET,  0},        /* 192 */
605 #endif /* NOCSETS */
606     { "cmdbufsize",VN_CMDBL, 0},        /* 195 */
607     { "cmdfile",   VN_CMDF,  0},
608     { "cmdlevel",  VN_CMDL,  0},
609     { "cmdsource", VN_CMDS,  0},
610     { "cols",      VN_COLS,  0},        /* 190 */
611 #ifdef NT
612     { "common",    VN_COMMON, 0},       /* 201 */
613 #endif /* NT */
614     { "connection",VN_CONN,  0},        /* 190 */
615     { "count",     VN_COUN,  0},
616 #ifndef NOXFER
617     { "cps",       VN_CPS,   0},        /* 190 */
618 #endif /* NOXFER */
619     { "cpu",       VN_CPU,   0},
620 #ifndef NOXFER
621     { "crc16",     VN_CRC16, 0},        /* 192 */
622     { "ctty",      VN_TTYNAM,0},        /* 196 */
623 #endif /* NOXFER */
624 #ifndef NOLOGDIAL
625 #ifndef NOLOCAL
626     { "cx_time",   VN_CXTIME,0},        /* 195 */
627     { "cx_status", VN_CX_STA,0},        /* 199 */
628 #endif /* NOLOCAL */
629 #endif /* NOLOGDIAL */
630 #ifndef NODIAL
631     { "d$ac",      VN_D_AC,  0},        /* 192 */
632     { "d$cc",      VN_D_CC,  0},        /* 192 */
633     { "d$ip",      VN_D_IP,  0},        /* 192 */
634     { "d$lc",      VN_D_LCP, 0},        /* 193 */
635     { "d$lcp",     VN_D_LCP, CM_INV},   /* 193 */
636     { "d$lp",      VN_D_LP,  0},        /* 192 */
637     { "d$px",      VN_D_PXX, 0},        /* 195 */
638     { "d$pxx",     VN_D_PXX, CM_INV},   /* 195 */
639 #endif /* NODIAL */
640     { "date",      VN_DATE,  0},
641     { "day",       VN_DAY,   0},
642 #ifdef NT
643     { "desktop",   VN_DESKTOP, 0},     /* 201 */
644 #endif /* NT */
645 #ifndef NODIAL
646     { "dialcount", VN_DRTR,  0},        /* 195 */
647     { "dialmessage",VN_DMSG, 0},	/* 212 */
648     { "dialnumber",VN_DNUM,  0},        /* 192 */
649     { "dialresult",VN_MDMSG, 0},        /* 192 */
650     { "dialstatus",VN_DIAL,  0},        /* 190 */
651     { "dialsuffix",VN_PDSFX, 0},        /* 193 */
652     { "dialtype",  VN_DTYPE, 0},        /* 193 */
653 #endif /* NODIAL */
654     { "directory", VN_DIRE,  0},
655 #ifndef NODIAL
656     { "dm_hf",     VN_DM_HF, 0},        /* 199 */
657     { "dm_lp",     VN_DM_LP, 0},        /* 195 */
658     { "dm_sp",     VN_DM_SP, 0},        /* 195 */
659     { "dm_pd",     VN_DM_PD, 0},        /* 195 */
660     { "dm_td",     VN_DM_TD, 0},        /* 195 */
661     { "dm_wa",     VN_DM_WA, 0},        /* 195 */
662     { "dm_wb",     VN_DM_WB, 0},        /* 199 */
663     { "dm_wd",     VN_DM_WD, 0},        /* 195 */
664     { "dm_rc",     VN_DM_RC, 0},        /* 195 */
665 #endif /* NODIAL */
666 #ifndef NOXFER
667     { "download",  VN_DLDIR, 0},        /* 192 */
668 #endif /* NOXFER */
669     { "editor",    VN_EDITOR,0},
670     { "editfile",  VN_EDFILE,0},
671     { "editopts",  VN_EDOPT, 0},
672     { "errno",     VN_ERRNO, 0},        /* 192 */
673     { "errstring", VN_ERSTR, 0},        /* 192 */
674     { "escape",    VN_ESC,   0},        /* 193 */
675     { "evaluate",  VN_EVAL,  0},        /* 190 */
676 #ifdef OS2ORUNIX
677     { "exedir",    VN_EXEDIR,0},        /* 192 */
678 #endif /* OS2ORUNIX */
679     { "exitstatus",VN_EXIT,  0},
680 #ifdef CKCHANNELIO
681     { "f_count",   VN_FCOU,  0},        /* 195 */
682     { "f_error",   VN_FERR,  0},        /* 195 */
683     { "f_max",     VN_FMAX,  0},        /* 195 */
684     { "fileerror", VN_FERR,  CM_INV},   /* 195 */
685     { "filemax",   VN_FERR,  CM_INV},   /* 195 */
686 #endif /* CKCHANNELIO */
687     { "filename",  VN_FNAM,  0},        /* 193 */
688     { "filenumber",VN_FNUM,  0},        /* 193 */
689     { "filespec",  VN_FILE,  0},
690     { "fsize",     VN_FFC,   0},        /* 190 */
691 #ifdef GFTIMER
692     { "ftime",     VN_FTIME, 0},        /* 199 */
693 #else
694     { "ftime",     VN_NTIM,  CM_INV},
695 #endif /* GFTIMER */
696 #ifndef NOFTP
697 #ifndef SYSFTP
698     { "ftp_code",         VN_FTP_C, 0}, /* 199 */
699     { "ftp_cpl",          VN_FTP_B, 0}, /* 199 */
700     { "ftp_connected",    VN_FTP_X, 0}, /* 199 */
701     { "ftp_dpl",          VN_FTP_D, 0}, /* 199 */
702     { "ftp_getputremote", VN_FTP_G, 0}, /* 199 */
703     { "ftp_host",         VN_FTP_H, 0}, /* 199 */
704     { "ftp_loggedin",     VN_FTP_L, 0}, /* 199 */
705     { "ftp_message",      VN_FTP_M, 0}, /* 199 */
706     { "ftp_msg",          VN_FTP_M, CM_INV}, /* 199 */
707     { "ftp_security",     VN_FTP_Z, 0}, /* 199 */
708     { "ftp_server",       VN_FTP_S, 0}, /* 199 */
709 #endif /* SYSFTP */
710 #endif /* NOFTP */
711     { "ftype",     VN_MODE,  0},        /* 190 */
712 #ifdef KUI
713     { "gui_fontname", VN_GUI_FNM, 0},	/* 205 */
714     { "gui_fontsize", VN_GUI_FSZ, 0},	/* 205 */
715     { "gui_runmode", VN_GUI_RUN, 0},    /* 205 */
716     { "gui_xpos",    VN_GUI_XP,  0},    /* 205 */
717     { "gui_xres",    VN_GUI_XR,  0},    /* 205 */
718     { "gui_ypos",    VN_GUI_YP,  0},    /* 205 */
719     { "gui_yres",    VN_GUI_YR,  0},    /* 205 */
720 #endif /* KUI */
721     { "herald",    VN_HERALD, 0},
722     { "home",      VN_HOME,   0},
723     { "host",      VN_HOST,   0},
724     { "hour",      VN_HOUR,   0},       /* 200 */
725 #ifndef NOHTTP
726     { "http_code",      VN_HTTP_C, 0},  /* 199 */
727     { "http_connected", VN_HTTP_N, 0},  /* 199 */
728     { "http_host",      VN_HTTP_H, 0},  /* 199 */
729     { "http_message",   VN_HTTP_M, 0},  /* 199 */
730     { "http_security",  VN_HTTP_S, 0},  /* 199 */
731 #endif /* NOHTTP */
732     { "hwparity",  VN_HWPAR, 0},        /* 195 */
733     { "input",     VN_IBUF,  0},
734     { "inchar",    VN_ICHR,  0},
735     { "incount",   VN_ICNT,  0},
736     { "inidir",    VN_INI,   0},        /* 192 */
737     { "inmatch",   VN_MATCH, 0},        /* 196 */
738     { "inmessage", VN_INPMSG,0},        /* 212 */
739     { "inscale",   VN_ISCALE,0},        /* 210 */
740     { "instatus",  VN_ISTAT, 0},        /* 192 */
741     { "intime",    VN_INTIME,0},        /* 193 */
742     { "inwait",    VN_INTMO, 0},        /* 195 */
743     { "ip",        VN_IPADDR, CM_ABR|CM_INV},
744     { "ipaddress", VN_IPADDR,0},        /* 192 */
745     { "iprompt",   VN_PROMPT,0},        /* 199 */
746     { "kbchar",    VN_KBCHAR,0},        /* 196 */
747 #ifndef NOLOCAL
748 #ifdef OS2
749     { "keyboard",  VN_KEYB,  0},
750 #endif /* OS2 */
751 #endif /* NOLOCAL */
752 #ifdef CK_KERBEROS
753     { "krb4errmsg",    VN_K4EMSG,0},
754     { "krb4errno",     VN_K4ENO, 0},
755     { "krb4principal", VN_K4PRN, 0},
756     { "krb4realm",     VN_K4RLM, 0},
757     { "krb4service",   VN_K4SRV, 0},
758     { "krb5cc",        VN_K5CC,  0},
759     { "krb5errmsg",    VN_K5EMSG,0},
760     { "krb5errno",     VN_K5ENO, 0},
761     { "krb5principal", VN_K5PRN, 0},
762     { "krb5realm",     VN_K5RLM, 0},
763     { "krb5service",   VN_K5SRV, 0},
764 #endif /* CK_KERBEROS */
765     { "lastcommand",   VN_PREVCMD, 0},	/* 299 */
766 #ifndef NOLASTFILE
767     { "lastfilespec",  VN_LASTFIL, 0},	/* 212 */
768 #endif	/* NOLASTFILE */
769     { "lastkeywordvalue",  VN_LASTKWV, 0}, /* 212 */
770     { "lastkwvalue",   VN_LASTKWV, CM_ABR|CM_INV}, /* 212 */
771     { "line",          VN_LINE,  0},
772     { "local",         VN_LCL,   0},
773 #ifdef UNIX
774     { "lockdir",       VN_LCKDIR,0},	/* 195 */
775     { "lockpid",       VN_LCKPID,0},	/* 195 */
776 #endif /* UNIX */
777     { "log_connection", VN_LOG_CON, 0}, /* 206 */
778     { "log_debug", VN_LOG_DEB, 0},      /* 206 */
779     { "log_packet", VN_LOG_PKT, 0},     /* 206 */
780     { "log_session", VN_LOG_SES, 0},    /* 206 */
781     { "log_transaction", VN_LOG_TRA, 0},/* 206 */
782     { "maclevel",  VN_MACLVL,0},        /* 195 */
783     { "macro",     VN_MAC,   0},
784 #ifdef FNFLOAT
785     { "math_e",    VN_MA_E,  0},        /* 195 */
786     { "math_pi",   VN_MA_PI, 0},        /* 195 */
787     { "math_precision", VN_MA_PR, 0},   /* 195 */
788 #endif /* FNFLOAT */
789     { "minput",    VN_MINP,  0},        /* 192 */
790     { "model",     VN_MODL,  0},        /* 193 */
791     { "modem",     VN_MDM,   0},
792     { "month",     VN_MONTH, 0},	/* 304 */
793 #ifndef NOLOCAL
794 #ifdef OS2
795     { "mousecurx", VN_MOU_X, 0},        /* K95 1.1.14 */
796     { "mousecury", VN_MOU_Y, 0},        /* K95 1.1.14 */
797 #endif /* OS2 */
798 #endif /* NOLOCAL */
799 #ifndef NODIAL
800     { "m_aa_off",  VN_M_AAX, 0},        /* all 192... */
801     { "m_aa_on",   VN_M_AAO, 0},
802     { "m_dc_off",  VN_M_DCX, 0},
803     { "m_dc_on",   VN_M_DCO, 0},
804     { "m_dial",    VN_M_DCM, 0},
805     { "m_ec_off",  VN_M_ECX, 0},
806     { "m_ec_on",   VN_M_ECO, 0},
807     { "m_fc_hw",   VN_M_HWF, 0},
808     { "m_fc_no",   VN_M_NFC, 0},
809     { "m_fc_sw",   VN_M_SWF, 0},
810     { "m_hup",     VN_M_HUP, 0},
811     { "m_init",    VN_M_INI, 0},
812     { "m_name",    VN_M_NAM, 0},        /* 195 */
813     { "m_pulse",   VN_M_PDM, 0},
814     { "m_sig_cd",  VN_MS_CD, 0},        /* 195 */
815     { "m_sig_cts", VN_MS_CTS,0},        /* 195 */
816     { "m_sig_dsr", VN_MS_DSR,0},        /* 195 */
817     { "m_sig_dtr", VN_MS_DTR,0},        /* 195 */
818     { "m_sig_ri",  VN_MS_RI, 0},        /* 195 */
819     { "m_sig_rts", VN_MS_RTS,0},        /* 195 */
820     { "m_tone",    VN_M_TDM, 0},
821 #endif /* NODIAL */
822     { "name",      VN_NAME,  0},
823     { "ndate",     VN_NDAT,  0},
824     { "nday",      VN_NDAY,  0},
825     { "newline",   VN_NEWL,  0},
826     { "nmonth",    VN_NMONTH,0},	/* 304 */
827     { "ntime",     VN_NTIM,  0},
828     { "osname",    VN_OSNAM, 0},        /* 193 */
829     { "osrelease", VN_OSREL, 0},        /* 193 */
830     { "osversion", VN_OSVER, 0},        /* 193 */
831 #ifndef NOXFER
832     { "packetlen", VN_RPSIZ, 0},        /* 192 */
833 #endif /* NOXFER */
834     { "parity",    VN_PRTY,  0},        /* 190 */
835     { "password",  VN_PWD,   CM_INV},   /* 192 */
836 #ifdef NT
837     { "personal",  VN_PERSONAL, 0},     /* 201 */
838 #endif /* NT */
839 #ifdef PEXITSTAT
840     { "pexitstat", VN_PEXIT, 0},        /* 193 */
841 #endif /* PEXITSTAT */
842 #ifdef CK_PID
843     { "pid",       VN_PID,   0},        /* 193 */
844 #endif /* CK_PID */
845     { "platform",  VN_SYSV,  0},
846     { "printer",   VN_PRINT, 0},        /* 193 */
847     { "program",   VN_PROG,  0},
848     { "prompt",    VN_PRM,   CM_INV},   /* 192 */
849 #ifndef NOXFER
850     { "protocol",  VN_PROTO, 0},        /* 192 */
851     { "p_8bit",    VN_P_8BIT,0},        /* 193 */
852     { "p_ctl",     VN_P_CTL, 0},        /* 193 */
853     { "p_rpt",     VN_P_RPT, 0},        /* 193 */
854     { "query",     VN_QUE,   0},        /* 190 */
855 #endif /* NOXFER */
856     { "remoteip",  VN_HOSTIP,0},	/* 212 */
857     { "return",    VN_RET,   0},
858 #ifdef CK_REXX
859     { "rexx",      VN_REXX,  0},        /* 190 */
860 #endif /* CK_REXX */
861 #ifdef TN_COMPORT
862     { "rfc2217_signature", VN_TNC_SIG, 0}, /* 201 */
863     { "rfc2717_signature", VN_TNC_SIG, CM_INV}, /* 202 */
864 #endif /* TN_COMPORT */
865     { "rows",      VN_ROWS,  0},        /* 190 */
866 #ifndef NOSEXP
867     { "sdepth",    VN_LSEXP,0},         /* 199 */
868 #endif /* NOSEXP */
869     { "secure",    VN_SECURE, 0},       /* 199 */
870 #ifndef NOLOCAL
871 #ifdef OS2
872     { "select",    VN_SELCT, 0},        /* 192 */
873 #endif /* OS2 */
874 #endif /* NOLOCAL */
875     { "sendlist",  VN_SNDL,  0},
876     { "serial",    VN_SERIAL,0},        /* 195 */
877     { "setlinemsg",VN_SLMSG, 0},        /* 195 */
878 #ifndef NOSEXP
879     { "sexpression",VN_SEXP, 0},        /* 199 */
880 #endif /* NOSEXP */
881     { "speed",     VN_SPEE,  0},
882 #ifdef OS2
883     { "space",     VN_SPA,   0},
884     { "startup",   VN_STAR,  0},        /* 190 */
885 #else
886 #ifdef UNIX
887     { "startup",   VN_STAR,  0},        /* 193 */
888 #else
889 #ifdef VMS
890     { "startup",   VN_STAR,  0},        /* 193 */
891 #endif /* VMS */
892 #endif /* UNIX */
893 #endif /* OS2 */
894     { "status",    VN_SUCC,  0},
895 #ifndef NOSEXP
896     { "svalue",    VN_VSEXP, 0},        /* 199 */
897 #endif /* NOSEXP */
898 #ifndef NOXFER
899     { "sysid",     VN_SYSI,  0},
900 #endif /* NOXFER */
901     { "system",    VN_SYST,  0},
902     { "terminal",  VN_TTYP,  0},
903 #ifdef OS2
904 #ifndef NOKVERBS
905     { "termkey",   VN_TRMK,  CM_INV},   /* 192 */
906 #endif /* NOKVERBS */
907 #endif /* OS2 */
908     { "test",      VN_TEST,  0},        /* 193 */
909     { "textdir",   VN_TXTDIR,0},        /* 195 */
910 #ifndef NOXFER
911     { "tfsize",    VN_TFC,   0},
912     { "tftime",    VN_TFTIM, 0},        /* 195 */
913 #endif /* NOXFER */
914     { "time",      VN_TIME,  0},
915     { "timestamp", VN_NOW,   0},        /* 200 */
916     { "tmpdir",    VN_TEMP,  0},        /* 192 */
917 #ifdef CK_TRIGGER
918     { "trigger",   VN_TRIG,  0},        /* 193 */
919 #endif /* CK_TRIGGER */
920 #ifdef CK_TTYFD
921     { "ttyfd",     VN_TTYF,  0},
922 #endif /* CK_TTYFD */
923     { "ty_ln",     VN_TY_LN, 0},        /* 195 */
924     { "ty_lc",     VN_TY_LC, 0},        /* 195 */
925     { "ty_lm",     VN_TY_LM, 0},        /* 195 */
926 #ifdef BROWSER
927     { "url",       VN_URL,   CM_INV},   /* 193 */
928 #endif /* BROWSER */
929     { "userid",    VN_UID,   0},        /* 192 */
930     { "vareval",   VN_VAREVAL, 0},	/* 212 */
931     { "version",   VN_VERS,  0},
932 #ifndef NOXFER
933     { "window",    VN_WINDO, 0},        /* 192 */
934 #endif /* NOXFER */
935 #ifdef IBMX25
936     { "x25local_nua", VN_X25LA, 0},     /* 193 */
937     { "x25remote_nua", VN_X25RA, 0},    /* 193 */
938 #endif /* IBMX25 */
939 #ifdef CK_SSL
940     { "x509_issuer",  VN_X509_I, 0},
941     { "x509_subject", VN_X509_S, 0},
942 #endif /* CK_SSL */
943 #ifndef NOXFER
944     { "xferstatus",VN_XFSTAT,0},        /* 193 */
945     { "xfermsg",   VN_XFMSG, 0},        /* 193 */
946     { "xfer_badpackets", VN_XF_BC, 0},  /* 195 */
947     { "xfer_timeouts",   VN_XF_TM, 0},  /* 195 */
948     { "xfer_retransmits",VN_XF_RX, 0},  /* 195 */
949 #endif /* NOXFER */
950     { "xprogram",  VN_XPROG, 0},        /* 193 */
951     { "xversion",  VN_XVNUM, 0},        /* 192 */
952     { "year",      VN_YEAR, 0}		/* 304 */
953 };
954 int nvars = (sizeof(vartab) / sizeof(struct keytab));
955 #endif /* NOSPL */
956 
957 #ifndef NOSPL
958 struct keytab fnctab[] = {              /* Function names */
959 #ifdef OS2
960     { ".oox",       FN_OOX, CM_INV},    /* ... */
961 #endif /* OS2 */
962 
963 #ifdef CKCHANNELIO
964     { "_eof",       FN_FEOF,   0},
965     { "_errmsg",    FN_FERMSG, 0},
966     { "_getblock",  FN_FGBLK,  0},
967     { "_getchar",   FN_FGCHAR, 0},
968     { "_getline",   FN_FGLINE, 0},
969     { "_handle",    FN_FILNO,  0},
970     { "_line",      FN_NLINE,  0},
971     { "_pos",       FN_FPOS,   0},
972     { "_putblock",  FN_FPBLK,  0},
973     { "_putchar",   FN_FPCHAR, 0},
974     { "_putline",   FN_FPLINE, 0},
975     { "_status",    FN_FSTAT,  0},
976 #endif /* CKCHANNELIO */
977 
978     { "aaconvert",  FN_AADUMP, 0},      /* Associative Array conversion */
979     { "absolute",   FN_ABS,  0},        /* Absolute value */
980 #ifdef TCPSOCKET
981     { "addr2name",  FN_HSTADD,CM_INV},  /* IP Address to Hostname */
982     { "addrtoname", FN_HSTADD,CM_INV},  /* IP Address to Hostname */
983 #endif /* TCPSOCKET */
984     { "arraylook",  FN_ALOOK,0},        /* Array lookup */
985     { "b64decode",  FN_FMB64,0},        /* Base-64 conversion */
986     { "b64encode",  FN_TOB64,0},        /* ... */
987     { "basename",   FN_BSN,  0},        /* Basename */
988     { "break",      FN_BRK,  0},        /* Break (as in Snobol) */
989     { "ca",         FN_CAP,  CM_INV|CM_ABR}, /* Abbreviation for capitablize */
990     { "cap",        FN_CAP,  CM_INV|CM_ABR}, /* Abbreviation for capitablize */
991     { "capitalize", FN_CAP,  0},        /* First Letter -> uppercase */
992     { "caps",       FN_CAP,  CM_INV},   /* ditto */
993     { "character",  FN_CHR,  0},        /* Character from code */
994     { "checksum",   FN_CHK,  0},        /* Checksum */
995     { "cmdstack",   FN_CMDSTK,0},       /* Command stack */
996     { "cmpdates",   FN_CMPDATE,0},      /* Compare dates */
997     { "code",       FN_COD,  0},        /* Code from character */
998 #ifndef NOPUSH
999     { "command",    FN_CMD,  0},        /* Output from a command */
1000 #endif /* NOPUSH */
1001     { "contents",   FN_CON,  0},        /* Definition (contents) of variable */
1002     { "count",      FN_COUNT, 0},       /* Occurrences of string in string */
1003     { "crc16",      FN_CRC,  0},        /* CRC-16 */
1004 #ifdef OS2
1005     { "crypt",      FN_CRY, CM_INV},
1006 #endif /* OS2 */
1007     { "cvtcset",    FN_XLATE, 0},	/* Convert character set */
1008     { "cvtdate",    FN_DTIM, 0},        /* Convert free date/time to std */
1009 #ifdef ZFCDAT
1010     { "date",       FN_FD,   0},        /* File modification/creation date */
1011 #endif /* ZFCDAT */
1012     { "day",        FN_DAY,    0},      /* Day of week */
1013     { "dayname",    FN_DAYNAME,0},      /* Name of day of week */
1014     { "dayofyear",  FN_JDATE,  0},      /* Date to Day of Year */
1015     { "decodehex",  FN_UNPCT,  0},	/* Decode string with hex escapes */
1016     { "definition", FN_DEF,    0},      /* Return definition of given macro */
1017     { "delta2secs", FN_DELSEC, 0},      /* Delta time to seconds */
1018     { "deltatosecs", FN_DELSEC, CM_INV}, /* Delta time to seconds */
1019 #ifndef NODIAL
1020     { "dialconvert",FN_PNCVT,0},        /* Convert portable phone number */
1021 #endif /* NODIAL */
1022     { "diffdates",  FN_DIFDATE,0},      /* Difference of two date-times */
1023     { "dimension",  FN_DIM,  0},        /* Dimension of array */
1024     { "dir",        FN_DIR,  CM_INV|CM_ABR}, /* Abbreviation for direct.. */
1025     { "dire",       FN_DIR,  CM_INV|CM_ABR}, /* Abbreviation for direct.. */
1026     { "direc",      FN_DIR,  CM_INV|CM_ABR}, /* Abbreviation for direct.. */
1027     { "direct",     FN_DIR,  CM_INV|CM_ABR}, /* Abbreviation for direct.. */
1028     { "directo",    FN_DIR,  CM_INV|CM_ABR}, /* Abbreviation for direct.. */
1029     { "director",   FN_DIR,  CM_INV|CM_ABR}, /* Abbreviation for direct.. */
1030     { "directories",FN_DIR,  0},        /* List of directories */
1031     { "directory",  FN_DIR,  CM_INV},	/* List of directories */
1032     { "dirname",    FN_DNAM, 0},        /* Directory part of filename */
1033     { "dos2unixpath",FN_PC_DU, CM_INV}, /* DOS to UNIX path */
1034     { "dostounixpath",FN_PC_DU, 0},     /* DOS to UNIX path */
1035     { "doy",        FN_JDATE,CM_INV},   /* Date to Day of Year */
1036     { "doy2date",   FN_DATEJ,0},        /* Day of Year to date */
1037     { "doytodate",  FN_DATEJ,CM_INV},   /* Day of Year to date */
1038     { "emailaddress",FN_EMAIL, 0},	/* Email address */
1039 #ifdef FN_ERRMSG
1040     { "errstring",  FN_ERRMSG,0},       /* Error code to message */
1041 #endif /* FN_ERRMSG */
1042     { "evaluate",   FN_EVA,  0},        /* Evaluate given arith expression */
1043     { "execute",    FN_EXE,  0},        /* Execute given macro */
1044     { "filecompare",FN_FILECMP, 0},	/* File compare */
1045     { "fileinfo",   FN_FILEINF, 0},	/* File information */
1046     { "files",      FN_FC,   0},        /* File count */
1047 #ifdef FNFLOAT
1048     { "fpabsolute", FN_FPABS, 0},       /* Floating-point absolute value */
1049     { "fpadd",      FN_FPADD, 0},       /* FP add */
1050     { "fpcosine",   FN_FPCOS, 0},       /* FP cosine */
1051     { "fpdivide",   FN_FPDIV, 0},       /* FP divide */
1052     { "fpexp",      FN_FPEXP, 0},       /* FP e to the x */
1053     { "fpint",      FN_FPINT, 0},       /* FP to integer */
1054     { "fplog10",    FN_FPLOG, 0},       /* FP base-10 logarithm */
1055     { "fplogn",     FN_FPLN,  0},       /* FP natural logarithm */
1056     { "fpmaximum",  FN_FPMAX, 0},       /* FP maxinum */
1057     { "fpminimum",  FN_FPMIN, 0},       /* FP mininum */
1058     { "fpmodulus",  FN_FPMOD, 0},       /* FP modulus */
1059     { "fpmultiply", FN_FPMUL, 0},       /* FP multiply */
1060     { "fpraise",    FN_FPPOW, 0},       /* FP raise to a power */
1061     { "fpround",    FN_FPROU, 0},       /* FP round */
1062     { "fpsine",     FN_FPSIN, 0},       /* FP sine */
1063     { "fpsqrt",     FN_FPSQR, 0},       /* FP square root */
1064     { "fpsubtract", FN_FPSUB, 0},       /* FP subtract */
1065     { "fptangent",  FN_FPTAN, 0},       /* FP tangent */
1066 #endif /* FNFLOAT */
1067     { "function",   FN_FUNC, 0 },       /* Test for existence of a function */
1068     { "getpidinfo", FN_PID, CM_INV },   /* Get PID info (synonym for pidnfo) */
1069     { "hex2ip",     FN_HEX2IP,0},       /* Hex to IP address */
1070     { "hextoip",    FN_HEX2IP,CM_INV},  /* Hex to IP address */
1071     { "hex2n",      FN_HEX2N, CM_INV},  /* Hex to decimal number */
1072     { "hexify",     FN_HEX,   0},       /* Hexify (string) */
1073     { "index",      FN_IND,   0},       /* Index (string search) */
1074     { "ip2hex",     FN_IP2HEX,0},       /* IP address to hex */
1075     { "iptohex",    FN_IP2HEX,CM_INV},  /* IP address to hex */
1076     { "ipaddress",  FN_IPA,   0},       /* Find and return IP address */
1077     { "jdate",      FN_JDATE, CM_INV},  /* Date to Day of Year */
1078     { "join",       FN_JOIN,  0},       /* Join array elements */
1079     { "keywordvalue",  FN_KWVAL, 0},    /* Keyword=Value */
1080 #ifdef CK_KERBEROS
1081     { "krbflags",      FN_KRB_FG, 0},   /* Kerberos functions */
1082     { "krbisvalid",    FN_KRB_IV, 0},
1083     { "krbnextticket", FN_KRB_NX, 0},
1084     { "krbtickets",    FN_KRB_TK, 0},
1085     { "krbtimeleft",   FN_KRB_TT, 0},
1086 #endif /* CK_KERBEROS */
1087     { "kwvalue",    FN_KWVAL, CM_INV},	/* Keyword=Value */
1088     { "left",       FN_LEF,  0},        /* Leftmost n characters of string */
1089     { "length",     FN_LEN,  0},        /* Return length of argument */
1090     { "literal",    FN_LIT,  0},        /* Return argument literally */
1091 #ifdef NT
1092     { "longpathname",FN_LNAME,0},	/* GetLongPathName() */
1093 #else
1094     { "longpathname",FN_FFN,CM_INV},
1095 #endif /* NT */
1096     { "lop",        FN_STL,  0},        /* Lop */
1097     { "lopx",       FN_LOPX, 0},        /* Lopx */
1098     { "lower",      FN_LOW,  0},        /* Return lowercased argument */
1099     { "lpad",       FN_LPA,  0},        /* Return left-padded argument */
1100     { "ltrim",      FN_LTR,  0},        /* Left-Trim */
1101     { "maximum",    FN_MAX,  0},        /* Return maximum of two arguments */
1102     { "minimum",    FN_MIN,  0},        /* Return minimum of two arguments */
1103     { "mjd",        FN_MJD,  0},        /* Date to Modified Julian Date */
1104     { "mjd2date",   FN_MJD2, CM_INV},	/* MJD to Date */
1105     { "mjdtodate",  FN_MJD2, 0},	/* MJD to Date */
1106     { "modulus",    FN_MOD,  0},        /* Return modulus of two arguments */
1107     { "monthname",  FN_MONNAME,0},      /* Name of month of year */
1108 #ifdef COMMENT
1109     { "msleep",     FN_MSLEEP,0},       /* Sleep for n milliseconds */
1110 #endif /* COMMENT */
1111     { "n2hex",      FN_2HEX, CM_INV},   /* Number to hex */
1112     { "n2octal",    FN_2OCT, CM_INV},   /* Number to octal */
1113     { "n2time",     FN_N2TIM,0},        /* Number to hh:mm:ss */
1114 #ifdef TCPSOCKET
1115     { "name2addr",  FN_HSTNAM,CM_INV},  /* Hostname to IP Address */
1116 #endif /* TCPSOCKET */
1117     { "nday",       FN_NDAY, 0},        /* Numeric day of week */
1118     { "nextfile",   FN_FIL,  0},        /* Next file in list */
1119     { "ntime",      FN_NTIM, 0},        /* Time to seconds since midnight */
1120     { "ntohex",     FN_2HEX, CM_INV},   /* Number to hex */
1121     { "ntooctal",   FN_2OCT, CM_INV},   /* Number to octal */
1122     { "ntotime",    FN_N2TIM,CM_INV},   /* Number to hh:mm:ss */
1123     { "oct2n",      FN_OCT2N,CM_INV},   /* Octal to decimal number */
1124     { "octton",     FN_OCT2N,CM_INV},   /* Octal to decimal number */
1125     { "pathname",   FN_FFN,  0},        /* Full file name */
1126     { "pattern",    FN_PATTERN, 0},     /* Pattern (for INPUT) */
1127 #ifdef CK_PERMS
1128     { "permissions",FN_PERM, 0},        /* Permissions of file */
1129 #else
1130     { "permissions",FN_PERM, CM_INV},   /* Permissions of file */
1131 #endif /* CK_PERMS */
1132 #ifdef SEEK_CUR
1133     { "pictureinfo",FN_PICTURE, 0 },	/* Picture orientation/dimensions */
1134 #endif	/* SEEK_CUR */
1135     { "pidinfo",    FN_PID, 0  },       /* Get PID info */
1136     { "radix",      FN_RADIX, 0 },	/* Radix conversion */
1137 #ifndef NORANDOM
1138     { "random",     FN_RAND, 0},        /* Random number */
1139 #endif /* NORANDOM */
1140 #ifndef NOPUSH
1141     { "rawcommand", FN_RAW,  0},        /* Output from a command (raw) */
1142 #endif /* NOPUSH */
1143 #ifdef RECURSIVE
1144     { "rdirectories", FN_RDIR, 0},      /* Recursive directory list */
1145 #endif /* RECURSIVE */
1146     { "recurse",    FN_RECURSE, 0},	/* Recursive variable evaluation */
1147 #ifdef RECURSIVE
1148     { "rfiles",       FN_RFIL, 0},      /* Recursive file list */
1149 #endif /* RECURSIVE */
1150     { "rep",        FN_REP, CM_INV|CM_ABR},
1151     { "repeat",     FN_REP,  0},        /* Repeat argument given # of times */
1152     { "replace",    FN_RPL,  0},        /* Replace characters in string */
1153     { "reverse",    FN_REV,  0},        /* Reverse the argument string */
1154     { "right",      FN_RIG,  0},        /* Rightmost n characters of string */
1155     { "rindex",     FN_RIX,  0},        /* Right index */
1156     { "rpad",       FN_RPA,  0},        /* Right-pad the argument */
1157     { "rsearch",    FN_RSEARCH, 0},     /* R-L Search for pattern in string */
1158 #ifdef OS2
1159     { "scrncurx",   FN_SCRN_CX,  0},    /* Screen Cursor X Pos */
1160     { "scrncury",   FN_SCRN_CY,  0},    /* Screen Cursor Y Pos */
1161     { "scrnstr",    FN_SCRN_STR, 0},    /* Screen String */
1162 #endif /* OS2 */
1163     { "search",     FN_SEARCH, 0},      /* L-R Search for pattern in string */
1164 #ifndef NOSEXP
1165     { "sexpression",FN_SEXP, 0},        /* S-Expression */
1166 #endif /* NOSEXP */
1167 #ifdef NT
1168     { "shortpathname",FN_SNAME,0},	/* GetShortPathName() */
1169 #else
1170     { "shortpathname",FN_FFN,CM_INV},
1171 #endif /* NT */
1172     { "size",       FN_FS,   0},        /* File size */
1173 #ifdef COMMENT
1174     { "sleep",      FN_SLEEP,0},        /* Sleep for n seconds */
1175 #endif /* COMMENT */
1176     { "span",       FN_SPN,  0},        /* Span - like Snobol */
1177     { "split",      FN_SPLIT,0},        /* Split string into words */
1178     { "squeeze",    FN_SQUEEZE,0},	/* Squeeze whitespace in string */
1179     { "strcmp",     FN_STRCMP,0},	/* String comparison */
1180     { "stringtype", FN_STRINGT,0},	/* String type (7-bit, 8-bit, UTF-8) */
1181     { "stripb",     FN_STB,  0},        /* Strip enclosing braces/brackets */
1182     { "stripn",     FN_STN,  0},        /* Strip n chars */
1183     { "stripx",     FN_STX,  0},        /* Strip suffix */
1184     { "su",         FN_SUB,  CM_INV|CM_ABR},
1185     { "sub",        FN_SUB,  CM_INV|CM_ABR},
1186     { "subs",       FN_SUB,  CM_INV|CM_ABR},
1187     { "subst",      FN_SUB,  CM_INV|CM_ABR},
1188     { "substitute", FN_SUBST,0},        /* Substitute chars */
1189     { "substring",  FN_SUB,  0},        /* Extract substring from argument */
1190     { "tablelook",  FN_TLOOK,0},        /* Table lookup */
1191     { "time",       FN_TIME, 0},        /* Free-format time to hh:mm:ss */
1192     { "tod2secs",   FN_NTIM, CM_INV},   /* Time-of-day-to-secs-since-midnite */
1193     { "todtosecs",  FN_NTIM, CM_INV},   /* Time-of-day-to-secs-since-midnite */
1194     { "trim",       FN_TRM,  0},        /* Trim */
1195     { "unhexify",   FN_UNH,  0},        /* Unhexify */
1196     { "unix2dospath",FN_PC_UD, CM_INV}, /* UNIX to DOS path */
1197     { "unixtodospath",FN_PC_UD, 0},     /* UNIX to DOS path */
1198     { "untabify",   FN_UNTAB,0},        /* Untabify */
1199     { "upper",      FN_UPP,  0},        /* Return uppercased argument */
1200     { "utcdate",    FN_TOGMT,0},        /* Date-time to UTC (GMT) */
1201     { "verify",     FN_VER,  0},        /* Verify */
1202     { "word",       FN_WORD, 0},        /* Extract a word */
1203     { "", 0, 0}
1204 };
1205 int nfuncs = (sizeof(fnctab) / sizeof(struct keytab)) - 1;
1206 #endif /* NOSPL */
1207 
1208 #ifndef NOSPL                           /* Buffer for expansion of */
1209 #ifdef BIGBUFOK                         /* built-in variables. */
1210 #define VVBUFL 1024
1211 #else
1212 #define VVBUFL 256
1213 #endif /* BIGBUFOK */
1214 char vvbuf[VVBUFL+1];
1215 #endif /* NOSPL */
1216 
1217 struct keytab disptb[] = {              /* Log file disposition */
1218     { "append",    1,  0},
1219     { "new",       0,  0}
1220 };
1221 
1222 #ifdef CKFLOAT
1223 
1224 /* I N I T F L O A T  --  Deduce floating-point precision by inspection */
1225 
1226 #ifdef COMMENT
1227 /* For looking at internal floating-point representations */
1228 static char fp_xbuf[128];
1229 static char *
tohex(s,n)1230 tohex(s, n) CHAR * s; int n; {
1231     int x;
1232     char * p = fp_xbuf;
1233     while (n-- > 0) {
1234         x = (*s >> 4) & 0x0f;
1235         *p++ = hexdigits[x];
1236         x = *s++ & 0x0f;
1237         *p++ = hexdigits[x];
1238     }
1239     *p = NUL;
1240     return((char *)fp_xbuf);
1241 }
1242 #endif /* COMMENT */
1243 
1244 char math_pi[] = "3.1415926535897932384626433832795";
1245 char math_e[] =  "2.7182818284590452353602874713527";
1246 
1247 VOID
initfloat()1248 initfloat() {
1249     char * buf = NULL;
1250     int i, x, y;
1251 /*
1252   We malloc a big temporary buffer for sprintf() to minimize likelihood of
1253   (and damage from) sprintf buffer overflows.  In any case, the only way this
1254   could happen would be if sprintf() itself had bugs, since the format
1255   descriptor says to cut it off at 250 decimal places.
1256 */
1257     if ((buf = (char *)malloc(4096))) {
1258         sprintf(buf,"%0.250f",(10.0 / 3.0));
1259         for (i = 2; i < 250 && buf[i] == '3'; i++) ;
1260         x = i - 1;
1261         debug(F111,"initfloat 10.0/3.0",buf,x);
1262         sprintf(buf,"%0.250f",(4.0 / 9.0));
1263         for (i = 2; i < 250 && buf[i] == '4'; i++) ;
1264         y = i - 1;
1265         debug(F111,"initfloat 4.0/9.0",buf,y);
1266         fp_digits = (x < y) ? x : y;
1267         if (fp_digits < sizeof(math_pi) - 1) {
1268             math_pi[fp_digits+1] = NUL;
1269             math_e[fp_digits+1] = NUL;
1270         }
1271         sprintf(buf,"%0.6f",(7.0 / 9.0));
1272         if (buf[7] == '8') fp_rounding = 1;
1273         debug(F111,"initfloat 7.0/9.0",buf,fp_rounding);
1274         debug(F101,"initfloat precision","",fp_digits);
1275         free(buf);
1276     }
1277 }
1278 #endif /* CKFLOAT */
1279 
1280 /*
1281   P R E S C A N -- A quick look through the command-line options for
1282   items that must be handled before the initialization file is executed.
1283 */
1284 #ifdef NT
1285 extern int StartedFromDialer;
1286 #endif /* NT */
1287 #ifdef OS2
1288 extern int k95stdio;
1289 unsigned long startflags = 0L;
1290 #endif /* OS2 */
1291 
1292 static char *
findinpath(arg)1293 findinpath(arg) char * arg; {
1294 #ifdef OS2
1295     char * scriptenv, * keymapenv;
1296     int len;
1297 #endif /* OS2 */
1298 #ifdef DCMDBUF
1299     extern char * cmdbuf;
1300 #else
1301     extern char cmdbuf[];
1302 #endif /* DCMDBUF */
1303     char takepath[4096];
1304     char * s;
1305     int x, z;
1306 
1307     /* Set up search path... */
1308 #ifdef OS2
1309     char * appdata0 = NULL, *appdata1 = NULL;
1310 #ifdef NT
1311     scriptenv = getenv("K95SCRIPTS");
1312     keymapenv = getenv("K95KEYMAPS");
1313     makestr(&appdata0,(char *)GetAppData(0));
1314     makestr(&appdata1,(char *)GetAppData(1));
1315 #else /* NT */
1316     scriptenv = getenv("K2SCRIPTS");
1317     keymapenv = getenv("K2KEYMAPS");
1318 #endif /* NT */
1319     if (!scriptenv)
1320       scriptenv = getenv("CK_SCRIPTS");
1321     if (!scriptenv)
1322       scriptenv = "";
1323     if (!keymapenv)
1324       keymapenv = getenv("CK_KEYMAPS");
1325     if (!keymapenv)
1326       keymapenv = "";
1327 
1328     debug(F110,"startupdir",startupdir,0);
1329     debug(F110,"common appdata directory",appdata1,0);
1330     debug(F110,"appdata directory",appdata0,0);
1331     debug(F110,"inidir",inidir,0);
1332     debug(F110,"home",zhome(),0);
1333     debug(F110,"exedir",exedir,0);
1334 
1335     len = strlen(scriptenv) + strlen(keymapenv) + 3*strlen(startupdir)
1336         + 3*strlen(inidir) + 3*strlen(zhome()) + 3*strlen(exedir)
1337         + (appdata0 ? 3*strlen(appdata0) : 0)
1338         + (appdata1 ? 3*strlen(appdata1) : 0)
1339         + 6*strlen("SCRIPTS/") + 6*strlen("KEYMAPS/") + 16;
1340 
1341     if (len >= 4096) {                  /* SAFE (length is checked) */
1342         takepath[0] = '\0';
1343         debug(F111,"findinpath error - path length too long","len",len);
1344     } else
1345       sprintf(takepath,
1346               /* semicolon-separated path list */
1347     "%s%s%s%s%s;%s%s;%s%s;%s%s%s%s%s%s%s%s%s%s%s%s%s;%s%s;%s%s;%s;%s%s;%s%s",
1348               scriptenv,
1349               (scriptenv[0] && scriptenv[strlen(scriptenv)-1]==';')?"":";",
1350               keymapenv,
1351               (keymapenv[0] && keymapenv[strlen(keymapenv)-1]==';')?"":";",
1352               startupdir,
1353               startupdir, "SCRIPTS/",
1354               startupdir, "KEYMAPS/",
1355               appdata1 ? appdata1 : "",
1356               appdata1 ? "Kermit 95;" : "",
1357               appdata1 ? appdata1 : "",
1358               appdata1 ? "Kermit 95/SCRIPTS/;" : "",
1359               appdata1 ? appdata1 : "",
1360               appdata1 ? "Kermit 95/KEYMAPS/;" : "",
1361               appdata0 ? appdata0 : "",
1362               appdata0 ? "Kermit 95;" : "",
1363               appdata0 ? appdata0 : "",
1364               appdata0 ? "Kermit 95/SCRIPTS/;" : "",
1365               appdata0 ? appdata0 : "",
1366               appdata0 ? "Kermit 95/KEYMAPS/;" : "",
1367               inidir,
1368               inidir, "SCRIPTS/",
1369               inidir, "KEYMAPS/",
1370               zhome(),
1371               zhome(), "SCRIPTS/",
1372               zhome(), "KEYMAPS/",
1373               exedir,
1374               exedir, "SCRIPTS/",
1375               exedir, "KEYMAPS/"
1376               );
1377     debug(F110,"findinpath takepath",takepath,0);
1378 #ifdef NT
1379     makestr(&appdata0,NULL);
1380     makestr(&appdata1,NULL);
1381 #endif /* NT */
1382 #else /* not OS2 */
1383 #ifndef NOSPL
1384     z = 1024;                           /* Look in home directory */
1385     s = takepath;
1386     zzstring("\\v(home)",&s,&z);
1387 #else
1388     takepath[0] = '\0';
1389 #endif /* NOSPL */
1390 #endif /* OS2 */
1391 /*
1392   All the logic for searching the take path is in the command parser.
1393   So even though we aren't parsing commands, we initialize and call the
1394   parser from here, with the purported filename stuffed into the command
1395   buffer, followed by some carriage returns to make the parser return.
1396   If the file is not found, or otherwise not accessible, the parser prints
1397   an appropriate message, and then we just exit.
1398 */
1399     cmdini();                           /* Allocate command buffers etc */
1400     cmini(0);                           /* Initialize them */
1401     /* Stuff filename into command buf with braces in case of spaces */
1402     ckmakmsg(cmdbuf,CMDBL,"{",arg,"}",NULL);
1403     debug(F110,"findinpath cmdbuf",cmdbuf,0);
1404     ckstrncat(cmdbuf,"\r\r",CMDBL);     /* And some carriage returns */
1405     if (cmifip("","",&s,&x,0,takepath,xxstring) < 0)
1406       return(NULL);
1407     cmres();
1408     return(s);
1409 }
1410 
1411 static int tr_int;                      /* Flag if TRANSMIT interrupted */
1412 
1413 #ifndef MAC
1414 SIGTYP
1415 #ifdef CK_ANSIC
trtrap(int foo)1416 trtrap(int foo)                         /* TRANSMIT interrupt trap */
1417 #else
1418 trtrap(foo) int foo;                    /* TRANSMIT interrupt trap */
1419 #endif /* CK_ANSIC */
1420 /* trtrap */ {
1421 #ifdef __EMX__
1422     signal(SIGINT, SIG_ACK);
1423 #endif
1424     tr_int = 1;                         /* (Need arg for ANSI C) */
1425     SIGRETURN;
1426 }
1427 #endif /* MAC */
1428 #endif /* NOICP */
1429 
1430 #ifdef UNIX
1431 VOID
getexedir()1432 getexedir() {
1433     extern char * xarg0;
1434     long xx;
1435   /*
1436     Unix provides no standard service for this.  We look in argv[0], and if
1437     we're lucky there's a full pathname.  If not we do a PATH search.
1438   */
1439     if (ckstrchr(xarg0,'/')) {          /* Global copy of argv[0] */
1440         int i, k;
1441         char * p = NULL;
1442         if ((k = ckstrncpy(tmpbuf,xarg0,TMPBUFSIZ-2)) > 0) {
1443             p = tmpbuf;
1444             /* Convert to fully qualified pathname */
1445             if (tmpbuf[0]) if (tmpbuf[0] != '/') {
1446                 line[0] = NUL;
1447                 zfnqfp(tmpbuf,LINBUFSIZ-2,(char *)line);
1448                 if (line[0])
1449                   p = line;
1450             }
1451             xx = zchki(p);
1452             if (xx > -1) {		/* Is the result an existing file? */
1453                 k = strlen(p);
1454                 for (i = k-1; i > 0; i--) { /* Yes, strip name part */
1455                     if (p[i] == '/') {
1456                         if (i < k-1)
1457                           p[i+1] = NUL;
1458                         break;
1459                     }
1460                 }
1461             }
1462 	    makestr(&exedir,p);		/* Save the result */
1463         }
1464     }
1465     if (!exedir && xarg0) {             /* Not found? */
1466         char * p;
1467         p = getenv("PATH");             /* Search the PATH */
1468         if (p) {                        /* If there is one... */
1469             char * q, * PATH = NULL;
1470             int k;
1471             makestr(&PATH,p);           /* Pokeable copy of PATH string */
1472             if (PATH) {                 /* If malloc succeeded... */
1473                 p = PATH;
1474                 while (p && *p) {        /* Loop through segments */
1475                     q = ckstrchr(p,':'); /* End of this segment */
1476                     if (q == p) {       /* Null PATH segment */
1477                         p++;            /* Skip over colon */
1478                         continue;
1479                     }
1480                     if (q)              /* If not at end of PATH string */
1481                       *q++ = NUL;       /* zero out the colon */
1482                     if ((k = ckstrncpy(tmpbuf,p,TMPBUFSIZ)) > 0) {
1483                         if (tmpbuf[k-1] != '/') { /* Copy this PATH segment */
1484                             tmpbuf[k++] = '/';    /* Append '/' if needed */
1485                             tmpbuf[k] = NUL;
1486                         }
1487                         /* Append the argv[0] value */
1488                         if (ckstrncpy(&tmpbuf[k],xarg0,TMPBUFSIZ) > 0) {
1489                             if (zchki(tmpbuf) > -1) { /* File exists? */
1490                                 tmpbuf[k] = NUL;      /* Yes, we're done */
1491                                 zfnqfp(tmpbuf,LINBUFSIZ,(char *)line);
1492                                 makestr(&exedir,line);
1493                                 break;
1494                             }
1495                         } else break;
1496                     } else break;
1497                     p = q;              /* Not found, go to next segment  */
1498                 } /* while */
1499                 free(PATH);             /* Free PATH copy */
1500             }
1501         }
1502         if (!exedir) {                  /* Still nothing? */
1503             if (zchki(xarg0) > -1) {    /* Maybe it's in the current dir */
1504                 zfnqfp(zgtdir(),LINBUFSIZ,(char *)line);
1505                 makestr(&exedir,line);
1506             }
1507         }
1508     }
1509     if (!exedir) {                      /* Still nothing? */
1510         makestr(&exedir,"/");           /* Fake it with with root. */
1511     }
1512 }
1513 #endif /* UNIX */
1514 
1515 int arg_x = 0;
1516 static int x_prescan = 0;
1517 
1518 /*
1519   The argument y once meant something but I can't imagine what so now
1520   it's ignored.  (Prior to 22 Aug 98, prescan() was called twice by main(),
1521   and the arg differentiated the two calls.  But this caused all sorts of
1522   problems & confusion, so I commented out the second call.  This issue might
1523   need to be revisited.)
1524 */
1525 VOID
prescan(dummy)1526 prescan(dummy) int dummy; {             /* Arg is ignored. */
1527     extern int howcalled;
1528     int yargc; char **yargv;
1529     char x;
1530     char *yp, *yy;
1531 #ifdef DEBUG
1532     int debcount = 0;
1533 #endif /* DEBUG */
1534     int z;
1535 
1536     if (x_prescan)                      /* Only run once */
1537       return;
1538     x_prescan = 1;
1539 
1540     yargc = xargc;                      /* Make copy of arg vector */
1541     yargv = xargv;
1542 
1543 #ifndef NOICP
1544 #ifdef DCMDBUF
1545     if (!kermrc)
1546       if (!(kermrc = (char *) malloc(KERMRCL+1)))
1547         fatal("prescan: no memory for kermrc");
1548 #endif /* DCMDBUF */
1549     ckstrncpy(kermrc,KERMRC,KERMRCL);   /* Default init file name */
1550 #endif /* NOICP */
1551 
1552 #ifdef OS2
1553     yp = getenv("K95STARTFLAGS");
1554     if (yp) {
1555         startflags = atoi(yp);
1556     }
1557 #endif /* OS2 */
1558 
1559 #ifdef IKSD
1560     if (howcalled == I_AM_IKSD)         /* Internet Kermit Service daemon */
1561       inserver = 1;                     /* (See inserver section of ckcmai) */
1562 #endif /* IKSD */
1563 
1564 /* Command line options for Kermit */
1565 
1566 #ifndef NOCMDL
1567     if (yargc > 1
1568         && *yargv[1] != '-'
1569         && (yargv[1][0] != '=')
1570 #ifdef KERBANG
1571         && (yargv[1][0] != '+')
1572 #endif /* KERBANG */
1573 #ifdef IKSD
1574         && (howcalled != I_AM_IKSD)
1575 #endif /* IKSD */
1576         ) {                             /* Filename as 1st argument */
1577 #ifndef NOICP
1578         char *s;
1579 #endif /* NOICP */
1580 #ifndef NOURL
1581         extern int haveurl;
1582         extern struct urldata g_url;
1583         if (urlparse(yargv[1],&g_url)) {
1584             if (!ckstrcmp(g_url.svc,"ftp",-1,0) ||
1585                 !ckstrcmp(g_url.svc,"ftps",-1,0)) {
1586                 haveurl = 1;
1587                 howcalled = I_AM_FTP;
1588             } else if (!ckstrcmp(g_url.svc,"telnet",-1,0) ||
1589                        !ckstrcmp(g_url.svc,"telnets",-1,0)) {
1590                 haveurl = 1;
1591                 howcalled = I_AM_TELNET;
1592             } else if (!ckstrcmp(g_url.svc,"ssh",-1,0)) {
1593                 haveurl = 1;
1594                 howcalled = I_AM_SSH;
1595             } else if (!ckstrcmp(g_url.svc,"iksd",-1,0) ||
1596                        !ckstrcmp(g_url.svc,"kermit",-1,0)) {
1597                 haveurl = 1;
1598                 howcalled = I_AM_KERMIT;
1599             } else if (!ckstrcmp(g_url.svc,"http",-1,0) ||
1600                        !ckstrcmp(g_url.svc,"https",-1,0)) {
1601                 haveurl = 1;
1602                 howcalled = I_AM_HTTP;
1603             }
1604             if (haveurl) {
1605                 while (--yargc > 0) {   /* Go through command-line args */
1606                     yargv++;            /* looking for -Y and -d */
1607                     yp = *yargv+1;
1608                     if (**yargv == '-') {
1609                         x = *(*yargv+1);
1610                         while (x) {
1611                             switch (x) {
1612 #ifndef NOICP
1613 			      case '+':
1614 			      case '-':
1615                                 if (doxarg(yargv,1) < 0) {
1616                                     fatal("Extended argument error");
1617                                 }
1618                                 yp = "";
1619                                 break;
1620 #endif /* NOICP */
1621                               case 'Y':
1622                                 noinit++;
1623                                 break;
1624 
1625                               case 'q':
1626                                 quiet = 1;
1627                                 break;
1628 
1629                               case 'h':
1630                                   noinit = 1;
1631 #ifdef OS2
1632                                   startflags |= 2;    /* No network DLLs */
1633                                   startflags |= 4;    /* No TAPI DLLs */
1634                                   startflags |= 8;    /* No Security DLLs */
1635                                   startflags |= 16;   /* No Zmodem DLLs */
1636                                   startflags |= 32;   /* Stdin */
1637                                   startflags |= 64;   /* Stdout */
1638 #endif /* OS2 */
1639                                   break;
1640                               case 'd': /* = SET DEBUG ON */
1641 #ifdef DEBUG
1642                                 if (debcount++ > 0)
1643                                   debtim = 1;
1644                                 if (!deblog)
1645                                   deblog = debopn("debug.log",0);
1646 #endif /* DEBUG */
1647                                 break;
1648 #ifdef OS2
1649                               case 'W':
1650                                 if (*(yp+1))
1651                                   fatal("invalid argument bundling after -W");
1652                                 yargv++, yargc--;
1653                                 if (yargc < 1)
1654                                   fatal("Window handle missing");
1655                                 hwndDialer = (HWND) atol(*yargv);
1656                                 StartedFromDialer = 1;
1657                                 yargv++, yargc--;
1658                                 KermitDialerID = atol(*yargv) ;
1659                                 break;
1660                               case '#': /* K95 initialization options */
1661                                 if (*(yp+1)) {
1662                                     fatal("invalid argument bundling");
1663                                 }
1664                                 yargv++, yargc--;
1665                                 if (yargc < 1)
1666                                   fatal("-# argument missing");
1667                                 startflags |= atol(*yargv);
1668                                 break;
1669 #endif /* OS2 */
1670                             }
1671                             if (!yp)
1672                               break;
1673                             x = *++yp;
1674                         }
1675                     }
1676                 }
1677                 return;
1678             }
1679         }
1680         /* after this point non-Kermit personalities must return */
1681         switch (howcalled) {
1682 	  case I_AM_KERMIT:
1683 	  case I_AM_IKSD:
1684 	  case I_AM_SSHSUB:
1685             break;
1686 	  default:
1687             return;
1688         }
1689 #endif /* NOURL */
1690 
1691 #ifndef NOICP
1692         /* If it is not a URL that we recognize, try to treat it as a file */
1693 
1694         if (!isabsolute(yargv[1]))      /* If not absolute */
1695           s = findinpath(yargv[1]);     /* Look in PATH */
1696         else
1697           s = yargv[1];
1698         if (!s)
1699           doexit(BAD_EXIT,xitsta);
1700         zfnqfp(s,CKMAXPATH,cmdfil);     /* In case of CD in file */
1701         yargc -= 1;                     /* Skip past the filename */
1702         yargv += 1;                     /* Otherwise we'll get an error */
1703 #endif /* NOICP */
1704     }
1705 
1706 #ifndef NOCMDL
1707 #ifdef NEWFTP
1708     if (howcalled == I_AM_FTP) {        /* Kermit's FTP client personality */
1709         while (--yargc > 0) {           /* Go through command-line args */
1710             yargv++;                    /* looking for -Y and -d */
1711             yp = *yargv+1;
1712             if (**yargv == '-') {
1713                 x = *(*yargv+1);
1714                 while (x) {
1715                     switch (x) {
1716 #ifndef NOICP
1717 		      case '+':
1718 		      case '-':
1719                         if (doxarg(yargv,1) < 0) {
1720                             fatal("Extended argument error");
1721                         }
1722                         yp = "";
1723                         break;
1724 #endif /* NOICP */
1725                       case 'Y':
1726                         noinit++;
1727                         break;
1728 
1729                       case 'q':
1730                           quiet = 1;
1731                           break;
1732 
1733                       case 'h':
1734                         noinit = 1;
1735 #ifdef OS2
1736                         startflags |= 2;    /* No network DLLs */
1737                         startflags |= 4;    /* No TAPI DLLs */
1738                         startflags |= 8;    /* No Security DLLs */
1739                         startflags |= 16;   /* No Zmodem DLLs */
1740                         startflags |= 32;   /* Stdin */
1741                         startflags |= 64;   /* Stdout */
1742 #endif /* OS2 */
1743                         break;
1744                       case 'd':             /* = SET DEBUG ON */
1745 #ifdef DEBUG
1746                         if (debcount++ > 0)
1747                           debtim = 1;
1748                         if (!deblog)
1749                           deblog = debopn("debug.log",0);
1750 #endif /* DEBUG */
1751                         break;
1752 #ifdef OS2
1753                       case 'W':
1754                         if (*(yp+1))
1755                           fatal("invalid argument bundling after -W");
1756                         yargv++, yargc--;
1757                         if (yargc < 1)
1758                           fatal("Window handle missing");
1759                         hwndDialer = (HWND) atol(*yargv);
1760                         StartedFromDialer = 1;
1761                         yargv++, yargc--;
1762                         KermitDialerID = atol(*yargv) ;
1763                         break;
1764                       case '#':         /* K95 initialization options */
1765                         if (*(yp+1)) {
1766                             fatal("invalid argument bundling");
1767                         }
1768                         yargv++, yargc--;
1769                         if (yargc < 1)
1770                           fatal("-# argument missing");
1771                         startflags |= atol(*yargv);
1772                         break;
1773 #endif /* OS2 */
1774                     }
1775                     if (!yp)
1776                       break;
1777                     x = *++yp;
1778                 }
1779             }
1780         }
1781         return;
1782     }
1783 #endif /* NEWFTP */
1784 #endif /* NOCMDL */
1785 
1786     while (--yargc > 0) {               /* Go through command-line args */
1787         yargv++;
1788         yp = *yargv+1;                  /* Pointer for bundled args */
1789         if (**yargv == '=')             /* Same rules as cmdlin()... */
1790           return;
1791         debug(F110,"prescan *yargv",*yargv,0);
1792 
1793 #ifndef NOICP
1794 #ifdef KERBANG
1795         yy = *yargv;
1796         if (!strcmp(yy,"+") || (*yy == '+' && *(yy+1) < (char)33)) {
1797             char * s;
1798             yargv++;
1799             noinit = 1;
1800             if (!*yargv)
1801               return;
1802             cfilef = 1;
1803             s = findinpath(*yargv);
1804             if (s) {
1805                 zfnqfp(s,CKMAXPATH,cmdfil);
1806                 return;
1807             } else
1808               doexit(BAD_EXIT,xitsta);
1809         }
1810 #endif /* KERBANG */
1811 #endif /* NOICP */
1812         if (!strcmp(*yargv,"--"))       /* getopt() conformance */
1813           return;
1814 #ifdef VMS
1815         else if (**yargv == '/')
1816           continue;
1817 #endif /* VMS */
1818         else if (**yargv == '-') {      /* Got an option (begins with dash) */
1819             x = *(*yargv+1);            /* Get option letter */
1820             while (x) {                 /* Allow for bundled options */
1821                 debug(F000,"prescan arg","",x);
1822                 switch (x) {
1823 #ifndef NOICP
1824                   case '+':
1825                   case '-':
1826                     if (doxarg(yargv,1) < 0) {
1827                         fatal("Extended argument error");
1828                     }
1829 #ifndef COMMENT				/* Jeff 28 Apr 2003 */
1830                     yp = NULL;		/* (not "") */
1831 #else
1832                     yargv++, yargc--;
1833                     yp = *yargv;
1834 #endif /* COMMENT */
1835                     break;
1836 #endif /* NOICP */
1837 
1838                   case '7':             /* Undocumented... */
1839                     sstelnet = 1;       /* (because it doesn't work) */
1840                     break;
1841 #ifdef IKSD
1842                   case 'A': {
1843                       char * p;
1844                       inserver = 1;     /* Flag that we are doing this */
1845                       srvcdmsg = 2;     /* Preset this */
1846                       /* See inserver section of ckcmai.c for more settings */
1847 #ifdef OS2
1848                       if (*(yp+1)) {
1849                           fatal("invalid argument bundling after -A");
1850                       }
1851 #ifdef NT
1852                       /* Support for Pragma Systems Telnet/Terminal Servers */
1853                       p = getenv("PRAGMASYS_INETD_SOCK");
1854                       if (p && atoi(p) != 0) {
1855                           ttname[0] = '$';
1856                           ckstrncpy(&ttname[1],p,TTNAMLEN-1);
1857                           break;
1858                       }
1859 #endif /* NT */
1860                       yargv++, yargc--;
1861                       if (yargc < 1 || **yargv == '-') {
1862                           fatal("-A argument missing");
1863                       } else {
1864                           ttname[0] = '$';
1865                           ckstrncpy(&ttname[1],*yargv,TTNAMLEN-1);
1866                       }
1867 #endif /* OS2 */
1868                       break;
1869                   }
1870 #endif /* IKSD */
1871 
1872 #ifdef OS2
1873                   case 'W':
1874                     if (*(yp+1))
1875                       fatal("invalid argument bundling after -W");
1876                     yargv++, yargc--;
1877                     if (yargc < 1)
1878                       fatal("Window handle missing");
1879 #ifdef COMMENT
1880                     if (dummy) {
1881                         yargv++, yargc--;
1882                         break;
1883                     } else {
1884 #endif /* COMMENT */
1885                         hwndDialer = (HWND) atol(*yargv);
1886                         StartedFromDialer = 1;
1887                         yargv++, yargc--;
1888                         KermitDialerID = atol(*yargv) ;
1889 #ifdef COMMENT
1890                     }
1891 #endif /* COMMENT */
1892                     break;
1893 
1894                   case '#':             /* K95 initialization options */
1895                     if (*(yp+1)) {
1896                         fatal("invalid argument bundling");
1897                     }
1898                     yargv++, yargc--;
1899                     if (yargc < 1)
1900                       fatal("-# argument missing");
1901                     startflags |= atol(*yargv);
1902                     break;
1903 #endif /* OS2 */
1904 
1905 #ifndef NOSPL
1906                   case 'M':                             /* My User Name */
1907                     if (*(yp+1)) {
1908                         fatal("invalid argument bundling");
1909                     }
1910                     yargv++, yargc--;
1911                     if ((yargc < 1) || (**yargv == '-')) {
1912                         fatal("missing username");
1913                     }
1914                     if ((int)strlen(*yargv) > UIDBUFLEN) {
1915                         fatal("username too long");
1916                     }
1917 #ifdef COMMENT
1918 /*
1919   This can't work.  uidbuf is overwritten in sysinit() which has yet to be
1920   called.  This cannot be set in prescan().
1921 */
1922 #ifdef IKSD
1923                     if (!inserver)
1924 #endif /* IKSD */
1925                       ckstrncpy(uidbuf,*yargv,UIDBUFLEN);
1926 #endif /* COMMENT */
1927                     break;
1928 #endif /* NOSPL */
1929                   case 'R':             /* Remote-only advisory */
1930 #ifdef CK_IFRO
1931                     remonly = 1;
1932 #endif /* CK_IFRO */
1933                     break;
1934                   case 'S':             /* STAY */
1935                     stayflg = 1;
1936                     break;
1937                   case 'h':
1938                     noinit = 1;
1939 #ifdef OS2
1940                     startflags |= 2;    /* No network DLLs */
1941                     startflags |= 4;    /* No TAPI DLLs */
1942                     startflags |= 8;    /* No Security DLLs */
1943                     startflags |= 16;   /* No Zmodem DLLs */
1944                     startflags |= 32;   /* Stdin */
1945                     startflags |= 64;   /* Stdout */
1946 #endif /* OS2 */
1947                     break;
1948 #ifndef NOICP
1949                   case 'Y':             /* No init file */
1950                     noinit = 1;
1951                     break;
1952 #endif /* NOICP */
1953 
1954                   case 'q':
1955                       quiet = 1;
1956                       break;
1957 
1958                   case 'd':             /* = SET DEBUG ON */
1959 #ifdef DEBUG
1960                     if (debcount++ > 0)
1961                       debtim = 1;
1962                     if (!deblog)
1963                       deblog = debopn("debug.log",0);
1964 #endif /* DEBUG */
1965                     break;
1966 
1967                   case 'x':             /* Server */
1968                     arg_x = 1;          /* Note in advance */
1969                     break;
1970 #ifndef NOICP
1971                   case 'y':             /* Alternative init file */
1972                     noinit = 0;
1973                     yargv++, yargc--;
1974                     if (yargc < 1) fatal("missing name in -y");
1975                     /* Replace init file name */
1976                     ckstrncpy(kermrc,*yargv,KERMRCL);
1977                     rcflag = 1;         /* Flag that this has been done */
1978                     debug(F111,"prescan kermrc",kermrc,rcflag);
1979                     break;
1980 #endif /* NOICP */
1981                   case 'z':             /* = SET BACKGROUND OFF */
1982                     bgset = 0;
1983                     backgrd = 0;
1984 #ifdef VMS
1985                     batch = 0;
1986 #endif /* VMS */
1987                     break;
1988 
1989                   case 'B':             /* Force background (batch) */
1990                     bgset = 1;
1991                     backgrd = 1;
1992 #ifdef VMS
1993                     batch = 1;
1994 #endif /* VMS */
1995                     break;
1996 
1997 #ifdef CK_NETBIOS
1998                   case 'N':
1999                     {
2000                         int n ;
2001                         yargv++, yargc--;
2002 #ifdef COMMENT
2003                         if (y)
2004                           break;
2005 #endif /* COMMENT */
2006                         if (strlen(*yargv) != 1 || (*yargv)[0] == 'X') {
2007                             NetBiosAdapter = -1;
2008                         } else {
2009                             n = atoi(*yargv);
2010                             if (n >= 0 && n <= 9)
2011                               NetBiosAdapter = n;
2012                             else
2013                               NetBiosAdapter = -1;
2014                         }
2015                     }
2016                     break;
2017 #endif /* CK_NETBIOS */
2018                   default:
2019                     break;
2020                 }
2021                 if (!yp)
2022                   break;
2023                 x = *++yp;              /* See if options are bundled */
2024             }
2025         }
2026     }
2027 #endif /* NOCMDL */
2028 }
2029 
2030 /*  G E T T C S  --  Get Transfer (Intermediate) Character Set  */
2031 
2032 /*
2033   Given two file character sets, this routine picks out the appropriate
2034   "transfer" character set to use for translating between them.
2035   The transfer character set number is returned.
2036 
2037   At present this routine is somewhat overloaded.  It's fine for file
2038   transfer because it does not assume the Kermit partner understands UTF8.
2039   However, for terminal emulation it *should* use UTF8 as the intermediate
2040   character set whenever possible so as not to lose characters it might
2041   have been able to display; for example on a terminal connection from
2042   the Linux console (UTF8) to a BBS that uses CP437 box-drawing characters.
2043   This is handled in the CONNECT command code in ckucns.c, which avoids
2044   calling this routine if the character-set on either end is UTF8.
2045 
2046   Translation between different Kanji character sets is not yet supported.
2047 */
2048 int
gettcs(cs1,cs2)2049 gettcs(cs1,cs2) int cs1, cs2; {
2050 #ifdef NOCSETS                          /* No character-set support */
2051     return(0);                          /* so no translation */
2052 #else
2053     int tcs = TC_TRANSP;
2054 #ifdef KANJI
2055 /* Kanji not supported yet */
2056     if (fcsinfo[cs1].alphabet == AL_JAPAN ||
2057         fcsinfo[cs2].alphabet == AL_JAPAN )
2058       tcs = TC_TRANSP;
2059     else
2060 #endif /* KANJI */
2061 #ifdef CYRILLIC
2062 /*
2063   I can't remember why we don't test both sets here, but I think there
2064   must have been a reason...
2065 */
2066       if (fcsinfo[cs2].alphabet == AL_CYRIL)
2067         tcs = TC_CYRILL;
2068       else
2069 #endif /* CYRILLIC */
2070 #ifdef HEBREW
2071           if (fcsinfo[cs1].alphabet == AL_HEBREW ||
2072               fcsinfo[cs2].alphabet == AL_HEBREW )
2073             tcs = TC_HEBREW;
2074           else
2075 #endif /* HEBREW */
2076 #ifdef GREEK
2077           if (fcsinfo[cs1].alphabet == AL_GREEK ||
2078               fcsinfo[cs2].alphabet == AL_GREEK )
2079             tcs = TC_GREEK;
2080           else
2081 #endif /* GREEK */
2082 
2083             /* Roman sets ... */
2084 
2085 #ifdef LATIN2                           /* East European */
2086         if (cs1 == FC_2LATIN  || cs2 == FC_2LATIN || /* Latin-2 */
2087             cs1 == FC_CP852   || cs2 == FC_CP852  || /* CP852 */
2088             cs1 == FC_CP1250  || cs2 == FC_CP1250 || /* Windows Latin-2 */
2089             cs1 == FC_MAZOVIA || cs2 == FC_MAZOVIA)  /* Polish Mazovia */
2090           tcs = TC_2LATIN;
2091         else
2092 #endif /* LATIN2 */
2093                                         /* West European Euro-aware */
2094           if (cs1 == FC_CP858 || cs1 == FC_9LATIN ||
2095               cs2 == FC_CP858 || cs2 == FC_9LATIN)
2096             tcs = TC_9LATIN;
2097           else                          /* Traditional West European */
2098             tcs = TC_1LATIN;
2099     return(tcs);
2100 #endif /* NOCSETS */
2101 }
2102 
2103 #ifndef NOLOCAL
2104 /*  D O C O N E C T  --  Do the connect command  */
2105 /*
2106   q = 0 means issue normal informational message about how to get back, etc.
2107   q != 0 means to skip the message.
2108 */
2109 
2110 int
doconect(q,async)2111 doconect(q,async) int q, async; {
2112     int x;                              /* Return code */
2113 #ifdef CK_AUTODL
2114     extern CHAR ksbuf[];
2115 #endif /* CK_AUTODL */
2116 #ifndef NOKVERBS                        /* Keyboard macro material */
2117     extern int keymac, keymacx;
2118 #endif /* NOKVERBS */
2119     extern int justone, adl_err;
2120     int qsave;                          /* For remembering "quiet" value */
2121 #ifdef OS2
2122     extern int term_io;
2123     int term_io_save;
2124 #ifdef KUI
2125     extern int kui_async;
2126 #endif /* KUI */
2127 #endif /* OS2 */
2128     int is_tn = 0;
2129 
2130 #ifdef IKSD
2131     if (inserver) {
2132         if (!quiet)
2133           printf("?Sorry, IKSD cannot CONNECT.\r\n");
2134         return(success = 0);
2135     }
2136 #endif /* IKSD */
2137 
2138     is_tn =
2139 #ifdef TNCODE
2140       (local && network && IS_TELNET()) || (!local && sstelnet)
2141 #else
2142         0
2143 #endif /* TNCODE */
2144           ;
2145 /*
2146   Saving, changing, and restoring the global "quiet" variable around calls
2147   to conect() to control whether the verbose CONNECT message is printed is
2148   obviously less elegant than passing a parameter to conect(), but we do it
2149   this way to avoid the need to change all of the ck?con.c modules.  NOTE:
2150   it is important to restore the value immediately upon return in case there
2151   is an autodownload or APC.
2152 */
2153     qsave = quiet;                      /* Save it */
2154     if (!quiet && q > -1)
2155       quiet = q;                        /* Use argument temporarily */
2156     conres();                           /* Put console back to normal */
2157     debug(F101,"doconect justone 1","",justone);
2158 #ifdef CK_AUTODL
2159     ksbuf[0] = NUL;                     /* Autodownload packet buffer */
2160 #endif /* CK_AUTODL */
2161 #ifdef OS2
2162     display_demo = 1;                   /* Remember to display demo */
2163 #endif /* OS2 */
2164 
2165 #ifdef IKS_OPTION
2166     if (is_tn && TELOPT_U(TELOPT_KERMIT) && ttchk() >= 0
2167 #ifdef OS2
2168        && !viewonly
2169 #endif /* OS2 */
2170         ) {
2171         /* If the remote side is in a state of IKS START-SERVER    */
2172         /* we request that the state be changed.  We will detect   */
2173         /* a failure to adhere to the request when we call ttinc() */
2174         if (!iks_wait(KERMIT_REQ_STOP,0) && !tcp_incoming) {
2175             if (!quiet) {
2176                 printf("\r\nEnter Client/Server Mode...  Use:\r\n\r\n");
2177                 printf(
2178 " REMOTE LOGIN <user> <password> to log in to the server if necessary.\r\n");
2179                 printf(" SEND and GET for file transfer.\r\n");
2180                 printf(" REMOTE commands for file management.\r\n");
2181                 printf(" FINISH to terminate Client/Server mode.\r\n");
2182                 printf(" BYE to terminate and close connection.\r\n");
2183                 printf(" REMOTE HELP for additional information.\r\n\r\n");
2184             }
2185             quiet = qsave;
2186             return(0);      /* Failure */
2187         }
2188     }
2189 
2190     /* Let our peer know our state. */
2191 #ifdef CK_AUTODL
2192     if (is_tn && TELOPT_ME(TELOPT_KERMIT)
2193 #ifdef OS2
2194         && !viewonly
2195 #endif /* OS2 */
2196          ) {
2197         if (autodl && !TELOPT_SB(TELOPT_KERMIT).kermit.me_start) {
2198             tn_siks(KERMIT_START);      /* Send Kermit-Server Start */
2199         } else if (!autodl && TELOPT_SB(TELOPT_KERMIT).kermit.me_start) {
2200             tn_siks(KERMIT_STOP);
2201         }
2202     }
2203 #else /* CK_AUTODL */
2204     if (is_tn && TELOPT_ME(TELOPT_KERMIT) &&
2205 	TELOPT_SB(TELOPT_KERMIT).kermit.me_start)
2206         tn_siks(KERMIT_STOP);
2207 #endif /* CK_AUTODL */
2208 #endif /* IKS_OPTION */
2209 
2210     debug(F101,"doconect flow","",flow);
2211 #ifdef OS2
2212     debug(F101,"doconect async","",async);
2213 #ifdef KUI
2214     if (kui_async)
2215       async = 1;;
2216 #endif /* KUI */
2217     x = conect(async);                  /* Connect the first time */
2218 #else /* OS2 */
2219     x = conect();
2220 #endif /* OS2 */
2221     debok = 1;
2222 
2223 #ifdef IKS_OPTION
2224     if (TELOPT_U(TELOPT_KERMIT) &&
2225         TELOPT_SB(TELOPT_KERMIT).kermit.u_start &&
2226         !tcp_incoming && !quiet && ttchk() >= 0
2227         ) {
2228         printf("\r\nEnter Client/Server Mode...  Use:\r\n\r\n");
2229         printf(
2230 " REMOTE LOGIN <user> <password> to log in to the server if necessary.\r\n");
2231         printf(" SEND and GET for file transfer.\r\n");
2232         printf(" REMOTE commands for file management.\r\n");
2233         printf(" FINISH to terminate Client/Server mode.\r\n");
2234         printf(" BYE to terminate and close connection.\r\n");
2235         printf(" REMOTE HELP for additional information.\r\n\r\n");
2236     }
2237 #endif /* IKS_OPTION */
2238 
2239     quiet = qsave;                      /* Restore "quiet" value */
2240     debug(F101,"doconect justone 2","",justone);
2241 
2242 #ifdef NETCONN
2243     if (network && tn_exit && ttchk() < 0)
2244       doexit(GOOD_EXIT,xitsta);         /* Exit with good status */
2245 #endif /* NETCONN */
2246 
2247 #ifdef OS2ORUNIX
2248     /* Exit on disconnect if the port is not open or carrier detect */
2249     if (exitonclose && (ttchk() < 0))
2250       doexit(GOOD_EXIT,xitsta);
2251 #endif /* OS2ORUNIX */
2252 
2253 #ifdef CKCONINTB4CB
2254     /* The order makes a difference in HP-UX 8.00. */
2255     /* The other order makes it think it's in the background when it */
2256     /* returns from CONNECT (Apr 1999). */
2257     setint();
2258     concb((char)escape);                /* Restore console for commands */
2259 #else
2260     /* This is how it has always been so better leave it */
2261     /* this way for all non-HP-UX-8.00 builds. */
2262     concb((char)escape);                /* Restore console for commands */
2263     setint();
2264 #endif /* CKCONINTB4CB */
2265 
2266 #ifdef OS2
2267     if (!async) {
2268         term_io_save = term_io;         /* Disable I/O by emulator */
2269         term_io = 0;
2270 #endif /* OS2 */
2271 
2272 #ifdef CK_APC
2273 /*
2274   If an APC command was received during CONNECT mode, we define it now
2275   as a macro, execute the macro, and then return to CONNECT mode.
2276   We do this in a WHILE loop in case additional APCs come during subsequent
2277   CONNECT sessions.
2278 */
2279         debug(F101,"doconect apcactive","",apcactive);
2280         debug(F101,"doconect success","",success);
2281 
2282         while (x > 0 && (apcactive == APC_LOCAL ||
2283                          (apcactive == APC_REMOTE && apcstatus != APC_OFF))) {
2284             debug(F101,"doconect justone 3","",justone);
2285             if (mlook(mactab,"_apc_commands",nmac) == -1) {
2286                 debug(F110,"doconect about to execute APC",apcbuf,0);
2287                 domac("_apc_commands",apcbuf,cmdstk[cmdlvl].ccflgs|CF_APC);
2288                 delmac("_apc_commands",1);
2289 #ifdef DEBUG
2290             } else {
2291                 debug(F100,"doconect APC in progress","",0);
2292 #endif /* DEBUG */
2293             }
2294             debug(F101,"doconect apcactive after domac","",apcactive);
2295             if (!apcactive) {               /* In case CLEAR APC was in APC */
2296                 debug(F101,"doconect quit APC loop: apcactive","",apcactive);
2297                 break;
2298             }
2299             /* Also don't reconnect if autodownload failed - very confusing! */
2300             /* Let them view the local screen to see what happened. - fdc */
2301 
2302             /* This should be conditional.  If someone is relying on the */
2303             /* connect mode autodownload for the kermit server to use with */
2304             /* a remotely executed script we should be able to return to */
2305             /* connect mode on the failure.  What we really need to do is */
2306             /* report the status of the transfer and then return to CONNECT. */
2307             /* In unix this would simply be a printf(), but in K95 it could */
2308             /* use a popup dialog to report the status. - Jeff */
2309 
2310 #ifndef NOXFER
2311             debug(F101,"doconect xferstat","",xferstat);
2312             if (apcactive == APC_LOCAL && !xferstat && adl_err != 0) {
2313                 debug(F101,"doconect quit APC loop: xferstat","",xferstat);
2314                 apcactive = APC_INACTIVE;
2315                 break;
2316             }
2317 #endif /* NOXFER */
2318 #ifdef OS2
2319             msleep(250);
2320 #endif /* OS2 */
2321             debug(F101,"doconect justone 4","",justone);
2322             qsave = quiet;              /* Do this again... */
2323             if (!quiet && q > -1)
2324               quiet = q;
2325 #ifdef CK_AUTODL
2326             ksbuf[0] = NUL;
2327 #endif /* CK_AUTODL */
2328 #ifdef IKS_OPTION
2329 #ifdef CK_AUTODL
2330             if (is_tn &&
2331                 TELOPT_ME(TELOPT_KERMIT) &&
2332                 !TELOPT_SB(TELOPT_KERMIT).kermit.me_start &&
2333                 autodl
2334 #ifdef CK_APC
2335                 && !apcactive
2336 #endif /* CK_APC */
2337 #ifdef OS2
2338                 && !viewonly
2339 #endif /* OS2 */
2340                 ) {
2341                 tn_siks(KERMIT_START);  /* Send Kermit-Server Start */
2342             }
2343 #endif /* CK_AUTODL */
2344 #endif /* IKS_OPTION */
2345 #ifndef OS2
2346             x = conect();               /* Re-CONNECT. */
2347 #else /* OS2 */
2348             x = conect(0);
2349             term_io = term_io_save;
2350 #endif /* OS2 */
2351             debok = 1;
2352             quiet = qsave;
2353             debug(F101,"doconect justone 5","",justone);
2354 #ifdef NETCONN
2355             if (network && ttchk() < 0) {
2356                 if (tn_exit || exitonclose)
2357                   doexit(GOOD_EXIT,xitsta);
2358                 else
2359                   break;
2360             }
2361 #endif /* NETCONN */
2362 
2363 #ifdef OS2ORUNIX
2364             /* If connection dropped */
2365             if (ttchk() < 0) {
2366                 concb((char)escape);    /* Restore console. */
2367                 if (exitonclose)
2368                   doexit(GOOD_EXIT,xitsta);
2369                 else
2370                   break;
2371             }
2372 #endif /* OS2ORUNIX */
2373         } /* Loop back for more. */
2374 #endif /* CK_APC */
2375 
2376 #ifndef NOKVERBS
2377         if ((keymac > 0) && (keymacx > -1)) { /* Executing a keyboard macro? */
2378             /* Set up the macro and return */
2379             /* Do not clear the keymac flag */
2380 #ifdef OS2
2381 	    term_io = term_io_save;
2382 #endif /* OS2 */
2383             return(dodo(keymacx,NULL,CF_KMAC|cmdstk[cmdlvl].ccflgs));
2384         }
2385 #endif /* NOKVERBS */
2386 #ifdef OS2
2387         term_io = term_io_save;
2388     } /* if (!async) */
2389 #endif /* OS2 */
2390 
2391 #ifdef CKCONINTB4CB
2392     /* The order makes a difference in HP-UX 8.00. */
2393     /* The other order makes it think it's in the background when it */
2394     /* returns from CONNECT (Apr 1999). */
2395     setint();
2396     concb((char)escape);                /* Restore console for commands */
2397 #else
2398     /* This is how it has always been so better leave it */
2399     /* this way for all non-HP-UX-8.00 builds. */
2400     concb((char)escape);                /* Restore console for commands */
2401     setint();
2402 #endif /* CKCONINTB4CB */
2403 #ifdef OS2
2404     if (!async)
2405 #endif /* OS2 */
2406       what = W_COMMAND;                 /* Back in command mode. */
2407     return(x);                          /* Done. */
2408 }
2409 #endif /* NOLOCAL */
2410 
2411 #ifndef NOICP
2412 #ifdef COMMENT
2413 /*
2414   It seemed that this was needed for OS/2, in which \v(cmdfile) and other
2415   file-oriented variables or functions can return filenames containing
2416   backslashes, which are subsequently interpreted as quotes rather than
2417   directory separators (e.g. see commented section for VN_CMDF below).
2418   But the problem can't be cured at this level.  Example:
2419 
2420     type \v(cmdfile)
2421 
2422   Without doubling, the filename is parsed correctly, but then when passed
2423   to UNIX 'cat' through the shell, the backslash is removed, and then cat
2424   can't open the file.  With doubling, the filename is not parsed correctly
2425   and the TYPE command fails immediately with a "file not found" error.
2426 */
2427 /*
2428   Utility routine to double all backslashes in a string.
2429   s1 is pointer to source string, s2 is pointer to destination string,
2430   n is length of destination string, both NUL-terminated.
2431   Returns 0 if OK, -1 if not OK (destination string too short).
2432 */
2433 int
dblbs(s1,s2,n)2434 dblbs(s1,s2,n) char *s1, *s2; int n; {
2435     int i = 0;
2436     while (*s1) {
2437         if (*s1 == '\\') {
2438             if (++i > n) return(-1);
2439             *s2++ = '\\';
2440         }
2441         if (++i > n) return(-1);
2442         *s2++ = *s1++;
2443     }
2444     *s2 = NUL;
2445     return(0);
2446 }
2447 #endif /* COMMENT */
2448 
2449 char *
gmdmtyp()2450 gmdmtyp() {                             /* Get modem type */
2451 #ifndef NODIAL
2452     int i, x;
2453 
2454     debug(F111,"gmdmtyp","mdmtyp",mdmtyp);
2455     debug(F111,"gmdmtyp","mdmsav",mdmsav);
2456 
2457     x = mdmtyp;
2458     if (x < 0)                          /* In case of network dialing */
2459       x = mdmsav;
2460     if (x < 1)
2461       return("none");
2462     else
2463       for (i = 0; i < nmdm; i++)
2464         if ((mdmtab[i].kwval == x) && (mdmtab[i].flgs == 0))
2465           return(mdmtab[i].kwd);
2466 #endif /* NODIAL */
2467     return("none");
2468 }
2469 
2470 #ifndef NOXMIT
2471 #ifndef NOLOCAL
2472 /*  T R A N S M I T  --  Raw upload  */
2473 
2474 /*  Obey current line, duplex, parity, flow, text/binary settings. */
2475 /*  Returns 0 upon apparent success, 1 on obvious failure.  */
2476 
2477 /***
2478  Things to add:
2479  . Make both text and binary mode obey set file bytesize.
2480  . Maybe allow user to specify terminators other than CR?
2481  . Maybe allow user to specify prompts other than single characters?
2482  . Make STATISTICS also work for TRANSMIT.
2483  . If TRANSMIT is done without echo, make some kind of (optional) display.
2484  . Make the same optimization for binary-mode transmit that was done for
2485    text-mode (in the no-echo / no-prompt / no-pause case).
2486 ***/
2487 
2488 /*  T R A N S M I T  --  Raw upload  */
2489 
2490 /*  s is the filename, t is the turnaround (prompt) character  */
2491 
2492 /*
2493   Maximum number of characters to buffer.
2494   Must be less than LINBUFSIZ
2495 */
2496 #ifdef OS2
2497 #define XMBUFS 4096                     /* For compatibility with XYZmodem */
2498 #else /* OS2 */
2499 #define XMBUFS 1024
2500 #endif /* OS2 */
2501 
2502 #ifdef TNCODE
2503 #ifndef IAC
2504 #define IAC 255
2505 #endif /* IAC */
2506 #endif /* TNCODE */
2507 
2508 #define OUTXBUFSIZ 15
2509 static CHAR inxbuf[OUTXBUFSIZ+1];       /* Host-to-screen expansion buffer */
2510 static int inxcount = 0;                /* and count */
2511 static CHAR outxbuf[OUTXBUFSIZ+1];      /* Keyboard-to-host expansion buf */
2512 static int outxcount = 0;               /* and count */
2513 
2514 /*  T R A N S M I T  --  Unguarded non-protocol file transmission  */
2515 /*
2516   Call with:
2517     char * s:   Name of file to transmit.
2518     char t:     Turnaround char for text-mode transmission (normally LF).
2519     int xlate:  nonzero = charset translation for text-mode xfer, 0 = skip.
2520     int binary: nonzero = transmit in binary mode, 0 = in text mode.
2521 */
2522 #define XBBUFSIZ 508			/* For binary blasting */
2523 static CHAR xbbuf[XBBUFSIZ+4];
2524 
2525 int
2526 #ifdef CK_ANSIC
transmit(char * s,char t,int xlate,int binary,int xxecho)2527 transmit(char * s, char t, int xlate, int binary, int xxecho)
2528 #else
2529 transmit(s,t,xlate,binary,xxecho) char *s; char t; int xlate, binary, xxecho;
2530 #endif /* CK_ANSIC */
2531 /* transmit */ {
2532 #ifdef MAC
2533     extern char sstate;
2534     int count = 100;
2535 #else
2536     int count = 0;
2537 #ifdef OS2
2538 #ifdef NT
2539     SIGTYP (* oldsig)(int);             /* For saving old interrupt trap. */
2540 #else /* NT */
2541     SIGTYP (* volatile oldsig)(int);
2542 #endif /* NT */
2543 
2544 #else /* OS2 */
2545     SIGTYP (* oldsig)();
2546 #endif /* OS2 */
2547 #endif /* MAC */
2548     int eof = 0;                        /* End of File flag */
2549     int eol = 0;                        /* End of Line flag */
2550     int rc = 1;                         /* Return code. 0=fail, 1=succeed. */
2551     int myflow;                         /* Local copy of global flow... */
2552     int is_tn = 0;                      /* Do Telnet negotiations */
2553     int xbufsiz = XMBUFS;               /* Size of TRANSMIT buffer */
2554     int x, y, c, i;                     /* Int workers... */
2555     int control = 0;                    /* Echo loop control */
2556     long nbytes = 0;                    /* File byte count */
2557     long zz;                            /* Long worker */
2558     char *p;                            /* Char * worker */
2559 
2560 #ifdef PIPESEND
2561     extern int pipesend;
2562 #endif /* PIPESEND */
2563 
2564 #ifndef NOCSETS
2565     int tcs = TC_TRANSP;                /* Intermediate (xfer) char set */
2566     int langsv = L_USASCII;             /* Save current language */
2567     int unicode = 0;
2568     int tcssize = 0;
2569 
2570 #ifdef CK_ANSIC /* ANSI C prototypes... */
2571     CHAR (*sxo)(CHAR);
2572     CHAR (*rxo)(CHAR);
2573     CHAR (*sxi)(CHAR);
2574     CHAR (*rxi)(CHAR);
2575 #else /* Not ANSI C... */
2576     CHAR (*sxo)();
2577     CHAR (*rxo)();
2578     CHAR (*sxi)();
2579     CHAR (*rxi)();
2580 #endif /* CK_ANSIC */
2581 #ifdef UNICODE
2582     union ck_short uc;
2583     int bomorder = 0;
2584 #ifdef CK_ANSIC
2585     extern int (*xl_ufc[MAXFCSETS+1])(USHORT);  /* Unicode to FCS */
2586     extern USHORT (*xl_fcu[MAXFCSETS+1])(CHAR); /* FCS to Unicode */
2587     extern int (*xuf)(USHORT);
2588     extern USHORT (*xfu)(CHAR);
2589 #else
2590     extern int (*xl_ufc[MAXFCSETS+1])();
2591     extern USHORT (*xl_fcu[MAXFCSETS+1])();
2592     extern int (*xuf)();
2593     extern USHORT (*xfu)();
2594 #endif /* CK_ANSIC */
2595 #endif /* UNICODE */
2596 #endif /* NOCSETS */
2597 
2598     debug(F101,"xmit t","",t);
2599     debug(F101,"xmit xlate","",xlate);
2600     debug(F101,"xmit binary","",binary);
2601 
2602 #ifdef PIPESEND
2603     if (pipesend) {
2604         if (nopush) return(-2);
2605         if (zxcmd(ZIFILE,s) < 1) {
2606             printf("?Can't start command: %s\n",s);
2607             return(0);
2608         }
2609     } else
2610 #endif /* PIPESEND */
2611     if (zopeni(ZIFILE,s) == 0) {        /* Open the file to be transmitted */
2612         printf("?Can't open file %s\n",s);
2613         return(0);
2614     }
2615     x = -1;                             /* Open the communication channel */
2616     if (ttopen(ttname,&x,mdmtyp,cdtimo) < 0) {  /* (no harm if already open) */
2617         printf("Can't open device %s\n",ttname);
2618         return(0);
2619     }
2620     zz = x ? speed : -1L;
2621     if (binary) {                       /* Binary file transmission */
2622         myflow = (flow == FLO_XONX) ? FLO_NONE : flow;
2623 
2624         if (ttvt(zz,myflow) < 0) {      /* So no Xon/Xoff! */
2625             printf("Can't condition line\n");
2626             return(0);
2627         }
2628     } else {
2629         if (ttpkt(zz,flow,parity) < 0) { /* Put the line in "packet mode" */
2630             printf("Can't condition line\n"); /* so Xon/Xoff will work, etc. */
2631             return(0);
2632         }
2633     }
2634     is_tn =
2635 #ifdef TNCODE
2636       (local && network && IS_TELNET()) || (!local && sstelnet)
2637 #else
2638         0
2639 #endif /* TNCODE */
2640           ;
2641 
2642 #ifndef NOCSETS
2643 /* Set up character set translations */
2644 
2645     tcs = 0;                            /* "Transfer" or "Other" charset */
2646     sxo = rxo = NULL;                   /* Initialize byte-to-byte functions */
2647     sxi = rxi = NULL;
2648     unicode = 0;                        /* Assume Unicode won't be involved */
2649     if (!binary && xlate) {             /* Set up charset translations */
2650 /*
2651   In the SENDING direction, we are converting from the local file's
2652   character-set (fcharset) to the remote terminal charset (tcsr).  In the
2653   RECEIVING direction (echoing) we are converting from the remote end of the
2654   terminal charset (tcsr) to its local end (tcsl), which is not necessarily
2655   the same as the file character-set.  Especially when the file character
2656   set is UCS-2, which is not a valid terminal character set.  The various
2657   combinations are represented in this table:
2658 
2659   FCS = File Character Set
2660   RCS = Remote Terminal Character Set
2661   CCS = Console (Local Terminal) Character Set
2662 
2663    8   4   2   1
2664   FCS FCS RCS CCS
2665   UCS UTF UTF UTF
2666    0   0   0   0   =   0   =   No translation
2667    0   0   0   1   =   1   =   FCS -> RCS, Echo RCS -> UTF
2668    0   0   1   0   =   2   =   FCS -> UTF, Echo UTF -> CCS
2669    0   0   1   1   =   3   =   FCS -> UTF, Echo no translation
2670 
2671    0   1   0   0   =   4   =   UTF -> RCS, Echo RCS -> CCS
2672    0   1   0   1   =   5   =   UTF -> RCS, Echo RCS -> UTF
2673    0   1   1   0   =   6   =   UTF -> UTF, Echo UTF -> CCS
2674    0   1   1   1   =   7   =   No translation
2675 
2676    1   0   0   0   =   8   =   UCS -> RCS, Echo RCS -> CCS
2677    1   0   0   1   =   9   =   UCS -> RCS, Echo RCS -> UTF
2678    1   0   1   0   =  10   =   UCS -> UTF, Echo UTF -> CCS
2679    1   0   1   1   =  11   =   UCS -> UTF, Echo no translation
2680 */
2681 #ifdef UNICODE
2682         xfu = NULL;                     /* Unicode translation functions */
2683         xuf = NULL;
2684         bomorder = ucsorder;            /* UCS-2 byte order */
2685 
2686         if (fcharset == FC_UCS2)        /* File charset is UCS-2 */
2687           unicode |= 8;
2688         else if (fcharset == FC_UTF8)   /* File charset is UTF-8 */
2689           unicode |= 4;
2690         if (tcsr == FC_UTF8)            /* Remote term charset is UTF-8 */
2691           unicode |= 2;
2692         if (tcsl == FC_UTF8)            /* Local term charset is UTF-8 */
2693           unicode |= 1;
2694 #endif /* UNICODE */
2695 /*
2696   When Unicode not involved -- TCS is the intermediate (xfer) set, and:
2697   sxo = File-to-Intermediate charset function
2698   rxo = Intermediate-to-Remote-Terminal charset function
2699   sxi = Remote-Terminal-to-Intermediate
2700   rxi = Intermediate-to-Local-Terminal
2701 */
2702         tcs = gettcs(tcsr,fcharset);    /* Get intermediate set. */
2703         sxo = xls[tcs][fcharset];       /* translation function */
2704         rxo = xlr[tcs][tcsr];           /* pointers for output functions */
2705         sxi = xls[tcs][tcsr];           /* and for input functions. */
2706         rxi = xlr[tcs][tcsl];
2707 /*
2708   At this point we have unicode nonzero if Unicode is involved in the
2709   conversion, and to 0 if it is not.
2710   The following is to prevent use of zmstuff() and zdstuff() by translation
2711   functions (stuffing works with file i/o, not with communication i/o).
2712 */
2713         langsv = language;              /* Save current SET LANGUAGE */
2714         language = L_USASCII;           /* No language-specific translations */
2715     }
2716 #endif /* NOCSETS */
2717 
2718     i = 0;                              /* Beginning of buffer. */
2719 #ifndef MAC
2720 #ifndef AMIGA
2721     oldsig = signal(SIGINT, trtrap);    /* Save current interrupt trap. */
2722 #endif /* AMIGA */
2723 #endif /* MAC */
2724     tr_int = 0;                         /* Have not been interrupted (yet). */
2725     rc = 1;                             /* Return code presumed good. */
2726 #ifdef VMS
2727     conres();
2728 #endif /* VMS */
2729 
2730 #ifndef NOCSETS
2731     debug(F101,"XMIT unicode","",unicode);
2732 #ifdef UNICODE
2733     debug(F101,"XMIT bomorder","",bomorder);
2734 #endif /* UNICODE */
2735 #endif /* NOCSETS */
2736 
2737     c = 0;                              /* Initial condition */
2738     while (c > -1 && !eof) {            /* Loop for all characters in file */
2739         eol = 0;
2740 #ifdef MAC
2741         /*
2742          * It is expensive to run the miniparser so don't do it for
2743          * every character.
2744          */
2745         if (--count < 0) {
2746             count = 100;
2747             miniparser(1);
2748             if (sstate == 'a') {
2749                 sstate = '\0';
2750                 goto xmitfail;
2751             }
2752         }
2753 #else /* Not MAC */
2754         if (tr_int) {                   /* Interrupted? */
2755             printf("^C...\n");          /* Print message */
2756             goto xmitfail;
2757         }
2758 #endif /* MAC */
2759         c = zminchar();                 /* Get a file character */
2760 #ifdef COMMENT
2761 /* too much */
2762 #ifdef DEBUG
2763         if (deblog) {
2764             if (c < 0)
2765               debug(F101,"XMIT zminchar","",c);
2766             else
2767               debug(F000,"XMIT zminchar","",c);
2768         }
2769 #endif /* DEBUG */
2770 #endif /* COMMENT */
2771         if (c < -1) {                   /* Other error */
2772             printf("?TRANSMIT file read error: %s\n",ck_errstr());
2773             goto xmitfail;
2774         } else if (c > -1) {
2775             nbytes++;
2776             c &= fmask;                 /* Apply SET FILE BYTESIZE mask */
2777         } else if (c == -1) {
2778             eof = 1;
2779             debug(F101,"XMIT eof","",eof);
2780         }
2781         if (binary) {                   /* Binary... */
2782             if (c == -1) {              /* If EOF */
2783                 rc = 1;                 /* Success */
2784                 eof = 1;
2785                 goto xmitexit;          /* Done */
2786             }
2787             if (!xmitw && !xxecho) {    /* Special "blast" mode */
2788                 if (count == XBBUFSIZ) { /* File input buffer full... */
2789                     while (count > 0) {
2790                         errno = 0;
2791                         y = ttol(xbbuf,count);
2792                         if (y < 0) {    /* try to send it. */
2793                             printf("?TRANSMIT output error: %s\n",
2794                                    ck_errstr());
2795                             debug(F111,"XMIT binary ttol error",
2796                                   ck_errstr(),errno);
2797                             rc = 0;
2798                             break;
2799                         }
2800                         if (y < 0) break;
2801                         count -= y;
2802                     }
2803                     count = 0;
2804                 }
2805                 xbbuf[count++] = c;
2806 #ifdef TNCODE
2807                 if (c == IAC && is_tn)  /* Telnet IAC */
2808                   xbbuf[count++] = IAC; /* must be doubled */
2809 #endif /* TNCODE */
2810                 continue;
2811             }
2812             if (ttoc(dopar((char) c)) < 0) { /* else just send the char */
2813                 printf("?Can't transmit character\n");
2814                 goto xmitfail;
2815             }
2816 #ifdef TNCODE
2817             if (c == IAC && is_tn)      /* Quote Telnet IAC */
2818               ttoc((char)IAC);
2819 #endif /* TNCODE */
2820 
2821             if (xmitw)                  /* Pause if requested */
2822               msleep(xmitw);
2823 
2824             if (xxecho) {               /* SET TRANSMIT ECHO ON? */
2825                 if (duplex) {           /* Yes, for half duplex */
2826 #ifndef NOLOCAL
2827 #ifdef OS2
2828                     /* Echo to emulator */
2829                     scriptwrtbuf((USHORT)(c & cmdmsk));
2830 #endif /* OS2 */
2831 #endif /* NOLOCAL */
2832                     if (conoc((char)(c & cmdmsk)) < 0) /* echo locally. */
2833                       goto xmitfail;
2834                 } else {                /* For full duplex, */
2835                     int i, n;           /* display whatever is there. */
2836                     n = ttchk();        /* See how many chars are waiting */
2837                     if (n < 0) {        /* Connection dropped? */
2838                         printf("?Connection lost\n");
2839                         goto xmitfail;
2840                     }
2841                     for (i = 0; i < n; i++) { /* Read and echo that many. */
2842                         x = ttinc(xmitt); /* Timed read just in case. */
2843                         if (x > -1) {   /* If no timeout */
2844                             if (parity) x &= 0x7f; /* display the char, */
2845 #ifndef NOLOCAL
2846 #ifdef OS2
2847                             /* Echo to emulator */
2848                             scriptwrtbuf((USHORT)x);
2849 #endif /* OS2 */
2850 #endif /* NOLOCAL */
2851                             if (conoc((char)(x & cmdmsk)) < 0) {
2852                                 printf("?Output error\n");
2853                                 goto xmitfail;
2854                             }
2855                         } else if (x == -2) {
2856                             printf("Connection closed.\n");
2857                             ttclos(1);
2858                             goto xmitfail;
2859                         } else if (x == -3) {
2860                             printf(
2861                             "Session Limit exceeded - closing connection.\n"
2862                                    );
2863                             ttclos(1);
2864                             goto xmitfail;
2865                         } else {
2866                             printf("?Communications error\n");
2867                             goto xmitfail;
2868                         }
2869                     }
2870                 }
2871             } else ttflui();            /* Not echoing, just flush input. */
2872 
2873         } else {                        /* Text mode, line at a time. */
2874 #ifdef UNICODE
2875             if (fcharset == FC_UCS2 && xlate) { /* Special for UCS-2 */
2876                 char xbuf[8];
2877                 x = 1 - (nbytes & 1);   /* Odd or even byte */
2878                 if (x == 0)             /* Note: 1 = the 1st, 0 = 2nd, etc */
2879                   uc.x_short = 0;
2880                 if (bomorder)           /* Little Endian */
2881                   x = 1 - x;            /* Save byte in appropriate half */
2882                 debug(F101,"XMIT UCS2 x","",x);
2883                 uc.x_char[x] = (CHAR) (c & 0xff);
2884                 if (nbytes & 1)         /* First byte, go back for next */
2885                   continue;
2886                 if (nbytes == 2) {      /* UCS-2 Byte Order Mark */
2887                     if (uc.x_short == (USHORT) 0xfeff) {
2888                         debug(F100,"XMIT UCS2 BOM FEFF","",bomorder);
2889                         continue;
2890                     } else if (uc.x_short == (USHORT) 0xfffe) {
2891                         bomorder = 1 - bomorder;
2892                         debug(F100,"XMIT UCS2 BOM FFFE (swap)","",bomorder);
2893                         continue;
2894                     }
2895                 }
2896                 sprintf(xbuf,"%04X",uc.x_short); /* SAFE */
2897                 debug(F111,"XMIT UCS2",xbuf,uc.x_short);
2898                 if (nbytes & 1)         /* Special eol test for UCS-2 */
2899                   if (uc.x_short == '\n')
2900                     eol = 1;
2901 #ifdef COMMENT
2902                 if (uc.x_short == 0x2028 || uc.x_short == 0x2029)
2903                     eol = 1;
2904 #endif /* COMMENT */
2905             } else
2906 #endif /* UNICODE */
2907               if (c == '\n') {          /* Normal eol test otherwise */
2908                   eol = 1;
2909             }
2910             if (eol) {                  /* End of line? */
2911                 int stuff = -1;
2912                 debug(F101,"XMIT eol length","",i);
2913                 if (i == 0) {           /* Blank line? */
2914                     if (xmitf)          /* Yes, insert fill if asked. */
2915                       line[i++] = dopar((char) xmitf);
2916                 }
2917                 if (i == 0 || ((char) line[i-1]) != ((char) dopar(CR)))
2918                   line[i++] = dopar(CR); /* Terminate it with CR */
2919                 if (xmitl) {
2920                     stuff = LF;
2921 #ifdef TNCODE
2922                 } else if (is_tn && (tn_nlm != TNL_CR)) {
2923                     /* TELNET NEWLINE ON/OFF/RAW */
2924                     stuff = (tn_nlm == TNL_CRLF) ? LF : NUL;
2925 #endif /* TNCODE */
2926                 }
2927                 if (stuff > -1)
2928                   line[i++] = dopar((char)stuff);
2929                 line[i] = NUL;
2930                 debug(F111,"XMIT eol line",line,i);
2931 
2932             } else if (c != -1) {       /* Not a newline, regular character */
2933                 int k, x;
2934                 outxbuf[0] = c;         /* In case of no translation */
2935                 outxcount = 1;          /* Assume result is one byte */
2936 #ifndef NOCSETS
2937                 switch (unicode) {
2938                   case 0:               /* No Unicode involved */
2939                   case 1:
2940                     if (xlate) {        /* If not /TRANSPARENT */
2941                         /* Local-to-intermediate */
2942                         if (sxo) c = (*sxo)((char)c);
2943                         /* Intermediate-to-remote */
2944                         if (rxo) c = (*rxo)((char)c);
2945                         outxbuf[0] = c;
2946                     }
2947                     break;
2948 #ifdef UNICODE
2949                   case 2:               /* Local byte to UTF-8 */
2950                   case 3:
2951                     xfu = xl_fcu[fcharset];
2952                     tcssize = fcsinfo[fcharset].size;
2953                     outxcount =
2954                       b_to_u((CHAR)c,outxbuf,OUTXBUFSIZ,tcssize);
2955                     break;
2956                   case 4:               /* Local UTF-8 to remote byte */
2957                   case 5:
2958                     xuf = xl_ufc[tcsr];
2959                     x = u_to_b((CHAR)c); /* Convert to byte */
2960                     if (x == -1) {      /* If more input bytes needed */
2961                         continue;       /* go back and get them */
2962                     } else if (x == -2) { /* LS or PS (shouldn't happen) */
2963                         outxbuf[0] = CR;
2964                     } else if (x == -9) { /* UTF-8 error */
2965                         outxbuf[0] = '?'; /* Insert error char */
2966                         outxbuf[1] = u_to_b2(); /* Insert next char */
2967                         outxcount = 2;
2968                     } else {
2969                         outxbuf[0] =    /* Otherwise store result */
2970                           (unsigned)(x & 0xff);
2971                     }
2972                     break;
2973                   case 6:               /* UTF-8 to UTF-8 */
2974                   case 7:
2975                     break;
2976                   case 8:               /* UCS-2 to byte */
2977                   case 9:
2978                     xuf = xl_ufc[tcsr];
2979                     outxbuf[0] = (*xuf)(uc.x_short);
2980                     break;
2981                   case 10:
2982                   case 11: {            /* UCS-2 to UTF-8 */
2983                       int j;
2984                       CHAR * buf = NULL;
2985                       x = ucs2_to_utf8(uc.x_short,&buf);
2986                       if (x < 0) {
2987                           outxbuf[0] = 0xff; /* (= U+FFFD) */
2988                           outxbuf[1] = 0xbd;
2989                           x = 2;
2990                       }
2991                       for (j = 0; j < x; j++)
2992                         outxbuf[j] = buf[j];
2993                       outxcount = x;
2994                       break;
2995                   }
2996 #endif /* UNICODE */
2997                 }
2998 #endif /* NOCSETS */
2999                 outxbuf[outxcount] = NUL;
3000                 debug(F111,"XMIT outxbuf",outxbuf,outxcount);
3001 /*
3002   Now the input character (1 or more bytes) is translated into the output
3003   expansion buffer (1 or more bytes); outxcount = number of bytes to add to
3004   the TRANSMIT line buffer, which we do here, taking care of parity, SI/SO
3005   processing, and quoting Telnet IACs.
3006 */
3007                 for (k = 0; k < outxcount; k++) {
3008                     c = outxbuf[k];
3009                     if (xmits && parity && (c & 0200)) { /* If shifting */
3010                         line[i++] = dopar(SO); /* needs to be done, */
3011                         line[i++] = dopar((char)c); /* do it here, */
3012                         line[i++] = dopar(SI); /* crudely. */
3013                     } else {
3014                         line[i++] = dopar((char)c);
3015 #ifdef TNCODE
3016                         if (c == (CHAR)IAC && is_tn)
3017                           line[i++] = (CHAR)IAC;
3018 #endif /* TNCODE */
3019                     }
3020                 }
3021             }
3022 /*
3023   Send characters if buffer full, or at end of line, or at end of file.
3024   (End of line only if echoing, waiting for a prompt, or pausing.)
3025 */
3026             debug(F000,"XMIT c",ckitoa(i),c);
3027             if (i >= xbufsiz || eof || (eol && (xxecho || xmitw || t))) {
3028                 p = line;
3029                 line[i] = '\0';
3030                 debug(F111,"transmit buf",p,i);
3031                 if (ttol((CHAR *)p,i) < 0) { /* try to send it. */
3032                     printf("?TRANSMIT output error: %s\n",ck_errstr());
3033                     rc = 0;
3034                     break;
3035                 }
3036                 i = 0;                  /* Reset buffer pointer. */
3037 /*
3038   Now we handle the echo.  If the user wants to see it, or if we have to
3039   wait for the turnaround character, t.  If the echo is being displayed,
3040   and terminal character-set translation is required, we do it here.
3041 */
3042                 if (duplex && xxecho) {  /* If local echo, echo it */
3043                     if (parity || cmdmsk == 0x7f) { /* Strip hi bits */
3044                         char *ss = line;             /* if necessary */
3045                         while (*ss) {
3046                             *ss &= 0x7f;
3047                             ss++;
3048                         }
3049                     }
3050 #ifndef NOLOCAL
3051 #ifdef OS2
3052                     {                   /* Echo to emulator */
3053                         char *ss = p;
3054                         while (*ss) {
3055                             scriptwrtbuf((USHORT)*ss);
3056                             ss++;
3057                         }
3058                     }
3059 #endif /* OS2 */
3060 #endif /* NOLOCAL */
3061                     if (conoll(p) < 0)
3062                       goto xmitfail;
3063                 }
3064                 if (xmitw)              /* Sleep TRANSMIT PAUSE interval */
3065                   msleep(xmitw);
3066 
3067                 control = 0;            /* Readback loop control */
3068                 if (t != 0 && eol)      /* TRANSMIT PROMPT given and at EOL */
3069                   control |= 1;
3070                 if (xxecho && !duplex)   /* Echo desired and is remote */
3071                   control |= 2;
3072 
3073                 if (control) {          /* Do this if reading back the echo */
3074                     int n;
3075                     x = 0;
3076                     while (1) {
3077                         if (control & 1) { /* Termination criterion */
3078                             if (x == t)    /* for turnaround */
3079                               break;
3080                         } else if (control & 2) { /* And for echoing */
3081                             if ((n = ttchk()) < 1)
3082                               break;
3083                         }
3084                         if ((x = ttinc(xmitt)) < 0) { /* Read with timeout */
3085                             switch (x) {
3086                               case -2:
3087                                 printf("Connection closed.\n");
3088                                 ttclos(1);
3089                                 goto xmitfail;
3090                               case -3:
3091                                 printf(
3092                               "Session Limit exceeded - closing connection.\n"
3093                                        );
3094                                 ttclos(1); /* full thru... */
3095                                 goto xmitfail;
3096                               default:
3097                                 printf("?Timeout\n");
3098                                 goto xmitfail;
3099                             }
3100                         }
3101                         if (x > -1 && (control & 2)) { /* Echo any echoes */
3102                             if (parity)
3103                               x &= 0x7f;
3104                             c = x;
3105 #ifndef NOLOCAL
3106 #ifdef OS2
3107                             scriptwrtbuf((USHORT)x);
3108 #endif /* OS2 */
3109 #endif /* NOLOCAL */
3110                             inxbuf[0] = c;
3111                             inxcount = 1;
3112 #ifndef NOCSETS
3113                             switch (unicode & 3) { /* Remote bits */
3114                               case 0:
3115                                 if (xlate) {
3116                                     if (sxi) c = (*sxi)((CHAR)c);
3117                                     if (rxi) c = (*rxi)((CHAR)c);
3118                                     inxbuf[0] = c;
3119                                 }
3120                                 break;
3121 #ifdef UNICODE
3122                               case 1:   /* Remote Byte to local UTF-8 */
3123                                 xfu = xl_fcu[tcsr];
3124                                 tcssize = fcsinfo[tcsr].size;
3125                                 inxcount =
3126                                   b_to_u((CHAR)c,
3127                                          inxbuf,
3128                                          OUTXBUFSIZ,
3129                                          tcssize
3130                                          );
3131                                 break;
3132                               case 2:   /* Remote UTF-8 to local Byte */
3133                                 xuf = xl_ufc[tcsl];
3134                                 x = u_to_b((CHAR)c);
3135                                 if (x < 0)
3136                                   continue;
3137                                 inxbuf[0] = (unsigned)(x & 0xff);
3138                                 break;
3139                               case 3:   /* UTF-8 to UTF-8 */
3140                                 break;
3141 #endif /* UNICODE */
3142                             }
3143 #endif /* NOCSETS */
3144                             inxbuf[inxcount] = NUL;
3145                             if (conxo(inxcount,(char *)inxbuf) < 0)
3146                               goto xmitfail;
3147                         }
3148                     }
3149                 } else                  /* Not echoing */
3150                   ttflui();             /* Just flush input buffer */
3151             } /* End of buffer-dumping block */
3152         } /* End of text mode */
3153         if (eof) {
3154             rc = 1;
3155             goto xmitexit;
3156         }
3157     } /* End of character-reading loop */
3158 
3159   xmitfail:                             /* Failure exit point */
3160     rc = 0;
3161 
3162   xmitexit:                             /* General exit point */
3163     if (rc > 0) {
3164         if (binary && !xmitw && !xxecho) { /* "blasting"? */
3165             while (count > 0) {            /* Partial buffer still to go? */
3166                 errno = 0;
3167                 y = ttol(xbbuf,count);
3168                 if (y < 0) {
3169                     printf("?TRANSMIT output error: %s\n",
3170                            ck_errstr());
3171                     debug(F111,"XMIT binary eof ttol error",
3172                           ck_errstr(),errno);
3173                     rc = 0;
3174                     break;
3175                 }
3176                 count -= y;
3177             }
3178         } else if (!binary && *xmitbuf) { /* Anything to send at EOF? */
3179             p = xmitbuf;                /* Yes, point to string. */
3180             while (*p)                  /* Send it. */
3181               ttoc(dopar(*p++));        /* Don't worry about echo here. */
3182         }
3183     }
3184 
3185 #ifndef AMIGA
3186 #ifndef MAC
3187     signal(SIGINT,oldsig);              /* Put old signal action back. */
3188 #endif /* MAC */
3189 #endif /* AMIGA */
3190 #ifdef VMS
3191     concb(escape);                      /* Put terminal back, */
3192 #endif /* VMS */
3193     zclose(ZIFILE);                     /* Close file, */
3194 #ifndef NOCSETS
3195     language = langsv;                  /* restore language, */
3196 #endif /* NOCSETS */
3197     ttres();                            /* and terminal modes, */
3198     return(rc);                         /* and return successfully. */
3199 }
3200 #endif /* NOLOCAL */
3201 #endif /* NOXMIT */
3202 
3203 #ifndef NOCSETS
3204 
3205 _PROTOTYP( CHAR (*sxx), (CHAR) );       /* Local translation function */
3206 _PROTOTYP( CHAR (*rxx), (CHAR) );       /* Local translation function */
3207 _PROTOTYP( CHAR zl1as, (CHAR) );        /* Latin-1 to ascii */
3208 _PROTOTYP( CHAR xl1as, (CHAR) );        /* ditto */
3209 
3210 /*  X L A T E  --  Translate a local file from one character set to another */
3211 
3212 /*
3213   Translates input file (fin) from character set csin to character set csout
3214   and puts the result in the output file (fout).  The two character sets are
3215   file character sets from fcstab.
3216 */
3217 
3218 int
xlate(fin,fout,csin,csout)3219 xlate(fin, fout, csin, csout) char *fin, *fout; int csin, csout; {
3220 
3221 #ifndef MAC
3222 #ifdef OS2
3223     extern int k95stdout;
3224     extern int wherex[], wherey[];
3225     extern unsigned char colorcmd;
3226 #ifdef NT
3227     SIGTYP (* oldsig)(int);             /* For saving old interrupt trap. */
3228 #else /* NT */
3229     SIGTYP (* volatile oldsig)(int);    /* For saving old interrupt trap. */
3230 #endif /* NT */
3231 #else /* OS2 */
3232     SIGTYP (* oldsig)();
3233 #endif /* OS2 */
3234 #endif /* MAC */
3235 #ifdef CK_ANSIC
3236     int (*fn)(char);                    /* Output function pointer */
3237 #else
3238     int (*fn)();
3239 #endif /* CK_ANSIC */
3240     extern int xlatype;
3241     int filecode;                       /* Code for output file */
3242     int scrnflg = 0;
3243 
3244     int z = 1;                          /* Return code. */
3245     int x, c, c2;                       /* Workers */
3246 #ifndef UNICODE
3247     int tcs;
3248 #endif /* UNICODE */
3249 
3250     ffc = 0L;
3251 
3252     if (zopeni(ZIFILE,fin) == 0) {      /* Open the file to be translated */
3253 #ifdef COMMENT
3254         /* An error message was already printed by zopeni() */
3255         printf("?Can't open input file %s\n",fin);
3256 #endif /* COMMENT */
3257         return(0);
3258     }
3259 #ifdef MAC
3260 /*
3261   If user specified no output file, it goes to the screen.  For the Mac,
3262   this must be done a special way (result goes to a new window); the Mac
3263   doesn't have a "controlling terminal" device name.
3264 */
3265     filecode = !strcmp(fout,CTTNAM) ? ZCTERM : ZOFILE;
3266 #else
3267 #ifdef VMS
3268     filecode = !strcmp(fout,CTTNAM) ? ZCTERM : ZMFILE;
3269 #else
3270 #ifdef OS2
3271     filecode = (!stricmp(fout,"con") || !stricmp(fout,"con:")) ?
3272         ZCTERM : ZMFILE;
3273     if ((filecode == ZCTERM) && !k95stdout && !inserver)
3274         csout = FC_UCS2;
3275 #else /* OS2 */
3276     filecode = ZOFILE;
3277 #endif /* OS2 */
3278 #endif /* VMS */
3279 #endif /* MAC */
3280     if (zopeno(filecode,fout,NULL,NULL) == 0) { /* And the output file */
3281         printf("?Can't open output file %s\n",fout);
3282         return(0);
3283     }
3284 #ifndef AMIGA
3285 #ifndef MAC
3286     oldsig = signal(SIGINT, trtrap);    /* Save current interrupt trap. */
3287 #endif /* MAC */
3288 #endif /* AMIGA */
3289 
3290     scrnflg = (filecode == ZCTERM);     /* Set output function */
3291     if (scrnflg)
3292       fn = NULL;
3293     else if (filecode == ZMFILE)
3294       fn = putmfil;
3295     else
3296       fn = putfil;
3297 
3298     tr_int = 0;                         /* Have not been interrupted (yet). */
3299     z = 1;                              /* Return code presumed good. */
3300 
3301     if (!scrnflg && !quiet)
3302       printf(" %s (%s) => %s (%s)\n",   /* Say what we're doing. */
3303              fin, fcsinfo[csin].keyword,
3304              fout,fcsinfo[csout].keyword
3305              );
3306 
3307 #ifndef UNICODE
3308 /*
3309   Non-Unicode picks the "most appropriate" transfer character set as the
3310   intermediate set, which results in loss of any characters that the source
3311   and target sets have in common, but are lacking from the intermediate set.
3312 */
3313 #ifdef KANJI
3314     /* Special handling for Japanese... */
3315 
3316     if (fcsinfo[csin].alphabet == AL_JAPAN ||
3317          fcsinfo[csout].alphabet == AL_JAPAN) {
3318         USHORT eu;
3319         int c, x, y;
3320 
3321         xpnbyte(-1,0,0,NULL);           /* Reset output machine */
3322         xlatype = XLA_JAPAN;
3323 
3324         while ((c = xgnbyte(FC_JEUC,csin,NULL)) > -1) { /* Get an EUC byte */
3325             if (tr_int) {               /* Interrupted? */
3326                 printf("^C...\n");      /* Print message */
3327                 z = 0;
3328                 break;
3329             }
3330             /* Send EUC byte to output machine */
3331             if ((x = xpnbyte(c,TC_JEUC,csout,fn)) < 0) {
3332                 z = -1;
3333                 break;
3334             }
3335         }
3336         goto xxlate;
3337     }
3338 #endif /* KANJI */
3339 
3340     /* Regular bytewise conversion... */
3341 
3342     tcs = gettcs(csin,csout);           /* Get intermediate set. */
3343     if (csin == csout) {                /* Input and output sets the same? */
3344         sxx = rxx = NULL;               /* If so, no translation. */
3345     } else {                            /* Otherwise, set up */
3346         if (tcs < 0 || tcs > MAXTCSETS ||
3347             csin < 0 || csin > MAXFCSETS ||
3348             csout < 0 || csout > MAXFCSETS) {
3349             debug(F100,"XLATE csets out of range","",0);
3350             sxx = rxx = NULL;
3351         } else {
3352             sxx = xls[tcs][csin];       /* translation function */
3353             rxx = xlr[tcs][csout];      /* pointers. */
3354             if (rxx == zl1as) rxx = xl1as;
3355         }
3356     }
3357     while ((c = zminchar()) != -1) { /* Loop for all characters in file */
3358         if (tr_int) {                   /* Interrupted? */
3359             printf("^C...\n");          /* Print message */
3360             z = 0;
3361             break;
3362         }
3363         if (sxx) c = (*sxx)((CHAR)c);   /* From fcs1 to tcs */
3364         if (rxx) c = (*rxx)((CHAR)c);   /* from tcs to fcs2 */
3365         if (zchout(filecode,(char)c) < 0) { /* Output xlated character */
3366             z = -1;
3367             break;
3368         }
3369     }
3370     goto xxlate;                        /* Done. */
3371 
3372 #else  /* UNICODE */
3373 /*
3374    Use Unicode as the intermediate character set.  It's simple and gives
3375    little or no loss, but the overhead is a bit higher.
3376 */
3377     initxlate(csin,csout);              /* Set up translation functions */
3378 
3379     if (xlatype == XLA_NONE) {
3380         while ((c = zminchar()) != -1) { /* Loop for all characters in file */
3381             if (tr_int) {               /* Interrupted? */
3382                 printf("^C...\n");      /* Print message */
3383                 z = 0;
3384                 break;
3385             }
3386             if (zchout(filecode,(char)c) < 0) { /* Output xlated character */
3387                 z = -1;
3388                 break;
3389             }
3390         }
3391         goto xxlate;                    /* Done. */
3392     }
3393 
3394 
3395 #ifndef NOLOCAL
3396 #ifdef OS2
3397     if (csout == FC_UCS2 &&             /* we're translating to UCS-2 */
3398         filecode == ZCTERM &&           /* for the real screen... */
3399         !k95stdout && !inserver
3400         ) {
3401         union {
3402             USHORT ucs2;
3403             UCHAR  bytes[2];
3404         } output;
3405 
3406         while (1) {                     /* In this case we go two-by-two. */
3407             if ((c = xgnbyte(FC_UCS2,csin,NULL)) < 0)
3408               break;
3409             output.bytes[0] = c;
3410             if ((c = xgnbyte(FC_UCS2,csin,NULL)) < 0)
3411               break;
3412             output.bytes[1] = c;
3413 
3414             if (tr_int) {               /* Interrupted? */
3415                 printf("^C...\n");      /* Print message */
3416                 z = 0;
3417                 break;
3418             }
3419 
3420             VscrnWrtUCS2StrAtt(VCMD,
3421                                &output.ucs2,
3422                                1,
3423                                wherey[VCMD],
3424                                wherex[VCMD],
3425                                &colorcmd
3426                                );
3427         }
3428     } else
3429 #endif /* OS2 */
3430 #endif /* NOLOCAL */
3431 
3432       /* General case: Get next byte translated from fcs to UCS-2 */
3433 
3434 #ifdef COMMENT
3435       while ((c = xgnbyte(FC_UCS2,csin,NULL)) > -1 &&
3436               (c2 = xgnbyte(FC_UCS2,csin,NULL)) > -1) {
3437           extern int fileorder;
3438 
3439           if (tr_int) {                 /* Interrupted? */
3440               printf("^C...\n");        /* Print message */
3441               z = 0;
3442               break;
3443           }
3444           debug(F001,"XLATE c","",c);
3445           debug(F001,"XLATE c2","",c2);
3446 
3447           /* And then send UCS-2 byte to translate-and-output machine */
3448 
3449           if ((x = xpnbyte(fileorder?c2:c,TC_UCS2,csout,fn)) < 0) {
3450               z = -1;
3451               break;
3452           }
3453           if ((x = xpnbyte(fileorder?c:c2,TC_UCS2,csout,fn)) < 0) {
3454               z = -1;
3455               break;
3456           }
3457       }
3458 #else
3459     while ((c = xgnbyte(FC_UCS2,csin,NULL)) > -1) {
3460           if (tr_int) {                 /* Interrupted? */
3461               printf("^C...\n");        /* Print message */
3462               z = 0;
3463               break;
3464           }
3465           if ((x = xpnbyte(c,TC_UCS2,csout,fn)) < 0) {
3466               z = -1;
3467               break;
3468           }
3469       }
3470 #endif /* COMMENT */
3471 
3472 #endif /* UNICODE */
3473 
3474   xxlate:                               /* Common exit point */
3475 
3476 #ifndef AMIGA
3477 #ifndef MAC
3478     signal(SIGINT,oldsig);              /* Put old signal action back. */
3479 #endif /* MAC */
3480 #endif /* AMIGA */
3481     tr_int = 0;
3482     if (z < 0) {
3483         if (z == -1)
3484           printf("?File output error: %s\n",ck_errstr());
3485         z = 0;
3486     }
3487     zclose(ZIFILE);                     /* Close files */
3488     zclose(filecode);                   /* ... */
3489     return(success = z);                /* and return status. */
3490 }
3491 
3492 int
doxlate()3493 doxlate() {
3494 #ifdef OS2ONLY
3495     extern int tt_font;
3496 #endif /* OS2ONLY */
3497 #ifdef UNIX
3498     extern char ** mtchs;               /* zxpand() file list */
3499 #endif /* UNIX */
3500     int x, y, incs, outcs, multiple = 0, wild = 0, fc = 0, len = 0;
3501     int ofisdir = 0;
3502     char * s, * tocs = "";
3503 
3504     if ((x = cmifi("File(s) to translate","",&s,&wild,xxstring)) < 0) {
3505         if (x == -3) {
3506             printf("?Name of an existing file\n");
3507             return(-9);
3508         } else
3509           return(x);
3510     }
3511     ckstrncpy(line,s,LINBUFSIZ);        /* Save copy of string just parsed. */
3512 
3513     if ((incs = cmkey(fcstab,nfilc,"from character-set","",xxstring)) < 0)
3514       return(incs);
3515 
3516 #ifdef OS2
3517     if (isunicode())
3518       tocs = "ucs2";
3519     else
3520 #endif /* OS2 */
3521       tocs = getdcset();
3522 
3523     if ((outcs = cmkey(fcstab,nfilc,"to character-set",tocs,xxstring)) < 0)
3524       return(outcs);
3525     if ((x = cmofi("output file",CTTNAM,&s,xxstring)) < 0) return(x);
3526     if (x > 1)
3527       ofisdir = 1;
3528 
3529     len = ckstrncpy(tmpbuf,s,TMPBUFSIZ);
3530     if ((y = cmcfm()) < 0) return(y);   /* Confirm the command */
3531 
3532     if (len < 1)
3533       return(-2);
3534 
3535     if (ofisdir)
3536       multiple = 2;
3537     else if (wild) {
3538         if (isdir(tmpbuf))
3539           multiple = 2;
3540         else if (!strcmp(tmpbuf,CTTNAM))
3541           multiple = 1;
3542 #ifdef OS2
3543         else if (!stricmp(tmpbuf,"con") || !stricmp(tmpbuf,"con:"))
3544           multiple = 1;
3545 #else
3546 #ifdef UNIXOROSK
3547         else if (!strncmp(tmpbuf,"/dev/",4))
3548           multiple = 1;
3549 #endif /* UNIXOROSK */
3550 #endif /* OS2 */
3551         if (!multiple) {
3552             printf("?A single file please\n");
3553             return(-9);
3554         }
3555     }
3556     if (!multiple) {                    /* Just one file */
3557         return(success = xlate(line,tmpbuf,incs,outcs));
3558     } else {                            /* Translate multiple files */
3559         char dirbuf[CKMAXPATH+4];
3560         int k;
3561 #ifndef ZXREWIND
3562         int flags = ZX_FILONLY;
3563 #endif /* ZXREWIND */
3564 
3565         if (multiple == 2) {            /* Target is a directory */
3566             k = ckstrncpy(dirbuf,tmpbuf,CKMAXPATH+1) - 1;
3567             if (k < 0)
3568               return(-2);
3569 #ifdef OS2ORUNIX
3570             if (dirbuf[k] != '/') {
3571                 dirbuf[k+1] = '/';
3572                 dirbuf[k+2] = NUL;
3573             }
3574 #else
3575 #ifdef OSK
3576             if (dirbuf[k] != '/') {
3577                 dirbuf[k+1] = '/';
3578                 dirbuf[k+2] = NUL;
3579             }
3580 #else
3581 #ifdef VMS
3582             if (ckmatch("*.DIR;1",s,0,0))
3583               k = cvtdir(tmpbuf,dirbuf,TMPBUFSIZ);
3584             if (dirbuf[k] != ']' &&
3585                 dirbuf[k] != '>' &&
3586                 dirbuf[k] != ':')
3587               return(-2);
3588 #else
3589 #ifdef datageneral
3590             if (dirbuf[k] != ':') {
3591                 dirbuf[k+1] = ':';
3592                 dirbuf[k+2] = NUL;
3593             }
3594 #else
3595 #ifdef STRATUS
3596             if (dirbuf[k] != '>') {
3597                 dirbuf[k+1] = '>';
3598                 dirbuf[k+2] = NUL;
3599             }
3600 #endif /* STRATUS */
3601 #endif /* datageneral */
3602 #endif /* VMS */
3603 #endif /* OSK */
3604 #endif /* OS2ORUNIX */
3605         }
3606 
3607 #ifdef ZXREWIND
3608         fc = zxrewind();                /* Rewind the file list */
3609 #else
3610         if (matchdot)  flags |= ZX_MATCHDOT;
3611         fc = nzxpand(line,flags);
3612 #endif /* ZXREWIND */
3613 
3614         if (fc < 1) {
3615             printf("?Wildcard expansion error\n");
3616             return(-9);
3617         }
3618 #ifdef UNIX
3619         sh_sort(mtchs,NULL,fc,0,0,filecase); /* Sort the file list */
3620 #endif /* UNIX */
3621 
3622         while (1) {                     /* Loop through the files */
3623             znext(line);
3624             if (!line[0])
3625               break;
3626             if (multiple == 2)
3627               ckmakmsg(tmpbuf,TMPBUFSIZ,dirbuf,line,NULL,NULL);
3628             if (xlate(line,tmpbuf,incs,outcs) < 1)
3629               return(success = 0);
3630         }
3631     }
3632     return(success = 1);
3633 }
3634 #endif /* NOCSETS */
3635 
3636 /*
3637   Returns path of user's actual home directory (or, if specified, SET CD HOME
3638   directory), properly terminated to allow filenames or sub-paths to be
3639   appended.
3640 */
3641 static char hompthbuf[CKMAXPATH+1] = { NUL, NUL }; /* Home directory path */
3642 
3643 char *
homepath()3644 homepath() {
3645     int x;
3646     extern char hompthbuf[];
3647     extern char * myhome;
3648     char * h;
3649 
3650     /* myhome = SET CD HOME; zhome() uses API to get home directory */
3651     h = myhome ? myhome : zhome();
3652     hompthbuf[0] = NUL;
3653 
3654 /* Ensure it is terminated with appropriate directory or device separator. */
3655 
3656 #ifdef UNIXOROSK
3657     x = ckstrncpy(hompthbuf,h,CKMAXPATH+1);
3658     if (x <= 0) {
3659         hompthbuf[0] = '/';
3660         hompthbuf[1] = NUL;
3661     } else if (x < CKMAXPATH - 2 && hompthbuf[x-1] != '/') {
3662         hompthbuf[x] = '/';
3663         hompthbuf[x+1] = NUL;
3664     }
3665     return(hompthbuf);
3666 #else
3667 #ifdef STRATUS
3668     if (strlen(h) < CKMAXPATH)
3669       sprintf(hompthbuf,"%s>",h);	/* SAFE */
3670     return(hompthbuf);
3671 #else
3672     return(h);
3673 #endif /* STRATUS */
3674 #endif /* UNIXOROSK */
3675 }
3676 
3677 /*  D O L O G  --  Do the log command  */
3678 
3679 int
dolog(x)3680 dolog(x) int x; {
3681     int y, disp; char *s = NULL, * p = NULL, * q = NULL;
3682     extern int isguest;
3683 #ifdef ZFNQFP
3684     struct zfnfp * fnp;
3685 #endif /* ZFNQFP */
3686 
3687     if (isguest) {
3688         printf("?Anonymous log creation not allowed\n");
3689         return(-9);
3690     }
3691     switch (x) {                        /* Which log... */
3692 
3693 #ifdef DEBUG
3694       case LOGD:
3695         q = "debug.log";
3696         y = cmofi("Name of debugging log file",q,&s,xxstring);
3697         break;
3698 #endif /* DEBUG */
3699 
3700       case LOGP:
3701         q = "packet.log";
3702         y = cmofi("Name of packet log file",q,&s,xxstring);
3703         break;
3704 
3705 #ifndef NOLOCAL
3706       case LOGS:
3707         q = "session.log";
3708         y = cmofi("Name of session log file",q,&s,xxstring);
3709         break;
3710 #endif /* NOLOCAL */
3711 
3712 #ifdef TLOG
3713       case LOGT:
3714         q = "transact.log";
3715         y = cmofi("Name of transaction log file",q,&s,xxstring);
3716         break;
3717 #endif /* TLOG */
3718 
3719 #ifdef CKLOGDIAL
3720       case LOGM: {
3721           int m, n;
3722           char mypath[CKMAXPATH+1];
3723           q = CXLOGFILE;
3724           m = ckstrncpy(mypath,homepath(),CKMAXPATH);
3725           n = strlen(CXLOGFILE);
3726           if (m + n < CKMAXPATH)
3727             ckstrncat(mypath,CXLOGFILE,CKMAXPATH);
3728           else
3729             ckstrncpy(mypath,CXLOGFILE,CKMAXPATH);
3730           y = cmofi("Name of connection log file",mypath,&s,xxstring);
3731           break;
3732       }
3733 #endif /* CKLOGDIAL */
3734 
3735       default:
3736         printf("\n?Unknown log designator - %d\n",x);
3737         return(-2);
3738     }
3739     if (y < 0) return(y);
3740     if (y == 2) {                       /* If they gave a directory name */
3741         int k;
3742         char * ds = "/";
3743         k = strlen(s);
3744         if (k > 0 && s[k-1] == '/') ds = "";
3745         ckmakmsg(tmpbuf,TMPBUFSIZ,s,ds,q,NULL);
3746         s = tmpbuf;
3747     }
3748 #ifdef ZFNQFP
3749 #ifdef OS2ORUNIX
3750     if (*s != '|')                      /* Allow for pipes */
3751 #else
3752 #ifdef OSK
3753     if (*s != '|')
3754 #endif /* OSK */
3755 #endif /* OS2ORUNIX */
3756       if ((fnp = zfnqfp(s,TMPBUFSIZ - 1,tmpbuf))) {
3757           if (fnp->fpath)
3758             if ((int) strlen(fnp->fpath) > 0)
3759               s = fnp->fpath;
3760       } /* else if error keep original string */
3761 #endif /* ZFNQFP */
3762 
3763     ckstrncpy(line,s,LINBUFSIZ);
3764     s = line;
3765 #ifdef MAC
3766     y = 0;
3767 #else
3768 
3769     p = "new";
3770 #ifdef TLOG
3771     if ((x == LOGT && tlogfmt == 2) || x == LOGM)
3772       p = "append";
3773 #endif /* TLOG */
3774 
3775     if ((y = cmkey(disptb,2,"Disposition",p,xxstring)) < 0)
3776       return(y);
3777 #endif /* MAC */
3778     disp = y;
3779     if ((y = cmcfm()) < 0) return(y);
3780 
3781     switch (x) {
3782 
3783 #ifdef DEBUG
3784       case LOGD:
3785         return(deblog = debopn(s,disp));
3786 #endif /* DEBUG */
3787 
3788 #ifndef NOXFER
3789       case LOGP:
3790         return(pktlog = pktopn(s,disp));
3791 #endif /* NOXFER */
3792 
3793 #ifndef NOLOCAL
3794       case LOGS:
3795         setseslog(sesopn(s,disp));
3796         return(seslog);
3797 #endif /* NOLOCAL */
3798 
3799 #ifdef TLOG
3800       case LOGT:
3801         return(tralog = traopn(s,disp));
3802 #endif /* TLOG */
3803 
3804 #ifdef CKLOGDIAL
3805       case LOGM:
3806         return(dialog = diaopn(s,disp,0));
3807 #endif /* CKLOGDIAL */
3808 
3809       default:
3810         return(-2);
3811     }
3812 }
3813 
3814 #ifndef NOXFER
3815 int
pktopn(s,disp)3816 pktopn(s,disp) char *s; int disp; {
3817     static struct filinfo xx;
3818 
3819     if (!s)
3820       s = "";
3821     if (!*s)
3822       return(0);
3823 
3824     debug(F111,"pktopn",s,disp);
3825 
3826     zclose(ZPFILE);
3827 
3828 #ifdef OS2ORUNIX
3829     if (s[0] == '|') {                  /* Pipe */
3830         char * p = s + 1;
3831         debug(F110,"pktopn p",p,0);
3832         while (*p) {
3833             if (*p != ' ')
3834               break;
3835             else
3836               p++;
3837         }
3838         debug(F110,"pktopn pipe",p,0);
3839         pktlog = zxcmd(ZPFILE,p);
3840         debug(F101,"pktopn seslog","",seslog);
3841     } else {                            /* File */
3842 #endif /* OS2ORUNIX */
3843         if (disp) {
3844             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
3845             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
3846             xx.lblopts = 0;
3847             pktlog = zopeno(ZPFILE,s,NULL,&xx);
3848         } else pktlog = zopeno(ZPFILE,s,NULL,NULL);
3849         if (!pktlog && !quiet)
3850           printf("?%s - %s\n",s,ck_errstr());
3851 #ifdef OS2ORUNIX
3852     }
3853 #endif /* OS2ORUNIX */
3854     if (pktlog > 0)
3855       ckstrncpy(pktfil,s,CKMAXPATH+1);
3856     else
3857       *pktfil = '\0';
3858     return(pktlog);
3859 }
3860 #endif /* NOXFER */
3861 
3862 int
traopn(s,disp)3863 traopn(s,disp) char *s; int disp; {
3864 #ifdef TLOG
3865     static struct filinfo xx;
3866 
3867     if (!s)
3868       s = "";
3869     if (!*s)
3870       return(0);
3871 
3872     debug(F111,"traopn",s,disp);
3873     debug(F101,"traopn tlogfmt","",tlogfmt);
3874 
3875     zclose(ZTFILE);
3876 
3877 #ifdef OS2ORUNIX
3878     if (tlogfmt == 2) {                 /* FTP format is special... */
3879         VOID doiklog();
3880         if (!disp)                      /* Append? */
3881           if (zchki(s) > -1)            /* No - does file exist? */
3882             (VOID) zdelet(s);           /* Yes - delete it. */
3883         xferlog = 1;
3884         ckstrncpy(trafil,s,CKMAXPATH);
3885         makestr(&xferfile,s);
3886         doiklog();
3887         return(1);
3888     }
3889     if (s[0] == '|') {                  /* Pipe */
3890         char * p = s + 1;
3891         debug(F110,"traopn p",p,0);
3892         while (*p) {
3893             if (*p != ' ')
3894               break;
3895             else
3896               p++;
3897         }
3898         debug(F110,"traopn pipe",p,0);
3899         tralog = zxcmd(ZTFILE,p);
3900         debug(F101,"traopn tralog","",tralog);
3901     }
3902 #endif /* OS2ORUNIX */
3903 
3904     if (s[0] != '|') {                  /* File */
3905         if (disp) {
3906             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
3907             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
3908             xx.lblopts = 0;
3909             tralog = zopeno(ZTFILE,s,NULL,&xx);
3910         } else tralog = zopeno(ZTFILE,s,NULL,NULL);
3911     }
3912     if (!tralog && !quiet)
3913       printf("?%s - %s\n",s,ck_errstr());
3914     if (tralog > 0 && tlogfmt > 0) {
3915         ckstrncpy(trafil,s,CKMAXPATH);
3916         tlog(F110,"Transaction Log:",versio,0L);
3917 #ifndef MAC
3918         tlog(F100,ckxsys,"",0L);
3919 #endif /* MAC */
3920         ztime(&s);
3921         tlog(F100,s,"",0L);
3922     } else
3923       *trafil = '\0';
3924     return(tralog);
3925 #else
3926     return(0);
3927 #endif /* TLOG */
3928 }
3929 
3930 #ifndef NOLOCAL
3931 int
sesopn(s,disp)3932 sesopn(s,disp) char * s; int disp; {
3933     static struct filinfo xx;
3934     extern int tsstate;
3935 
3936     tsstate = 0;                        /* Session log timestamp state */
3937 
3938     if (!s)
3939       s = "";
3940     if (!*s)
3941       return(0);
3942 
3943     debug(F111,"sesopn",s,disp);
3944 
3945     zclose(ZSFILE);
3946 
3947 #ifdef OS2ORUNIX
3948     if (s[0] == '|') {                  /* Pipe */
3949         char * p = s + 1;
3950         debug(F110,"sesopn p",p,0);
3951         while (*p) {
3952             if (*p != ' ')
3953               break;
3954             else
3955               p++;
3956         }
3957         debug(F110,"sesopn pipe",p,0);
3958         setseslog(zxcmd(ZSFILE,p));
3959         debug(F101,"sesopn seslog","",seslog);
3960     } else {                            /* File */
3961 #endif /* OS2ORUNIX */
3962         if (disp) {
3963             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
3964             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
3965             xx.lblopts = 0;
3966             setseslog(zopeno(ZSFILE,s,NULL,&xx));
3967         } else
3968           setseslog(zopeno(ZSFILE,s,NULL,NULL));
3969         if (!seslog && !quiet)
3970           printf("?%s - %s\n",s,ck_errstr());
3971 #ifdef OS2ORUNIX
3972     }
3973 #endif /* OS2ORUNIX */
3974     if (seslog > 0)
3975       ckstrncpy(sesfil,s,CKMAXPATH+1);
3976     else
3977       *sesfil = '\0';
3978     return(seslog);
3979 }
3980 #endif /* NOLOCAL */
3981 #endif /* NOICP */
3982 
3983 int
debopn(s,disp)3984 debopn(s,disp) char *s; int disp; {
3985 #ifdef DEBUG
3986 #ifdef CK_UTSNAME
3987     extern char unm_mch[], unm_nam[], unm_rel[], unm_ver[], unm_mod[];
3988 #endif /* CK_UTSNAME */
3989 #ifdef OS2
3990     extern char ckxsystem[];
3991 #endif /* OS2 */
3992     char *tp;
3993     static struct filinfo xx;
3994 
3995     if (!s)
3996       s = "";
3997     if (!*s)
3998       return(0);
3999 
4000     zclose(ZDFILE);
4001 
4002 #ifdef OS2ORUNIX
4003     if (s[0] == '|') {                  /* Pipe */
4004         char * p = s + 1;
4005         debug(F110,"debopn p",p,0);
4006         while (*p) {
4007             if (*p != ' ')
4008               break;
4009             else
4010               p++;
4011         }
4012         debug(F110,"debopn pipe",p,0);
4013         deblog = zxcmd(ZDFILE,p);
4014         debug(F101,"debopn deblog","",deblog);
4015     } else {                            /* File */
4016 #endif /* OS2ORUNIX */
4017         if (disp) {
4018             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
4019             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
4020             xx.lblopts = 0;
4021             deblog = zopeno(ZDFILE,s,NULL,&xx);
4022         } else
4023           deblog = zopeno(ZDFILE,s,NULL,NULL);
4024         if (!deblog && !quiet)
4025           printf("?%s - %s\n",s,ck_errstr());
4026 #ifdef OS2ORUNIX
4027     }
4028 #endif /* OS2ORUNIX */
4029     if (deblog > 0) {
4030         ckstrncpy(debfil,s,CKMAXPATH+1);
4031         debug(F110,"Debug Log ",versio,0);
4032 #ifndef MAC
4033 #ifdef OS2
4034         debug(F110,ckxsys,ckxsystem,0);
4035 #else /* OS2 */
4036         debug(F100,ckxsys,"",0);
4037 #endif /* OS2 */
4038 #endif /* MAC */
4039 #ifdef CK_UTSNAME
4040         if (unm_mch[0]) {
4041             debug(F110,"uname machine",unm_mch,0);
4042             if (unm_mod[0])
4043               debug(F110,"uname model  ",unm_mod,0);
4044             debug(F110,"uname sysname",unm_nam,0);
4045             debug(F110,"uname release",unm_rel,0);
4046             debug(F110,"uname version",unm_ver,0);
4047         }
4048 #ifdef KTARGET
4049         {
4050             char * s;                   /* Makefile target */
4051             s = KTARGET;
4052             if (!s) s = "";
4053             if (!*s) s = "(unknown)";
4054             debug(F110,"build target",s,0);
4055         }
4056 #endif /* KTARGET */
4057         deblog = 0;
4058         ztime(&tp);
4059         deblog = 1;
4060         debug(F100,tp,"",0);
4061 #endif /* UTSNAME */
4062         debug(F101,"byteorder","",byteorder);
4063 #ifndef NOICP
4064 #ifndef NOLOCAL
4065         if (local) {
4066             debug(F110,"Active connection: ",ttname,0);
4067             if (!network) {
4068                 debug(F101,"Speed","",speed);
4069                 if (hwparity)
4070                   debug(F110,"Parity[hardware]",parnam((char)hwparity),0);
4071                 else
4072                   debug(F110,"Parity",parnam((char)parity),0);
4073                 deblog = 0;
4074                 debug(F110,"Modem",gmdmtyp(),0);
4075                 deblog = 1;
4076             }
4077         } else {
4078             debug(F110,"Active connection: ","none",0);
4079         }
4080 #endif /* NOLOCAL */
4081 #endif /* NOICP */
4082     } else *debfil = '\0';
4083     return(deblog);
4084 #else
4085     return(0);
4086 #endif /* MAC */
4087 }
4088 
4089 
4090 /*  C K D A T E  --  Returns current date/time in standard format  */
4091 
4092 static char nowbuf[18];
4093 
4094 char *
ckdate()4095 ckdate() {
4096     extern struct keytab cmonths[];
4097     int x;
4098     char * t;                   /* Substitute today's date */
4099     char dbuf[32];
4100     ztime(&t);
4101 
4102 /*  012345678901234567890123 */
4103 /*  Sat Jul  4 12:16:43 1998 */
4104 
4105     ckstrncpy(dbuf,t,32);
4106     t = dbuf;
4107     debug(F110,"ckdate dbuf",dbuf,0);
4108     nowbuf[0] = t[20];
4109     nowbuf[1] = t[21];
4110     nowbuf[2] = t[22];
4111     nowbuf[3] = t[23];
4112 
4113     nowbuf[4] = NUL;
4114     debug(F110,"ckdate nowbuf",nowbuf,0);
4115 
4116     t[7] = NUL;
4117     if ((x = lookup(cmonths,t+4,12,NULL)) < 0) {
4118         debug(F110,"ckdate bad month",t,0);
4119         return("<BAD_MONTH>");
4120     }
4121     sprintf(nowbuf+4,"%02d",x);         /* SAFE */
4122     nowbuf[6] = (t[8] == SP) ? '0' : t[8];
4123     nowbuf[7] = t[9];
4124     nowbuf[8] = ' ';
4125 
4126     nowbuf[9] = NUL;
4127     debug(F110,"ckdate nowbuf",nowbuf,0);
4128 
4129     for (x = 11; x < 19; x++) nowbuf[x-2] = t[x];
4130     nowbuf[17] = NUL;
4131     debug(F110,"ckdate nowbuf",nowbuf,0);
4132 
4133     return((char *)nowbuf);
4134 }
4135 
4136 #ifndef NOICP
4137 #ifdef CKLOGDIAL
4138 
4139 /*
4140   fc = 0 for initial open, meaning open, then close immediately.
4141   fc > 0 for subsequent opens, meaning open for use, leave open.
4142 */
4143 int
diaopn(s,disp,fc)4144 diaopn(s,disp,fc) char *s; int disp, fc; {
4145     static struct filinfo xx;
4146 
4147     if (!s)
4148       s = "";
4149     if (!*s)
4150       return(0);
4151 
4152     debug(F110,"diaopn log",s,0);
4153     debug(F101,"diaopn fc",s,fc);
4154     debug(F101,"diaopn disp 1",s,disp);
4155     if (fc) disp = 1;                   /* Force append if open for use */
4156     debug(F101,"diaopn disp 2",s,disp);
4157 
4158     zclose(ZDIFIL);                     /* In case a log was already open */
4159 
4160 #ifdef OS2ORUNIX
4161     if (s[0] == '|') {                  /* Pipe */
4162         char * p = s + 1;
4163         debug(F110,"diaopn p",p,0);
4164         while (*p) {
4165             if (*p != ' ')
4166               break;
4167             else
4168               p++;
4169         }
4170         debug(F110,"diaopn pipe",p,0);
4171         dialog = zxcmd(ZDIFIL,p);
4172         debug(F101,"diaopn dialog","",dialog);
4173     } else {                            /* File */
4174 #endif /* OS2ORUNIX */
4175         if (disp) {
4176             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
4177             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
4178             xx.lblopts = 0;
4179             dialog = zopeno(ZDIFIL,s,NULL,&xx);
4180         } else dialog = zopeno(ZDIFIL,s,NULL,NULL);
4181         if (!dialog)
4182           printf("?%s - %s\n",s,ck_errstr());
4183 #ifdef OS2ORUNIX
4184     }
4185 #endif /* OS2ORUNIX */
4186     if (dialog > 0)
4187       ckstrncpy(diafil,s,CKMAXPATH+1);
4188     else
4189       *diafil = '\0';
4190     if (fc == 0)                        /* Initial open */
4191       zclose(ZDIFIL);                   /* close it */
4192     return(dialog);
4193 }
4194 #endif /* CKLOGDIAL */
4195 
4196 #ifndef NOSHOW
4197 
4198 /*  SHOW command routines */
4199 
4200 char *
shoxm()4201 shoxm() {
4202     char * s;
4203     switch (binary) {
4204       case XYFT_T: s = "text";         break;
4205 #ifdef VMS
4206       case XYFT_B: s = "binary fixed"; break;
4207       case XYFT_I: s = "image";        break;
4208       case XYFT_L: s = "labeled";      break;
4209       case XYFT_U: s = "binary undef"; break;
4210 #else
4211 #ifdef MAC
4212       case XYFT_B: s = "binary";       break;
4213       case XYFT_M: s = "macbinary";    break;
4214 #else
4215       case XYFT_B: s = "binary";       break;
4216 #ifdef CK_LABELED
4217       case XYFT_L: s = "labeled";      break;
4218 #endif /* CK_LABELED */
4219 #endif /* MAC */
4220 #endif /* VMS */
4221       default: s = "unknown"; break;
4222     }
4223     return(s);
4224 }
4225 
4226 #ifndef NOXFER
4227 VOID                                    /* SHOW TRANSFER */
shoxfer()4228 shoxfer() {
4229     extern int docrc, usepipes, xfrxla, whereflg;
4230     extern char * xfrmsg;
4231     printf("\n");
4232     printf(" Transfer Bell: %s\n",showoff(xfrbel));
4233     printf(" Transfer Interruption: %s\n",showoff(xfrint));
4234     printf(" Transfer Cancellation: %s\n",showoff(xfrcan));
4235 #ifndef NOCSETS
4236     printf(" Transfer Translation:  %s\n",showoff(xfrxla));
4237     printf(" Transfer Character-set: ");
4238     if (tcharset == TC_TRANSP)
4239       printf("Transparent\n");
4240     else
4241       printf("%s\n",tcsinfo[tcharset].keyword);
4242 #endif /* NOCSETS */
4243     printf(" Transfer CRC-calculation: %s\n",showoff(docrc));
4244     printf(" Transfer Display: ");
4245     switch (fdispla) {
4246       case XYFD_N: printf("%s\n","none"); break;
4247       case XYFD_R: printf("%s\n","serial"); break;
4248       case XYFD_C: printf("%s\n","fullscreen"); break;
4249       case XYFD_S: printf("%s\n","crt"); break;
4250       case XYFD_B: printf("%s\n","brief"); break;
4251       case XYFD_G: printf("%s\n","gui"); break;
4252     }
4253     printf(" Transfer Message: %s\n", xfrmsg ? xfrmsg : "(none)");
4254     printf(" Transfer Locking-shift: ");
4255     if (lscapu == 2) {
4256         printf("forced");
4257     } else {
4258         printf("%s", (lscapr ? "enabled" : "disabled"));
4259         if (lscapr) printf(",%s%s", (lscapu ? " " : " not "), "used");
4260     }
4261     printf("\n Transfer Mode: %s\n",
4262            xfermode == XMODE_A ?
4263            "automatic" :
4264            "manual"
4265            );
4266     printf(" Transfer Pipes: %s\n", showoff(usepipes));
4267     printf(" Transfer Protocol: %s\n",ptab[protocol].p_name);
4268     printf(" Transfer Report: %s\n",showoff(whereflg));
4269     printf(" Transfer Slow-start: %s\n",showoff(slostart));
4270     printf("\n");
4271 }
4272 #endif /* NOXFER */
4273 
4274 VOID
shoflow()4275 shoflow() {
4276     int i, x;
4277     extern int cxflow[], cxtype, ncxname, nfloname, autoflow;
4278     extern char * cxname[];
4279     printf("\nConnection type:        %s\n",cxname[cxtype]);
4280     if (autoflow) {
4281         printf("Current flow-control:   %s\n", floname[cxflow[cxtype]]);
4282         printf("Switches automatically: yes\n");
4283     } else {
4284         printf("Current flow-control:   %s\n", floname[flow]);
4285         printf("Switches automatically: no\n");
4286     }
4287     printf("\nDefaults by connection type:\n");
4288     debug(F111,"shoflow cxtype",cxname[cxtype],cxtype);
4289     debug(F101,"shoflow flow","",flow);
4290     for (i = 0; i < ncxname; i++) {
4291 #ifdef NOLOCAL
4292         if (i > 0) break;
4293 #endif /* NOLOCAL */
4294 #ifndef NETCONN
4295         if (i > 2) break;
4296 #endif /* NETCONN */
4297 #ifndef DECNET
4298         if (i == CXT_DECNET) continue;
4299 #endif /* DECNET */
4300 #ifndef DECNET
4301 #ifndef SUPERLAT
4302         if (i == CXT_LAT) continue;
4303 #endif /* SUPERLAT */
4304 #endif /* DECNET */
4305 #ifndef CK_NETBIOS
4306         if (i == CXT_NETBIOS) continue;
4307 #endif /* CK_NETBIOS */
4308 #ifndef NPIPE
4309         if (i == CXT_NPIPE) continue;
4310 #endif /* NPIPE */
4311 #ifndef NETCMD
4312         if (i == CXT_PIPE) continue;
4313 #endif /* NETCMD */
4314 #ifndef ANYX25
4315         if (i == CXT_X25) continue;
4316 #endif /* ANYX25 */
4317         x = cxflow[i];
4318         debug(F101,"shoflow x","",x);
4319         if (x < nfloname)
4320           printf("  %-14s: %s\n",cxname[i],floname[x]);
4321         else
4322           printf("  %-14s: (%d)\n",cxname[i],x);
4323     }
4324     printf("\n");
4325 }
4326 
4327 #ifndef NOLOCAL
4328 #ifdef ANYX25
4329 int
shox25(n)4330 shox25(n) int n; {
4331     if (nettype == NET_SX25) {
4332         printf("SunLink X.25 V%d.%d",x25ver / 10,x25ver % 10);
4333         if (ttnproto == NP_X3) printf(", PAD X.3, X.28, X.29 protocol,");
4334         printf("\n");
4335         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4336         printf(" Reverse charge call %s",
4337                revcall ? "selected" : "not selected");
4338         printf (", Closed user group ");
4339         if (closgr > -1)
4340           printf ("%d",closgr);
4341         else
4342           printf ("not selected");
4343         printf("\n");
4344         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4345         printf(" Call user data %s.\n", cudata ? udata : "not selected");
4346         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4347     } else if (nettype == NET_VX25) {
4348         if (ttnproto == NP_X3) printf(", PAD X.3, X.28, X.29 protocol,");
4349         printf("\n");
4350         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4351         printf(" Reverse charge call %s",
4352                revcall ? "selected" : "not selected");
4353         printf (", Closed user group [unsupported]");
4354         if (closgr > -1)
4355           printf ("%d",closgr);
4356         else
4357           printf ("not selected");
4358         printf (",");
4359         printf("\n");
4360         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4361         printf(" Call user data %s.\n", cudata ? udata : "not selected");
4362         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4363     } else if (nettype == NET_IX25) {
4364         printf("AIX NPI X.25\n");
4365         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4366         printf("\n Reverse charge call %s",
4367                revcall ? "selected" : "not selected");
4368         printf (", Closed user group [unsupported]");
4369         if (closgr > -1)
4370           printf ("%d",closgr);
4371         else
4372           printf ("not selected");
4373         printf (",");
4374         printf("\n Call user data %s.\n", cudata ? udata : "not selected");
4375     }
4376     return(n);
4377 }
4378 
4379 #ifndef IBMX25
4380 int
shopad(n)4381 shopad(n) int n; {
4382     int i;
4383     printf("\nX.3 PAD Parameters:\n");
4384     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4385     for (i = 0; i < npadx3; i++) {
4386         printf(" [%d] %s %d\n",padx3tab[i].kwval,padx3tab[i].kwd,
4387                padparms[padx3tab[i].kwval]);
4388         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4389     }
4390     return(n);
4391 }
4392 #endif /* IBMX25 */
4393 #endif /* ANYX25 */
4394 
4395 VOID
shoparc()4396 shoparc() {
4397     extern int reliable, stopbits, clsondisc;
4398     int i; char *s;
4399     long zz;
4400 
4401 #ifdef NEWFTP
4402     if (ftpisconnected()) {
4403         shoftp(1);
4404         printf("\n");
4405     }
4406 #endif /* NEWFTP */
4407 
4408     printf("Communications Parameters:\n");
4409 
4410     if (network
4411 #ifdef IKSD
4412          || inserver
4413 #endif /* IKSD */
4414          ) {
4415         printf(" Network Host: %s%s",ttname,
4416                (reliable == SET_ON || (reliable == SET_AUTO && !local)
4417 #ifdef TN_COMPORT
4418                && !istncomport()
4419 #endif /* TN_COMPORT */
4420 #ifdef IKSD
4421                || inserver
4422 #endif /* IKSD */
4423                ) ? " (reliable)" : "");
4424 #ifdef TN_COMPORT
4425         if (istncomport()) {
4426             int modemstate;
4427             char * oflow, * iflow = "", * parity, * stopsize, * signature;
4428             int baud = tnc_get_baud();
4429 
4430             switch (tnc_get_oflow()) {
4431               case TNC_CTL_OFLOW_NONE:
4432                 oflow = "none";
4433                 break;
4434               case TNC_CTL_OFLOW_XON_XOFF:
4435                 oflow = "xon/xoff";
4436                 break;
4437               case TNC_CTL_OFLOW_RTS_CTS:
4438                 oflow = "rts/cts";
4439                 break;
4440               case TNC_CTL_OFLOW_DCD:
4441                 oflow = "dcd";
4442                 break;
4443               case TNC_CTL_OFLOW_DSR:
4444                 oflow = "dsr";
4445                 break;
4446               default:
4447                 oflow = "(unknown)";
4448             }
4449             switch (tnc_get_iflow()) {
4450               case TNC_CTL_IFLOW_NONE:
4451                 iflow = "none";
4452                 break;
4453               case TNC_CTL_IFLOW_XON_XOFF:
4454                 iflow = "xon/xoff";
4455                 break;
4456               case TNC_CTL_IFLOW_RTS_CTS:
4457                 iflow = "rts/cts";
4458                 break;
4459               case TNC_CTL_IFLOW_DTR:
4460                 break;
4461               default:
4462                 iflow = oflow;
4463             }
4464             switch (tnc_get_parity()) {
4465               case TNC_PAR_NONE:
4466                 parity = "none";
4467                 break;
4468               case TNC_PAR_ODD:
4469                 parity = "odd";
4470                 break;
4471               case TNC_PAR_EVEN:
4472                 parity = "even";
4473                 break;
4474               case TNC_PAR_MARK:
4475                 parity = "mark";
4476                 break;
4477               case TNC_PAR_SPACE:
4478                 parity = "space";
4479                 break;
4480               default:
4481                 parity = "(unknown)";
4482             }
4483             switch (tnc_get_stopsize()) {
4484               case TNC_SB_1:
4485                 stopsize = "1";
4486                 break;
4487               case TNC_SB_1_5:
4488                 stopsize = "1.5";
4489                 break;
4490               case TNC_SB_2:
4491                 stopsize = "2";
4492                 break;
4493               default:
4494                 stopsize = "(unknown)";
4495             }
4496 	    signature = (char *)tnc_get_signature();
4497             printf("\n  Signature            : %s\n",signature?signature:"");
4498             if (baud <= 0)
4499               printf("  Speed                : (unknown)\n");
4500             else
4501               printf("  Speed                : %d\n", baud);
4502             printf("  Outbound Flow Control: %s\n", oflow);
4503             printf("  Inbound Flow Control : %s\n", iflow);
4504             printf("  Parity               : %s\n", parity);
4505             printf("  Data Size            : %d\n", tnc_get_datasize());
4506             printf("  Stop Bits            : %s\n", stopsize);
4507             printf("  DTR Signal           : %d\n", tnc_get_dtr_state());
4508             printf("  RTS Signal           : %d\n", tnc_get_rts_state());
4509             printf("  Modem State:\n");
4510             modemstate = tnc_get_ms();
4511             if (modemstate & TNC_MS_EDGE_RING)
4512               printf("    Trailing Edge Ring Detector On\n");
4513             else
4514               printf("    Trailing Edge Ring Detector Off\n");
4515             if (modemstate & TNC_MS_CTS_SIG)
4516               printf("    CTS Signal On\n");
4517             else
4518               printf("    CTS Signal Off\n");
4519             if (modemstate & TNC_MS_DSR_SIG)
4520               printf("    DSR Signal On\n");
4521             else
4522               printf("    DSR Signal Off\n");
4523             if (modemstate & TNC_MS_RI_SIG)
4524               printf("    Ring Indicator On\n");
4525             else
4526               printf("    Ring Indicator Off\n");
4527             if (modemstate & TNC_MS_RLSD_SIG)
4528               printf("    RLSD (CD) Signal On\n");
4529             else
4530               printf("    RLSD (CD) Signal Off\n");
4531             printf("\n");
4532         }
4533 #endif /* TN_COMPORT */
4534     } else {
4535 
4536         printf(" %s: %s%s, speed: ",
4537 #ifdef OS2
4538                "Port",
4539 #else
4540                "Line",
4541 #endif /* OS2 */
4542                ttname,
4543 #ifdef CK_TTYFD
4544                (local &&
4545 #ifdef VMS
4546                 vmsttyfd() < 0
4547 #else
4548                 ttyfd == -1
4549 #endif /* VMS */
4550                 ) ?
4551                  " (closed)" :
4552                    (reliable == SET_ON ? " (reliable)" : "")
4553 #else
4554                ""
4555 #endif /* CK_TTYFD */
4556                );
4557         if (
4558 #ifdef CK_TTYFD
4559 #ifdef VMS
4560             vmsttyfd() < 0
4561 #else
4562             ttyfd == -1
4563 #endif /* VMS */
4564             ||
4565 #endif /* CK_TTYFD */
4566             (zz = ttgspd()) < 0) {
4567             printf("unknown");
4568         } else {
4569             if (speed == 8880) printf("75/1200");
4570             else if (speed == 134) printf("134.5");
4571             else printf("%ld",zz);
4572         }
4573     }
4574     if (network
4575 #ifdef IKSD
4576          || inserver
4577 #endif /* IKSD */
4578          )
4579       printf("\n Mode: ");
4580     else
4581       printf(", mode: ");
4582     if (local) printf("local"); else printf("remote");
4583     if (network == 0
4584 #ifdef IKSD
4585          && !inserver
4586 #endif/* IKSD */
4587          ) {
4588 #ifdef CK_TAPI
4589         if (tttapi && !tapipass )
4590           printf(", modem: %s","TAPI");
4591         else
4592 #endif /* CK_TAPI */
4593         printf(", modem: %s",gmdmtyp());
4594     } else {
4595 #ifdef NETCONN
4596        if (nettype == NET_TCPA) printf(", TCP/IP");
4597        if (nettype == NET_TCPB) printf(", TCP/IP");
4598        if (nettype == NET_DEC) {
4599            if (ttnproto == NP_LAT) printf(", DECnet LAT");
4600            else if ( ttnproto == NP_CTERM ) printf(", DECnet CTERM");
4601            else printf(", DECnet");
4602        }
4603        if (nettype == NET_SLAT) printf(", Meridian Technologies' SuperLAT");
4604 #ifdef NETFILE
4605        if (nettype == NET_FILE) printf(", local file");
4606 #endif /* NETFILE */
4607 #ifdef NETCMD
4608        if (nettype == NET_CMD) printf(", pipe");
4609 #endif /* NETCMD */
4610 #ifdef NETPTY
4611        if (nettype == NET_PTY) printf(", pseudoterminal");
4612 #endif /* NETPTY */
4613 #ifdef NETDLL
4614        if (nettype == NET_DLL) printf(", dynamic load library");
4615 #endif /* NETDLL */
4616        if (nettype == NET_PIPE) printf(", Named Pipes");
4617 #ifdef SSHBUILTIN
4618        if (nettype == NET_SSH)
4619          printf(", Secure Shell protocol (SECURE)");
4620 #endif /* SSHBUILTIN */
4621 #ifdef ANYX25
4622        if (shox25(0) < 0) return;
4623 #endif /* ANYX25 */
4624        if (IS_TELNET()) {
4625            printf(", telnet protocol");
4626            if (0
4627 #ifdef CK_ENCRYPTION
4628                || ck_tn_encrypting() && ck_tn_decrypting()
4629 #endif /* CK_ENCRYPTION */
4630 #ifdef CK_SSL
4631                || tls_active_flag || ssl_active_flag
4632 #endif /* CK_SSL */
4633                )
4634              printf(" (SECURE)");
4635        }
4636 #ifdef RLOGCODE
4637        else if (ttnproto == NP_RLOGIN || ttnproto == NP_K4LOGIN ||
4638                 ttnproto == NP_K5LOGIN)
4639          printf(", rlogin protocol");
4640        else if (ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN)
4641          printf(", rlogin protocol (SECURE)");
4642 #endif /* RLOGCODE */
4643 #ifdef CK_KERBEROS
4644 #ifdef KRB5
4645        else if (ttnproto == NP_K5U2U)
4646          printf(", Kerberos 5 User to User protocol (SECURE)");
4647 #endif /* KRB5 */
4648 #endif /* CK_KERBEROS */
4649 #endif /* NETCONN */
4650     }
4651     printf("\n");
4652     if (hwparity && local && !network)
4653       s = parnam((char)hwparity);
4654     else
4655       s = parnam((char)parity);
4656     printf(" Parity: %s%s",hwparity ? "hardware " : "", s);
4657 #ifndef NOLOCAL
4658     if (local && !network) {
4659         int sb;
4660         char c;
4661         c = s[0];
4662         if (islower(c)) c = toupper(c);
4663         sb = stopbits;
4664         if (sb < 1) {
4665             sb = (speed > 0 && speed <= 110L) ? 2 : 1;
4666             printf(", stop-bits: (default)");
4667         } else {
4668             printf(", stop-bits: %d",sb);
4669         }
4670         if (hwparity)
4671           printf(" (8%c%d)",c,sb);
4672         else if (parity)
4673           printf(" (7%c%d)",c,sb);
4674         else
4675           printf(" (8N%d)",sb);
4676         printf("\n D");
4677     } else
4678       printf(", d");
4679 #endif /* NOLOCAL */
4680 
4681     printf("uplex: %s, ", duplex ? "half" : "full");
4682     debug(F101,"shoparp flow","",flow);
4683     printf("flow: %s", floname[flow]);
4684     printf(", handshake: ");
4685     if (turn) printf("%d\n",turnch); else printf("none\n");
4686 #ifdef COMMENT
4687     if (local && !network) {            /* SET CARRIER-WATCH */
4688 #endif /* COMMENT */
4689         if (carrier == CAR_OFF) s = "off";
4690         else if (carrier == CAR_ON) s = "on";
4691         else if (carrier == CAR_AUT) s = "auto";
4692         else s = "unknown";
4693         printf(" Carrier-watch: %s", s);
4694         if (carrier == CAR_ON) {
4695             if (cdtimo) printf(", timeout: %d sec", cdtimo);
4696             else printf(", timeout: none");
4697         }
4698 #ifdef COMMENT
4699     }
4700 #endif /* COMMENT */
4701     printf(", close-on-disconnect: %s\n",showoff(clsondisc));
4702 
4703 #ifdef UNIX                             /* UUCP lockfile, UNIX only */
4704     if (local) {
4705 #ifndef NOUUCP
4706         if (!network && haslock && *flfnam)
4707           printf(" Lockfile: %s",flfnam);
4708 #ifndef USETTYLOCK
4709         if (!network && haslock && lock2[0])
4710           printf("\n Secondary lockfile: %s",lock2);
4711 #endif /* USETTYLOCK */
4712 #else
4713 #ifdef QNX
4714         {
4715             extern int qnxportlock, qnxopencount();
4716             if (local)
4717               printf(" Qnx-port-lock: %s, Open count: %d",
4718                      showoff(qnxportlock),
4719                      qnxopencount()
4720                      );
4721             else
4722               printf(" Qnx-port-lock: %s", showoff(qnxportlock));
4723         }
4724 #endif /* QNX */
4725 #endif /* NOUUCP */
4726         printf("\n");
4727     } else {
4728         char * s;
4729         s = ttglckdir();
4730         if (!s) s = "";
4731         printf(" Lockfile directory: %s\n", *s ? s : "(none)");
4732     }
4733 #endif /* UNIX */
4734 #ifndef MACOSX
4735     if (!local) {
4736         printf(" Typical port device name: %s\n",ttgtpn());
4737     }
4738 #endif	/* MACOSX */
4739     if (local) {
4740         int i;
4741         i = parity ? 7 : 8;
4742         if (i == 8) i = (cmask == 0177) ? 7 : 8;
4743         printf(" Terminal bytesize: %d,",i);
4744         printf(" escape character: %d (^%c)\n",escape,ctl(escape));
4745     }
4746 }
4747 
4748 int
shotcp(n)4749 shotcp(n) int n; {
4750 #ifdef TCPSOCKET
4751     if (nettype == NET_TCPA || nettype == NET_TCPB) {
4752         printf("SET TCP parameters:\n");
4753         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4754         printf(" Reverse DNS lookup: %s\n", showooa(tcp_rdns));
4755         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4756 
4757 #ifdef CK_DNS_SRV
4758         printf(" DNS Service Records lookup: %s\n", showooa(tcp_dns_srv));
4759         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4760 #endif /* CK_DNS_SRV */
4761 
4762 #ifndef NOTCPOPTS
4763 #ifdef SOL_SOCKET
4764 #ifdef SO_KEEPALIVE
4765         printf(" Keepalive: %s\n", showoff(tcp_keepalive));
4766         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4767 #endif /* SO_KEEPALIVE */
4768 
4769 #ifdef SO_LINGER
4770         printf(" Linger: %s", tcp_linger ? "on, " : "off\n" );
4771         if (tcp_linger) {
4772             if (tcp_linger_tmo)
4773               printf("%d x 10 milliseconds\n",tcp_linger_tmo);
4774             else
4775               printf("no timeout\n");
4776         }
4777         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4778 #endif /* SO_LINGER */
4779 
4780 #ifdef SO_DONTROUTE
4781         printf(" DontRoute: %s\n", tcp_dontroute ? "on" : "off" );
4782         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4783 #endif /* SO_DONTROUTE */
4784 
4785 #ifdef TCP_NODELAY
4786         printf(" Nodelay: %s\n", showoff(tcp_nodelay));
4787         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4788 #endif /* TCP_NODELAY */
4789 
4790 #ifdef SO_SNDBUF
4791         if (tcp_sendbuf <= 0)
4792           printf(" Send buffer: (default size)\n");
4793         else
4794           printf(" Send buffer: %d bytes\n", tcp_sendbuf);
4795         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4796 #endif /* SO_SNDBUF */
4797 #ifdef SO_RCVBUF
4798         if (tcp_recvbuf <= 0)
4799           printf(" Receive buffer: (default size)\n");
4800         else
4801           printf(" Receive buffer: %d bytes\n", tcp_recvbuf);
4802         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4803 #endif /* SO_RCVBUF */
4804 #endif /* SOL_SOCKET */
4805 #endif /* NOTCPOPTS */
4806         printf(" address: %s\n",tcp_address ? tcp_address : "(none)");
4807         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4808 #ifndef NOHTTP
4809         printf(" http-proxy: %s\n",tcp_http_proxy ? tcp_http_proxy : "(none)");
4810         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4811 #endif /* NOHTTP */
4812 #ifdef NT
4813 #ifdef CK_SOCKS
4814         printf(" socks-server: %s\n",tcp_socks_svr ? tcp_socks_svr : "(none)");
4815         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4816 #ifdef CK_SOCKS_NS
4817         printf(" socks-name-server: %s\n",
4818                tcp_socks_ns ? tcp_socks_ns : "(none)");
4819         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4820 #endif /* CK_SOCKS_NS */
4821 #endif /* CK_SOCKS */
4822 #endif /* NT */
4823     }
4824 #endif /* TCPSOCKET */
4825     return(n);
4826 }
4827 
4828 #ifdef TNCODE
4829 int
shotopt(n)4830 shotopt(n) int n; {
4831     int opt;
4832 
4833     printf("%-21s %12s %12s %12s %12s\n\n",
4834            "Telnet Option","Me (client)","U (client)",
4835            "Me (server)","U (server)");
4836     n += 2;
4837     if (n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4838 
4839     for ( opt = TELOPT_FIRST; opt <= TELOPT_LAST; opt++) {
4840         switch (opt) {
4841           case TELOPT_AUTHENTICATION:
4842           case TELOPT_ENCRYPTION:
4843           case TELOPT_TTYPE:
4844           case TELOPT_NAWS:
4845           case TELOPT_BINARY:
4846           case TELOPT_NEWENVIRON:
4847           case TELOPT_SNDLOC:
4848           case TELOPT_XDISPLOC:
4849           case TELOPT_SGA:
4850           case TELOPT_ECHO:
4851           case TELOPT_KERMIT:
4852           case TELOPT_START_TLS:
4853           case TELOPT_FORWARD_X:
4854           case TELOPT_COMPORT:
4855             break;
4856           default:
4857             continue;
4858         }
4859         printf("%03d %-17s ",
4860                opt, TELOPT(opt)
4861                );
4862         printf("%12s %12s ",
4863                TELOPT_MODE(TELOPT_DEF_C_ME_MODE(opt)),
4864                TELOPT_MODE(TELOPT_DEF_C_U_MODE(opt))
4865                );
4866         printf("%12s %12s\n",
4867                TELOPT_MODE(TELOPT_DEF_S_ME_MODE(opt)),
4868                TELOPT_MODE(TELOPT_DEF_S_U_MODE(opt))
4869                );
4870 
4871         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4872         if (sstelnet)
4873           printf("%21s %12s %12s %12s %12s\n",
4874                  "",
4875                  "",
4876                  "",
4877                  (TELOPT_ME(opt)?"WILL":"WONT"),
4878                  (TELOPT_U(opt)?"DO":"DONT")
4879                  );
4880         else
4881           printf("%21s %12s %12s %12s %12s\n",
4882                  "",
4883                  (TELOPT_ME(opt)?"WILL":"WONT"),
4884                  (TELOPT_U(opt)?"DO":"DONT"),
4885                  "",
4886                  ""
4887                  );
4888         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4889     }
4890     return(n);
4891 }
4892 
4893 int
shotel(n)4894 shotel(n) int n; {
4895     extern int tn_duplex;
4896 #ifdef CK_ENVIRONMENT
4897     extern int tn_env_flg;
4898     extern char tn_env_acct[];
4899     extern char tn_env_job[];
4900     extern char tn_env_prnt[];
4901     extern char tn_env_sys[];
4902     extern char * tn_env_uservar[8][2];
4903     int x;
4904 #endif /* CK_ENVIRONMENT */
4905 #ifdef CK_SNDLOC
4906     extern char * tn_loc;
4907 #endif /* CK_SNDLOC */
4908     printf("SET TELNET parameters:\n echo: %s\n NVT newline-mode: ",
4909            tn_duplex ? "local" : "remote");
4910     switch (tn_nlm) {
4911       case TNL_CRNUL: printf("%s\n","off (cr-nul)"); break;
4912       case TNL_CRLF:  printf("%s\n","on (cr-lf)"); break;
4913       case TNL_CR:    printf("%s\n","raw (cr)"); break;
4914       case TNL_LF:    printf("%s\n","(lf)"); break;
4915     }
4916     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4917 #ifdef CK_AUTHENTICATION
4918     {
4919         int type = ck_tn_authenticated();
4920         printf(" authentication: ");
4921         switch (sstelnet ?
4922                 TELOPT_U_MODE(TELOPT_AUTHENTICATION) :
4923                  TELOPT_ME_MODE(TELOPT_AUTHENTICATION)
4924                 ) {
4925           case TN_NG_AC: printf( "accepted " ); break;
4926           case TN_NG_RF: printf( "refused  " ); break;
4927           case TN_NG_RQ: printf( "requested"); break;
4928           case TN_NG_MU: printf( "required "); break;
4929         }
4930 
4931 #ifdef CK_SSL
4932         if ((ssl_active_flag || tls_active_flag) &&
4933              ck_tn_auth_valid() == AUTH_VALID &&
4934              (!TELOPT_U(TELOPT_AUTHENTICATION) ||
4935                type == AUTHTYPE_NULL ||
4936                type == AUTHTYPE_AUTO))
4937             printf("   in use: X.509 certificate\n");
4938         else
4939 #endif /* CK_SSL */
4940           printf("   in use: %s\n",AUTHTYPE_NAME(type));
4941         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4942         if (forward_flag)
4943           printf("  credentials forwarding requested %s\n",
4944                  forwarded_tickets ? "and completed" :
4945                  "but not completed");
4946         else
4947           printf("  credentials forwarding disabled\n");
4948         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4949     }
4950 #endif /* CK_AUTHENTICATION */
4951 #ifdef CK_ENCRYPTION
4952     {
4953         int i,x;
4954         int e_type = ck_tn_encrypting();
4955         int d_type = ck_tn_decrypting();
4956         char * e_str = NULL, * d_str = NULL;
4957         static struct keytab * tnetbl = NULL;
4958         static int ntnetbl = 0;
4959 
4960         x = ck_get_crypt_table(&tnetbl,&ntnetbl);
4961 
4962         for (i = 0; i < ntnetbl; i++) {
4963             if (e_type == tnetbl[i].kwval)
4964               e_str = tnetbl[i].kwd;
4965             if (d_type == tnetbl[i].kwval)
4966               d_str = tnetbl[i].kwd;
4967         }
4968         printf(" encryption: ");
4969         switch (TELOPT_ME_MODE(TELOPT_ENCRYPTION)) {
4970           /* This should be changed to report both ME and U modes */
4971           case TN_NG_AC: printf( "accepted " ); break;
4972           case TN_NG_RF: printf( "refused  " ); break;
4973           case TN_NG_RQ: printf( "requested"); break;
4974           case TN_NG_MU: printf( "required "); break;
4975         }
4976         printf("       in use: ");
4977         switch ((e_type ? 1 : 0) | (d_type ? 2 : 0)) {
4978           case 0:
4979             printf("plain text in both directions");
4980             break;
4981           case 1:
4982             printf("%s output, plain text input",e_str);
4983             break;
4984           case 2:
4985             printf("plain text output, %s input",d_str);
4986             break;
4987           case 3:
4988             printf("%s output, %s input",e_str,d_str);
4989             break;
4990         }
4991         printf("\n");
4992         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4993     }
4994 #endif /* CK_ENCRYPTION */
4995 #ifdef IKS_OPTION
4996     printf(" kermit: ");
4997     switch (TELOPT_U_MODE(TELOPT_KERMIT)) {
4998       case TN_NG_AC: printf( "u, accepted;  " ); break;
4999       case TN_NG_RF: printf( "u, refused;   " ); break;
5000       case TN_NG_RQ: printf( "u, requested; "); break;
5001       case TN_NG_MU: printf( "u, required;  "); break;
5002     }
5003     switch (TELOPT_ME_MODE(TELOPT_KERMIT)) {
5004       case TN_NG_AC: printf( "me, accepted;  " ); break;
5005       case TN_NG_RF: printf( "me, refused;   " ); break;
5006       case TN_NG_RQ: printf( "me, requested; "); break;
5007       case TN_NG_MU: printf( "me, required;  "); break;
5008     }
5009     if (TELOPT_U(TELOPT_KERMIT))
5010       printf(" u, %s",
5011              TELOPT_SB(TELOPT_KERMIT).kermit.u_start ?
5012              "started" :
5013              "stopped"
5014              );
5015     else
5016       printf(" u, n/a");
5017     if (TELOPT_ME(TELOPT_KERMIT))
5018       printf(" me, %s;",
5019              TELOPT_SB(TELOPT_KERMIT).kermit.me_start ?
5020              "started" :
5021              "stopped"
5022              );
5023     else
5024       printf(" me, n/a;");
5025     printf("\n");
5026     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5027 #endif /* IKS_OPTION */
5028     printf(" BINARY newline-mode: ");
5029     switch (tn_b_nlm) {
5030       case TNL_CRNUL: printf("%s\n","off (cr-nul)"); break;
5031       case TNL_CRLF:  printf("%s\n","on (cr-lf)"); break;
5032       case TNL_CR:    printf("%s\n","raw (cr)"); break;
5033       case TNL_LF:    printf("%s\n","(lf)"); break;
5034     }
5035     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5036     printf(" binary-mode: ");
5037     switch (TELOPT_U_MODE(TELOPT_BINARY)) {
5038       case TN_NG_AC: printf( "u, accepted;  " ); break;
5039       case TN_NG_RF: printf( "u, refused;   " ); break;
5040       case TN_NG_RQ: printf( "u, requested; "); break;
5041       case TN_NG_MU: printf( "u, required;  "); break;
5042     }
5043     switch (TELOPT_ME_MODE(TELOPT_BINARY)) {
5044       case TN_NG_AC: printf( "me, accepted; " ); break ;
5045       case TN_NG_RF: printf( "me, refused; " ); break;
5046       case TN_NG_RQ: printf( "me, requested; "); break;
5047       case TN_NG_MU: printf( "me, required;  "); break;
5048     }
5049     printf("u, %s; me, %s\n",
5050            TELOPT_U(TELOPT_BINARY) ? "BINARY" : "NVT",
5051            TELOPT_ME(TELOPT_BINARY) ? "BINARY" : "NVT"
5052            );
5053     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5054     printf(" binary-transfer-mode: %s\n",showoff(tn_b_xfer));
5055     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5056     printf(" bug binary-me-means-u-too: %s\n",showoff(tn_b_meu));
5057     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5058     printf(" bug binary-u-means-me-too: %s\n",showoff(tn_b_ume));
5059     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5060     printf(" bug sb-implies-will-do: %s\n",showoff(tn_sb_bug));
5061     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5062     printf(" bug auth-krb5-des: %s\n",showoff(tn_auth_krb5_des_bug));
5063     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5064     printf(" terminal-type: ");
5065     if (tn_term) {
5066         printf("%s\n",tn_term);
5067     } else {
5068         char *p;
5069 #ifdef OS2
5070         p = (tt_type >= 0 && tt_type <= max_tt) ?
5071           tt_info[tt_type].x_name :
5072             "UNKNOWN";
5073 #else
5074         p = getenv("TERM");
5075 #endif /* OS2 */
5076         if (p)
5077           printf("none (%s will be used)\n",p);
5078         else printf("none\n");
5079     }
5080     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5081 #ifdef CK_ENVIRONMENT
5082     printf(" environment: %s\n", showoff(tn_env_flg));
5083     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5084     printf("   ACCOUNT: %s\n",tn_env_acct);
5085     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5086     printf("   DISPLAY: %s\n",(char *)tn_get_display() ?
5087             (char *)tn_get_display() : "");
5088     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5089     printf("   JOB    : %s\n",tn_env_job);
5090     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5091     printf("   PRINTER: %s\n",tn_env_prnt);
5092     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5093 #ifndef NOSPL
5094     printf("   USER   : %s\n",uidbuf);
5095     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5096 #endif /* NOSPL */
5097     printf("   SYSTEM : %s\n",tn_env_sys);
5098     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5099     for (x = 0; x < 8; x++) {
5100         if (tn_env_uservar[x][0] && tn_env_uservar[x][1]) {
5101             printf("   %-7s: %s\n",tn_env_uservar[x][0],
5102                    tn_env_uservar[x][1]);
5103             if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5104         }
5105     }
5106 #endif /* CK_ENVIRONMENT */
5107 #ifdef CK_SNDLOC
5108     printf("  LOCATION: %s\n", tn_loc ? tn_loc : "");
5109     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5110 #endif /* CK_SNDLOC */
5111 #ifdef CK_FORWARD_X
5112     printf(" .Xauthority-file: %s\n", (char *)XauFileName() ?
5113             (char *)XauFileName() : "(none)");
5114     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5115 #endif /* CK_FORWARD_X */
5116     return(n);
5117 }
5118 #endif /* TNCODE */
5119 
5120 #ifdef CK_NETBIOS
5121 static int
shonb(n)5122 shonb(n) int n; {
5123     printf("NETBIOS parameters:\n");
5124     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5125     printf(" API       : %s\n",
5126            NetbeuiAPI ?
5127            "NETAPI.DLL - IBM Extended Services or Novell Netware Requester"
5128            : "ACSNETB.DLL - IBM Network Transport Services/2" ) ;
5129     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5130     printf(" Local Name: [%s]\n", NetBiosName);
5131     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5132     printf(" Adapter   : %d\n", NetBiosAdapter);
5133     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5134     if (NetBiosLSN > 0xFF) {
5135         printf(" Session   : %d\n", NetBiosLSN);
5136     } else {
5137         printf(" Session   : none active\n");
5138     }
5139     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5140     return(n);
5141 }
5142 #endif /* CK_NETBIOS */
5143 
5144 #ifndef NONET
5145 int
shonet()5146 shonet() {
5147 
5148 #ifndef NETCONN
5149     printf("\nNo networks are supported in this version of C-Kermit\n");
5150 
5151 #else
5152 #ifdef NOLOCAL
5153     printf("\nNo networks are supported in this version of C-Kermit\n");
5154 
5155 #else /* rest of this routine */
5156 
5157     int i, n = 4;
5158 
5159 #ifndef NODIAL
5160     if (nnetdir <= 1) {
5161         printf("\nNetwork directory: %s\n",netdir[0] ? netdir[0] : "(none)");
5162         n++;
5163     } else {
5164         int i;
5165         printf("\nNetwork directories:\n");
5166         for (i = 0; i < nnetdir; i++) {
5167             printf("%2d. %s\n",i,netdir[i]);
5168             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5169         }
5170     }
5171 #endif /* NODIAL */
5172 
5173 #ifdef SSHCMD
5174     {
5175         extern char * sshcmd;
5176         printf("SSH COMMAND: %s\n",sshcmd ? sshcmd : "ssh -e none");
5177         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5178     }
5179 #endif /* SSHCMD */
5180 
5181 #ifdef OS2
5182     printf("\nNetwork availability:\n");
5183 #else
5184     printf("\nSupported networks:\n");
5185 #endif /* OS2 */
5186     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5187 
5188 #ifdef VMS
5189 
5190 #ifdef TCPWARE
5191     printf(" Process Software Corporation TCPware for OpenVMS");
5192 #else
5193 #ifdef MULTINET
5194     printf(" TGV MultiNet TCP/IP");
5195 #else
5196 #ifdef WINTCP
5197     printf(" WOLLONGONG WIN/TCP");
5198 #else
5199 #ifdef DEC_TCPIP
5200     {
5201         static $DESCRIPTOR(tcp_desc,"_TCP0:");
5202         int status;
5203         long devclass;
5204         static int itmcod = DVI$_DEVCLASS;
5205 
5206 #ifdef COMMENT
5207         status = LIB$GETDVI(&itmcod, 0, &tcp_desc, &devclass);
5208 #else
5209         /* Martin Zinser 9/96 */
5210         status = lib$getdvi(&itmcod, 0, &tcp_desc, &devclass);
5211 #endif /* COMMENT */
5212         if ((status & 1) && (devclass == DC$_SCOM))
5213           printf(" Process Software Corporation TCPware for OpenVMS");
5214         else
5215 #ifdef UCX50
5216           printf(" DEC TCP/IP Services for (Open)VMS 5.0");
5217 #else
5218           printf(" DEC TCP/IP Services for (Open)VMS");
5219 #endif /* UCX50 */
5220     }
5221 #else
5222 #ifdef CMU_TCPIP
5223     printf(" CMU-OpenVMS/IP");
5224 #else
5225     printf(" None");
5226 #endif /* CMU_TCPIP */
5227 #endif /* DEC_TCPIP */
5228 #endif /* WINTCP */
5229 #endif /* MULTINET */
5230 #endif /* TCPWARE */
5231     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5232 #ifdef TNCODE
5233     printf(", TELNET protocol\n\n");
5234     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5235     n = shotel(n);
5236     if (n < 0) return(0);
5237     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5238 #endif /* TNCODE */
5239     printf("\n");
5240     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5241     printf("\n");
5242     n = shotcp(++n);
5243     if (n < 0) return(0);
5244 #else /* Not VMS */
5245 
5246 #ifdef SUNX25
5247     printf(" SunLink X.25\n");
5248     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5249 #endif /* SUNX25 */
5250 
5251 #ifdef STRATUSX25
5252     printf(" Stratus VOS X.25\n");
5253     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5254 #endif /* STRATUSX25 */
5255 
5256 #ifdef IBMX25
5257     printf(" IBM AIX X.25\n");
5258     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5259 #endif /* IBMX25 */
5260 
5261 #ifdef HPX25
5262     printf(" HP-UX X.25\n");
5263     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5264 #endif /* HPX25 */
5265 
5266 #ifdef SSHBUILTIN
5267     if (ck_ssleay_is_installed())
5268         printf(" SSH V1 and V2 protocols\n");
5269     else
5270         printf(" SSH V1 and V2 protocols - not available\n");
5271 #endif /* SSHBUILTIN */
5272 
5273 #ifdef DECNET
5274 #ifdef OS2
5275 #ifdef NT
5276     if (dnet_avail)
5277       printf(" DECnet, LAT and CTERM protocols\n");
5278     else
5279       printf(" DECnet, LAT and CTERM protocols - not available\n");
5280     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5281 #else /* NT */
5282     if (dnet_avail)
5283       printf(" DECnet, LAT protocol\n");
5284     else
5285       printf(" DECnet, LAT protocol - not available\n");
5286     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5287 #endif /* NT */
5288 #else
5289     printf(" DECnet\n");
5290     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5291 #endif /* OS2 */
5292 #endif /* DECNET */
5293 
5294 #ifdef NPIPE
5295     printf(" Named Pipes\n");
5296     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5297 #endif /* NPIPE */
5298 
5299 #ifdef CK_NETBIOS
5300     if (netbiosAvail)
5301       printf(" NETBIOS\n");
5302     else
5303       printf(" NETBIOS - not available\n");
5304     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5305 #endif /* CK_NETBIOS */
5306 
5307 #ifdef SUPERLAT
5308     if (slat_avail)
5309       printf(" SuperLAT\n");
5310     else
5311       printf(" SuperLAT - not available\n") ;
5312     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5313 #endif /* SUPERLAT */
5314 
5315 #ifdef TCPSOCKET
5316     if (
5317 #ifdef OS2
5318         tcp_avail
5319 #else
5320         1
5321 #endif /* OS2 */
5322         ) {
5323         char ipaddr[16];
5324 
5325         if (getlocalipaddrs(ipaddr,16,0) < 0) {
5326 #ifdef OS2ONLY
5327             printf(" TCP/IP via %s\n", tcpname);
5328 #else
5329             printf(" TCP/IP\n");
5330 #endif /* OS2ONLY */
5331             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5332         } else {
5333             int i = 1;
5334 #ifdef OS2ONLY
5335           printf(" TCP/IP [%16s] via %s\n", ipaddr, tcpname);
5336 #else
5337           printf(" TCP/IP [%16s]\n",ipaddr);
5338 #endif /* OS2ONLY */
5339             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5340 
5341             while (getlocalipaddrs(ipaddr,16,i++) >= 0) {
5342                 printf("        [%16s]\n",ipaddr);
5343                 if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5344             }
5345         }
5346         if (nettype == NET_TCPB) {
5347             printf("\n");
5348             n = shotcp(++n);
5349             if (n < 0) return(0);
5350 #ifdef TNCODE
5351             printf("\n");
5352             n = shotel(++n);
5353             if (n < 0) return(0);
5354 #endif /* TNCODE */
5355             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5356         }
5357 #ifdef OS2
5358     } else {
5359         printf(" TCP/IP - not available%s\n",tcpname[0] ? tcpname : "" );
5360         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5361 #endif /* OS2 */
5362     }
5363 #endif /* TCPSOCKET */
5364 
5365 #ifdef CK_NETBIOS
5366     if (netbiosAvail && nettype == NET_BIOS) {
5367        printf("\n") ;
5368        if ((n = shonb(++n)) < 0) return(0);
5369        if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5370     }
5371 #endif /* CK_NETBIOS */
5372 
5373 #endif /* VMS */
5374 
5375     printf("\nActive network connection:\n");
5376     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5377 
5378     if (network) {
5379         printf(" Host: %s",ttname);
5380         if ((nettype == NET_TCPA || nettype == NET_TCPB) && *ipaddr)
5381           printf(" [%s]",ipaddr);
5382     } else
5383       printf(" Host: none");
5384     printf(", via: ");
5385     if (nettype == NET_TCPA || nettype == NET_TCPB)
5386       printf("tcp/ip\n");
5387     else if (nettype == NET_SX25)
5388       printf("SunLink X.25\n");
5389     else if (nettype == NET_VX25)
5390       printf("Stratus VOS X.25\n");
5391     else if (nettype == NET_IX25)
5392       printf("IBM AIX X.25\n");
5393     else if (nettype == NET_HX25)
5394       printf("HP-UX X.25\n");
5395     else if (nettype == NET_DEC) {
5396         if ( ttnproto == NP_LAT )
5397           printf("DECnet LAT\n");
5398         else if ( ttnproto == NP_CTERM )
5399           printf("DECnet CTERM\n");
5400         else
5401           printf("DECnet\n");
5402     } else if (nettype == NET_PIPE)
5403       printf("Named Pipes\n");
5404     else if (nettype == NET_BIOS)
5405       printf("NetBIOS\n");
5406     else if (nettype == NET_SLAT)
5407       printf("SuperLAT\n");
5408 
5409 #ifdef NETFILE
5410     else if ( nettype == NET_FILE )
5411       printf("local file\n");
5412 #endif /* NETFILE */
5413 #ifdef NETCMD
5414     else if ( nettype == NET_CMD )
5415       printf("pipe\n");
5416 #endif /* NETCMD */
5417 #ifdef NETPTY
5418     else if ( nettype == NET_PTY )
5419         printf("pseudoterminal\n");
5420 #endif /* NETPTY */
5421 #ifdef NETDLL
5422     else if ( nettype == NET_DLL )
5423       printf("dynamic link library\n");
5424 #endif /* NETDLL */
5425     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5426 
5427 #ifdef ANYX25
5428     if ((nettype == NET_SX25) ||
5429         (nettype == NET_VX25) ||
5430         (nettype == NET_IX25))
5431       if ((n = shox25(n)) < 0) return(0);
5432 #endif /* ANYX25 */
5433 
5434 #ifdef SSHBUILTIN
5435     if (nettype == NET_SSH) {
5436         printf("Secure Shell protocol\n");
5437         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5438     }
5439 #endif /* SSHBUILTIN */
5440 
5441     if (nettype == NET_TCPA || nettype == NET_TCPB) {
5442 #ifdef RLOGCODE
5443         if (ttnproto == NP_RLOGIN) {
5444             printf(" LOGIN (rlogin) protocol\n");
5445             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5446         }
5447 #ifdef CK_KERBEROS
5448         else if (ttnproto == NP_K4LOGIN) {
5449             printf(" Kerberos 4 LOGIN (klogin) protocol\n");
5450             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5451         }
5452         else if (ttnproto == NP_EK4LOGIN) {
5453             printf(" Encrypted Kerberos 4 LOGIN (eklogin) protocol\n");
5454             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5455         }
5456         else if (ttnproto == NP_K5LOGIN) {
5457             printf(" Kerberos 5 LOGIN (klogin) protocol\n");
5458             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5459         }
5460         else if (ttnproto == NP_EK5LOGIN) {
5461             printf(" Encrypted Kerberos 5 LOGIN (eklogin) protocol\n");
5462             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5463         }
5464 #endif /* CK_KERBEROS */
5465 #endif /* RLOGCODE */
5466 #ifdef CK_KERBEROS
5467         if (ttnproto == NP_K5U2U) {
5468             printf(" Kerberos 5 User to User protocol\n");
5469             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5470         }
5471 #endif /* CK_KERBEROS */
5472 
5473 #ifdef TNCODE
5474         if (IS_TELNET()) {
5475             printf(" TELNET protocol\n");
5476             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5477             printf(" Echoing is currently %s\n",duplex ? "local" : "remote");
5478             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5479         }
5480 #endif /* TNCODE */
5481         if (ttnproto == NP_TCPRAW) {
5482             printf(" Raw TCP socket\n");
5483             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5484         }
5485     }
5486     printf("\n");
5487 #endif /* NOLOCAL */
5488 #endif /* NETCONN */
5489     return(0);
5490 }
5491 #endif /* NONET */
5492 
5493 #ifndef NODIAL
5494 VOID
shodial()5495 shodial() {
5496     if (mdmtyp >= 0 || local != 0) doshodial();
5497 }
5498 
5499 VOID
shods(s)5500 shods(s) char *s; {                     /* Show a dial-related string */
5501     char c;
5502     if (s == NULL || !(*s)) {           /* Empty? */
5503         printf("(none)\n");
5504     } else {                            /* Not empty. */
5505         while ((c = *s++))              /* Can contain controls */
5506           if (c == '\\')                /* a backslash */
5507             printf("\\\\");
5508           else if (c > 31 && c < 127) {
5509               putchar(c);
5510           } else
5511             printf("\\{%d}",c);
5512         printf("\n");
5513     }
5514 }
5515 
5516 int
doshodial()5517 doshodial() {
5518 
5519     int i, n = 2;
5520 
5521     printf(" Dial status:  %d", dialsta);
5522 
5523 #ifdef BIGBUFOK
5524     if (dialsta > 90)
5525       printf(" = Unknown error");
5526     else if (dialsta < 0)
5527       printf(" = (none)");
5528     else if (dialsta < 35 && dialmsg[dialsta])
5529       printf(" = %s", dialmsg[dialsta]);
5530 #endif /* BIGBUFOK */
5531     n++;
5532     if (ndialdir <= 1) {
5533         printf("\n Dial directory: %s\n",dialdir[0] ? dialdir[0] : "(none)");
5534     } else {
5535         int i;
5536         printf("\n Dial directories:\n");
5537         for (i = 0; i < ndialdir; i++)
5538           printf("%2d. %s\n",i+1,dialdir[i]);
5539         n += ndialdir;
5540     }
5541     printf(" Dial method:  ");
5542     if      (dialmauto)         printf("auto   ");
5543     else if (dialmth == XYDM_D) printf("default");
5544     else if (dialmth == XYDM_P) printf("pulse  ");
5545     else if (dialmth == XYDM_T) printf("tone   ");
5546     printf("         Dial sort: %s\n",dialsrt ? "on" : "off");
5547     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5548     printf(" Dial hangup:  %s             Dial display: %s\n",
5549            dialhng ? "on " : "off", dialdpy ? "on" : "off");
5550     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5551     if (dialrtr > 0) {
5552         printf(" Dial retries: %-12d    Dial interval: %d\n",
5553                dialrtr, dialint);
5554     } else {
5555         printf(" Dial retries: (auto)          Dial interval: %d\n", dialint);
5556     }
5557     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5558     printf(" Dial timeout: ");
5559 #ifdef CK_TAPI
5560     if (tttapi && !tapipass)
5561         printf("(tapi)");
5562     else
5563 #endif /* CK_TAPI */
5564     if (dialtmo > 0)
5565       printf("%4d sec", dialtmo);
5566     else
5567       printf("0 (auto)");
5568     printf("        Redial number: %s\n",dialnum ? dialnum : "(none)");
5569     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5570     printf(" Dial confirmation: %s        Dial convert-directory: %s\n",
5571            dialcnf ? "on " : "off",
5572            dialcvt ? ((dialcvt == 1) ? "on" : "ask") : "off");
5573     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5574     printf(" Dial ignore-dialtone: %s", dialidt ? "on " : "off");
5575     printf("     Dial pacing: %d\n",dialpace);
5576     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5577     printf(
5578 " Dial prefix:                  %s\n", dialnpr ? dialnpr : "(none)");
5579     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5580     printf(
5581 " Dial suffix:                  %s\n", dialsfx ? dialsfx : "(none)");
5582     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5583     printf(
5584 " Dial country-code:            %-12s", diallcc ? diallcc : "(none)");
5585     printf("Dial connect:  %s", dialcon ? ((dialcon == 1) ? "on" : "auto")
5586            : "off");
5587     if (dialcon != CAR_OFF)
5588       printf(" %s", dialcq ? "quiet" : "verbose");
5589     printf(
5590 "\n Dial area-code:               %-12s", diallac ? diallac : "(none)");
5591     n++;
5592     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5593     printf("Dial restrict: ");
5594     if (dialrstr == 5) printf("international\n");
5595     else if (dialrstr == 4) printf("long-distance\n");
5596     else if (dialrstr == 2) printf("local\n");
5597     else if (dialrstr == 6) printf("none\n");
5598     else printf("?\n");
5599     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5600     printf(" Dial lc-area-codes:           ");
5601     if (nlocalac == 0)
5602       printf("(none)");
5603     else
5604       for (i = 0; i < nlocalac; i++)
5605         printf("%s ", diallcac[i]);
5606     printf(
5607 "\n Dial lc-prefix:               %s\n", diallcp ? diallcp : "(none)");
5608     n++;
5609     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5610     printf(
5611 " Dial lc-suffix:               %s\n", diallcs ? diallcs : "(none)");
5612     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5613     printf(
5614 " Dial ld-prefix:               %s\n", dialldp ? dialldp : "(none)");
5615     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5616     printf(
5617 " Dial ld-suffix:               %s\n", diallds ? diallds : "(none)");
5618     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5619     printf(
5620 " Dial force-long-distance      %s\n", showoff(dialfld));
5621     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5622     printf(
5623 " Dial intl-prefix:             %s\n", dialixp ? dialixp : "(none)");
5624     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5625     printf(
5626 " Dial intl-suffix:             %s\n", dialixs ? dialixs : "(none)");
5627     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5628     printf(
5629 " Dial toll-free-area-code:     ");
5630     if (ntollfree == 0)
5631       printf("(none)");
5632     else
5633       for (i = 0; i < ntollfree; i++)
5634         printf("%s ", dialtfc[i]);
5635     printf("\n");
5636     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5637 
5638     printf(
5639 " Dial pulse-countries:         ");
5640     if (ndialpucc == 0)
5641       printf("(none)");
5642     else
5643       for (i = 0; i < ndialpucc; i++)
5644         printf("%s ", dialpucc[i]);
5645     printf("\n");
5646     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5647 
5648     printf(
5649 " Dial tone-countries:          ");
5650     if (ndialtocc == 0)
5651       printf("(none)");
5652     else
5653       for (i = 0; i < ndialtocc; i++)
5654         printf("%s ", dialtocc[i]);
5655     printf("\n");
5656     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5657 
5658     printf(
5659 	" Dial toll-free-prefix:        %s\n",
5660 	dialtfp ? dialtfp :
5661 	(dialldp ? dialldp : "(none)")
5662 	);
5663     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5664     printf(" Dial pbx-exchange:            ");
5665     if (ndialpxx == 0)
5666       printf("(none)");
5667     else
5668       for (i = 0; i < ndialpxx; i++)
5669         printf("%s ", dialpxx[i]);
5670     printf("\n");
5671 
5672     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5673     printf(
5674 " Dial pbx-inside-prefix:       %s\n", dialpxi ? dialpxi : "(none)");
5675     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5676     printf(
5677 " Dial pbx-outside-prefix:      %s\n", dialpxo ? dialpxo : "(none)");
5678     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5679     printf(
5680 " Dial macro:                   %s\n", dialmac ? dialmac : "(none)");
5681     return(0);
5682 }
5683 #endif /* NODIAL */
5684 #endif /* NOLOCAL */
5685 
5686 /*  Show File Parameters */
5687 
5688 static char *
pathval(x)5689 pathval(x) int x; {
5690     switch (x) {
5691       case PATH_OFF:  return("off");
5692       case PATH_ABS:  return("absolute");
5693       case PATH_REL:  return("relative");
5694       case PATH_AUTO: return("auto");
5695       default: return("unknown");
5696     }
5697 }
5698 
5699 VOID
shofil()5700 shofil() {
5701     char *s; int i = 0, n = 1;
5702     extern char * ifdnam[];
5703     extern int wildena;
5704 #ifdef UNIX
5705     extern int wildxpand;
5706 #endif /* UNIX */
5707     extern char * snd_move, * snd_rename, * rcv_move, * rcv_rename;
5708 #ifdef PATTERNS
5709     extern int patterns;
5710 #endif /* PATTERNS */
5711     extern char * rfspec, * sfspec;
5712 #ifdef UNIX
5713     extern int zobufsize, zofbuffer, zofblock;
5714 #endif /* UNIX */
5715 #ifdef CK_CTRLZ
5716     extern int eofmethod;
5717 #endif /* CK_CTRLZ */
5718 
5719     printf("\n");
5720 
5721 #ifdef VMS
5722     printf(" File record-Length:      %5d\n",frecl);
5723     n++;
5724 #endif /* VMS */
5725 
5726 #ifndef NOXFER
5727     printf(" Transfer mode:           %s\n",
5728            xfermode == XMODE_A ?
5729            "automatic" :
5730            "manual"
5731            );
5732     n++;
5733 #ifdef PATTERNS
5734     printf(" File patterns:           %s", showooa(patterns));
5735     if (xfermode == XMODE_M && patterns)
5736       printf(" (but disabled by TRANSFER-MODE MANUAL)");
5737     else if (patterns)
5738       printf(" (SHOW PATTERNS for list)");
5739     printf("\n");
5740     n++;
5741 #endif /* PATTERNS */
5742     if (filepeek)
5743       printf(" File scan:               on %d\n", nscanfile);
5744     else
5745       printf(" File scan:               off\n");
5746     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5747     if (xfermode == XMODE_A)
5748       printf(" Default file type:       %s\n",shoxm());
5749     else
5750       printf(" File type:               %s\n",shoxm());
5751     n++;
5752     if (fncnv == XYFN_L)
5753       s = "literal";
5754     else if (fncnv == XYFN_C)
5755       s = "converted";
5756     else
5757       s = "(unknown)";
5758     printf(" File names:              %s\n",s);
5759     n++;
5760     printf(" Send pathnames:          %s\n", pathval(fnspath));
5761     n++;
5762     printf(" Receive pathnames:       %s\n", pathval(fnrpath));
5763     n++;
5764 #ifdef UNIXOROSK
5765     printf(" Match dot files:         %s\n", matchdot ? "yes" : "no");
5766     n++;
5767 #ifdef UNIX
5768     printf(" Wildcard-expansion:      %s (%s)\n", showoff(wildena),
5769 	   wildxpand ? "shell" : "kermit");
5770     n++;
5771 #endif /* UNIX */
5772 #endif /* UNIXOROSK */
5773     printf(" File collision:          ");
5774     for (i = 0; i < ncolx; i++)
5775       if (colxtab[i].kwval == fncact) break;
5776     printf("%s\n", (i == ncolx) ? "unknown" : colxtab[i].kwd);
5777     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5778     printf(" File destination:        %s\n",
5779            (dest == DEST_D) ? "disk" :
5780            ((dest == DEST_S) ? "screen" :
5781             ((dest == DEST_N) ? "nowhere" :
5782             "printer"))
5783            );
5784     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5785     s = (keep >= 0 && keep <= 2) ? ifdnam[keep] : "keep";
5786     printf(" File incomplete:         %s\n",s);
5787     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5788     printf(" File bytesize:           %d\n",(fmask == 0177) ? 7 : 8);
5789     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5790 #ifndef NOCSETS
5791     printf(" File character-set:      %s\n",fcsinfo[fcharset].keyword);
5792     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5793     printf(" File default 7-bit:      %s\n",fcsinfo[dcset7].keyword);
5794     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5795     printf(" File default 8-bit:      %s\n",fcsinfo[dcset8].keyword);
5796     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5797 #ifdef UNICODE
5798     printf(" File UCS bom:            %s\n",showoff(ucsbom));
5799     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5800     printf(" File UCS byte-order:     %s-endian\n",
5801            ucsorder ? "little" : "big");
5802     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5803     printf(" Computer byteorder:      %s-endian\n",
5804            byteorder ? "little" : "big");
5805     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5806 #endif /* UNICODE */
5807 #endif /* NOCSETS */
5808 
5809     printf(" File end-of-line:        ");
5810     i = feol;
5811     switch (feol) {
5812       case XYFA_C: printf("%s\n","cr"); break;
5813       case XYFA_L: printf("%s\n","lf"); break;
5814       case XYFA_2: printf("%s\n","crlf"); break;
5815       default: printf("%d\n",i);
5816     }
5817     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5818 #endif /* NOXFER */
5819 
5820 #ifdef CK_CTRLZ
5821     printf(" File eof:                %s\n", eofmethod ? "ctrl-z" : "length");
5822     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5823 #endif /* CK_CTRLZ */
5824 #ifndef NOXFER
5825 #ifdef CK_TMPDIR
5826     printf(" File download-directory: %s\n", dldir ? dldir : "(none)");
5827     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5828 #ifdef COMMENT
5829     i = 256;
5830     s = line;
5831     zzstring("\\v(tmpdir)",&s,&i);
5832     printf(" Temporary directory:     %s\n", line);
5833     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5834 #endif /* COMMENT */
5835 #endif /* CK_TMPDIR */
5836 #ifdef VMS
5837     {
5838         extern int vmssversions, vmsrversions;
5839         printf(" Send version-numbers:    %s\n",showoff(vmssversions));
5840         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5841         printf(" Receive version-numbers: %s\n",showoff(vmsrversions));
5842         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5843     }
5844 #endif /* VMS */
5845     printf(" Send move-to:            %s\n",
5846            snd_move ? snd_move : "(none)");
5847     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5848     printf(" Send rename-to:          %s\n",
5849            snd_rename ? snd_rename : "(none)");
5850     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5851     printf(" Receive move-to:         %s\n",
5852            rcv_move ? rcv_move : "(none)");
5853     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5854     printf(" Receive rename-to:       %s\n",
5855            rcv_rename ? rcv_rename : "(none)");
5856     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5857 #endif /* NOXFER */
5858 #ifdef KERMRC
5859     printf(" Initialization file:     %s\n", noinit ? "(none)" :
5860 #ifdef CK_SYSINI
5861            CK_SYSINI
5862 #else
5863            kermrc
5864 #endif /* CK_SYSINI */
5865            );
5866 #endif /* KERMRC */
5867     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5868 
5869     if (k_info_dir) {
5870         printf(" Kermit doc files:        %s\n", k_info_dir);
5871         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5872     }
5873 
5874 #ifdef CKROOT
5875     s = zgetroot();
5876     printf(" Root set:                %s\n", s ? s : "(none)");
5877 #endif /* CKROOT */
5878 
5879 #ifdef UNIX
5880     printf(" Disk output buffer:      %d (writes are %s, %s)\n",
5881            zobufsize,
5882            zofbuffer ? "buffered" : "unbuffered",
5883            zofblock ? "blocking" : "nonblocking"
5884            );
5885     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5886 #ifdef DYNAMIC
5887     printf(" Stringspace:             %d\n", zsetfil(0,2));
5888     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5889     printf(" Listsize:                %d\n", zsetfil(0,4));
5890     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5891 #endif /* DYNAMIC */
5892 #endif /* UNIX */
5893 #ifdef OS2ORUNIX
5894     printf(" Longest filename:        %d\n", maxnam);
5895     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5896     printf(" Longest pathname:        %d\n", maxpath);
5897     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5898 #endif /* OS2ORUNIX */
5899 
5900     printf(" Last file sent:          %s\n", sfspec ? sfspec : "(none)");
5901     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5902     printf(" Last file received:      %s\n", rfspec ? rfspec : "(none)");
5903     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5904     printf("\n Also see:\n");
5905     n++;
5906     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5907     printf(" SHOW PROTOCOL, SHOW XFER");
5908 #ifdef CK_LABELED
5909     printf(", SHOW LABELED");
5910 #endif /* CK_LABELED */
5911 #ifdef PATTERNS
5912     printf(", SHOW PATTERNS");
5913 #endif /* PATTERNS */
5914 #ifdef STREAMING
5915     printf(", SHOW STREAMING");
5916 #endif /* STREAMING */
5917 #ifndef NOCSETS
5918     printf(", SHOW CHARACTER-SETS");
5919 #endif /* NOCSETS */
5920     printf("\n\n");
5921 }
5922 
5923 #ifndef NOXFER
5924 VOID
shoparp()5925 shoparp() {                             /* Protocol */
5926     extern int docrc, skipbup;
5927     char *s;
5928 
5929 #ifdef CK_TIMERS
5930     extern int rttflg;
5931 #endif /* CK_TIMERS */
5932 
5933     printf("Protocol: %s\n",ptab[protocol].p_name);
5934 
5935     if (protocol == PROTO_K) {
5936         printf("\nProtocol Parameters:   Send    Receive");
5937         if (timef)
5938           printf("\n Timeout (used=%2d):%7d*%8d ", timint, rtimo, pkttim);
5939         else
5940           printf("\n Timeout (used=%2d):%7d%9d ",  timint, rtimo, pkttim);
5941 #ifdef XFRCAN
5942         printf("       Cancellation:    %s",showoff(xfrcan));
5943         if (xfrcan)
5944           printf(" %d %d", xfrchr, xfrnum);
5945 #endif /* XFRCAN */
5946         printf("\n Padding:      %11d%9d", npad,   mypadn);
5947         if (bctr == 4)
5948           printf("        Block Check: blank-free-2\n");
5949         else
5950           printf("        Block Check: %6d\n",bctr);
5951         printf(  " Pad Character:%11d%9d", padch,  mypadc);
5952         printf("        Delay:       %6d\n",ckdelay);
5953         printf(  " Pause:        %11d%9d", pktpaus, pktpaus);
5954         printf("        Attributes:      %s\n",showoff(atcapr));
5955         printf(  " Packet Start: %11d%9d", mystch, stchr);
5956         printf("        Max Retries: %6d%s\n",
5957                maxtry,
5958                (maxtry == 0) ? " (unlimited)" : ""
5959                );
5960         printf(  " Packet End:   %11d%9d", seol,   eol);
5961         if (ebqflg)
5962           printf("        8th-Bit Prefix: '%c'",ebq);
5963         else
5964           printf("        8th-Bit Prefix: ('%c' but not used)",ebq);
5965         printf(  "\n Packet Length:%11d ", spmax);
5966         printf("%8d     ",  urpsiz);
5967         if (rptflg)
5968           printf("   Repeat Prefix:  '%c'",rptq);
5969         else
5970           printf("   Repeat Prefix:  ('%c' but not used)",rptq);
5971         printf(  "\n Maximum Length: %9d%9d", maxsps, maxrps);
5972         printf("        Window Size:%7d set, %d used\n",wslotr,wmax);
5973         printf(    " Buffer Size:  %11d%9d", bigsbsiz, bigrbsiz);
5974         printf("        Locking-Shift:    ");
5975         if (lscapu == 2) {
5976             printf("forced");
5977         } else {
5978             printf("%s", (lscapr ? "enabled" : "disabled"));
5979             if (lscapr) printf(",%s%s", (lscapu ? " " : " not "), "used");
5980         }
5981         printf("\n\n");
5982 
5983         if (!(s = ptab[protocol].h_b_init)) s = "";
5984         printf(" Auto-upload command (binary): ");
5985         if (*s) {
5986             shostrdef((CHAR *)s);
5987             printf("\n");
5988         } else {
5989             printf("(none)\n");
5990         }
5991         if (!(s = ptab[protocol].h_t_init)) s = "";
5992         printf(" Auto-upload command (text):   ");
5993         if (*s) {
5994             shostrdef((CHAR *)s);
5995             printf("\n");
5996         } else {
5997             printf("(none)\n");
5998         }
5999         if (!(s = ptab[protocol].h_x_init)) s = "";
6000         printf(" Auto-server command:          ");
6001         if (*s) {
6002             shostrdef((CHAR *)s);
6003             printf("\n");
6004         } else {
6005             printf("(none)\n");
6006         }
6007         tmpbuf[0] = NUL;
6008 #ifdef CK_TIMERS
6009         if (rttflg) {
6010             extern int mintime, maxtime;
6011             sprintf(tmpbuf," Packet timeouts: dynamic %d:%d", /* SAFE */
6012                     mintime,
6013                     maxtime);
6014         } else {
6015             sprintf(tmpbuf," Packet timeouts: fixed"); /* SAFE */
6016         }
6017 #endif /* CK_TIMERS */
6018         if (tmpbuf[0])
6019           printf("%-31s",tmpbuf);
6020         printf("Send backup: %s\n",showoff(!skipbup));
6021 
6022         printf(" Transfer mode:   %s", xfermode == XMODE_A ?
6023                "automatic   " :
6024                "manual      "
6025                );
6026         printf(" Transfer slow-start: %s, crc: %s\n",
6027                showoff(slostart),
6028                showoff(docrc)
6029                );
6030 #ifdef PIPESEND
6031         {
6032             extern int usepipes;
6033             printf(" Transfer pipes:  %s         ",usepipes ? "on " : "off");
6034         }
6035 #endif /* PIPESEND */
6036 #ifndef NOCSETS
6037         printf(" Transfer character-set: ");
6038         if (tcharset == TC_TRANSP)
6039           printf("transparent\n");
6040         else
6041           printf("%s\n", tcsinfo[tcharset].keyword );
6042 #endif /* NOCSETS */
6043 #ifdef PIPESEND
6044         {
6045             extern char * sndfilter, * rcvfilter;
6046             printf(" Send filter:     %s\n", sndfilter ? sndfilter : "(none)");
6047             printf(" Receive filter:  %s\n", rcvfilter ? rcvfilter : "(none)");
6048         }
6049 #endif /* PIPESEND */
6050         printf("\nAlso see:\n");
6051         printf(" SHOW FILE, SHOW XFER");
6052 
6053 #ifdef CK_LABELED
6054         printf(", SHOW LABELED");
6055 #endif /* CK_LABELED */
6056 #ifdef PATTERNS
6057         printf(", SHOW PATTERNS");
6058 #endif /* PATTERNS */
6059 #ifdef STREAMING
6060         printf(", SHOW STREAMING");
6061 #endif /* STREAMING */
6062 #ifndef NOCSETS
6063         printf(", SHOW CHARACTER-SETS");
6064 #endif /* NOCSETS */
6065     }
6066 
6067 #ifdef CK_XYZ
6068 #ifdef XYZ_INTERNAL
6069     if (protocol != PROTO_K) {
6070         int i;
6071         int x;
6072         printf(" File type: %s\n", binary ? "binary" : "text");
6073         if (protocol == PROTO_Z) {              /* Zmodem */
6074             printf(" Window size:   ");
6075             if (ptab[protocol].winsize < 1)
6076               printf("none\n");
6077             else
6078               printf("%d\n",wslotr);
6079 #ifdef COMMENT
6080             printf(" Packet (frame) length: ");
6081             if (ptab[protocol].spktlen < 0)
6082               printf("none\n");
6083             else
6084               printf("%d\n",spmax);
6085 #endif /* COMMENT */
6086         } else {
6087             if (ptab[protocol].spktlen >= 1000)
6088               printf(" 1K packets\n");
6089             else
6090               printf(" 128-byte packets\n");
6091         }
6092         printf(" Pathname stripping when sending:   %s\n",
6093                showoff(ptab[protocol].fnsp)
6094                );
6095         printf(" Pathname stripping when receiving: %s\n",
6096                showoff(ptab[protocol].fnrp)
6097                );
6098         printf(" Filename collision action:         ");
6099         for (i = 0; i < ncolx; i++)
6100           if (colxtab[i].kwval == fncact) break;
6101         printf("%-12s", (i == ncolx) ? "unknown" : colxtab[i].kwd);
6102 
6103         printf("\n Escape control characters:          ");
6104         x = ptab[protocol].prefix;
6105         if (x == PX_ALL)
6106           printf("all\n");
6107         else if (x == PX_CAU || x==PX_WIL)
6108           printf("minimal\n");
6109         else
6110           printf("none\n");
6111         if (!(s = ptab[protocol].h_b_init))
6112           s = "";
6113         printf(" Autoreceive command (binary): %s\n", *s ? s : "(none)");
6114         if (!(s = ptab[protocol].h_t_init))
6115           s = "";
6116         printf(" Autoreceive command (text):   %s\n", *s ? s : "(none)");
6117     }
6118 #else
6119 #ifndef NOPUSH
6120     if (protocol != PROTO_K) {
6121 	_PROTOTYP( VOID shoextern, (void) );
6122         printf("\nExecuted by external commands:\n\n");
6123         s = ptab[protocol].p_b_scmd;
6124         if (!s) s = "";
6125         printf(" SEND command (binary):        %s\n", *s ? s : "(none)");
6126         s = ptab[protocol].p_t_scmd;
6127         if (!s) s = "";
6128         printf(" SEND command (text):          %s\n", *s ? s : "(none)");
6129         s = ptab[protocol].p_b_rcmd;
6130         if (!s) s = "";
6131         printf(" RECEIVE command (binary):     %s\n", *s ? s : "(none)");
6132         s = ptab[protocol].p_t_rcmd;
6133         if (!s) s = "";
6134         printf(" RECEIVE command (text):       %s\n", *s ? s : "(none)");
6135         s = ptab[protocol].h_b_init;
6136         if (!s) s = "";
6137         printf(" Autoreceive command (binary): %s\n", *s ? s : "(none)");
6138         s = ptab[protocol].h_t_init;
6139         if (!s) s = "";
6140         printf(" Autoreceive command (text):   %s\n", *s ? s : "(none)");
6141 	(VOID) shoextern();
6142     }
6143 #endif /* NOPUSH */
6144 #endif /* XYZ_INTERNAL */
6145 #endif /* CK_XYZ */
6146 }
6147 #endif /* NOXFER */
6148 
6149 #ifndef NOCSETS
6150 /* Character-set items */
6151 
6152 extern int s_cset, r_cset, axcset[], afcset[];
6153 extern struct keytab xfrmtab[];
6154 
6155 VOID
shoparl()6156 shoparl() {
6157 #ifdef COMMENT
6158     int i;
6159 /* Misleading... */
6160     printf("\nAvailable Languages:\n");
6161     for (i = 0; i < MAXLANG; i++) {
6162         printf(" %s\n",langs[i].description);
6163     }
6164 #else
6165     printf("\nLanguage-specific translation rules: %s\n",
6166            language == L_USASCII ? "none" : langs[language].description);
6167     shocharset();
6168     printf("\n\n");
6169 #endif /* COMMENT */
6170 }
6171 
6172 VOID
shocharset()6173 shocharset() {
6174     int x;
6175 #ifdef COMMENT
6176     char * s = "Unknown";
6177     extern int xlatype;
6178 #endif /* COMMENT */
6179 
6180 #ifndef NOXFER
6181     extern int xfrxla;
6182 #endif /* NOXFER */
6183 
6184     debug(F101,"SHOW FILE CHAR","",fcharset);
6185     printf("\n");
6186 #ifndef NOXFER
6187     printf(" Transfer Translation: %s\n", showoff(xfrxla));
6188     if (!xfrxla) {
6189         printf(
6190       " Because transfer translation is off, the following are ignored:\n\n");
6191     }
6192 #endif /* NOXFER */
6193     printf(" File Character-Set: %s (%s), ",
6194            fcsinfo[fcharset].keyword,
6195            fcsinfo[fcharset].name
6196            );
6197     if ((x = fcsinfo[fcharset].size) == 128)
6198       printf("7-bit");
6199     else if (x == 256)
6200       printf("8-bit");
6201     else
6202       printf("multibyte");
6203     printf("\n");
6204     printf(" File Scan: %s\n",showoff(filepeek));
6205     printf("   Default 7bit-Character-Set: %s\n",fcsinfo[dcset7].keyword);
6206     printf("   Default 8bit-Character-Set: %s\n",fcsinfo[dcset8].keyword);
6207     printf(" Transfer Character-Set");
6208 #ifdef COMMENT
6209     if (tslevel == TS_L2)
6210       printf(": (international)");
6211     else
6212 #endif /* COMMENT */
6213     if (tcharset == TC_TRANSP)
6214       printf(": Transparent");
6215     else
6216       printf(": %s (%s)",tcsinfo[tcharset].keyword, tcsinfo[tcharset].name);
6217     printf("\n");
6218 #ifdef COMMENT
6219     switch (xlatype) {
6220       case XLA_NONE: s = "None"; break;
6221       case XLA_BYTE: s = "Byte"; break;
6222       case XLA_JAPAN: s = "Japanese"; break;
6223       case XLA_UNICODE: s = "Unicode"; break;
6224     }
6225     printf("\n Translation type: %s\n",s);
6226 #endif /* COMMENT */
6227     printf(" SEND character-set-selection: %s\n",xfrmtab[s_cset].kwd);
6228     printf(" RECEIVE character-set-selection: %s\n",xfrmtab[r_cset].kwd);
6229     if (s_cset == XMODE_A || r_cset == XMODE_A)
6230       printf(
6231       " (Use SHOW ASSOCIATIONS to list automatic character-set selections.)\n"
6232              );
6233 }
6234 
6235 VOID
showassoc()6236 showassoc() {
6237     int i, k, n = 4;
6238     char * s;
6239     printf("\nFor incoming files:\n\n");
6240     printf("Transfer Character-Set   File Character-Set\n");
6241     for (i = 1; i <= MAXTCSETS; i++) {
6242         k = axcset[i];
6243         if (k < 0 || k > MAXFCSETS)
6244           s = "(none)";
6245         else
6246           s = fcsinfo[k].keyword;
6247         if (!s) s = "";
6248         if (!*s) s = "(none)";
6249         printf(" %-25s%s\n",tcsinfo[i].keyword,s);
6250         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
6251     }
6252     printf("\nFor outbound files:\n\n");
6253     n += 2;
6254     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
6255     printf("File Character-Set       Transfer Character-Set\n");
6256     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
6257     for (i = 0; i <= MAXFCSETS; i++) {
6258         k = afcset[i];
6259         if (k < 0 || k > MAXTCSETS)
6260           s = "(none)";
6261         else
6262           s = tcsinfo[k].keyword;
6263         if (!s) s = "";
6264         if (!*s) s = "(none)";
6265         printf(" %-25s%s\n",fcsinfo[i].keyword,s);
6266         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
6267     }
6268 }
6269 #endif /* NOCSETS */
6270 
6271 VOID
shopar()6272 shopar() {
6273     printf("Show what?  (Type \"show ?\" for a list of possibilities.)\n");
6274 }
6275 #endif /* NOSHOW */
6276 
6277 #ifndef NOXFER
6278 /*  D O S T A T  --  Display file transfer statistics.  */
6279 
6280 int
dostat(brief)6281 dostat(brief) int brief; {
6282     extern long filrej, peakcps;
6283     extern int lastspmax, streamed, cleared, streamok;
6284     extern char whoareu[];
6285     int n = 0, ftp = 0;
6286     extern int docrc, interrupted, fatalio;
6287 
6288     ftp = lastxfer & W_FTP;
6289 
6290 #ifdef CK_TTGWSIZ
6291 #ifdef OS2
6292     if (tt_cols[VTERM] < 0 || tt_rows[VTERM] < 0)
6293       ttgwsiz();
6294 #else /* OS2 */
6295     if (ttgwsiz() > 0) {
6296         if (tt_rows > 0 && tt_cols > 0) {
6297             cmd_rows = tt_rows;
6298             cmd_cols = tt_cols;
6299         }
6300     }
6301 #endif /* OS2 */
6302 #endif /* CK_TTGWSIZ */
6303 
6304     debug(F101,"dostat xferstat","",xferstat);
6305     if (xferstat < 0) {
6306         printf(" No file transfers yet.\n");
6307         return(1);
6308     }
6309     n = 0;
6310     if (brief) { printf("\n"); n++; };
6311     printf(" protocol               : %s\n",
6312            ftp ? "ftp" : ptab[protocol].p_name);
6313     n++;
6314     printf(" status                 : ");
6315     if (xferstat) printf("SUCCESS\n");
6316     else if (interrupted) printf("FAILURE (interrupted)\n");
6317     else if (fatalio) printf("FAILURE (i/o error)\n");
6318     else printf("FAILURE\n");
6319 #ifndef XYZ_INTERNAL
6320     if (!ftp && protocol != PROTO_K) {
6321         printf("\n external protocol statistics not available\n");
6322         return(1);
6323     }
6324 #endif /* XYZ_INTERNAL */
6325     n++;
6326     if (!ftp) {
6327         if (!xferstat > 0) {
6328             if (docrc)
6329               printf(" crc-16 of file(s)      : %ld\n", crc16);
6330             else
6331               printf(" crc-16 of file(s)      : (disabled)\n");
6332             n++;
6333         }
6334         if (!xferstat && *epktmsg) {
6335             printf(" reason                 : %s\n", epktmsg);
6336             n++;
6337         }
6338     }
6339     if (!brief) {
6340 #ifdef NEWFTP
6341         if (ftp) {
6342             extern char ftp_srvtyp[];
6343             printf(" remote system type     : %s\n",ftp_srvtyp);
6344         } else
6345 #endif /* NEWFTP */
6346           if (whoareu[0]) {
6347             printf(" remote system type     : %s\n",
6348                    getsysid((char *)whoareu));
6349             n++;
6350         }
6351         printf(" files transferred      : %ld\n",filcnt - filrej);
6352         if (!ftp)
6353           printf(" files not transferred  : %ld\n",filrej);
6354         printf(" characters last file   : %s\n",ckfstoa(ffc));
6355         printf(" total file characters  : %s\n",ckfstoa(tfc));
6356         n += ftp ? 3 : 4;
6357         if (!ftp) {
6358             printf(" communication line in  : %s\n",ckfstoa(tlci));
6359             printf(" communication line out : %s\n",ckfstoa(tlco));
6360             printf(" packets sent           : %d\n", spackets);
6361             printf(" packets received       : %d\n", rpackets);
6362             n += 4;
6363         }
6364     }
6365     if (ftp) goto dotimes;
6366 
6367     printf(" damaged packets rec'd  : %d\n", crunched);
6368     printf(" timeouts               : %d\n", timeouts);
6369     printf(" retransmissions        : %d\n", retrans);
6370     n += 3;
6371 
6372     if (!brief) {
6373         if (filcnt > 0) {
6374             printf(" parity                 : %s",parnam((char)parity));
6375             n++;
6376             if (autopar) { printf(" (detected automatically)"); n++; }
6377             printf(
6378                  "\n control characters     : %ld prefixed, %ld unprefixed\n",
6379                    ccp, ccu);
6380             n++;
6381             printf(" 8th bit prefixing      : ");
6382             n++;
6383             if (ebqflg) printf("yes [%c]\n",ebq); else printf("no\n");
6384             n++;
6385             printf(" locking shifts         : %s\n", lscapu ? "yes" : "no");
6386             n++;
6387         }
6388     }
6389     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6390     if (streamed > 0)
6391       printf(" window slots used      : (streaming)\n");
6392     else
6393       printf(" window slots used      : %d of %d\n", wmax, wslotr);
6394     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6395     printf(" reliable:              : %s%s\n",
6396            streamok ? "" : "not ", "negotiated");
6397     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6398     printf(" clearchannel:          : %s%s\n",
6399            cleared  ? "" : "not ", "negotiated");
6400     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6401 
6402     if (!brief) {
6403         printf(" packet length          : %d (send), %d (receive)\n",
6404                lastspmax, urpsiz);
6405         if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6406         printf(" compression            : ");
6407         if (rptflg)
6408           printf("yes [%c] (%ld)\n",(char) rptq,rptn);
6409         else
6410           printf("no\n");
6411         if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6412         if (bctu == 4)
6413           printf(" block check type used  : blank-free-2\n");
6414         else
6415           printf(" block check type used  : %d\n",bctu);
6416         if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6417     }
6418 
6419   dotimes:
6420 
6421 #ifdef GFTIMER
6422 #ifdef COMMENT
6423     printf(" elapsed time           : %0.3f sec, %s\n", fptsecs,hhmmss(tsecs));
6424 #endif /* COMMENT */
6425     printf(" elapsed time           : %s (%0.3f sec)\n",
6426            hhmmss((long)(fptsecs + 0.5)),fptsecs);
6427 #else
6428 #ifdef COMMENT
6429     printf(" elapsed time           : %s (%d sec)\n",hhmmss(tsecs),tsecs);
6430 #endif /* COMMENT */
6431     printf(" elapsed time           : %d sec, %s\n",tsecs,hhmmss(tsecs));
6432 #endif /* GFTIMER */
6433     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6434     if (!ftp && local && !network && !brief) {
6435         if (speed <= 0L) speed = ttgspd();
6436         if (speed > 0L) {
6437             if (speed == 8880)
6438               printf(" transmission rate      : 75/1200 bps\n");
6439             else
6440               printf(" transmission rate      : %ld bps\n",speed);
6441             if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6442         }
6443     }
6444     if (!ftp && local && !network &&    /* Only makes sense for */
6445         mdmtyp == 0 &&                  /* direct serial connections */
6446         speed > 99L &&                  /* when we really know the speed */
6447         speed != 8880L
6448         ) {
6449         int eff;
6450         eff = (((tfcps * 100L) / (speed / 100L)) + 5L) / 10L;
6451         printf(" effective data rate    : %ld cps (%d%%)\n",tfcps,eff);
6452     } else
6453       printf(" effective data rate    : %ld cps\n", tfcps);
6454     if (!ftp && peakcps > 0L && peakcps > tfcps)
6455       printf(" peak data rate         : %ld cps\n", peakcps);
6456     if (brief)
6457       printf("\nUse STATISTICS /VERBOSE for greater detail.\n\n");
6458     return(1);
6459 }
6460 #endif /* NOXFER */
6461 
6462 #ifndef NOSPL
6463 
6464 /* The INPUT command */
6465 
6466 /*
6467   NOTE: An INPUT timeout of 0 means to perform a nonblocking read of the
6468   material that has already arrived and is waiting to be read, and perform
6469   matches against it, without doing any further reads.  It should succeed
6470   or fail instantaneously.
6471 */
6472 
6473 /* Output buffering for "doinput" */
6474 
6475 #ifdef pdp11
6476 #define MAXBURST 16             /* Maximum size of input burst */
6477 #else
6478 #define MAXBURST 1024
6479 #endif /* pdp11 */
6480 #ifdef OSK
6481 static CHAR *conbuf;            /* Buffer to hold output for console */
6482 #else
6483 static CHAR conbuf[MAXBURST];   /* Buffer to hold output for console */
6484 #endif /* OSK */
6485 static int concnt = 0;          /* Number of characters buffered */
6486 #ifdef OSK
6487 static CHAR *sesbuf;            /* Buffer to hold output for session log */
6488 #else
6489 static CHAR sesbuf[MAXBURST];   /* Buffer to hold output for session log */
6490 #endif /* OSK */
6491 static int sescnt = 0;          /* Number of characters buffered */
6492 
6493 extern int debses;                      /* TERMINAL DEBUG ON/OFF */
6494 
6495 static VOID                             /* Flush INPUT echoing */
myflsh()6496 myflsh() {                              /* and session log output. */
6497     if (concnt > 0) {
6498         if (debses) {                   /* Terminal debugging? */
6499             int i;
6500             for (i = 0; i < concnt; i++)
6501               conol(dbchr(conbuf[i]));
6502         } else
6503           conxo(concnt, (char *) conbuf);
6504         concnt = 0;
6505     }
6506     if (sescnt > 0) {
6507         logstr((char *) sesbuf, sescnt);
6508         sescnt = 0;
6509     }
6510 }
6511 
6512 /* Execute the INPUT and MINPUT commands */
6513 
6514 int instatus = -1;
6515 long inetime = -1L;
6516 int inwait = 0;
6517 int nowrap = 0;
6518 
6519 /* For returning the input sequence that matched */
6520 
6521 #ifdef BIGBUFOK
6522 #define MATCHBUFSIZ 8191
6523 #else
6524 #define MATCHBUFSIZ 1023
6525 #endif /* BIGBUFOK */
6526 static char * matchbuf = NULL;
6527 static int matchindex = 0;
6528 static int burst = 0;                      /* Chars remaining in input burst */
6529 /*
6530   timo = How long to wait:
6531          < 0 = Wait forever
6532            0 = Don't wait at all - material must already have arrived
6533          > 0 = Wait this many seconds
6534   ms   = Array of strings to wait for.
6535   mp   = Array of flags.
6536          If mp[i] == 0, ms[i] is literal, else it's a pattern.
6537   flags = bit mask
6538     INPSW_NOM = /NOMATCH = 1
6539     INPSW_CLR = /CLEAR   = 2
6540     INPSW_NOW = /NOWRAP  = 4
6541     INPSW_COU = /COUNT   = 8
6542   count = /COUNT: value if a /COUNT switch was given.
6543 
6544  Returns:
6545     0 on failure, 1 on success.
6546 */
6547 #ifndef ES_NORMAL
6548 #define ES_NORMAL 0
6549 #endif	/* ES_NORMAL */
6550 extern int inesc[], oldesc[];
6551 
6552 int
doinput(timo,ms,mp,flags,count)6553 doinput(timo,ms,mp,flags,count)
6554     int timo; char *ms[]; int mp[]; int flags; int count; {
6555     extern int inintr;
6556 #ifdef CK_AUTODL
6557     extern int inautodl;
6558 #endif /* CK_AUTODL */
6559     int x, y, i, t, rt, icn, anychar = 0, mi[MINPMAX];
6560 #ifdef GFTIMER
6561     CKFLOAT fpt = 0.0;
6562 #endif /* GFTIMER */
6563     int savecount = 0;
6564     int nomatch = 0;
6565     int clearfirst = 0;
6566     int lastchar = 0;
6567     int waiting = 0;
6568     int imask = 0;
6569     char ch, *xp, *s;
6570     CHAR c;
6571 #ifndef NOLOCAL
6572 #ifdef OS2
6573     extern int term_io;
6574     int term_io_save;
6575 #endif /* OS2 */
6576 #endif /* NOLOCAL */
6577 #ifdef TNCODE
6578     static int cr = 0;
6579 #endif /* TNCODE */
6580     int is_tn = 0;
6581 #ifdef SSHBUILTIN
6582     extern int ssh_cas;
6583     extern char * ssh_cmd;
6584 #endif /* SSHBUILTIN */
6585     int noescseq = 0;			/* Filter escape sequences */
6586 
6587     debug(F101,"input count","",count);
6588     debug(F101,"input flags","",flags);
6589 
6590 /*
6591   CK_BURST enables the INPUT speedup code, which depends on ttchk() returning
6592   accurate information.  If INPUT fails with this code enabled, change the
6593   above "#define" to "#undef".
6594 */
6595 #define CK_BURST
6596 
6597 /***** CHANGE THIS TO A SET INPUT PARAMETER *****/
6598 
6599     noescseq = (sessft == XYFT_T);	/* Filter escape sequences */
6600 
6601     imask = cmask;
6602     if (parity) imask = 0x7f;
6603     inwait = timo;                      /* For \v(inwait) */
6604 
6605     /* Options from command switches */
6606 
6607     nowrap = flags & INPSW_NOW;		/* 4 = /NOWRAP */
6608     nomatch = flags & INPSW_NOM;	/* 1 = /NOMATCH */
6609     clearfirst = flags & INPSW_CLR;	/* 2 = /CLEAR */
6610     savecount = count;
6611 
6612     makestr(&inpmatch,NULL);
6613     if (!matchbuf) {
6614         matchbuf = malloc(MATCHBUFSIZ+1);
6615         matchbuf[0] = NUL;
6616     }
6617     matchindex = 0;
6618 
6619     /* If last time through we returned because of /NOWRAP and buffer full */
6620     /* now we have to clear the buffer to make room for another load. */
6621 
6622     if (nowrap && instatus == INP_BF)
6623       clearfirst = 1;
6624 
6625     if (clearfirst) {			/* INPUT /CLEAR */
6626 	int i;
6627 	myflsh();			/* Flush screen and log buffers */
6628 	for (i = 0; i < inbufsize; i++)
6629 	  inpbuf[i] = NUL;
6630 	inpbp = inpbuf;
6631     }
6632     is_tn =
6633 #ifdef TNCODE
6634         (local && network && IS_TELNET()) || (!local && sstelnet)
6635 #else
6636          0
6637 #endif /* TNCODE */
6638           ;
6639 
6640 #ifdef CK_SSL
6641     if (is_tn) if (ssl_raw_flag || tls_raw_flag) is_tn = 0;
6642 #endif	/* CK_SSL */
6643 
6644     instatus = INP_IE;                  /* 3 = internal error */
6645     kbchar = 0;
6646 
6647 #ifdef OSK
6648     if (conbuf == NULL) {
6649         if ((conbuf = (CHAR *)malloc(MAXBURST*2)) == NULL) {
6650             return(0);
6651         }
6652         sesbuf = conbuf + MAXBURST;
6653     }
6654 #endif /* OSK */
6655 
6656 #ifndef NOLOCAL
6657     if (local) {                        /* In local mode... */
6658         if ((waiting = ttchk()) < 0) {  /* check that connection is open */
6659 	    if (!quiet) {
6660 		if ((!network
6661 #ifdef TN_COMPORT
6662 		      || istncomport()
6663 #endif /* TN_COMPORT */
6664 		      ) && carrier != CAR_OFF)
6665 		    printf("?Carrier detect failure on %s.\n", ttname);
6666 		else
6667 		    printf("?Connection %s %s is not open.\n",
6668 		       network ? "to" : "on",
6669 		       ttname
6670 		       );
6671 	    }
6672             instatus = INP_IO;
6673             return(0);
6674         }
6675         debug(F101,"doinput waiting","",waiting);
6676         y = ttvt(speed,flow);           /* Put line in "ttvt" mode */
6677         if (y < 0) {
6678             printf("?INPUT initialization error\n");
6679             instatus = INP_IO;
6680             return(0);                  /* Watch out for failure. */
6681         }
6682     }
6683 #endif /* NOLOCAL */
6684 
6685 #ifdef SSHBUILTIN
6686     if ( network && nettype == NET_SSH && ssh_cas && ssh_cmd &&
6687          !(strcmp(ssh_cmd,"kermit") && strcmp(ssh_cmd,"sftp"))) {
6688         if (!quiet)
6689 	  printf("?SSH Subsystem active: %s\n", ssh_cmd);
6690         instatus = INP_IKS;
6691         return(0);
6692     }
6693 #endif /* SSHBUILTIN */
6694 
6695     debug(F111,"doinput ms[0]",ms[0],waiting);
6696 
6697     if (!ms[0] || isemptystring(ms[0])) { /* No search string was given nor */
6698 	if (count < 2)			  /* a /COUNT: switch so we just */
6699 	  anychar = 1;			  /* wait for the first character */
6700     }
6701     if (nomatch) anychar = 0;		/* Don't match anything */
6702 
6703     if (!anychar && waiting == 0 && timo == 0)
6704       return(0);
6705 
6706 #ifndef NODEBUG
6707     if (deblog) {
6708         char xbuf[100];
6709         debug(F101,"doinput anychar","",anychar);
6710         debug(F101,"doinput timo","",timo);
6711         debug(F101,"doinput echo","",inecho);
6712 #ifdef CK_BURST
6713         debug(F101,"doinput burst","",burst);
6714 #endif	/* CK_BURST */
6715         y = -1;
6716         while (ms[++y]) {
6717             sprintf(xbuf,"doinput string %2d",y); /* SAFE (24) */
6718             debug(F111,xbuf,ms[y],mp[y]);
6719         }
6720     }
6721 #endif /* NODEBUG */
6722 
6723 #ifdef IKS_OPTION
6724     if (is_tn) {
6725         /* If the remote side is in a state of IKS START-SERVER    */
6726         /* we request that the state be changed.  We will detect   */
6727         /* a failure to adhere to the request when we call ttinc() */
6728         if (TELOPT_U(TELOPT_KERMIT) &&
6729             TELOPT_SB(TELOPT_KERMIT).kermit.u_start)
6730           iks_wait(KERMIT_REQ_STOP,0);  /* Send Request-Stop */
6731 #ifdef CK_AUTODL
6732         /* If we are processing packets during INPUT and we have not */
6733         /* sent a START message, do so now.                          */
6734         if (inautodl && TELOPT_ME(TELOPT_KERMIT) &&
6735 			!TELOPT_SB(TELOPT_KERMIT).kermit.me_start) {
6736             tn_siks(KERMIT_START);      /* Send Kermit-Server Start */
6737         }
6738 #endif /* CK_AUTODL */
6739     }
6740 #endif /* IKS_OPTION */
6741     x = 0;                              /* Return code, assume failure */
6742     instatus = INP_TO;                  /* Status, assume timeout */
6743 
6744     for (y = 0; y < MINPMAX; y++)	/* Initialize... */
6745       mi[y] = 0;                        /*  ..string pattern match position */
6746 
6747     if (!inpcas[cmdlvl]) {              /* INPUT CASE = IGNORE?  */
6748         y = -1;
6749         while ((xp = ms[++y])) {	/* Convert each target to lowercase */
6750             while (*xp) {
6751                 if (isupper(*xp)) *xp = (char) tolower(*xp);
6752                 xp++;
6753             }
6754         }
6755     }
6756     rtimer();                           /* Reset timer. */
6757 #ifdef GFTIMER
6758     rftimer();                          /* Floating-point timer too. */
6759 #endif /* GFTIMER */
6760     inetime = -1L;                      /* Initialize elapsed time. */
6761     t = 0;                              /* Time now is 0. */
6762     m_found = 0;                        /* Default to timed-out */
6763     incount = 0;                        /* Character counter */
6764     rt = (timo == 0) ? 0 : 1;           /* Character-read timeout interval */
6765 
6766 #ifndef NOLOCAL
6767 #ifdef OS2
6768     term_io_save = term_io;             /* Disable I/O by emulator */
6769     term_io = 0;
6770 #endif /* OS2 */
6771 #endif /* NOLOCAL */
6772 
6773     while (1) {                         /* Character-getting loop */
6774 #ifdef CK_APC
6775         /* Check to see if there is an Autodown or other APC command */
6776         if (apcactive == APC_LOCAL ||
6777             (apcactive == APC_REMOTE && apcstatus != APC_OFF)) {
6778             if (mlook(mactab,"_apc_commands",nmac) == -1) {
6779                 debug(F110,"doinput about to execute APC",apcbuf,0);
6780                 domac("_apc_commands",apcbuf,cmdstk[cmdlvl].ccflgs|CF_APC);
6781                 delmac("_apc_commands",1);
6782                 apcactive = APC_INACTIVE;
6783 #ifdef DEBUG
6784             } else {
6785                 debug(F100,"doinput APC in progress","",0);
6786 #endif /* DEBUG */
6787             }
6788         }
6789 #endif /* CK_APC */
6790 
6791         if (timo == 0 && waiting < 1) { /* Special exit criterion */
6792             instatus = INP_TO;          /* for timeout == 0 */
6793             break;
6794         }
6795         if (local) {                    /* One case for local */
6796             y = ttinc(rt);              /* Get character from comm device */
6797             debug(F101,"doinput ttinc(rt) returns","",y);
6798             if (y < -1) {               /* Connection failed. */
6799                 instatus = INP_IO;      /* Status = i/o error */
6800 #ifndef NOLOCAL
6801 #ifdef OS2
6802                 term_io = term_io_save;
6803 #endif /* OS2 */
6804 #endif /* NOLOCAL */
6805                 switch (y) {
6806                   case -2:              /* Connection lost */
6807                     if (local && !network && carrier != CAR_OFF) {
6808                         dologend();
6809                         printf("Connection closed.\n");
6810                         ttclos(1);
6811                     }
6812                     break;
6813                   case -3:
6814                     dologend();
6815                     printf("Session Limit exceeded - closing connection.\n");
6816                     ttclos(1);
6817                   default:
6818                     break;
6819                 }
6820                 debug(F111,"doinput Connection failed","returning 0",y);
6821                 return(0);
6822             }
6823             if (inintr) {
6824                 debug(F111,"doinput","inintr",inintr);
6825                 if ((icn = conchk()) > 0) { /* Interrupted from keyboard? */
6826                     debug(F101,"input interrupted from keyboard","",icn);
6827                     kbchar = coninc(0);
6828                     if (kbchar >= 0) {
6829                         while (--icn > 0) {
6830                             debug(F110,"doinput","absorbing",0);
6831                             coninc(0);      /* Yes, absorb what was typed. */
6832                         }
6833                         instatus = INP_UI;  /* Fail and remember why. */
6834                         break;
6835                     }
6836                 }
6837             }
6838         } else {                        /* Another for remote */
6839             y = coninc(rt);
6840             debug(F101,"doinput coninc(rt) returns","",y);
6841         }
6842         if (y > -1) {                   /* A character arrived */
6843             debug(F111,"doinput","a character arrived",y);
6844             if (timo == 0)
6845               waiting--;
6846 #ifndef OS2
6847 #define TN_NOLO
6848 #endif /* OS2 */
6849 #ifdef NOLOCAL
6850 #define TN_NOLO
6851 #endif /* NOLOCAL */
6852 
6853 #ifdef TN_NOLO
6854             debug(F100,"doinput TN_NOLO","",0);
6855 #ifdef TNCODE
6856             /* Check for telnet protocol negotiation */
6857             if (is_tn) {
6858                 switch (y & 0xff) {
6859                   case IAC:
6860                     cr = 0;
6861                     myflsh();   /* Break from input burst for tn_doop() */
6862 #ifdef CK_BURST
6863                     burst = 0;
6864 #endif /* CK_BURST */
6865                     waiting -= 2;       /* (not necessarily...) */
6866                     switch (tn_doop((CHAR)(y & 0xff),duplex,ttinc)) {
6867                       case 2: duplex = 0; continue;
6868                       case 1: duplex = 1; continue;
6869 #ifdef IKS_OPTION
6870                       case 4:
6871                         if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start &&
6872                              !tcp_incoming) {
6873                             instatus = INP_IKS;
6874                             printf(
6875  " Internet Kermit Service in SERVER mode.\n Please use REMOTE commands.\n"
6876                                    );
6877                             break;
6878                         }
6879                         continue;
6880 #endif /* IKS_OPTION */
6881                       case 6:           /* TELNET DO LOGOUT received */
6882 			continue;
6883 		      case 7:
6884 		      case 3:		/* A quoted IAC */
6885 			break;
6886                       default:
6887 			continue;
6888                     }
6889                   case CR:
6890                     cr = 1;
6891                     break;
6892                   case NUL:
6893                     if (!TELOPT_U(TELOPT_BINARY) && cr) {
6894                         cr = 0;
6895                         continue;
6896                     }
6897                     cr = 0;
6898                     break;
6899                   default:
6900                     cr = 0;
6901                 }
6902                 /* I'm echoing remote chars */
6903                 if (TELOPT_ME(TELOPT_ECHO) && tn_rem_echo)
6904                   ttoc((char)y);
6905             }
6906 #endif /* TNCODE */
6907 #ifdef CK_AUTODL
6908             /* Check for file transfer packets */
6909             if (inautodl) autodown(y);
6910 #endif /* CK_AUTODL */
6911 #else  /* TN_NOLO */
6912             debug(F100,"doinput !TN_NOLO","",0);
6913 #ifdef TNCODE
6914             /* Check for telnet protocol negotiation */
6915             if (is_tn) {
6916                 int tx;
6917                 switch (y & 0xff) {
6918                   case IAC:
6919                     myflsh();   /* Break from input burst for tn_doop() */
6920 #ifdef CK_BURST
6921                     burst = 0;
6922 #endif /* CK_BURST */
6923 #ifdef IKS_OPTION
6924                     tx = scriptwrtbuf((USHORT)y);
6925                     if (tx == 4) {
6926                         if (TELOPT_U(TELOPT_KERMIT) &&
6927 			    TELOPT_SB(TELOPT_KERMIT).kermit.u_start &&
6928                             !tcp_incoming
6929                             ) {
6930                             instatus = INP_IKS;
6931                             printf(
6932   " Internet Kermit Service in SERVER mode.\n Please use REMOTE commands.\n"
6933                                    );
6934                             break;
6935                         }
6936                     } else if (tx == 6) {
6937                         /* TELNET DO LOGOUT received */
6938 
6939                     }
6940 #else /* IKS_OPTION */
6941                     /* Handles Telnet negotiations */
6942                     tx = scriptwrtbuf((USHORT)y);
6943                     if (tx == 6) {
6944                         /* TELNET DO LOGOUT received */
6945                     }
6946 #endif /* IKS_OPTION */
6947                     waiting -= 2;       /* (not necessarily...) */
6948                     cr = 0;
6949                     continue;           /* and autodownload check */
6950                   case CR:
6951                     cr = 1;
6952                     tx = scriptwrtbuf((USHORT)y);
6953                     if (tx == 6) {
6954                         /* TELNET DO LOGOUT received */
6955                     }
6956                     break;
6957                   case NUL:
6958                     cr = 0;
6959                     if (!TELOPT_U(TELOPT_BINARY) && cr)
6960                       continue;
6961                     tx = scriptwrtbuf((USHORT)y);
6962                     if (tx == 6) {
6963                         /* TELNET DO LOGOUT received */
6964                     }
6965                     break;
6966                   default:
6967                     cr = 0;
6968                     tx = scriptwrtbuf((USHORT)y);
6969                     if (tx == 6) {
6970                         /* TELNET DO LOGOUT received */
6971                     }
6972                 }
6973                 /* I'm echoing remote chars */
6974                 if (TELOPT_ME(TELOPT_ECHO) && tn_rem_echo)
6975                   ttoc((CHAR)y);
6976             } else
6977 #endif /* TNCODE */
6978               /* Handles terminal emulation responses */
6979               scriptwrtbuf((USHORT)y);
6980 #endif /* TN_NOLO */
6981 
6982             /* Real input character to be checked */
6983 
6984 #ifdef CK_BURST
6985             burst--;                    /* One less character waiting */
6986             debug(F101,"doinput burst","",burst);
6987 #endif /* CK_BURST */
6988             c = (CHAR) (imask & (CHAR) y); /* Mask off any parity */
6989             inchar[0] = c;              /* Remember character for \v(inchar) */
6990 #ifdef COMMENT
6991 #ifdef CK_BURST
6992             /* Update "lastchar" time only once during input burst */
6993             if (burst <= 0)
6994 #endif /* CK_BURST */
6995 #endif /* COMMENT */
6996               lastchar = gtimer();      /* Remember when it came */
6997 
6998             if (c == '\0') {            /* NUL, we can't use it */
6999                 if (anychar) {          /* Except if any character will do? */
7000                     x = 1;              /* Yes, done. */
7001 		    instatus = INP_OK;
7002                     incount = 1;        /* This must be the first and only. */
7003                     break;
7004                 } else goto refill;	/* Otherwise continue INPUTting */
7005             }
7006             *inpbp++ = c;               /* Store char in circular buffer */
7007             incount++;                  /* Count it for \v(incount) */
7008 
7009 	    if (flags & INPSW_COU) {	/* INPUT /COUNT */
7010 		if (--count < 1) {
7011 		    x = 1;
7012 		    instatus = INP_OK;
7013                     incount = savecount;
7014                     break;
7015 		}
7016 	    }
7017             if (matchbuf) {
7018                 if (matchindex < MATCHBUFSIZ) {
7019                     matchbuf[matchindex++] = c;
7020                     matchbuf[matchindex] = NUL;
7021                 }
7022             }
7023 #ifdef MAC
7024             {
7025                 extern char *ttermw;    /* fake pointer cast */
7026                 if (inecho) {
7027                     outchar(ttermw, c); /* echo to terminal window */
7028                     /* this might be too much overhead to do here ? */
7029                     updatecommand(ttermw);
7030                 }
7031             }
7032 #else /* Not MAC */
7033             if (inecho) {               /* Buffer console output */
7034                 conbuf[concnt++] = c;
7035             }
7036 #endif /* MAC */
7037 #ifndef OS2
7038             if (seslog) {
7039 		int dummy = 0, skip = 0;
7040 #ifndef NOLOCAL
7041 		if (noescseq) {
7042 		    dummy = chkaes(c,0);
7043 		    if (inesc[0] != ES_NORMAL || oldesc[0] != ES_NORMAL)
7044 		      skip = 1;
7045 		}
7046 #endif	/* NOLOCAL */
7047 #ifdef UNIXOROSK
7048 		if (sessft == XYFT_T) {
7049 #ifdef UNIX
7050 		    if (c == '\r')
7051 #else
7052 #ifdef OSK
7053 		    if (c == '\012')
7054 #endif /* OSK */
7055 #endif /* UNIX */
7056 		      skip = 1;
7057 		}
7058 #endif	/* UNIXOROSK */
7059 		if (!skip)
7060                   sesbuf[sescnt++] = c; /* Buffer session log output */
7061             }
7062 #endif /* OS2 */
7063             if (anychar) {              /* Any character will do? */
7064                 x = 1;
7065 		instatus = INP_OK;
7066                 break;
7067             }
7068             if (!inpcas[cmdlvl]) {      /* Ignore alphabetic case? */
7069                 if (isupper(c))         /* Yes, convert input char to lower */
7070                   c = (CHAR) tolower(c);
7071             }
7072             debug(F000,"doinput char","",c);
7073 
7074             /* Here is the matching section */
7075 
7076             y = -1;                     /* Loop thru search strings */
7077             while (!nomatch && (s = ms[++y])) {	/* ...as many as we have. */
7078                 if (mp[y]) {            /* Pattern match? */
7079 #ifdef COMMENT
7080                     int j;
7081                     /* This is gross but it works... */
7082                     /* We could just as easily have prepended '*' to the  */
7083                     /* pattern and skipped the loop, except then we would */
7084                     /* not have any way to identify the matching string.  */
7085                     for (j = 0; j < matchindex; j++) {
7086                         if (ckmatch(s,&matchbuf[j],1,1)) {
7087                             matchindex = j;
7088 			    instatus = INP_OK;
7089                             x = 1;
7090                             break;
7091                         }
7092                     }
7093                     if (x > 0)
7094                       break;
7095 #else
7096                     /* July 2001 - ckmatch() returns match position. */
7097                     /* It works and it's not gross. */
7098 		    /* (4 = floating pattern) */
7099                     x = ckmatch(s,matchbuf,inpcas[cmdlvl],1+4);
7100                     if (x > 0) {
7101                         matchindex = x - 1;
7102 			instatus = INP_OK;
7103                         x = 1;
7104                         break;
7105                     }
7106 #endif /* COMMENT */
7107                     continue;
7108                 }                       /* Literal match. */
7109                 i = mi[y];              /* Match-position in search string. */
7110                 debug(F000,"compare char","",(CHAR)s[i]);
7111                 if (c == (CHAR) s[i]) { /* Check for match */
7112                     i++;                /* Got one, go to next character */
7113                 } else {                /* Don't have a match */
7114                     int j;
7115                     for (j = i; i > 0; ) { /* Back up in search string */
7116                         i--; /* (Do this here to prevent compiler foulup) */
7117                         /* j is the length of the substring that matched */
7118                         if (c == (CHAR) s[i]) {
7119                             if (!strncmp(s,&s[j-i],i)) {
7120                                 i++;          /* c actually matches -- cfk */
7121                                 break;
7122                             }
7123                         }
7124                     }
7125                 }
7126                 if ((CHAR) s[i] == (CHAR) '\0') { /* Matched to end? */
7127                     ckstrncpy(matchbuf,ms[y],MATCHBUFSIZ);
7128                     matchindex = 0;
7129 		    instatus = INP_OK;  /* Yes, */
7130                     x = 1;
7131                     break;              /* done. */
7132                 }
7133                 mi[y] = i;              /* No, remember match-position */
7134             }
7135             if (x == 1) {               /* Set \v(minput) result */
7136 		instatus = INP_OK;
7137                 m_found = y + 1;
7138                 break;
7139             }
7140             if (inpbp >= inpbuf + inbufsize) { /* Reached end of buffer? */
7141 		if (nowrap) {		/* If /NOWRAP...*/
7142 		    instatus = INP_BF;	/* ...return indicating buffer full. */
7143 		    *inpbp = NUL;
7144 		    goto xinput;
7145 		}
7146                 *inpbp = NUL;           /* Make it null-terminated */
7147                 inpbp = inpbuf;         /* Yes. */
7148             }
7149         }
7150 #ifdef CK_BURST
7151         else if (y <= -1 && burst > 0) {
7152             debug(F111,"doinput (y<=-1&&burst>0)","burst",burst);
7153                                         /* A timeout occurred so there can't */
7154             burst = 0;                  /* be data waiting; must check timo */
7155         }
7156       refill:
7157         if (burst <= 0) {               /* No buffered chars remaining... */
7158             myflsh();                   /* Flush buffered output */
7159             if (local) {                /* Get size of next input burst */
7160                 burst = ttchk();
7161                 if (burst < 0) {        /* ttchk() says connection is closed */
7162                     instatus = INP_IO;  /* Status = i/o error */
7163 #ifndef NOLOCAL
7164 #ifdef OS2
7165                     term_io = term_io_save;
7166 #endif /* OS2 */
7167 #endif /* NOLOCAL */
7168 
7169 		    if ((!network
7170 #ifdef TN_COMPORT
7171 		         || istncomport()
7172 #endif /* TN_COMPORT */
7173 			 ) && carrier != CAR_OFF) {
7174 	/* The test is written this way because the Microsoft compiler
7175 	 * is producing bad code if written:
7176 	 *
7177 	 *  if (network && (!istncomport() || carrier == CAR_OFF) )
7178 	 */
7179 			break;
7180                      } else {
7181 			 printf("Fatal error - disconnected.\n");
7182 			 ttclos(1);
7183 			 break;
7184 		     }
7185                 }
7186                 if (inintr) {
7187                     if ((icn = conchk()) > 0) { /* Interrupt from keyboard? */
7188                         kbchar = coninc(0);
7189                         debug(F101,"input interrupted from keyboard","",icn);
7190                         while (--icn > 0) coninc(0); /* Yes, absorb chars. */
7191                         break;          /* And fail. */
7192                     }
7193                 }
7194             } else {
7195                 burst = conchk();
7196             }
7197             debug(F101,"doinput burst","",burst);
7198             /* Prevent overflow of "conbuf" and "sesbuf" */
7199             if (burst > MAXBURST)
7200               burst = MAXBURST;
7201 
7202             /* Did not match, timer exceeded? */
7203             t = gtimer();
7204             debug(F111,"doinput gtimer","burst",t);
7205             debug(F101,"doinput timo","",timo);
7206             if ((t >= timo) && (timo > 0))
7207               break;
7208             else if (insilence > 0 && (t - lastchar) > insilence)
7209               break;
7210         } else {
7211             debug(F111,"doinput (burst > 0)","burst",burst);
7212         }
7213 #else  /* CK_BURST */
7214       refill:
7215         myflsh();                       /* Flush buffered output */
7216         /* Did not match, timer exceeded? */
7217         t = gtimer();
7218         debug(F111,"doinput gtimer","no burst",t);
7219         debug(F101,"doinput timo","",timo);
7220         if ((t >= timo) && (timo > -1))
7221           break;
7222         else if (insilence > 0 && (t - lastchar) > insilence)
7223           break;
7224 #endif /* CK_BURST */
7225     }                                   /* Still have time left, continue. */
7226   xinput:
7227     myflsh();                           /* Flush buffered output */
7228     if (instatus == INP_BF) {		/* Buffer full and /NOWAIT */
7229 	x = 0;				/* Must not succeed */
7230     } else {				/* Buffer full and /NOWAIT */
7231 	if (nomatch) x = 1;		/* Succeed if nomatch and timed out */
7232 	if (x > 0 && !nomatch)
7233 	  instatus = 0;
7234     }
7235 #ifndef NOLOCAL
7236 #ifdef OS2
7237     term_io = term_io_save;
7238 #endif /* OS2 */
7239 #endif /* NOLOCAL */
7240 #ifdef COMMENT
7241 #ifdef IKS_OPTION
7242 #ifdef CK_AUTODL
7243     if (is_tn && TELOPT_ME(TELOPT_KERMIT) && inautodl) {
7244         tn_siks(KERMIT_STOP);           /* Send Kermit-Server Stop */
7245     }
7246 #endif /* CK_AUTODL */
7247 #endif /* IKS_OPTION */
7248 #endif /* COMMENT */
7249 
7250 #ifdef GFTIMER
7251     fpt = gftimer();                    /* Get elapsed time */
7252 
7253 /* If a long is 32 bits, it would take about 50 days for this to overflow. */
7254 
7255     inetime = (int)(fpt * (CKFLOAT)1000.0);
7256 #else
7257     inetime = (int)(gtimer() * 1000);
7258 #endif /* GFTIMER */
7259 
7260     if (x > 0)
7261       makestr(&inpmatch,&matchbuf[matchindex]); /* \v(inmatch) */
7262     return(x);                          /* Return the return code. */
7263 }
7264 #endif /* NOSPL */
7265 
7266 #ifndef NOSPL
7267 /* REINPUT Command */
7268 
7269 /*
7270   Note, the timeout parameter is required, but ignored.  Syntax is compatible
7271   with MS-DOS Kermit except timeout can't be omitted.  This function only
7272   looks at the characters already received and does not read any new
7273   characters from the connection.
7274 */
7275 int
doreinp(timo,s,pat)7276 doreinp(timo,s,pat) int timo; char *s; int pat; {
7277     int x, y, i;
7278     char *xx, *xp, *xq = (char *)0;
7279     CHAR c;
7280 
7281     if (!s) s = "";
7282     debug(F101,"doreinput pat","",pat);
7283 
7284     y = (int)strlen(s);
7285     debug(F111,"doreinput search",s,y);
7286 
7287     if (y > inbufsize) {                /* If search string longer than */
7288         debug(F101,"doreinput inbufsize","",inbufsize);
7289         return(0);                      /* input buffer, fail. */
7290     }
7291     makestr(&inpmatch,NULL);
7292     if (!matchbuf)
7293       matchbuf = malloc(MATCHBUFSIZ+1);
7294     matchindex = 0;
7295 
7296     x = 0;                              /* Return code, assume failure */
7297     i = 0;                              /* String pattern match position */
7298 
7299     if (!inpcas[cmdlvl]) {              /* INPUT CASE = IGNORE?  */
7300         xp = malloc(y+2);               /* Make a separate copy of the */
7301         if (!xp) {                      /* search string. */
7302             printf("?malloc error 6\n");
7303             return(x);
7304         } else xq = xp;                 /* Keep pointer to beginning. */
7305         while (*s) {                    /* Yes, convert to lowercase */
7306             *xp = *s;
7307             if (isupper(*xp)) *xp = (char) tolower(*xp);
7308             xp++; s++;
7309         }
7310         *xp = NUL;                      /* Terminate it! */
7311         s = xq;                         /* Move search pointer to it. */
7312     }
7313     xx = *inpbp ? inpbp : inpbuf;       /* Current INPUT buffer pointer */
7314     do {
7315         c = *xx++;                      /* Get next character */
7316         if (!c) break;
7317         if (xx >= inpbuf + inbufsize)   /* Wrap around if necessary */
7318           xx = inpbuf;
7319         if (!inpcas[cmdlvl]) {          /* Ignore alphabetic case? */
7320             if (isupper(c)) c = (CHAR) tolower(c); /* Yes */
7321         }
7322         if (pat) {
7323             int j;
7324             if (matchbuf) {
7325                 if (matchindex < MATCHBUFSIZ) {
7326                     matchbuf[matchindex++] = c;
7327                     matchbuf[matchindex] = NUL;
7328                 }
7329                 for (j = 0; j < matchindex; j++) { /* Gross but effective */
7330                     if (ckmatch(s,&matchbuf[j],1,1)) {
7331                         debug(F101,"GOT IT","",j);
7332                         matchindex = j;
7333                         x = 1;
7334                         break;
7335                     }
7336                 }
7337             }
7338             if (x > 0)
7339               break;
7340             continue;
7341         }
7342         debug(F000,"doreinp char","",c);
7343         debug(F000,"compare char","",(CHAR) s[i]);
7344         if (((char) c) == ((char) s[i])) { /* Check for match */
7345             i++;                        /* Got one, go to next character */
7346         } else {                        /* Don't have a match */
7347             int j;
7348             for (j = i; i > 0; ) {      /* [jrs] search backwards for it  */
7349                 i--;
7350                 if (((char) c) == ((char) s[i])) {
7351                     if (!strncmp(s,&s[j-i],i)) {
7352                         i++;
7353                         break;
7354                     }
7355                 }
7356             }
7357         }                               /* [jrs] or return to zero from -1 */
7358         if (s[i] == '\0') {             /* Matched all the way to end? */
7359             ckstrncpy(matchbuf,s,MATCHBUFSIZ);
7360             matchindex = 0;
7361             x = 1;                      /* Yes, */
7362             break;                      /* done. */
7363         }
7364     } while (xx != inpbp && x < 1);     /* Until back where we started. */
7365 
7366     if (!inpcas[cmdlvl]) if (xq) free(xq); /* Free this if it was malloc'd. */
7367     makestr(&inpmatch,&matchbuf[matchindex]); /* \v(inmatch) */
7368     return(x);                          /* Return search result. */
7369 }
7370 
7371 /*  X X S T R I N G  --  Interpret strings containing backslash escapes  */
7372 /*  Z Z S T R I N G  --  (new name...)  */
7373 /*
7374  Copies result to new string.
7375   strips enclosing braces or doublequotes.
7376   interprets backslash escapes.
7377   returns 0 on success, nonzero on failure.
7378   tries to be compatible with MS-DOS Kermit.
7379 
7380  Syntax of input string:
7381   string = chars | "chars" | {chars}
7382   chars = (c*e*)*
7383   where c = any printable character, ascii 32-126
7384   and e = a backslash escape
7385   and * means 0 or more repetitions of preceding quantity
7386   backslash escape = \operand
7387   operand = {number} | number | fname(operand) | v(name) | $(name) | m(name)
7388   number = [r]n[n[n]]], i.e. an optional radix code followed by 1-3 digits
7389   radix code is oO (octal), xX (hex), dD or none (decimal) (see xxesc()).
7390 */
7391 
7392 #ifndef NOFRILLS
7393 int
yystring(s,s2)7394 yystring(s,s2) char *s; char **s2; {    /* Reverse a string */
7395     int x;
7396     static char *new;
7397     new = *s2;
7398     if (!s || !new) return(-1);         /* Watch out for null pointers. */
7399     if ((x = (int)strlen(s)) == 0) {    /* Recursion done. */
7400         *new = '\0';
7401         return(0);
7402     }
7403     x--;                                /* Otherwise, call self */
7404     *new++ = s[x];                      /* to reverse rest of string. */
7405     s[x] = 0;
7406     return(yystring(s,&new));
7407 }
7408 #endif /* NOFRILLS */
7409 
7410 static char ipabuf[16] = { NUL };       /* IP address buffer */
7411 
7412 static char *
getip(s)7413 getip(s) char *s; {
7414     char c=NUL;                         /* Workers... */
7415     int i=0, p=0, d=0;
7416     int state = 0;                      /* State of 2-state FSA */
7417 
7418     while ((c = *s++)) {
7419         switch(state) {
7420           case 0:                       /* Find first digit */
7421             i = 0;                      /* Output buffer index */
7422             ipabuf[i] = NUL;            /* Initialize output buffer */
7423             p = 0;                      /* Period counter */
7424             d = 0;                      /* Digit counter */
7425             if (isdigit(c)) {           /* Have first digit */
7426                 d = 1;                  /* Count it */
7427                 ipabuf[i++] = c;        /* Copy it */
7428                 state = 1;              /* Change state */
7429             }
7430             break;
7431 
7432           case 1:                       /* In numeric field */
7433             if (isdigit(c)) {           /* Have digit */
7434                 if (++d > 3)            /* Too many */
7435                   state = 0;            /* Start over */
7436                 else                    /* Not too many */
7437                   ipabuf[i++] = c;      /* Keep it */
7438             } else if (c == '.' && p < 3) { /* Have a period */
7439                 p++;                    /* Count it */
7440                 if (d == 0)             /* Not preceded by a digit */
7441                   state = 0;            /* Start over */
7442                 else                    /* OK */
7443                   ipabuf[i++] = c;      /* Keep it */
7444                 d = 0;                  /* Reset digit counter */
7445             } else if (p == 3 && d > 0) { /* Not part of address */
7446                 ipabuf[i] = NUL;        /* If we have full IP address */
7447                 return((char *)ipabuf); /* Return it */
7448             } else {                    /* Otherwise */
7449                 state = 0;              /* Start over */
7450                 ipabuf[0] = NUL;        /* (in case no more chars left) */
7451             }
7452         }
7453     }                                   /* Fall thru at end of string */
7454     ipabuf[i] = NUL;                    /* Maybe we have one */
7455     return((p == 3 && d > 0) ? (char *)ipabuf : "");
7456 }
7457 #endif /* NOSPL */
7458 
7459 /* Date Routines */
7460 
7461 /* Z J D A T E  --  Convert yyyymmdd date to Day of Year */
7462 
7463 static int jdays[12] = {  0,31,59,90,120,151,181,212,243,273,304,334 };
7464 static int ldays[12] = {  0,31,60,91,121,152,182,213,244,274,305,335 };
7465 static char zjdbuf[12] = { NUL, NUL };
7466 /*
7467   Deinde, ne in posterum a XII kalendas aprilis aequinoctium recedat,
7468   statuimus bissextum quarto quoque anno (uti mos est) continuari debere,
7469   praeterquam in centesimis annis; qui, quamvis bissextiles antea semper
7470   fuerint, qualem etiam esse volumus annum MDC, post eum tamen qui deinceps
7471   consequentur centesimi non omnes bissextiles sint, sed in quadringentis
7472   quibusque annis primi quique tres centesimi sine bissexto transigantur,
7473   quartus vero quisque centesimus bissextilis sit, ita ut annus MDCC, MDCCC,
7474   MDCCCC bissextiles non sint. Anno vero MM, more consueto dies bissextus
7475   intercaletur, februario dies XXIX continente, idemque ordo intermittendi
7476   intercalandique bissextum diem in quadringentis quibusque annis perpetuo
7477   conservetur.  - Gregorius XIII, Anno Domini MDLXXXII.
7478 */
7479 char *
zjdate(date)7480 zjdate(date) char * date; {             /* date = yyyymmdd */
7481     char year[5];
7482     char month[3];
7483     char day[3];
7484     int d, m, x, y;
7485     int leapday, j;
7486     char * time = NULL;
7487 
7488     if (!date) date = "";               /* Validate arg */
7489     x = strlen(date);
7490     if (x < 1) return("0");
7491     if (x < 8) return("-1");
7492     for (x = 0; x < 8; x++)
7493       if (!isdigit(date[x]))
7494         return("-1");
7495 
7496     if (date[8]) if (date[9])
7497       time = date + 9;
7498 
7499     year[0] = date[0];                  /* Isolate year */
7500     year[1] = date[1];
7501     year[2] = date[2];
7502     year[3] = date[3];
7503     year[4] = '\0';
7504 
7505     month[0] = date[4];                 /* Month */
7506     month[1] = date[5];
7507     month[2] = '\0';;
7508 
7509     day[0] = date[6];                   /* And day */
7510     day[1] = date[7];
7511     day[2] = '\0';
7512 
7513     leapday = 0;                        /* Assume no leap day */
7514     y = atoi(year);
7515     m = atoi(month);
7516     d = atoi(day);
7517     if (m > 2) {                        /* No Leap day before March */
7518         if (y % 4 == 0) {               /* If year is divisible by 4 */
7519             leapday = 1;                /* It's a Leap year */
7520             if (y % 100 == 0) {         /* Except if divisible by 100 */
7521                 if (y % 400 != 0)       /* but not by 400 */
7522                   leapday = 0;
7523             }
7524         }
7525     }
7526     j = jdays[m - 1] + d + leapday;     /* Day of year */
7527     if (time)
7528       sprintf(zjdbuf,"%04d%03d %s",y,j,time); /* SAFE */
7529     else
7530       sprintf(zjdbuf,"%04d%03d",y,j);   /* SAFE */
7531     return((char *)zjdbuf);
7532 }
7533 
7534 static char jzdbuf[32];
7535 
7536 /* J Z D A T E  --  Convert Day of Year to yyyyddmm date */
7537 
7538 char *
jzdate(date)7539 jzdate(date) char * date; {             /* date = yyyyddd */
7540     char year[5];                       /* with optional time */
7541     char day[4];
7542     char * time = NULL, * p;
7543     int d, m, x, y;
7544     int leapday, j;
7545     int * zz;
7546 
7547     if (!date) date = "";               /* Validate arg */
7548     x = strlen(date);
7549 
7550     debug(F111,"jzdate len",date,x);
7551 
7552     if (x < 1) return("0");
7553     if (x < 7) return("-1");
7554     if (x > 8) time = date + 8;
7555 
7556     for (x = 0; x < 7; x++)
7557       if (!isdigit(date[x]))
7558         return("-1");
7559 
7560     year[0] = date[0];                  /* Isolate year */
7561     year[1] = date[1];
7562     year[2] = date[2];
7563     year[3] = date[3];
7564     year[4] = '\0';
7565 
7566     debug(F110,"jzdate year",year,0);
7567 
7568     day[0] = date[4];                   /* And day */
7569     day[1] = date[5];
7570     day[2] = date[6];
7571     day[3] = '\0';
7572 
7573     debug(F110,"jzdate day",day,0);
7574 
7575     j = atoi(day);
7576     if (j > 366)
7577       return("-1");
7578 
7579     leapday = 0;                        /* Assume no leap day */
7580     y = atoi(year);
7581     if (y % 4 == 0) {                   /* If year is divisible by 4 */
7582         leapday = 1;                    /* It's a Leap year */
7583         if (y % 100 == 0) {             /* Except if divisible by 100 */
7584             if (y % 400 != 0)           /* but not by 400 */
7585               leapday = 0;
7586         }
7587     }
7588     debug(F101,"jzdate leapday","",leapday);
7589     zz = leapday ? ldays : jdays;
7590 
7591     for (x = 0; x < 11; x++)
7592       if (j > zz[x] && j <= zz[x+1])
7593         break;
7594     m = x + 1;
7595 
7596     debug(F101,"jzdate m","",m);
7597 
7598     d = j - zz[x];
7599 
7600     debug(F101,"jzdate d","",d);
7601 
7602     if (time)
7603       sprintf(jzdbuf,"%04d%02d%02d %s",y,m,d,time); /* SAFE */
7604     else
7605       sprintf(jzdbuf,"%04d%02d%02d",y,m,d); /* SAFE */
7606 
7607     debug(F101,"jzdate jzdbuf",jzdbuf,0);
7608 
7609     p = ckcvtdate((char *)jzdbuf, 0);   /* Convert to standard form */
7610     ckstrncpy(jzdbuf,p,32);
7611     if (!time) jzdbuf[8] = NUL;         /* Remove time if not wanted */
7612     return((char *)jzdbuf);
7613 }
7614 
7615 /* M J D  --  Modified Julian Date */
7616 /*
7617   Call with:
7618     Standard-format date-time string: yyyymmdd[ hh:mm:ss].
7619     The time, if any, is ignored.
7620 
7621   Returns:
7622     -1L on error, otherwise:
7623     The number of days since 17 Nov 1858 as a whole number:
7624     16 Nov 1858 = -1, 17 Nov 1858 = 0, 18 Nov 1858 = 1, 19 Nov 1858 = 2, ...
7625 
7626   The Modified Julian Date is defined by the International Astronomical
7627   Union as the true Julian date minus 2400000.5 days.  The true Julian
7628   date is the number days since since noon of 1 January 4713 BCE of the
7629   Julian proleptic calendar.  Conversions between calendar dates and
7630   Julian dates, however, assume Gregorian dating.  The day of the week
7631   is MJD mod 7: 0=We, 1=Th, 2=Fr, 3=Sa, 4=Su, 5=Mo, 6=Tu.
7632 */
7633 long
mjd(date)7634 mjd(date) char * date; {
7635     char year[5];
7636     char month[3];
7637     char day[3];
7638     int x, a, d, m, y;
7639     long z;
7640 
7641     if (!date) date = "";               /* Validate arg */
7642     x = strlen(date);
7643     if (x < 1) return(0L);
7644     if (x < 8) return(-1L);
7645     for (x = 0; x < 8; x++)
7646       if (!isdigit(date[x]))
7647         return(-1L);
7648 
7649     year[0] = date[0];                  /* Isolate year */
7650     year[1] = date[1];
7651     year[2] = date[2];
7652     year[3] = date[3];
7653     year[4] = '\0';
7654 
7655     month[0] = date[4];                 /* Month */
7656     month[1] = date[5];
7657     month[2] = '\0';;
7658     m = atoi(month);
7659 
7660     day[0] = date[6];                   /* And day */
7661     day[1] = date[7];
7662     day[2] = '\0';
7663     d = atoi(day);
7664 
7665     a = (14-m)/12;                      /* Calculate true Julian date */
7666     y = atoi(year) + 4800 - a;
7667     m = m + 12 * a - 3;
7668     z = d + (long)(306*m+5)/10 + (long)(y*365) + y/4 - y/100 + y/400 - 32045L;
7669 
7670     z -= 2400001L;                      /* Convert JD to MJD */
7671 
7672     return(z);
7673 }
7674 
7675 static char mjd2dbuf[32];
7676 
7677 /*  M J D 2 D A T E  --  Converts MJD to yyyymmdd  */
7678 
7679 char *
7680 #ifdef CK_ANSIC
mjd2date(long mjd)7681 mjd2date(long mjd)
7682 #else
7683 mjd2date(mjd) long mjd;
7684 #endif /* CK_ANSIC */
7685 /* mjd2date */ {
7686     long jd, l, n;
7687     int d, m, y;
7688     jd = (long)(mjd + 2400001L);
7689     l = jd + 68569;
7690     n = 4 * l / 146097L;
7691     l = l - (146097 * n + 3) / 4;
7692     y = 4000 * (l + 1) / 1461001L;
7693     l = l - 1461 * y / 4 + 31;
7694     m = 80 * l / 2447;
7695     d = l - 2447 * m / 80;
7696     l = m / 11;
7697     m = m + 2 - 12 * l;
7698     y = 100 * (n - 49) + y + l;
7699     sprintf(mjd2dbuf,"%04d%02d%02d",y,m,d); /* SAFE */
7700     return((char *)mjd2dbuf);
7701 }
7702 
7703 #ifndef NOSPL
7704 static char ** flist = (char **) NULL;  /* File list for \fnextfile() */
7705 static int flistn = 0;                  /* Number of items in file list */
7706 
7707 /*
7708   The function return-value buffer must be global, since fneval() returns a
7709   pointer to it.  fneval() is called only by zzstring(), which always copies
7710   the result out of this buffer to somewhere else, so it's OK to have only
7711   one buffer for this in most cases.  However, since function calls can be
7712   nested -- e.g. functions whose arguments are functions, or recursive
7713   functions, at some point we should convert this to an array of buffers,
7714   indexed by function depth (which might or might not be the same as the
7715   "depth" variable).  Also, since function results are potentially quite big,
7716   we'd need to allocate and deallocate dynamically as we descend and ascend
7717   function depth.  Left for a future release...
7718 */
7719 char fnval[FNVALL+2];                   /* Function return value  */
7720 static int fndepth = 0;                 /* (we don't actually use this yet) */
7721 int fnsuccess = 1;
7722 extern int fnerror;
7723 
7724 /* f p f o r m a t  --  Floating-point number nicely formatted.  */
7725 /*
7726    Returns results from a circular 1K buffer.
7727    Don't count on too many results remaining available at once; it could
7728    be anywhere from 5 to maybe 100, depending on the sizes of the results.
7729 */
7730 #ifdef CKFLOAT
7731 #define FPFMTSIZ 1024
7732 static char fpfmtbuf[FPFMTSIZ] = { NUL, NUL };
7733 static int fpfbufpos = 0;               /* (why was this char before?) */
7734 
7735 char *
fpformat(fpresult,places,round)7736 fpformat(fpresult,places,round) CKFLOAT fpresult; int places, round; {
7737     char fbuf[16];                      /* For creating printf format */
7738     int nines = 0, sign = 0, x, y, i, j, size = 0;
7739     char * buf;
7740     CKFLOAT ftmp;
7741 
7742     x = places ? places : (fp_digits ? fp_digits : 6);
7743 
7744     debug(F101,"fpformat fpresult","",fpresult);
7745     debug(F101,"fpformat places","",places);
7746     debug(F101,"fpformat fpfbufpos 1","",fpfbufpos);
7747 
7748     ftmp = fpresult;
7749     if (ftmp < 0.0) ftmp = 0.0 - fpresult;
7750 
7751 #ifdef FNFLOAT
7752     if (!fp_rounding &&                 /* If printf doesn't round, */
7753         (places > 0 ||                  /* round result to decimal places. */
7754          (places == 0 && round)))
7755       fpresult += (0.5 / pow(10.0,(CKFLOAT)places));
7756     y = (ftmp == 0.0) ? 1 : (int)log10(ftmp);
7757     size = y + x + 3;                   /* Estimated length of result */
7758     if (fpresult < 0.0) size++;
7759 #else
7760     size = 200;                         /* No way to estimate, be generous */
7761 #endif /* FNFLOAT */
7762 
7763     debug(F101,"fpformat size","",size);
7764 
7765     if (fpfbufpos > (FPFMTSIZ - size))  /* Wrap around if necessary */
7766       fpfbufpos = 0;
7767     debug(F101,"fpformat fpfbufpos 1","",fpfbufpos);
7768 
7769     buf = &fpfmtbuf[fpfbufpos];
7770 
7771     if (places > 0) {                   /* If places specified */
7772         /* use specified places to write given number of digits */
7773         sprintf(fbuf,"%%0.%df",places); /* SAFE */
7774         sprintf(buf,fbuf,fpresult);     /* SAFE */
7775     } else {                            /* Otherwise... */
7776         /* Go for max precision */
7777         sprintf(fbuf,"%%0.%df",fp_digits); /* SAFE */
7778         sprintf(buf,fbuf,fpresult);     /* SAFE */
7779     }
7780     if (buf[0] == '-') sign = 1;
7781     debug(F111,"fpresult 1 errno",buf,errno); /* Check for over/underflow */
7782     debug(F111,"fpresult 1 fpfbufpos",buf,fpfbufpos);
7783     /* Give requested decimal places */
7784     for (i = sign; i < FPFMTSIZ && buf[i]; i++) {
7785         if (buf[i] == '.')              /* First find the decimal point */
7786           break;
7787         else if (i > fp_digits + sign - 1) /* replacing garbage */
7788           buf[i] = '0';                 /* digits with 0... */
7789     }
7790     if (buf[i] == '.') {                /* Have decimal point */
7791         int gotend = 0;
7792         /* places < 0 so truncate fraction */
7793         if (places < 0 || (places == 0 && round)) {
7794             buf[i] = NUL;
7795         } else if (places > 0) {        /* d > 0 so this many decimal places */
7796             i++;                           /* First digit after decimal */
7797             for (j = 0; j < places; j++) { /* Truncate after d decimal */
7798                 if (!buf[j+i])        /* places or extend to d  */
7799                   gotend = 1;              /* decimal places. */
7800                 if (gotend || j+i+sign > fp_digits)
7801                   buf[j+i] = '0';
7802             }
7803             buf[j+i] = NUL;
7804         } else {                        /* places == 0 so Do The Right Thing */
7805             for (j = (int)strlen(buf) - 1; j > i+1; j--) {
7806                 if ((j - sign) > fp_digits)
7807                   buf[j] = '0';
7808                 if (buf[j] == '0')
7809                   buf[j] = NUL; /* Strip useless trailing 0's. */
7810                 else
7811                   break;
7812             }
7813         }
7814     }
7815     fpfmtbuf[FPFMTSIZ-1] = NUL;
7816     j = strlen(buf);
7817     sign = 0;
7818     for (i = j-1; i >= 0; i--) {
7819         if (buf[i] == '9')
7820           nines++;
7821         else
7822           break;
7823     }
7824     /* Do something about xx.xx99999999... */
7825     if (nines > 5) {
7826         if (isdigit(buf[i]) && i < FPFMTSIZ - 2) {
7827             buf[i] = buf[i] + 1;
7828             buf[i+1] = '0';
7829             buf[i+2] = '\0';
7830         }
7831     }
7832     if (!strncmp(buf,"-0.0",FPFMTSIZ))
7833       ckstrncpy(buf,"0.0",FPFMTSIZ);
7834     fpfbufpos += (int)strlen(buf) + 1;
7835     return((char *)buf);
7836 }
7837 #endif /* CKFLOAT */
7838 
7839 static VOID
evalerr(fn)7840 evalerr(fn) char * fn; {
7841     if (fndiags) {
7842         if (divbyzero)
7843           ckmakmsg(fnval,FNVALL,"<ERROR:DIVIDE_BY_ZERO:\\f",fn,"()>",NULL);
7844         else
7845           ckmakmsg(fnval,FNVALL,"<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
7846     }
7847 }
7848 
7849 
7850 static int
ckcindex(c,s)7851 ckcindex(c,s) char c, *s; {
7852     int rc;
7853     if (!c || !s) return(0);
7854     for (rc = 0; s[rc]; rc++) {
7855 	if (c == s[rc]) return(rc+1);
7856     }
7857     return(0);
7858 }
7859 
7860 static char *
dokwval(s,sep)7861 dokwval(s,sep) char * s, * sep; {
7862     char c = '\0', * p, * kw = NULL, * vp = NULL;
7863     char * rc = "0";			/* Return code */
7864     int x = 0;
7865     if (!s) return(rc);
7866     if (!*s) return(rc);
7867     debug(F110,"kwval arg",s,0);
7868     debug(F110,"kwval sep",sep,0);
7869     p = (char *)malloc((int)strlen(s)+1);
7870     if (!p) goto xdokwval;
7871     strcpy(p,s);                        /* SAFE */
7872     s = p;
7873     while (*s < '!' && *s > '\0')       /* Get first nonblank */
7874       s++;
7875     if (!*s) goto xdokwval;
7876     if (ckcindex(*s,sep))		/* Separator but no keyword */
7877       goto xdokwval;
7878     kw = s;                             /* Keyword */
7879     while (*s > ' ') {
7880 	if (ckcindex(*s,sep)) {		/* keyword=... */
7881             c = *s;
7882             break;
7883         }
7884         s++;
7885     }
7886     if (*kw) rc = "1";			/* Have keyword, promote return code */
7887     *s++ = NUL;                         /* Terminate keyword */
7888     while (*s < '!' && *s > '\0')       /* Skip blanks */
7889       s++;
7890     if (!c && ckcindex(*s,sep)) {
7891         c = *s++;                       /* Have separator */
7892         while (*s < '!' && *s > '\0')   /* Skip blanks */
7893           s++;
7894     }
7895     if (c) {
7896         vp = s;
7897 	if (*vp) rc = "2";		/* Have value, another promotion */
7898 #ifdef COMMENT
7899         while (*s > ' ')                /* Skip to end */
7900           s++;
7901         *s = NUL;                       /* Terminate value */
7902 #endif	/* COMMENT */
7903     }
7904     debug(F110,"kwval c",ckctoa(c),0);
7905     debug(F110,"kwval keyword",kw,0);
7906     debug(F110,"kwval value",vp,0);
7907     makestr(&lastkwval,kw);
7908     vp = brstrip(vp);
7909     debug(F110,"kwval value",vp,0);
7910     x = addmac(kw,vp);
7911     debug(F111,"kwval addmac",kw,x);
7912   xdokwval:
7913     if (p) free(p);
7914     return((x < 0) ? "-1" : rc);
7915 }
7916 
7917 static int
isaarray(s)7918 isaarray(s) char * s; {			/* Is s an associative array element */
7919     int state = 0;
7920     CHAR c;
7921     if (!s) return(0);
7922     while ((c = *s++)) {
7923 	if (!isprint(c)) {
7924 	    return(0);
7925 	} else if (c == '<') {
7926 	    if (state != 0)
7927 	      return(0);
7928 	    state = 1;
7929 	} else if (c == '>') {
7930 	    return((state != 1 || *s) ? 0 : 1);
7931 	}
7932     }
7933     return(0);
7934 }
7935 
7936 /* J P G D A T E  --  Get "date taken" from JPG file Exif data */
7937 
7938 /*
7939   This routine doesn't follow the Exif spec, it just hunts for
7940   date-time strings in the first 8K of the JPG and keeps the earliest one.
7941   Call with file pointer, returns Exif date-time string "yyyy:mm:dd hh:mm:ss"
7942   or an empty string if none found.
7943 */
7944 #define JPGYEAR  1
7945 #define JPGMONTH 2
7946 #define JPGDAY   3
7947 #define JPGHOUR  4
7948 #define JPGMIN   5
7949 #define JPGSEC   6
7950 
7951 #define JPGDATEBUF 8192	   /* Should be more than enough bytes to find date */
7952 
7953 static char *
jpgdate(fp)7954 jpgdate(fp) FILE * fp; {
7955     static char datebuf[20];
7956     char tmpbuf[20];
7957     CHAR buf[JPGDATEBUF+1];
7958     CHAR * p;
7959     CHAR * z;
7960     CHAR c;
7961     int i;
7962     int k = 0;
7963     int n = 0;
7964     int count = 0;
7965     int state = 0;
7966 
7967     if (fp == NULL)
7968       return("");
7969     rewind(fp);
7970 
7971     for (i = 0; i < 20; i++) { datebuf[i] = NUL; tmpbuf[i] = NUL; }
7972 
7973     datebuf[0] = NUL;
7974     tmpbuf[0] = NUL;
7975 
7976     count = fread(buf,1,JPGDATEBUF,fp); /* Read a buffer */
7977     if (count == EOF || count == 0) {
7978 	return("");
7979     }
7980     p = (CHAR *) buf;
7981     z = p + JPGDATEBUF;
7982 
7983     while (p < z) {
7984 	c = *p++;
7985 	n++;
7986 	if (c != ' ' && c != ':' && !isdigit(c)) {
7987 	    state = 0;
7988 	    k = 0;
7989 	    continue;
7990 	}
7991 	switch (state) {
7992 	  case 0:
7993 	    if (c == '1' && *p == '9') state = JPGYEAR;
7994 	    else if (c == '2' && *p == '0') state = JPGYEAR;
7995 	    if (state == JPGYEAR) {
7996 		k = 0;
7997 		tmpbuf[k++] = c;
7998 	    }
7999 	    continue;
8000 
8001 	  case JPGYEAR:
8002 	    if (c == ':' && k == 4) {
8003 		tmpbuf[k++] = c;
8004 		state = JPGMONTH;
8005 		continue;
8006 	    }
8007 	    if (k > 3 || !isdigit(c))
8008 	      state = k = 0;
8009 	    else
8010 	      tmpbuf[k++] = c;
8011 	    continue;
8012 
8013 	  case JPGMONTH:
8014 	    if (c == ':' && k == 7) {
8015 		tmpbuf[k++] = c;
8016 		state = JPGDAY;
8017 		continue;
8018 	    }
8019 	    if (k > 6 || !isdigit(c))
8020 	      state = k = 0;
8021 	    else
8022 	      tmpbuf[k++] = c;
8023 	    continue;
8024 
8025 	  case JPGDAY:
8026 	    if (c == ' ' && k == 10) {
8027 		tmpbuf[k++] = c;
8028 		state = JPGHOUR;
8029 		continue;
8030 	    }
8031 	    if (k > 9 || !isdigit(c))
8032 	      state = k = 0;
8033 	    else
8034 	      tmpbuf[k++] = c;
8035 	    continue;
8036 
8037 	  case JPGHOUR:
8038 	    if (c == ':' && k == 13) {
8039 		tmpbuf[k++] = c;
8040 		state = JPGMIN;
8041 		continue;
8042 	    }
8043 	    if (k > 12 || !isdigit(c))
8044 	      state = k = 0;
8045 	    else
8046 	      tmpbuf[k++] = c;
8047 	    continue;
8048 
8049 	  case JPGMIN:
8050 	    if (c == ':' && k == 16) {
8051 		tmpbuf[k++] = c;
8052 		state = JPGSEC;
8053 		continue;
8054 	    }
8055 	    if (k > 15 || !isdigit(c))
8056 	      state = k = 0;
8057 	    else
8058 	      tmpbuf[k++] = c;
8059 	    continue;
8060 
8061 	  case JPGSEC:
8062 	    if (!isdigit(c) || !isdigit(*p)) {
8063 		state = k = 0;
8064 		continue;
8065 	    }
8066 	    tmpbuf[k++] = c;
8067 	    tmpbuf[k++] = *p;
8068 	    tmpbuf[k] = NUL;
8069 	}
8070         if (!datebuf[0]) {		/* First date */
8071 	    strncpy(datebuf,tmpbuf,19);
8072         } else if (strncmp(tmpbuf,datebuf,19) < 0) { /* Earlier date */
8073 	    strncpy(datebuf,tmpbuf,19);
8074         }
8075     }
8076     return((char *) datebuf);
8077 }
8078 
8079 int                                     /* Is character alphnumeric? */
cisalphanum(ch)8080 cisalphanum(ch) CHAR ch; {              /* i.e. a letter, digit, @, $, or _ */
8081     /* All 8-bit characters are counted as alphanumeric */
8082     int c;
8083     c = (int)ch;                        /* Avoid C-language character syntax */
8084     if (c == 36) return(1);             /* '$' is alphanumeric */
8085     if (c == 95) return(1);             /* '_' is alphanumeric */
8086     if (c < 48) return(0);              /* Space, punctuation, math */
8087     if (c > 57 && c < 64) return(0);    /* Between '9' and '@' */
8088     if (c > 90 && c < 97) return(0);    /* Between 'Z' and 'a' */
8089     if (c > 122 && c < 127) return(0);  /* Between 'z' and DEL */
8090     return(1);
8091 }
8092 
8093 int                                     /* Is character non-alphanumeric */
cnonalphanum(ch)8094 cnonalphanum(ch) CHAR ch; {             /* i.e. not letter, digit, [$@_] */
8095     int c;
8096     c = (int)ch;                        /* Avoid C-language character syntax */
8097     if (c == 36) return(0);             /* '$' is alphanumeric */
8098     if (c == 95) return(0);             /* '_' is alphanumeric */
8099     if (c < 48) return(1);              /* Space, punctuation, math */
8100     if (c > 57 && c < 64) return(1);    /* Between '9' and '@' */
8101     if (c > 90 && c < 97) return(1);    /* Between 'Z' and 'a' */
8102     if (c > 122 && c < 127) return(1);  /* Between 'z' and DEL */
8103     return(0);
8104 }
8105 
8106 /* Tell if a string contains only alphanumeric characters */
8107 int
isalphanum(s)8108 isalphanum(s) char *s; {
8109     CHAR c;
8110     while ((c = (int)(*s++))) {
8111         if (!cisalphanum(c)) return(0);
8112     }
8113     return(1);
8114 }
8115 /* Tell if a string contains only non-alphanumeric characters */
8116 int
nonalphanum(s)8117 nonalphanum(s) char *s; {
8118     CHAR c;
8119     while ((c = (int)(*s++))) {
8120         if (!cnonalphanum(c)) return(0);
8121     }
8122     return(1);
8123 }
8124 
8125 static char *                           /* Evaluate builtin functions */
fneval(fn,argp,argn,xp)8126 fneval(fn,argp,argn,xp) char *fn, *argp[]; int argn; char * xp; {
8127     int i=0, j=0, k=0, len1=0, len2=0, len3=0, n=0, t=0, x=0, y=0;
8128     int cx, failed = 0;                 /* Return code, 0 = ok */
8129     long z = 0L;
8130     char *bp[FNARGS + 1];               /* Pointers to malloc'd strings */
8131     char c = NUL;
8132     char *p = NULL, *s = NULL;
8133     char *val1 = NULL, *val2 = NULL;    /* Pointers to numeric string values */
8134 
8135 #ifdef RECURSIVE
8136     int rsave = recursive;
8137 #endif /* RECURSIVE */
8138 #ifdef OS2
8139     int zsave = zxpn;
8140 #endif /* OS2 */
8141 
8142     if (!fn) fn = "";                   /* Protect against null pointers */
8143     if (!*fn) return("");
8144 
8145     for (i = 0; i < FNARGS; i++)        /* Initialize argument pointers */
8146       bp[i] = NULL;
8147 /*
8148   IMPORTANT: Note that argn is not an accurate count of the number of
8149   arguments.  We can't really tell if an argument is null until after we
8150   execute the code below.  So argn is really the maximum number of arguments
8151   we might have.  Argn should always be at least 1, even if the function is
8152   called with empty parentheses (but don't count on it).
8153 */
8154     debug(F111,"fneval",fn,argn);
8155     debug(F110,"fneval",argp[0],0);
8156     if (argn > FNARGS)                  /* Discard excess arguments */
8157       argn = FNARGS;
8158 
8159     fndepth++;
8160     debug(F101,"fneval fndepth","",fndepth);
8161     p = fnval;
8162     fnval[0] = NUL;
8163     y = lookup(fnctab,fn,nfuncs,&x);    /* Look up the function name */
8164     cx = y;                             /* Because y is too generic... */
8165     if (cx < 0) {                        /* Not found */
8166         failed = 1;
8167         if (fndiags) {                  /* FUNCTION DIAGNOSTIC ON */
8168             int x;
8169             x = strlen(fn);
8170             /* The following sprintf's are safe */
8171             switch (cx) {
8172               case -1:
8173                 if (x + 32 < FNVALL)
8174                   sprintf(fnval,"<ERROR:NO_SUCH_FUNCTION:\\f%s()>",fn);
8175                 else
8176                   sprintf(fnval,"<ERROR:NO_SUCH_FUNCTION>");
8177                 break;
8178               case -2:
8179                 if (x + 26 < FNVALL)
8180                   sprintf(fnval,"<ERROR:NAME_AMBIGUOUS:\\f%s()>",fn);
8181                 else
8182                   sprintf(fnval,"<ERROR:NAME_AMBIGUOUS>");
8183                 break;
8184               case -3:
8185                 sprintf(fnval,"<ERROR:FUNCTION_NAME_MISSING:\\f()>");
8186                 break;
8187               default:
8188                 if (x + 26 < FNVALL)
8189                   sprintf(fnval,"<ERROR:LOOKUP_FAILURE:\\f%s()>",fn);
8190                 else
8191                   sprintf(fnval,"<ERROR:LOOKUP_FAILURE>");
8192                 break;
8193             }
8194         }
8195         goto fnend;                     /* Always leave via common exit */
8196     }
8197     fn = fnctab[x].kwd;                 /* Full name of function */
8198 
8199     if (argn < 0) {
8200         failed = 1;
8201         p = fnval;
8202         if (fndiags)
8203           sprintf(fnval,"<ERROR:MISSING_ARG:\\f%s()>",fn);
8204         goto fnend;
8205     }
8206     if (cx == FN_LIT) {                 /* literal(arg1) */
8207         debug(F010,"flit",xp,0);
8208         p = xp ? xp : "";               /* Return a pointer to arg itself */
8209         goto fnend;
8210     }
8211 
8212 #ifdef DEBUG
8213     if (deblog) {
8214         int j;
8215         for (j = 0; j < argn; j++)
8216 	  debug(F111,"fneval arg",argp[j],j);
8217     }
8218 #endif /* DEBUG */
8219     for (j = argn-1; j >= 0; j--) {     /* Uncount empty trailing args */
8220         if (!argp[j])
8221           argn--;
8222         else if (!*(argp[j]))
8223           argn--;
8224         else break;
8225     }
8226     debug(F111,"fneval argn",fn,argn);
8227 /*
8228   \fliteral() and \fcontents() are special functions that do not evaluate
8229   their arguments, and are treated specially here.  After these come the
8230   functions whose arguments are evaluated in the normal way.
8231 */
8232 #ifdef COMMENT
8233     /* (moved up) */
8234     if (cx == FN_LIT) {                 /* literal(arg1) */
8235         debug(F110,"flit",xp,0);
8236         p = xp ? xp : "";               /* Return a pointer to arg itself */
8237         goto fnend;
8238     }
8239 #endif /* COMMENT */
8240     if (cx == FN_CON) {                 /* Contents of variable, unexpanded. */
8241         char c;
8242         int subscript = 0;
8243         if (!(p = argp[0]) || !*p) {
8244             failed = 1;
8245             p = fnval;
8246             if (fndiags)
8247               sprintf(fnval,"<ERROR:MISSING_ARG:\\fcontents()>");
8248             goto fnend;
8249         }
8250         p = brstrip(p);
8251         if (*p == CMDQ) p++;
8252         if ((c = *p) == '%') {          /* Scalar variable. */
8253             c = *++p;                   /* Get ID character. */
8254             p = "";                     /* Assume definition is empty */
8255             if (!c) {                   /* Double paranoia */
8256                 failed = 1;
8257                 p = fnval;
8258                 if (fndiags)
8259                   sprintf(fnval,"<ERROR:ARG_BAD_VARIABLE:\\fcontents()>");
8260                 goto fnend;
8261             }
8262             if (c >= '0' && c <= '9') { /* Digit for macro arg */
8263                 if (maclvl < 0)         /* Digit variables are global */
8264                   p = g_var[c];         /* if no macro is active */
8265                 else                    /* otherwise */
8266                   p = m_arg[maclvl][c - '0']; /* they're on the stack */
8267             } else if (c == '*') {
8268 #ifdef COMMENT
8269                 p = (maclvl > -1) ? m_line[maclvl] : topline;
8270                 if (!p) p = "";
8271 #else
8272                 int nx = FNVALL;
8273                 char * sx = fnval;
8274                 p = fnval;
8275 #ifdef COMMENT
8276                 if (cmdsrc() == 0 && topline)
8277                   p = topline;
8278                 else
8279 #endif /* COMMENT */
8280                   if (zzstring("\\fjoin(&_[],{ },1)",&sx,&nx) < 0) {
8281                     failed = 1;
8282                     p = fnval;
8283                     if (fndiags)
8284                       sprintf(fnval,"<ERROR:OVERFLOW:\\fcontents()>");
8285 		    debug(F110,"zzstring fcontents(\\%*)",p,0);
8286                 }
8287 #endif /* COMMENT */
8288             } else {
8289                 if (isupper(c)) c -= ('a'-'A');
8290                 p = g_var[c];           /* Letter for global variable */
8291             }
8292             if (!p) p = "";
8293             goto fnend;
8294         } else if (c == '&') {          /* Array reference. */
8295             int vbi, d;
8296             if (arraynam(p,&vbi,&d) < 0) { /* Get name and subscript */
8297                 failed = 1;
8298                 p = fnval;
8299                 if (fndiags)
8300                   sprintf(fnval,"<ERROR:ARG_BAD_ARRAY:\\fcontents()>");
8301                 goto fnend;
8302             }
8303 	    subscript = chkarray(vbi,d); /* Check the array */
8304             if (subscript >= 0) {	/* Array is declared? */
8305                 vbi -= ARRAYBASE;       /* Convert name to index */
8306                 if (a_dim[vbi] >= d) {  /* If subscript in range */
8307                     char **ap;
8308                     ap = a_ptr[vbi];    /* get data pointer */
8309                     if (ap) {           /* and if there is one */
8310                         p = ap[d];	/* return it */
8311                         goto fnend;
8312                     }
8313                 }
8314             } else {			/* Array not declared or element */
8315                 fnval[0] = NUL;		/* out of range - return null string */
8316                 p = fnval;		/* fdc 2010-12-30 */
8317                 goto fnend;
8318             }
8319         } else {
8320             failed = 1;
8321             p = fnval;
8322             if (fndiags)
8323               sprintf(fnval,"<ERROR:ARG_NOT_VARIABLE:\\fcontents()>");
8324             goto fnend;
8325         }
8326     }
8327     p = fnval;                          /* Default result pointer */
8328     fnval[0] = NUL;                     /* Default result = empty string */
8329 
8330     for (i = 0; i < argn; i++) {        /* Loop to expand each argument */
8331         n = MAXARGLEN;                  /* Allow plenty of space */
8332         bp[i] = s = malloc(n+1);        /* Allocate space for this argument */
8333         if (bp[i] == NULL) {            /* Handle failure to get space */
8334             failed = 1;
8335             if (fndiags)
8336               ckmakmsg(fnval,FNVALL,"<ERROR:MALLOC_FAILURE:\\f",fn,"()>",NULL);
8337             goto fnend;
8338         }
8339         p = argp[i] ? argp[i] : "";     /* Point to this argument */
8340 
8341 /*
8342   Trim leading and trailing spaces from the original argument, before
8343   evaluation.  This code new to edit 184.  Fixed in edit 199 to trim
8344   blanks BEFORE stripping braces.
8345 
8346 */
8347         {
8348             int x, j;
8349             x = strlen(p);
8350             j = x - 1;                  /* Trim trailing whitespace */
8351             while (j > 0 && (*(p + j) == SP || *(p + j) == HT))
8352               *(p + j--) = NUL;
8353             while (*p == SP || *p == HT) /* Strip leading whitespace */
8354               p++;
8355             x = strlen(p);
8356             if (*p == '{' && *(p+x-1) == '}') { /* NOW strip braces */
8357                 p[x-1] = NUL;
8358                 p++;
8359                 x -= 2;
8360             }
8361         }
8362 
8363 /* Now evaluate the argument */
8364 
8365         debug(F111,"fneval calling zzstring",p,n);
8366         t = zzstring(p,&s,&n);          /* Expand arg into new space */
8367         debug(F101,"fneval zzstring","",t);
8368         debug(F101,"fneval zzstring","",n);
8369         if (t < 0) {
8370             debug(F101,"fneval zzstring fails, arg","",i);
8371             failed = 1;
8372             if (fndiags) {
8373                 if (n == 0)
8374                   ckmakmsg(fnval,FNVALL,
8375                            "<ERROR:ARG_TOO_LONG:\\f",fn,"()>",NULL);
8376                 else
8377                   ckmakmsg(fnval,FNVALL,
8378                            "<ERROR:ARG_EVAL_FAILURE:\\f",fn,"()>",NULL);
8379             }
8380             goto fnend;
8381         }
8382         debug(F111,"fneval arg",bp[i],i);
8383     }
8384 
8385 #ifdef DEBUG
8386     if (deblog) {
8387         int j;
8388         debug(F110,"fneval",fn,0);
8389         for (j = 0; j < argn; j++) {
8390             debug(F111,"fneval arg post eval",argp[j],j);
8391             debug(F111,"fneval evaluated arg",bp[j],j);
8392         }
8393     }
8394 #endif /* DEBUG */
8395     {
8396 	/* Adjust argn for empty trailing arguments. */
8397 	/* For example when an arg is a variable name but the */
8398 	/* variable has no value.   July 2006. */
8399 	int j, old; char *p;
8400 	old = argn;
8401 	for (j = argn - 1; j >= 0; j--) {
8402 	    p = bp[j];
8403 	    if (!p)
8404 	      argn--;
8405 	    else if (!*p)
8406 	      argn--;
8407 	    else
8408 	      break;
8409 	}
8410 #ifdef DEBUG
8411 	if (argn != old)
8412 	  debug(F101,"fneval adjusted argn","",argn);
8413 #endif	/* DEBUG */
8414     }
8415 
8416 /*
8417   From this point on, bp[0..argn-1] are not NULL and all must be freed
8418   before returning.  BUT NOTE: if no arguments were given, bp[0] *is* NULL.
8419 */
8420     if (argn < 1) {                     /* Catch required args missing */
8421         switch (cx) {
8422           case FN_DEF:
8423           case FN_EVA:
8424           case FN_EXE:
8425           case FN_CHR:
8426        /* case FN_COD: */
8427           case FN_MAX:
8428           case FN_MIN:
8429           case FN_MOD:
8430           case FN_FD:
8431           case FN_FS:
8432           case FN_TOD:
8433           case FN_FFN:
8434           case FN_BSN:
8435           case FN_RAW:
8436           case FN_CMD:
8437           case FN_2HEX:
8438           case FN_2OCT:
8439           case FN_DNAM:
8440 #ifdef FN_ERRMSG
8441           case FN_ERRMSG:
8442 #endif /* FN_ERRMSG */
8443 #ifdef CK_KERBEROS
8444           case FN_KRB_TK:
8445           case FN_KRB_NX:
8446           case FN_KRB_IV:
8447           case FN_KRB_TT:
8448           case FN_KRB_FG:
8449 #endif /* CK_KERBEROS */
8450           case FN_MJD2:
8451           case FN_N2TIM:
8452           case FN_DIM:
8453           case FN_DATEJ:
8454           case FN_PNCVT:
8455           case FN_PERM:
8456           case FN_ALOOK:
8457           case FN_TLOOK:
8458           case FN_ABS:
8459           case FN_AADUMP:
8460           case FN_JOIN:
8461 #ifdef CKFLOAT
8462           case FN_FPABS:
8463           case FN_FPEXP:
8464           case FN_FPLOG:
8465           case FN_FPLN:
8466           case FN_FPMOD:
8467           case FN_FPSQR:
8468           case FN_FPADD:
8469           case FN_FPDIV:
8470           case FN_FPMUL:
8471           case FN_FPPOW:
8472           case FN_FPSUB:
8473           case FN_FPINT:
8474           case FN_FPROU:
8475           case FN_FPSIN:
8476           case FN_FPCOS:
8477           case FN_FPTAN:
8478 #endif /* CKFLOAT */
8479 #ifdef TCPSOCKET
8480           case FN_HSTADD:
8481           case FN_HSTNAM:
8482 #endif /* TCPSOCKET */
8483           case FN_DELSEC:
8484 #ifdef COMMENT
8485           case FN_KWVAL:
8486           case FN_SLEEP:
8487           case FN_MSLEEP:
8488 #endif /* COMMENT */
8489 #ifdef NT
8490           case FN_SNAME:
8491           case FN_LNAME:
8492 #endif /* NT */
8493 	  case FN_FILEINF:
8494 	  case FN_FILECMP:
8495             failed = 1;
8496             p = fnval;
8497             if (fndiags)
8498               ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
8499             goto fnend;
8500         }
8501     }
8502     p = fnval;                          /* Reset these again. */
8503     fnval[0] = NUL;
8504 
8505     switch (cx) {                       /* Do function on expanded args. */
8506 #ifdef TCPSOCKET
8507       case FN_HSTADD:
8508         p = ckaddr2name(bp[0]);
8509         goto fnend;
8510       case FN_HSTNAM:
8511         p = ckname2addr(bp[0]);
8512         goto fnend;
8513 #endif /* TCPSOCKET */
8514 
8515       case FN_DEF:                      /* \fdefinition(arg1) */
8516         k = isaarray(bp[0]) ?
8517 	    mxxlook(mactab,bp[0],nmac) :
8518 		mxlook(mactab,bp[0],nmac);
8519         p = (k > -1) ? mactab[k].mval : "";
8520         goto fnend;
8521 
8522       case FN_EVA:                      /* \fevaluate(arg1) */
8523         p = *(bp[0]) ? evalx(bp[0]) : "";
8524         if (!*p && fndiags) {
8525             failed = 1;
8526             p = fnval;
8527             evalerr(fn);
8528         }
8529         goto fnend;
8530 
8531       case FN_EXE:                      /* \fexecute(arg1) */
8532         j = (int)strlen(s = bp[0]);     /* Length of macro invocation */
8533         p = "";                         /* Initialize return value to null */
8534         if (j) {                        /* If there is a macro to execute */
8535             while (*s == SP) s++,j--;   /* strip leading spaces */
8536             p = s;                      /* remember beginning of macro name */
8537             for (i = 0; i < j; i++) {   /* find end of macro name */
8538                 if (*s == SP)
8539                   break;
8540                 s++;
8541             }
8542             if (*s == SP)       {       /* if there was a space after */
8543                 *s++ = NUL;             /* terminate the macro name */
8544                 while (*s == SP) s++;   /* skip past any extra spaces */
8545             } else
8546               s = "";                   /* maybe there are no arguments */
8547             if (p && *p) {
8548                 k = mlook(mactab,p,nmac); /* Look up the macro name */
8549                 debug(F111,"fexec mlook",p,k);
8550             } else
8551               k = -1;
8552             if (k < 0) {
8553                 char * p2 = p;
8554                 failed = 1;
8555                 p = fnval;
8556                 if (fndiags)
8557                   ckmakxmsg(fnval,FNVALL,
8558                             "<ERROR:NO_SUCH_MACRO:\\f",fn,"(",p2,")>",
8559                             NULL,NULL,NULL,NULL,NULL,NULL,NULL);
8560                 goto fnend;
8561             }
8562 /*
8563   This is just a WEE bit dangerous because we are copying up to 9 arguments
8564   into the space reserved for one.  It won't overrun the buffer, but if there
8565   are lots of long arguments we might lose some.  The other problem is that if
8566   the macro has more than 3 arguments, the 4th through last are all
8567   concatenated onto the third.  (The workaround is to use spaces rather than
8568   commas to separate them.)  Leaving it like this to avoid having to allocate
8569   tons more buffers.
8570 */
8571             if (argn > 1) {             /* Commas used instead of spaces */
8572                 int i;
8573                 char *p = bp[0];        /* Reuse this space */
8574                 *p = NUL;               /* Make into dodo() arg list */
8575                 for (i = 1; i < argn; i++) {
8576                     ckstrncat(p,bp[i],MAXARGLEN);
8577                     ckstrncat(p," ",MAXARGLEN);
8578                 }
8579                 s = bp[0];              /* Point to new list */
8580             }
8581             p = "";                     /* Initialize return value */
8582             if (k >= 0) {               /* If macro found in table */
8583                 /* Go set it up (like DO cmd) */
8584                 if ((j = dodo(k,s,cmdstk[cmdlvl].ccflgs)) > 0) {
8585                     if (cmpush() > -1) { /* Push command parser state */
8586                         extern int ifc;
8587                         int ifcsav = ifc; /* Push IF condition on stack */
8588                         k = parser(1);  /* Call parser to execute the macro */
8589                         cmpop();        /* Pop command parser */
8590                         ifc = ifcsav;   /* Restore IF condition */
8591                         if (k == 0) {   /* No errors, ignore action cmds. */
8592                             p = mrval[maclvl+1]; /* If OK, set return value. */
8593                             if (p == NULL) p = "";
8594                         }
8595                     } else {            /* Can't push any more */
8596                         debug(F100,"zzstring fneval fexec failure","",0);
8597                         printf("\n?\\fexec() too deeply nested\n");
8598                         while (cmpop() > -1) ;
8599                         p = "";
8600                     }
8601                 }
8602             }
8603         }
8604         debug(F110,"zzstring fneval fexecute final p",p,0);
8605         goto fnend;
8606 
8607 #ifdef RECURSIVE
8608       case FN_RDIR:                     /* \frdir..() - Recursive dir count */
8609       case FN_RFIL:                     /* \frfiles() - Recursive file count */
8610         /* recursive = 2; */            /* fall thru... */
8611 #endif /* RECURSIVE */
8612       case FN_FC:                       /* \ffiles() - File count. */
8613       case FN_DIR: {                    /* \ffdir.() - Directory count. */
8614           char abuf[16], *s;
8615           char ** ap = NULL;
8616           int x, xflags = 0;
8617           if (matchdot)
8618             xflags |= ZX_MATCHDOT;
8619           if (cx == FN_RDIR || cx == FN_RFIL) {
8620               xflags |= ZX_RECURSE;
8621 #ifdef CKSYMLINK
8622               /* Recursive - don't follow symlinks */
8623               xflags |= ZX_NOLINKS;
8624 #endif /* CKSYMLINK */
8625           }
8626           failed = 0;
8627           if (argn < 1) {
8628               p = "0";
8629               goto fnend;
8630           }
8631           if (cx == FN_DIR || cx == FN_RDIR) { /* Only list directories */
8632 	      debug(F100,"FN_DIR or FN_RDIR","",0);
8633               xflags |= ZX_DIRONLY;
8634 #ifdef OS2
8635               zxpn = 1;                 /* Use the alternate list */
8636 #endif /* OS2 */
8637           } else {                      /* List only files */
8638 	      debug(F100,"Not FN_DIR or FN_RDIR","",0);
8639               xflags |= ZX_FILONLY;
8640 #ifdef OS2
8641               zxpn = 1;                 /* Use the alternate list */
8642 #endif /* OS2 */
8643           }
8644           if (*(bp[0])) {
8645               k = nzxpand(bp[0],xflags);
8646               if (k < 0) k = 0;
8647               sprintf(fnval,"%d",k);    /* SAFE */
8648               p = fnval;
8649           } else
8650             p = "0";
8651 
8652           if (argn > 1) {               /* Assign list to array */
8653               fnval[0] = NUL;           /* Initial return value */
8654               ckstrncpy(abuf,bp[1],16); /* Get array reference */
8655               s = abuf;
8656               if (*s == CMDQ) s++;
8657               failed = 1;               /* Assume it's bad */
8658               p = fnval;                /* Point to result */
8659               if (fndiags)              /* Default is this error message */
8660                 ckmakmsg(fnval,FNVALL,
8661                          "<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
8662               if (s[0] != '&')          /* "Address" of array */
8663                 goto fnend;
8664               if (s[2])
8665                 if (s[2] != '[' || s[3] != ']')
8666                   goto fnend;
8667               if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */
8668                 s[1] += 32;
8669               if ((x = dclarray(s[1],k)) < 0) /* File list plus count */
8670                 goto fnend;
8671               failed = 0;               /* Unset failure flag */
8672               ap = a_ptr[x];            /* Point to array we just declared */
8673               sprintf(fnval,"%d",k);    /* SAFE */
8674           }
8675 #ifdef OS2
8676           if (ap) {                     /* We are making an array */
8677               int i;
8678               char tmp[16];
8679               ap[0] = NULL;             /* Containing number of files    */
8680               makestr(&(ap[0]),ckitoa(k));
8681 
8682               ckstrncpy(tmp,fnval,16);  /* Save return value */
8683 
8684               for (i = 1; i <= k; i++) { /* Fill it */
8685                   ap[i] = NULL;
8686                   znext(fnval);         /* Next filename */
8687                   if (!*fnval)          /* No more, done */
8688                     break;              /* In case a premature end */
8689                   makestr(&(ap[i]),fnval);
8690               }
8691 #ifdef ZXREWIND
8692               k = zxrewind();           /* Reset the file expansion */
8693 #else
8694               k = nzxpand(bp[0],xflags);
8695 #endif /* ZXREWIND */
8696               ckstrncpy(fnval,tmp,FNVALL); /* Restore return value */
8697           }
8698 #else /* OS2 */
8699           {                             /* Make copies of the list */
8700               int i; char tmp[16];
8701               if (flist) {              /* Free old file list, if any */
8702                   for (i = 0; flist[i]; i++) { /* and each string */
8703                       free(flist[i]);
8704                       flist[i] = NULL;
8705                   }
8706                   free((char *)flist);
8707               }
8708               ckstrncpy(tmp,fnval,16);  /* Save our return value */
8709               flist = (char **) malloc((k+1) * sizeof(char *)); /* New array */
8710               if (flist) {
8711                   for (i = 0; i <= k; i++) { /* Fill it */
8712                       flist[i] = NULL;
8713                       znext(fnval);     /* Next filename */
8714                       if (!*fnval)      /* No more, done */
8715                         break;
8716                       makestr(&(flist[i]),fnval);
8717                   }
8718                   if (ap) {             /* If array pointer given */
8719                       ap[0] = NULL;
8720                       makestr(&(ap[0]),ckitoa(k));
8721                       for (i = 0; i < k; i++) { /* Copy file list to array */
8722                           ap[i+1] = NULL;
8723                           makestr(&(ap[i+1]),flist[i]);
8724                       }
8725                   }
8726               }
8727               ckstrncpy(fnval,tmp,FNVALL); /* Restore return value */
8728               flistn = 0;               /* Reset global list pointer */
8729           }
8730 #endif /* OS2 */
8731 #ifdef RECURSIVE
8732           recursive = rsave;
8733 #endif /* RECURSIVE */
8734 #ifdef OS2
8735           zxpn = zsave;
8736 #endif /* OS2 */
8737       }
8738       goto fnend;
8739 
8740       case FN_FIL:                      /* \fnextfile() - Next file in list. */
8741         p = fnval;                      /* (no args) */
8742         *p = NUL;
8743 #ifdef OS2
8744         zxpn = 1;                       /* OS/2 - use the alternate list */
8745         znext(p);                       /* Call system-dependent function */
8746         zxpn = zsave;                   /* Restore original list */
8747 #else
8748         if (flist)                      /* Others, use our own list. */
8749           if (flist[flistn])
8750 	    p = flist[flistn++];
8751 #endif /* OS2 */
8752         goto fnend;
8753 
8754     } /* Break up big switch... */
8755 
8756     switch (cx) {
8757       case FN_IND:                      /* \findex(s1,s2,start,occurrence) */
8758       case FN_RIX:                      /* \frindex(s1,s2,start,occurrence) */
8759       case FN_SEARCH:                   /* \fsearch(pat,string,start,occ) */
8760       case FN_RSEARCH:			/* \frsearch(pat,string,start,occ) */
8761       case FN_COUNT: {			/* \fcount(s1,s2,start) */
8762 	int i = 0, right = 0, search = 0, count = 0;
8763 	int desired = 1;
8764         right = (cx == FN_RIX || cx == FN_RSEARCH);
8765         search = (cx == FN_SEARCH || cx == FN_RSEARCH);
8766 	count = (cx == FN_COUNT);
8767         p = "0";
8768         if (argn > 1) {                 /* Only works if we have 2 or 3 args */
8769             int start = 0;
8770             char * pat = NULL;
8771             len1 = (int)strlen(pat = bp[0]); /* length of string to look for */
8772             len2 = (int)strlen(s = bp[1]); /* length of string to look in */
8773             if (len1 < 1 || len2 < 1)   /* Watch out for empty strings */
8774               goto fnend;
8775             start = right ? -1 : 0;     /* Default starting position */
8776             if (argn > 2) {
8777                 val1 = *(bp[2]) ? evalx(bp[2]) : "1";
8778 		if (argn > 3) {         /* Occurrence */
8779 		    val2 = *(bp[3]) ? evalx(bp[3]) : "1";
8780 		    if (chknum(val2)) desired = atoi(val2);
8781 		    if (desired * len1 > len2) goto fnend;
8782 		}
8783                 if (chknum(val1)) {
8784                     int t;
8785                     t = atoi(val1);
8786                     if (!search) {      /* Index or Rindex */
8787                         j = len2 - len1; /* Length difference */
8788                         t--;             /* Convert position to 0-based */
8789                         if (t < 0) t = 0;
8790                         start = t;
8791                         if (!right && start < 0) start = 0;
8792                     } else {            /* Search or Rsearch */
8793                         int x;
8794                         if (t < 0) t = 0;
8795                         if (right) {    /* Right to left */
8796                             if (t > len2) t = len2;
8797                             start = len2 - t - 1;
8798                             if (start < 0)
8799                               goto fnend;
8800                             x = len2 - t;
8801                             s[x] = NUL;
8802                         } else {        /* Left to right */
8803                             start = t - 1;
8804                             if (start < 0) start = 0;
8805                             if (start >= len2)
8806                               goto fnend;
8807                         }
8808                     }
8809                 } else {
8810                     failed = 1;
8811                     evalerr(fn);
8812                     p = fnval;
8813                     goto fnend;
8814                 }
8815             }
8816 	    if (count) {		/* \fcount() */
8817 		int j;
8818 		for (i = 0; start < len2; i++) {
8819 		    j = ckindex(pat,bp[1],start,0,inpcas[cmdlvl]);
8820 		    if (j == 0) break;
8821 		    start = j;
8822 		}
8823 
8824 	    } else if (search) {	/* \fsearch() or \frsearch() */
8825 
8826                 if (right && pat[0] == '^') {
8827                     right = 0;
8828                     start = 0;
8829                 }
8830                 if (right) {		/* From right */
8831 		    int k, j = 1;
8832                     if (start < 0)
8833 		      start = len2 - 1;
8834 		    i = 0;
8835 		    while (start >= 0 && j <= desired) {
8836 			for (i = start;
8837 			     (i >= 0) &&
8838 				 !(k = ckmatch(pat,s+i,inpcas[cmdlvl],1+4));
8839 			     i--) ;
8840 			if (k < 1) {	/* No match */
8841 			    i = 0;
8842 			    break;
8843 			}
8844 			if (j == desired) { /* The match we want? */
8845 			    i += k;	/* Yes, return string index */
8846 			    break;
8847 			}
8848 			j++;		/* No, count this match */
8849 			s[i] = NUL;	/* null it out */
8850 			start = i-1;	/* move left and look again */
8851 		    }
8852 
8853                 } else {		/* From left */
8854 		    int j;
8855 		    i = 0;
8856 		    for (j = 1; j <= desired && start < len2; j++) {
8857 			i = ckmatch(pat,&s[start],inpcas[cmdlvl],1+4);
8858 			if (i == 0 || j == desired) break;
8859 			start += i + 1;
8860 		    }
8861 		    if (j == desired && i != 0)
8862 		      i += start;
8863 		    else
8864 		      i = 0;
8865                 }
8866             } else {			/* index or rindex */
8867 		int j = 0;
8868 		i = 0;
8869 		for (j = 1; j <= desired && start < len2; j++) {
8870 		    i = ckindex(pat,bp[1],start,right,inpcas[cmdlvl]);
8871 		    if (i == 0 || j == desired) break;
8872 		    start = (right) ? len2 - i + 1 : i;
8873 		}
8874 		if (j != desired)
8875 		  i = 0;
8876             }
8877             sprintf(fnval,"%d",i);      /* SAFE */
8878             p = fnval;
8879         }
8880         goto fnend;
8881       }
8882 
8883       case FN_RPL:                      /* \freplace(s1,s2,s3) */
8884       /*
8885         s = bp[0] = source string (len1)
8886             bp[1] = match string (len2)
8887             bp[2] = replacement string (len3)
8888             bp[3] = which occurrence (default = all);
8889             bp[4] = context sensitive ("word mode")  20171005
8890             fnval = p = destination (result) string
8891       */
8892         if (argn < 1)                   /* Nothing */
8893           goto fnend;
8894         if (argn < 2) {                 /* Need at least two args */
8895             ckstrncpy(p,bp[0],FNVALL);
8896         } else {
8897             int occur = 0, xx = 0, context = 0, j2;
8898             int left = 0, right = 0, ok = 0;
8899             len1 = (int)strlen(bp[0]);  /* length of string to look in */
8900             len2 = (int)strlen(bp[1]);  /* length of string to look for */
8901             len3 = (argn < 3) ? 0 : (int)strlen(bp[2]); /* Len of replacemnt */
8902 
8903             j = len1 - len2 + 1;        /* source - match + 1  */
8904             j2 = j;
8905             if (argn > 3) {
8906                 if (chknum(bp[3])) {
8907                     occur = atoi(bp[3]);
8908                 } else {
8909                     failed = 1;
8910                     if (fndiags)
8911                       ckmakmsg(fnval,FNVALL,
8912                                "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
8913                     goto fnend;
8914                 }
8915             }
8916 /*
8917   Context-sensitive "word mode" replace, new for C-Kermit 9.0.304 Dev.23,
8918   October 2017.  Can be removed by defining NORPLWORDMODE.
8919 */
8920 #ifdef RPLWORDMODE
8921             if (argn > 4) {
8922                 if (chknum(bp[4])) {
8923                     context = atoi(bp[4]);
8924                 } else {
8925                     failed = 1;
8926                     if (fndiags)
8927                       ckmakmsg(fnval,FNVALL,
8928                                "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
8929                     goto fnend;
8930                 }
8931             }
8932 #endif  /* RPLWORDMODE */
8933             /* If args out of whack... */
8934             if (j < 1 || len1 == 0 || len2 == 0) {
8935                 ckstrncpy(p,bp[0],FNVALL); /* just return original string */
8936                 p[FNVALL] = NUL;
8937             } else {
8938               ragain:
8939                 s = bp[0];           /* Point to beginning of source string */
8940 
8941                 xx = 0;       		/* Match counter */
8942                 while (*s) {            /* For each character in it...*/
8943                     /* Compare current segment with target string */
8944                     if (!ckstrcmp(bp[1],s,len2,inpcas[cmdlvl])) {
8945                         ok = 1;         /* Assume OK to replace */
8946 #ifdef RPLWORDMODE
8947                         if (context) {  /* New 2017-10-05 */
8948                             CHAR c;
8949                             left = 0;
8950                             right = 0;
8951                             ok = 0;   /* Mustcheck context before replacing */
8952                             if (!strncmp(bp[1],"...",len2)) {
8953                                 /* Special case for ellipsis */
8954                                 if (s > bp[0]) { /* Can't begin a line */
8955                                     c = *(s-1);   /* Check preceding char */
8956                                     if (c != 32 && c != '.')
8957                                       left = 1;
8958                                 }
8959                                 c = *(s+len2); /* Check following char */
8960                                 if (c != '.' && (c == SP || c == '\0'))
8961                                   right = 1;
8962                             } else if (isalphanum(bp[1])) {
8963                                 /* Target string is alphanumeric... */
8964                                 if (s == bp[0]) { /* At beginning of string */
8965                                     left = 1;     /* So left boundary ok */
8966                                 } else {          /* Otherwise */
8967                                     c = *(s-1);   /* Check preceding char */
8968                                     if (cnonalphanum(c)) /* If not alphamum */
8969                                       left = 2;  /* left boundary ok */
8970                                 }
8971                                 c = *(s+len2); /* Check following character */
8972                                 if (c == '\0') /* If end of string */
8973                                   right = 1;   /* Right boundary OK */
8974                                 else if (cnonalphanum(c))
8975                                   right = 2; /* Right boundary OK */
8976                             } else if (nonalphanum(bp[1])) {
8977                                 /* Target is non-salphanumeric */
8978                                 if (s == bp[0]) { /* At beginning of line */
8979                                     left = 1;     /* Left OK */
8980                                 } else {          /* Otherwise */
8981                                     c = *(s-1);   /* Check preceding char */
8982                                     if (cisalphanum(c) || c == SP)
8983                                       left = 2; /* Left OK */
8984                                 }
8985                                 c = *(s+len2); /* Check char after target */
8986                                 if (c == '\0') { /* At end of string */
8987                                     right = 1;   /* Right OK */
8988                                 } else {         /* Otherwise */
8989                                     if (cisalphanum(c) || c <= SP)
8990                                       right = 2; /* Right ok */
8991                                 }
8992                             }
8993                             /* If none of the above nothing is replaced */
8994                             if (left && right) { /* Match accepted */
8995                                 ok = 1;          /* OK to replace */
8996                                 xx++;   /* Count this match */
8997                             }
8998                         } else
8999 #endif  /* RPLWORDMODE */
9000                           xx++;     /* Straight replace - count every match */
9001 
9002                         if (ok && (occur == 0 || occur == xx)) {
9003                             if (len3) {
9004                                 ckstrncpy(p,bp[2],FNVALL);
9005                                 p += len3;
9006                             }
9007                             s += len2;      /* and skip past it. */
9008                         } else {            /* matched but not selected */
9009                             *p++ = *s++;    /* Just copy this character */
9010                         }
9011                     } else {            /* Didn't match */
9012                         *p++ = *s++;    /* just copy this character */
9013                     }
9014                 }
9015                 *p = NUL;
9016                 while ((*p++ = *s++));  /* Append the rest */
9017                 if (occur < 0) {        /* cheap... */
9018                     occur = xx + occur + 1;
9019                     xx = 0;
9020                     p = fnval;
9021                     j = j2;
9022                     if (occur > 0)
9023                       goto ragain;
9024                 }
9025             }
9026         }
9027         p = fnval;
9028         goto fnend;
9029 
9030       case FN_CHR:                      /* \fcharacter(arg1) */
9031         val1 = *(bp[0]) ? evalx(bp[0]) : "";
9032         if (chknum(val1)) {             /* Must be numeric */
9033             i = atoi(val1);
9034             if (i >= 0 && i < 256) {    /* Must be an 8-bit value */
9035                 p = fnval;
9036                 *p++ = (char) i;
9037                 *p = NUL;
9038                 p = fnval;
9039             } else {
9040                 failed = 1;
9041                 if (fndiags)
9042                   ckmakmsg(fnval,FNVALL,
9043                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
9044             }
9045         } else {
9046             failed = 1;
9047             evalerr(fn);
9048         }
9049         goto fnend;
9050 
9051       case FN_COD:                      /* \fcode(string) */
9052         /*
9053           returns decimal character value of first char in string.
9054           or 0 if string empty or no string given.
9055         */
9056         p = "0";
9057         if (!bp[0]) goto fnend;         /* No argument given */
9058         if ((int)strlen(bp[0]) < 1)     /* Empty argument */
9059           goto fnend;
9060         i = (int) bp[0][0];
9061         debug(F111,"FN_CODE",bp[0],i);
9062         p = fnval;
9063         sprintf(p,"%d",(i & 0xff)); /* SAFE */
9064         debug(F111,"FN_CODE fnval",fnval,i);
9065         goto fnend;
9066 
9067       case FN_LEN:                      /* \flength(arg1) */
9068         if (argn > 0) {
9069             p = fnval;
9070             sprintf(p,"%d",(int)strlen(bp[0])); /* SAFE */
9071         } else p = "0";
9072         goto fnend;
9073 
9074       case FN_LOW:                      /* \flower(arg1) */
9075         s = bp[0] ? bp[0] : "";
9076         p = fnval;
9077         while (*s) {
9078             if (isupper(*s))
9079               *p = (char) tolower(*s);
9080             else
9081               *p = *s;
9082             p++; s++;
9083         }
9084         *p = NUL;
9085         p = fnval;
9086         goto fnend;
9087 
9088       case FN_MAX:                      /* \fmax(arg1,arg2) */
9089       case FN_MIN:                      /* \fmin(arg1,arg2) */
9090       case FN_MOD:                      /* \fmod(arg1,arg2) */
9091         val1 = *(bp[0]) ? evalx(bp[0]) : "";
9092 #ifdef COMMENT
9093         /* No longer necessary because evalx() no longer overwrites its */
9094         /* result every time it's called (2000/09/23). */
9095         free(bp[0]);
9096         bp[0] = NULL;
9097 #endif /* COMMENT */
9098         if (argn > 1) {
9099 #ifdef COMMENT
9100             /* Ditto... */
9101             bp[0] = malloc((int)strlen(val1)+1);
9102             if (bp[0])
9103               strcpy(bp[0],val1);       /* safe */
9104             val1 = bp[0];
9105 #endif /* COMMENT */
9106             val2 = *(bp[1]) ? evalx(bp[1]) : "";
9107             if (chknum(val1) && chknum(val2)) {
9108                 i = atoi(val1);
9109                 j = atoi(val2);
9110                 switch (y) {
9111                   case FN_MAX:
9112                     if (j < i) j = i;
9113                     break;
9114                   case FN_MIN:
9115                     if (j > i) j = i;
9116                     break;
9117                   case FN_MOD:
9118                     if (j == 0) {
9119                         failed = 1;
9120                         if (fndiags)
9121                           ckmakmsg(fnval,FNVALL,
9122                                    "<ERROR:DIVIDE_BY_ZERO:\\f",fn,"()>",NULL);
9123                         else
9124                           fnval[0] = NUL;
9125                         goto fnend;
9126                     } else
9127                       j = i % j;
9128                 }
9129                 p = fnval;
9130                 sprintf(p,"%d",j);      /* SAFE */
9131             } else {
9132                 failed = 1;
9133                 evalerr(fn);
9134             }
9135         } else p = val1;
9136         goto fnend;
9137     } /* Break up big switch... */
9138 
9139     switch (y) {
9140       case FN_SUB:                      /* \fsubstr(arg1,arg2,arg3) */
9141       case FN_RIG:                      /* \fright(arg1,arg2) */
9142       case FN_LEF:                      /* \fleft(arg1,arg2) */
9143         if (argn < 1)
9144           goto fnend;
9145         val1 = "";
9146         if (argn > 1)
9147           if (*(bp[1]))
9148             val1 =  evalx(bp[1]);
9149 #ifdef COMMENT
9150         if (bp[1]) free(bp[1]);         /* Have to copy this */
9151         bp[1] = malloc((int)strlen(val1)+1);
9152         if (!bp[1]) {
9153             failed = 1;
9154             if (fndiags) {
9155                 p = fnval;
9156                 ckmakmsg(fnval,FNVALL,
9157                          "<ERROR:MALLOC_FAILURE:\\f",fn,"()>",NULL);
9158             }
9159             goto fnend;
9160         }
9161         strcpy(bp[1],val1);             /* safe */
9162         val1 = bp[1];
9163 #endif /* COMMENT */
9164         val2 = "";
9165         if (argn > 2)
9166           if (*(bp[2]))
9167             val2 = evalx(bp[2]);
9168         if (
9169             ((argn > 1) && (int)strlen(val1) && !rdigits(val1)) ||
9170             ((cx == FN_SUB) &&
9171               ((argn > 2) && (int)strlen(val2) && !rdigits(val2)))
9172             ) {
9173             failed = 1;
9174             evalerr(fn);
9175         } else {
9176             int lx;
9177             p = fnval;                  /* pointer to result */
9178             lx = strlen(bp[0]);         /* length of arg1 */
9179             if (cx == FN_SUB) {         /* substring */
9180                 k = (argn > 2) ? atoi(val2) : MAXARGLEN; /* length */
9181                 j = (argn > 1) ? atoi(val1) : 1; /* start pos for substr */
9182             } else if (cx == FN_LEF) {  /* left */
9183                 k = (argn > 1) ? atoi(val1) : lx;
9184                 j = 1;
9185             } else {                             /* right */
9186                 k = (argn > 1) ? atoi(val1) : lx; /* length */
9187                 j = lx - k + 1;                  /* start pos for right */
9188                 if (j < 1) j = 1;
9189             }
9190             if (k > 0 && j <= lx) {              /* if start pos in range */
9191                 s = bp[0]+j-1;                   /* point to source string */
9192                 for (i = 0; (i < k) && (*p++ = *s++); i++) ;  /* copy */
9193             }
9194             *p = NUL;                   /* terminate the result */
9195             p = fnval;                  /* and point to it. */
9196         }
9197         goto fnend;
9198 
9199       case FN_UPP:			/* \fupper(arg1) */
9200         s = bp[0] ? bp[0] : "";
9201         p = fnval;
9202         while (*s) {
9203             if (islower(*s))
9204               *p = (char) toupper(*s);
9205             else
9206               *p = *s;
9207             p++; s++;
9208         }
9209         *p = NUL;
9210         p = fnval;
9211         goto fnend;
9212 
9213       case FN_REP:                      /* \frepeat(text,number) */
9214         if (argn < 1)
9215           goto fnend;
9216         val1 = "1";
9217         if (argn > 1)
9218           if (*(bp[1]))
9219             val1 = evalx(bp[1]);
9220         if (chknum(val1)) {             /* Repeat count */
9221             n = atoi(val1);
9222             debug(F111,"SUNDAY frepeat n",val1,n);
9223             if (n > 0) {                /* Make n copies */
9224                 p = fnval;
9225                 *p = '\0';
9226                 k = (int)strlen(bp[0]); /* Make sure string has some length */
9227                 debug(F111,"SUNDAY frepeat k","",k);
9228                 debug(F111,"SUNDAY frepeat FNVALL","",FNVALL);
9229                 if (k * n >= FNVALL) {  /* But not too much... */
9230                     failed = 1;
9231                     if (fndiags)
9232                       ckmakmsg(fnval,FNVALL,
9233                                "<ERROR:RESULT_TOO_LONG:\\f",fn,"()>",NULL);
9234                     else
9235                       fnval[0] = NUL;
9236                     p = fnval;
9237                     goto fnend;
9238                 }
9239                 if (k > 0) {            /* If there is something to copy */
9240                     for (i = 0; i < n; i++) { /* Copy loop */
9241                         s = bp[0];
9242                         for (j = 0; j < k; j++) {
9243                             if ((p - fnval) >= FNVALL)
9244                               break;    /* shouldn't happen... */
9245                             else
9246                               *p++ = *s++;
9247                         }
9248                     }
9249                     *p = NUL;
9250                 }
9251             }
9252         } else {
9253             failed = 1;
9254             evalerr(fn);
9255         }
9256         p = fnval;
9257         goto fnend;
9258 
9259 #ifndef NOFRILLS
9260       case FN_REV:                      /* \freverse() */
9261         if (argn < 1)
9262           goto fnend;
9263         yystring(bp[0],&p);
9264         goto fnend;
9265 #endif /* NOFRILLS */
9266 
9267       case FN_RPA:                      /* \frpad() and \flpad() */
9268       case FN_LPA:
9269         if (argn < 1)
9270           goto fnend;
9271         val1 = "";
9272         if (argn > 1)
9273           if (*(bp[1]))
9274             val1 = evalx(bp[1]);
9275         if (argn == 1) {                /* If a number wasn't given */
9276             p = fnval;                  /* just return the original string */
9277             ckstrncpy(p,bp[0],FNVALL);
9278         } else if (argn > 1 &&  !*val1) {
9279             failed = 1;
9280             evalerr(fn);
9281         } else /* if (chknum(val1)) */ { /* Repeat count */
9282             char pc;
9283             n = atoi(val1);
9284             if (n >= 0) {
9285                 p = fnval;
9286                 k = (int)strlen(bp[0]); /* Length of string to be padded */
9287                 if (k >= n) {           /* It's already long enough */
9288                     ckstrncpy(p,bp[0],FNVALL);
9289                 } else {
9290                     if (n + k <= FNVALL) {
9291                         pc = (char) ((argn < 3) ? SP : *bp[2]);
9292                         if (!pc) pc = SP;
9293                         if (cx == FN_RPA) { /* RPAD */
9294                             strncpy(p,bp[0],k); /* (leave it like this) */
9295                             p[k] = NUL;
9296                             p += k;
9297                             for (i = k; i < n; i++)
9298                               *p++ = pc;
9299                         } else {        /* LPAD */
9300                             n -= k;
9301                             for (i = 0; i < n; i++)
9302                               *p++ = pc;
9303                             strncpy(p,bp[0],k); /* (leave it like this) */
9304                             p[k] = NUL;
9305                             p += k;
9306                         }
9307                     }
9308                     *p = NUL;
9309                 }
9310             }
9311         }
9312         p = fnval;
9313         goto fnend;
9314 
9315 #ifdef ZFCDAT
9316       case FN_FD:                       /* \fdate(filename) */
9317         p = fnval;
9318         s = zfcdat(bp[0]);
9319         if (!s) s = "";
9320         if (!*s) {
9321             failed = 1;
9322             if (fndiags)
9323               ckmakmsg(fnval,FNVALL,"<ERROR:FILE_NOT_FOUND:\\f",fn,"()>",NULL);
9324             goto fnend;
9325         }
9326         ckstrncpy(fnval,s,FNVALL);
9327 #endif /* ZFCDAT */
9328         goto fnend;
9329 
9330     } /* Break up big switch... */
9331 
9332     switch (y) {
9333       case FN_FS: {			/* \fsize(filename) */
9334 	  CK_OFF_T z;
9335 	  p = fnval;
9336 	  z = zchki(bp[0]);
9337 	  if (z < (CK_OFF_T)0) {
9338 	      failed = 1;
9339 	      if (fndiags) {
9340 		  if (z == (CK_OFF_T)-1)
9341 		    ckmakmsg(fnval,FNVALL,
9342 			     "<ERROR:FILE_NOT_FOUND:\\f",fn,"()>",NULL);
9343 		  else if (z == (CK_OFF_T)-2)
9344 		    ckmakmsg(fnval,FNVALL,
9345 			     "<ERROR:FILE_NOT_READABLE:\\f",fn,"()>",NULL);
9346 		  else if (z == (CK_OFF_T)-3)
9347 		    ckmakmsg(fnval,FNVALL,
9348 			     "<ERROR:FILE_NOT_ACCESSIBLE:\\f",fn,"()>",NULL);
9349 		  else
9350 		    ckmakmsg(fnval,FNVALL,
9351 			     "<ERROR:FILE_ERROR:\\f",fn,"()>",NULL);
9352 	      }
9353 	      goto fnend;
9354 	  }
9355 	  ckstrncpy(fnval,ckfstoa(z),FNVALL);
9356 	  goto fnend;
9357       }
9358       case FN_VER:                      /* \fverify() */
9359 	p = "-1";
9360 	if (argn == 1)			/* No second arg */
9361 	  goto fnend;
9362 	else if (!bp[1])		/* Or second arg null */
9363 	  goto fnend;
9364 	else if (!*(bp[1]))		/* or empty. */
9365 	  goto fnend;
9366         p = "0";
9367         if (argn > 1) {                 /* Only works if we have 2 or 3 args */
9368             int start;
9369             char *s2, ch1, ch2;
9370             start = 0;
9371             if (argn > 2) {             /* Starting position specified */
9372                 val1 = *(bp[2]) ? evalx(bp[2]) : "0";
9373                 if (chknum(val1)) {
9374                     start = atoi(val1) /* - 1 */;
9375                     if (start < 0) start = 0;
9376                     if (start > (int)strlen(bp[1]))
9377                       goto verfin;
9378                 } else {
9379                     failed = 1;
9380                     evalerr(fn);
9381                     goto fnend;
9382                 }
9383             }
9384             i = start;
9385             p = "0";
9386             for (s = bp[1] + start; *s; s++,i++) {
9387                 ch1 = *s;
9388                 if (!inpcas[cmdlvl]) if (islower(ch1)) ch1 = toupper(ch1);
9389                 j = 0;
9390                 for (s2 = bp[0]; *s2; s2++) {
9391                     ch2 = *s2;
9392                     if (!inpcas[cmdlvl]) if (islower(ch2)) ch2 = toupper(ch2);
9393                     if (ch1 == ch2) {
9394                         j = 1;
9395                         break;
9396                     }
9397                 }
9398                 if (j == 0) {
9399                     sprintf(fnval,"%d",i+1); /* SAFE */
9400                     p = fnval;
9401                     break;
9402                 }
9403             }
9404         }
9405       verfin:
9406         goto fnend;
9407 
9408       case FN_IPA:                      /* Find and return IP address */
9409         if (argn > 0) {                 /* in argument string. */
9410             int start = 0;
9411             if (argn > 1) {             /* Starting position specified */
9412                 if (chknum(bp[1])) {
9413                     start = atoi(bp[1]) - 1;
9414                     if (start < 0) start = 0;
9415                 } else {
9416                     failed = 1;
9417                     if (fndiags)
9418                       ckmakmsg(fnval,FNVALL,
9419                                "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9420                     goto fnend;
9421                 }
9422             }
9423             p = getip(bp[0]+start);
9424         } else p = "";
9425         goto fnend;
9426 
9427 #ifdef OS2
9428       case FN_CRY:
9429         p = "";
9430         if (argn > 0) {
9431             p = fnval;
9432             ckstrncpy(p,bp[0],FNVALL);
9433             ck_encrypt(p);
9434         }
9435         goto fnend;
9436 
9437       case FN_OOX:
9438         p = "";
9439         if (argn > 0)
9440           p = (char *) ck_oox(bp[0], (argn > 1) ? bp[1] : "");
9441         goto fnend;
9442 #endif /* OS2 */
9443 
9444       case FN_HEX:                      /* \fhexify(arg1) */
9445         if (argn < 1)
9446           goto fnend;
9447         if ((int)strlen(bp[0]) < (FNVALL / 2)) {
9448             s = bp[0];
9449             p = fnval;
9450             while (*s) {
9451                 x = (*s >> 4) & 0x0f;
9452                 *p++ = hexdigits[x];
9453                 x = *s++ & 0x0f;
9454                 *p++ = hexdigits[x];
9455             }
9456             *p = NUL;
9457             p = fnval;
9458         }
9459         goto fnend;
9460 
9461       case FN_UNTAB:			/* \funtab(arg1) */
9462 	if (argn < 1)
9463 	  goto fnend;
9464 	if ((int)strlen(bp[0]) < (FNVALL * 2)) {
9465 	    s = bp[0];
9466 	    p = fnval;
9467 	    if (untabify(bp[0],p,FNVALL) < 0) {
9468 		failed = 1;
9469 		if (fndiags)
9470 		  ckmakmsg(fnval,FNVALL,
9471 			   "<ERROR:OVERFLOW:\\f",fn,"()>",NULL);
9472 	    }
9473 	    goto fnend;
9474 	}
9475 
9476       case FN_UNH: {                    /* \funhex(arg1) */
9477           int c[2], i;
9478           if (argn < 1)
9479             goto fnend;
9480           if ((int)strlen(bp[0]) < (FNVALL * 2)) {
9481               s = bp[0];
9482               p = fnval;
9483               while (*s) {
9484                   for (i = 0; i < 2; i++) {
9485                       c[i] = *s++;
9486                       if (!c[i]) { p = ""; goto unhexfin; }
9487                       if (islower(c[i])) c[i] = toupper(c[i]);
9488                       if (c[i] >= '0' && c[i] <= '9') {
9489                           c[i] -= 0x30;
9490                       } else if (c[i] >= 'A' && c[i] <= 'F') {
9491                           c[i] -= 0x37;
9492                       } else {
9493                           failed = 1;
9494                           if (fndiags)
9495                             ckmakmsg(fnval,
9496                                      FNVALL,
9497                                      "<ERROR:ARG_OUT_OF_RANGE:\\f",
9498                                      fn,
9499                                      "()>",
9500                                      NULL
9501                                      );
9502                           goto fnend;
9503                       }
9504                   }
9505                   *p++ = ((c[0] << 4) & 0xf0) | (c[1] & 0x0f);
9506               }
9507               *p = NUL;
9508               p = fnval;
9509           }
9510         unhexfin:
9511           goto fnend;
9512       }
9513 
9514       case FN_BRK: {                    /* \fbreak() */
9515           char * c;                     /* Characters to break on */
9516           char c2, s2;
9517           int start = 0;
9518           int done = 0;
9519           if (argn < 1)
9520             goto fnend;
9521           if (argn > 2) {
9522               s = bp[2] ? bp[2] : "0";
9523               if (chknum(s)) {
9524                   start = atoi(s);
9525                   if (start < 0) start = 0;
9526                   if (start > (int)strlen(bp[0]))
9527                     goto brkfin;
9528               } else {
9529                   failed = 1;
9530                   if (fndiags)
9531                     ckmakmsg(fnval,FNVALL,
9532                              "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9533                   goto fnend;
9534               }
9535           }
9536           s = bp[0] + start;            /* Source pointer */
9537 
9538           while (*s && !done) {
9539               s2 = *s;
9540               if (!inpcas[cmdlvl] && islower(s2)) s2 = toupper(s2);
9541               c = bp[1] ? bp[1] : "";   /* Character to break on */
9542               while (*c) {
9543                   c2 = *c;
9544                   if (!inpcas[cmdlvl] && islower(c2)) c2 = toupper(c2);
9545                   if (c2 == s2) {
9546                       done = 1;
9547                       break;
9548                   }
9549                   c++;
9550               }
9551               if (done) break;
9552               *p++ = *s++;
9553           }
9554           *p = NUL;                     /* terminate the result */
9555           p = fnval;                    /* and point to it. */
9556         brkfin:
9557           goto fnend;
9558       }
9559 
9560       case FN_SPN: {                    /* \fspan() */
9561           char *q;
9562           char c1, c2;
9563           int start = 0;
9564           if (argn < 1)
9565             goto fnend;
9566           if (argn > 2) {               /* Starting position */
9567               s = bp[2] ? bp[2] : "0";
9568               if (chknum(s)) {
9569                   start = atoi(s);
9570                   if (start < 0) start = 0;
9571               } else {
9572                   failed = 1;
9573                   if (fndiags)
9574                     ckmakmsg(fnval,FNVALL,
9575                              "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9576                   goto fnend;
9577               }
9578           }
9579           s = bp[0] + start;            /* Source pointer */
9580           if (argn > 1 &&
9581               (int)strlen(bp[1]) > 0 &&
9582               start <= (int)strlen(bp[0])) {
9583               while (*s) {              /* Loop thru source string */
9584                   q = bp[1];            /* Span string */
9585                   c1 = *s;
9586                   if (!inpcas[cmdlvl])
9587                     if (islower(c1)) c1 = toupper(c1);
9588                   x = 0;
9589                   while ((c2 = *q++)) {
9590                       if (!inpcas[cmdlvl])
9591                         if (islower(c2)) c2 = toupper(c2);
9592                       if (c1 == c2) { x = 1; break; }
9593                   }
9594                   if (!x) break;
9595                   *p++ = *s++;
9596               }
9597               *p = NUL;                 /* Terminate and return the result */
9598               p = fnval;
9599           }
9600           goto fnend;
9601       }
9602     } /* Break up big switch... */
9603 
9604     switch (y) {
9605       case FN_TRM:                      /* \ftrim(s1[,s2]) */
9606       case FN_LTR:                      /* \fltrim(s1[,s2]) */
9607         if (argn < 1)
9608           goto fnend;
9609         if ((len1 = (int)strlen(bp[0])) > 0) {
9610             if (len1 > FNVALL)
9611               len1 = FNVALL;
9612             s = " \t\r\n";
9613             if (argn > 1)               /* Trim list given */
9614               s = bp[1];
9615             len2 = (int)strlen(s);
9616             if (len2 < 1) {             /* or not... */
9617                 s = " \t\r\n";          /* Default is to trim whitespace */
9618                 len2 = 2;
9619             }
9620             if (cx == FN_TRM) {         /* Trim from right */
9621                 char * q, p2, q2;
9622                 ckstrncpy(fnval,bp[0],FNVALL); /* Copy string to output */
9623                 p = fnval + len1 - 1;   /* Point to last character */
9624 
9625                 while (p >= (char *)fnval) { /* Go backwards */
9626                     q = s;              /* Point to trim list */
9627                     p2 = *p;
9628                     if (!inpcas[cmdlvl])
9629                       if (islower(p2)) p2 = toupper(p2);
9630                     while (*q) {        /* Is this char in trim list? */
9631                         q2 = *q;
9632                         if (!inpcas[cmdlvl])
9633                           if (islower(q2)) q2 = toupper(q2);
9634                         if (p2 == q2) { /* Yes, null it out */
9635                             *p = NUL;
9636                             break;
9637                         }
9638                         q++;
9639                     }
9640                     if (!*q)            /* Trim list exhausted */
9641                       break;            /* So we're done. */
9642                     p--;                /* Else keep trimming */
9643                 }
9644             } else {                    /* Trim from left */
9645                 char * q, p2, q2;
9646                 p = bp[0];              /* Source */
9647                 while (*p) {
9648                     p2 = *p;
9649                     if (!inpcas[cmdlvl])
9650                       if (islower(p2)) p2 = toupper(p2);
9651                     q = s;
9652                     while (*q) {        /* Is this char in trim list? */
9653                         q2 = *q;
9654                         if (!inpcas[cmdlvl])
9655                           if (islower(q2)) q2 = toupper(q2);
9656                         if (p2 == q2) { /* Yes, point past it */
9657                             p++;        /* and try next source character */
9658                             break;
9659                         }
9660                         q++;            /* No, try next trim character */
9661                     }
9662                     if (!*q)            /* Trim list exhausted */
9663                       break;            /* So we're done. */
9664                 }
9665                 ckstrncpy(fnval,p,FNVALL);
9666             }
9667             p = fnval;
9668         } else p = "";
9669         goto fnend;
9670 
9671       case FN_CAP:                      /* \fcapitalize(arg1) */
9672         if (argn < 1)
9673           goto fnend;
9674         s = bp[0];
9675         p = fnval;
9676         x = 0;
9677         while ((c = *s++)) {
9678             if (isalpha(c)) {
9679                 if (x == 0) {
9680                     x = 1;
9681                     if (islower(c))
9682                       c = toupper(c);
9683                 } else if (isupper(c))
9684                   c = tolower(c);
9685             }
9686             *p++ = c;
9687         }
9688         *p = NUL;
9689         p = fnval;
9690         goto fnend;
9691 
9692 #ifdef COMMENT
9693       case FN_TOD:                      /* Time of day to secs since midnite */
9694         sprintf(fnval,"%ld",tod2sec(bp[0])); /* SAFE */
9695         goto fnend;
9696 #endif /* COMMENT */
9697 
9698       case FN_FFN:                      /* Full pathname of file */
9699         zfnqfp(bp[0],FNVALL,p);
9700         if (!p) p = "";
9701         goto fnend;
9702 
9703       case FN_CHK: {                    /* \fchecksum() */
9704           long chk = 0;
9705           p = (argn > 0) ? bp[0] : "";
9706           while (*p) chk += *p++;
9707           sprintf(fnval,"%lu",chk);     /* SAFE */
9708           p = fnval;
9709           goto fnend;
9710       }
9711 
9712 #ifndef NOXFER
9713       case FN_CRC:                      /* \fcrc16() */
9714         if (argn > 0)
9715           sprintf(fnval,"%u",           /* SAFE */
9716                   chk3((CHAR *)bp[0],(int)strlen(bp[0])));
9717         else
9718           p = "0";
9719         goto fnend;
9720 #endif /* NOXFER */
9721 
9722       case FN_BSN:                      /* \fbasename() */
9723         zstrip(bp[0],&p);
9724         goto fnend;
9725 
9726 #ifndef NOLOCAL
9727 #ifdef OS2
9728       case FN_SCRN_CX:                  /* \fscrncurx() */
9729         p = fnval;
9730         sprintf(p,"%d",(int)VscrnGetCurPos(VTERM)->x); /* SAFE */
9731         goto fnend;
9732 
9733       case FN_SCRN_CY:                  /* \fscrncury() */
9734         p = fnval;
9735         sprintf(p,"%d",(int)VscrnGetCurPos(VTERM)->y); /* SAFE */
9736         goto fnend;
9737 
9738       case FN_SCRN_STR: {               /* \fscrnstr() */
9739           videoline * line = NULL;
9740           viocell * cells = NULL;
9741           int row = 0, col = 0, len = 0;
9742           /* NOTE: On Unicode systems, the screen contents are stored in */
9743           /* in Unicode.  Therefore, we should really be performing a    */
9744           /* conversion to the local character set.                      */
9745 
9746           /* 6/18/2000 - added the translation to lcs */
9747 
9748           if (bp[0] == NULL || bp[0][0] == '\0') {
9749               row = 0;
9750           } else {
9751               if (chknum(bp[0])) {
9752                   row = atoi(bp[0]);
9753                   if (row < 0)
9754                     row = 0;
9755               } else {
9756                   failed = 1;
9757                   if (fndiags)
9758                     ckmakmsg(fnval,FNVALL,
9759                              "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9760                   goto fnend;
9761               }
9762           }
9763           line = VscrnGetLineFromTop( VTERM, (USHORT) row );
9764           if (line != NULL) {
9765               if (bp[1] == NULL || bp[1][0] == '\0')
9766                 col = 0;
9767               else {
9768                   if (chknum(bp[0])) {
9769                       col = atoi(bp[1]);
9770                       if (col < 0 || col >= line->width)
9771                         col = 0;
9772                   } else {
9773                       failed = 1;
9774                       if (fndiags)
9775                         ckmakmsg(fnval,FNVALL,
9776                                  "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9777                       goto fnend;
9778                   }
9779               }
9780               if (bp[2] == NULL || bp[2][0] == '\0') {
9781                   len = line->width - (col+1);
9782               } else {
9783                   if (!chknum(bp[2])) {
9784                       failed = 1;
9785                       if (fndiags)
9786                         ckmakmsg(fnval,FNVALL,
9787                                  "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9788                       goto fnend;
9789                   }
9790                   len = atoi(bp[2]);
9791                   if (len < 0 || len > line->width)
9792                     len = line->width;
9793               }
9794               cells = line->cells;
9795               for (i = 0; i < len; i++) {
9796                   int pos = i + col;
9797                   if (pos < line->width) {
9798                       if (isunicode())
9799                         fnval[i] = (CHAR) utolxlat(cells[pos].c);
9800                       else
9801                         fnval[i] = (CHAR) (cells[pos].c & 0xFF);
9802                       if (fnval[i] == 0)
9803                         fnval[i] = SP;
9804                   } else
9805                     fnval[i] = SP;
9806               }
9807               fnval[i] = '\0';
9808           } else {
9809               fnval[0] = '\0';
9810           }
9811           p = fnval;
9812           goto fnend;
9813       }
9814 #endif /* OS2 */
9815 #endif /* NOLOCAL */
9816 
9817 #ifndef NOPUSH
9818       case FN_RAW:                      /* \frawcommand() */
9819       case FN_CMD: {                    /* \fcommand() */
9820           int x, c, n = FNVALL;
9821           x = 0;                        /* Completion flag */
9822 /*
9823   ZIFILE can be safely used because we can't possibly be transferring a file
9824   while executing this function.
9825 */
9826           if (!nopush && zxcmd(ZIFILE,bp[0]) > 0) { /* Open the command */
9827               while (n-- > -1) {        /* Read from it */
9828                   if ((c = zminchar()) < 0) {
9829                       x = 1;             /* EOF - set completion flag */
9830                       if (cx == FN_CMD) { /* If not "rawcommand" */
9831                           p--;           /* remove trailing newlines */
9832                           while (*p == CR || *p == LF)
9833                             p--;
9834                           p++;
9835                       }
9836                       *p = NUL;         /* Terminate the string */
9837                       break;
9838                   } else                /* Command still running */
9839                     *p++ = c;           /* Copy the bytes */
9840               }
9841               zclose(ZIFILE);           /* Close the command */
9842           }
9843           /* Return null string if command's output was too long. */
9844           p = fnval;
9845           if (!x) {
9846               failed = 1;
9847               if (fndiags)
9848                 ckmakmsg(fnval,FNVALL,
9849                          "<ERROR:RESULT_TOO_LONG:\\f",fn,"()>",NULL);
9850           }
9851           goto fnend;
9852       }
9853 #endif /* NOPUSH */
9854     } /* Break up big switch... */
9855 
9856     switch (y) {
9857       case FN_STX:                      /* \fstripx(string,c) */
9858         if (!(s = bp[0]))               /* Make sure there is a string */
9859           goto fnend;
9860         c = '.';                        /* Character to strip from */
9861         if (argn > 1) if (*bp[1]) c = *bp[1];
9862         n = ckstrncpy(fnval,bp[0],FNVALL);
9863         while (--n >= 0) {
9864             if (fnval[n] == c) {
9865                 fnval[n] = NUL;
9866                 break;
9867             }
9868         }
9869         p = fnval;
9870         goto fnend;
9871 
9872       case FN_STL:                      /* \flop(string,c) */
9873       case FN_LOPX: {			/* \flopx(string,c) */
9874 	  int n = 1;
9875 	  if (!(s = bp[0]))		/* Make sure there is a string */
9876 	    goto fnend;
9877 	  c = '.';			/* Character to strip to */
9878 	  if (argn > 1) if (*bp[1]) c = *bp[1];
9879 	  if (argn > 2) if (*bp[2]) {
9880 #ifndef NOFLOAT
9881 	      n = 0;
9882 	      if (isfloat(bp[2],0)) {
9883 		  n = (int)floatval;
9884 		  if (n < 0) n = 0;
9885 	      } else
9886 #endif	/* NOFLOAT */
9887 		n = atoi(bp[2]);
9888 	  }
9889 	  x = 0;
9890 	  if (cx == FN_LOPX) {		/* Lopx (from right) */
9891 	      if (n == 0)
9892 		goto fnend;
9893 	      s += strlen(s) - 1;	/* We already know it's > 0 */
9894 	      while (s-- >= bp[0]) {
9895 		  if (*s == c) {
9896 		      n--;
9897 		      if (n == 0) {
9898 			  s++;
9899 			  x = 1;
9900 			  break;
9901 		      }
9902 		  }
9903 	      }
9904 	      if (!x) s = "";
9905 	  } else {			/* Lop (from left) */
9906 	      if (n == 0) {
9907 		  p = bp[0];
9908 		  goto fnend;
9909 	      }
9910 	      while (*s++) {
9911 		  if (*(s-1) == c) {
9912 		      if (--n == 0) {
9913 			  x = 1;
9914 			  break;
9915 		      }
9916 		  }
9917 	      }
9918 	      if (!x) s = bp[0];
9919 	  }
9920 	  ckstrncpy(fnval,s,FNVALL);
9921 	  p = fnval;
9922 	  goto fnend;
9923       }
9924       case FN_STN:                      /* \fstripn(string,n) */
9925         if (argn < 1)                   /* Remove n chars from right */
9926           goto fnend;
9927         val1 = "0";
9928         if (argn > 1)
9929           if (*(bp[1]))
9930             val1 = evalx(bp[1]);
9931         if (!chknum(val1)) {
9932             failed = 1;
9933             evalerr(fn);
9934             goto fnend;
9935         }
9936         n = atoi(val1);
9937         if (n < 0) n = 0;
9938         k = (int)strlen(s = bp[0]) - n;
9939         if (k < 0) k = 0;
9940         p = fnval;
9941         while (k-- > 0)
9942           *p++ = *s++;
9943         *p = NUL;
9944         p = fnval;
9945         goto fnend;
9946 
9947       case FN_STB: {                    /* \fstripb(string,c) */
9948           char c2 = NUL;
9949           int i, k = 0;
9950           char * gr_opn = "\"{'([<";    /* Group open brackets */
9951           char * gr_cls = "\"}')]>";    /* Group close brackets */
9952 
9953           p = fnval;
9954           *p = NUL;
9955           if (!(s = bp[0]))             /* Make sure there is a string */
9956             goto fnend;
9957           if ((x = strlen(s)) < 1)
9958             goto fnend;
9959           c = NUL;                      /* Brace/bracket kind */
9960           if (argn > 1) {
9961               if (*bp[1]) {
9962                   if (chknum(bp[1])) {
9963                       k = atoi(bp[1]);
9964                       if (k < 0) k = 63;
9965                       for (i = 0; i < 6; i++) {
9966                           if (k & (1<<i)) {
9967                               if (s[0] == gr_opn[i] && s[x-1] == gr_cls[i]) {
9968                                   ckstrncpy(fnval,s+1,FNVALL);
9969                                   fnval[x-2] = NUL;
9970                                   goto fnend;
9971                               }
9972                           }
9973                       }
9974                       ckstrncpy(fnval,s,FNVALL); /* No match */
9975                       goto fnend;
9976                   }
9977               }
9978           }
9979 	  c = !bp[1] ? 0 : *bp[1];
9980           if (!c) c = s[0];
9981           if (argn > 2) if (*bp[2]) c2 = *bp[2];
9982           if (*s == c) {
9983               if (!c2) {
9984                   switch (c) {
9985                     case '(': c2 = ')'; break;
9986                     case '[': c2 = ']'; break;
9987                     case '{': c2 = '}'; break;
9988                     case '<': c2 = '>'; break;
9989                     case '"': c2 = '"'; break;
9990                     case 39:  c2 = 39;  break;
9991                     case 96:  c2 = 39;  break;
9992                     default:
9993                       if (argn == 2) {
9994                           c2 = c;
9995                       } else {
9996                           strncpy(fnval,s,x); /* Leave it like this */
9997                           fnval[x] = NUL;
9998                           goto fnend;
9999                       }
10000                   }
10001               }
10002               if (s[x-1] == c2) {
10003                   strncpy(fnval,s+1,x-2); /* Leave it like this */
10004                   fnval[x-2] = NUL;
10005                   goto fnend;
10006               }
10007           }
10008           strncpy(fnval,s,x);
10009           fnval[x] = NUL;
10010           goto fnend;
10011       }
10012 
10013       case FN_2HEX:                     /* Number to hex */
10014       case FN_2OCT:                     /* Number to octal */
10015         val1 = evalx(bp[0]);
10016         if (!*val1) {
10017             failed = 1;
10018             evalerr(fn);
10019             goto fnend;
10020         }
10021         sprintf(fnval, cx == FN_2HEX ? "%lx" : "%lo", atol(val1)); /* SAFE */
10022         if (cx == FN_2HEX && (int)(strlen(fnval)&1))
10023           sprintf(fnval,"0%lx",atol(val1)); /* SAFE */
10024         p = fnval;
10025         goto fnend;
10026 
10027       case FN_DNAM: {                   /* Directory part of file name */
10028           char *s;
10029           zfnqfp(bp[0],FNVALL,p);       /* Get full name */
10030           if (!isdir(p)) {              /* Is it already a directory? */
10031               zstrip(p,&s);             /* No get basename */
10032               if (*s) {
10033                   x = ckindex(s,p,0,0,0); /* Pos of latter in former */
10034                   if (x > 0) p[x-1] = NUL;
10035               }
10036           }
10037           if (!p) p = "";
10038           goto fnend;
10039       }
10040 
10041 #ifndef NORANDOM
10042       case FN_RAND:                     /* Random number */
10043 #ifdef CK_SSL
10044         if (RAND_bytes((unsigned char *)&k,sizeof(k)) < 0)
10045 #endif /* CK_SSL */
10046           k = rand();
10047         x = 0;
10048         if (argn > 0) {
10049             if (!chknum(bp[0])) {
10050                 failed = 1;
10051                 if (fndiags)
10052                   ckmakmsg(fnval,FNVALL,
10053                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10054                 goto fnend;
10055             }
10056             x = atoi(bp[0]);
10057         }
10058 #ifdef COMMENT
10059         sprintf(fnval,"%d", (x > 0 && k > 0) || (x < 0 && k < 0) ? k % x :
10060                 (x == 0 ? 0 : (0 - (k % (-x)))));
10061 #else
10062         debug(F111,"rand",ckitoa(x),k);
10063 #ifdef SUNOS4
10064 /* This is really strange but on SunOS, if we are requesting random numbers */
10065 /* between 0 and 4 or less, they always come out in sequence: 0 1 2 3 0 1 2 */
10066 /* Shifting the result of rand() in this case gives a more random result.   */
10067         if (x < 5)
10068           k = k >> 5;
10069 #endif /* SUNOS4 */
10070         if ((x > 0 && k > 0) || (x < 0 && k < 0))
10071           x = k % x;
10072         else if (x == 0)
10073           x = 0;
10074         else
10075           x = 0 - (k % (-x));
10076         debug(F101,"rand x","",x);
10077         sprintf(fnval,"%d", x);         /* SAFE */
10078 #endif /* COMMENT */
10079         p = fnval;
10080         goto fnend;
10081 #endif /* NORANDOM */
10082     } /* Break up big switch... */
10083 
10084     switch (y) {
10085       case FN_SPLIT:                    /* \fsplit(s1,a,s2,s3,mask) */
10086       case FN_WORD: {                   /* \fword(s1,n,s2,s3,mask) */
10087           int wordnum = 0;
10088           int splitting = 0;
10089           int x;
10090           int array = 0;
10091           int grouping = 0;
10092           int nocollapse = 0;
10093           char * sep = "";
10094           char * notsep = "";
10095           char * bp0 = NULL;
10096           char * bp1 = NULL;
10097           char   abuf[16];
10098           struct stringarray * q = NULL;
10099 
10100           splitting = (cx == FN_SPLIT); /* Our job */
10101 	  debug(F101,"FN_SPLIT splitting","",splitting);
10102 
10103           fnval[0] = splitting ? '0' : NUL; /* Initial return value */
10104           fnval[1] = NUL;
10105           p = fnval;
10106           bp0 = bp[0];                  /* Source string */
10107           if (!bp0) bp0 = "";
10108           debug(F111,"fsplit bp[0]",bp0,argn);
10109           if (argn < 1 || !*bp0)        /* If none, return default value */
10110             goto fnend;
10111 
10112           bp1 = bp[1];                  /* Function-dependent arg */
10113           if (!bp1) bp1 = "";           /* (array or number) */
10114           debug(F110,"fsplit bp[1]",bp1,0);
10115           if (bp[5]) {
10116               if (!chknum(bp[5])) {
10117                   failed = 1;
10118                   ckmakmsg(fnval,FNVALL,
10119                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10120                   goto fnend;
10121               }
10122               x = atoi(bp[5]);
10123               nocollapse = x;
10124           }
10125           if (!splitting) {             /* \fword(): n = desired word number */
10126               val1 = "1";               /* Default is first word */
10127               if (argn > 1)             /* Word number supplied */
10128                 if (*bp1)
10129                   val1 = evalx(bp1);
10130               if (!chknum(val1)) {
10131                   failed = 1;
10132                   ckmakmsg(fnval,FNVALL,
10133                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10134                   goto fnend;
10135               }
10136               n = atoi(val1);
10137           } else if (argn > 1 && *bp1) { /* \fsplit(): n = word count */
10138               ckstrncpy(abuf,bp1,16);   /* Get array reference */
10139               debug(F110,"fsplit abuf 1",abuf,0);
10140               failed = 1;               /* Assume it's bad */
10141               if (fndiags)              /* Default is this error message */
10142                 ckmakmsg(fnval,FNVALL,
10143                          "<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
10144               if (abuf[0] != '&')       /* "Address" of array */
10145                 goto fnend;             /* It's bad */
10146               if (abuf[2]) {            /* Check for brackets */
10147                   if (abuf[2] != '[' || abuf[3] != ']') {
10148                       goto fnend;       /* Bad */
10149                   }
10150               }
10151               debug(F110,"fsplit abuf 2",abuf,0);
10152               if (abuf[1] > 64 && abuf[1] < 91) /* Convert upper to lower */
10153                 abuf[1] += 32;
10154               if (abuf[1] < 97 || abuf[1] > 122) { /* Check for a-z */
10155                   goto fnend;
10156               }
10157               debug(F110,"fsplit abuf 3",abuf,0);
10158               array = 1;
10159               fnval[0] = NUL;           /* No error, erase message */
10160               failed = 0;               /* Unset failure flag */
10161               n = 0;                    /* Initialize word counter */
10162           }
10163           if (argn > 2)                 /* Have break set? */
10164             sep = bp[2];
10165           debug(F111,"fsplit sep",sep,argn);
10166           if (argn > 3)                 /* Have include set? */
10167             notsep = bp[3];
10168           debug(F111,"fsplit notsep",notsep,argn);
10169           if (argn > 4) {               /* Have grouping set? */
10170               char * bp4 = bp[4];
10171               debug(F111,"fsplit bp4",bp4,argn);
10172               if (!bp4) bp4 = "0";
10173               if (!*bp4) bp4 = "0";
10174               if (chknum(bp4)) {
10175                   grouping = atoi(bp4);
10176                   if (grouping == -1)
10177                     grouping = 127;
10178               } else {
10179                   failed = 1;
10180                   if (fndiags)
10181                     ckmakmsg(fnval,FNVALL,
10182                              "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10183                   goto fnend;
10184               }
10185           }
10186           /* Args parsed, now do the work */
10187 
10188           debug(F111,"fsplit bp0",bp0,n);
10189           q = cksplit(splitting,n,bp0,sep,notsep,grouping,0,nocollapse,0);
10190 
10191           wordnum = q ? q->a_size : -1; /* Check result */
10192           if (wordnum < 0) {
10193               failed = 1;               /* Failure */
10194               if (fndiags)
10195                 ckmakmsg(fnval,FNVALL,
10196                          (wordnum == -1) ?
10197                          "<ERROR:MALLOC_FAILURE:\\f" :
10198                          "<ERROR:TOO_MANY_WORDS:\\f",
10199                          fn,
10200                          "()>",
10201                          NULL
10202                          );
10203               goto fnend;
10204           }
10205           if (splitting) {              /* \fsplit() result */
10206               ckstrncpy(fnval,ckitoa(wordnum),FNVALL);
10207               if (array) {              /* Array was not declared. */
10208                   int i;
10209                   if ((x = dclarray(abuf[1],wordnum)) < 0) { /* Declare it. */
10210                       failed = 1;
10211                       if (fndiags)
10212                         ckmakmsg(fnval,FNVALL,
10213                                  "<ERROR:MALLOC_FAILURE:\\f",fn,"()>",NULL);
10214                       goto fnend;
10215                   }
10216                   for (i = 1; i <= wordnum; i++) { /* Copy results */
10217                       makestr(&(a_ptr[x][i]),q->a_head[i]);
10218                   }
10219                   a_ptr[x][0] = NULL;   /* Array is 1-based */
10220                   makestr(&(a_ptr[x][0]),fnval); /* Element = size */
10221               }
10222           } else {                      /* \fword() result */
10223               char * s;
10224               s = q->a_head[1];
10225               if (!s) s = "";
10226               ckstrncpy(fnval,s,FNVALL);
10227           }
10228           goto fnend;                   /* Done */
10229       }
10230 
10231     } /* Break up big switch... */
10232 
10233     switch (y) {
10234 
10235 #ifdef CK_KERBEROS
10236       case FN_KRB_TK:                   /* Kerberos tickets */
10237       case FN_KRB_NX:                   /* Kerberos next ticket */
10238       case FN_KRB_IV:                   /* Kerberos ticket is valid */
10239       case FN_KRB_FG:                   /* Kerberos Ticket flags */
10240       case FN_KRB_TT: {                 /* Kerberos ticket time */
10241           int kv = 0;                   /* Kerberos version */
10242           int n = 0;
10243           char * s = NULL;
10244           if (rdigits(bp[0])) {
10245               kv = atoi(bp[0]);
10246           } else {
10247               failed = 1;
10248               if (fndiags)
10249                 ckmakmsg(fnval,FNVALL,
10250                          "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10251               goto fnend;
10252           }
10253           if (kv != 4 && kv != 5) {
10254               failed = 1;
10255               if (fndiags)
10256                 ckmakmsg(fnval,FNVALL,
10257                          "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
10258               goto fnend;
10259           }
10260           if ((cx == FN_KRB_IV || cx == FN_KRB_TT || cx == FN_KRB_FG) &&
10261                argn < 2) {
10262               failed = 1;
10263               if (fndiags)
10264                 ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
10265               goto fnend;
10266           }
10267           switch (y) {
10268             case FN_KRB_TK:             /* Number of Kerberos tickets */
10269 #ifdef CK_AUTHENTICATION
10270               switch (kv) {
10271                 case 4:
10272                   n = ck_krb4_get_tkts();
10273                   sprintf(fnval, "%d", (n >= 0) ? n : 0); /* SAFE */
10274                   goto fnend;
10275                 case 5: {
10276                     extern char * krb5_d_cc;
10277                     n = ck_krb5_get_tkts(krb5_d_cc);
10278                     sprintf(fnval, "%d", (n >= 0) ? n : 0); /* SAFE */
10279                     goto fnend;
10280                 }
10281               }
10282 #else
10283               sprintf(fnval,"%d",0);    /* SAFE */
10284 #endif /* CK_AUTHENTICATION */
10285               goto fnend;
10286 
10287             case FN_KRB_NX:             /* Kerberos next ticket */
10288 #ifdef CK_AUTHENTICATION
10289               switch (kv) {
10290                 case 4:
10291                   s = ck_krb4_get_next_tkt();
10292                   ckstrncpy(fnval, s ? s : "",FNVALL);
10293                   goto fnend;
10294                 case 5:
10295                   s = ck_krb5_get_next_tkt();
10296                   ckstrncpy(fnval, s ? s : "",FNVALL);
10297                   goto fnend;
10298               }
10299 #else
10300               sprintf(fnval,"k%d next-ticket-string",kv); /* SAFE */
10301 #endif /* CK_AUTHENTICATION */
10302               goto fnend;
10303 
10304             case FN_KRB_IV:             /* Kerberos ticket is valid */
10305 #ifdef CK_AUTHENTICATION
10306               /* Return 1 if valid, 0 if not */
10307               switch (kv) {
10308                 case 4:
10309                   n = ck_krb4_tkt_isvalid(bp[1]);
10310                   sprintf(fnval, "%d", n > 0 ? 1 : 0); /* SAVE */
10311                   goto fnend;
10312                 case 5: {
10313                     extern char * krb5_d_cc;
10314                     n = ck_krb5_tkt_isvalid(krb5_d_cc,bp[1]);
10315                     sprintf(fnval,"%d", n > 0 ? 1 : 0); /* SAFE */
10316                     goto fnend;
10317                 }
10318               }
10319 #else
10320               sprintf(fnval,"%d",0);    /* SAFE */
10321 #endif /* CK_AUTHENTICATION */
10322               goto fnend;
10323 
10324             case FN_KRB_TT:             /* Kerberos ticket time */
10325 #ifdef CK_AUTHENTICATION
10326               switch (kv) {
10327                 case 4:
10328                   n = ck_krb4_tkt_time(bp[1]);
10329                   sprintf(fnval,"%d", n >= 0 ? n : 0); /* SAFE */
10330                   goto fnend;
10331                 case 5: {
10332                     extern char * krb5_d_cc;
10333                     n = ck_krb5_tkt_time(krb5_d_cc,bp[1]);
10334                     sprintf(fnval,"%d", n >= 0 ? n : 0); /* SAFE */
10335                     goto fnend;
10336                 }
10337               }
10338 #else
10339               ckstrncpy(fnval,"600",FNVALL); /* Some time */
10340 #endif /* CK_AUTHENTICATION */
10341               goto fnend;
10342 
10343             case FN_KRB_FG:             /* Kerberos ticket flags */
10344 #ifdef CK_AUTHENTICATION
10345               switch (kv) {
10346                 case 4:
10347                   fnval[0] = '\0';
10348                   goto fnend;
10349                 case 5: {
10350                     extern char * krb5_d_cc;
10351                     ckstrncpy(fnval,ck_krb5_tkt_flags(krb5_d_cc,bp[1]),FNVALL);
10352                     goto fnend;
10353                 }
10354               }
10355 #else
10356               fnval[0] = '\0';
10357 #endif /* CK_AUTHENTICATION */
10358               goto fnend;
10359           }
10360           p = fnval;
10361           goto fnend;
10362       }
10363 #endif /* CK_KERBEROS */
10364 
10365 #ifdef FN_ERRMSG
10366       case FN_ERRMSG:
10367         if (rdigits(bp[0])) {
10368             k = atoi(bp[0]);
10369         } else {
10370             failed = 1;
10371             if (fndiags)
10372              ckmakmsg(fnval,FNVALL,"<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10373             goto fnend;
10374         }
10375 #ifdef VMS
10376         ckstrncpy(fnval,ckvmserrstr(k),FNVALL);
10377 #else
10378         x = errno;
10379         errno = k;
10380         ckstrncpy(fnval,ck_errstr(),FNVALL);
10381         errno = x;
10382 #endif /* VMS */
10383         p = fnval;
10384         goto fnend;
10385 #endif /* FN_ERRMSG */
10386 
10387       case FN_DIM: {
10388           int max;
10389           char abuf[16], *s;
10390           fnval[0] = NUL;               /* Initial return value */
10391           ckstrncpy(abuf,bp[0],16);     /* Get array reference */
10392           s = abuf;
10393           if (*s == CMDQ) s++;
10394           failed = 1;                   /* Assume it's bad */
10395           p = fnval;                    /* Point to result */
10396           if (fndiags)                  /* Default is this error message */
10397             ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
10398           if (s[0] != '&') {            /* "Address" of array */
10399               goto fnend;
10400           }
10401           if (s[2]) {
10402               if (s[2] != '[' || s[3] != ']') {
10403                   goto fnend;
10404               }
10405           }
10406           if (s[1] >= 64 && s[1] < 91)  /* Convert upper to lower */
10407             s[1] += 32;
10408           if (s[1] < 95 || s[1] > 122) { /* Check for a-z */
10409               goto fnend;	 	 /* Bad */
10410           }
10411           if ((max = chkarray(s[1],1)) < 1) /* (second arg was 1) */
10412             max = 0;
10413           failed = 0;                   /* Unset failure flag */
10414           sprintf(fnval,"%d",max);      /* SAFE */
10415           goto fnend;
10416       }
10417       case FN_FILEINF: {		/* File information */
10418 #ifdef UNIX
10419 	  /* from zgetfs in ckufio.c */
10420 	  extern int zgfs_dir, zgfs_link;
10421           extern char linkname[];
10422 	  char * tx;			/* For tilde expansion */
10423 #else
10424 	  int zgfs_dir = 0, zgfs_link = 0;
10425 #endif /* UNIX */
10426           char abuf[16], *s;
10427           char ** ap = NULL;
10428 	  char workbuf[MAXPATHLEN];
10429 	  int attrs = 9;		/* Number of attributes defined */
10430 	  int k = 0;			/* current attribute index */
10431 	  int i,j,n;
10432           int m;			/* For scanfile() */
10433 	  int dir = -1;			/* 1 = arg is a directory file */
10434 	  CK_OFF_T z, z2;		/* For file size */
10435 
10436           workbuf[0] = NUL;
10437           workbuf[1] = NUL;
10438 	  if (argn < 2) {		/* An array designator is required */
10439 	     if (fndiags)
10440 	      ckmakmsg(fnval,FNVALL,"<ERROR:ARRAY_REQUIRED:\\f",fn,"()>",NULL);
10441 	     goto fnend;
10442 	  }
10443 #ifdef UNIX
10444 	  if (*(bp[0]) == '~') {	/* Expand any tildes in filenames. */
10445 	      tx = tilde_expand(bp[0]);	/* We recycle bp[0] */
10446 	      if (tx) if (*tx) {	/* this way so they will be freed */
10447 		  free(bp[0]);		/* automatically later. */
10448 		  bp[0] = NULL;
10449 		  makestr(&(bp[0]),tx);
10450 	      }
10451 	  }
10452 #endif /* UNIX */
10453 
10454 	  j = ckstrncpy(workbuf,bp[0],MAXPATHLEN); /* Strip any trailing '/' */
10455 	  if (workbuf[j-1] == '/') {
10456 	      workbuf[j-1] = NUL;
10457 	      makestr(&(bp[0]),workbuf);
10458 	  }
10459 	  z = zchki(bp[0]);		/* Check accessibility */
10460 	  if (z == -1L || z == -3L) {   /* Access denied or whatever */
10461 	      p = "0";
10462 	      goto fnend;
10463 	      /* Note: z > 0 is the file size but only of regular files */
10464 	      /* Thus the zgetfs call just below */
10465 	  }
10466 #ifdef UNIX
10467 	  if ((z2 = zgetfs(bp[0])) > 0) { /* Get size and some attributes */
10468 	      z = z2;			/* Have size */
10469 	      dir = zgfs_dir;		/* File is/isn't a directory */
10470 	  }
10471 #endif /* UNIX */
10472 	  if (dir < 0)			/* Check if file is a a directory */
10473 	    dir = isdir(bp[0]);		/* if previous clause didn't already */
10474 
10475 	  fnval[0] = NUL;		/* Initial return value */
10476 	  ckstrncpy(abuf,bp[1],16);	/* Get array reference */
10477 	  s = abuf;
10478 	  if (*s == CMDQ) s++;
10479 	  failed = 1;			/* Assume it's bad */
10480 	  p = fnval;			/* Point to result */
10481 	  if (fndiags)			/* Default is this error message */
10482 	    ckmakmsg(fnval,FNVALL,
10483 		     "<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
10484 	  if (s[0] != '&')		/* "Address" of array */
10485 	    goto fnend;
10486 	  if (s[2])
10487 	    if (s[2] != '[' || s[3] != ']')
10488 	      goto fnend;
10489 	  if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */
10490 	    s[1] += 32;
10491 	  if ((x = dclarray(s[1],attrs)) < 0) /* One element per attribute */
10492 	    goto fnend;
10493 	  failed = 0;			/* Unset failure flag */
10494 	  ap = a_ptr[x];		/* Point to array we just declared */
10495 	  sprintf(fnval,"%d",k);	/* SAFE */
10496 
10497 	  /* Element 1 = filename */
10498 
10499 	  s = bp[0];			/* Argument (might include path) */
10500 	  n = strlen(s);
10501 	  for (i = n; i > 0; i--) {	/* Get filename without path */
10502 	      if (ISDIRSEP(s[i-1])) {	/* Platform independent way */
10503 		  s += i;
10504 		  break;
10505 	      }
10506 	  }
10507 	  a_ptr[x][1] = NULL;		/* Filename */
10508 	  makestr(&(a_ptr[x][1]),s);
10509 
10510 	  /* Element 2 - Full pathname */
10511 
10512 	  s = workbuf;
10513 	  zfnqfp(bp[0],FNVALL,s);
10514 	  n = strlen(s);
10515 	  for (i = n; i > 0; i--) {	/* Get filename without path */
10516 	      if (ISDIRSEP(s[i-1])) {	/* Platform independent way */
10517 		  s[i] = NUL;
10518 		  break;
10519 	      }
10520 	  }
10521 	  a_ptr[x][2] = NULL;
10522 	  makestr(&(a_ptr[x][2]),s);
10523 
10524 	  /* Element 3 - Modification date-time */
10525 
10526 	  s = zfcdat(bp[0]);
10527 	  a_ptr[x][3] = NULL;
10528 	  makestr(&(a_ptr[x][3]),s);
10529 
10530 	  /* Element 4 - Permissions string */
10531 
10532 #ifdef UNIX
10533 	  if (zgfs_link)
10534 	    s = "lrwxrwxrwx";
10535 	  else
10536 #endif /* UNIX */
10537 
10538 /* [jt] 2013/11/21:
10539  * K-95 doesn't have ziperm.  However, I have not read through this
10540  * code thoroughly, and this needs to be double checked to see if there are
10541  * any side effects of commenting this out.
10542  */
10543 #ifdef CK_PERMS
10544 	    s = ziperm(bp[0]);
10545 	  a_ptr[x][4] = NULL;
10546 	  makestr(&(a_ptr[x][4]),s);
10547 	  ckstrncpy(workbuf,s,32);	/* Save for later */
10548 #endif /* CK_PERMS */
10549 
10550 	  /* Element 5 - Permissions numeric code */
10551 
10552 	  s = zgperm(bp[0]);
10553 	  a_ptr[x][5] = NULL;
10554 	  makestr(&(a_ptr[x][5]),s);
10555 
10556 	  /* Element 6 - Size in bytes */
10557 
10558 #ifdef UNIX /* [fdc] 2021-09-14 only Unix has file links */
10559 	  s = zgfs_link ? ckitoa((int)strlen((char *)linkname)) : ckfstoa(z);
10560 #else
10561 	  s = ckfstoa(z);
10562 #endif /* UNIX */
10563 	  a_ptr[x][6] = NULL;
10564 	  makestr(&(a_ptr[x][6]),s);
10565 
10566 	  /* Element 7 - File type */
10567 
10568 	  j = 0;
10569 	  if (dir) j = 3;
10570 #ifdef UNIX
10571 	  else if (zgfs_link) j = 4;
10572 	  else if (ckindex("x",(char *)workbuf,0,0,1)) j = 2;
10573 	  else if (workbuf[1] != '-') j = 1;
10574 #else
10575 #ifdef VMS
10576 	  else if (ckindex("E",(char *)workbuf,0,0,1)) j = 2;
10577 	  else j = 1;
10578 #endif /* VMS */
10579 #endif /* UNIX */
10580 	  a_ptr[x][7] = NULL;
10581 	  switch (j) {
10582 	    case 0: s = "unknown"; break;
10583 	    case 1: s = "regular"; break;
10584 	    case 2: s = "executable"; break;
10585 	    case 3: s = "directory"; break;
10586 	    case 4: s = "link"; break;
10587 	    default: s = "unknown";
10588 	  }
10589 	  makestr(&(a_ptr[x][7]),s);
10590 	  k = 7;
10591 
10592 	  /* Element 8 - Name of linked-to file (if link) */
10593 
10594 	  a_ptr[x][8] = NULL;
10595 #ifdef UNIX
10596 	  if (zgfs_link) {
10597 	      makestr(&(a_ptr[x][8]),(char *)linkname);
10598 	      k++;
10599 	  }
10600 #endif /* UNIX */
10601 
10602 	  /* Element 9 - File scan result */
10603 
10604 	  if (j == 1 || j == 2) {	/* Regular file */
10605 	      m = scanfile(bp[0],NULL,nscanfile);
10606 	      if (m > -1) {
10607 		  if (k < 8) k = 8;	/* Insert empty element for link */
10608 		  makestr(&(a_ptr[x][8]),"");
10609 		  k++;
10610 		  switch (m) {
10611 		    case FT_7BIT: s = "text:7bit"; break;
10612 		    case FT_UTF8: s = "text:utf8"; break;
10613 		    case FT_UCS2: s = "text:ucs2"; break;
10614 		    case FT_8BIT: s = "text:8bit"; break;
10615 		    case FT_TEXT: s = "text:unknown"; break;
10616 		    case FT_BIN:  s = "binary"; break;
10617 		    default: s = "unknown";
10618 		  }
10619 		  a_ptr[x][9] = NULL;
10620 		  makestr(&(a_ptr[x][9]),s);
10621 	      }
10622 	  }
10623 	  /* If adding another change attrs declaration to match */
10624 
10625 	  /* Element 0 = array size */
10626 
10627 	  p = ckitoa(k);		/* Number of elements */
10628 
10629 	  a_ptr[x][0] = NULL;		/* Put number of elements in [0] */
10630 	  makestr(&(a_ptr[x][0]),p);
10631 	  debug(F101,"FILEINF","",attrs);
10632           goto fnend;
10633       }
10634       case FN_FILECMP: {		/* File comparison */
10635 	FILE *fp1 = NULL;
10636 	FILE *fp2 = NULL;
10637         char * s, * s1 = NULL, * s2 = NULL;
10638 #ifdef UNIX
10639 	char * tx;			/* For tilde expansion */
10640 #endif /* UNIX */
10641 	int c1, c2;
10642         int eof1 = 0, eof2 = 0;
10643 
10644 	failed = 1;			/* Assume files differ */
10645 	p[0] = '1';			/* Default return value = differ */
10646 	p[1] = NUL;
10647 	if (argn != 2) {		/* Need two args */
10648 	    if (fndiags)
10649 	      ckmakmsg(fnval,FNVALL,"<ERROR:ARG_COUNT:\\f",fn,"()>",NULL);
10650 	    goto fnend;
10651 	}
10652 	s1 = bp[0];
10653 	s2 = bp[1];
10654 #ifdef UNIX
10655 	if (*s1 == '~') {		/* Expand any tildes in filenames. */
10656 	    tx = tilde_expand(bp[0]);	/* We recycle bp[0] and bp[1] */
10657 	    if (tx) if (*tx) {		/* this way so they will be freed */
10658 		free(bp[0]);		/* automatically later. */
10659 		bp[0] = NULL;
10660 		makestr(&(bp[0]),tx);
10661 	    }
10662 	    s1 = bp[0];
10663 	}
10664 	if (*s2 == '~') {
10665 	    tx = tilde_expand(bp[1]);
10666 	    if (tx) if (*tx) {
10667 		free(bp[1]);
10668 		bp[1] = NULL;
10669 		makestr(&(bp[1]),tx);
10670 	    }
10671 	    s2 = bp[1];
10672 	}
10673 #endif /* UNIX */
10674 	fp1 = fopen(s1, "r");		/* Open it first file*/
10675 	fp2 = fopen(s2, "r");		/* Open it first file*/
10676 	failed = 0;			/* No failure from here down */
10677 	if (fp1 == NULL || fp2 == NULL) { /* Open failure */
10678 	    p[0] = '-';
10679 	    p[1] = '1';
10680 	    p[2] = NUL;			/* Return -1 */
10681 	    if (fp1) fclose(fp1);
10682 	    if (fp1) fclose(fp2);
10683 	    goto fnend;
10684 	}
10685 	while (1) {
10686 	    if (!eof1) {
10687 		c1 = getc(fp1);
10688 		if (c1 == (unsigned int)EOF) {
10689 		    eof1++;
10690 		    fclose(fp1);
10691 		}
10692 	    }
10693 	    if (!eof2) {
10694 		c2 = getc(fp2);
10695 		if (c2 == (unsigned int)EOF) {
10696 		    eof2++;
10697 		    fclose(fp2);
10698 		}
10699 	    }
10700 	    if (eof1 && eof2) {
10701 		p[0] = '0';		/* Success */
10702 		p[1] = NUL;
10703 		failed = 0;
10704 		goto fnend;
10705 	    }
10706 	    if (eof1 || eof2 || (c1 != c2)) {
10707 		if (!eof1) fclose(fp1);
10708 		if (!eof2) fclose(fp2);
10709 		goto fnend;
10710 	    }
10711 	}
10712       }
10713 
10714     } /* Break up big switch... */
10715 
10716     switch (y) {
10717       case FN_JDATE:
10718         if (argn < 1)                   /* Check number of args */
10719           p = ckdate();                 /* None, get today's date-time */
10720         else                            /* Some */
10721           p = bp[0];                    /* Use first */
10722         p = ckcvtdate(p,0);             /* Convert to standard form */
10723         ckstrncpy(fnval,zjdate(p),FNVALL); /* Convert to Julian */
10724         p = fnval;                      /* Point to result */
10725         failed = 0;
10726         if (*p == '-') {
10727             failed = 1;
10728             if (fndiags)                /* Default is this error message */
10729               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_DATE:\\f",fn,"()>",NULL);
10730         }
10731         goto fnend;
10732 
10733       case FN_DATEJ:
10734         ckstrncpy(fnval,jzdate(bp[0]),FNVALL); /* Convert to yyyy<dayofyear> */
10735         p = fnval;                      /* Point to result */
10736         failed = 0;
10737         if (*p == '-') {
10738             failed = 1;
10739             if (fndiags)                /* Default is this error message */
10740               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_DATE:\\f",fn,"()>",NULL);
10741         }
10742         goto fnend;
10743 
10744       case FN_DTIM:                     /* \fcvtdate() */
10745       case FN_TIME:                     /* Free-format time to hh:mm:ss */
10746       case FN_NTIM:                     /* Time to sec since midnight */
10747         s = (argn > 0) ? bp[0] : "";
10748         if (!s) s = "";
10749         if (!*s)
10750           p = ckdate();                 /* None, get today's date */
10751         else                            /* Some */
10752           p = bp[0];                    /* Use first */
10753 	{
10754 	    char * s;
10755 	    s = p;
10756 	    while (*s) {                /* Strip leading spaces/ctrls */
10757 		if (*s < 32) {
10758 		    *s = NUL;
10759 		    break;
10760 		}
10761 		s++;
10762 	    }
10763 	    /* do { if (*s < '!') *s = NUL; break; } while (*s++); */
10764 	}
10765         p = ckcvtdate(p,2);             /* Convert to standard form */
10766         if (*p == '<') {
10767             failed = 1;
10768             if (fndiags)                /* Default is this error message */
10769               ckmakmsg(fnval,FNVALL,
10770                        "<ERROR:ARG_BAD_DATE_OR_TIME:\\f",fn,"()>",NULL);
10771             p = fnval;
10772             goto fnend;
10773         }
10774         if (argn > 1) {			/* Format code */
10775             s = evalx(bp[1]);
10776             if (!s) s = "";
10777             if (!*s) s = "0";
10778             if (!chknum(s)) {
10779                 failed = 1;
10780                 if (fndiags)
10781                   ckmakmsg(fnval,FNVALL,
10782                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10783                 p = fnval;
10784                 goto fnend;
10785             }
10786             x = atoi(s);
10787             /* if (x) */ p = shuffledate(p,x);
10788         }
10789         if (cx == FN_TIME) {
10790             p += 9;
10791         } else if (cx == FN_NTIM) {
10792             long sec = 0L;
10793             p[11] = NUL;
10794             p[14] = NUL;
10795             sec = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15);
10796             sprintf(fnval,"%ld",sec);   /* SAFE */
10797             p = fnval;
10798         }
10799         goto fnend;
10800 
10801       case FN_MJD:                      /* Modified Julian Date */
10802         if (argn < 1)                   /* Check number of args */
10803           p = zzndate();                /* None, get today's date-time */
10804         else                            /* Some */
10805           p = bp[0];                    /* Use first */
10806         p = ckcvtdate(p,0);             /* Convert to standard form */
10807         if (*p == '-') {
10808             failed = 1;
10809             if (fndiags)                /* Default is this error message */
10810               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_DATE:\\f",fn,"()>",NULL);
10811             goto fnend;
10812         }
10813         /* Convert to modified Julian date */
10814         sprintf(fnval,"%ld",mjd(p));    /* SAFE */
10815         p = fnval;                      /* Point to result */
10816         goto fnend;
10817 
10818       case FN_MJD2: {
10819           long k = 0L;
10820           int n = 0;
10821           p = evalx(bp[0]);
10822           if (*p == '-') {
10823               p++;
10824               n = 1;
10825           }
10826           if (!rdigits(p)) {
10827               failed = 1;
10828               evalerr(fn);
10829               p = fnval;
10830               goto fnend;
10831           } else {
10832               k = atol(p);
10833               if (n) k = -k;
10834           }
10835           ckstrncpy(fnval,mjd2date(k),FNVALL); /* Convert to Date */
10836           p = fnval;                    /* Point to result */
10837           failed = 0;
10838           goto fnend;
10839       }
10840 
10841 #ifndef NODIAL
10842       case FN_PNCVT: {                  /* Convert phone number */
10843           extern char * pncvt();
10844           failed = 0;
10845           p = pncvt(bp[0]);
10846           if (!p) p = "";
10847           if (!*p) {
10848             failed = 1;
10849             if (fndiags)                /* Default is this error message */
10850               ckmakmsg(fnval,FNVALL,
10851                        "<ERROR:ARG_BAD_PHONENUM:\\f",fn,"()>",NULL);
10852         }
10853         goto fnend;
10854       }
10855 #endif /* NODIAL */
10856 
10857       case FN_DAY:
10858       case FN_NDAY:
10859         if (argn < 1)                   /* Check number of args */
10860           p = zzndate();                /* None, get today's date-time */
10861         else                            /* Some */
10862           p = bp[0];                    /* Use first */
10863         p = ckcvtdate(p,0);             /* Convert to standard form */
10864         if (*p == '-') {
10865             failed = 1;
10866             if (fndiags)                /* Default is this error message */
10867               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_DATE:\\f",fn,"()>",NULL);
10868             goto fnend;
10869         }
10870         failed = 0;
10871         z = mjd(p);                     /* Convert to modified Julian date */
10872         z = z % 7L;
10873         if (z < 0) {
10874             z = 0 - z;
10875             k = 6 - ((int)z + 3) % 7;
10876         } else {
10877             k = ((int)z + 3) % 7;	/* Day of week */
10878         }
10879         p = fnval;                      /* Point to result */
10880         if (cx == FN_NDAY)
10881           sprintf(fnval,"%d",k);        /* SAFE */
10882         else
10883           ckstrncpy(fnval,wkdays[k],FNVALL);
10884         goto fnend;
10885 
10886       case FN_N2TIM: {                  /* Sec since midnight to hh:mm:ss */
10887           long k = 0L;
10888           int n = 0, hh, mm, ss;
10889           char * s = bp[0];
10890           if (argn < 1)                 /* If no arg substitute 0 */
10891             s = "0";
10892           p = evalx(s);                 /* Evaluate expression silently */
10893           if (*p == '-') {              /* Check result for minus sign */
10894               p++;
10895               n = 1;
10896           }
10897           if (!rdigits(p)) { /* Check for numeric */
10898               failed = 1;
10899               ckmakmsg(fnval,FNVALL,
10900                        "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10901               p = fnval;
10902               goto fnend;
10903           } else {
10904               k = atol(p);
10905               if (n) k = -k;
10906           }
10907           if (k < 0) {                  /* Check for negative */
10908               failed = 1;
10909               if (fndiags)
10910                 ckmakmsg(fnval,FNVALL,
10911                          "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
10912               p = fnval;
10913               goto fnend;
10914           }
10915           hh = k / 3600L;               /* Have positive number */
10916           mm = (k % 3600L) / 60L;       /* break it down... */
10917           ss = ((k % 3600L) % 60L);
10918 
10919           sprintf(fnval,"%02d:%02d:%02d",hh,mm,ss); /* SAFE */
10920           p = fnval;
10921           failed = 0;
10922           goto fnend;
10923       }
10924 
10925       case FN_PERM: {                   /* File permissions */
10926           p = fnval;
10927           z = zchki(bp[0]);
10928           if (z < 0) {
10929               failed = 1;
10930               if (fndiags) {
10931                   if (z == -1)
10932                     ckmakmsg(fnval,FNVALL,
10933                              "<ERROR:FILE_NOT_FOUND:\\f",fn,"()>",NULL);
10934                   else if (z == -2)
10935                     ckmakmsg(fnval,FNVALL,
10936                              "<ERROR:FILE_NOT_READABLE:\\f",fn,"()>",NULL);
10937                   else if (z == -3)
10938                     ckmakmsg(fnval,FNVALL,
10939                              "<ERROR:FILE_NOT_ACCESSIBLE:\\f",fn,"()>",NULL);
10940                   else
10941                     ckmakmsg(fnval,FNVALL,
10942                              "<ERROR:FILE_ERROR:\\f",fn,"()>",NULL);
10943               }
10944               goto fnend;
10945           }
10946 #ifdef CK_PERMS
10947           ckstrncpy(fnval,ziperm(bp[0]),FNVALL);
10948 #else
10949           ckstrncpy(fnval,"(unknown)",FNVALL);
10950 #endif /* CK_PERMS */
10951           goto fnend;
10952       }
10953       case FN_TLOOK:                    /* tablelook() */
10954       case FN_ALOOK: {                  /* arraylook() */
10955           int i, x, hi, lo, max, cmdlen;
10956           char abuf[16], *s, *pat;
10957           char kwbuf[256];
10958           char delim = ':';
10959           failed = 1;                   /* Assume failure */
10960           ckstrncpy(fnval,"-1",FNVALL);
10961           pat = bp[0];                  /* Point to search pattern */
10962           if (!pat) pat = "";           /* Watch out for NULL pointer */
10963           cmdlen = strlen(pat);         /* Get pattern length */
10964           if (argn < 2 /* || cmdlen < 1 */ ) { /* Need two args */
10965               if (fndiags)
10966                 ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
10967               goto fnend;
10968           }
10969           ckstrncpy(abuf,bp[1],16);     /* Get array reference */
10970           if (argn > 2)
10971             delim = *(bp[2]);
10972           s = abuf;
10973           if ((x = arraybounds(s,&lo,&hi)) < 0) { /* Get index and bounds */
10974               if (fndiags)
10975                ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
10976               goto fnend;
10977           }
10978           p = fnval;                    /* Point to result */
10979           max = a_dim[x];               /* Size of array */
10980           if (lo < 0) lo = 0;           /* Use given range if any */
10981           if (lo > max) lo = max;
10982           if (hi < 0) hi = max;
10983           if (hi > max) hi = max;
10984           failed = 0;                   /* Unset failure flag */
10985           if (max < 1)
10986             goto fnend;
10987           kwbuf[255] = NUL;
10988           for (i = lo; i <= hi; i++) {
10989               if (!a_ptr[x][i])
10990                 continue;
10991               if (cx == FN_ALOOK) {
10992                   if (ckmatch(pat,a_ptr[x][i],inpcas[cmdlvl],1+4)) {
10993                       sprintf(fnval,"%d",i); /* SAFE */
10994                       goto fnend;
10995                   }
10996               } else if (cx == FN_TLOOK) {
10997                   char * aa;
10998                   int j = 0, v = 0, len;
10999                   if (i == hi)
11000                     break;
11001                   aa = a_ptr[x][i];     /* Point to this array element */
11002                   if (!aa) aa = "";
11003                   while (j < 254 && *aa) { /* Isolate keyword */
11004                       if (*aa == delim)
11005                         break;
11006                       kwbuf[j++] = *aa++;
11007                   }
11008                   kwbuf[j] = NUL;
11009                   len = j;
11010                   v = 0;
11011                   if ((len == cmdlen && !ckstrcmp(kwbuf,pat,len,0)) ||
11012                       ((v = !ckstrcmp(kwbuf,pat,cmdlen,0)) &&
11013                        ckstrcmp(a_ptr[x][i+1],pat,cmdlen,0))) {
11014                       sprintf(fnval,"%d",i); /* SAFE */
11015                       goto fnend;
11016                   }
11017                   if (v) {              /* Ambiguous */
11018                       ckstrncpy(fnval,"-2",FNVALL);
11019                       goto fnend;
11020                   }
11021               }
11022           }
11023           if (cx == FN_TLOOK) {         /* tablelook() last element */
11024               ckstrncpy(fnval,"-1",FNVALL);
11025               if (!ckstrcmp(a_ptr[x][hi],pat,cmdlen,0))
11026                 sprintf(fnval,"%d",hi); /* SAFE */
11027           }
11028           goto fnend;
11029       }
11030       case FN_TOB64:                    /* Base-64 conversion */
11031       case FN_FMB64:
11032         p = fnval;
11033         *p = NUL;
11034         if (argn < 1)
11035           goto fnend;
11036         if (cx == FN_TOB64) {
11037             x = b8tob64(bp[0],-1,fnval,FNVALL);
11038         } else {
11039             x = strlen(bp[0]);
11040             if (x % 4) {                /* length must be multiple of 4 */
11041                 failed = 1;
11042                 ckmakmsg(fnval,FNVALL,
11043                          "<ERROR:ARG_INCOMPLETE:\\f",fn,"()>",NULL);
11044                 goto fnend;
11045             }
11046             b64tob8(NULL,0,NULL,0);     /* Reset */
11047             x = b64tob8(bp[0],-1,fnval,FNVALL);
11048             b64tob8(NULL,0,NULL,0);     /* Reset again */
11049         }
11050         if (x < 0) {
11051             failed = 1;
11052             if (fndiags) {
11053                 char * m = "INTERNAL_ERROR";
11054                 switch (x) {
11055                   case -1: m = "ARG_TOO_LONG"; break;
11056                   case -2: m = "ARG_OUT_OF_RANGE"; break;
11057                 }
11058                 if (ckmakmsg(fnval,FNVALL,"<ERROR:",m,"\\f",fn) > 0)
11059                   ckstrncat(fnval,"()>",FNVALL);
11060             }
11061         }
11062         goto fnend;
11063 
11064       case FN_ABS: {
11065           char * s;
11066           s = bp[0];
11067           if (*s == '-' || *s == '+')
11068             s++;
11069           if (!rdigits(s)) {
11070               if (fndiags)
11071                 ckmakmsg(fnval,FNVALL,
11072                          "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11073               goto fnend;
11074           }
11075           ckstrncpy(fnval,s,FNVALL);
11076           goto fnend;
11077       }
11078 
11079       case FN_AADUMP: {
11080           char abuf[16], *s = NULL, **ap = NULL, **vp = NULL;
11081           char pattern[VNAML];
11082           int slen, i, j, k, first = -1;
11083           extern int xdelmac();
11084           p = fnval;
11085           if (argn < 2) {
11086               if (fndiags)
11087                 ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG2:\\f",fn,"()>",NULL);
11088               goto fnend;
11089           }
11090           debug(F101,"aaconvert argn","",argn);
11091           s = bp[0];
11092           slen = strlen(s);
11093 
11094           /* Count elements so we can create the array */
11095 
11096           ckmakmsg(pattern,VNAML,s,"<*>",NULL,NULL);
11097           for (k = 0, i = 0; i < nmac; i++) {
11098               if (ckmatch(pattern,mactab[i].kwd,0,1)) {
11099                   if (first < 0)        /* Remember location of first match */
11100                     first = i;
11101                   k++;
11102               }
11103           }
11104           debug(F101,"aaconvert matches","",k);
11105           debug(F101,"aaconvert first","",first);
11106           fnval[0] = NUL;               /* Initial return value */
11107           ckstrncpy(abuf,bp[1],16);     /* Get array reference */
11108           s = abuf;
11109           if (*s == CMDQ) s++;
11110           p = fnval;                    /* Point to result */
11111           if (fndiags)                  /* Default is this error message */
11112             ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
11113           if (s[0] != '&')              /* Address of array */
11114             goto fnend;
11115           if (s[2])
11116             if (s[2] != '[' || s[3] != ']')
11117               goto fnend;
11118           if (s[1] >= 64 && s[1] < 91)  /* Convert upper to lower */
11119             s[1] += 32;
11120           if ((x = dclarray(s[1],k)) < 0) /* Declare array to size */
11121             goto fnend;
11122           ap = a_ptr[x];                /* Point to array we just declared */
11123           /* debug(F111,"aaconvert array 1",abuf,ap); */
11124           abuf[0] = NUL;
11125           if (argn > 2) {
11126               ckstrncpy(abuf,bp[2],16); /* Get value array reference */
11127               s = abuf;
11128               if (*s == CMDQ) s++;
11129               if (s[0] != '&')          /* Address of array */
11130                 goto fnend;
11131               if (s[2])
11132                 if (s[2] != '[' || s[3] != ']')
11133                   goto fnend;
11134               if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */
11135                 s[1] += 32;
11136               if ((x = dclarray(s[1],k)) < 0)
11137                 goto fnend;
11138               vp = a_ptr[x];            /* Point to array we just declared */
11139           }
11140           /* debug(F111,"aaconvert array 2",abuf,vp); */
11141           makestr(&ap[0],ckitoa(k));
11142           if (vp) makestr(&vp[0],ckitoa(k));
11143           if (fndiags)
11144            ckmakmsg(fnval,FNVALL,"<ERROR:ASSOCIATIVE_ARRAY:\\f",fn,"()>",NULL);
11145 
11146           /* Copy macro index & value to the arrays and then remove the */
11147           /* macro, so the 'first' pointer keeps indicating the next one. */
11148           /* We could combine the initial counting loop with this one but */
11149           /* then it would be harder to create the array and anyway this */
11150           /* function is plenty fast as it is. */
11151 
11152           for (i = 1; i <= k; ) {
11153               if (!ckmatch(pattern,mactab[first].kwd,0,1)) {
11154                   debug(F111,"aaconvert oddball",mactab[first].kwd,first);
11155                   first++;
11156                   continue;
11157               }
11158               ckstrncpy(tmpbuf,mactab[first].kwd,TMPBUFSIZ); /* Macro name */
11159               s = tmpbuf;                       /* Make writeable copy */
11160               s += slen;                        /* Isolate "index" */
11161               j = strlen(s) - 1;
11162               if (*s != '<' || *(s+j) != '>') { /* Check syntax */
11163                   /* This shouldn't happen */
11164                   debug(F111,"aaconvert ERROR",mactab[first].kwd,first);
11165                   goto fnend;
11166               }
11167               *(s+j) = NUL;             /* Remove final '>' */
11168               debug(F111,"aaconvert",s+1,i);
11169               makestr(&(ap[i]),s+1);    /* Set first array to index */
11170               if (vp)
11171                 makestr(&(vp[i]),mactab[first].mval); /* 2nd to value */
11172               if (xdelmac(first) < 0)
11173                 goto fnend;
11174               i++;
11175           }
11176           sprintf(fnval,"%d",k);        /* SAFE */
11177           p = fnval;                    /* Return size of array */
11178           debug(F110,"aaconvert return",p,0);
11179           failed = 0;                   /* Unset failure flag */
11180           goto fnend;
11181       }
11182 
11183     } /* End of switch() */
11184 
11185 #ifdef FNFLOAT
11186 /*
11187   Floating-point functions.  To be included only if FNFLOAT is defined, which
11188   should happen only if CKFLOAT is also defined, and if the math library is
11189   linked in.  Even then, we might have float-vs-double confusion as well as
11190   confusion about what the final "%f" format effector is supposed to reference
11191   (32 bits, 64 bits, etc).  Expect trouble if CKFLOAT does not match the data
11192   type of math library functions or args.
11193 */
11194     if (cx == FN_FPABS ||               /* Floating-point functions */
11195         cx == FN_FPADD ||
11196         cx == FN_FPDIV ||
11197         cx == FN_FPEXP ||
11198         cx == FN_FPLOG ||
11199         cx == FN_FPLN  ||
11200         cx == FN_FPMOD ||
11201         cx == FN_FPMAX ||
11202         cx == FN_FPMIN ||
11203         cx == FN_FPMUL ||
11204         cx == FN_FPPOW ||
11205         cx == FN_FPSQR ||
11206         cx == FN_FPINT ||
11207         cx == FN_FPSUB ||
11208         cx == FN_FPROU ||
11209         cx == FN_FPSIN ||
11210         cx == FN_FPCOS ||
11211         cx == FN_FPTAN) {
11212         CKFLOAT farg[2], fpresult = 0.0;
11213         char fpbuf[64], * bp0;
11214         double dummy;
11215         /* int sign = 0; */
11216         int i, j, places = 0;
11217         int argcount = 1;
11218 
11219         failed = 1;
11220         p = fnval;
11221         bp0 = bp[0];
11222         if (!bp0)
11223           bp0 = "0";
11224         else if (!*bp0)
11225           bp0 = "0";
11226         if (!isfloat(bp0,0)) {
11227             k = mxlook(mactab,bp0,nmac);
11228             bp0 = (k > -1) ? mactab[k].mval : NULL;
11229             if (bp0) {
11230                 if (!isfloat(bp0,0)) {
11231                     if (fndiags)
11232                       ckmakmsg(fnval,FNVALL,
11233                                "<ERROR:ARG_NOT_FLOAT:\\f",fn,"()>",NULL);
11234                     goto fnend;
11235                 }
11236             }
11237         }
11238         if (cx == FN_FPINT) {           /* Float to int */
11239             failed = 0;
11240             ckstrncpy(fnval,bp0,FNVALL);
11241             for (i = 0; fnval[i]; i++) {
11242                 if (fnval[i] == '.') {
11243                     fnval[i] = NUL;
11244                     break;
11245                 }
11246             }
11247             goto fnend;
11248         }
11249         switch (y) {                    /* These need 2 args */
11250           case FN_FPADD:
11251           case FN_FPDIV:
11252           case FN_FPMOD:
11253           case FN_FPMAX:
11254           case FN_FPMIN:
11255           case FN_FPMUL:
11256           case FN_FPPOW:
11257           case FN_FPSUB:
11258             argcount = 2;
11259         }
11260         /* Missing arguments are supplied as 0.0 */
11261 
11262         debug(F111,fn,"argcount",argcount);
11263         for (i = 0; i < argcount; i++) { /* Get floating-point args */
11264 #ifdef DEBUG
11265             if (deblog) {
11266                 ckmakmsg(fpbuf,
11267                          64,
11268                          "bp[",
11269                          ckitoa(i),
11270                          bp[i] ? bp[i] : "(null)",
11271                          "]"
11272                          );
11273                 debug(F100,fpbuf,"",0);
11274             }
11275 #endif /* DEBUG */
11276             if (!bp[i]) {
11277                 farg[i] = 0.0;
11278             } else if (!*(bp[i])) {
11279                 farg[i] = 0.0;
11280             } else if (!isfloat(bp[i],0)) {
11281                 char * tmp;
11282                 k = mxlook(mactab,bp[i],nmac);
11283                 tmp = (k > -1) ? mactab[k].mval : NULL;
11284                 if (tmp) {
11285                     if (!isfloat(tmp,0)) {
11286                         if (fndiags)
11287                           ckmakmsg(fnval,FNVALL,
11288                                    "<ERROR:ARG_NOT_FLOAT:\\f",fn,"()>",NULL);
11289                         goto fnend;
11290                     }
11291                 }
11292             }
11293             farg[i] = floatval;
11294 
11295 #ifdef DEBUG
11296             if (deblog) {
11297                 sprintf(fpbuf,"farg[%d]=%f",i,farg[i]); /* SAFE */
11298                 debug(F100,fpbuf,"",0);
11299             }
11300 #endif /* DEBUG */
11301         }
11302         if (bp[argcount]) {             /* Get decimal places */
11303             char * s;
11304             s = bp[argcount];
11305             if (!s) s = "";
11306             if (!*s) s = "0";
11307             s = evalx(s);
11308             if (!s) s = "";
11309             if (!*s) {
11310                 evalerr(fn);
11311                 goto fnend;
11312             }
11313             places = atoi(s);
11314         }
11315         errno = 0;
11316         failed = 0;
11317         switch (y) {                    /* Now do the requested function */
11318           case FN_FPABS:                /* Floating-point absolute value */
11319 #ifndef COMMENT
11320             fpresult = fabs(farg[0]);
11321 #else
11322             if (farg[0] < 0.0)
11323               fpresult = 0.0 - farg[0];
11324 #endif /* COMMENT */
11325             break;
11326           case FN_FPADD:                /* FP add */
11327             fpresult = farg[0] + farg[1];
11328             break;
11329           case FN_FPDIV:                /* FP divide */
11330           case FN_FPMOD:                /* FP modulus */
11331             if (!farg[1]) {
11332                 failed = 1;
11333                 if (fndiags)
11334                   ckmakmsg(fnval,FNVALL,
11335                            "<ERROR:DIVIDE_BY_ZERO:\\f",fn,"()>",NULL);
11336             } else
11337               fpresult = (cx == FN_FPDIV) ?
11338                 (farg[0] / farg[1]) :
11339                   fmod(farg[0],farg[1]);
11340             break;
11341           case FN_FPEXP:                /* FP e to the x */
11342             fpresult = (CKFLOAT) exp(farg[0]);
11343             break;
11344           case FN_FPLOG:                /* FP base-10 logarithm */
11345           case FN_FPLN:                 /* FP natural logarithm */
11346             if (farg[0] < 0.0) {
11347                 failed = 1;
11348                 if (fndiags)
11349                   ckmakmsg(fnval,FNVALL,
11350                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11351             } else
11352               fpresult = (cx == FN_FPLOG) ? log10(farg[0]) : log(farg[0]);
11353             break;
11354           case FN_FPMUL:                /* FP multiply */
11355             fpresult = farg[0] * farg[1];
11356             break;
11357           case FN_FPPOW:                /* FP raise to a power */
11358             fpresult = modf(farg[1],&dummy);
11359             if ((!farg[0] && farg[1] <= 0.0) ||
11360                 (farg[0] < 0.0 && fpresult)) {
11361                 failed = 1;
11362                 if (fndiags)
11363                   ckmakmsg(fnval,FNVALL,
11364                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11365             } else
11366               fpresult = pow(farg[0],farg[1]);
11367             break;
11368           case FN_FPSQR:                /* FP square root */
11369             if (farg[0] < 0.0) {
11370                 failed = 1;
11371                 if (fndiags)
11372                   ckmakmsg(fnval,FNVALL,
11373                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11374             } else
11375               fpresult = sqrt(farg[0]);
11376             break;
11377           case FN_FPSUB:                /* FP subtract */
11378             fpresult = farg[0] - farg[1];
11379             break;
11380           case FN_FPROU:                /* FP round */
11381             fpresult = farg[0];
11382             break;
11383           case FN_FPSIN:                /* FP sine */
11384             fpresult = (CKFLOAT) sin(farg[0]);
11385             break;
11386           case FN_FPCOS:                /* FP cosine */
11387             fpresult = (CKFLOAT) cos(farg[0]);
11388             break;
11389           case FN_FPTAN:                /* FP tangent */
11390             fpresult = (CKFLOAT) tan(farg[0]);
11391             break;
11392           case FN_FPMAX:
11393             fpresult = (farg[0] > farg[1]) ? farg[0] : farg[1];
11394             break;
11395           case FN_FPMIN:
11396             fpresult = (farg[0] < farg[1]) ? farg[0] : farg[1];
11397             break;
11398         }
11399 
11400         /* Get here with fpresult = function result */
11401 
11402         if (errno) {                    /* If range or domain error */
11403             failed = 1;
11404             if (fndiags)
11405               ckmakmsg(fnval,FNVALL,
11406                        "<ERROR:FLOATING-POINT-OP:\\f",fn,"()>",NULL);
11407         }
11408         if (failed)                     /* and/or any other kind of error, */
11409           goto fnend;                   /* fail. */
11410 #ifndef COMMENT
11411         /* Call routine containing code that was formerly inline */
11412         ckstrncpy(fnval,fpformat(fpresult,places,cx == FN_FPROU),FNVALL);
11413 #else
11414         {
11415             char fbuf[16];              /* For creating printf format */
11416             if (!fp_rounding &&         /* If printf doesn't round, */
11417                 (places > 0 ||          /* round result to decimal places. */
11418                  (places == 0 && cx == FN_FPROU)))
11419               fpresult += (0.5 / pow(10.0,(CKFLOAT)places));
11420             if (places > 0) {                   /* If places specified */
11421                 /* use specified places to write given number of digits */
11422                 sprintf(fbuf,"%%0.%df",places); /* SAFE */
11423                 sprintf(fnval,fbuf,fpresult);   /* SAFE */
11424             } else {                            /* Otherwise... */
11425 #ifdef COMMENT
11426 /*
11427   Here we want to print exactly fp_digits significant digits, no matter which
11428   side of the decimal point they are on.  That is, we want want the default
11429   format to show the maximum number of non-garbage digits, AND we want the last
11430   such digit to be rounded.  Of course there is no way to do that, since the
11431   digit after the last non-garbage digit is, well, garbage.  So the following
11432   clever ruse does no good.
11433 */
11434                 int sign = 0, m = 0;
11435                 sprintf(fnval,"%f",fpresult);
11436                 if (fnval[0] == '-') sign = 1;
11437                 for (i = sign; i < FNVALL; i++) {
11438                     if (isdigit(fnval[i]))
11439                       m++;
11440                     else
11441                       break;
11442                 }
11443                 if (m > 1) {
11444                     int d = fp_digits - m;
11445                     if (d < 1) d = 1;
11446                     sprintf(fbuf,"%%%d.%df",fp_digits+sign+1,d);
11447                 } else {
11448                     sprintf(fbuf,"%%0.%df",fp_digits);
11449                 }
11450                 sprintf(fnval,fbuf,fpresult);
11451 #else
11452                 /* Go for max precision */
11453                 sprintf(fbuf,"%%0.%df",fp_digits); /* SAFE */
11454                 sprintf(fnval,fbuf,fpresult); /* SAFE */
11455 
11456 #endif /* COMMENT */
11457             }
11458             if (fnval[0] == '-') sign = 1;
11459         }
11460         debug(F111,"fpresult 1",fnval,errno); /* Check for over/underflow */
11461         for (i = sign; fnval[i]; i++) { /* Give requested decimal places */
11462             if (fnval[i] == '.')        /* First find the decimal point */
11463               break;
11464             else if (i > fp_digits + sign - 1) /* replacing garbage */
11465               fnval[i] = '0';           /* digits with 0... */
11466         }
11467         if (fnval[i] == '.') {          /* Have decimal point */
11468             int gotend = 0;
11469             /* d < 0 so truncate fraction */
11470             if (places < 0 || (places == 0 && cx == FN_FPROU)) {
11471                 fnval[i] = NUL;
11472             } else if (places > 0) {    /* d > 0 so this many decimal places */
11473                 i++;                           /* First digit after decimal */
11474                 for (j = 0; j < places; j++) { /* Truncate after d decimal */
11475                     if (!fnval[j+i])           /* places or extend to d  */
11476                       gotend = 1;              /* decimal places. */
11477                     if (gotend || j+i+sign > fp_digits)
11478                       fnval[j+i] = '0';
11479                 }
11480                 fnval[j+i] = NUL;
11481             } else {                    /* d == 0 so Do The Right Thing */
11482                 for (j = (int)strlen(fnval) - 1; j > i+1; j--) {
11483                     if ((j - sign) > fp_digits)
11484                       fnval[j] = '0';
11485                     if (fnval[j] == '0')
11486                       fnval[j] = NUL;   /* Strip useless trailing 0's. */
11487                     else
11488                       break;
11489                 }
11490             }
11491         }
11492 #endif /* COMMENT */
11493         debug(F111,"fpresult 2",fnval,errno);
11494         goto fnend;
11495 
11496     }
11497 #endif /* FNFLOAT */
11498 
11499 #ifdef CKCHANNELIO
11500     if (cx == FN_FSTAT  ||              /* File functions */
11501         cx == FN_FPOS   ||
11502         cx == FN_FEOF   ||
11503         cx == FN_FGCHAR ||
11504         cx == FN_FGLINE ||
11505         cx == FN_FGBLK  ||
11506         cx == FN_FPCHAR ||
11507         cx == FN_FPLINE ||
11508         cx == FN_FPBLK  ||
11509         cx == FN_NLINE  ||
11510         cx == FN_FERMSG ||
11511         cx == FN_FILNO) {
11512         int x = 0, t = 0, channel;
11513         long z;
11514         extern int z_maxchan;
11515 
11516         failed = 1;                     /* Assume failure */
11517         p = fnval;                      /* until we validate args */
11518         if (cx == FN_FERMSG) {
11519             extern int z_error;
11520             if (argn < 1) {
11521                 x = z_error;
11522             } else if (chknum(bp[0])) {
11523                 x = atoi(bp[0]);
11524             } else if (fndiags)
11525               ckmakmsg(fnval,FNVALL,
11526                        "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11527             failed = 0;
11528             ckstrncpy(fnval,ckferror(x),FNVALL);
11529             goto fnend;
11530         }
11531         if (argn < 1) {                 /* All file functions need channel */
11532 	    if (cx == FN_FSTAT) {	/* Except f_status(), e.g. when */
11533 		fnval[0] = '0';		/* called with a variable that */
11534 		fnval[1] = NUL;		/* hasn't been defined yet. */
11535 		failed = 0;
11536 	    } else {
11537 		if (fndiags)
11538 		 ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
11539 	    }
11540             goto fnend;
11541         }
11542         if (rdigits(bp[0])) {           /* Channel must be numeric */
11543             channel = atoi(bp[0]);
11544         } else {                        /* Fail if it isn't */
11545             if (fndiags)
11546               ckmakmsg(fnval,FNVALL,
11547                        "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11548             goto fnend;
11549         }
11550         if (channel < 0 || channel > z_maxchan) { /* Check channel range */
11551             if (fndiags)
11552               ckmakmsg(fnval,FNVALL,
11553                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11554             goto fnend;
11555         }
11556         x = z_getmode(channel);         /* Find out about the channel */
11557 
11558         failed = 0;                     /* Assume success from here down */
11559         if (cx == FN_FSTAT) {           /* Status / modes of channel */
11560             if (x > -1)
11561               x &= FM_RWB;              /* Mask out irrelevant bits */
11562             else                        /* In this case not open is OK */
11563               x = 0;                    /* 0 if not open, 1-7 if open */
11564             sprintf(fnval,"%d",x);      /* SAFE */
11565             goto fnend;
11566         } else if (x < 1) {             /* Not \f_status() so must be open */
11567             failed = 1;
11568             if (fndiags)
11569               ckmakmsg(fnval,FNVALL,"<ERROR:FILE_NOT_OPEN:\\f",fn,"()>",NULL);
11570             goto fnend;
11571         }
11572         switch (y) {                    /* Do the requested function */
11573           case FN_FPOS:                 /* Get position */
11574             z = z_getpos(channel);	/* FIX THIS */
11575             sprintf(fnval,"%ld",z);     /* SAFE */
11576             goto fnend;
11577 
11578           case FN_NLINE:                /* Get line number */
11579             z = z_getline(channel);	/* FIX THIS */
11580             sprintf(fnval,"%ld",z);     /* SAFE */
11581             goto fnend;
11582 
11583           case FN_FEOF:                 /* Check EOF */
11584             t = 0;
11585             if (x & FM_EOF) t = 1;
11586             sprintf(fnval,"%d",t);      /* SAFE */
11587             goto fnend;
11588 
11589           case FN_FILNO:                /* Get file handle */
11590             x = z_getfnum(channel);
11591             sprintf(fnval,"%d",x);      /* SAFE */
11592             goto fnend;
11593 
11594           case FN_FPBLK:                /* Read or write block */
11595           case FN_FGBLK:
11596             if (argn < 2) {
11597                 if (fndiags)
11598                   ckmakmsg(fnval,FNVALL,
11599                            "<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
11600                 goto fnend;
11601             }
11602             if (rdigits(bp[1])) {
11603                 t = atoi(bp[1]);
11604             } else {
11605                 if (fndiags)
11606                   ckmakmsg(fnval,FNVALL,
11607                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11608                 goto fnend;
11609             }
11610           case FN_FGCHAR:               /* Read or write character or line */
11611           case FN_FPCHAR:
11612           case FN_FGLINE:
11613           case FN_FPLINE:
11614             fnval[0] = NUL;
11615             switch (y) {
11616               case FN_FGCHAR: t = z_in(channel,fnval,FNVALL,1,1); break;
11617               case FN_FGLINE: t = z_in(channel,fnval,FNVALL,FNVALL-1,0); break;
11618               case FN_FGBLK:
11619                 if (t >= FNVALL) t = FNVALL - 1;
11620                 t = z_in(channel,fnval,FNVALL,t,1);
11621                 break;
11622               case FN_FPCHAR: t = z_out(channel,bp[1],1,1);  break;
11623               case FN_FPLINE: t = z_out(channel,bp[1],-1,0); break;
11624               case FN_FPBLK:  t = z_out(channel,bp[1],-1,1); break;
11625             }
11626             if (t < 0) {                /* Handle read/write error */
11627                 failed = 1;
11628                 if (fndiags && t != FX_EOF)
11629                   ckmakmsg(fnval,FNVALL,
11630                            "<ERROR:FILE_ERROR_%d:\\f",fn,"()>",NULL);
11631                 goto fnend;
11632             }
11633             if (cx == FN_FGCHAR)        /* Null terminate char */
11634               fnval[1] = NUL;
11635             /* Write (put) functions return numeric status code */
11636             if (cx == FN_FPCHAR || cx == FN_FPLINE || cx == FN_FPBLK)
11637               sprintf(fnval,"%d",t);    /* SAFE */
11638             goto fnend;
11639         }
11640     }
11641 #endif /* CKCHANNELIO */
11642 
11643     if (cx == FN_SQUEEZE) {		/* String function \fsqueeze() */
11644 	/* Squeeze out whitespace */
11645 	/* Add options later for whether to trim leading and trailing blanks */
11646         /* and what to do about control characters, 8-bit whitespace, etc */
11647 	int started = 0;		/* Flag for first non-whitespace */
11648 	int n = 0;			/* Blank/Tab counter */
11649         s = bp[0] ? bp[0] : "";
11650         p = fnval;			/* Result buffer */
11651 	while (*s) {			/* While there is input */
11652 	    if (!started && (*s == ' ' || *s == '\011')) {
11653 		s++;			/* Skip past leading whitespace */
11654 		continue;
11655 	    }
11656 	    started++;			/* Leading whitespace was skipped */
11657 	    if (*s != ' ' && *s != '\011') { /* Have a nonspace char */
11658 		n = 0;			/* reset space counter */
11659 		*p++ = *s++;		/* copy char to destination */
11660 		continue;
11661 	    }
11662 	    if (n++ > 0) {		/* Have blank or tab */
11663 		s++;			/* don't copy more than one */
11664 		continue;
11665 	    }
11666 	    *p++ = ' ';			/* Deposit one space */
11667 	    s++;			/* and go to next source char */
11668 	}
11669 	if (*(p-1) == ' ') p--;		/* Remove trailing space */
11670         *p = NUL;			/* Terminate string */
11671         p = fnval;			/* point to beginning */
11672         goto fnend;			/* Done. */
11673     }
11674     if (cx == FN_PATTERN) {             /* \fpattern() for INPUT */
11675         itsapattern = 1;
11676         if (argn > 0) {
11677             p = fnval;
11678             ckstrncpy(fnval,bp[0],FNVALL);
11679         } else p = "";
11680         goto fnend;
11681     }
11682     if (cx == FN_HEX2N || cx == FN_OCT2N) { /* \fhex2n(), \foct2n() */
11683         p = "0";
11684         if (argn < 1)
11685           goto fnend;
11686         p = ckradix(bp[0], ((cx == FN_HEX2N) ? 16 : 8), 10);
11687         if (!p) {
11688             if (fndiags)
11689               ckmakmsg(fnval,FNVALL,
11690                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11691             goto fnend;
11692         }
11693         failed = 0;
11694         ckstrncpy(fnval,p,FNVALL);
11695         p = fnval;
11696         goto fnend;
11697     }
11698 
11699     if (cx == FN_HEX2IP) {
11700         int c[2], ip[4], i, k;
11701         p = "0";
11702         if (argn < 1)
11703           goto fnend;
11704         s = bp[0];
11705         if ((int)strlen(s) != 8) {
11706             failed = 1;
11707             if (fndiags)
11708               ckmakmsg(fnval,FNVALL,
11709                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11710             goto fnend;
11711         }
11712         p = fnval;
11713         for (k = 0; k < 8; k += 2) {
11714             for (i = 0; i < 2; i++) {
11715                 c[i] = *s++;
11716                 if (islower(c[i])) c[i] = toupper(c[i]);
11717                 if (c[i] >= '0' && c[i] <= '9') {
11718                     c[i] -= 0x30;
11719                 } else if (c[i] >= 'A' && c[i] <= 'F') {
11720                     c[i] -= 0x37;
11721                 } else {
11722                     failed = 1;
11723                     if (fndiags)
11724                       ckmakmsg(fnval,FNVALL,
11725                                "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11726                     goto fnend;
11727                 }
11728                 ip[k/2] = c[0] << 4 | c[1];
11729             }
11730             sprintf(p,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]); /* SAFE */
11731         }
11732         goto fnend;
11733     }
11734     if (cx == FN_IP2HEX) {
11735         int ip[4], i;
11736         char * q;
11737         p = "00000000";
11738         if (argn < 1)
11739           goto fnend;
11740         s = bp[0];
11741         p = fnval;
11742         for (i = 0; i < 3; i++) {
11743             q = ckstrchr(s,'.');
11744             if (q) {
11745                 *q++ = NUL;
11746                 ip[i] = atoi(s);
11747                 s = q;
11748             } else {
11749                 failed = 1;
11750                 if (fndiags)
11751                   ckmakmsg(fnval,FNVALL,
11752                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11753                 goto fnend;
11754             }
11755         }
11756         ip[3] = atoi(s);
11757         sprintf(p,"%02x%02x%02x%02x",ip[0],ip[1],ip[2],ip[3]); /* SAFE */
11758         goto fnend;
11759     }
11760     if (cx == FN_RADIX) {
11761         failed = 1;
11762         p = fnval;
11763         if (argn < 3) {
11764             if (fndiags)
11765               ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
11766             goto fnend;
11767         }
11768         if (!rdigits(bp[1]) || !rdigits(bp[2])) {
11769             if (fndiags)
11770               ckmakmsg(fnval,FNVALL,
11771                        "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11772             goto fnend;
11773         }
11774         p = ckradix(bp[0],atoi(bp[1]),atoi(bp[2]));
11775         if (!p) {
11776             if (fndiags)
11777               ckmakmsg(fnval,FNVALL,
11778                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11779             goto fnend;
11780         }
11781         failed = 0;
11782         ckstrncpy(fnval,p,FNVALL);
11783         p = fnval;
11784         goto fnend;
11785     }
11786     if (cx == FN_JOIN) {
11787         int i, x, y, z, flag, flag2, hi, lo, max, seplen, grouping = 0;
11788         char abuf[16], c, *s, *q, *sep = NULL;
11789         char * gr_opn = "\"{'([<";      /* Group open brackets */
11790         char * gr_cls = "\"}')]>";      /* Group close brackets */
11791         char lb[2], rb[2];              /* Selected left and right brackets */
11792 	int csv = 0, tsv = 0;		/* Function flags */
11793 	char specialchar = 0;		/* Field char that triggers grouping */
11794 	char *s2 = NULL;		/* Address of malloc'd storage */
11795 
11796         failed = 1;                     /* Assume failure */
11797         fnval[0] = NUL;
11798         debug(F101,"FNJOIN ARGN","",argn);
11799 
11800         ckstrncpy(abuf,bp[0],16);       /* Get array reference */
11801         s = abuf;
11802         if ((x = arraybounds(s,&lo,&hi)) < 0) {  /* Get index and bounds */
11803             if (fndiags)
11804               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
11805             goto fnend;
11806         }
11807         p = fnval;                      /* Point to result */
11808         max = a_dim[x];                 /* Size of array */
11809         if (lo < 0) lo = 1;             /* Use given range if any */
11810         if (lo > max) lo = max;
11811 #ifdef COMMENT
11812 	hi = max;
11813 #else
11814 /*
11815   This is a workaround for the problem in which the dimension of the \&_[]
11816   array (but not its contents) grows upon entry to a SWITCH block.  But this
11817   code prevents the dimension from growing.  Go figure.
11818 */
11819         if (hi < 0) {			/* Bounds not given */
11820             if (x)			/* Regular array */
11821 	      hi = max;
11822 	    else			/* Argument vector array */
11823 	      for (hi = max; hi >= lo; hi--) { /* ignore any trailing */
11824 		  if (!a_ptr[x][hi]) continue; /* empty elements */
11825 		  if (!*(a_ptr[x][hi])) continue;
11826 		  break;
11827 	      }
11828 	}
11829 #endif /* COMMENT */
11830         if (hi > max) hi = max;
11831         failed = 0;                     /* Unset failure flag */
11832         if (max < 1)
11833           goto fnend;
11834         sep = " ";                      /* Separator */
11835         lb[0] = NUL;			/* Group start char (as string) */
11836         rb[0] = NUL;
11837         lb[1] = NUL;			/* Group end char as string */
11838         rb[1] = NUL;
11839 
11840         if (argn > 1) {
11841 	    if (bp[1]) if (*bp[1]) {	/* If arg1 given and not empty */
11842 		if (!strcmp(bp[1],"CSV")) { /* Special "CSV" symbolic arg */
11843 		    csv++;		/* Make a comma separated list */
11844 		    sep = ",";		/* Comma */
11845 		    specialchar = *sep;	/* Separator is special character */
11846 		    grouping = 1;	/* Group with doublequotes */
11847 		    lb[0] = '"';	/* and here */
11848 		    rb[0] = '"';	/* they are */
11849 		} else if (!strcmp(bp[1],"TSV")) { /* "TSV" symbolic arg */
11850 		    tsv++;		/* Make a Tab separated list */
11851 		    sep = "\011";	/* Tab */
11852 		    specialchar = *sep;
11853 		    grouping = 0;	/* No grouping */
11854 		} else			/* Normal case */
11855 		  sep = bp[1];		/* use the separator char specified */
11856 	    }
11857 	}
11858         if (argn > 2 && !csv && !tsv) {	/* Grouping? */
11859             char * bp2 = bp[2];
11860             if (!bp2) bp2 = "0";
11861             if (!*bp2) bp2 = "0";
11862             if (chknum(bp2)) {
11863                 grouping = atoi(bp2);
11864                 if (grouping < 0 || grouping > 63)
11865                   grouping = 1;
11866             } else {
11867                 failed = 1;
11868                 if (fndiags)
11869                   ckmakmsg(fnval,FNVALL,
11870                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11871                 goto fnend;
11872             }
11873             if (grouping) {             /* Take lowest-order one */
11874                 int j, k;               /* and set the others to 0 */
11875                 for (k = 0; k < 6; k++) {
11876                     j = 1 << k;
11877                     if (grouping & j) {
11878                         lb[0] = gr_opn[k];
11879                         rb[0] = gr_cls[k];
11880                         break;
11881                     }
11882                 }
11883             }
11884         }
11885 	if (!csv && !tsv) {		/* Normal case, not CSV or TSV */
11886 	    specialchar = SP;		/* Special character is space */
11887 	    if (argn > 3)		/* Nonzero 4th arg for no separator */
11888 	      if (chknum(bp[3]))
11889 		if (atoi(bp[3]) > 0)
11890 		  sep = NULL;
11891 	    if (!sep) {
11892 		sep = "";
11893 		seplen = 0;
11894 	    } else
11895 	      seplen = strlen(sep);
11896 	}
11897         for (i = lo; i <= hi; i++) {    /* Loop thru selected array elements */
11898             s = a_ptr[x][i];            /* Get next element */
11899             if (!s)
11900               s = "";
11901             flag = 0;                   /* Flag to indicate grouping needed */
11902 	    flag2 = 0;			/* Flag for internal doublequotes */
11903             if (grouping) {             /* Does this element need quoting? */
11904                 q = s;                  /* Look for special character */
11905                 while ((c = *q++)) {	/* If found */
11906 		    if (c == specialchar) /* grouping is required */
11907 		      flag++;
11908 		    if (csv && (c == '"')) /* Character that needs doubling */
11909 		      flag2++;		   /* in comma-separated list */
11910 		    if (flag && !csv)	/* Exit early if no more to do */
11911 		      break;
11912 		}
11913             }
11914             y = strlen(s);              /* Get length of this element */
11915 	    if ((y > 0) && csv && !flag) { /* CSV item needs grouping */
11916 		if (s[0] == SP || s[y-1] == SP || /* if it has leading */
11917 		    s[0] == HT || s[y-1] == HT)	/* or trailing whitespace */
11918 		  flag++;		/* then it needs grouping */
11919 	    }
11920 	    if (flag || flag2) {	/* String needs grouping or quoting */
11921 		char *ss = s;
11922                 q = (char *)malloc(y + flag2 + 3); /* Make new buffer */
11923 		if (q) {
11924 		    s2 = q;		/* and this is what to free */
11925 		    if (flag)		/* If grouping */
11926 		      *q++ = lb[0];	/* put opening group quote */
11927 		    while (*ss) {	/* Loop through string */
11928 			if (flag2 && (*ss == '"')) /* If CSV and this a '"' */
11929 			  *q++ = *ss;	           /* double it. */
11930 			*q++ = *ss++;	/* Copy the character */
11931 		    }
11932 		    if (flag)		/* If grouping */
11933 		      *q++ = rb[0];	/* add closing group quote */
11934 		    *q = NUL;		/* terminate the result. */
11935 		    s = s2;
11936 		    y = strlen(s);
11937 		}
11938 	    }
11939             z = 0;                      /* Number of chars copied */
11940             flag = 0;                   /* flag is now buffer-overrun flag */
11941             if (y > 0)                  /* If this string is not empty */
11942               z = ckstrncat(fnval,s,FNVALL); /* copy it. */
11943 	    if (s2) free(s2);		/* Free temp storage */
11944             if (z < y)                  /* Now check for buffer overrun. */
11945               flag++;
11946             if (!flag && *sep && i < hi) { /* If buffer still has room */
11947                 z = ckstrncat(fnval,sep,FNVALL); /* copy delimiter */
11948                 if (z < seplen)
11949                   flag++;
11950             }
11951             if (flag) {
11952                 failed = 1;
11953                 if (fndiags)
11954                   ckmakmsg(fnval,FNVALL,
11955                            "<ERROR:RESULT_TOO_LONG:\\f",fn,"()>",NULL);
11956                 goto fnend;
11957             }
11958         }
11959 	isjoin = 1;
11960         goto fnend;
11961     }
11962     if (cx == FN_SUBST) {               /* \fsubstitute() */
11963         CHAR c, * s, * r, * tp[2], buf1[256], buf2[256], buf3[256];
11964         int len, i, j, state = 0, lo = 0, hi = 0;
11965 
11966         failed = 0;
11967         p = fnval;                      /* Result pointer */
11968         *p = NUL;
11969         if (!bp[0])                     /* No target, no result*/
11970           goto fnend;
11971 
11972         len = strlen(bp[0]);            /* Length of source */
11973         if (len == 0)
11974           goto fnend;
11975         if (len > FNVALL) {
11976             failed = 1;
11977             if (fndiags)
11978               ckmakmsg(fnval,FNVALL,
11979                        "<ERROR:RESULT_TOO_LONG:\\f",fn,"()>",NULL);
11980             goto fnend;
11981         }
11982         if (!bp[1]) {
11983             ckstrncpy(bp[0],fnval,FNVALL);
11984             goto fnend;
11985         }
11986         tp[0] = buf1;                   /* For s2-s3 interpretation loop */
11987         tp[1] = buf2;
11988 
11989         for (i = 0; i < 256; i++) {     /* Initialize working buffers */
11990             buf1[i] = 0;                /* s2 expansion buffer */
11991             buf2[i] = 0;                /* s3 expansion buffer */
11992             buf3[i] = i;                /* Translation table */
11993         }
11994         for (i = 0; i < 2; i++) {       /* Interpret s2 and s3 */
11995             s = (CHAR *)bp[i+1];        /* Arg pointer */
11996             if (!s) s = (CHAR *)"";
11997             r = tp[i];                  /* To construct interpreted arg */
11998             j = 0;                      /* Output buf pointer */
11999             state = 0;                  /* Initial state */
12000             while (c = *s++) {          /* Loop thru arg chars */
12001                 if (j > 255)            /* Output buf full */
12002                   break;
12003                 switch (state) {
12004                   case 0:               /* Normal state */
12005                     switch (c) {
12006                       case '\\':        /* Have quote */
12007                         state = 1;
12008                         break;
12009                       case '[':         /* Have range starter */
12010                         state = 2;
12011                         break;
12012                       default:          /* Anything else */
12013                         r[j++] = c;
12014                         break;
12015                     }
12016                     continue;
12017                   case 1:               /* Quoted char */
12018                     r[j++] = c;
12019                     state = 0;
12020                     continue;
12021                   case 2:               /* Range bottom */
12022                     lo = c;
12023                     state++;
12024                     continue;
12025                   case 3:               /* Range separater */
12026                     if (c != '-') {
12027                         failed = 1;
12028                         if (fndiags)
12029                           ckmakmsg(fnval,FNVALL,
12030                                    "<ERROR:BAD_RANGE:\\f",fn,"()>",NULL);
12031                         goto fnend;
12032                     }
12033                     state++;
12034                     continue;
12035                   case 4:               /* Range top */
12036                     hi = c;
12037                     state++;
12038                     continue;
12039                   case 5:               /* Range end */
12040                     if (c != ']') {
12041                         failed = 1;
12042                         if (fndiags)
12043                           ckmakmsg(fnval,FNVALL,
12044                                    "<ERROR:BAD_RANGE:\\f",fn,"()>",NULL);
12045                         goto fnend;
12046                     }
12047                     for (k = lo; k <= hi && j < 255; k++) /* Fill in */
12048                       r[j++] = k;
12049                     lo = 0; hi = 0;     /* Reset */
12050                     state = 0;
12051                     continue;
12052                 }
12053             }
12054         }
12055         for (i = 0; i < 256 && buf1[i]; i++) {  /* Create translation table */
12056             k = (unsigned)buf1[i];
12057             buf3[k] = buf2[i];
12058         }
12059         s = (CHAR *)bp[0];              /* Point to source string */
12060         for (i = 0; i < len; i++) {     /* Translation loop */
12061             k = (unsigned)s[i];         /* Get next char */
12062             if (!buf3[k])               /* Remove this char */
12063               continue;
12064             *p++ = buf3[k];             /* Substitute this char */
12065         }
12066         *p = NUL;
12067         p = fnval;
12068         goto fnend;
12069     }
12070 
12071 #ifndef NOSEXP
12072     if (cx == FN_SEXP) {                /* \fsexpression(arg1) */
12073         char * p2;
12074         fsexpflag++;
12075         p = (argn > 0) ? dosexp(bp[0]) : "";
12076         fsexpflag--;
12077         p2 = fnval;
12078         while ((*p2++ = *p++)) ;
12079         p = fnval;
12080         goto fnend;
12081     }
12082 #endif /* NOSEXP */
12083 
12084     if (cx == FN_CMDSTK) {              /* \fcmdstack(n1,n2) */
12085         int i, j, k;
12086         char * s;
12087 
12088         if (bp[0])
12089           val1 = *(bp[0]) ? evalx(bp[0]) : ckitoa(cmdlvl);
12090         else
12091           val1 = ckitoa(cmdlvl);
12092 #ifdef COMMENT
12093         free(bp[0]);                    /* (evalx() always uses same buffer) */
12094         bp[0] = NULL;                   /* (not any more!) */
12095 #endif /* COMMENT */
12096         failed = 1;
12097         if (argn > 1) {
12098 #ifdef COMMENT
12099             makestr(&(bp[0]),val1);
12100             val1 = bp[0];
12101 #endif /* COMMENT */
12102             val2 = *(bp[1]) ? evalx(bp[1]) : "0";
12103             if (!(chknum(val1) && chknum(val2))) {
12104                 if (fndiags)
12105                   ckmakmsg(fnval,FNVALL,
12106                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
12107                 goto fnend;
12108             }
12109         } else {
12110             val1 = ckitoa(cmdlvl);
12111             val2 = "0";
12112         }
12113         i = atoi(val1);                 /* Level */
12114         j = atoi(val2);                 /* Flags */
12115         if (i < 0 || i > cmdlvl) {
12116             if (fndiags)
12117               ckmakmsg(fnval,FNVALL,
12118                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
12119             goto fnend;
12120         }
12121         failed = 0;
12122         p = fnval;
12123         k = cmdstk[i].src;              /* What (prompt, file, macro) */
12124         if (j) {
12125             ckstrncpy(fnval,ckitoa(k),FNVALL);
12126             goto fnend;
12127         }
12128         switch (k) {
12129           case CMD_KB:
12130             ckstrncpy(fnval,"(prompt)",FNVALL);
12131             break;
12132           case CMD_TF:
12133             s = tfnam[cmdstk[i].lvl];
12134             if (!zfnqfp(s,FNVALL,fnval))
12135               ckstrncpy(fnval,s,FNVALL);
12136             break;
12137           case CMD_MD:
12138             ckstrncpy(fnval,m_arg[cmdstk[i].lvl][0],FNVALL);
12139             break;
12140         }
12141         goto fnend;
12142     }
12143 #ifdef CKFLOAT
12144     if (cx == FN_DIFDATE) {             /* \fdiffdates(d1,d2) */
12145         char * d1, * d2;
12146         d1 = bp[0] ? bp[0] : ckdate();
12147         d2 = bp[1] ? bp[1] : ckdate();
12148         p = (char *)cmdiffdate(d1,d2);
12149         if (!p) {
12150             failed = 1;
12151             if (fndiags) {
12152                 ckmakmsg(fnval,FNVALL,"<ERROR:BAD_DATE:\\f",fn,"()>",NULL);
12153                 p = fnval;
12154             }
12155         }
12156         goto fnend;
12157     }
12158 #endif /* CKFLOAT */
12159     if (cx == FN_CMPDATE) {             /* \fcmddates(d1,d2) */
12160         int x = 0;
12161         char d1[18], d2[18], * dp;
12162         failed = 0;
12163         d1[0] = NUL;
12164         d2[0] = NUL;
12165         p = fnval;
12166         dp = cmcvtdate(bp[0],1);
12167         if (dp) {
12168             ckstrncpy(d1,dp,18);
12169             if ((dp = cmcvtdate(bp[1],1))) {
12170                 ckstrncpy(d2,dp,18);
12171                 x = 1;
12172             }
12173         }
12174         if (x == 0) {
12175             failed = 1;
12176             if (fndiags)
12177               ckmakmsg(fnval,FNVALL,"<ERROR:BAD_DATE:\\f",fn,"()>",NULL);
12178         } else {
12179             x = strcmp(d1,d2);
12180             if (x > 0)
12181               x = 1;
12182             else if (x < 0)
12183               x = -1;
12184             sprintf(fnval,"%d",x);
12185         }
12186         goto fnend;
12187     }
12188     if (cx == FN_TOGMT) {               /* \futcdate(d1) */
12189         char * d1, * dp;
12190         char datebuf[32];
12191         char d2[32];
12192         p = fnval;
12193         failed = 1;
12194         if ((dp = cmcvtdate(bp[0],1))) { /* The given date */
12195             ckstrncpy(datebuf,dp,18);
12196             ckstrncpy(d2,dp,18);        /* local time */
12197             ckstrncat(datebuf,"Z",19);  /* Same time GMT */
12198             if ((dp = cmcvtdate(datebuf,1))) /* converted to local time */
12199               ckstrncpy(datebuf,dp,18);
12200             if ((p = (char *)cmdiffdate(d2,datebuf))) { /* Get offset */
12201                 ckstrncat(d2,p,32);     /* Append offset to local time */
12202                 if ((dp = cmcvtdate(d2,1))) {
12203                     failed = 0;
12204                     ckstrncpy(fnval,dp,FNVALL);
12205                     p = fnval;
12206                 }
12207             }
12208         }
12209         if (failed && fndiags)
12210           ckmakmsg(fnval,FNVALL,"<ERROR:BAD_DATE:\\f",fn,"()>",NULL);
12211         goto fnend;
12212     }
12213     if (cx == FN_DELSEC) {              /* \fdelta2secs(delta-time) */
12214         long secs;
12215         p = fnval;
12216         if ((x = delta2sec(bp[0],&secs)) < 0) {
12217             failed = 1;
12218             if (fndiags)
12219               ckmakmsg(fnval,FNVALL,
12220                        (x == -1) ?
12221                          "<ERROR:BAD_DELTA_TIME:\\f" :
12222                          "<ERROR:OVERFLOW:\\f",
12223                        fn,
12224                        "()>",
12225                        NULL
12226                        );
12227             goto fnend;
12228         }
12229         sprintf(p,"%ld",secs);
12230         goto fnend;
12231     }
12232     if (cx == FN_PC_DU) {
12233         char c, * s = bp[0];
12234         if (!s) s = "";
12235         p = fnval;
12236         while ((c = *s++)) {
12237             if (c == ':') {
12238                 if (*s != '\\')
12239                   *p++ = '/';
12240             } else if (c == '\\') {
12241                 *p++ = '/';
12242             } else {
12243                 *p++ = c;
12244             }
12245         }
12246         *p = NUL;
12247         p = fnval;
12248         goto fnend;
12249     }
12250     if (cx == FN_PC_UD) {               /* Unix to DOS path */
12251         char c, * s = bp[0];
12252         if (!s) s = "";
12253         if (*s == '~') {                /* Skip leading tilde */
12254             s++;
12255             if (*s == '/')
12256               s++;
12257         }
12258         p = fnval;
12259         while ((c = *s++))
12260           *p ++ = (c == '/') ? '\\' : c;
12261         *p = NUL;
12262         p = fnval;
12263         goto fnend;
12264     }
12265     if (cx == FN_KWVAL) {               /* Keyword=Value */
12266         p = dokwval(bp[0],bp[1]?bp[1]:"=");
12267         goto fnend;
12268     }
12269 #ifdef COMMENT
12270 /* Cute idea but doesn't work */
12271     if (cx == FN_SLEEP || cx == FN_MSLEEP) {
12272         p = "";
12273         if (chknum(bp[0])) {
12274             x = atoi(bp[0]);
12275         } else {
12276             failed = 1;
12277             if (fndiags) {
12278                 ckmakmsg(fnval,FNVALL,
12279                          "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
12280                 p = fnval;
12281             }
12282             goto fnend;
12283         }
12284         if (cx == FN_SLEEP)
12285           x *= 1000;
12286         msleep(x);
12287         goto fnend;
12288     }
12289 #endif /* COMMENT */
12290 
12291 #ifdef NT
12292     if (cx == FN_SNAME) {
12293         GetShortPathName(bp[0],fnval,FNVALL);
12294         goto fnend;
12295     }
12296     if (cx == FN_LNAME) {
12297         ckGetLongPathName(bp[0],fnval,FNVALL);
12298         goto fnend;
12299     }
12300 #endif /* NT */
12301 
12302 /*
12303   \femailaddress():
12304   Picks the email address out of an RFC 2822 From: or Sender: header.
12305   Added 26 Nov 2005.  Handles all common, and some uncommon, cases but
12306   doesn't totally bother about nested comments.  Needed this for fetching
12307   email from a POP server and then constructing the BSD "From " line.
12308   Works with or without the "From: " or "Sender: " tag.
12309 */
12310     if (cx == FN_EMAIL) {
12311         char c, * s = bp[0], * s2, * s3, * ap = "";
12312 	int k, state = 0, quote = 0, infield = 0;
12313 	int pc = 0;			/* For nested comments */
12314         if (!s) s = "";
12315 	if (!*s) goto xemail;
12316 
12317 	if (ckindex("From: ",s,0,0,0) == 1) s += 5;
12318 	if (ckindex("Sender: ",s,0,0,0) == 1) s += 7;
12319 
12320 	k = strlen(s);			/* Strip junk from end */
12321 	if (k < 1) goto xemail;
12322 	k--;
12323 	while (k >= 0 && s[k] == CR || s[k] == LF)
12324 	  s[k--] = NUL;
12325 	while (k >= 0 && s[k] == SP || s[k] == HT)
12326 	  s[k--] = NUL;
12327 	if (k == 0)
12328 	  goto xemail;
12329 
12330 #ifndef COMMENT			     /* Simple method if not 100% foolproof */
12331 	k = 0;
12332 	for (s2 = s; *s2; s2++) {	/* Find at-sign */
12333 	    if (*s2 == '@') {
12334 		k++;			/* If more than one use rightmost */
12335 		s3 = s2;
12336 	    }
12337 	}
12338 	if (k < 1)			/* No at-sign */
12339 	  goto xemail;
12340 
12341 	for (ap = s3-1; ap >= s; ap--) { /* Back up to beginning of address */
12342 	    if (isspace(*ap) || *ap == '<') {
12343 		ap++;
12344  		break;
12345 	    }
12346 	    if (ap == s)
12347 	      break;
12348 	}
12349 	for (s2 = s3+1; *s2; s2++) {	/* Find end of address */
12350 	    if (isspace(*s2) || *s2 == '>')
12351 	      break;
12352 	}
12353 	*s2-- = NUL;
12354 	if (*ap == '[' && *s2 == ']') {	/* Handle [blah@blah.blah] */
12355 	    ap++;
12356 	    *s2 = NUL;
12357 	}
12358 	if (!ckstrcmp(ap,"mailto:",7,0)) /* Handle mailto: URLs */
12359 	  ap += 7;
12360 
12361 #else  /* Too complicated and error-prone */
12362 
12363 	k = 0;
12364 	for (s2 = s; *s2; s2++) {	/* Strip leading whitespace */
12365 	    if (*s2 == SP || *s2 == HT) {
12366 		k = 1;
12367 		break;
12368 	    }
12369 	}
12370 	if (!k) {			/* Simple address */
12371 	    ap = s;
12372 	    goto xemail;
12373 	}
12374 	do {				/* Not simple, have to extract it */
12375 	    if (quote) {
12376 		quote = 0;
12377 		continue;
12378 	    } else if (*s == '\\') {
12379 		quote = 1;
12380 		continue;
12381 	    }
12382 	    switch (state) {
12383 	      case 0:
12384 		if (!infield && *s == '"') { /* Quoted string */
12385 		    infield = 1;
12386 		    c = '"';
12387 		    state = 1;
12388 		} else if (!infield && *s == '(') { /* Comment in parens */
12389 		    pc++;
12390 		    infield = 1;
12391 		    c = ')';
12392 		    if (*ap) *s = NUL;
12393 		    state = 1;
12394 		} else if (!infield && *s == '<') { /* Address */
12395 		    infield = 1;
12396 		    c = '>';
12397 		    ap = s+1;
12398 		    state = 2;
12399 		} else if (infield && (*s == SP || *s == HT)) {
12400 		    infield = 0;
12401 		} else {		/* One or more bare words */
12402 		    infield = 1;	/* Could be an address */
12403 		    if (!*ap) ap = s;	/* Could be comments */
12404 		}
12405 		continue;
12406 	      case 1:			/* In Quoted string or Comment */
12407 		if (infield && *s == c) { /* Look for end */
12408 		    infield = 0;
12409 		    *s++ = NUL;
12410 		    while (*s == SP || *s == HT) s++;
12411 		    if (!*ap)
12412 		      ap = s;
12413 		    state = 0;
12414 		}
12415 		continue;
12416 	      case 2:			/* In address */
12417 		if (infield && *s == c) { /* Looking for end */
12418 		    infield = 0;
12419 		    *s = NUL;
12420 		    break;
12421 		}
12422 	    }
12423 	} while (*s++);
12424 
12425       xemail:
12426 	if (*ap) {
12427 	    while (*ap == SP || *ap == HT) ap++;
12428 	}
12429 	k = strlen(ap) - 1;
12430 	while (k >= 0 && (ap[k] == SP || ap[k] == HT))
12431 	  ap[k--] = NUL;
12432 	if (*ap) {
12433 	    failed = 0;
12434 	    if (*ap == '<') {
12435 		k = strlen(ap);
12436 		if (*(ap+k-1) == '>') {
12437 		    ap[k-1] = NUL;
12438 		    ap++;
12439 		}
12440 	    }
12441 	} else
12442 	  failed = 1;
12443 	/* Here we might also want check against "*@*.*" */
12444 #endif	/* COMMENt */
12445       xemail:
12446 	ckstrncpy(fnval,ap,FNVALL);
12447 	goto fnend;
12448     }
12449 
12450 #ifdef SEEK_CUR
12451 /*
12452    \fpictureinfo():   Get dimensions of GIF or JPG image - fdc June 2006.
12453     NOTE: The height and width of a JPG image do not necessarily indicate
12454     an image's actual orientation.  This is given by the Exif Orientation
12455     tag (0x0122).  But locating it in a JPG file without having a full-blown
12456     Exif parser is probably not possible.
12457 */
12458     if (cx == FN_PICTURE) {
12459 	FILE *fp = NULL;
12460 	int c, x, w = 0, h = 0, eof = 0;
12461 	unsigned int i, j, k;
12462 	unsigned char buf[1024];
12463 	char abuf[16], * p, * s;
12464 	char ** ap = NULL;
12465 #ifdef UNIX
12466 	char * tx;
12467 #endif /* UNIX */
12468 
12469 	p = fnval;			/* Point to result */
12470 	failed = 1;			/* Assume failure */
12471 	s = bp[0];
12472 #ifdef UNIX
12473 	if (*s == '~') {
12474 	    tx = tilde_expand(bp[0]);
12475 	    if (tx) if (*tx) {
12476 		free(bp[0]);
12477 		bp[0] = NULL;
12478 		makestr(&(bp[0]),tx);
12479 	    }
12480 	    s = bp[0];
12481 	}
12482 #endif /* UNIX */
12483 
12484 	if (argn > 1) {
12485 	    int xi;
12486 	    ckstrncpy(abuf,bp[1],16);	/* Get array reference */
12487 	    s = abuf;
12488 	    if (*s == CMDQ) s++;
12489 	    if (fndiags)		/* Default is this error message */
12490 	      ckmakmsg(fnval,FNVALL,
12491 		       "<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
12492 	    if (s[0] != '&')		/* "Address" of array */
12493 	      goto fnend;
12494 	    if (s[2])
12495 	      if (s[2] != '[' || s[3] != ']')
12496 		goto fnend;
12497 	    if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */
12498 	      s[1] += 32;
12499 	    if ((xi = dclarray(s[1],3)) < 0) /* three elements */
12500 	      goto fnend;
12501 	    ap = a_ptr[xi];		/* Point to array we just declared */
12502 	}
12503 	s = bp[0];			/* Filename */
12504 	failed = 0;			/* From here on we don't fail */
12505 	p[0] = '0';			/* Default return value */
12506 	p[1] = NUL;
12507 
12508 	/* Tail anchor removed 2013-10-15 -fdc */
12509 	if (!ckmatch("*.{jpg,jpeg,gif}",s,0,1)) /* Appropriate name? */
12510 	  goto fnend;			/* No, fail */
12511 
12512 	fp = fopen(s, "r");		/* Open it */
12513 	if (fp == NULL) {		/* Can't, fail */
12514 	    p[0] = '-';
12515 	    p[1] = '1';
12516 	    p[2] = NUL;			/* Return -1 */
12517 	    goto fnend;
12518 	}
12519 	k = strlen(s);
12520 	if (!ckstrcmp(&s[k-4],".gif",4,0)) { /* GIF file */
12521 	    if (fread(buf,1,10,fp) != 10) {
12522 		fclose(fp);
12523 		goto fnend;
12524 	    }
12525 	    /* Check signature */
12526 	    if (ckstrcmp((char *)buf,"GIF87a",6,0) &&
12527 		ckstrcmp((char *)buf,"GIF89a",6,0)) {
12528 		fclose(fp);
12529 		goto fnend;
12530 	    }
12531 	    w = buf[6] + 256 * buf[7];
12532 	    h = buf[8] + 256 * buf[9];
12533 	    goto picend;
12534 	} else if (!ckstrcmp(&s[k-4],".jpg",4,0) || /* JPEG file */
12535 		   !ckstrcmp(&s[k-5],".jpeg",5,0)) { /* (according to name) */
12536 	    if (fread(buf,1,2,fp) != 2) {            /* Read 1st bytes */
12537 		fclose(fp);
12538 		goto fnend;
12539 	    }
12540 	    if (buf[0] != 0xff || buf[1] != 0xd8) { /* Check signature */
12541 		fclose(fp);                         /* Should be FFD8 */
12542 		goto fnend;
12543 	    }
12544 	    eof = 0;
12545 	    while (!eof) {		/* Loop for each marker */
12546 		while (!eof) {		/* Find next marker */
12547 		    c = getc(fp);
12548 		    if (c == (unsigned int)EOF) {
12549 			eof++;
12550 			break;
12551 		    }
12552 		    if (c == 0xff) {
12553 			buf[0] = c;
12554 			c = getc(fp);
12555 			if (c == (unsigned int)EOF) {
12556 			    eof++;
12557 			    break;
12558 			}
12559 			buf[1] = c;
12560 			if (c == 0xd9)  /* FFD9 means End of Image */
12561 			  eof++;
12562 			if (c >= 0xc0 && c <= 0xfe)
12563 			  break;
12564 		    }
12565 		}
12566 		if (eof) break;
12567 		x = buf[1];
12568 		if (x == 0xc0 || x == 0xc1 || x == 0xc2 || x == 0xc3 ||
12569 		    x == 0xc9 || x == 0xca || x == 0xcb) {
12570 		    if (fread(buf,1,7,fp) != 7) {
12571 			fclose(fp);
12572 			goto fnend;
12573 		    }
12574 		    h = buf[3] * 256 + buf[4];
12575 		    w = buf[5] * 256 + buf[6];
12576 		    goto picend;
12577 		} else {		/* Not a desired field */
12578 		    if (feof(fp)) {
12579 			eof++;
12580 			break;
12581 		    }
12582 		    if (fread(buf,1,2,fp) != 2) { /* Length of this field */
12583 			fclose(fp);
12584 			goto fnend;
12585 		    }
12586 		    j = 256 * buf[0] + buf[1] - 2; /* Skip next field */
12587 		    if (CKFSEEK(fp,(CK_OFF_T)j,SEEK_CUR) != 0) {
12588 			fclose(fp);
12589 			goto fnend;
12590 		    }
12591 		}
12592 	    }
12593 	}
12594       picend:
12595 
12596 	if (ap) {
12597 	    char * s;
12598 	    makestr(&(ap[0]),"2");
12599 	    makestr(&(ap[1]),ckitoa(w));
12600 	    makestr(&(ap[2]),ckitoa(h));
12601 	    s = jpgdate(fp);
12602 	    debug(F110,"jpgdate",s,0);
12603 	    if (s) if (*s) makestr(&(ap[3]),s);
12604 	}
12605 	fclose(fp);
12606 	if (w > 0 && h > 0) {
12607 	    if (w > h) p[0] = '1';	/* Landscape */
12608 	    else if (h > w) p[0] = '2';	/* Portrait */
12609 	    else p[0] = '3';		/* Square - 2013-10-05 */
12610 	}
12611 	goto fnend;
12612     }
12613 #endif	/* SEEK_CUR */
12614 
12615     if (cx == FN_PID) {
12616 	int x = -1;
12617 	if (chknum(bp[0])) {		/* Need numeric argument */
12618 	    int pid;
12619 	    pid = atoi(bp[0]);		/* Convert to int */
12620 #ifdef UNIX
12621 	    if (kill(pid,0) < 0) {	/* Test it */
12622 		if (errno ==
12623 #ifdef ESRCH
12624 		    ESRCH		/* No such process */
12625 #else
12626 		    3
12627 #endif	/* ESRCH */
12628 		    )
12629 		  x = 0;
12630 	    } else			/* Process exists */
12631 	      x = 1;
12632 #endif	/* UNIX */
12633 	}
12634 	sprintf(fnval,"%d",x);		/* SAFE */
12635 	goto fnend;
12636     }
12637 
12638     if (cx == FN_FUNC) {
12639 	char * s = bp[0];
12640 	p = "0";
12641         debug(F111,"ffunc",s,argn);
12642 	if (argn > 0) {
12643 	    int x, y;
12644 	    for (p = s; *p; p++) {	/* Chop off trailing parens if any */
12645 		if (*p == '(') {
12646 		    *p = NUL;
12647 		    break;
12648 		}
12649 	    }
12650 	    /* Chop off leading "\\f" or "\f" or "f" */
12651 	    p = s;
12652 	    if (*p == CMDQ)		/* Allow for \\f... */
12653 	      p++;
12654 	    if (*p == CMDQ && (*(p+1) == 'f' || *(p+1) == 'F')) { /* or \f */
12655 		p += 2;
12656 	    } else if (*p == 'f' || *p == 'F') { /* or just f */
12657 		p++;
12658 	    }
12659 	    y = lookup(fnctab,p,nfuncs,&x); /* Look up the result */
12660 	    debug(F111,"ffunc",p,y);
12661 	    p = (y > -1) ? "1" : "0";
12662 	}
12663 	goto fnend;
12664     }
12665     if (cx == FN_RECURSE) {
12666 	int t, n;
12667 	char * s;
12668 	fnval[0] = NUL;			/* Default result is empty string */
12669 	s = bp[0];			/* Check for null argument */
12670 	if (!s) s = "";			/* or empty argument */
12671 	if (!*s) goto fnend;		/* in which case return empty string */
12672         n = FNVALL;			/* Not empty, max size for result */
12673         s = fnval;			/* Location of result */
12674 	{
12675 	    /* Force VARIABLE-EVALUATION SIMPLE RECURSIVE */
12676 	    /* NOTE: This is vulnerable to SIGINT and whatnot... */
12677 	    int tmp = vareval;		/* Save VARIABLE-EVALUATION setting */
12678 	    vareval = 1;		/* Force it to RECURSIVE */
12679 	    zzstring(bp[0],&s,&n);	/* Expand arg into result space */
12680 	    vareval = tmp;		/* Restore VARIABLE-EVALUATION */
12681 	}
12682 	goto fnend;
12683     }
12684 
12685     if (cx == FN_XLATE) {		/* f_cvtcset() */
12686 #ifdef NOFRILLS
12687 	ckstrncpy(fnval,bp[0],FNVALL);
12688 #else
12689 #ifndef NOUNICODE
12690 	_PROTOTYP( char * cvtstring, (char *, int, int) );
12691         char * string, * cset1, * cset2;
12692 	int id1, id2;
12693 #endif	/* NOUNICODE */
12694         fnval[0] = NUL;
12695 #ifdef NOUNICODE
12696 	ckstrncpy(fnval,bp[0],FNVALL);
12697 #else
12698         string = bp[0] ? bp[0] : "";	/* String to convert */
12699 	if (!*string) goto fnend;	/* It's empty */
12700 
12701         cset1 = bp[1] ? bp[1] : "ascii"; /* Current charset of string */
12702         cset2 = bp[2] ? bp[2] : "ascii"; /* Charset to convert to */
12703 
12704 	id1 = lookup(fcstab,cset1,nfilc,NULL); /* Lookup 'from' set */
12705 	if (id1 < 0) {
12706             failed = 1;
12707 	    ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN_CHARSET:\\f",fn,"()>",NULL);
12708 	    goto fnend;
12709         }
12710 	id2 = lookup(fcstab,cset2,nfilc,NULL); /* Lookup 'to' set */
12711 	if (id2 < 0) {
12712             failed = 1;
12713 	    ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN_CHARSET:\\f",fn,"()>",NULL);
12714 	    goto fnend;
12715         }
12716 	string = cvtstring(string,id1,id2);
12717 	ckstrncpy(fnval,string,FNVALL);
12718 #endif	/* NOUNICODE */
12719 #endif	/* NOFRILLS */
12720 	goto fnend;
12721     }
12722 
12723 /* Decode strings containing hex escapes */
12724 
12725     if (cx == FN_UNPCT) {		/* \fdecodehex() */
12726         char *s1, *s2;
12727 	char *prefix;			/* Can be 1 or 2 chars */
12728 	char buf[3];
12729 	int n = 0, k;
12730 
12731 	p = fnval;
12732 	*p = NUL;
12733         if (argn < 1) goto fnend;	/* Empty string */
12734 
12735 	s1 = bp[0] ? bp[0] : "";	/* Original string */
12736 	prefix = bp[1] ? bp[1] : "%%";	/* Hex byte prefix */
12737 	n = (int)strlen(prefix);	/* Length of prefix */
12738 	if (n < 1 || n > 2) {		/* must be 1 or 2 */
12739 	    ckmakmsg(fnval,FNVALL,
12740 		       "<ERROR:INVALID_HEX_PREFIX:\\f",fn,"()>",NULL);
12741 	    goto xunpct;
12742 	}
12743         while (*s1) {
12744 	    if (!ckstrcmp(s1,prefix,n,0)) { /* Case-independent */
12745 		if (!*(s1+n)) {
12746 		    ckmakmsg(fnval,FNVALL,
12747 			     "<ERROR:INCOMPLETE_SEQUENCE:\\f",fn,"()>",NULL);
12748 		    goto xunpct;
12749 		}
12750 		buf[0] = *(s1+n);	/* First hex character */
12751 		buf[1] = *(s1+n+1);	/* Second hex character */
12752 		buf[2] = NUL;
12753 		if ((k = ckhexbytetoint((char *)buf)) > -1) {
12754 		    *p++ = (char) k;	/* Deposit decoded result */
12755 		    s1 += 2+n;		/* and advance the source pointer */
12756 		} else {		/* Fail on conversion error */
12757 		    ckmakmsg(fnval,FNVALL,
12758 			     "<ERROR:NON_HEX_CHARS:\\f",fn,"()>",NULL);
12759 		    goto xunpct;
12760 		}
12761 	    } else {			/* Not a hex escape sequence */
12762 		*p++ = *s1++;		/* Just copy the character */
12763 	    }
12764         }
12765 	*p = NUL;			/* Terminate the result string */
12766         failed = 0;			/* Say we didn't fail */
12767         p = fnval;			/* Set up result pointer */
12768         goto fnend;			/* and finish */
12769 
12770       xunpct:				/* Error exit */
12771 	p = fnval;
12772 	failed = 1;
12773 	goto fnend;
12774     }
12775 
12776 /* Check a string for encoding family */
12777 
12778     if (cx == FN_STRINGT) {		/* \fstringtype() */
12779 	p = "UNK";
12780 	switch (scanstring(bp[0])) {
12781 	  case FT_7BIT: p = "7BIT"; break;
12782 	  case FT_8BIT: p = "8BIT"; break;
12783 	  case FT_UTF8: p = "UTF8"; break;
12784 	  case FT_UCS2: p = "UCS2"; break;
12785 	  case FT_TEXT: p = "TEXT"; break;
12786 	  case FT_BIN:  p = "BINARY"; break;
12787 	}
12788 	ckstrncpy(fnval,p,FNVALL);
12789 	p = fnval;
12790 	goto fnend;
12791     }
12792 
12793 /* String compare s1, s2, [ case ], [ start ] , [ len ] */
12794 
12795     if (cx == FN_STRCMP) {
12796         int docase = 0;			/* Case matters or not */
12797         int start = 0;			/* Start of substring */
12798 	int len = -1;			/* Length of substring to compare */
12799 	int x; char * s1, * s2;		/* workers */
12800 
12801         p = "0";			/* Return value */
12802         if (argn == 0) {		/* Two null strings are equal */
12803 	    ckstrncpy(fnval,p,FNVALL);
12804 	    p = fnval;
12805 	    goto fnend;
12806 	}
12807         if (argn == 1) {		/* Non-null string > null string */
12808 	    p = "1";
12809 	    ckstrncpy(fnval,p,FNVALL);
12810 	    p = fnval;
12811 	    goto fnend;
12812 	}
12813 	if (argn > 2) {
12814 	    s = *(bp[2]) ? evalx(bp[2]) : "0"; /* 0 = caseless */
12815 	    if (chknum(s)) docase = atoi(s);
12816 	    if (argn > 3) {
12817 		s = *(bp[3]) ? evalx(bp[3]) : "1"; /* start is 1-based */
12818 		if (chknum(s)) start = atoi(s);
12819 		if (argn > 4) {
12820 		    s = *(bp[4]) ? evalx(bp[4]) : "-1";	/* -1 = whole thing */
12821 		    if (chknum(s)) len = atoi(s);
12822 		}
12823 	    }
12824 	}
12825 	if (start > 0) start--; 	/* start is 0-based internally */
12826 	s1 = bp[0];			/* Get length of first arg */
12827 	x = (int)strlen(s1);
12828 	if (x > start)			/* Point to start position of s1 */
12829 	  s1 += start;
12830 	else
12831 	  s1 = "";
12832 	s2 = bp[1];			/* Get length of second arg */
12833 	x = (int)strlen(s2);
12834 	if (x > start)			/* Point to start position of s2 */
12835 	  s2 += start;
12836 	else
12837 	  s2 = "";
12838 	x = ckstrcmp(s,s2,len,docase);
12839 	p = ckitoa(x);
12840 	ckstrncpy(fnval,p,FNVALL);
12841 	p = fnval;
12842 	goto fnend;
12843     }
12844 #ifdef HAVE_LOCALE
12845 /*  \fdayname() - Returns locale-dependent day name string - 2013/07/23 */
12846 
12847     if (cx == FN_DAYNAME) {
12848 	_PROTOTYP( char * locale_dayname, (int, int) );
12849         char *s1, *s2;
12850 	char buf[3];
12851 	int fc = 0, day = 999;
12852 
12853 	s1 = bp[0];
12854 	s2 = bp[1];
12855 	p = fnval;
12856 	*p = NUL;
12857 
12858 	if (!s1) s1 = "";
12859 	if (!*s1) {
12860 	    s1 = ckdate();
12861 	} else if (rdigits(s1) && (int)strlen(s1) < 8) {
12862 	    day = atoi(s1);
12863 	    if (day == 0) day = 7;	/* In case \v(nday) used as arg */
12864 	    if (day < 1 || day > 7) {
12865 		ckmakmsg(fnval,FNVALL,"<ERROR:BAD_DAYNUM\\f",fn,"()>",NULL);
12866 		goto fnend;
12867 	    }
12868 	    day--;			/* Adjust to zero-based */
12869 	} else if (!(s1 = cmcvtdate(s1,1))) {
12870 	    ckmakmsg(fnval,FNVALL,"<ERROR:BAD_DATE\\f",fn,"()>",NULL);
12871 	    goto fnend;
12872 	}
12873 	if (!s2) s2 = "";		/* Parse function code */
12874 	if (!*s2) {
12875 	    fc = 0;
12876 	} else if (rdigits(s2)) {
12877 	    fc = atoi(s2);
12878 	} else {
12879             failed = 1;
12880 	    ckmakmsg(fnval,FNVALL,"<ERROR:FUNCTION_CODE:\\f",fn,"()>",NULL);
12881 	    goto fnend;
12882 	}
12883 	if (day > 6) {			/* Day number was not given */
12884 	    day = (mjd(s1) % 7) + 2;	/* Get day number */
12885 	    if (day > 6) day -= 7;	/* Adjust to 0=Sunday */
12886 	}
12887 	s1 = locale_dayname(day,fc);	/* Get locale-based day name */
12888 	if (!s1) {
12889             failed = 1;
12890 	    ckmakmsg(fnval,FNVALL,"<ERROR:NO_RESULT:\\f",fn,"()>",NULL);
12891 	    goto fnend;
12892 	}
12893 	ckstrncpy(fnval,s1,FNVALL);
12894 	goto fnend;
12895     }
12896     /* \fmonthname() - Returns locale-dependent month name string 2013/07/24 */
12897 
12898     if (cx == FN_MONNAME) {
12899 	_PROTOTYP( char * locale_monthname, (int, int) );
12900         char *s1, *s2;
12901 	char buf[3];
12902 	int fc = 0, month = 999;
12903 
12904 	s1 = bp[0];
12905 	s2 = bp[1];
12906 	p = fnval;
12907 	*p = NUL;
12908 
12909 	if (!s1) s1 = "";
12910 	if (!*s1) {
12911 	    s1 = ckdate();
12912 	} else if (rdigits(s1) && (int)strlen(s1) < 8) {
12913 	    month = atoi(s1);
12914             if (month == 0) month = 12;
12915 	    if (month < 1 || month > 12) {
12916 		ckmakmsg(fnval,FNVALL,"<ERROR:BAD_MONTHNUM:\\f",fn,"()>",NULL);
12917 		goto fnend;
12918 	    }
12919 	    month--;			/* Adjust to zero-based */
12920 	} else if (!(s1 = cmcvtdate(s1,1))) {
12921 	    ckmakmsg(fnval,FNVALL,"<ERROR:BAD_DATE\\f",fn,"()>",NULL);
12922 	    goto fnend;
12923 	}
12924 	if (month > 12) {
12925 	    char mn[3];
12926 	    mn[0] = s1[4];
12927 	    mn[1] = s1[5];
12928 	    mn[2] = NUL;
12929 	    month = atoi((char *)mn) - 1;
12930 	}
12931 	if (!s2) s2 = "";		/* Parse function code */
12932 	if (!*s2) {
12933 	    fc = 0;
12934 	} else if (rdigits(s2)) {
12935 	    fc = atoi(s2);
12936 	} else {
12937             failed = 1;
12938 	    ckmakmsg(fnval,FNVALL,"<ERROR:FUNCTION_CODE:\\f",fn,"()>",NULL);
12939 	    goto fnend;
12940 	}
12941 	s1 = locale_monthname(month,fc); /* Get locale-based month name */
12942 	if (!s1) {
12943             failed = 1;
12944 	    ckmakmsg(fnval,FNVALL,"<ERROR:NO_RESULT:\\f",fn,"()>",NULL);
12945 	    goto fnend;
12946 	}
12947 	ckstrncpy(fnval,s1,FNVALL);
12948 	goto fnend;
12949     }
12950 #endif /* HAVE_LOCALE */
12951 
12952 /* Note: when adding new functions remember to update dohfunc in ckuus2.c. */
12953 
12954     failed = 1;
12955     if (fndiags)
12956       ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN_FUNCTION:\\f",fn,"()>",NULL);
12957 
12958   fnend:
12959     /* Free temporary storage for aguments */
12960     for (k = 0; k < argn; k++) if (bp[k]) free(bp[k]);
12961     fndepth--;
12962     if (failed) {                       /* Handle failure */
12963         debug(F111,"fnend",fnval,errno);
12964         if (!p) p = "";
12965         if (p[0]) {
12966             /* In case this wasn't caught above... */
12967             k = strlen(p);
12968             if (p[0] != '<' && p[k-1] != '>') {
12969                 ckmakmsg(fnval,FNVALL,"<ERROR:BAD_ARG:\\f",fn,"()>",NULL);
12970                 p = fnval;
12971             }
12972         } else {
12973             ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN:\\f",fn,"()>",NULL);
12974             p = fnval;
12975         }
12976         if (fnerror)                    /* SET FUNCTION ERROR ON */
12977           fnsuccess = 0;                /* Make command fail (see ckuus5.c) */
12978         debug(F111,"fneval failed",p,fnsuccess);
12979         if (fndiags)                    /* SET FUNCTION DIAGNOSTICS ON */
12980           printf("?%s\n",p);            /* Print error message now. */
12981         else
12982           return("");                   /* Return nothing. */
12983     }
12984     return(p);
12985 }
12986 #endif /* NOSPL */
12987 
12988 static char ckpidbuf[32] = "????";
12989 
12990 #ifdef VMS
12991 _PROTOTYP(long zgpid,(void));
12992 #endif /* VMS */
12993 
12994 char *
ckgetpid()12995 ckgetpid() {                            /* Return pid as string */
12996 #ifdef CK_PID
12997 #ifdef OS2
12998 #define getpid _getpid
12999     unsigned long zz;
13000 #else
13001     long zz;
13002 #endif /* OS2 */
13003 #ifdef VMS
13004     zz = zgpid();
13005 #else
13006     zz = getpid();
13007 #endif /* VMS */
13008     sprintf(ckpidbuf,"%ld",zz);         /* SAFE */
13009 #endif /* CK_PID */
13010     return((char *)ckpidbuf);
13011 }
13012 
13013 #ifndef NOSPL
13014 #define EMBUFLEN 128                    /* Error message buffer length */
13015 
13016 static char embuf[EMBUFLEN+1];
13017 
13018 char *                                  /* Evaluate builtin variable */
nvlook(s)13019 nvlook(s) char *s; {
13020     int x, y, cx;
13021     long z;
13022     char *p;
13023 #ifndef NODIAL
13024     MDMINF * m;
13025 #endif /* NODIAL */
13026 #ifndef NOKVERBS                        /* Keyboard macro material */
13027     extern int keymac, keymacx;
13028 #endif /* NOKVERBS */
13029 #ifdef CK_LOGIN
13030     extern int isguest;
13031 #endif /* CK_LOGIN */
13032     if (!s) s = "";
13033     x = strlen(s);
13034     if (fndiags) {                      /* FUNCTION DIAGNOSTIC ON */
13035         if (x + 32 < EMBUFLEN)
13036           sprintf(embuf,"<ERROR:NO_SUCH_VARIABLE:\\v(%s)>",s); /* SAFE */
13037         else
13038           sprintf(embuf,"<ERROR:NO_SUCH_VARIABLE>"); /* SAFE */
13039     } else                              /* FUNCTION DIAGNOSTIC OFF */
13040       embuf[0] = NUL;
13041     x = VVBUFL;
13042     p = vvbuf;
13043     if (zzstring(s,&p,&x) < 0) {        /* e.g. for \v(\%a) */
13044         y = -1;
13045     } else {
13046         s = vvbuf;
13047         y = lookup(vartab,s,nvars,&x);
13048     }
13049     cx = y;                             /* y is too generic */
13050 #ifndef NODIAL
13051     m = (mdmtyp > 0) ? modemp[mdmtyp] : NULL; /* For \v(m_xxx) variables */
13052 #endif /* NODIAL */
13053 
13054     debug(F101,"nvlook y","",y);
13055 
13056     switch (y) {
13057       case VN_ARGC:                     /* ARGC */
13058         sprintf(vvbuf,"%d",maclvl < 0 ? topargc : macargc[maclvl]); /* SAFE */
13059         return(vvbuf);
13060 
13061       case VN_ARGS:                     /* ARGS */
13062         sprintf(vvbuf,"%d",xargs);      /* SAFE */
13063         return(vvbuf);
13064 
13065       case VN_COUN:                     /* COUNT */
13066         sprintf(vvbuf,"%d",count[cmdlvl]); /* SAFE */
13067         return(vvbuf);
13068 
13069       case VN_DATE:                     /* DATE */
13070         ztime(&p);                      /* Get "asctime" string */
13071         if (p == NULL || *p == NUL) return(NULL);
13072         vvbuf[0] = p[8];                /* dd */
13073         vvbuf[1] = p[9];
13074         vvbuf[2] = SP;
13075         vvbuf[3] = p[4];                /* mmm */
13076         vvbuf[4] = p[5];
13077         vvbuf[5] = p[6];
13078         vvbuf[6] = SP;
13079         for (x = 20; x < 24; x++)       /* yyyy */
13080           vvbuf[x - 13] = p[x];
13081         vvbuf[11] = NUL;
13082         return(vvbuf);
13083 
13084       case VN_NDAT:                     /* Numeric date */
13085         ckstrncpy(vvbuf,zzndate(),VVBUFL);
13086         return(vvbuf);
13087 
13088       case VN_DIRE:                     /* DIRECTORY */
13089         s = zgtdir();                   /* Get current directory */
13090         if (!s)
13091 #ifdef UNIXOROSK
13092           s = "./";
13093 #else
13094 #ifdef VMS
13095           s = "[]";
13096 #else
13097           s = "";
13098 #endif /* VMS */
13099 #endif /* UNIXOROSK */
13100         ckstrncpy(vvbuf,s,VVBUFL);
13101         s = vvbuf;
13102 #ifdef UNIXOROSK
13103         x = strlen(s);
13104         if (x < VVBUFL - 1) {
13105             if (s[x-1] != '/') {
13106                 s[x] = '/';
13107                 s[x+1] = NUL;
13108             }
13109         }
13110 #endif /* UNIXOROSK */
13111         return(s);
13112 
13113       case VN_FILE:                     /* filespec */
13114         return(fspec);
13115 
13116       case VN_HOST:                     /* host name */
13117         if (*myhost) {                  /* If known */
13118             return(myhost);             /* return it. */
13119         } else {                        /* Otherwise */
13120             ckstrncpy(vvbuf,"unknown",VVBUFL); /* just say "unknown" */
13121             return(vvbuf);
13122         }
13123 
13124       case VN_SYST:                     /* System type */
13125 #ifdef UNIX
13126         ckstrncpy(vvbuf,"UNIX",VVBUFL);
13127 #else
13128 #ifdef VMS
13129         ckstrncpy(vvbuf,"VMS",VVBUFL);
13130 #else
13131 #ifdef OSK
13132         ckstrncpy(vvbuf,"OS9/68K",VVBUFL);
13133 #else
13134 #ifdef AMIGA
13135         ckstrncpy(vvbuf,"Amiga",VVBUFL);
13136 #else
13137 #ifdef MAC
13138         ckstrncpy(vvbuf,"Macintosh",VVBUFL);
13139 #else
13140 #ifdef OS2
13141 #ifdef NT
13142         ckstrncpy(vvbuf,"WIN32",VVBUFL) ;
13143 #else /* NT */
13144         ckstrncpy(vvbuf,"OS/2",VVBUFL);
13145 #endif /* NT */
13146 #else
13147 #ifdef datageneral
13148         ckstrncpy(vvbuf,"AOS/VS",VVBUFL);
13149 #else
13150 #ifdef GEMDOS
13151         ckstrncpy(vvbuf,"Atari_ST",VVBUFL);
13152 #else
13153 #ifdef STRATUS
13154         ckstrncpy(vvbuf,"Stratus_VOS",VVBUFL);
13155 #else
13156         ckstrncpy(vvbuf,"unknown",VVBUFL);
13157 #endif /* STRATUS */
13158 #endif /* GEMDOS */
13159 #endif /* datageneral */
13160 #endif /* OS2 */
13161 #endif /* MAC */
13162 #endif /* AMIGA */
13163 #endif /* OSK */
13164 #endif /* VMS */
13165 #endif /* UNIX */
13166         return(vvbuf);
13167 
13168       case VN_SYSV:                     /* System herald */
13169 #ifdef IKSD
13170 #ifdef CK_LOGIN
13171         if (inserver && isguest)
13172           return("");
13173 #endif /* CK_LOGIN */
13174 #endif /* IKSD */
13175         for (x = y = 0; x < VVBUFL; x++) {
13176             if (ckxsys[x] == SP && y == 0) continue;
13177             vvbuf[y++] = (char) ((ckxsys[x] == SP) ? '_' : ckxsys[x]);
13178         }
13179         vvbuf[y] = NUL;
13180         return(vvbuf);
13181     } /* Break up long switch statements... */
13182 
13183     switch(y) {
13184       case VN_TIME:                     /* TIME. Assumes that ztime returns */
13185         ztime(&p);                      /* "Thu Feb  8 12:00:00 1990" */
13186         if (p == NULL || *p == NUL)     /* like asctime()! */
13187           return("");
13188         for (x = 11; x < 19; x++)       /* copy hh:mm:ss */
13189           vvbuf[x - 11] = p[x];         /* to vvbuf */
13190         vvbuf[8] = NUL;                 /* terminate */
13191         return(vvbuf);                  /* and return it */
13192 
13193       case VN_NTIM:                     /* Numeric time */
13194         ztime(&p);                      /* "Thu Feb  8 12:00:00 1990" */
13195         if (p == NULL || *p == NUL)     /* like asctime()! */
13196           return(NULL);
13197         z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
13198         sprintf(vvbuf,"%ld",z);         /* SAFE */
13199         return(vvbuf);
13200 
13201 #ifdef CK_TTYFD
13202       case VN_TTYF:                     /* TTY file descriptor */
13203         sprintf(vvbuf,"%d",             /* SAFE */
13204 #ifdef VMS
13205                 vmsttyfd()
13206 #else
13207                 ttyfd
13208 #endif /* VMS */
13209                 );
13210         return(vvbuf);
13211 #endif /* CK_TTYFD */
13212 
13213       case VN_VERS:                     /* Numeric Kermit version number */
13214         sprintf(vvbuf,"%ld",vernum);    /* SAFE */
13215         return(vvbuf);
13216 
13217       case VN_XVNUM:                    /* Product-specific version number */
13218         sprintf(vvbuf,"%ld",xvernum);   /* SAFE */
13219         return(vvbuf);
13220 
13221       case VN_HOME:                     /* Home directory */
13222         return(homepath());
13223 
13224       case VN_IBUF:                     /* INPUT buffer */
13225         return((char *)inpbuf);
13226 
13227       case VN_ICHR:                     /* INPUT character */
13228         inchar[1] = NUL;
13229         return((char *)inchar);
13230 
13231       case VN_ICNT:                     /* INPUT character count */
13232         sprintf(vvbuf,"%d",incount);    /* SAFE */
13233         return(vvbuf);
13234 
13235       case VN_SPEE: {                   /* Transmission SPEED */
13236           long t;
13237           t = ttgspd();
13238           if (t < 0L)
13239             sprintf(vvbuf,"unknown");   /* SAFE */
13240           else
13241             sprintf(vvbuf,"%ld",t);     /* SAFE */
13242           return(vvbuf);
13243       }
13244 
13245       case VN_SUCC:                     /* SUCCESS flag */
13246         /* Note inverted sense */
13247         sprintf(vvbuf,"%d",(success == 0) ? 1 : 0); /* SAFE */
13248         return(vvbuf);
13249 
13250       case VN_LINE: {                   /* LINE */
13251 #ifdef DEBUG
13252           if (deblog) {
13253               debug(F111,"\\v(line) local",ttname,local);
13254               debug(F111,"\\v(line) inserver","",inserver);
13255 #ifdef TNCODE
13256               debug(F111,"\\v(line) tcp_incoming","",tcp_incoming);
13257 #endif /* TNCODE */
13258 #ifdef CK_TAPI
13259               debug(F111,"\\v(line) tttapi","",tttapi);
13260 #endif /* CK_TAPI */
13261           }
13262 #endif /* DEBUG */
13263 
13264 #ifdef CK_TAPI
13265           if (tttapi) {                 /* If I have made a TAPI connection */
13266               int i;                    /* return the TAPI device name */
13267               for (i = 0; i < ntapiline; i++) {
13268                   if (!strcmp(ttname,tapilinetab[i].kwd)) {
13269                       p = _tapilinetab[i].kwd;
13270                       return(p);
13271                   }
13272               }
13273           }
13274 #endif /* CK_TAPI */
13275 #ifndef NOXFER
13276           if (inserver                  /* If I am a TCP server */
13277 #ifdef TNCODE
13278               || tcp_incoming
13279 #endif /* TNCODE */
13280               )
13281 #ifdef TCPSOCKET
13282             p = ckgetpeer();            /* return peer name */
13283           else
13284 #endif /* TCPSOCKET */
13285 #endif /* NOXFER */
13286           if (local)                    /* Otherwise if in local mode */
13287             p = (char *) ttname;        /* return SET LINE / SET HOST name */
13288           else                          /* Otherwise */
13289             p = "";                     /* return empty string */
13290           if (!p)                       /* In case ckgetpeer() returns */
13291             p = "";                     /* null pointer... */
13292           debug(F110,"\\v(line) p",p,0);
13293           if (!*p)
13294             p = (char *) ttname;
13295           return(p);
13296       }
13297       case VN_PROG:                     /* Program name */
13298         return("C-Kermit");
13299 
13300     } /* Break up long switch statements... */
13301 
13302     switch(y) {
13303       case VN_RET:                      /* Value of most recent RETURN */
13304         debug(F111,"\\v(return)",mrval[maclvl+1],maclvl+1);
13305         p = mrval[maclvl+1];
13306         if (p == NULL) p = "";
13307         return(p);
13308 
13309       case VN_FFC:                      /* Size of most recent file */
13310         sprintf(vvbuf, "%s", ckfstoa(ffc)); /* SAFE */
13311         return(vvbuf);
13312 
13313       case VN_TFC:                      /* Size of most recent file group */
13314         sprintf(vvbuf, "%s", ckfstoa(tfc)); /* SAFE */
13315         return(vvbuf);
13316 
13317       case VN_CPU:                      /* CPU type */
13318 #ifdef IKSD
13319 #ifdef CK_LOGIN
13320         if (inserver && isguest)
13321           return("");
13322 #endif /* CK_LOGIN */
13323 #endif /* IKSD */
13324 #ifdef OS2
13325          {
13326             char * getcpu(void) ;
13327             return getcpu();
13328          }
13329 #else /* OS2 */
13330 #ifdef CKCPU
13331         return(CKCPU);                  /* Traditionally, compile-time value */
13332 #else
13333 #ifdef CK_UTSNAME
13334         {                               /* But if none, try runtime value */
13335             extern char unm_mch[];
13336             return((char *)unm_mch);
13337         }
13338 #else
13339         p = getenv("HOSTTYPE");		/* 20091116 */
13340 	if (p) if (*p) return(p);
13341         return("unknown");
13342 #endif /* CK_UTSNAME */
13343 #endif /* CKCPU */
13344 #endif /* OS2 */
13345 
13346       case VN_CMDL:                     /* Command level */
13347         sprintf(vvbuf, "%d", cmdlvl);   /* SAFE */
13348         return(vvbuf);
13349 
13350       case VN_DAY:                      /* Current day of the week */
13351         ztime(&p);			/* three-letter abbreviation */
13352         if (p != NULL && *p != NUL)     /* ztime() succeeded. */
13353           ckstrncpy(vvbuf,p,4);
13354         else
13355           vvbuf[0] = NUL;               /* ztime() failed. */
13356         return(vvbuf);                  /* Return what we got. */
13357 
13358       case VN_NDAY: {                   /* Numeric day of week */
13359           long k;
13360           z = mjd(zzndate());           /* Get modified Julian date */
13361           k = (((int)(z % 7L)) + 3) % 7; /* Get day number */
13362           sprintf(vvbuf,"%ld",k);       /* SAFE */
13363           return(vvbuf);
13364 
13365       case VN_MONTH:
13366         ztime(&p);			/* three-letter abbreviation */
13367         if (p != NULL && *p != NUL)     /* ztime() succeeded. */
13368           ckstrncpy(vvbuf,p+4,5);
13369         else
13370           vvbuf[0] = NUL;               /* ztime() failed. */
13371         return(vvbuf);                  /* Return what we got. */
13372 
13373       case VN_NMONTH: {			/* Numeric month (1-12) */
13374 	  int x;
13375 	  ztime(&p);			/* asctime three-letter abbreviation */
13376 	  for (x = 0; x < 12; x++)
13377 	    if (!strncmp(p+4,months[x],3)) break;
13378 	  if (x == 12) {
13379 	      vvbuf[0] = '?';
13380 	      vvbuf[1] = '?';
13381 	  } else {
13382 	      x++;
13383 	      vvbuf[0] = (char) ((x < 10) ? '0' : '1');
13384 	      vvbuf[1] = (char) ((x % 10) + 48);
13385 	  }
13386 	  vvbuf[2] = NUL;
13387 	  return(vvbuf);		/* Return what we got. */
13388       }
13389 
13390       case VN_YEAR:			/* Current year */
13391         ztime(&p);
13392         if (p != NULL && *p != NUL)     /* ztime() succeeded. */
13393           ckstrncpy(vvbuf,p+20,5);
13394         else
13395           vvbuf[0] = NUL;               /* ztime() failed. */
13396         return(vvbuf);                  /* Return what we got. */
13397 
13398 
13399       }
13400 
13401       case VN_LCL:                      /* Local (vs remote) mode */
13402         ckstrncpy(vvbuf, local ? "1" : "0",VVBUFL);
13403         return(vvbuf);
13404 
13405       case VN_CMDS:                     /* Command source */
13406         if (cmdstk[cmdlvl].src == CMD_KB)
13407           ckstrncpy(vvbuf,"prompt",VVBUFL);
13408         else if (cmdstk[cmdlvl].src == CMD_MD)
13409           ckstrncpy(vvbuf,"macro",VVBUFL);
13410         else if (cmdstk[cmdlvl].src == CMD_TF)
13411           ckstrncpy(vvbuf,"file",VVBUFL);
13412         else
13413           ckstrncpy(vvbuf,"unknown",VVBUFL);
13414         return(vvbuf);
13415 
13416       case VN_CMDF:                     /* Current command file name */
13417 #ifdef COMMENT                          /* (see comments above) */
13418         if (tfnam[tlevel]) {            /* (near dblbs declaration) */
13419             dblbs(tfnam[tlevel],vvbuf,VVBUFL);
13420             return(vvbuf);
13421         } else return("");
13422 #else
13423         if (tlevel < 0)
13424           return("");
13425         else
13426           return(tfnam[tlevel] ? tfnam[tlevel] : "");
13427 #endif /* COMMENT */
13428 
13429       case VN_MAC:                      /* Current macro name */
13430         return((maclvl > -1) ? m_arg[maclvl][0] : "");
13431 
13432       case VN_EXIT:
13433         sprintf(vvbuf,"%d",xitsta);     /* SAFE */
13434         return(vvbuf);
13435 
13436     } /* Break up long switch statements... */
13437 
13438     switch(y) {
13439       case VN_PRTY: {                   /* Parity */
13440           char *ss;
13441           switch (parity) {
13442             case 0:   ss = "none";  break;
13443             case 'e': ss = "even";  break;
13444             case 'm': ss = "mark";  break;
13445             case 'o': ss = "odd";   break;
13446             case 's': ss = "space"; break;
13447             default:  ss = "unknown"; break;
13448           }
13449           ckstrncpy(vvbuf,ss,VVBUFL);
13450           return(vvbuf);
13451       }
13452 
13453       case VN_DIAL:
13454         sprintf(vvbuf,"%d",             /* SAFE */
13455 #ifndef NODIAL
13456                 dialsta
13457 #else
13458                 -1
13459 #endif /* NODIAL */
13460                 );
13461         return(vvbuf);
13462 
13463 #ifndef NODIAL
13464       case VN_DMSG:
13465 #ifdef BIGBUFOK
13466 	ckstrncpy(vvbuf,
13467               ((dialsta < 0) ? "(none)" : dialmsg[dialsta]),
13468               VVBUFL); /* Safe if src == NULL.. mdw 20140213 */
13469 #endif	/* BIGBUFOK */
13470 	return((char *)vvbuf);
13471 #endif	/* NODIAL */
13472 
13473 #ifdef OS2
13474       case VN_KEYB:
13475         ckstrncpy(vvbuf,conkbg(),VVBUFL);
13476         return(vvbuf);
13477       case VN_SELCT: {
13478 #ifndef NOLOCAL
13479           const char * selection = GetSelection();
13480           return( (char *) (selection ? selection : "" )) ;
13481 #else
13482           return("");
13483 #endif /* NOLOCAL */
13484       }
13485 #endif /* OS2 */
13486 
13487 #ifndef NOXFER
13488       case VN_CPS:
13489         sprintf(vvbuf,"%ld",tfcps);     /* SAFE */
13490         return(vvbuf);
13491 #endif /* NOXFER */
13492 
13493       case VN_MODE:                     /* File transfer mode */
13494         switch (binary) {
13495           case XYFT_T: ckstrncpy(vvbuf,"text",VVBUFL); break;
13496           case XYFT_B:
13497           case XYFT_U: ckstrncpy(vvbuf,"binary",VVBUFL); break;
13498           case XYFT_I: ckstrncpy(vvbuf,"image",VVBUFL); break;
13499           case XYFT_L: ckstrncpy(vvbuf,"labeled",VVBUFL); break;
13500           case XYFT_M: ckstrncpy(vvbuf,"macbinary",VVBUFL); break;
13501           default:     ckstrncpy(vvbuf,"unknown",VVBUFL);
13502         }
13503         return(vvbuf);
13504 
13505 #ifdef CK_REXX
13506       case VN_REXX:
13507         return(rexxbuf);
13508 #endif /* CK_REXX */
13509 
13510       case VN_NEWL:                     /* System newline char or sequence */
13511 #ifdef UNIX
13512         ckstrncpy(vvbuf,"\n",VVBUFL);
13513 #else
13514 #ifdef datageneral
13515         ckstrncpy(vvbuf,"\n",VVBUFL);
13516 #else
13517 #ifdef OSK
13518         ckstrncpy(vvbuf,"\15",VVBUFL);  /* Remember, these are octal... */
13519 #else
13520 #ifdef MAC
13521         ckstrncpy(vvbuf,"\15",VVBUFL);
13522 #else
13523 #ifdef OS2
13524         ckstrncpy(vvbuf,"\15\12",VVBUFL);
13525 #else
13526 #ifdef STRATUS
13527         ckstrncpy(vvbuf,"\n",VVBUFL);
13528 #else
13529 #ifdef VMS
13530         ckstrncpy(vvbuf,"\15\12",VVBUFL);
13531 #else
13532 #ifdef AMIGA
13533         ckstrncpy(vvbuf,"\n",VVBUFL);
13534 #else
13535 #ifdef GEMDOS
13536         ckstrncpy(vvbuf,"\n",VVBUFL);
13537 #else
13538         ckstrncpy(vvbuf,"\n",VVBUFL);
13539 #endif /* GEMDOS */
13540 #endif /* AMIGA */
13541 #endif /* VMS */
13542 #endif /* STRATUS */
13543 #endif /* OS2 */
13544 #endif /* MAC */
13545 #endif /* OSK */
13546 #endif /* datageneral */
13547 #endif /* UNIX */
13548         return(vvbuf);
13549 
13550       case VN_ROWS:                     /* ROWS */
13551       case VN_COLS:                     /* COLS */
13552         ckstrncpy(vvbuf,(cx == VN_ROWS) ? "24" : "80",VVBUFL); /* Default */
13553 #ifdef CK_TTGWSIZ
13554 #ifdef OS2
13555         if (tt_cols[VTERM] < 0 || tt_rows[VTERM] < 0)
13556           ttgwsiz();
13557         sprintf(vvbuf,"%d",             /* SAFE */
13558                 (cx == VN_ROWS) ? tt_rows[VTERM] : tt_cols[VTERM]);
13559 #else /* OS2 */
13560         if (ttgwsiz() > 0)              /* Get window size */
13561           if (tt_cols > 0 && tt_rows > 0) /* sets tt_rows, tt_cols */
13562             sprintf(vvbuf,"%d",         /* SAFE */
13563                     (cx == VN_ROWS) ? tt_rows : tt_cols);
13564 #endif /* OS2 */
13565 #endif /* CK_TTGWSIZ */
13566         return(vvbuf);
13567 
13568       case VN_TTYP:
13569 #ifdef NOTERM
13570         ckstrncpy(vvbuf,"unknown",VVBUFL);
13571 #else
13572 #ifdef OS2
13573         sprintf(vvbuf, "%s",            /* SAFE */
13574                 (tt_type >= 0 && tt_type <= max_tt) ?
13575                 tt_info[tt_type].x_name :
13576                 "unknown"
13577                 );
13578 #else
13579 #ifdef MAC
13580         ckstrncpy(vvbuf,"vt320",VVBUFL);
13581 #else
13582         p = getenv("TERM");
13583         ckstrncpy(vvbuf,p ? p : "unknown",VVBUFL+1);
13584 #endif /* MAC */
13585 #endif /* OS2 */
13586 #endif /* NOTERM */
13587         return(vvbuf);
13588 
13589       case VN_MINP:                     /* MINPUT */
13590         sprintf(vvbuf, "%d", m_found);  /* SAFE */
13591         return(vvbuf);
13592     } /* Break up long switch statements... */
13593 
13594     switch(y) {
13595       case VN_CONN:                     /* CONNECTION */
13596         if (!local) {
13597           ckstrncpy(vvbuf,"remote",VVBUFL);
13598         } else {
13599             if (!network)
13600               ckstrncpy(vvbuf,"serial",VVBUFL);
13601 #ifdef TCPSOCKET
13602             else if (nettype == NET_TCPB || nettype == NET_TCPA) {
13603                 if (ttnproto == NP_TELNET)
13604                   ckstrncpy(vvbuf,"tcp/ip_telnet",VVBUFL);
13605 #ifdef CK_SSL
13606                 else if (ttnproto == NP_SSL || ttnproto == NP_SSL_RAW)
13607                   ckstrncpy(vvbuf,"tcp/ip_ssl",VVBUFL);
13608                 else if (ttnproto == NP_TLS || ttnproto == NP_SSL_RAW)
13609                   ckstrncpy(vvbuf,"tcp/ip_tls",VVBUFL);
13610 #endif /* CK_SSL */
13611                 else
13612                   ckstrncpy(vvbuf,"tcp/ip",VVBUFL);
13613             }
13614 #endif /* TCPSOCKET */
13615 #ifdef SSHBUILTIN
13616             else if (nettype == NET_SSH)
13617                   ckstrncpy(vvbuf,"tcp/ip_ssh",VVBUFL);
13618 #endif /* SSHBUILTIN */
13619 #ifdef ANYX25
13620             else if (nettype == NET_SX25 ||
13621                      nettype == NET_VX25 ||
13622                      nettype == NET_IX25
13623                      )
13624               ckstrncpy(vvbuf,"x.25",VVBUFL);
13625 #endif /* ANYX25 */
13626 #ifdef DECNET
13627             else if (nettype == NET_DEC) {
13628                 if (ttnproto == NP_LAT)
13629                   ckstrncpy(vvbuf,"decnet_lat",VVBUFL);
13630                 else if ( ttnproto == NP_CTERM )
13631                   ckstrncpy(vvbuf,"decnet_cterm",VVBUFL);
13632                 else
13633                   ckstrncpy(vvbuf,"decnet",VVBUFL);
13634             }
13635 #endif /* DECNET */
13636 #ifdef SUPERLAT
13637             else if (nettype == NET_SLAT)
13638               ckstrncpy(vvbuf,"superlat",VVBUFL);
13639 #endif /* SUPERLAT */
13640 #ifdef NETFILE
13641             else if (nettype == NET_FILE)
13642               ckstrncpy(vvbuf,"local_file",VVBUFL);
13643 #endif /* NETFILE */
13644 #ifdef NETCMD
13645             else if (nettype == NET_CMD)
13646               ckstrncpy(vvbuf,"pipe",VVBUFL);
13647 #endif /* NETCMD */
13648 #ifdef NETPTY
13649             else if (nettype == NET_PTY)
13650               ckstrncpy(vvbuf,"pseudoterminal",VVBUFL);
13651 #endif /* NETPTY */
13652 #ifdef NETDLL
13653             else if (nettype == NET_DLL)
13654               ckstrncpy(vvbuf,"dynamic_link_library",VVBUFL);
13655 #endif /* NETDLL */
13656 
13657 #ifdef NPIPE
13658             else if (nettype == NET_PIPE)
13659               ckstrncpy(vvbuf,"named_pipe",VVBUFL);
13660 #endif /* NPIPE */
13661 #ifdef CK_NETBIOS
13662             else if (nettype == NET_BIOS)
13663               ckstrncpy(vvbuf,"netbios",VVBUFL);
13664 #endif /* CK_NETBIOS */
13665             else
13666               ckstrncpy(vvbuf,"unknown",VVBUFL);
13667         }
13668         return(vvbuf);
13669 
13670 #ifndef NOXFER
13671       case VN_SYSI:                     /* System ID, Kermit code */
13672         return((char *)cksysid);
13673 #endif /* NOXFER */
13674 
13675 #ifdef OS2
13676       case VN_SPA: {
13677           unsigned long space = zdskspace(0);
13678           if (space > 0 && space < 1024)
13679             sprintf(vvbuf,"-1");
13680           else
13681             sprintf(vvbuf,"%lu",space); /* SAFE */
13682           return(vvbuf);
13683       }
13684 #endif /* OS2 */
13685 
13686 #ifndef NOXFER
13687       case VN_QUE: {
13688           extern char querybuf[];
13689           return(querybuf);
13690       }
13691 #endif /* NOXFER */
13692 
13693 #ifndef NOCSETS
13694       case VN_CSET:
13695 #ifdef OS2
13696         sprintf(vvbuf,"cp%d",os2getcp()); /* SAFE */
13697 #else
13698         ckstrncpy(vvbuf,fcsinfo[fcharset].keyword,VVBUFL+1);
13699 #endif /* OS2 */
13700         return(vvbuf);
13701 #endif /* NOCSETS */
13702 
13703 #ifdef OS2
13704       case VN_EXEDIR:
13705         return(exedir);
13706       case VN_STAR:
13707         return(startupdir);
13708 #else
13709       case VN_EXEDIR:
13710         return(exedir ? exedir : "");
13711 #ifdef VMSORUNIX
13712       case VN_STAR:
13713         return(startupdir);
13714 #endif /* VMSORUNIX */
13715 #endif /* OS2 */
13716 
13717       case VN_INI:
13718         return(inidir);
13719 
13720       case VN_MDM:
13721         return(gmdmtyp());
13722 
13723       case VN_EVAL:
13724         return(evalbuf);
13725 
13726 #ifndef NODIAL
13727       case VN_D_CC:                     /* DIAL COUNTRY-CODE */
13728         return(diallcc ? diallcc : "");
13729 
13730       case VN_D_AC:                     /* DIAL AREA-CODE */
13731         return(diallac ? diallac : "");
13732 
13733       case VN_D_IP:                     /* DIAL INTERNATIONAL-PREFIX */
13734         return(dialixp ? dialixp : "");
13735 
13736       case VN_D_LP:                     /* DIAL LD-PREFIX */
13737         return(dialldp ? dialldp : "");
13738 
13739       case VN_D_LCP:                    /* DIAL LOCAL-PREFIX */
13740         return(diallcp ? diallcp : "");
13741 
13742       case VN_D_PXX:                    /* DIAL PBX-EXCHANGE that matched */
13743         return(matchpxx ? matchpxx : "");
13744 #else
13745       case VN_D_CC:                     /* DIAL COUNTRY-CODE */
13746       case VN_D_AC:                     /* DIAL AREA-CODE */
13747       case VN_D_IP:                     /* DIAL INTERNATIONAL-PREFIX */
13748       case VN_D_LP:                     /* DIAL LD-PREFIX */
13749       case VN_D_LCP:                    /* DIAL LOCAL-PREFIX */
13750       case VN_D_PXX:                    /* DIAL PBX-EXCHANGE */
13751         return("");
13752 #endif /* NODIAL */
13753       case VN_UID:
13754 #ifdef UNIX
13755         {
13756 #ifdef IKSD
13757             if (inserver)
13758               return((char *)uidbuf);
13759             else
13760 #endif /* IKSD */
13761               if (uidbuf[0])
13762                 return((char *)uidbuf);
13763               else
13764                 return(whoami());
13765         }
13766 #else
13767         return((char *)uidbuf);
13768 #endif /* UNIX */
13769     } /* Break up long switch statements... */
13770 
13771     switch(y) {
13772       case VN_PWD:
13773 #ifdef OS2
13774         if (activecmd == XXOUT || activecmd == XXLNOUT) {
13775             ckstrncpy(vvbuf,pwbuf,VVBUFL);
13776             ck_encrypt((char *)vvbuf);
13777             return((char *)vvbuf);
13778         } else
13779 #endif /* OS2 */
13780           return((char *)pwbuf);
13781 
13782       case VN_PRM:
13783         return((char *)prmbuf);
13784 
13785       case VN_PROTO:
13786 #ifdef NOXFER
13787         return("none");
13788 #else
13789 #ifdef CK_XYZ
13790         return(ptab[protocol].p_name);
13791 #else
13792         return("kermit");
13793 #endif /* CK_XYZ */
13794 #endif /* NOXFER */
13795 
13796 #ifndef NOXFER
13797 #ifdef CK_TMPDIR
13798       case VN_DLDIR:
13799         return(dldir ? dldir : "");
13800 #endif /* CK_TMPDIR */
13801 #endif /* NOXFER */
13802 
13803 #ifndef NODIAL
13804       case VN_M_INI:                    /* Modem init string */
13805         return(dialini ? dialini : (m ? m->wake_str : ""));
13806 
13807       case VN_M_DCM:                    /* Modem dial command */
13808         return(dialcmd ? dialcmd : (m ? m->dial_str : ""));
13809 
13810       case VN_M_DCO:                    /* Modem data compression on */
13811         return(dialdcon ? dialdcon : (m ? m->dc_on_str : ""));
13812 
13813       case VN_M_DCX:                    /* Modem data compression off */
13814         return(dialdcoff ? dialdcoff : (m ? m->dc_off_str : ""));
13815 
13816       case VN_M_ECO:                    /* Modem error correction on */
13817         return(dialecon ? dialecon : (m ? m->ec_on_str : ""));
13818 
13819       case VN_M_ECX:                    /* Modem error correction off */
13820         return(dialecoff ? dialecoff : (m ? m->ec_off_str : ""));
13821 
13822       case VN_M_AAO:                    /* Modem autoanswer on */
13823         return(dialaaon ? dialaaon : (m ? m->aa_on_str : ""));
13824 
13825       case VN_M_AAX:                    /* Modem autoanswer off */
13826         return(dialaaoff ? dialaaoff : (m ? m->aa_off_str : ""));
13827 
13828       case VN_M_HUP:                    /* Modem hangup command */
13829         return(dialhcmd ? dialhcmd : (m ? m->hup_str : ""));
13830 
13831       case VN_M_HWF:                    /* Modem hardware flow command */
13832         return(dialhwfc ? dialhwfc : (m ? m->hwfc_str : ""));
13833 
13834       case VN_M_SWF:                    /* Modem software flow command */
13835         return(dialswfc ? dialswfc : (m ? m->swfc_str : ""));
13836 
13837       case VN_M_NFC:                    /* Modem no flow-control command */
13838         return(dialnofc ? dialnofc : (m ? m->nofc_str : ""));
13839 
13840       case VN_M_PDM:                    /* Modem pulse dialing mode */
13841         return(dialpulse ? dialpulse : (m ? m->pulse : ""));
13842 
13843       case VN_M_TDM:                    /* Modem tone dialing mode */
13844         return(dialtone ? dialtone : (m ? m->tone : ""));
13845 
13846       case VN_M_NAM:                    /* Modem full name */
13847         return(dialname ? dialname : (m ? m->name : ""));
13848 #else
13849       case VN_M_INI:                    /* Modem init string */
13850       case VN_M_DCM:                    /* Modem dial command */
13851       case VN_M_DCO:                    /* Modem data compression on */
13852       case VN_M_DCX:                    /* Modem data compression off */
13853       case VN_M_ECO:                    /* Modem error correction on */
13854       case VN_M_ECX:                    /* Modem error correction off */
13855       case VN_M_AAO:                    /* Modem autoanswer on */
13856       case VN_M_AAX:                    /* Modem autoanswer off */
13857       case VN_M_HUP:                    /* Modem hangup command */
13858       case VN_M_HWF:                    /* Modem hardware flow command */
13859       case VN_M_SWF:                    /* Modem software flow command */
13860       case VN_M_NFC:                    /* Modem no flow-control command */
13861       case VN_M_PDM:                    /* Modem pulse dialing mode */
13862       case VN_M_TDM:                    /* Modem tone dialing mode */
13863       case VN_M_NAM:
13864         return("");
13865 #endif /* NODIAL */
13866 
13867       case VN_ISTAT:                    /* INPUT status */
13868         sprintf(vvbuf, "%d", instatus); /* SAFE */
13869         return(vvbuf);
13870 
13871       case VN_TEMP:                     /* Temporary directory */
13872         if (tempdir) {                  /* This is where SET TEMP-DIRECTORY */
13873             p = tempdir;                /* puts it -- just use what's here */
13874             ckstrncpy(vvbuf,tempdir,VVBUFL);
13875         } else {
13876             char c;
13877 /*
13878   If tempdir was empty, we have to figure out what the temp directory should
13879   be here and then put the result where we should have found it in the first
13880   place.  This is done by checking environment variables (or logical names
13881   in VMS, same thing from the API point of view), and if that fails using
13882   platform-specific conventions, like the /tmp directory in Unix.
13883 */
13884 #ifdef OS2
13885 #ifdef NT
13886             p = getenv("K95TMP");
13887 #else
13888             p = getenv("K2TMP");
13889 #endif /* NT */
13890             if ( !p )
13891 #endif /* OS2 */
13892               p = getenv("CK_TMP");
13893             if (!p) p = getenv("TMPDIR");
13894             if (!p) p = getenv("TEMP");
13895             if (!p) p = getenv("TMP");
13896 
13897 #ifdef OS2ORUNIX
13898             if (p) {
13899                 int len = strlen(p);
13900                 if (p[len-1] != '/'
13901 #ifdef OS2
13902                     && p[len-1] != '\\'
13903 #endif /* OS2 */
13904                      ) {
13905                     ckstrncpy(vvbuf,p,VVBUFL);
13906                     if (vvbuf[0]) ckstrncat(vvbuf,"/",CKMAXPATH);
13907                     p = vvbuf;
13908                 }
13909             }
13910 #endif /* OS2ORUNIX */
13911 
13912             if (!p) {
13913 #ifdef UNIX                             /* Systems that have a standard */
13914                 p = "/tmp/";            /* temporary directory... */
13915 #else
13916 #ifdef datageneral
13917                 p = ":TMP:";
13918 #else
13919                 p = "";
13920 #endif /* datageneral */
13921 #endif /* UNIX */
13922                 ckstrncpy(vvbuf,p,VVBUFL);
13923                 p = vvbuf;
13924             }
13925 /*
13926   If the result does not end with a directory separator, tack on the
13927   appropriate one.  This way scripts can be written in a platform-independent
13928   way, without having to hardwire a particlar OS's directory separator;
13929   e.g. \v(tmpfile)foo.bar instead of \v(tmpfile)/foo.bar, which would not be
13930   portable to (say) VMS.  In a better world the following code would be in the
13931   platform-specific modules, ck?fio.c, but it's too late to go back and redo
13932   them all.  Note: Windows and OS/2 use backslash (\) as the directory
13933   separate in the user interface, but accept slash(/) in their APIs.
13934 */
13935             while (*p) {                /* For Windows and OS/2 */
13936 #ifdef OS2                              /* flip the backslash */
13937                 if (*p == '\\') *p = '/';
13938 #endif /* OS2 */
13939                 p++;
13940             }
13941             p = vvbuf;
13942 #ifndef VMS
13943             if (p > vvbuf) {          /* Directory termination character */
13944                   c =
13945 #ifdef MAC
13946                       ':'
13947 #else
13948 #ifdef datageneral
13949                       ':'
13950 #else
13951 #ifdef STRATUS
13952                       '>'
13953 #else
13954                       '/'
13955 #endif /* STRATUS */
13956 #endif /* datageneral */
13957 #endif /* MAC */
13958                       ;
13959                   if (*(p-1) != c) {    /* Add it to the end of the */
13960                       *p++ = c;         /* string if it was not already */
13961                       *p = NUL;         /* there */
13962                   }
13963               }
13964 #endif /* Not VMS */
13965 /*
13966   But if the result is just the one character, e.g. '/' in Unix, erase it
13967   because that's the root directory and obviously can't be used for temporary
13968   files.
13969 */
13970               if (vvbuf[0] == c && vvbuf[1] == NUL) {
13971                   vvbuf[0] = NUL;
13972               }
13973         }
13974         makestr(&tempdir,p); /* Save result where we can find it next time */
13975         return(vvbuf);
13976     }
13977     /* Break up long switch statements... */
13978 
13979     switch(y) {
13980       case VN_ERRNO:                    /* Error number */
13981 #ifdef VMS
13982         {
13983             extern int vms_lasterr;
13984             sprintf(vvbuf, "%d", vms_lasterr); /* SAFE */
13985         }
13986 #else
13987         sprintf(vvbuf, "%d", errno);    /* SAFE */
13988 #endif /* VMS */
13989         return(vvbuf);
13990 
13991       case VN_ERSTR:                    /* Error string */
13992         ckstrncpy(vvbuf,ck_errstr(),VVBUFL);
13993         return(vvbuf);
13994 
13995 #ifndef NOXFER
13996       case VN_RPSIZ:                    /* RECEIVE packet-length */
13997         sprintf(vvbuf,"%d",urpsiz);     /* SAFE */
13998         return(vvbuf);
13999 
14000       case VN_WINDO:                    /* WINDOW slots */
14001         sprintf(vvbuf,"%d",wslotr);     /* SAFE */
14002         return(vvbuf);
14003 #endif /* NOXFER */
14004 
14005       case VN_TFLN:                     /* TAKE-file line number */
14006         if (tlevel > -1) {
14007             sprintf(vvbuf, "%d", tfline[tlevel]); /* SAFE */
14008             return(vvbuf);
14009         } else
14010           return("0");
14011 
14012       case VN_MDMSG:                    /* DIALRESULT */
14013 #ifndef NODIAL
14014         return((char *)modemmsg);
14015 #else
14016         return("");
14017 #endif /* NODIAL */
14018 
14019       case VN_DNUM:                     /* DIALNUMBER */
14020 #ifndef NODIAL
14021         return(dialnum ? (char *) dialnum : "");
14022 #else
14023         return("");
14024 #endif /* NODIAL */
14025 
14026       case VN_APC:
14027         sprintf(vvbuf, "%d",            /* SAFE */
14028 #ifdef CK_APC
14029                 apcactive
14030 #else
14031                 0
14032 #endif /* CK_APC */
14033                 );
14034         return((char *)vvbuf);
14035 
14036 #ifdef OS2
14037 #ifndef NOKVERBS
14038       case VN_TRMK:
14039           sprintf(vvbuf, "%d", keymac); /* SAFE */
14040         return((char *)vvbuf);
14041 #endif /* NOKVERBS */
14042 #endif /* OS2 */
14043 
14044       case VN_IPADDR:
14045 #ifdef TCPSOCKET
14046 #ifndef OSK
14047       /* This dumps core on OS-9 for some reason, but only if executed */
14048       /* before we have made a TCP connection.  This is obviously not */
14049       /* the ideal fix. */
14050         if (!myipaddr[0])
14051           getlocalipaddr();
14052 #endif /* OSK */
14053 #endif /* TCPSOCKET */
14054         ckstrncpy(vvbuf,
14055 #ifdef TCPSOCKET
14056                 (char *)myipaddr,
14057 #else
14058                 "",
14059 #endif /* TCPSOCKET */
14060                 VVBUFL);
14061         return((char *)vvbuf);
14062 
14063 #ifndef NOXFER
14064       case VN_CRC16:                    /* CRC-16 of most recent transfer */
14065         sprintf(vvbuf,"%d",crc16);      /* SAFE */
14066         return(vvbuf);
14067 #endif /* NOXFER */
14068 
14069 #ifdef CK_PID
14070       case VN_PID:
14071 #ifdef IKSD
14072 #ifdef CK_LOGIN
14073         if (inserver && isguest)
14074           return("");
14075 #endif /* CK_LOGIN */
14076 #endif /* IKSD */
14077         return(ckgetpid());
14078 #endif /* CK_PID */
14079 
14080 #ifndef NOXFER
14081       case VN_FNAM: {                   /* \v(filename) */
14082           extern char filnam[], ofn1[], *sfspec, *rrfspec;
14083           char * tmp;
14084           switch (what) {               /* File transfer is in progress */
14085 #ifdef NEWFTP
14086             case (W_FTP|W_RECV):
14087             case (W_FTP|W_SEND):
14088               return((char *)filnam);
14089 #endif /* NEWFTP */
14090             case W_RECV:
14091             case W_REMO:
14092               return((char *)ofn1);
14093             default:                    /* Most recent file transferred */
14094               if (filnam[0]) {          /* (if any) */
14095                   return((char *)filnam);
14096               } else if (lastxfer & W_SEND && sfspec) {
14097                   if (fnspath == PATH_OFF)
14098                     zstrip(sfspec,&tmp);
14099                   else
14100                     tmp = sfspec;
14101                   return(tmp);
14102               } else if (lastxfer & W_RECV && rrfspec) {
14103                   if (fnrpath == PATH_OFF)
14104                     zstrip(rrfspec,&tmp);
14105                   else
14106                     tmp = rrfspec;
14107                   return(tmp);
14108               } else
14109                 return("");
14110           }
14111       }
14112       case VN_FNUM:                     /* \v(filenum) */
14113         sprintf(vvbuf,"%ld",filcnt);    /* SAFE */
14114         return((char *)vvbuf);
14115 #endif /* NOXFER */
14116 
14117 #ifdef PEXITSTAT
14118       case VN_PEXIT: {
14119           extern int pexitstat;
14120           sprintf(vvbuf,"%d",pexitstat); /* SAFE */
14121           return((char *)vvbuf);
14122       }
14123 #endif /* PEXITSTAT */
14124 
14125 #ifndef NOXFER
14126       case VN_P_8BIT:
14127         vvbuf[0] = parity ? ebq : NUL;
14128         vvbuf[1] = NUL;
14129         return((char *)vvbuf);
14130 
14131       case VN_P_CTL: {
14132           extern CHAR myctlq;
14133           vvbuf[0] = myctlq;
14134           vvbuf[1] = NUL;
14135           return((char *)vvbuf);
14136       }
14137       case VN_P_RPT: {
14138           extern int rptena;
14139           vvbuf[0] = rptena ? rptq : NUL;
14140           vvbuf[1] = NUL;
14141           return((char *)vvbuf);
14142       }
14143 #endif /* NOXFER */
14144 
14145     } /* Break up long switch statements... */
14146 
14147     switch(y) {
14148       case VN_XPROG:
14149 #ifndef COMMENT
14150 /* C-Kermit 9.0 and later for Windows and OS/2 is just C-Kermit */
14151         return("C-Kermit");
14152 #else
14153 #ifdef OS2
14154 #ifdef NT
14155 #ifdef KUI
14156         return("K-95G");
14157 #else
14158         return("K-95");
14159 #endif /* KUI */
14160 #else
14161         return("K/2");
14162 #endif /* NT */
14163 #else
14164         return("C-Kermit");
14165 #endif /* OS2 */
14166 #endif /* COMMENT */
14167 
14168       case VN_EDITOR:
14169 #ifdef NOFRILLS
14170         return("");
14171 #else
14172 #ifdef NOPUSH
14173         return("");
14174 #else
14175         {
14176             extern char editor[];
14177             char *ss;
14178             if (!editor[0]) {
14179                 ss = getenv("EDITOR");
14180                 if (ss) {
14181                     ckstrncpy(editor,ss,CKMAXPATH);
14182                 }
14183             }
14184             debug(F110,"\\v(editor)",editor,0);
14185             return(editor[0] ? (char *)editor : "");
14186         }
14187 #endif /* NOPUSH */
14188 #endif /* NOFRILLS */
14189 
14190       case VN_EDOPT:
14191 #ifdef NOFRILLS
14192         return("");
14193 #else
14194 #ifdef NOPUSH
14195         return("");
14196 #else
14197         {
14198             extern char editopts[];
14199             return(editopts[0] ? (char *)editopts : "");
14200         }
14201 #endif /* NOPUSH */
14202 #endif /* NOFRILLS */
14203 
14204       case VN_EDFILE:
14205 #ifdef NOFRILLS
14206         return("");
14207 #else
14208 #ifdef NOPUSH
14209         return("");
14210 #else
14211         {
14212             extern char editfile[];
14213             return(editfile[0] ? (char *)editfile : "");
14214         }
14215 #endif /* NOPUSH */
14216 #endif /* NOFRILLS */
14217 
14218 #ifdef BROWSER
14219       case VN_BROWSR: {
14220           extern char browser[];
14221           if (!browser[0]) {
14222               s = getenv("BROWSER");
14223               if (s) ckstrncpy(browser,s,CKMAXPATH);
14224           }
14225           return(browser[0] ? (char *)browser : "");
14226       }
14227       case VN_BROPT: {
14228           extern char browsopts[];
14229           return(browsopts[0] ? (char *)browsopts : "");
14230       }
14231       case VN_URL: {
14232           extern char browsurl[];
14233           return(browsurl[0] ? (char *)browsurl : "");
14234       }
14235 #endif /* BROWSER */
14236       case VN_HERALD:
14237         return((char *)versio);
14238 
14239       case VN_TEST: {                   /* test */
14240           extern char * ck_s_test, * ck_s_tver;
14241           if (!ck_s_test) ck_s_test = "";
14242           if (!ck_s_tver) ck_s_tver = "";
14243           if (*ck_s_test) {
14244               ckstrncpy(vvbuf,ck_s_test,VVBUFL);
14245               if (*ck_s_tver) {
14246                   ckstrncat(vvbuf,".",VVBUFL);
14247                   ckstrncat(vvbuf,ck_s_tver,VVBUFL);
14248               }
14249           } else
14250             ckstrncpy(vvbuf,"0",VVBUFL);
14251           return((char *)vvbuf);
14252       }
14253 
14254 #ifndef NOXFER
14255       case VN_XFSTAT:                   /* xferstatus */
14256         x = xferstat;                   /* Like success */
14257         if (x > -1) x = (x == 0) ? 1 : 0; /* External value is reversed */
14258         sprintf(vvbuf,"%d",x);          /* SAFE */
14259         return((char *)vvbuf);
14260 
14261       case VN_XFMSG:                    /* xfermsg */
14262         return((char *)epktmsg);
14263 
14264 #ifndef NOMSEND
14265       case VN_SNDL: {                   /* sendlist */
14266           extern int filesinlist;
14267           sprintf(vvbuf,"%d",filesinlist); /* SAFE */
14268           return((char *)vvbuf);
14269       }
14270 #endif /* NOMSEND */
14271 #endif /* NOXFER */
14272 
14273 #ifdef CK_TRIGGER
14274       case VN_TRIG: {
14275           extern char * triggerval;
14276           return(triggerval ? triggerval : "");
14277       }
14278 #endif /* CK_TRIGGER */
14279 #ifdef OS2MOUSE
14280 #ifdef OS2
14281       case VN_MOU_X: {
14282           extern int MouseCurX;
14283           sprintf(vvbuf,"%d",MouseCurX); /* SAFE */
14284           return((char *)vvbuf);
14285       }
14286       case VN_MOU_Y: {
14287           extern int MouseCurY;
14288           sprintf(vvbuf,"%d",MouseCurY); /* SAFE */
14289           return((char *)vvbuf);
14290       }
14291 #endif /* OS2 */
14292 #endif /* OS2MOUSE */
14293       case VN_PRINT: {
14294           extern int printpipe;
14295           extern char * printername;
14296 #ifdef PRINTSWI
14297           extern int noprinter;
14298           if (noprinter) return("");
14299 #endif /* PRINTSWI */
14300           ckmakmsg(vvbuf,VVBUFL,
14301                    printpipe ? "|" : "",
14302                    printername ? printername :
14303 #ifdef OS2
14304                    "PRN",
14305 #else
14306                    "(default)",
14307 #endif /* OS2 */
14308                    NULL,
14309                    NULL
14310                    );
14311           return((char *)vvbuf);
14312       }
14313     } /* Break up long switch statements... */
14314 
14315     switch(y) {
14316       case VN_ESC:                      /* Escape character */
14317         sprintf(vvbuf,"%d",escape);     /* SAFE */
14318         return((char *)vvbuf);
14319 
14320       case VN_INTIME:
14321         sprintf(vvbuf,"%ld",inetime);   /* SAFE */
14322         return((char *)vvbuf);
14323 
14324       case VN_INTMO:
14325         sprintf(vvbuf,"%d",inwait);     /* SAFE */
14326         return((char *)vvbuf);
14327 
14328       case VN_SECURE:
14329         if (0
14330 #ifdef SSHBUILTIN
14331             || IS_SSH()
14332 #endif /* SSHBUILTIN */
14333 #ifdef CK_ENCRYPTION
14334             || ck_tn_encrypting() && ck_tn_decrypting()
14335 #endif /* CK_ENCRYPTION */
14336 #ifdef CK_SSL
14337             || tls_active_flag || ssl_active_flag
14338 #endif /* CK_SSL */
14339             )
14340           return("1");
14341         else
14342           return("0");
14343 
14344       case VN_AUTHN:
14345 #ifdef CK_AUTHENTICATION
14346         {
14347             extern char szUserNameAuthenticated[];
14348             return((char *)szUserNameAuthenticated);
14349         }
14350 #else /* CK_AUTHENTICATION */
14351         return((char *)"");
14352 #endif /* CK_AUTHENTICATION */
14353 
14354       case VN_AUTHS:
14355 #ifdef CK_AUTHENTICATION
14356         switch (ck_tn_auth_valid()) {
14357           case AUTH_UNKNOWN:
14358             return((char *)"unknown");
14359           case AUTH_OTHER:
14360             return((char *)"other");
14361           case AUTH_USER:
14362             return((char *)"user");
14363           case AUTH_VALID:
14364             return((char *)"valid");
14365           case AUTH_REJECT:
14366           default:
14367             return((char *)"rejected");
14368         }
14369 #else /* CK_AUTHENTICATION */
14370         return((char *)"rejected");
14371 #endif /* CK_AUTHENTICATION */
14372 
14373       case VN_AUTHT:
14374 #ifdef CK_AUTHENTICATION
14375 #ifdef CK_SSL
14376         if ((ssl_active_flag || tls_active_flag) &&
14377             ck_tn_auth_valid() == AUTH_VALID &&
14378             (sstelnet ? (!TELOPT_U(TELOPT_AUTHENTICATION)) :
14379                         (!TELOPT_ME(TELOPT_AUTHENTICATION))) ||
14380              ck_tn_authenticated() == AUTHTYPE_NULL ||
14381              ck_tn_authenticated() == AUTHTYPE_AUTO)
14382           return("X_509_CERTIFICATE");
14383         else
14384 #endif /* CK_SSL */
14385           return(AUTHTYPE_NAME(ck_tn_authenticated()));
14386 #else /* CK_AUTHENTICATION */
14387         return((char *)"NULL");
14388 #endif /* CK_AUTHENTICATION */
14389 
14390 #ifdef CK_KERBEROS
14391       case VN_K4PRN: {
14392           extern char * krb4_d_principal;
14393           if (krb4_d_principal)
14394             ckstrncpy(vvbuf,krb4_d_principal,VVBUFL+1);
14395           else
14396             *vvbuf = NUL;
14397           return((char *)vvbuf);
14398       }
14399       case VN_K5PRN: {
14400           extern char * krb5_d_principal;
14401           if (krb5_d_principal)
14402             ckstrncpy(vvbuf,krb5_d_principal,VVBUFL+1);
14403           else
14404             *vvbuf = NUL;
14405           return((char *)vvbuf);
14406       }
14407       case VN_K4RLM: {
14408           extern char * krb4_d_realm;
14409           if (krb4_d_realm) {
14410               ckstrncpy(vvbuf,krb4_d_realm,VVBUFL+1);
14411           } else {
14412               char * s = ck_krb4_getrealm();
14413               ckstrncpy(vvbuf,s ? s : "",VVBUFL+1);
14414           }
14415           return((char *)vvbuf);
14416       }
14417       case VN_K4SRV: {
14418           extern char * krb4_d_srv;
14419           if (krb4_d_srv)
14420             ckstrncpy(vvbuf,krb4_d_srv,VVBUFL+1);
14421           else
14422             ckstrncpy(vvbuf,"rcmd",VVBUFL);
14423           return((char *)vvbuf);
14424       }
14425       case VN_K5RLM: {
14426           extern char * krb5_d_realm;
14427           extern char * krb5_d_cc;
14428           if (krb5_d_realm) {
14429               ckstrncpy(vvbuf,krb5_d_realm,VVBUFL+1);
14430           } else {
14431               char * s = ck_krb5_getrealm(krb5_d_cc);
14432               ckstrncpy(vvbuf,s,VVBUFL+1);
14433           }
14434           return((char *)vvbuf);
14435       }
14436       case VN_K5CC: {
14437           extern char * krb5_d_cc;
14438           if (krb5_d_cc)
14439             ckstrncpy(vvbuf,krb5_d_cc,VVBUFL+1);
14440           else
14441             ckstrncpy(vvbuf,ck_krb5_get_cc_name(),VVBUFL+1);
14442           return((char *)vvbuf);
14443       }
14444       case VN_K5SRV: {
14445           extern char * krb5_d_srv;
14446           if (krb5_d_srv)
14447             ckstrncpy(vvbuf,krb5_d_srv,VVBUFL+1);
14448           else
14449             ckstrncpy(vvbuf,"host",VVBUFL);
14450           return((char *)vvbuf);
14451       }
14452       case VN_K4ENO: {
14453         extern int krb4_errno;
14454         sprintf(vvbuf,"%d",krb4_errno); /* SAFE */
14455         return((char *)vvbuf);
14456       }
14457       case VN_K5ENO: {
14458         extern int krb5_errno;
14459         sprintf(vvbuf,"%d",krb5_errno); /* SAFE */
14460         return((char *)vvbuf);
14461       }
14462       case VN_K4EMSG: {
14463         extern char * krb4_errmsg;
14464         ckstrncpy(vvbuf,krb4_errmsg?krb4_errmsg:"",VVBUFL+1);
14465         return((char *)vvbuf);
14466       }
14467       case VN_K5EMSG: {
14468         extern char * krb5_errmsg;
14469         ckstrncpy(vvbuf,krb5_errmsg,VVBUFL+1);
14470         return((char *)vvbuf);
14471       }
14472 #endif /* CK_KERBEROS */
14473 #ifdef CK_SSL
14474       case VN_X509_S:
14475         if (ssl_active_flag)
14476           ckstrncpy(vvbuf,ssl_get_subject_name(ssl_con),VVBUFL+1);
14477         else if (tls_active_flag)
14478           ckstrncpy(vvbuf,ssl_get_subject_name(tls_con),VVBUFL+1);
14479         else
14480           ckstrncpy(vvbuf,"",VVBUFL+1);
14481         return((char *)vvbuf);
14482       case VN_X509_I:
14483         if (ssl_active_flag)
14484           ckstrncpy(vvbuf,ssl_get_issuer_name(ssl_con),VVBUFL+1);
14485         else if (tls_active_flag)
14486           ckstrncpy(vvbuf,ssl_get_issuer_name(tls_con),VVBUFL+1);
14487         else
14488           ckstrncpy(vvbuf,"",VVBUFL+1);
14489         return((char *)vvbuf);
14490 #endif /* CK_SSL */
14491 
14492       case VN_OSNAM:
14493 #ifdef IKSD
14494 #ifdef CK_LOGIN
14495         if (inserver && isguest)
14496           return("");
14497 #endif /* CK_LOGIN */
14498 #endif /* IKSD */
14499 #ifdef CK_UTSNAME
14500         {
14501             extern char unm_nam[];
14502             return((char *)unm_nam);
14503         }
14504 #else
14505         for (x = y = 0; x < VVBUFL; x++) {
14506             if (ckxsys[x] == SP && cx == 0) continue;
14507             vvbuf[y++] = (char) ((ckxsys[x] == SP) ? '_' : ckxsys[x]);
14508         }
14509         vvbuf[y] = NUL;
14510         return(vvbuf);
14511 #endif /* CK_UTSNAME */
14512 
14513       case VN_OSVER: {
14514 #ifdef CK_UTSNAME
14515           extern char unm_ver[];
14516 #ifdef IKSD
14517 #ifdef CK_LOGIN
14518           if (inserver && isguest)
14519             return("");
14520 #endif /* CK_LOGIN */
14521 #endif /* IKSD */
14522           return((char *)unm_ver);
14523 #else
14524           return("");
14525 #endif /* CK_UTSNAME */
14526       }
14527 
14528       case VN_OSREL: {
14529 #ifdef CK_UTSNAME
14530           extern char unm_rel[];
14531 #ifdef IKSD
14532 #ifdef CK_LOGIN
14533           if (inserver && isguest)
14534             return("");
14535 #endif /* CK_LOGIN */
14536 #endif /* IKSD */
14537           return((char *)unm_rel);
14538 #else
14539           return("");
14540 #endif /* CK_UTSNAME */
14541       }
14542     } /* Break up long switch statements... */
14543 
14544     switch(y) {
14545       case VN_NAME: {
14546           extern char * myname;
14547           return(myname);
14548       }
14549 
14550       case VN_MODL: {
14551 #ifdef CK_UTSNAME
14552           extern char unm_mod[], unm_mch[];
14553           int y = VVBUFL - 1;
14554           char * s = unm_mod;
14555 #endif /* CK_UTSNAME */
14556 #ifdef IKSD
14557 #ifdef CK_LOGIN
14558           if (inserver && isguest)
14559             return("");
14560 #endif /* CK_LOGIN */
14561 #endif /* IKSD */
14562 
14563 #ifdef COMMENT                          /* was HPUX */
14564           if (!unm_mod[0] && !nopush)
14565             zzstring("\\fcommand(model)",&s,&y);
14566 /*
14567    Another possibility would be:
14568      "\\fcommand(ksh -c 'whence model 1>&- && model || uname -m')"
14569    But that would depend on having ksh.
14570 */
14571 #else
14572 #ifdef OSF32                            /* Digital UNIX 3.2 and higher... */
14573 /* Note: Ultrix has /etc/sizer, but it is not publicly executable. */
14574 /* sizer -c outputs 'cpu:<tab><tab>"DECxxxx"' */
14575           if (!unm_mod[0]) {
14576               char * p;
14577               int flag = 0;
14578               zzstring("\\fcommand(/usr/sbin/sizer -c)",&s,&y);
14579               debug(F110,"DU model",unm_mod,0);
14580               s = unm_mod;
14581               p = unm_mod;
14582               while (*p) {              /* Extract the part in quotes */
14583                   if (*p == '"') {
14584                       if (flag)
14585                         break;
14586                       flag = 1;
14587                       p++;
14588                       continue;
14589                   }
14590                   if (flag)
14591                     *s++ = *p;
14592                   p++;
14593               }
14594               *s = NUL;
14595           }
14596 #endif /* OSF32 */
14597 #endif /* COMMENT */
14598 
14599 #ifdef CK_UTSNAME
14600           if (unm_mod[0])
14601             return((char *)unm_mod);
14602           else
14603             return((char *)unm_mch);
14604 #else
14605           return("");
14606 #endif /* CK_UTSNAME */
14607       }
14608 
14609 #ifdef IBMX25
14610       /* X.25 variables (local and remote address) */
14611       case VN_X25LA:
14612         if (!local_nua[0] && !x25local_nua(local_nua))
14613           *vvbuf = NULL;
14614         else
14615           ckstrncpy(vvbuf,local_nua,VVBUFL+1);
14616         return((char *)vvbuf);
14617 
14618       case VN_X25RA:
14619         if (!remote_nua[0])
14620           *vvbuf = NULL;
14621         else
14622           ckstrncpy(vvbuf,remote_nua,VVBUFL+1);
14623         return((char *)vvbuf);
14624 #endif /* IBMX25 */
14625 
14626 #ifndef NODIAL
14627       case VN_PDSFX: {
14628           extern char pdsfx[];
14629           return((char *)pdsfx);
14630       }
14631       case VN_DTYPE: {
14632           extern int dialtype;
14633           sprintf(vvbuf,"%d",dialtype); /* SAFE */
14634           return((char *)vvbuf);
14635       }
14636 #endif /* NODIAL */
14637 
14638 #ifdef UNIX
14639       case VN_LCKPID: {
14640           extern char lockpid[];
14641           return((char *)lockpid);
14642       }
14643 #endif /* UNIX */
14644 
14645 #ifndef NOXFER
14646       case VN_BLK:
14647         sprintf(vvbuf,"%d",bctr);       /* SAFE */
14648         return((char *)vvbuf);
14649 
14650       case VN_TFTIM:
14651         sprintf(vvbuf,                  /* SAFE */
14652 #ifdef GFTIMER
14653                 "%ld", (long)(fptsecs + 0.5)
14654 #else
14655                 "%d", tsecs
14656 #endif /* GFTIMER */
14657                 );
14658         return((char *)vvbuf);
14659 #endif /* NOXFER */
14660 
14661       case VN_HWPAR:
14662       case VN_SERIAL: {
14663           int sb;
14664           char c, * ss;
14665           extern int stopbits;
14666           vvbuf[0] = NUL;
14667           if (hwparity && local && !network)
14668             ss = parnam((char)hwparity);
14669           else
14670             ss = parnam((char)parity);
14671           if (cx == VN_HWPAR) {
14672               ckstrncpy(vvbuf,ss,VVBUFL);
14673               return((char *)vvbuf);
14674           }
14675           c = ss[0];
14676           if (islower(c)) c = toupper(c);
14677           sb = stopbits;
14678           if (sb < 1)
14679             sb = (speed > 0 && speed <= 110L) ? 2 : 1;
14680           if (hwparity)
14681             sprintf(vvbuf," 8%c%d",c,sb); /* SAFE */
14682           else if (parity)
14683             sprintf(vvbuf," 7%c%d",c,sb); /* SAFE */
14684           else
14685             sprintf(vvbuf," 8N%d",sb);  /* SAFE */
14686           return((char *)vvbuf);
14687       }
14688 
14689 #ifdef UNIX
14690       case VN_LCKDIR: {
14691 #ifndef NOUUCP
14692           extern char * uucplockdir;
14693           ckstrncpy(vvbuf,uucplockdir,VVBUFL);
14694           x = strlen(vvbuf);
14695           if (x > 0) {
14696               if (vvbuf[x-1] != '/') {
14697                   vvbuf[x] = '/';
14698                   vvbuf[x+1] = NUL;
14699               }
14700           }
14701 #else
14702           vvbuf[0] = NUL;
14703 #endif /* NOUUCP */
14704           return((char *)vvbuf);
14705       }
14706 #endif /* UNIX */
14707     } /* Break up long switch statements... */
14708 
14709     switch(y) {
14710 #ifndef NODIAL
14711       case VN_DM_LP:
14712       case VN_DM_SP:
14713       case VN_DM_PD:
14714       case VN_DM_TD:
14715       case VN_DM_WA:
14716       case VN_DM_WD:
14717       case VN_DM_HF:
14718       case VN_DM_WB:
14719       case VN_DM_RC: {
14720           extern char * getdm();
14721           ckstrncpy(vvbuf,getdm(y),VVBUFL);
14722           return((char *)vvbuf);
14723       }
14724 #endif /* NODIAL */
14725 
14726       case VN_TY_LN:
14727       case VN_TY_LC: {
14728           extern int typ_lines;
14729           sprintf(vvbuf,"%d",typ_lines); /* SAFE */
14730           return((char *)vvbuf);
14731       }
14732       case VN_TY_LM: {
14733           extern int typ_mtchs;
14734           sprintf(vvbuf,"%d",typ_mtchs); /* SAFE */
14735           return((char *)vvbuf);
14736       }
14737       case VN_MACLVL:
14738         sprintf(vvbuf,"%d",maclvl);     /* SAFE */
14739         return((char *)vvbuf);
14740     } /* Break up long switch statements... */
14741 
14742     switch(y) {
14743 #ifndef NOLASTFILE
14744       case VN_LASTFIL: {
14745 	  extern char * lastfile;
14746 	  return(lastfile ? lastfile : "");
14747       }
14748 #endif	/* NOLASTFILE */
14749 #ifndef NOXFER
14750       case VN_XF_BC:
14751         sprintf(vvbuf,"%d",crunched);   /* SAFE */
14752         return((char *)vvbuf);
14753 
14754       case VN_XF_TM:
14755         sprintf(vvbuf,"%d",timeouts);   /* SAFE */
14756         return((char *)vvbuf);
14757 
14758       case VN_XF_RX:
14759         sprintf(vvbuf,"%d",retrans);    /* SAFE */
14760         return((char *)vvbuf);
14761 #endif /* NOXFER */
14762 
14763       case VN_MS_CD:                    /* Modem signals */
14764       case VN_MS_CTS:
14765       case VN_MS_DSR:
14766       case VN_MS_DTR:
14767       case VN_MS_RI:
14768       case VN_MS_RTS: {
14769           int x, z = -1;
14770           x = ttgmdm();                 /* Try to get them */
14771           if (x > -1) {
14772               switch (y) {
14773                 case VN_MS_CD:  z = (x & BM_DCD) ? 1 : 0; break;
14774                 case VN_MS_DSR: z = (x & BM_DSR) ? 1 : 0; break;
14775                 case VN_MS_CTS: z = (x & BM_CTS) ? 1 : 0; break;
14776 #ifdef MAC
14777                 case VN_MS_DTR: z = (x & BM_DTR) ? 1 : 0; break;
14778 #else
14779 #ifndef STRATUS
14780                 case VN_MS_RI:  z = (x & BM_RNG) ? 1 : 0; break;
14781 #ifndef NT
14782                 case VN_MS_DTR: z = (x & BM_DTR) ? 1 : 0; break;
14783                 case VN_MS_RTS: z = (x & BM_RTS) ? 1 : 0; break;
14784 #endif /* NT */
14785 #endif /* STRATUS */
14786 #endif /* MAC */
14787               }
14788           }
14789           sprintf(vvbuf,"%d",z);        /* SAFE */
14790           return((char *)vvbuf);
14791       }
14792       case VN_MATCH:                    /* INPUT MATCH */
14793         return(inpmatch ? inpmatch : "");
14794 
14795 #ifdef CKFLOAT
14796       case VN_ISCALE:			/* INPUT SCALE-FACTOR */
14797         return(inpscale ? inpscale : "1.0");
14798 #endif	/* CKFLOAT */
14799 
14800       case VN_SLMSG: {                  /* SET LINE / HOST message */
14801           extern char * slmsg;
14802           vvbuf[0] = NUL;
14803           if (slmsg)
14804             ckstrncpy(vvbuf,slmsg,VVBUFL);
14805          return(vvbuf);
14806       }
14807 
14808       case VN_TXTDIR:                   /* TEXTDIR */
14809         return(k_info_dir ? k_info_dir : "");
14810 
14811 #ifdef FNFLOAT
14812       case VN_MA_PI:
14813         return(math_pi);
14814 
14815       case VN_MA_E:
14816         return(math_e);
14817 
14818       case VN_MA_PR:
14819         sprintf(vvbuf,"%d",fp_digits);  /* SAFE */
14820         return(vvbuf);
14821 #endif /* FNFLOAT */
14822 
14823       case VN_CMDBL:
14824         sprintf(vvbuf,"%d",CMDBL);      /* SAFE */
14825         return(vvbuf);
14826 
14827 #ifdef CKCHANNELIO
14828       case VN_FERR: {
14829           extern int z_error;
14830           sprintf(vvbuf,"%d",z_error);  /* SAFE */
14831           return(vvbuf);
14832       }
14833       case VN_FMAX: {
14834           extern int z_maxchan;
14835           sprintf(vvbuf,"%d",z_maxchan); /* SAFE */
14836           return(vvbuf);
14837       }
14838       case VN_FCOU: {
14839           extern int z_filcount;
14840           sprintf(vvbuf,"%d",z_filcount); /* SAFE */
14841           return(vvbuf);
14842       }
14843 #endif /* CKCHANNELIO */
14844 
14845 #ifndef NODIAL
14846       case VN_DRTR: {
14847           extern int dialcount;
14848           sprintf(vvbuf,"%d",dialcount); /* SAFE */
14849           return(vvbuf);
14850       }
14851 #endif /* NODIAL */
14852 
14853 #ifndef NOLOGDIAL
14854 #ifndef NOLOCAL
14855       case VN_CXTIME:
14856         sprintf(vvbuf,"%ld",dologshow(0)); /* SAFE */
14857         return(vvbuf);
14858 #endif /* NOLOCAL */
14859 #endif /* NOLOGDIAL */
14860 
14861       case VN_BYTE:
14862         sprintf(vvbuf,"%d",byteorder);  /* SAFE */
14863         return(vvbuf);
14864 
14865       case VN_KBCHAR:
14866         vvbuf[0] = NUL;
14867         vvbuf[1] = NUL;
14868         if (kbchar > 0)
14869           vvbuf[0] = (kbchar & 0xff);
14870         return(vvbuf);
14871 
14872       case VN_TTYNAM: {
14873 #ifdef HAVECTTNAM
14874           extern char cttnam[];
14875           return((char *)cttnam);
14876 #else
14877           return(CTTNAM);
14878 #endif /* HAVECTTNAM */
14879       }
14880 
14881       case VN_PROMPT:
14882         return(cmgetp());
14883 
14884       case VN_BUILD: {
14885           extern char * buildid;
14886           return(buildid);
14887       }
14888 
14889 #ifndef NOSEXP
14890       case VN_SEXP: {
14891           extern char * lastsexp;
14892           return(lastsexp ? lastsexp : "");
14893       }
14894       case VN_VSEXP: {
14895           extern char * sexpval;
14896           return(sexpval ? sexpval : "");
14897       }
14898       case VN_LSEXP: {
14899           extern int sexpdep;
14900           ckstrncpy(vvbuf,ckitoa(sexpdep),VVBUFL);
14901           return(vvbuf);
14902       }
14903 #endif /* NOSEXP */
14904 
14905 #ifdef GFTIMER
14906       case VN_FTIME: {
14907           CKFLOAT f;
14908           ztime(&p);
14909           if (p == NULL || *p == NUL)
14910             return(NULL);
14911           z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
14912           f = (CKFLOAT)z + ((CKFLOAT)ztusec) / 1000000.0;
14913           sprintf(vvbuf,"%f",f);        /* SAFE */
14914           return(vvbuf);
14915       }
14916 #endif /* GFTIMER */
14917 
14918 #ifndef NOHTTP
14919       case VN_HTTP_C: {                 /* HTTP Code */
14920           extern int http_code;
14921           return(ckitoa(http_code));
14922       }
14923       case VN_HTTP_N:                   /* HTTP Connected */
14924         return( http_isconnected() ? "1" : "0");
14925       case VN_HTTP_H:                   /* HTTP Host */
14926         return( (char *)http_host() );
14927       case VN_HTTP_M: {                 /* HTTP Message */
14928           extern char http_reply_str[];
14929           return((char *)http_reply_str);
14930       }
14931       case VN_HTTP_S:                   /* HTTP Security */
14932         return((char *)http_security());
14933 #endif /* NOHTTP */
14934 
14935 #ifdef NEWFTP
14936       case VN_FTP_B:
14937         return((char *)ftp_cpl_mode());
14938       case VN_FTP_D:
14939         return((char *)ftp_dpl_mode());
14940       case VN_FTP_Z:
14941         return((char *)ftp_authtype());
14942       case VN_FTP_C: {
14943           extern int ftpcode;
14944           return(ckitoa(ftpcode));
14945       }
14946       case VN_FTP_M: {
14947           extern char ftp_reply_str[];
14948           if (isdigit(ftp_reply_str[0]) &&
14949               isdigit(ftp_reply_str[1]) &&
14950               isdigit(ftp_reply_str[2]) &&
14951               ftp_reply_str[3] == ' ')
14952             return(&ftp_reply_str[4]);
14953           else
14954             return(ftp_reply_str);
14955       }
14956       case VN_FTP_S: {
14957           extern char ftp_srvtyp[];
14958           return((char *)ftp_srvtyp);
14959       }
14960       case VN_FTP_H: {
14961           extern char * ftp_host;
14962           return(ftp_host ? ftp_host : "");
14963       }
14964       case VN_FTP_X: {                  /* FTP Connected */
14965           return(ftpisconnected() ? "1" : "0");
14966       }
14967       case VN_FTP_L: {                  /* FTP Logged in */
14968           return(ftpisloggedin() ? "1" : "0");
14969       }
14970       case VN_FTP_G: {                  /* FTP GET-PUT-REMOTE */
14971           extern int ftpget;
14972           char * s = "";
14973           switch (ftpget) {
14974             case 0: s = "kermit"; break;
14975             case 1: s = "ftp"; break;
14976             case 2: s = "auto"; break;
14977           }
14978           return(s);
14979       }
14980 #endif /* NEWFTP */
14981 
14982 #ifndef NOLOCAL
14983       case VN_CX_STA: {                 /* CONNECT status */
14984           extern int cx_status;
14985           return(ckitoa(cx_status));
14986       }
14987 #endif /* NOLOCAL */
14988       case VN_NOW:                      /* Timestamp */
14989         return(ckcvtdate(p,0));
14990 
14991       case VN_HOUR:                     /* Hour of the day */
14992         ztime(&p);                      /* "Thu Feb  8 12:00:00 1990" */
14993         if (!p) p = "";
14994         if (!*p) return(p);
14995         vvbuf[0] = p[11];
14996         vvbuf[1] = p[12];
14997         vvbuf[2] = NUL;
14998         return(vvbuf);                  /* and return it */
14999 
15000       case VN_BITS:			/* Bits (16, 32, 64) */
15001 	if (sizeof(long) > 4)
15002 	  return(ckitoa(8*sizeof(long)));
15003 	else
15004 	  return(ckitoa(8*sizeof(int)));
15005 
15006       case VN_LASTKWV:			/* 212 */
15007 	return(lastkwval ? lastkwval : "");
15008 
15009       case VN_HOSTIP: {			/* 212 */
15010 #ifdef TCPSOCKET
15011 	  extern char hostipaddr[];
15012 	  return((char *)hostipaddr);
15013 #else
15014 	  return("");
15015 #endif	/* TCPSOCKET */
15016       }
15017       case VN_INPMSG:
15018 	switch (instatus) {
15019 	  case INP_OK:  return("SUCCESS");
15020 	  case INP_TO:  return("Timed out");
15021 	  case INP_UI:  return("Keyboard interrupt");
15022 	  case INP_IE:  return("Internal error");
15023 	  case INP_IO:  return("I/O error or connection lost");
15024 	  case INP_IKS: return("INPUT disabled");
15025 	  case INP_BF:  return("Buffer filled and /NOWRAP set");
15026 	  default:      return("Unknown");
15027 	}
15028 
15029       case VN_VAREVAL:			/* 212 */
15030 	return(vareval ? "recursive" : "simple");
15031 
15032       case VN_LOG_CON:			/* \v(...) for log files */
15033 #ifdef CKLOGDIAL
15034         return(diafil);
15035 #else
15036         return("");
15037 #endif
15038       case VN_LOG_PKT:
15039 #ifndef NOXFER
15040         return(pktfil);
15041 #else
15042         return("");
15043 #endif
15044       case VN_LOG_SES:
15045 #ifndef NOLOCAL
15046         return(sesfil);
15047 #else
15048         return("");
15049 #endif
15050       case VN_LOG_TRA:
15051 #ifdef TLOG
15052         return(trafil);
15053 #else
15054         return("");
15055 #endif
15056       case VN_LOG_DEB:
15057 #ifdef DEBUG
15058         return(debfil);
15059 #else
15060         return("");
15061 #endif
15062       case VN_PREVCMD: {
15063 	  extern char * prevcmd;
15064 	  return(prevcmd ?  prevcmd : "");
15065       }
15066     }
15067 
15068 #ifndef NODIAL
15069     switch (y) {                        /* Caller ID values */
15070       extern char
15071         * callid_date, * callid_time, * callid_name,
15072         * callid_nmbr, * callid_mesg;
15073 
15074       case VN_CI_DA:
15075         return(callid_date ? callid_date : "");
15076 
15077       case VN_CI_TI:
15078         return(callid_time ? callid_time : "");
15079 
15080       case VN_CI_NA:
15081         return(callid_name ? callid_name : "");
15082 
15083       case VN_CI_NU:
15084         return(callid_nmbr ? callid_nmbr : "");
15085 
15086       case VN_CI_ME:
15087         return(callid_mesg ? callid_mesg : "");
15088 
15089     } /* End of variable-name switches */
15090 #endif /* NODIAL */
15091 
15092 #ifdef NT
15093     switch (y) {
15094       case VN_PERSONAL:
15095         p = (char *)GetPersonal();
15096         if (p) {
15097             GetShortPathName(p,vvbuf,VVBUFL);
15098             return(vvbuf);
15099         }
15100         return("");
15101       case VN_DESKTOP:
15102           p = (char *)GetDesktop();
15103           if (p) {
15104               GetShortPathName(p,vvbuf,VVBUFL);
15105               return(vvbuf);
15106           }
15107           return("");
15108       case VN_COMMON:
15109         p = (char *)GetAppData(1);
15110         if (p) {
15111             ckmakmsg(vvbuf,VVBUFL,p,"Kermit 95/",NULL,NULL);
15112             GetShortPathName(vvbuf,vvbuf,VVBUFL);
15113             return(vvbuf);
15114         }
15115         return("");
15116       case VN_APPDATA:
15117         p = (char *)GetAppData(0);
15118         if (p) {
15119             ckmakmsg(vvbuf,VVBUFL,p,"Kermit 95/",NULL,NULL);
15120             GetShortPathName(vvbuf,vvbuf,VVBUFL);
15121             return(vvbuf);
15122         }
15123         return("");
15124     }
15125 #endif /* NT */
15126 
15127 #ifdef TN_COMPORT
15128     switch (y) {
15129       case VN_TNC_SIG: {
15130         p = (char *) tnc_get_signature();
15131         ckstrncpy(vvbuf,p ? p : "",VVBUFL);
15132         return(vvbuf);
15133       }
15134     }
15135 #endif /* TN_COMPORT */
15136 
15137 #ifdef KUI
15138     switch (y) {
15139       case VN_GUI_RUN: {
15140 	  extern HWND getHwndKUI();
15141 	  if ( IsIconic(getHwndKUI()) )
15142             return("minimized");
15143 	  if ( IsZoomed(getHwndKUI()) )
15144             return("maximized");
15145 	  return("restored");
15146       }
15147       case VN_GUI_XP:
15148         sprintf(vvbuf,"%d",get_gui_window_pos_x());  /* SAFE */
15149         return(vvbuf);
15150       case VN_GUI_YP:
15151         sprintf(vvbuf,"%d",get_gui_window_pos_y());  /* SAFE */
15152         return(vvbuf);
15153       case VN_GUI_XR:
15154         sprintf(vvbuf,"%d",GetSystemMetrics(SM_CXSCREEN));  /* SAFE */
15155         return(vvbuf);
15156       case VN_GUI_YR:
15157         sprintf(vvbuf,"%d",GetSystemMetrics(SM_CYSCREEN));  /* SAFE */
15158         return(vvbuf);
15159       case VN_GUI_FNM:
15160           if ( ntermfont > 0 ) {
15161               int i;
15162               for (i = 0; i < ntermfont; i++) {
15163                   if (tt_font == term_font[i].kwval) {
15164                       ckstrncpy(vvbuf,term_font[i].kwd,VVBUFL);
15165                       return(vvbuf);
15166                   }
15167               }
15168           }
15169           return("(unknown)");
15170       case VN_GUI_FSZ:
15171           ckstrncpy(vvbuf,ckitoa(tt_font_size/2),VVBUFL);
15172           if ( tt_font_size % 2 )
15173               ckstrncat(vvbuf,".5",VVBUFL);
15174           return(vvbuf);
15175     }
15176 #endif /* KUI */
15177 
15178     fnsuccess = 0;
15179     if (fnerror) {
15180         fnsuccess = 0;
15181     }
15182     if (fndiags) {
15183         if (!embuf[0])
15184           ckstrncpy(embuf,"<ERROR:NO_SUCH_VARIABLE>",EMBUFLEN);
15185         printf("?%s\n",embuf);
15186         return((char *)embuf);
15187     } else
15188       return("");
15189 }
15190 #endif /* NOSPL */
15191 
15192 /* warning, this won't work for VMS */
15193 char *
getbasename(s)15194 getbasename(s) char *s; {
15195     int n, i;
15196     if (!s) s = "";
15197     if (!*s) return("");
15198     n = (int)strlen(s);
15199     for (i = n-2; i >= 0; i--) {
15200         if (ISDIRSEP(s[i]))
15201           return(s+i+1);
15202     }
15203     return(s);
15204 }
15205 
15206 /*
15207   X X S T R I N G  --  Expand variables and backslash codes.
15208 
15209     int xxtstring(s,&s2,&n);
15210 
15211   Expands \ escapes via recursive descent.
15212   Argument s is a pointer to string to expand (source).
15213   Argument s2 is the address of where to put result (destination).
15214   Argument n is the length of the destination string (to prevent overruns).
15215   Returns -1 on failure, 0 on success,
15216     with destination string null-terminated and s2 pointing to the
15217     terminating null, so that subsequent characters can be added.
15218     Failure reasons include destination buffer is filled up.
15219 */
15220 
15221 #define XXDEPLIM 100                    /* Recursion depth limit */
15222 /*
15223   In Windows the stack is limited to 256K so big character arrays like
15224   vnambuf can't be on the stack in recursive functions like zzstring().
15225   But that's no reason use malloc() in Unix or VMS, which don't have
15226   this kind of restriction.
15227 */
15228 #ifdef DVNAMBUF				/* Dynamic vnambuf[] */
15229 #undef DVNAMBUF				/* Clean slate */
15230 #endif /* DVNAMBUF */
15231 
15232 #ifndef NOSPL				/* Only if SPL included */
15233 #ifdef OS2				/* Only for K95 */
15234 #define DVNAMBUF
15235 #endif /* OS2 */
15236 #endif /* NOSPL */
15237 
15238 int
zzstring(s,s2,n)15239 zzstring(s,s2,n) char *s; char **s2; int *n; {
15240     int x,                              /* Current character */
15241         xx,                             /* Worker */
15242         y,                              /* Worker */
15243         pp,                             /* Paren level */
15244         kp,                             /* Brace level */
15245         argn,                           /* Function argument counter */
15246         n2,                             /* Local copy of n */
15247         d,                              /* Array dimension */
15248         vbi,                            /* Variable id (integer form) */
15249         argl,                           /* String argument length */
15250         nx,                             /* Save original length */
15251 	quoting = 0;			/* 299 */
15252 
15253     char vb,                            /* Variable id (char form) */
15254         *vp,                            /* Pointer to variable definition */
15255         *new,                           /* Local pointer to target string */
15256 #ifdef COMMENT
15257         *old,                           /* Save original target pointer */
15258 #endif /* COMMENT */
15259         *p,                             /* Worker */
15260         *q,                             /* Worker */
15261         *s3;                            /* Worker */
15262     int  x3;                            /* Worker */
15263     char *r  = (char *)0;               /* For holding function args */
15264     char *r2 = (char *)0;
15265     char *r3p;
15266 
15267 #ifndef NOSPL
15268 #ifdef DVNAMBUF
15269     char * vnambuf = NULL;              /* Buffer for variable/function name */
15270 #else /* DVNAMBUF */
15271     char vnambuf[VNAML];                /* Buffer for variable/function name */
15272 #endif /* DVNAMBUF */
15273     char *argp[FNARGS];                 /* Pointers to function args */
15274 #endif /* NOSPL */
15275 
15276     static int depth = 0;               /* Call depth, avoid overflow */
15277 
15278     n2 = *n;                            /* Make local copies of args */
15279     nx = n2;
15280 
15281 #ifdef COMMENT
15282     /* This is always 32K in BIGBUFOK builds */
15283     if (depth == 0)
15284       debug(F101,"zzstring top-level n","",n2);
15285 #endif	/* COMMENT */
15286 
15287     new = *s2;                          /* for one less level of indirection */
15288 #ifdef COMMENT
15289     old = new;
15290 #endif /* COMMENT */
15291 
15292 #ifndef NOSPL
15293     itsapattern = 0;			/* For \fpattern() */
15294     isjoin = 0;				/* For \fjoin() */
15295 #endif /* NOSPL */
15296     depth++;                            /* Sink to a new depth */
15297     if (depth > XXDEPLIM) {             /* Too deep? */
15298         (VOID) newerrmsg("Definition is circular or too deep");
15299         debug(F111,"zzstring","Definition is circular or too deep",XXDEPLIM);
15300         depth = 0;
15301         *new = NUL;
15302         return(-1);
15303     }
15304     if (!s || !new) {                   /* Watch out for null pointers */
15305         debug(F101,"zzstring fail 2","",depth);
15306         if (new)
15307           *new = NUL;
15308         depth = 0;
15309         return(-1);
15310     }
15311     s3 = s;
15312     argl = 0;
15313     while (*s3++) argl++;              /* Get length of source string */
15314     debug(F010,"zzstring entry",s,0);
15315     if (argl == 0) {                    /* Empty string */
15316         debug(F111,"zzstring empty arg",s,argl);
15317         depth = 0;
15318         *new = NUL;
15319         return(0);
15320     }
15321     if (argl < 0) {                     /* Watch out for garbage */
15322         debug(F101,"zzstring fail 3","",depth);
15323         *new = NUL;
15324         depth = 0;
15325         return(-1);
15326     }
15327 #ifdef DVNAMBUF
15328     debug(F100,"vnambuf malloc...","",0);
15329     vnambuf = malloc(VNAML);
15330     if (vnambuf == NULL) {
15331         printf("?Out of memory");
15332         return(-1);
15333     }
15334     debug(F100,"vnambuf malloc ok","",0);
15335 #endif /* DVNAMBUF */
15336 
15337     while ((x = *s)) {                  /* Loop for all characters */
15338         if (x != CMDQ) {                /* Is it the command-quote char? */
15339             *new++ = *s++;              /* No, normal char, just copy */
15340             if (--n2 < 0) {             /* and count it, careful of overflow */
15341                 debug(F101,"zzstring overflow 1","",depth);
15342                 depth = 0;
15343 #ifdef DVNAMBUF
15344                 if (vnambuf) free(vnambuf);
15345 #endif /* DVNAMBUF */
15346                 return(-1);
15347             }
15348             continue;
15349         }
15350 
15351 /* We have the command-quote character. */
15352 
15353         x = *(s+1);                     /* Get the following character. */
15354         if (isupper(x)) x = tolower(x);
15355         switch (x) {                    /* Act according to variable type */
15356 #ifndef NOSPL
15357           case 0:                       /* It's a lone backslash */
15358             *new++ = *s++;
15359             if (--n2 < 0) {
15360                 debug(F101,"zzstring overflow 2","",0);
15361 #ifdef DVNAMBUF
15362                 if (vnambuf) free(vnambuf);
15363 #endif /* DVNAMBUF */
15364                 return(-1);
15365             }
15366             break;
15367           case '%':                     /* Variable */
15368             s += 2;                     /* Get the letter or digit */
15369             vb = *s++;                  /* and move source pointer past it */
15370             vp = NULL;                  /* Assume definition is empty */
15371             if (vb >= '0' && vb <= '9') { /* Digit for macro arg */
15372                 if (maclvl < 0)         /* Digit variables are global */
15373                   vp = g_var[vb];       /* if no macro is active */
15374                 else                    /* otherwise */
15375                   vp = m_arg[maclvl][vb - '0']; /* they're on the stack */
15376             } else if (vb == '*') {     /* Macro args string */
15377 #ifdef COMMENT
15378                 /* This doesn't take changes into account */
15379                 vp = (maclvl >= 0) ? m_line[maclvl] : topline;
15380                 if (!vp) vp = "";
15381 #else
15382 		char * ss = new;
15383                 if (zzstring("\\fjoin(&_[],,1)",&new,&n2) < 0) {
15384 #ifdef DVNAMBUF
15385 		    if (vnambuf) free(vnambuf);
15386 #endif /* DVNAMBUF */
15387 		    return(-1);
15388 		}
15389 		debug(F110,"zzstring \\%*",ss,0);
15390                 break;
15391 #endif /* COMMENT */
15392             } else {
15393                 if (isupper(vb)) vb += ('a'-'A');
15394                 vp = g_var[vb];         /* Letter for global variable */
15395             }
15396             if (!vp) vp = "";
15397 #ifdef COMMENT
15398             if (vp) {                   /* If definition not empty */
15399 #endif /* COMMENT */
15400 		if (vareval) {
15401 		    debug(F010,"zzstring %n vp",vp,0);
15402 		    /* call self to evaluate it */
15403 		    if (zzstring(vp,&new,&n2) < 0) {
15404 			debug(F101,"zzstring fail 6","",depth);
15405 #ifdef DVNAMBUF
15406 			if (vnambuf) free(vnambuf);
15407 #endif /* DVNAMBUF */
15408 			return(-1);	/* Pass along failure */
15409 		    }
15410 		} else {
15411 		    while ((*new++ = *vp++)) /* copy it to output string. */
15412 		      if (--n2 < 0) {
15413 			  if (q) free(q);
15414 			  debug(F101,"zzstring overflow 4.5","",depth);
15415 #ifdef DVNAMBUF
15416 			  if (vnambuf) free(vnambuf);
15417 #endif /* DVNAMBUF */
15418 			  return(-1);
15419 		      }
15420 		    new--;		/* Back up over terminating null */
15421 		    n2++;		/* to allow for further deposits. */
15422 		}
15423 #ifdef COMMENT
15424             } else {
15425                 debug(F110,"zzstring %n vp","(NULL)",0);
15426                 n2 = nx;
15427                 new = old;
15428                 *new = NUL;
15429             }
15430 #endif /* COMMENT */
15431             break;
15432           case '&':                     /* An array reference */
15433             x = arraynam(s,&vbi,&d);    /* Get name and subscript */
15434             debug(F111,"zzstring arraynam",s,x);
15435             if (x < 0) {
15436                 debug(F101,"zzstring fail 7","",depth);
15437 #ifdef DVNAMBUF
15438                 if (vnambuf) free(vnambuf);
15439 #endif /* DVNAMBUF */
15440                 return(-1);
15441             }
15442             pp = 0;                     /* Bracket counter */
15443             while (*s) {                /* Advance source pointer... */
15444                 if (*s == '[') pp++;
15445                 if (*s == ']' && --pp == 0) break;
15446                 s++;
15447             }
15448             if (*s == ']') s++;         /* ...past the closing bracket. */
15449 
15450             xx = chkarray(vbi,d);        /* Array is declared? */
15451             debug(F101,"zzstring chkarray","",x);
15452             if (xx > -1) {
15453 #ifdef COMMENT
15454                 char * s1 = NULL;
15455 #endif /* COMMENT */
15456                 vbi -= ARRAYBASE;       /* Convert name to index */
15457 
15458                   if (a_dim[vbi] >= d) { /* If subscript in range */
15459                     char **ap;
15460                     ap = a_ptr[vbi];    /* get data pointer */
15461                     if (ap) {           /* and if there is one */
15462                         if (ap[d]) {    /* If definition not empty */
15463                             debug(F111,"zzstring ap[d]",ap[d],d);
15464 			    if (vareval) {
15465 				if (zzstring(ap[d],&new,&n2) < 0) {
15466 				    debug(F101,"zzstring fail 8","",depth);
15467 #ifdef DVNAMBUF
15468 				    if (vnambuf) free(vnambuf);
15469 #endif /* DVNAMBUF */
15470 				    return(-1); /* Pass along failure */
15471 				}
15472 			    } else {
15473 				vp = ap[d];
15474 				while ((*new++ = *vp++)) /* copy to result */
15475 				  if (--n2 < 0) {
15476 				      if (q) free(q);
15477 				      debug(F101,
15478 					    "zzstring overflow 8.5","",depth);
15479 #ifdef DVNAMBUF
15480 				      if (vnambuf) free(vnambuf);
15481 #endif /* DVNAMBUF */
15482 				      return(-1);
15483 				  }
15484 				new--;	/* Back up over terminating null */
15485 				n2++;	/* to allow for further deposits. */
15486 			    }
15487 			}
15488 
15489                     } else {
15490                         /* old = new; */
15491                         n2 = nx;
15492                     }
15493                 }
15494 	    }
15495             break;
15496 
15497           case 'f':                     /* A builtin function */
15498             q = vnambuf;                /* Copy the name */
15499             y = 0;                      /* into a separate buffer */
15500             s += 2;                     /* point past 'F' */
15501             while (y++ < VNAML) {
15502                 if (*s == '(') { s++; break; } /* Look for open paren */
15503                 if ((*q = *s) == NUL) break;   /* or end of string */
15504                 s++; q++;
15505             }
15506             *q = NUL;                   /* Terminate function name */
15507             if (y >= VNAML) {           /* Handle pathological case */
15508                 while (*s && (*s != '(')) /* of very long string entered */
15509                   s++;                    /* as function name. */
15510                 if (*s == ')') s++;       /* Skip past it. */
15511             }
15512             r = r2 = malloc(argl+2);    /* And make a place to copy args */
15513             /* debug(F101,"zzstring r2","",r2); */
15514             if (!r2) {                  /* Watch out for malloc failure */
15515                 debug(F101,"zzstring fail 9","",depth);
15516                 *new = NUL;
15517                 depth = 0;
15518 #ifdef DVNAMBUF
15519                 if (vnambuf) free(vnambuf);
15520 #endif /* DVNAMBUF */
15521                 return(-1);
15522             }
15523             if (r3) free(r3); /* And another to copy literal arg string */
15524             r3 = malloc(argl+2);
15525             /* debug(F101,"zzstring r3","",r3); */
15526             if (!r3) {
15527                 debug(F101,"zzstring fail 10","",depth);
15528                 depth = 0;
15529                 *new = NUL;
15530                 if (r2) free(r2);
15531 #ifdef DVNAMBUF
15532                 if (vnambuf) free(vnambuf);
15533 #endif /* DVNAMBUF */
15534                 return(-1);
15535             } else
15536               r3p = r3;
15537             argn = 0;                   /* Argument counter */
15538             argp[argn++] = r;           /* Point to first argument */
15539             y = 0;                      /* Completion flag */
15540             pp = 1;                     /* Paren level (already have one). */
15541             kp = 0;
15542             while (1) {                 /* Copy each argument, char by char. */
15543                 *r3p++ = *s;            /* This is a literal copy for \flit */
15544                 if (!*s) break;
15545 
15546                 if (*s == '{') {        /* Left brace */
15547                     kp++;
15548                 }
15549                 if (*s == '}') {        /* Right brace */
15550                     kp--;
15551                 }
15552                 if (*s == '(' && kp <= 0) { /* Open paren not in brace */
15553                     pp++;               /* Count it */
15554                 }
15555                 *r = *s;                /* Now copy resulting byte */
15556                 if (!*r)                /* If NUL, done. */
15557                   break;
15558                 if (*r == ')' && kp <= 0) { /* Closing paren, count it. */
15559                     if (--pp == 0) {    /* Final one? */
15560                         *r = NUL;       /* Make it a terminating null */
15561                         *(r3p - 1) = NUL;
15562                         s++;            /* Point past it in source string */
15563                         y = 1;          /* Flag we've got all the args */
15564                         break;          /* Done with while loop */
15565                     }
15566                 }
15567                 if (*r == ',' && kp <= 0) { /* Comma */
15568                     if (pp == 1) {          /* If not within ()'s, */
15569                         if (argn >= FNARGS) { /* Too many args */
15570                             s++; r++;   /* Keep collecting flit() string */
15571                             continue;
15572                         }
15573                         *r = NUL;           /* New arg, skip past comma */
15574                         argp[argn++] = r+1; /* In range, point to new arg */
15575                     }                   /* Otherwise just skip past  */
15576                 }
15577                 s++; r++;               /* Advance pointers */
15578             }
15579             if (!y)                     /* If we didn't find closing paren */
15580               argn = -1;
15581 #ifdef DEBUG
15582             if (deblog) {
15583                 char buf[24];
15584                 debug(F111,"zzstring function name",vnambuf,y);
15585                 debug(F010,"zzstring function r3",r3,0);
15586                 for (y = 0; y < argn; y++) {
15587                     sprintf(buf,"arg %2d ",y);
15588                     debug(F010,buf,argp[y],0);
15589                 }
15590             }
15591 #endif /* DEBUG */
15592 	    {
15593 	     /* In case the function name itself is constructed */
15594 		char buf[64]; char * p = buf; int n = 64;
15595 		if (zzstring(vnambuf,&p,&n) > -1)
15596 		  ckstrncpy(vnambuf,buf,64);
15597 	    }
15598             vp = fneval(vnambuf,argp,argn,r3); /* Evaluate the function. */
15599             if (vp) {                      /* If definition not empty */
15600                 while ((*new++ = *vp++)) { /* copy it to output string */
15601                     if (--n2 < 0) {        /* watch out for overflow */
15602                         debug(F101,"zzstring fail 12","",depth);
15603                         if (r2) { free(r2); r2 = NULL; }
15604                         if (r3) { free(r3); r3 = NULL; }
15605 #ifdef DVNAMBUF
15606                         if (vnambuf) free(vnambuf);
15607 #endif /* DVNAMBUF */
15608                         return(-1);
15609                     }
15610                 }
15611                 new--;                  /* Back up over terminating null */
15612                 n2++;                   /* to allow for further deposits. */
15613             }
15614             if (r2) { free(r2); r2 = NULL; }
15615             if (r3) { free(r3); r3 = NULL; }
15616             break;
15617 	  case 'q':			/* 299 String to be take literally */
15618 	    quoting = 1;		/* 299 */
15619           case '$':                     /* An environment variable */
15620           case 'v':                     /* Or a named builtin variable. */
15621           case 'm':                     /* Or a macro /long variable */
15622           case 's':                     /* 196 Macro substring */
15623           case ':':                     /* 196 \-variable substring */
15624             pp = 0;
15625             p = s+2;                    /* $/V/M must be followed by (name) */
15626             if (*p != '(') {            /* as in \$(HOME) or \V(count) */
15627                 *new++ = *s++;          /* If not, just copy it */
15628                 if (--n2 < 0) {
15629                     debug(F101,"zzstring overflow 3","",depth);
15630 #ifdef DVNAMBUF
15631                     if (vnambuf) free(vnambuf);
15632 #endif /* DVNAMBUF */
15633                     return(-1);
15634                 }
15635                 break;
15636             }
15637             pp++;
15638             p++;                        /* Point to 1st char of name */
15639             q = vnambuf;                /* Copy the name */
15640             y = 0;                      /* into a separate buffer */
15641 	    debug(F110,">>>> \\q(ARG)",p,0);
15642             while (y++ < VNAML) {       /* Watch out for name too long */
15643                 if (*p == '(') {        /* Parens can be nested... */
15644 		    if (*(p-1) != CMDQ)	/* 299 */
15645 		      pp++;
15646                 } else if (*p == ')') { /* Name properly terminated with ')' */
15647 		    if (*(p-1) != CMDQ)	/* 299 */
15648 		      pp--;
15649                     if (pp == 0) {
15650                         p++;            /* Move source pointer past ')' */
15651                         break;
15652                     }
15653                 }
15654                 if ((*q = *p) == NUL)   /* String ends before ')' */
15655                   break;
15656                 p++; q++;               /* Advance pointers */
15657             }
15658             *q = NUL;                   /* Terminate the variable name */
15659             if (y >= VNAML) {           /* Handle pathological case */
15660                 while (*p && (*p != ')')) /* of very long string entered */
15661                   p++;                    /* as variable name. */
15662                 if (*p == ')') p++;       /* Skip ahead to the end of it. */
15663             }
15664 /* At this point vnambuf contains the macro name from inside the parens */
15665 
15666             s = p;                      /* Adjust global source pointer */
15667             s3 = vnambuf;
15668             x3 = 0;
15669             while (*s3++) x3++;         /* Length needed */
15670 
15671 /* The following is in case the macro name itself contains variables */
15672 
15673             p = malloc(x3 + 1);         /* Make temporary space */
15674             if (p && !quoting) {	/* If we got the space */
15675                 vp = vnambuf;           /* Point to original */
15676                 strcpy(p,vp);           /* (safe) Make a copy of it */
15677                 y = VNAML;              /* Length of name buffer */
15678                 zzstring(p,&vp,&y);     /* Evaluate the copy */
15679                 free(p);                /* Free the temporary space */
15680                 p = NULL;
15681             }
15682 /* At this point vnambuf contains the fully evaluated macro name */
15683 
15684             debug(F110,"zzstring macro name",vnambuf,0);
15685             q = NULL;
15686 	    if (x == 'q') {		/* 299 Quoting this string */
15687 		vp = vnambuf;		/* 299 */
15688 		debug(F110,">>> VP",vp,0);
15689 	    } else if (x == '$') {	/* Look up its value */
15690                 vp = getenv(vnambuf);   /* This way for environment variable */
15691             } else if (x == 'm' || x == 's' || x == ':') { /* Macro / substr */
15692                 int k, x1 = -1, x2 = -1;
15693 		char c = NUL;
15694                 k = strlen(vnambuf);
15695                 /* \s(name[n:m]) -- Compact substring notation */
15696                 if ((x == 's' || x == ':') && (k > 1)) { /* Substring wanted */
15697 		    int bprc;
15698                     if (vnambuf[k-1] == ']') {
15699                         int i;
15700                         for (i = 0; i < k-1; i++) {
15701                             if (vnambuf[i] == '[') {
15702 				bprc = boundspair(vnambuf,":_.",&x1,&x2,&c);
15703 				debug(F111,"zzstring boundspair",vnambuf,bprc);
15704 				debug(F000,"zzstring boundspair c","",c);
15705 				if (bprc > -1) {
15706 				    vnambuf[i] = NUL;
15707 				    if (x1 < 1)
15708 				      x1 = 1;
15709 				    x1--;	/* Adjust to 0-base */
15710 				}
15711 				break;
15712                             }
15713                         }
15714 		    }
15715                 }
15716                 if (x == ':') {		/* Variable type (s or :) */
15717                     vp = vnambuf;
15718                 } else {                /* Regular macro or associative array*/
15719 		    y = isaarray(vnambuf) ?
15720 			mxxlook(mactab,vnambuf,nmac) : /* Assoc Array */
15721 			mxlook(mactab,vnambuf,nmac);   /* Macro */
15722                     if (y > -1) {	/* Got definition */
15723                         vp = mactab[y].mval;
15724                     } else {
15725                         vp = NULL;
15726                     }
15727                 }
15728 		debug(F111,"zzstring vp",vp,(vp==NULL)?0:strlen(vp));
15729 
15730                 if (vp) {
15731                     if ((x == 's' || x == ':') && (k > 1)) {
15732                         /* Compact substring notation */
15733                         if (x2 == 0) {  /* Length */
15734                             vp = NULL;
15735                         } else if (x1 > -1) { /* Start */
15736                             k = strlen(vp);
15737 			    debug(F101,">>> k","",k);
15738 			    /* If it's off the end, result is empty */
15739                             if (x1 > k) {
15740                                 vp = NULL;
15741                             } else if (k > 0) {
15742 				/* Stay in bounds */
15743 				if (c == '_' && x2 > k)	/* startpos_endpos */
15744 				  x2 = k;
15745 				if (c == ':' && x1 + x2 > k) /* start:length */
15746 				  x2 = -1;
15747 				debug(F101,">>> x2","",x2);
15748 				debug(F000,">>> c","",c);
15749                                 if ((q = malloc(k+1))) {
15750                                     strcpy(q,vp); /* safe */
15751 				    if (c == '.') {
15752 					q[x1+1] = NUL;
15753 					debug(F000,"XXX. q",q,c);
15754 				    }
15755 				    if (c == ':') { /* start:length */
15756 					if ((x2 > -1) && ((x1 + x2) <= k)) {
15757 					    q[x1+x2] = NUL;
15758 					}
15759 					debug(F000,"XXX: q",q,c);
15760 				    } else if (c == '_') { /* start_endpos */
15761 					if (x1 >= x2) {
15762 					    q[x1 = 0] = NUL;
15763 					} else if (x2 < k && x2 > -1) {
15764 					    q[x2] = NUL;
15765 					}
15766 					debug(F000,"XXX_ q",q,c);
15767 				    }
15768 				    vp = q+x1;
15769                                 } else vp = NULL;
15770                             } else vp = NULL;
15771                         }
15772 
15773 			debug(F110,"XXX vnambuf",vnambuf,0);
15774 			debug(F000,"XXX c","",c);
15775 			debug(F101,"XXX x1","",x1);
15776 			debug(F101,"XXX x2","",x2);
15777 			debug(F110,"XXX result",vp,0);
15778 #ifdef DEBUG
15779                         if (deblog) {
15780                             if (!vp) {
15781                             } else {
15782                                 k = strlen(vp);
15783                             }
15784                         }
15785 #endif /* DEBUG */
15786                     }
15787                 }
15788             } else {                    /* or */
15789                 vp = nvlook(vnambuf);   /* this way for builtin variable */
15790             }
15791             if (vp) {                   /* If definition not empty */
15792                 while ((*new++ = *vp++)) /* copy it to output string. */
15793                   if (--n2 < 0) {
15794                       if (q) free(q);
15795                       debug(F101,"zzstring overflow 4","",depth);
15796 #ifdef DVNAMBUF
15797                       if (vnambuf) free(vnambuf);
15798 #endif /* DVNAMBUF */
15799                       return(-1);
15800                   }
15801                 new--;                  /* Back up over terminating null */
15802                 n2++;                   /* to allow for further deposits. */
15803             }
15804             if (q) {
15805                 free(q);
15806                 q = NULL;
15807             }
15808             break;
15809 #endif /* NOSPL */                      /* Handle \nnn even if NOSPL. */
15810 
15811 #ifndef NOKVERBS
15812         case 'K':
15813         case 'k': {
15814             extern struct keytab kverbs[];
15815             extern int nkverbs;
15816 #define K_BUFLEN 30
15817             char kbuf[K_BUFLEN + 1];    /* Key verb name buffer */
15818             int x, y, z, brace = 0;
15819             s += 2;
15820 /*
15821   We assume that the verb name is {braced}, or it extends to the end of the
15822   string, s, or it ends with a space, control character, or backslash.
15823 */
15824             p = kbuf;                   /* Copy verb name into local buffer */
15825             x = 0;
15826             if (*s == '{')  {
15827                 s++;
15828                 brace++;
15829             }
15830             while ((x++ < K_BUFLEN) && (*s > SP) && (*s != CMDQ)) {
15831                 if (brace && *s == '}') {
15832                     s++;
15833                     break;
15834                 }
15835                 *p++ = *s++;
15836             }
15837             brace = 0;
15838             *p = NUL;                   /* Terminate. */
15839             p = kbuf;                   /* Point back to beginning */
15840             debug(F110,"zzstring kverb",p,0);
15841             y = xlookup(kverbs,p,nkverbs,&x); /* Look it up */
15842             debug(F101,"zzstring lookup",0,y);
15843             if (y > -1) {
15844                 dokverb(VCMD,y);
15845 #ifndef NOSPL
15846             } else {                    /* Is it a macro? */
15847                 y = mxlook(mactab,p,nmac);
15848                 if (y > -1) {
15849                     debug(F111,"zzstring mxlook",p,y);
15850                     if ((z = dodo(y,NULL,cmdstk[cmdlvl].ccflgs)) > 0) {
15851                         if (cmpush() > -1) {  /* Push command parser state */
15852                             extern int ifc;
15853                             int ifcsav = ifc; /* Push IF condition on stack */
15854                             y = parser(1);    /* New parser to execute macro */
15855                             cmpop();          /* Pop command parser */
15856                             ifc = ifcsav;     /* Restore IF condition */
15857                             if (y == 0) {     /* No errors, ignore actions */
15858                                 p = mrval[maclvl+1]; /* If OK set return val */
15859                                 if (p == NULL) p = "";
15860                             }
15861                         } else {                /* Can't push any more */
15862                             debug(F101,"zzstring pushed too deep","",depth);
15863                             printf(
15864                                "\n?Internal error: zzstring stack overflow\n"
15865                                    );
15866                             while (cmpop() > -1);
15867                             p = "";
15868                         }
15869                     }
15870                 }
15871 #endif /* NOSPL */
15872             }
15873             break;
15874         }
15875 #endif /* NOKVERBS */
15876 
15877         default:                        /* Maybe it's a backslash code */
15878           y = xxesc(&s);                /* Go interpret it */
15879           if (y < 0) {                  /* Upon failure */
15880               *new++ = (char) x;        /* Just quote the next character */
15881               s += 2;                   /* Move past the pair */
15882               n2 -= 2;
15883               if (n2 < 0) {
15884                   debug(F101,"zzstring overflow 5","",depth);
15885 #ifdef DVNAMBUF
15886                   if (vnambuf) free(vnambuf);
15887 #endif /* DVNAMBUF */
15888                   return(-1);
15889               }
15890               continue;                 /* and go back for more */
15891           } else {
15892               *new++ = (char) y;        /* else deposit interpreted value */
15893               if (--n2 < 0) {
15894                   debug(F101,"zzstring overflow 6","",depth);
15895 #ifdef DVNAMBUF
15896                   if (vnambuf) free(vnambuf);
15897 #endif /* DVNAMBUF */
15898                   return(-1);
15899               }
15900           }
15901         }
15902     }
15903     *new = NUL;                         /* Terminate the new string */
15904     debug(F010,"zzstring while exit",*s2,0);
15905 
15906     depth--;                            /* Adjust stack depth gauge */
15907     *s2 = new;                          /* Copy results back into */
15908     *n = n2;                            /* the argument addresses */
15909     debug(F101,"zzstring ok","",depth);
15910 #ifdef DVNAMBUF
15911     if (vnambuf) free(vnambuf);
15912 #endif /* DVNAMBUF */
15913     return(0);                          /* and return. */
15914 }
15915 #endif /* NOICP */
15916