Standards-Version: 3.9.6 (no changes)
[ckermit.git] / ckuus4.c
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, Columbia University, New York City
9     Jeffrey E Altman <jaltman@secure-endpoints.com>
10       Secure Endpoints Inc., New York City
11
12   Copyright (C) 1985, 2011,
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 */
17
18 /*
19   File ckuus4.c -- Functions moved from other ckuus*.c modules to even
20   out their sizes.
21 */
22 #include "ckcdeb.h"
23 #include "ckcasc.h"
24 #include "ckcker.h"
25 #include "ckcnet.h"                     /* Network symbols */
26 #include "ckuusr.h"
27 #include "ckuver.h"
28 #include "ckcxla.h"                     /* Character sets */
29
30 #ifdef CK_AUTHENTICATION
31 #include "ckuath.h"
32 #endif /* CK_AUTHENTICATION */
33 #ifdef CK_SSL
34 #include "ck_ssl.h"
35 #endif /* CK_SSL */
36
37 #ifdef VMS
38 #include <errno.h>                      /* For \v(errno) */
39 extern char * ckvmserrstr(unsigned long);
40 #ifndef OLD_VMS
41 #include <lib$routines.h>               /* Not for VAX C 2.4 */
42 #else
43 #include <libdef.h>
44 #endif /* OLD_VMS */
45 _PROTOTYP(int vmsttyfd, (void) );
46 #endif /* VMS */
47
48 #ifdef OS2
49 #ifndef NT
50 #define INCL_NOPM
51 #define INCL_VIO                        /* Needed for ckocon.h */
52 #include <os2.h>
53 #undef COMMENT
54 #else
55 #include <windows.h>
56 #include <tapi.h>
57 #include "ckntap.h"
58 #define APIRET ULONG
59 #endif /* NT */
60 #include "ckocon.h"
61 #include "ckoetc.h"
62 int StartedFromDialer = 0;
63 HWND hwndDialer = 0;
64 LONG KermitDialerID = 0;
65 #ifdef putchar
66 #undef putchar
67 #endif /* putchar */
68 #define putchar(x) conoc(x)
69 #ifdef CK_PID
70 #include <process.h>
71 #endif /* CK_PID */
72 #endif /* OS2 */
73
74 #ifdef KUI
75 extern struct keytab * term_font;
76 extern int ntermfont, tt_font, tt_font_size;
77 #endif /* KUI */
78
79 extern xx_strp xxstring;
80
81 #ifdef DEC_TCPIP
82 #include <descrip>
83 #include <dvidef>
84 #include <dcdef>
85 #endif /* DEC_TCPIP */
86
87 #ifdef FNFLOAT
88 #include <math.h>                       /* Floating-point functions */
89 #endif /* FNFLOAT */
90
91 extern int quiet, network, xitsta, escape, nopush, xferstat,
92   exitonclose, tn_exit, ttnproto, autodl, flow, byteorder, what, lastxfer;
93
94 extern int filepeek, nscanfile, makestrlen;
95 extern char * k_info_dir;
96
97 #ifndef MAC
98 #ifndef AMIGA
99 extern int ttyfd;
100 #endif /* MAC */
101 #endif /* AMIGA */
102
103 #ifdef TNCODE
104 extern int tn_nlm, tn_b_nlm, tn_b_xfer, tn_sb_bug;
105 extern int tn_rem_echo;
106 extern int tn_b_meu, tn_b_ume, tn_auth_krb5_des_bug;
107 #endif /* TNCODE */
108
109 static char * lastkwval = NULL;
110
111 char * xferfile = NULL;
112 int xferlog = 0;
113
114 extern int local, xargc, stayflg, rcflag, bgset, backgrd, cfilef,
115   inserver, srvcdmsg, success;
116
117 #ifdef VMS
118 extern int batch;
119 #endif /* VMS */
120
121 extern char cmdfil[], *versio, *ckxsys, **xargv;
122 #ifdef DEBUG
123 extern char debfil[];                   /* Debug log file name */
124 extern int debtim;
125 #endif /* DEBUG */
126
127 extern int noinit;
128
129 static char ndatbuf[10];
130
131 char *months[] = {
132     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
133     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
134 };
135
136 char *
137 zzndate() {                             /* Returns today's date as yyyymmdd */
138     char * p = NULL;
139     int x;
140
141 /* WARNING - This will fail if asctime() returns non-English month names */
142
143     ztime(&p);                          /* Get "asctime" string */
144     if (p == NULL || *p == NUL) return("");
145     for (x = 20; x < 24; x++)           /* yyyy */
146       ndatbuf[x - 20] = p[x];
147     ndatbuf[6] = (char) ((p[8] == ' ') ? '0' : p[8]);
148     ndatbuf[7] = p[9];                  /* dd */
149     for (x = 0; x < 12; x++)            /* mm */
150       if (!strncmp(p+4,months[x],3)) break;
151     if (x == 12) {
152         ndatbuf[4] = ndatbuf[5] = '?';
153     } else {
154         x++;
155         ndatbuf[4] = (char) ((x < 10) ? '0' : '1');
156         ndatbuf[5] = (char) ((x % 10) + 48);
157     }
158     ndatbuf[8] = NUL;
159     debug(F110,"zzndate return",ndatbuf,0);
160     return((char *)ndatbuf);
161 }
162
163 #ifdef DCMDBUF
164 extern struct cmdptr *cmdstk;
165 extern char *line, *tmpbuf;
166 #else
167 extern struct cmdptr cmdstk[];
168 extern char line[], tmpbuf[];
169 #endif /* DCMDBUF */
170
171 #ifdef OS2
172 extern char exedir[];
173 #else
174 extern char * exedir;
175 #endif /* OS2 */
176
177 extern int nettype;
178
179 #ifndef NOICP                           /* Most of this file... */
180 #ifdef CKLOGDIAL
181 extern char diafil[];
182 #endif /* CKLOGDIAL */
183
184 #ifndef AMIGA
185 #ifndef MAC
186 #include <signal.h>
187 #endif /* MAC */
188 #endif /* AMIGA */
189
190 #ifdef SV68                             /* July 2006 believe it or not */
191 #ifndef SEEK_CUR
192 #include <unistd.h>
193 #endif  /* SEEK_CUR */
194 #endif  /* SV68 */
195
196 #ifdef SCO32                            /* June 2011 believe it or not... */
197 #ifdef XENIX
198 #ifndef SEEK_CUR
199 #include <unistd.h>
200 #endif  /* SEEK_CUR */
201 #endif  /* XENIX */
202 #endif  /* SCO32 */
203
204 #ifdef STRATUS                          /* Stratus Computer, Inc.  VOS */
205 #ifdef putchar
206 #undef putchar
207 #endif /* putchar */
208 #define putchar(x) conoc(x)
209 #ifdef getchar
210 #undef getchar
211 #endif /* getchar */
212 #define getchar(x) coninc(0)
213 #endif /* STRATUS */
214
215
216 #ifdef ANYX25
217 extern int revcall, closgr, cudata;
218 int x25ver;
219 extern char udata[];
220 #ifndef IBMX25
221 extern int npadx3;
222 extern CHAR padparms[];
223 extern struct keytab padx3tab[];
224 #endif /* !IBMX25 */
225 #ifdef IBMX25
226 /* global variables only available for IBM X.25 - possibly interesting for
227  * other implementations
228  */
229 extern x25addr_t local_nua;
230 extern x25addr_t remote_nua;
231 #endif /* IBMX25 */
232 #endif /* ANYX25 */
233
234 #ifdef NETCONN
235 #ifndef NODIAL
236 extern int nnetdir;
237 extern char *netdir[];
238 #endif /* NODIAL */
239 extern char ipaddr[];
240
241 #ifdef CK_NETBIOS
242 extern unsigned short netbiosAvail;
243 extern unsigned long NetbeuiAPI;
244 extern unsigned char NetBiosName[];
245 extern unsigned char NetBiosAdapter;
246 extern unsigned char NetBiosLSN;
247 #endif /* CK_NETBIOS */
248
249 #ifdef TCPSOCKET
250 extern char myipaddr[];
251 extern int tcp_rdns;
252 #ifdef CK_DNS_SRV
253 extern int tcp_dns_srv;
254 #endif /* CK_DNS_SRV */
255 extern char * tcp_address;
256 #ifndef NOHTTP
257 extern char * tcp_http_proxy;
258 #endif /* NOHTTP */
259 #ifdef NT
260 #ifdef CK_SOCKS
261 extern char * tcp_socks_svr;
262 #ifdef CK_SOCKS_NS
263 extern char * tcp_socks_ns;
264 #endif /* CK_SOCKS_NS */
265 #endif /* CK_SOCKS */
266 #endif /* NT */
267
268 #ifndef NOTCPOPTS
269 #ifdef SOL_SOCKET
270 #ifdef SO_LINGER
271 extern int tcp_linger;
272 extern int tcp_linger_tmo;
273 #endif /* SO_LINGER */
274 #ifdef SO_DONTROUTE
275 extern int tcp_dontroute;
276 #endif /* SO_DONTROUTE */
277 #ifdef TCP_NODELAY
278 extern int tcp_nodelay;
279 #endif /* TCP_NODELAY */
280 #ifdef SO_SNDBUF
281 extern int tcp_sendbuf;
282 #endif /* SO_SNDBUF */
283 #ifdef SO_RCVBUF
284 extern int tcp_recvbuf;
285 #endif /* SO_RCVBUF */
286 #ifdef SO_KEEPALIVE
287 extern int tcp_keepalive;
288 #endif /* SO_KEEPALIVE */
289 #endif /* SOL_SOCKET */
290 #endif /* NOTCPOPTS */
291 #endif /* TCPSOCKET */
292 #endif /* NETCONN */
293
294 extern char * floname[];
295
296 #ifndef NOSPL
297 extern int vareval;                     /* Variable evaluation method */
298 extern int fndiags;                     /* Function diagnostics on/off */
299 extern int divbyzero;
300 int itsapattern = 0;
301 int isinbuflen = 0;
302 int isjoin = 0;
303 #ifdef CK_APC
304 extern int apcactive;                   /* Nonzero = APC command was rec'd */
305 extern int apcstatus;                   /* Are APC commands being processed? */
306 #ifdef DCMDBUF
307 extern char *apcbuf;                    /* APC command buffer */
308 #else
309 extern char apcbuf[];
310 #endif /* DCMDBUF */
311 #endif /* CK_APC */
312
313 extern char evalbuf[];                  /* EVALUATE result */
314 extern char uidbuf[], pwbuf[], prmbuf[];
315 _PROTOTYP( static char * fneval, (char *, char * [], int, char * ) );
316 _PROTOTYP( static VOID myflsh, (void) );
317 _PROTOTYP( static char * getip, (char *) );
318 _PROTOTYP( int delta2sec, (char *, long *) );
319
320 #ifdef NEWFTP
321 _PROTOTYP( char * ftp_cpl_mode, (void) );
322 _PROTOTYP( char * ftp_dpl_mode, (void) );
323 _PROTOTYP( char * ftp_authtype, (void) );
324 #endif /* NEWFTP */
325
326 #ifndef NOHTTP
327 _PROTOTYP( char * http_host, (void) );
328 _PROTOTYP( int http_isconnected, (void) );
329 _PROTOTYP( char * http_security, (void) );
330 #endif /* NOHTTP */
331
332 #ifndef NOSEXP
333 _PROTOTYP( char * dosexp, (char *) );
334 int fsexpflag = 0;
335 #endif /* NOSEXP */
336
337 static char hexdigits[16] = {
338     '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
339 };
340 extern char * tempdir;
341
342 #ifdef CK_REXX
343 extern char rexxbuf[];
344 #endif /* CK_REXX */
345
346 extern int tfline[];
347
348 char *wkdays[] = {
349     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
350 };
351 #endif /* NOSPL */
352
353 #ifdef OS2
354 extern char startupdir[], inidir[];
355 #else
356 #ifdef VMSORUNIX
357 extern char startupdir[];
358 #endif /* VMSORUNIX */
359 #endif /* OS2 */
360
361 #ifdef OS2
362 _PROTOTYP (int os2getcp, (void) );
363 #ifdef TCPSOCKET
364 extern char tcpname[];
365 #endif /* TCPSOCKET */
366 extern int tcp_avail;
367 #ifdef DECNET
368 extern int dnet_avail;
369 #endif /* DECNET */
370 #ifdef SUPERLAT
371 extern int slat_avail;
372 #endif /* SUPERLAT */
373
374 #ifndef NOTERM
375 extern int tt_type, max_tt;
376 extern struct tt_info_rec tt_info[];
377 #endif /* NOTERM */
378 extern int tt_rows[], tt_cols[];
379 #else /* OS2 */
380 extern int tt_rows, tt_cols;
381 #endif /* OS2 */
382
383 #ifdef CK_TAPI
384 extern int tttapi;
385 extern int tapipass;
386 extern struct keytab * tapilinetab;
387 extern struct keytab * _tapilinetab;
388 extern int ntapiline;
389 #endif /* CK_TAPI */
390
391 extern struct keytab colxtab[];
392 extern int ncolx;
393
394 extern char ttname[], *zinptr, *kermrc;
395 extern char inidir[];
396
397 extern int activecmd, remonly, cmd_rows, cmd_cols, parity, seslog,
398   sessft, sosi, hwparity, tsecs, xargs, zincnt, tlevel, insilence, cmdmsk,
399   timint, timef, inbufsize, dialog, binary, carrier, cdtimo, cmask, duplex,
400   fmask, inecho, nmac, turnch, turn, kbchar;
401
402 #ifndef NOXFER
403 extern CHAR eol,  mypadc, mystch, padch, seol, stchr, * epktmsg, feol;
404 extern char *cksysid;
405 extern struct ck_p ptab[];
406 extern int
407   protocol, prefixing, xfrbel, xfrcan, xfrint, xfrchr, xfrnum, pktpaus,
408   lscapr, lscapu, xfermode, dest, slostart, maxrps, maxsps, maxtry, mypadn,
409   npad, pkttim, bigrbsiz, bigsbsiz, keep, atcapr, autopar, bctr, bctu,
410   crunched, ckdelay, ebq, ebqflg, pktlog, retrans, rpackets, rptflg, rptq,
411   rtimo, spackets, spsiz, spsizf, spsizr, timeouts, fncact, fncnv, urpsiz,
412   wmax, wslotn, wslotr, fdispla, spmax, fnrpath, fnspath, crc16;
413 #endif /* NOXFER */
414
415 #ifdef OS2
416 extern int zxpn;
417 extern int viewonly;
418 #endif /* OS2 */
419
420 #ifndef NOXFER
421 #ifdef GFTIMER
422 extern CKFLOAT fptsecs, fpxfsecs;
423 #endif /* GFTIMER */
424 extern long xfsecs, tfcps;
425
426 #ifdef CK_TMPDIR
427 extern char *dldir;
428 #endif /* CK_TMPDIR */
429 #endif /* NOXFER */
430
431 #ifdef RECURSIVE
432 extern int recursive;
433 #endif /* RECURSIVE */
434
435 #ifdef VMS
436 extern int frecl;
437 #endif /* VMS */
438
439 extern CK_OFF_T ffc, tfc, tlci, tlco;
440 extern long filcnt, rptn, speed,  ccu, ccp, vernum, xvernum;
441
442 #ifndef NOSPL
443 extern char fspec[], myhost[];
444 #endif /* NOSPL */
445
446 extern char *tfnam[];                   /* Command file names */
447
448 extern char pktfil[],                   /* Packet log file name */
449 #ifdef TLOG
450   trafil[],                             /* Transaction log file name */
451 #endif /* TLOG */
452   sesfil[];                             /* Session log file name */
453
454 #ifndef NOXMIT                          /* TRANSMIT command variables */
455 extern char xmitbuf[];
456 extern int xmitf, xmitl, xmitx, xmits, xmitw, xmitt;
457 #endif /* NOXMIT */
458
459 extern int cmdlvl;
460
461 #ifndef NOSPL
462 /* Script programming language items */
463 extern char **a_ptr[];                  /* Arrays */
464 extern int a_dim[];
465 static char * inpmatch = NULL;
466 #ifdef CKFLOAT
467 char * inpscale = NULL;
468 #endif  /* CKFLOAT */
469 extern char * inpbuf, inchar[];         /* Buffers for INPUT and REINPUT */
470 extern char *inpbp;                     /* And pointer to same */
471 static char *r3 = (char *)0;
472 extern int incount;                     /* INPUT character count */
473 extern int m_found;                     /* MINPUT result */
474 extern int maclvl;                      /* Macro invocation level */
475 extern struct mtab *mactab;             /* Macro table */
476 extern char *mrval[];
477 extern int macargc[], topargc;
478
479 #ifdef COMMENT
480 extern char *m_line[];
481 extern char *topline;
482 #endif /* COMMENT */
483
484 extern char *m_arg[MACLEVEL][10]; /* You have to put in the dimensions */
485 extern char *g_var[GVARS];        /* for external 2-dimensional arrays. */
486 #ifdef DCMDBUF
487 extern int *count, *inpcas;
488 #else
489 extern int count[], inpcas[];
490 #endif /* DCMDBUF */
491 #endif /* NOSPL */
492
493 #ifdef UNIX
494 extern int haslock;                     /* For UUCP locks */
495 extern char flfnam[];
496 #ifndef USETTYLOCK
497 extern char lock2[];
498 #endif /* USETTYLOCK */
499 #endif /* UNIX */
500
501 #ifdef OS2ORUNIX
502 extern int maxnam, maxpath;             /* Longest name, path length */
503 #endif /* OS2ORUNIX */
504
505 extern int mdmtyp, mdmsav;
506
507 #ifndef NODIAL
508 /* DIAL-related variables */
509 extern char modemmsg[];
510 extern MDMINF *modemp[];                /* Pointers to modem info structs */
511 extern int nmdm, dialhng, dialtmo, dialksp, dialdpy, dialsrt, dialsta;
512 extern int dialrtr, dialint, dialrstr, dialcon, dialcq, dialfld;
513 extern int mdmspd, dialec, dialdc, dialmth, dialmauto, dialesc;
514 extern char *dialnum,   *dialini,  *dialdir[], *dialcmd,  *dialnpr,
515  *dialdcon, *dialdcoff, *dialecon, *dialecoff, *dialhcmd, *diallac,
516  *dialhwfc, *dialswfc,  *dialnofc, *dialpulse, *dialtone, *dialname,
517  *dialaaon, *dialaaoff, *dialmac;
518 extern char *diallcc,   *dialixp,  *dialixs,   *dialldp,  *diallds,
519  *dialpxi,  *dialpxo,   *dialsfx,  *dialtfp;
520 extern char *diallcp,   *diallcs;
521 extern int ntollfree, ndialpxx, nlocalac;
522 extern char *dialtfc[], *diallcac[], *dialpxx[], *matchpxx;
523 extern int ndialpucc, ndialtocc;
524 extern char *dialtocc[], *dialpucc[];
525 extern int ndialdir, dialcnf, dialcvt, dialidt, dialpace;
526 extern long dialmax, dialcapas;
527
528 extern struct keytab mdmtab[];
529
530 #ifdef BIGBUFOK
531 #define ARGBUFSIZ 8191
532 #else
533 #define ARGBUFSIZ 1023
534 #endif /* BIGBUFOK */
535
536 #ifdef BIGBUFOK
537 extern char * dialmsg[];
538 #endif /* BIGBUFOK */
539
540 #endif /* NODIAL */
541
542 #ifndef NOCSETS
543 /* Translation stuff */
544 extern int nfilc;
545 extern struct keytab fcstab[];
546 extern int fcharset, tcharset, tslevel, language, nlng, tcsr, tcsl;
547 extern int dcset7, dcset8;
548 extern struct keytab lngtab[];
549 extern struct csinfo fcsinfo[], tcsinfo[];
550 extern struct langinfo langs[];
551 #ifdef CK_ANSIC
552 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Character set */
553 extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* translation functions */
554 #else
555 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); /* Character set */
556 extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(); /* translation functions. */
557 #endif /* CK_ANSIC */
558 #ifdef UNICODE
559     extern int ucsbom, ucsorder;
560 #endif /* UNICODE */
561 #endif /* NOCSETS */
562
563 #ifndef NOSPL
564 /* Built-in variable names, maximum length VNAML (20 characters) */
565
566 struct keytab vartab[] = {
567     { "_line",     VN_TFLN,  CM_INV},   /* 192 */
568 #ifdef OS2
569     { "_regname",  VN_REGN,  CM_INV},   /* 1.1.12 */
570     { "_regorg",   VN_REGO,  CM_INV},   /* 1.1.12 */
571     { "_regnum",   VN_REGS,  CM_INV},   /* 1.1.12 */
572 #endif /* OS2 */
573     { "apcactive", VN_APC,   CM_INV},   /* 192 */
574 #ifdef NT
575     { "appdata",   VN_APPDATA, 0},      /* 201 */
576 #endif /* NT */
577     { "argc",      VN_ARGC,  0},
578     { "args",      VN_ARGS,  0},
579     { "authname",  VN_AUTHN, 0},        /* 196 */
580     { "authstate", VN_AUTHS, 0},        /* 195 */
581     { "authtype",  VN_AUTHT, 0},        /* 195 */
582     { "bits",      VN_BITS,  0},        /* 212 */
583     { "blockcheck",VN_BLK,   0},        /* 195 */
584 #ifdef BROWSER
585     { "browser",   VN_BROWSR,0},        /* 193 */
586     { "browsopts", VN_BROPT, 0},        /* 193 */
587     { "browsurl",  VN_URL,   0},        /* 193 */
588     { "buildid",   VN_BUILD, 0},        /* 199 */
589 #endif /* BROWSER */
590     { "byteorder", VN_BYTE,  0},        /* 195 */
591 #ifndef NOCSETS
592     { "charset",   VN_CSET,  0},        /* 192 */
593 #endif /* NOCSETS */
594     { "cmdbufsize",VN_CMDBL, 0},        /* 195 */
595     { "cmdfile",   VN_CMDF,  0},
596     { "cmdlevel",  VN_CMDL,  0},
597     { "cmdsource", VN_CMDS,  0},
598     { "cols",      VN_COLS,  0},        /* 190 */
599 #ifdef NT
600     { "common",    VN_COMMON, 0},       /* 201 */
601 #endif /* NT */
602     { "connection",VN_CONN,  0},        /* 190 */
603     { "count",     VN_COUN,  0},
604 #ifndef NOXFER
605     { "cps",       VN_CPS,   0},        /* 190 */
606 #endif /* NOXFER */
607     { "cpu",       VN_CPU,   0},
608 #ifndef NOXFER
609     { "crc16",     VN_CRC16, 0},        /* 192 */
610     { "ctty",      VN_TTYNAM,0},        /* 196 */
611 #endif /* NOXFER */
612 #ifndef NOLOGDIAL
613 #ifndef NOLOCAL
614     { "cx_time",   VN_CXTIME,0},        /* 195 */
615     { "cx_status", VN_CX_STA,0},        /* 199 */
616 #endif /* NOLOCAL */
617 #endif /* NOLOGDIAL */
618 #ifndef NODIAL
619     { "d$ac",      VN_D_AC,  0},        /* 192 */
620     { "d$cc",      VN_D_CC,  0},        /* 192 */
621     { "d$ip",      VN_D_IP,  0},        /* 192 */
622     { "d$lc",      VN_D_LCP, 0},        /* 193 */
623     { "d$lcp",     VN_D_LCP, CM_INV},   /* 193 */
624     { "d$lp",      VN_D_LP,  0},        /* 192 */
625     { "d$px",      VN_D_PXX, 0},        /* 195 */
626     { "d$pxx",     VN_D_PXX, CM_INV},   /* 195 */
627 #endif /* NODIAL */
628     { "date",      VN_DATE,  0},
629     { "day",       VN_DAY,   0},
630 #ifdef NT
631     { "desktop",   VN_DESKTOP, 0},     /* 201 */
632 #endif /* NT */
633 #ifndef NODIAL
634     { "dialcount", VN_DRTR,  0},        /* 195 */
635     { "dialmessage",VN_DMSG, 0},        /* 212 */
636     { "dialnumber",VN_DNUM,  0},        /* 192 */
637     { "dialresult",VN_MDMSG, 0},        /* 192 */
638     { "dialstatus",VN_DIAL,  0},        /* 190 */
639     { "dialsuffix",VN_PDSFX, 0},        /* 193 */
640     { "dialtype",  VN_DTYPE, 0},        /* 193 */
641 #endif /* NODIAL */
642     { "directory", VN_DIRE,  0},
643 #ifndef NODIAL
644     { "dm_hf",     VN_DM_HF, 0},        /* 199 */
645     { "dm_lp",     VN_DM_LP, 0},        /* 195 */
646     { "dm_sp",     VN_DM_SP, 0},        /* 195 */
647     { "dm_pd",     VN_DM_PD, 0},        /* 195 */
648     { "dm_td",     VN_DM_TD, 0},        /* 195 */
649     { "dm_wa",     VN_DM_WA, 0},        /* 195 */
650     { "dm_wb",     VN_DM_WB, 0},        /* 199 */
651     { "dm_wd",     VN_DM_WD, 0},        /* 195 */
652     { "dm_rc",     VN_DM_RC, 0},        /* 195 */
653 #endif /* NODIAL */
654 #ifndef NOXFER
655     { "download",  VN_DLDIR, 0},        /* 192 */
656 #endif /* NOXFER */
657     { "editor",    VN_EDITOR,0},
658     { "editfile",  VN_EDFILE,0},
659     { "editopts",  VN_EDOPT, 0},
660     { "errno",     VN_ERRNO, 0},        /* 192 */
661     { "errstring", VN_ERSTR, 0},        /* 192 */
662     { "escape",    VN_ESC,   0},        /* 193 */
663     { "evaluate",  VN_EVAL,  0},        /* 190 */
664 #ifdef OS2ORUNIX
665     { "exedir",    VN_EXEDIR,0},        /* 192 */
666 #endif /* OS2ORUNIX */
667     { "exitstatus",VN_EXIT,  0},
668 #ifdef CKCHANNELIO
669     { "f_count",   VN_FCOU,  0},        /* 195 */
670     { "f_error",   VN_FERR,  0},        /* 195 */
671     { "f_max",     VN_FMAX,  0},        /* 195 */
672     { "fileerror", VN_FERR,  CM_INV},   /* 195 */
673     { "filemax",   VN_FERR,  CM_INV},   /* 195 */
674 #endif /* CKCHANNELIO */
675     { "filename",  VN_FNAM,  0},        /* 193 */
676     { "filenumber",VN_FNUM,  0},        /* 193 */
677     { "filespec",  VN_FILE,  0},
678     { "fsize",     VN_FFC,   0},        /* 190 */
679 #ifdef GFTIMER
680     { "ftime",     VN_FTIME, 0},        /* 199 */
681 #else
682     { "ftime",     VN_NTIM,  CM_INV},
683 #endif /* GFTIMER */
684 #ifndef NOFTP
685 #ifndef SYSFTP
686     { "ftp_code",         VN_FTP_C, 0}, /* 199 */
687     { "ftp_cpl",          VN_FTP_B, 0}, /* 199 */
688     { "ftp_connected",    VN_FTP_X, 0}, /* 199 */
689     { "ftp_dpl",          VN_FTP_D, 0}, /* 199 */
690     { "ftp_getputremote", VN_FTP_G, 0}, /* 199 */
691     { "ftp_host",         VN_FTP_H, 0}, /* 199 */
692     { "ftp_loggedin",     VN_FTP_L, 0}, /* 199 */
693     { "ftp_message",      VN_FTP_M, 0}, /* 199 */
694     { "ftp_msg",          VN_FTP_M, CM_INV}, /* 199 */
695     { "ftp_security",     VN_FTP_Z, 0}, /* 199 */
696     { "ftp_server",       VN_FTP_S, 0}, /* 199 */
697 #endif /* SYSFTP */
698 #endif /* NOFTP */
699     { "ftype",     VN_MODE,  0},        /* 190 */
700 #ifdef KUI
701     { "gui_fontname", VN_GUI_FNM, 0},   /* 205 */
702     { "gui_fontsize", VN_GUI_FSZ, 0},   /* 205 */
703     { "gui_runmode", VN_GUI_RUN, 0},    /* 205 */
704     { "gui_xpos",    VN_GUI_XP,  0},    /* 205 */
705     { "gui_xres",    VN_GUI_XR,  0},    /* 205 */
706     { "gui_ypos",    VN_GUI_YP,  0},    /* 205 */
707     { "gui_yres",    VN_GUI_YR,  0},    /* 205 */
708 #endif /* KUI */
709     { "herald",    VN_HERALD, 0},
710     { "home",      VN_HOME,   0},
711     { "host",      VN_HOST,   0},
712     { "hour",      VN_HOUR,   0},       /* 200 */
713 #ifndef NOHTTP
714     { "http_code",      VN_HTTP_C, 0},  /* 199 */
715     { "http_connected", VN_HTTP_N, 0},  /* 199 */
716     { "http_host",      VN_HTTP_H, 0},  /* 199 */
717     { "http_message",   VN_HTTP_M, 0},  /* 199 */
718     { "http_security",  VN_HTTP_S, 0},  /* 199 */
719 #endif /* NOHTTP */
720     { "hwparity",  VN_HWPAR, 0},        /* 195 */
721     { "input",     VN_IBUF,  0},
722     { "inchar",    VN_ICHR,  0},
723     { "incount",   VN_ICNT,  0},
724     { "inidir",    VN_INI,   0},        /* 192 */
725     { "inmatch",   VN_MATCH, 0},        /* 196 */
726     { "inmessage", VN_INPMSG,0},        /* 212 */
727     { "inscale",   VN_ISCALE,0},        /* 210 */
728     { "instatus",  VN_ISTAT, 0},        /* 192 */
729     { "intime",    VN_INTIME,0},        /* 193 */
730     { "inwait",    VN_INTMO, 0},        /* 195 */
731     { "ip",        VN_IPADDR, CM_ABR|CM_INV},
732     { "ipaddress", VN_IPADDR,0},        /* 192 */
733     { "iprompt",   VN_PROMPT,0},        /* 199 */
734     { "kbchar",    VN_KBCHAR,0},        /* 196 */
735 #ifndef NOLOCAL
736 #ifdef OS2
737     { "keyboard",  VN_KEYB,  0},
738 #endif /* OS2 */
739 #endif /* NOLOCAL */
740 #ifdef CK_KERBEROS
741     { "krb4errmsg",    VN_K4EMSG,0},
742     { "krb4errno",     VN_K4ENO, 0},
743     { "krb4principal", VN_K4PRN, 0},
744     { "krb4realm",     VN_K4RLM, 0},
745     { "krb4service",   VN_K4SRV, 0},
746     { "krb5cc",        VN_K5CC,  0},
747     { "krb5errmsg",    VN_K5EMSG,0},
748     { "krb5errno",     VN_K5ENO, 0},
749     { "krb5principal", VN_K5PRN, 0},
750     { "krb5realm",     VN_K5RLM, 0},
751     { "krb5service",   VN_K5SRV, 0},
752 #endif /* CK_KERBEROS */
753     { "lastcommand",   VN_PREVCMD, 0},  /* 299 */
754 #ifndef NOLASTFILE
755     { "lastfilespec",  VN_LASTFIL, 0},  /* 212 */
756 #endif  /* NOLASTFILE */
757     { "lastkeywordvalue",  VN_LASTKWV, 0}, /* 212 */
758     { "lastkwvalue",   VN_LASTKWV, CM_ABR|CM_INV}, /* 212 */
759     { "line",          VN_LINE,  0},
760     { "local",         VN_LCL,   0},
761 #ifdef UNIX
762     { "lockdir",       VN_LCKDIR,0},    /* 195 */
763     { "lockpid",       VN_LCKPID,0},    /* 195 */
764 #endif /* UNIX */
765     { "log_connection", VN_LOG_CON, 0}, /* 206 */
766     { "log_debug", VN_LOG_DEB, 0},      /* 206 */
767     { "log_packet", VN_LOG_PKT, 0},     /* 206 */
768     { "log_session", VN_LOG_SES, 0},    /* 206 */
769     { "log_transaction", VN_LOG_TRA, 0},/* 206 */
770     { "maclevel",  VN_MACLVL,0},        /* 195 */
771     { "macro",     VN_MAC,   0},
772 #ifdef FNFLOAT
773     { "math_e",    VN_MA_E,  0},        /* 195 */
774     { "math_pi",   VN_MA_PI, 0},        /* 195 */
775     { "math_precision", VN_MA_PR, 0},   /* 195 */
776 #endif /* FNFLOAT */
777     { "minput",    VN_MINP,  0},        /* 192 */
778     { "model",     VN_MODL,  0},        /* 193 */
779     { "modem",     VN_MDM,   0},
780 #ifndef NOLOCAL
781 #ifdef OS2
782     { "mousecurx", VN_MOU_X, 0},        /* K95 1.1.14 */
783     { "mousecury", VN_MOU_Y, 0},        /* K95 1.1.14 */
784 #endif /* OS2 */
785 #endif /* NOLOCAL */
786 #ifndef NODIAL
787     { "m_aa_off",  VN_M_AAX, 0},        /* all 192... */
788     { "m_aa_on",   VN_M_AAO, 0},
789     { "m_dc_off",  VN_M_DCX, 0},
790     { "m_dc_on",   VN_M_DCO, 0},
791     { "m_dial",    VN_M_DCM, 0},
792     { "m_ec_off",  VN_M_ECX, 0},
793     { "m_ec_on",   VN_M_ECO, 0},
794     { "m_fc_hw",   VN_M_HWF, 0},
795     { "m_fc_no",   VN_M_NFC, 0},
796     { "m_fc_sw",   VN_M_SWF, 0},
797     { "m_hup",     VN_M_HUP, 0},
798     { "m_init",    VN_M_INI, 0},
799     { "m_name",    VN_M_NAM, 0},        /* 195 */
800     { "m_pulse",   VN_M_PDM, 0},
801     { "m_sig_cd",  VN_MS_CD, 0},        /* 195 */
802     { "m_sig_cts", VN_MS_CTS,0},        /* 195 */
803     { "m_sig_dsr", VN_MS_DSR,0},        /* 195 */
804     { "m_sig_dtr", VN_MS_DTR,0},        /* 195 */
805     { "m_sig_ri",  VN_MS_RI, 0},        /* 195 */
806     { "m_sig_rts", VN_MS_RTS,0},        /* 195 */
807     { "m_tone",    VN_M_TDM, 0},
808 #endif /* NODIAL */
809     { "name",      VN_NAME,  0},
810     { "ndate",     VN_NDAT,  0},
811     { "nday",      VN_NDAY,  0},
812     { "newline",   VN_NEWL,  0},
813     { "ntime",     VN_NTIM,  0},
814     { "osname",    VN_OSNAM, 0},        /* 193 */
815     { "osrelease", VN_OSREL, 0},        /* 193 */
816     { "osversion", VN_OSVER, 0},        /* 193 */
817 #ifndef NOXFER
818     { "packetlen", VN_RPSIZ, 0},        /* 192 */
819 #endif /* NOXFER */
820     { "parity",    VN_PRTY,  0},        /* 190 */
821     { "password",  VN_PWD,   CM_INV},   /* 192 */
822 #ifdef NT
823     { "personal",  VN_PERSONAL, 0},     /* 201 */
824 #endif /* NT */
825 #ifdef PEXITSTAT
826     { "pexitstat", VN_PEXIT, 0},        /* 193 */
827 #endif /* PEXITSTAT */
828 #ifdef CK_PID
829     { "pid",       VN_PID,   0},        /* 193 */
830 #endif /* CK_PID */
831     { "platform",  VN_SYSV,  0},
832     { "printer",   VN_PRINT, 0},        /* 193 */
833     { "program",   VN_PROG,  0},
834     { "prompt",    VN_PRM,   CM_INV},   /* 192 */
835 #ifndef NOXFER
836     { "protocol",  VN_PROTO, 0},        /* 192 */
837     { "p_8bit",    VN_P_8BIT,0},        /* 193 */
838     { "p_ctl",     VN_P_CTL, 0},        /* 193 */
839     { "p_rpt",     VN_P_RPT, 0},        /* 193 */
840     { "query",     VN_QUE,   0},        /* 190 */
841 #endif /* NOXFER */
842     { "remoteip",  VN_HOSTIP,0},        /* 212 */
843     { "return",    VN_RET,   0},
844 #ifdef CK_REXX
845     { "rexx",      VN_REXX,  0},        /* 190 */
846 #endif /* CK_REXX */
847 #ifdef TN_COMPORT
848     { "rfc2217_signature", VN_TNC_SIG, 0}, /* 201 */
849     { "rfc2717_signature", VN_TNC_SIG, CM_INV}, /* 202 */
850 #endif /* TN_COMPORT */
851     { "rows",      VN_ROWS,  0},        /* 190 */
852 #ifndef NOSEXP
853     { "sdepth",    VN_LSEXP,0},         /* 199 */
854 #endif /* NOSEXP */
855     { "secure",    VN_SECURE, 0},       /* 199 */
856 #ifndef NOLOCAL
857 #ifdef OS2
858     { "select",    VN_SELCT, 0},        /* 192 */
859 #endif /* OS2 */
860 #endif /* NOLOCAL */
861     { "sendlist",  VN_SNDL,  0},
862     { "serial",    VN_SERIAL,0},        /* 195 */
863     { "setlinemsg",VN_SLMSG, 0},        /* 195 */
864 #ifndef NOSEXP
865     { "sexpression",VN_SEXP, 0},        /* 199 */
866 #endif /* NOSEXP */
867     { "speed",     VN_SPEE,  0},
868 #ifdef OS2
869     { "space",     VN_SPA,   0},
870     { "startup",   VN_STAR,  0},        /* 190 */
871 #else
872 #ifdef UNIX
873     { "startup",   VN_STAR,  0},        /* 193 */
874 #else
875 #ifdef VMS
876     { "startup",   VN_STAR,  0},        /* 193 */
877 #endif /* VMS */
878 #endif /* UNIX */
879 #endif /* OS2 */
880     { "status",    VN_SUCC,  0},
881 #ifndef NOSEXP
882     { "svalue",    VN_VSEXP, 0},        /* 199 */
883 #endif /* NOSEXP */
884 #ifndef NOXFER
885     { "sysid",     VN_SYSI,  0},
886 #endif /* NOXFER */
887     { "system",    VN_SYST,  0},
888     { "terminal",  VN_TTYP,  0},
889 #ifdef OS2
890 #ifndef NOKVERBS
891     { "termkey",   VN_TRMK,  CM_INV},   /* 192 */
892 #endif /* NOKVERBS */
893 #endif /* OS2 */
894     { "test",      VN_TEST,  0},        /* 193 */
895     { "textdir",   VN_TXTDIR,0},        /* 195 */
896 #ifndef NOXFER
897     { "tfsize",    VN_TFC,   0},
898     { "tftime",    VN_TFTIM, 0},        /* 195 */
899 #endif /* NOXFER */
900     { "time",      VN_TIME,  0},
901     { "timestamp", VN_NOW,   0},        /* 200 */
902     { "tmpdir",    VN_TEMP,  0},        /* 192 */
903 #ifdef CK_TRIGGER
904     { "trigger",   VN_TRIG,  0},        /* 193 */
905 #endif /* CK_TRIGGER */
906 #ifdef CK_TTYFD
907     { "ttyfd",     VN_TTYF,  0},
908 #endif /* CK_TTYFD */
909     { "ty_ln",     VN_TY_LN, 0},        /* 195 */
910     { "ty_lc",     VN_TY_LC, 0},        /* 195 */
911     { "ty_lm",     VN_TY_LM, 0},        /* 195 */
912 #ifdef BROWSER
913     { "url",       VN_URL,   CM_INV},   /* 193 */
914 #endif /* BROWSER */
915     { "userid",    VN_UID,   0},        /* 192 */
916     { "vareval",   VN_VAREVAL, 0},      /* 212 */
917     { "version",   VN_VERS,  0},
918 #ifndef NOXFER
919     { "window",    VN_WINDO, 0},        /* 192 */
920 #endif /* NOXFER */
921 #ifdef IBMX25
922     { "x25local_nua", VN_X25LA, 0},     /* 193 */
923     { "x25remote_nua", VN_X25RA, 0},    /* 193 */
924 #endif /* IBMX25 */
925 #ifdef CK_SSL
926     { "x509_issuer",  VN_X509_I, 0},
927     { "x509_subject", VN_X509_S, 0},
928 #endif /* CK_SSL */
929 #ifndef NOXFER
930     { "xferstatus",VN_XFSTAT,0},        /* 193 */
931     { "xfermsg",   VN_XFMSG, 0},        /* 193 */
932     { "xfer_badpackets", VN_XF_BC, 0},  /* 195 */
933     { "xfer_timeouts",   VN_XF_TM, 0},  /* 195 */
934     { "xfer_retransmits",VN_XF_RX, 0},  /* 195 */
935 #endif /* NOXFER */
936     { "xprogram",  VN_XPROG, 0},        /* 193 */
937     { "xversion",  VN_XVNUM, 0}         /* 192 */
938 };
939 int nvars = (sizeof(vartab) / sizeof(struct keytab));
940 #endif /* NOSPL */
941
942 #ifndef NOSPL
943 struct keytab fnctab[] = {              /* Function names */
944 #ifdef OS2
945     { ".oox",       FN_OOX, CM_INV},    /* ... */
946 #endif /* OS2 */
947
948 #ifdef CKCHANNELIO
949     { "_eof",       FN_FEOF,   0},
950     { "_errmsg",    FN_FERMSG, 0},
951     { "_getblock",  FN_FGBLK,  0},
952     { "_getchar",   FN_FGCHAR, 0},
953     { "_getline",   FN_FGLINE, 0},
954     { "_handle",    FN_FILNO,  0},
955     { "_line",      FN_NLINE,  0},
956     { "_pos",       FN_FPOS,   0},
957     { "_putblock",  FN_FPBLK,  0},
958     { "_putchar",   FN_FPCHAR, 0},
959     { "_putline",   FN_FPLINE, 0},
960     { "_status",    FN_FSTAT,  0},
961 #endif /* CKCHANNELIO */
962
963     { "aaconvert",  FN_AADUMP, 0},      /* Associative Array conversion */
964     { "absolute",   FN_ABS,  0},        /* Absolute value */
965 #ifdef TCPSOCKET
966     { "addr2name",  FN_HSTADD,CM_INV},  /* IP Address to Hostname */
967     { "addrtoname", FN_HSTADD,CM_INV},  /* IP Address to Hostname */
968 #endif /* TCPSOCKET */
969     { "arraylook",  FN_ALOOK,0},        /* Array lookup */
970     { "b64decode",  FN_FMB64,0},        /* Base-64 conversion */
971     { "b64encode",  FN_TOB64,0},        /* ... */
972     { "basename",   FN_BSN,  0},        /* Basename */
973     { "break",      FN_BRK,  0},        /* Break (as in Snobol) */
974     { "ca",         FN_CAP,  CM_INV|CM_ABR}, /* Abbreviation for capitablize */
975     { "cap",        FN_CAP,  CM_INV|CM_ABR}, /* Abbreviation for capitablize */
976     { "capitalize", FN_CAP,  0},        /* First Letter -> uppercase */
977     { "caps",       FN_CAP,  CM_INV},   /* ditto */
978     { "character",  FN_CHR,  0},        /* Character from code */
979     { "checksum",   FN_CHK,  0},        /* Checksum */
980     { "cmdstack",   FN_CMDSTK,0},       /* Command stack */
981     { "cmpdates",   FN_CMPDATE,0},      /* Compare dates */
982     { "code",       FN_COD,  0},        /* Code from character */
983 #ifndef NOPUSH
984     { "command",    FN_CMD,  0},        /* Output from a command */
985 #endif /* NOPUSH */
986     { "contents",   FN_CON,  0},        /* Definition (contents) of variable */
987     { "count",      FN_COUNT, 0},       /* Occurrences of string in string */
988     { "crc16",      FN_CRC,  0},        /* CRC-16 */
989 #ifdef OS2
990     { "crypt",      FN_CRY, CM_INV},
991 #endif /* OS2 */
992     { "cvtcset",    FN_XLATE, 0},       /* Convert character set */
993     { "cvtdate",    FN_DTIM, 0},        /* Convert free date/time to std */
994 #ifdef ZFCDAT
995     { "date",       FN_FD,   0},        /* File modification/creation date */
996 #endif /* ZFCDAT */
997     { "day",        FN_DAY,  0},        /* Day of week */
998     { "dayofyear",  FN_JDATE,0},        /* Date to Day of Year */
999     { "decodehex",  FN_UNPCT, 0},       /* Decode string with hex escapes */
1000     { "definition", FN_DEF,  0},        /* Return definition of given macro */
1001     { "delta2secs", FN_DELSEC, 0},      /* Delta time to seconds */
1002     { "deltatosecs", FN_DELSEC, CM_INV}, /* Delta time to seconds */
1003 #ifndef NODIAL
1004     { "dialconvert",FN_PNCVT,0},        /* Convert portable phone number */
1005 #endif /* NODIAL */
1006     { "diffdates",  FN_DIFDATE,0},      /* Difference of two date-times */
1007     { "dimension",  FN_DIM,  0},        /* Dimension of array */
1008     { "dir",        FN_DIR,  CM_INV|CM_ABR}, /* Abbreviation for direct.. */
1009     { "dire",       FN_DIR,  CM_INV|CM_ABR}, /* Abbreviation for direct.. */
1010     { "direc",      FN_DIR,  CM_INV|CM_ABR}, /* Abbreviation for direct.. */
1011     { "direct",     FN_DIR,  CM_INV|CM_ABR}, /* Abbreviation for direct.. */
1012     { "directo",    FN_DIR,  CM_INV|CM_ABR}, /* Abbreviation for direct.. */
1013     { "director",   FN_DIR,  CM_INV|CM_ABR}, /* Abbreviation for direct.. */
1014     { "directories",FN_DIR,  0},        /* List of directories */
1015     { "directory",  FN_DIR,  CM_INV},   /* List of directories */
1016     { "dirname",    FN_DNAM, 0},        /* Directory part of filename */
1017     { "dos2unixpath",FN_PC_DU, },       /* DOS to UNIX path */
1018     { "dostounixpath",FN_PC_DU, CM_INV}, /* DOS to UNIX path */
1019     { "doy",        FN_JDATE,CM_INV},   /* Date to Day of Year */
1020     { "doy2date",   FN_DATEJ,0},        /* Day of Year to date */
1021     { "doytodate",  FN_DATEJ,CM_INV},   /* Day of Year to date */
1022     { "emailaddress",FN_EMAIL, 0},      /* Email address */
1023 #ifdef FN_ERRMSG
1024     { "errstring",  FN_ERRMSG,0},       /* Error code to message */
1025 #endif /* FN_ERRMSG */
1026     { "evaluate",   FN_EVA,  0},        /* Evaluate given arith expression */
1027     { "execute",    FN_EXE,  0},        /* Execute given macro */
1028     { "files",      FN_FC,   0},        /* File count */
1029 #ifdef FNFLOAT
1030     { "fpabsolute", FN_FPABS, 0},       /* Floating-point absolute value */
1031     { "fpadd",      FN_FPADD, 0},       /* FP add */
1032     { "fpcosine",   FN_FPCOS, 0},       /* FP cosine */
1033     { "fpdivide",   FN_FPDIV, 0},       /* FP divide */
1034     { "fpexp",      FN_FPEXP, 0},       /* FP e to the x */
1035     { "fpint",      FN_FPINT, 0},       /* FP to integer */
1036     { "fplog10",    FN_FPLOG, 0},       /* FP base-10 logarithm */
1037     { "fplogn",     FN_FPLN,  0},       /* FP natural logarithm */
1038     { "fpmaximum",  FN_FPMAX, 0},       /* FP maxinum */
1039     { "fpminimum",  FN_FPMIN, 0},       /* FP mininum */
1040     { "fpmodulus",  FN_FPMOD, 0},       /* FP modulus */
1041     { "fpmultiply", FN_FPMUL, 0},       /* FP multiply */
1042     { "fpraise",    FN_FPPOW, 0},       /* FP raise to a power */
1043     { "fpround",    FN_FPROU, 0},       /* FP round */
1044     { "fpsine",     FN_FPSIN, 0},       /* FP sine */
1045     { "fpsqrt",     FN_FPSQR, 0},       /* FP square root */
1046     { "fpsubtract", FN_FPSUB, 0},       /* FP subtract */
1047     { "fptangent",  FN_FPTAN, 0},       /* FP tangent */
1048 #endif /* FNFLOAT */
1049     { "function",   FN_FUNC, 0 },       /* Test for existence of a function */
1050     { "getpidinfo", FN_PID, 0  },       /* Get PID info */
1051     { "hex2ip",     FN_HEX2IP,0},       /* Hex to IP address */
1052     { "hextoip",    FN_HEX2IP,CM_INV},  /* Hex to IP address */
1053     { "hex2n",      FN_HEX2N, CM_INV},  /* Hex to decimal number */
1054     { "hexify",     FN_HEX,   0},       /* Hexify (string) */
1055     { "index",      FN_IND,   0},       /* Index (string search) */
1056     { "ip2hex",     FN_IP2HEX,0},       /* IP address to hex */
1057     { "iptohex",    FN_IP2HEX,CM_INV},  /* IP address to hex */
1058     { "ipaddress",  FN_IPA,   0},       /* Find and return IP address */
1059     { "jdate",      FN_JDATE, CM_INV},  /* Date to Day of Year */
1060     { "join",       FN_JOIN,  0},       /* Join array elements */
1061     { "keywordvalue",  FN_KWVAL, 0},    /* Keyword=Value */
1062 #ifdef CK_KERBEROS
1063     { "krbflags",      FN_KRB_FG, 0},   /* Kerberos functions */
1064     { "krbisvalid",    FN_KRB_IV, 0},
1065     { "krbnextticket", FN_KRB_NX, 0},
1066     { "krbtickets",    FN_KRB_TK, 0},
1067     { "krbtimeleft",   FN_KRB_TT, 0},
1068 #endif /* CK_KERBEROS */
1069     { "kwvalue",    FN_KWVAL, CM_INV},  /* Keyword=Value */
1070     { "left",       FN_LEF,  0},        /* Leftmost n characters of string */
1071     { "length",     FN_LEN,  0},        /* Return length of argument */
1072     { "literal",    FN_LIT,  0},        /* Return argument literally */
1073 #ifdef NT
1074     { "longpathname",FN_LNAME,0},       /* GetLongPathName() */
1075 #else
1076     { "longpathname",FN_FFN,CM_INV},
1077 #endif /* NT */
1078     { "lop",        FN_STL,  0},        /* Lop */
1079     { "lopx",       FN_LOPX, 0},        /* Lopx */
1080     { "lower",      FN_LOW,  0},        /* Return lowercased argument */
1081     { "lpad",       FN_LPA,  0},        /* Return left-padded argument */
1082     { "ltrim",      FN_LTR,  0},        /* Left-Trim */
1083     { "maximum",    FN_MAX,  0},        /* Return maximum of two arguments */
1084     { "minimum",    FN_MIN,  0},        /* Return minimum of two arguments */
1085     { "mjd",        FN_MJD,  0},        /* Date to Modified Julian Date */
1086     { "mjd2date",   FN_MJD2, 0},        /* MJD to Date */
1087     { "mjdtodate",  FN_MJD2, CM_INV},   /* MJD to Date */
1088     { "modulus",    FN_MOD,  0},        /* Return modulus of two arguments */
1089 #ifdef COMMENT
1090     { "msleep",     FN_MSLEEP,0},       /* Sleep for n milliseconds */
1091 #endif /* COMMENT */
1092     { "n2hex",      FN_2HEX, CM_INV},   /* Number to hex */
1093     { "n2octal",    FN_2OCT, CM_INV},   /* Number to octal */
1094     { "n2time",     FN_N2TIM,0},        /* Number to hh:mm:ss */
1095 #ifdef TCPSOCKET
1096     { "name2addr",  FN_HSTNAM,CM_INV},  /* Hostname to IP Address */
1097 #endif /* TCPSOCKET */
1098     { "nday",       FN_NDAY, 0},        /* Numeric day of week */
1099     { "nextfile",   FN_FIL,  0},        /* Next file in list */
1100     { "ntime",      FN_NTIM, 0},        /* Time to seconds since midnight */
1101     { "ntohex",     FN_2HEX, CM_INV},   /* Number to hex */
1102     { "ntooctal",   FN_2OCT, CM_INV},   /* Number to octal */
1103     { "ntotime",    FN_N2TIM,CM_INV},   /* Number to hh:mm:ss */
1104     { "oct2n",      FN_OCT2N,CM_INV},   /* Octal to decimal number */
1105     { "octton",     FN_OCT2N,CM_INV},   /* Octal to decimal number */
1106     { "pathname",   FN_FFN,  0},        /* Full file name */
1107     { "pattern",    FN_PATTERN, 0},     /* Pattern (for INPUT) */
1108 #ifdef CK_PERMS
1109     { "permissions",FN_PERM, 0},        /* Permissions of file */
1110 #else
1111     { "permissions",FN_PERM, CM_INV},   /* Permissions of file */
1112 #endif /* CK_PERMS */
1113 #ifdef SEEK_CUR
1114     { "pictureinfo",FN_PICTURE, 0 },    /* Picture orientation/dimensions */
1115 #endif  /* SEEK_CUR */
1116     { "radix",      FN_RADIX, 0 },      /* Radix conversion */
1117 #ifndef NORANDOM
1118     { "random",     FN_RAND, 0},        /* Random number */
1119 #endif /* NORANDOM */
1120 #ifndef NOPUSH
1121     { "rawcommand", FN_RAW,  0},        /* Output from a command (raw) */
1122 #endif /* NOPUSH */
1123 #ifdef RECURSIVE
1124     { "rdirectories", FN_RDIR, 0},      /* Recursive directory list */
1125 #endif /* RECURSIVE */
1126     { "recurse",    FN_RECURSE, 0},     /* Recursive variable evaluation */
1127 #ifdef RECURSIVE
1128     { "rfiles",       FN_RFIL, 0},      /* Recursive file list */
1129 #endif /* RECURSIVE */
1130     { "rep",        FN_REP, CM_INV|CM_ABR},
1131     { "repeat",     FN_REP,  0},        /* Repeat argument given # of times */
1132     { "replace",    FN_RPL,  0},        /* Replace characters in string */
1133     { "reverse",    FN_REV,  0},        /* Reverse the argument string */
1134     { "right",      FN_RIG,  0},        /* Rightmost n characters of string */
1135     { "rindex",     FN_RIX,  0},        /* Right index */
1136     { "rpad",       FN_RPA,  0},        /* Right-pad the argument */
1137     { "rsearch",    FN_RSEARCH, 0},     /* R-L Search for pattern in string */
1138 #ifdef OS2
1139     { "scrncurx",   FN_SCRN_CX,  0},    /* Screen Cursor X Pos */
1140     { "scrncury",   FN_SCRN_CY,  0},    /* Screen Cursor Y Pos */
1141     { "scrnstr",    FN_SCRN_STR, 0},    /* Screen String */
1142 #endif /* OS2 */
1143     { "search",     FN_SEARCH, 0},      /* L-R Search for pattern in string */
1144 #ifndef NOSEXP
1145     { "sexpression",FN_SEXP, 0},        /* S-Expression */
1146 #endif /* NOSEXP */
1147 #ifdef NT
1148     { "shortpathname",FN_SNAME,0},      /* GetShortPathName() */
1149 #else
1150     { "shortpathname",FN_FFN,CM_INV},
1151 #endif /* NT */
1152     { "size",       FN_FS,   0},        /* File size */
1153 #ifdef COMMENT
1154     { "sleep",      FN_SLEEP,0},        /* Sleep for n seconds */
1155 #endif /* COMMENT */
1156     { "span",       FN_SPN,  0},        /* Span - like Snobol */
1157     { "split",      FN_SPLIT,0},        /* Split string into words */
1158     { "squeeze",    FN_SQUEEZE,0},      /* Squeeze whitespace in string */
1159     { "strcmp",     FN_STRCMP,0},       /* String comparison */
1160     { "stringtype", FN_STRINGT,0},      /* String type (7-bit, 8-bit, UTF-8) */
1161     { "stripb",     FN_STB,  0},        /* Strip enclosing braces/brackets */
1162     { "stripn",     FN_STN,  0},        /* Strip n chars */
1163     { "stripx",     FN_STX,  0},        /* Strip suffix */
1164     { "su",         FN_SUB,  CM_INV|CM_ABR},
1165     { "sub",        FN_SUB,  CM_INV|CM_ABR},
1166     { "subs",       FN_SUB,  CM_INV|CM_ABR},
1167     { "subst",      FN_SUB,  CM_INV|CM_ABR},
1168     { "substitute", FN_SUBST,0},        /* Substitute chars */
1169     { "substring",  FN_SUB,  0},        /* Extract substring from argument */
1170     { "tablelook",  FN_TLOOK,0},        /* Table lookup */
1171     { "time",       FN_TIME, 0},        /* Free-format time to hh:mm:ss */
1172     { "tod2secs",   FN_NTIM, CM_INV},   /* Time-of-day-to-secs-since-midnite */
1173     { "todtosecs",  FN_NTIM, CM_INV},   /* Time-of-day-to-secs-since-midnite */
1174     { "trim",       FN_TRM,  0},        /* Trim */
1175     { "unhexify",   FN_UNH,  0},        /* Unhexify */
1176     { "unix2dospath",FN_PC_UD, 0},      /* UNIX to DOS path */
1177     { "unixtodospath",FN_PC_UD, CM_INV}, /* UNIX to DOS path */
1178     { "untabify",   FN_UNTAB,0},        /* Untabify */
1179     { "upper",      FN_UPP,  0},        /* Return uppercased argument */
1180     { "utcdate",    FN_TOGMT,0},        /* Date-time to UTC (GMT) */
1181     { "verify",     FN_VER,  0},        /* Verify */
1182     { "word",       FN_WORD, 0},        /* Extract a word */
1183     { "", 0, 0}
1184 };
1185 int nfuncs = (sizeof(fnctab) / sizeof(struct keytab)) - 1;
1186 #endif /* NOSPL */
1187
1188 #ifndef NOSPL                           /* Buffer for expansion of */
1189 #ifdef BIGBUFOK                         /* built-in variables. */
1190 #define VVBUFL 1024
1191 #else
1192 #define VVBUFL 256
1193 #endif /* BIGBUFOK */
1194 char vvbuf[VVBUFL+1];
1195 #endif /* NOSPL */
1196
1197 struct keytab disptb[] = {              /* Log file disposition */
1198     { "append",    1,  0},
1199     { "new",       0,  0}
1200 };
1201
1202 #ifdef CKFLOAT
1203
1204 /* I N I T F L O A T  --  Deduce floating-point precision by inspection */
1205
1206 int fp_rounding = 0;                /* Nonzero if printf("%f") rounds */
1207 int fp_digits = 0;                  /* Digits of floating point precision */
1208
1209 #ifdef COMMENT
1210 /* For looking at internal floating-point representations */
1211 static char fp_xbuf[128];
1212 static char *
1213 tohex(s, n) CHAR * s; int n; {
1214     int x;
1215     char * p = fp_xbuf;
1216     while (n-- > 0) {
1217         x = (*s >> 4) & 0x0f;
1218         *p++ = hexdigits[x];
1219         x = *s++ & 0x0f;
1220         *p++ = hexdigits[x];
1221     }
1222     *p = NUL;
1223     return((char *)fp_xbuf);
1224 }
1225 #endif /* COMMENT */
1226
1227 char math_pi[] = "3.1415926535897932384626433832795";
1228 char math_e[] =  "2.7182818284590452353602874713527";
1229
1230 VOID
1231 initfloat() {
1232     char * buf = NULL;
1233     int i, x, y;
1234 /*
1235   We malloc a big temporary buffer for sprintf() to minimize likelihood of
1236   (and damage from) sprintf buffer overflows.  In any case, the only way this
1237   could happen would be if sprintf() itself had bugs, since the format
1238   descriptor says to cut it off at 250 decimal places.
1239 */
1240     if ((buf = (char *)malloc(4096))) {
1241         sprintf(buf,"%0.250f",(10.0 / 3.0));
1242         for (i = 2; i < 250 && buf[i] == '3'; i++) ;
1243         x = i - 1;
1244         debug(F111,"initfloat 10.0/3.0",buf,x);
1245         sprintf(buf,"%0.250f",(4.0 / 9.0));
1246         for (i = 2; i < 250 && buf[i] == '4'; i++) ;
1247         y = i - 1;
1248         debug(F111,"initfloat 4.0/9.0",buf,y);
1249         fp_digits = (x < y) ? x : y;
1250         if (fp_digits < sizeof(math_pi) - 1) {
1251             math_pi[fp_digits+1] = NUL;
1252             math_e[fp_digits+1] = NUL;
1253         }
1254         sprintf(buf,"%0.6f",(7.0 / 9.0));
1255         if (buf[7] == '8') fp_rounding = 1;
1256         debug(F111,"initfloat 7.0/9.0",buf,fp_rounding);
1257         debug(F101,"initfloat precision","",fp_digits);
1258         free(buf);
1259     }
1260 }
1261 #endif /* CKFLOAT */
1262
1263 /*
1264   P R E S C A N -- A quick look through the command-line options for
1265   items that must be handled before the initialization file is executed.
1266 */
1267 #ifdef NT
1268 extern int StartedFromDialer;
1269 #endif /* NT */
1270 #ifdef OS2
1271 extern int k95stdio;
1272 unsigned long startflags = 0L;
1273 #endif /* OS2 */
1274
1275 static char *
1276 findinpath(arg) char * arg; {
1277 #ifdef OS2
1278     char * scriptenv, * keymapenv;
1279     int len;
1280 #endif /* OS2 */
1281 #ifdef DCMDBUF
1282     extern char * cmdbuf;
1283 #else
1284     extern char cmdbuf[];
1285 #endif /* DCMDBUF */
1286     char takepath[4096];
1287     char * s;
1288     int x, z;
1289
1290     /* Set up search path... */
1291 #ifdef OS2
1292     char * appdata0 = NULL, *appdata1 = NULL;
1293 #ifdef NT
1294     scriptenv = getenv("K95SCRIPTS");
1295     keymapenv = getenv("K95KEYMAPS");
1296     makestr(&appdata0,(char *)GetAppData(0));
1297     makestr(&appdata1,(char *)GetAppData(1));
1298 #else /* NT */
1299     scriptenv = getenv("K2SCRIPTS");
1300     keymapenv = getenv("K2KEYMAPS");
1301 #endif /* NT */
1302     if (!scriptenv)
1303       scriptenv = getenv("CK_SCRIPTS");
1304     if (!scriptenv)
1305       scriptenv = "";
1306     if (!keymapenv)
1307       keymapenv = getenv("CK_KEYMAPS");
1308     if (!keymapenv)
1309       keymapenv = "";
1310
1311     debug(F110,"startupdir",startupdir,0);
1312     debug(F110,"common appdata directory",appdata1,0);
1313     debug(F110,"appdata directory",appdata0,0);
1314     debug(F110,"inidir",inidir,0);
1315     debug(F110,"home",zhome(),0);
1316     debug(F110,"exedir",exedir,0);
1317
1318     len = strlen(scriptenv) + strlen(keymapenv) + 3*strlen(startupdir)
1319         + 3*strlen(inidir) + 3*strlen(zhome()) + 3*strlen(exedir)
1320         + (appdata0 ? 3*strlen(appdata0) : 0) 
1321         + (appdata1 ? 3*strlen(appdata1) : 0)
1322         + 6*strlen("SCRIPTS/") + 6*strlen("KEYMAPS/") + 16;
1323
1324     if (len >= 4096) {                  /* SAFE (length is checked) */
1325         takepath[0] = '\0';
1326         debug(F111,"findinpath error - path length too long","len",len);
1327     } else
1328       sprintf(takepath,
1329               /* semicolon-separated path list */
1330     "%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",
1331               scriptenv,
1332               (scriptenv[0] && scriptenv[strlen(scriptenv)-1]==';')?"":";",
1333               keymapenv,
1334               (keymapenv[0] && keymapenv[strlen(keymapenv)-1]==';')?"":";",
1335               startupdir,
1336               startupdir, "SCRIPTS/",
1337               startupdir, "KEYMAPS/",
1338               appdata1 ? appdata1 : "", 
1339               appdata1 ? "Kermit 95;" : "",
1340               appdata1 ? appdata1 : "",
1341               appdata1 ? "Kermit 95/SCRIPTS/;" : "",
1342               appdata1 ? appdata1 : "",
1343               appdata1 ? "Kermit 95/KEYMAPS/;" : "",
1344               appdata0 ? appdata0 : "",
1345               appdata0 ? "Kermit 95;" : "",
1346               appdata0 ? appdata0 : "",
1347               appdata0 ? "Kermit 95/SCRIPTS/;" : "",
1348               appdata0 ? appdata0 : "",
1349               appdata0 ? "Kermit 95/KEYMAPS/;" : "",
1350               inidir,
1351               inidir, "SCRIPTS/",
1352               inidir, "KEYMAPS/",
1353               zhome(),
1354               zhome(), "SCRIPTS/",
1355               zhome(), "KEYMAPS/",
1356               exedir,
1357               exedir, "SCRIPTS/",
1358               exedir, "KEYMAPS/"
1359               );
1360     debug(F110,"findinpath takepath",takepath,0);
1361 #ifdef NT
1362     makestr(&appdata0,NULL);
1363     makestr(&appdata1,NULL);
1364 #endif /* NT */
1365 #else /* not OS2 */
1366 #ifndef NOSPL
1367     z = 1024;                           /* Look in home directory */
1368     s = takepath;
1369     zzstring("\\v(home)",&s,&z);
1370 #else
1371     takepath[0] = '\0';
1372 #endif /* NOSPL */
1373 #endif /* OS2 */
1374 /*
1375   All the logic for searching the take path is in the command parser.
1376   So even though we aren't parsing commands, we initialize and call the
1377   parser from here, with the purported filename stuffed into the command
1378   buffer, followed by some carriage returns to make the parser return.
1379   If the file is not found, or otherwise not accessible, the parser prints
1380   an appropriate message, and then we just exit.
1381 */
1382     cmdini();                           /* Allocate command buffers etc */
1383     cmini(0);                           /* Initialize them */
1384     /* Stuff filename into command buf with braces in case of spaces */
1385     ckmakmsg(cmdbuf,CMDBL,"{",arg,"}",NULL);
1386     debug(F110,"findinpath cmdbuf",cmdbuf,0);
1387     ckstrncat(cmdbuf,"\r\r",CMDBL);     /* And some carriage returns */
1388     if (cmifip("","",&s,&x,0,takepath,xxstring) < 0)
1389       return(NULL);
1390     cmres();
1391     return(s);
1392 }
1393
1394 static int tr_int;                      /* Flag if TRANSMIT interrupted */
1395
1396 #ifndef MAC
1397 SIGTYP
1398 #ifdef CK_ANSIC
1399 trtrap(int foo)                         /* TRANSMIT interrupt trap */
1400 #else
1401 trtrap(foo) int foo;                    /* TRANSMIT interrupt trap */
1402 #endif /* CK_ANSIC */
1403 /* trtrap */ {
1404 #ifdef __EMX__
1405     signal(SIGINT, SIG_ACK);
1406 #endif
1407     tr_int = 1;                         /* (Need arg for ANSI C) */
1408     SIGRETURN;
1409 }
1410 #endif /* MAC */
1411 #endif /* NOICP */
1412
1413 #ifdef UNIX
1414 VOID
1415 getexedir() {
1416     extern char * xarg0;
1417     long xx;
1418   /*
1419     Unix provides no standard service for this.  We look in argv[0], and if
1420     we're lucky there's a full pathname.  If not we do a PATH search.
1421   */
1422     if (ckstrchr(xarg0,'/')) {          /* Global copy of argv[0] */
1423         int i, k;
1424         char * p = NULL;
1425         if ((k = ckstrncpy(tmpbuf,xarg0,TMPBUFSIZ-2)) > 0) {
1426             p = tmpbuf;
1427             /* Convert to fully qualified pathname */
1428             if (tmpbuf[0]) if (tmpbuf[0] != '/') {
1429                 line[0] = NUL;
1430                 zfnqfp(tmpbuf,LINBUFSIZ-2,(char *)line);
1431                 if (line[0])
1432                   p = line;
1433             }
1434             xx = zchki(p);
1435             if (xx > -1) {              /* Is the result an existing file? */
1436                 k = strlen(p);
1437                 for (i = k-1; i > 0; i--) { /* Yes, strip name part */
1438                     if (p[i] == '/') {
1439                         if (i < k-1)
1440                           p[i+1] = NUL;
1441                         break;
1442                     }
1443                 }
1444             }
1445             makestr(&exedir,p);         /* Save the result */
1446         }
1447     }
1448     if (!exedir && xarg0) {             /* Not found? */
1449         char * p;
1450         p = getenv("PATH");             /* Search the PATH */
1451         if (p) {                        /* If there is one... */
1452             char * q, * PATH = NULL;
1453             int k;
1454             makestr(&PATH,p);           /* Pokeable copy of PATH string */
1455             if (PATH) {                 /* If malloc succeeded... */
1456                 p = PATH;
1457                 while (p && *p) {        /* Loop through segments */
1458                     q = ckstrchr(p,':'); /* End of this segment */
1459                     if (q == p) {       /* Null PATH segment */
1460                         p++;            /* Skip over colon */
1461                         continue;
1462                     }
1463                     if (q)              /* If not at end of PATH string */
1464                       *q++ = NUL;       /* zero out the colon */
1465                     if ((k = ckstrncpy(tmpbuf,p,TMPBUFSIZ)) > 0) {
1466                         if (tmpbuf[k-1] != '/') { /* Copy this PATH segment */
1467                             tmpbuf[k++] = '/';    /* Append '/' if needed */
1468                             tmpbuf[k] = NUL;
1469                         }
1470                         /* Append the argv[0] value */
1471                         if (ckstrncpy(&tmpbuf[k],xarg0,TMPBUFSIZ) > 0) {
1472                             if (zchki(tmpbuf) > -1) { /* File exists? */
1473                                 tmpbuf[k] = NUL;      /* Yes, we're done */
1474                                 zfnqfp(tmpbuf,LINBUFSIZ,(char *)line);
1475                                 makestr(&exedir,line);
1476                                 break;
1477                             }
1478                         } else break;
1479                     } else break;
1480                     p = q;              /* Not found, go to next segment  */
1481                 } /* while */
1482                 free(PATH);             /* Free PATH copy */
1483             }
1484         }
1485         if (!exedir) {                  /* Still nothing? */
1486             if (zchki(xarg0) > -1) {    /* Maybe it's in the current dir */
1487                 zfnqfp(zgtdir(),LINBUFSIZ,(char *)line);
1488                 makestr(&exedir,line);
1489             }
1490         }
1491     }
1492     if (!exedir) {                      /* Still nothing? */
1493         makestr(&exedir,"/");           /* Fake it with with root. */
1494     }
1495 }
1496 #endif /* UNIX */
1497
1498 int arg_x = 0;
1499 static int x_prescan = 0;
1500
1501 /*
1502   The argument y once meant something but I can't imagine what so now
1503   it's ignored.  (Prior to 22 Aug 98, prescan() was called twice by main(),
1504   and the arg differentiated the two calls.  But this caused all sorts of
1505   problems & confusion, so I commented out the second call.  This issue might
1506   need to be revisited.)
1507 */
1508 VOID
1509 prescan(dummy) int dummy; {             /* Arg is ignored. */
1510     extern int howcalled;
1511     int yargc; char **yargv;
1512     char x;
1513     char *yp, *yy;
1514 #ifdef DEBUG
1515     int debcount = 0;
1516 #endif /* DEBUG */
1517     int z;
1518
1519     if (x_prescan)                      /* Only run once */
1520       return;
1521     x_prescan = 1;
1522
1523     yargc = xargc;                      /* Make copy of arg vector */
1524     yargv = xargv;
1525
1526 #ifndef NOICP
1527 #ifdef DCMDBUF
1528     if (!kermrc)
1529       if (!(kermrc = (char *) malloc(KERMRCL+1)))
1530         fatal("prescan: no memory for kermrc");
1531 #endif /* DCMDBUF */
1532     ckstrncpy(kermrc,KERMRC,KERMRCL);   /* Default init file name */
1533 #endif /* NOICP */
1534
1535 #ifdef OS2
1536     yp = getenv("K95STARTFLAGS");
1537     if (yp) {
1538         startflags = atoi(yp);
1539     }
1540 #endif /* OS2 */
1541
1542 #ifdef IKSD
1543     if (howcalled == I_AM_IKSD)         /* Internet Kermit Service daemon */
1544       inserver = 1;                     /* (See inserver section of ckcmai) */
1545 #endif /* IKSD */
1546
1547 /* Command line options for Kermit */
1548
1549 #ifndef NOCMDL
1550     if (yargc > 1
1551         && *yargv[1] != '-'
1552         && (yargv[1][0] != '=')
1553 #ifdef KERBANG
1554         && (yargv[1][0] != '+')
1555 #endif /* KERBANG */
1556 #ifdef IKSD
1557         && (howcalled != I_AM_IKSD)
1558 #endif /* IKSD */
1559         ) {                             /* Filename as 1st argument */
1560 #ifndef NOICP
1561         char *s;
1562 #endif /* NOICP */
1563 #ifndef NOURL
1564         extern int haveurl;
1565         extern struct urldata g_url;
1566         if (urlparse(yargv[1],&g_url)) {
1567             if (!ckstrcmp(g_url.svc,"ftp",-1,0) ||
1568                 !ckstrcmp(g_url.svc,"ftps",-1,0)) {
1569                 haveurl = 1;
1570                 howcalled = I_AM_FTP;
1571             } else if (!ckstrcmp(g_url.svc,"telnet",-1,0) ||
1572                        !ckstrcmp(g_url.svc,"telnets",-1,0)) {
1573                 haveurl = 1;
1574                 howcalled = I_AM_TELNET;
1575             } else if (!ckstrcmp(g_url.svc,"ssh",-1,0)) {
1576                 haveurl = 1;
1577                 howcalled = I_AM_SSH;
1578             } else if (!ckstrcmp(g_url.svc,"iksd",-1,0) ||
1579                        !ckstrcmp(g_url.svc,"kermit",-1,0)) {
1580                 haveurl = 1;
1581                 howcalled = I_AM_KERMIT;
1582             } else if (!ckstrcmp(g_url.svc,"http",-1,0) ||
1583                        !ckstrcmp(g_url.svc,"https",-1,0)) {
1584                 haveurl = 1;
1585                 howcalled = I_AM_HTTP;
1586             }
1587             if (haveurl) {
1588                 while (--yargc > 0) {   /* Go through command-line args */
1589                     yargv++;            /* looking for -Y and -d */
1590                     yp = *yargv+1;
1591                     if (**yargv == '-') {
1592                         x = *(*yargv+1);
1593                         while (x) {
1594                             switch (x) {
1595 #ifndef NOICP
1596                               case '+':
1597                               case '-':
1598                                 if (doxarg(yargv,1) < 0) {
1599                                     fatal("Extended argument error");
1600                                 }
1601                                 yp = "";
1602                                 break;
1603 #endif /* NOICP */
1604                               case 'Y':
1605                                 noinit++;
1606                                 break;
1607
1608                               case 'q':
1609                                 quiet = 1;
1610                                 break;
1611
1612                               case 'h':
1613                                   noinit = 1;
1614 #ifdef OS2
1615                                   startflags |= 2;    /* No network DLLs */
1616                                   startflags |= 4;    /* No TAPI DLLs */
1617                                   startflags |= 8;    /* No Security DLLs */
1618                                   startflags |= 16;   /* No Zmodem DLLs */
1619                                   startflags |= 32;   /* Stdin */
1620                                   startflags |= 64;   /* Stdout */
1621 #endif /* OS2 */
1622                                   break;
1623                               case 'd': /* = SET DEBUG ON */
1624 #ifdef DEBUG
1625                                 if (debcount++ > 0)
1626                                   debtim = 1;
1627                                 if (!deblog)
1628                                   deblog = debopn("debug.log",0);
1629 #endif /* DEBUG */
1630                                 break;
1631 #ifdef OS2
1632                               case 'W':
1633                                 if (*(yp+1))
1634                                   fatal("invalid argument bundling after -W");
1635                                 yargv++, yargc--;
1636                                 if (yargc < 1)
1637                                   fatal("Window handle missing");
1638                                 hwndDialer = (HWND) atol(*yargv);
1639                                 StartedFromDialer = 1;
1640                                 yargv++, yargc--;
1641                                 KermitDialerID = atol(*yargv) ;
1642                                 break;
1643                               case '#': /* K95 initialization options */
1644                                 if (*(yp+1)) {
1645                                     fatal("invalid argument bundling");
1646                                 }
1647                                 yargv++, yargc--;
1648                                 if (yargc < 1)
1649                                   fatal("-# argument missing");
1650                                 startflags |= atol(*yargv);
1651                                 break;
1652 #endif /* OS2 */
1653                             }
1654                             if (!yp)
1655                               break;
1656                             x = *++yp;
1657                         }
1658                     }
1659                 }
1660                 return;
1661             }
1662         }
1663         /* after this point non-Kermit personalities must return */
1664         switch (howcalled) {
1665           case I_AM_KERMIT:
1666           case I_AM_IKSD:
1667           case I_AM_SSHSUB:
1668             break;
1669           default:
1670             return;
1671         }
1672 #endif /* NOURL */
1673
1674 #ifndef NOICP
1675         /* If it is not a URL that we recognize, try to treat it as a file */
1676
1677         if (!isabsolute(yargv[1]))      /* If not absolute */
1678           s = findinpath(yargv[1]);     /* Look in PATH */
1679         else
1680           s = yargv[1];
1681         if (!s)
1682           doexit(BAD_EXIT,xitsta);
1683         zfnqfp(s,CKMAXPATH,cmdfil);     /* In case of CD in file */
1684         yargc -= 1;                     /* Skip past the filename */
1685         yargv += 1;                     /* Otherwise we'll get an error */
1686 #endif /* NOICP */
1687     }
1688
1689 #ifndef NOCMDL
1690 #ifdef NEWFTP
1691     if (howcalled == I_AM_FTP) {        /* Kermit's FTP client personality */
1692         while (--yargc > 0) {           /* Go through command-line args */
1693             yargv++;                    /* looking for -Y and -d */
1694             yp = *yargv+1;
1695             if (**yargv == '-') {
1696                 x = *(*yargv+1);
1697                 while (x) {
1698                     switch (x) {
1699 #ifndef NOICP
1700                       case '+':
1701                       case '-':
1702                         if (doxarg(yargv,1) < 0) {
1703                             fatal("Extended argument error");
1704                         }
1705                         yp = "";
1706                         break;
1707 #endif /* NOICP */
1708                       case 'Y':
1709                         noinit++;
1710                         break;
1711
1712                       case 'q':
1713                           quiet = 1;
1714                           break;
1715
1716                       case 'h':
1717                         noinit = 1;
1718 #ifdef OS2
1719                         startflags |= 2;    /* No network DLLs */
1720                         startflags |= 4;    /* No TAPI DLLs */
1721                         startflags |= 8;    /* No Security DLLs */
1722                         startflags |= 16;   /* No Zmodem DLLs */
1723                         startflags |= 32;   /* Stdin */
1724                         startflags |= 64;   /* Stdout */
1725 #endif /* OS2 */
1726                         break;
1727                       case 'd':             /* = SET DEBUG ON */
1728 #ifdef DEBUG
1729                         if (debcount++ > 0)
1730                           debtim = 1;
1731                         if (!deblog)
1732                           deblog = debopn("debug.log",0);
1733 #endif /* DEBUG */
1734                         break;
1735 #ifdef OS2
1736                       case 'W':
1737                         if (*(yp+1))
1738                           fatal("invalid argument bundling after -W");
1739                         yargv++, yargc--;
1740                         if (yargc < 1)
1741                           fatal("Window handle missing");
1742                         hwndDialer = (HWND) atol(*yargv);
1743                         StartedFromDialer = 1;
1744                         yargv++, yargc--;
1745                         KermitDialerID = atol(*yargv) ;
1746                         break;
1747                       case '#':         /* K95 initialization options */
1748                         if (*(yp+1)) {
1749                             fatal("invalid argument bundling");
1750                         }
1751                         yargv++, yargc--;
1752                         if (yargc < 1)
1753                           fatal("-# argument missing");
1754                         startflags |= atol(*yargv);
1755                         break;
1756 #endif /* OS2 */
1757                     }
1758                     if (!yp)
1759                       break;
1760                     x = *++yp;
1761                 }
1762             }
1763         }
1764         return;
1765     }
1766 #endif /* NEWFTP */
1767 #endif /* NOCMDL */
1768
1769     while (--yargc > 0) {               /* Go through command-line args */
1770         yargv++;
1771         yp = *yargv+1;                  /* Pointer for bundled args */
1772         if (**yargv == '=')             /* Same rules as cmdlin()... */
1773           return;
1774         debug(F110,"prescan *yargv",*yargv,0);
1775
1776 #ifndef NOICP
1777 #ifdef KERBANG
1778         yy = *yargv;
1779         if (!strcmp(yy,"+") || (*yy == '+' && *(yy+1) < (char)33)) {
1780             char * s;
1781             yargv++;
1782             noinit = 1;
1783             if (!*yargv)
1784               return;
1785             cfilef = 1;
1786             s = findinpath(*yargv);
1787             if (s) {
1788                 zfnqfp(s,CKMAXPATH,cmdfil);
1789                 return;
1790             } else
1791               doexit(BAD_EXIT,xitsta);
1792         }
1793 #endif /* KERBANG */
1794 #endif /* NOICP */
1795         if (!strcmp(*yargv,"--"))       /* getopt() conformance */
1796           return;
1797 #ifdef VMS
1798         else if (**yargv == '/')
1799           continue;
1800 #endif /* VMS */
1801         else if (**yargv == '-') {      /* Got an option (begins with dash) */
1802             x = *(*yargv+1);            /* Get option letter */
1803             while (x) {                 /* Allow for bundled options */
1804                 debug(F000,"prescan arg","",x);
1805                 switch (x) {
1806 #ifndef NOICP
1807                   case '+':
1808                   case '-':
1809                     if (doxarg(yargv,1) < 0) {
1810                         fatal("Extended argument error");
1811                     }
1812 #ifndef COMMENT                         /* Jeff 28 Apr 2003 */
1813                     yp = NULL;          /* (not "") */
1814 #else
1815                     yargv++, yargc--;
1816                     yp = *yargv;
1817 #endif /* COMMENT */
1818                     break;
1819 #endif /* NOICP */
1820
1821                   case '7':             /* Undocumented... */
1822                     sstelnet = 1;       /* (because it doesn't work) */
1823                     break;
1824 #ifdef IKSD
1825                   case 'A': {
1826                       char * p;
1827                       inserver = 1;     /* Flag that we are doing this */
1828                       srvcdmsg = 2;     /* Preset this */
1829                       /* See inserver section of ckcmai.c for more settings */
1830 #ifdef OS2
1831                       if (*(yp+1)) {
1832                           fatal("invalid argument bundling after -A");
1833                       }
1834 #ifdef NT
1835                       /* Support for Pragma Systems Telnet/Terminal Servers */
1836                       p = getenv("PRAGMASYS_INETD_SOCK");
1837                       if (p && atoi(p) != 0) {
1838                           ttname[0] = '$';
1839                           ckstrncpy(&ttname[1],p,TTNAMLEN-1);
1840                           break;
1841                       }
1842 #endif /* NT */
1843                       yargv++, yargc--;
1844                       if (yargc < 1 || **yargv == '-') {
1845                           fatal("-A argument missing");
1846                       } else {
1847                           ttname[0] = '$';
1848                           ckstrncpy(&ttname[1],*yargv,TTNAMLEN-1);
1849                       }
1850 #endif /* OS2 */
1851                       break;
1852                   }
1853 #endif /* IKSD */
1854
1855 #ifdef OS2
1856                   case 'W':
1857                     if (*(yp+1))
1858                       fatal("invalid argument bundling after -W");
1859                     yargv++, yargc--;
1860                     if (yargc < 1)
1861                       fatal("Window handle missing");
1862 #ifdef COMMENT
1863                     if (dummy) {
1864                         yargv++, yargc--;
1865                         break;
1866                     } else {
1867 #endif /* COMMENT */
1868                         hwndDialer = (HWND) atol(*yargv);
1869                         StartedFromDialer = 1;
1870                         yargv++, yargc--;
1871                         KermitDialerID = atol(*yargv) ;
1872 #ifdef COMMENT
1873                     }
1874 #endif /* COMMENT */
1875                     break;
1876
1877                   case '#':             /* K95 initialization options */
1878                     if (*(yp+1)) {
1879                         fatal("invalid argument bundling");
1880                     }
1881                     yargv++, yargc--;
1882                     if (yargc < 1)
1883                       fatal("-# argument missing");
1884                     startflags |= atol(*yargv);
1885                     break;
1886 #endif /* OS2 */
1887
1888 #ifndef NOSPL
1889                   case 'M':                             /* My User Name */
1890                     if (*(yp+1)) {
1891                         fatal("invalid argument bundling");
1892                     }
1893                     yargv++, yargc--;
1894                     if ((yargc < 1) || (**yargv == '-')) {
1895                         fatal("missing username");
1896                     }
1897                     if ((int)strlen(*yargv) > UIDBUFLEN) {
1898                         fatal("username too long");
1899                     }
1900 #ifdef COMMENT
1901 /*
1902   This can't work.  uidbuf is overwritten in sysinit() which has yet to be
1903   called.  This cannot be set in prescan().
1904 */
1905 #ifdef IKSD
1906                     if (!inserver)
1907 #endif /* IKSD */
1908                       ckstrncpy(uidbuf,*yargv,UIDBUFLEN);
1909 #endif /* COMMENT */
1910                     break;
1911 #endif /* NOSPL */
1912                   case 'R':             /* Remote-only advisory */
1913 #ifdef CK_IFRO
1914                     remonly = 1;
1915 #endif /* CK_IFRO */
1916                     break;
1917                   case 'S':             /* STAY */
1918                     stayflg = 1;
1919                     break;
1920                   case 'h':
1921                     noinit = 1;
1922 #ifdef OS2
1923                     startflags |= 2;    /* No network DLLs */
1924                     startflags |= 4;    /* No TAPI DLLs */
1925                     startflags |= 8;    /* No Security DLLs */
1926                     startflags |= 16;   /* No Zmodem DLLs */
1927                     startflags |= 32;   /* Stdin */
1928                     startflags |= 64;   /* Stdout */
1929 #endif /* OS2 */
1930                     break;
1931 #ifndef NOICP
1932                   case 'Y':             /* No init file */
1933                     noinit = 1;
1934                     break;
1935 #endif /* NOICP */
1936
1937                   case 'q':
1938                       quiet = 1;
1939                       break;
1940
1941                   case 'd':             /* = SET DEBUG ON */
1942 #ifdef DEBUG
1943                     if (debcount++ > 0)
1944                       debtim = 1;
1945                     if (!deblog)
1946                       deblog = debopn("debug.log",0);
1947 #endif /* DEBUG */
1948                     break;
1949
1950                   case 'x':             /* Server */
1951                     arg_x = 1;          /* Note in advance */
1952                     break;
1953 #ifndef NOICP
1954                   case 'y':             /* Alternative init file */
1955                     noinit = 0;
1956                     yargv++, yargc--;
1957                     if (yargc < 1) fatal("missing name in -y");
1958                     /* Replace init file name */
1959                     ckstrncpy(kermrc,*yargv,KERMRCL);
1960                     rcflag = 1;         /* Flag that this has been done */
1961                     debug(F111,"prescan kermrc",kermrc,rcflag);
1962                     break;
1963 #endif /* NOICP */
1964                   case 'z':             /* = SET BACKGROUND OFF */
1965                     bgset = 0;
1966                     backgrd = 0;
1967 #ifdef VMS
1968                     batch = 0;
1969 #endif /* VMS */
1970                     break;
1971
1972                   case 'B':             /* Force background (batch) */
1973                     bgset = 1;
1974                     backgrd = 1;
1975 #ifdef VMS
1976                     batch = 1;
1977 #endif /* VMS */
1978                     break;
1979
1980 #ifdef CK_NETBIOS
1981                   case 'N':
1982                     {
1983                         int n ;
1984                         yargv++, yargc--;
1985 #ifdef COMMENT
1986                         if (y)
1987                           break;
1988 #endif /* COMMENT */
1989                         if (strlen(*yargv) != 1 || (*yargv)[0] == 'X') {
1990                             NetBiosAdapter = -1;
1991                         } else {
1992                             n = atoi(*yargv);
1993                             if (n >= 0 && n <= 9)
1994                               NetBiosAdapter = n;
1995                             else
1996                               NetBiosAdapter = -1;
1997                         }
1998                     }
1999                     break;
2000 #endif /* CK_NETBIOS */
2001                   default:
2002                     break;
2003                 }
2004                 if (!yp)
2005                   break;
2006                 x = *++yp;              /* See if options are bundled */
2007             }
2008         }
2009     }
2010 #endif /* NOCMDL */
2011 }
2012
2013 /*  G E T T C S  --  Get Transfer (Intermediate) Character Set  */
2014
2015 /*
2016   Given two file character sets, this routine picks out the appropriate
2017   "transfer" character set to use for translating between them.
2018   The transfer character set number is returned.
2019
2020   Translation between two file character sets is done, for example,
2021   by the CONNECT, TRANSMIT, and TRANSLATE commands.
2022
2023   Translation between Kanji character sets is not yet supported.
2024 */
2025 int
2026 gettcs(cs1,cs2) int cs1, cs2; {
2027 #ifdef NOCSETS                          /* No character-set support */
2028     return(0);                          /* so no translation */
2029 #else
2030     int tcs = TC_TRANSP;
2031 #ifdef KANJI
2032 /* Kanji not supported yet */
2033     if (fcsinfo[cs1].alphabet == AL_JAPAN ||
2034         fcsinfo[cs2].alphabet == AL_JAPAN )
2035       tcs = TC_TRANSP;
2036     else
2037 #endif /* KANJI */
2038 #ifdef CYRILLIC
2039 /*
2040   I can't remember why we don't test both sets here, but I think there
2041   must have been a reason...
2042 */
2043       if (fcsinfo[cs2].alphabet == AL_CYRIL)
2044         tcs = TC_CYRILL;
2045       else
2046 #endif /* CYRILLIC */
2047 #ifdef HEBREW
2048           if (fcsinfo[cs1].alphabet == AL_HEBREW ||
2049               fcsinfo[cs2].alphabet == AL_HEBREW )
2050             tcs = TC_HEBREW;
2051           else
2052 #endif /* HEBREW */
2053 #ifdef GREEK
2054           if (fcsinfo[cs1].alphabet == AL_GREEK ||
2055               fcsinfo[cs2].alphabet == AL_GREEK )
2056             tcs = TC_GREEK;
2057           else
2058 #endif /* GREEK */
2059
2060             /* Roman sets ... */
2061
2062 #ifdef LATIN2                           /* East European */
2063         if (cs1 == FC_2LATIN  || cs2 == FC_2LATIN || /* Latin-2 */
2064             cs1 == FC_CP852   || cs2 == FC_CP852  || /* CP852 */
2065             cs1 == FC_CP1250  || cs2 == FC_CP1250 || /* Windows Latin-2 */
2066             cs1 == FC_MAZOVIA || cs2 == FC_MAZOVIA)  /* Polish Mazovia */
2067           tcs = TC_2LATIN;
2068         else
2069 #endif /* LATIN2 */
2070                                         /* West European Euro-aware */
2071           if (cs1 == FC_CP858 || cs1 == FC_9LATIN ||
2072               cs2 == FC_CP858 || cs2 == FC_9LATIN)
2073             tcs = TC_9LATIN;
2074           else                          /* Traditional West European */
2075             tcs = TC_1LATIN;
2076     return(tcs);
2077 #endif /* NOCSETS */
2078 }
2079
2080 #ifndef NOLOCAL
2081 /*  D O C O N E C T  --  Do the connect command  */
2082 /*
2083   q = 0 means issue normal informational message about how to get back, etc.
2084   q != 0 means to skip the message.
2085 */
2086
2087 int
2088 doconect(q,async) int q, async; {
2089     int x;                              /* Return code */
2090 #ifdef CK_AUTODL
2091     extern CHAR ksbuf[];
2092 #endif /* CK_AUTODL */
2093 #ifndef NOKVERBS                        /* Keyboard macro material */
2094     extern int keymac, keymacx;
2095 #endif /* NOKVERBS */
2096     extern int justone, adl_err;
2097     int qsave;                          /* For remembering "quiet" value */
2098 #ifdef OS2
2099     extern int term_io;
2100     extern int display_demo;
2101     int term_io_save;
2102 #ifdef KUI
2103     extern int kui_async;
2104 #endif /* KUI */
2105 #endif /* OS2 */
2106     int is_tn = 0;
2107
2108 #ifdef IKSD
2109     if (inserver) {
2110         if (!quiet)
2111           printf("?Sorry, IKSD cannot CONNECT.\r\n");
2112         return(success = 0);
2113     }
2114 #endif /* IKSD */
2115
2116     is_tn =
2117 #ifdef TNCODE
2118       (local && network && IS_TELNET()) || (!local && sstelnet)
2119 #else
2120         0
2121 #endif /* TNCODE */
2122           ;
2123 /*
2124   Saving, changing, and restoring the global "quiet" variable around calls
2125   to conect() to control whether the verbose CONNECT message is printed is
2126   obviously less elegant than passing a parameter to conect(), but we do it
2127   this way to avoid the need to change all of the ck?con.c modules.  NOTE:
2128   it is important to restore the value immediately upon return in case there
2129   is an autodownload or APC.
2130 */
2131     qsave = quiet;                      /* Save it */
2132     if (!quiet && q > -1)
2133       quiet = q;                        /* Use argument temporarily */
2134     conres();                           /* Put console back to normal */
2135     debug(F101,"doconect justone 1","",justone);
2136 #ifdef CK_AUTODL
2137     ksbuf[0] = NUL;                     /* Autodownload packet buffer */
2138 #endif /* CK_AUTODL */
2139 #ifdef OS2
2140     display_demo = 1;                   /* Remember to display demo */
2141 #endif /* OS2 */
2142
2143 #ifdef IKS_OPTION
2144     if (is_tn && TELOPT_U(TELOPT_KERMIT) && ttchk() >= 0
2145 #ifdef OS2
2146        && !viewonly
2147 #endif /* OS2 */
2148         ) {
2149         /* If the remote side is in a state of IKS START-SERVER    */
2150         /* we request that the state be changed.  We will detect   */
2151         /* a failure to adhere to the request when we call ttinc() */
2152         if (!iks_wait(KERMIT_REQ_STOP,0) && !tcp_incoming) {
2153             if (!quiet) {
2154                 printf("\r\nEnter Client/Server Mode...  Use:\r\n\r\n");
2155                 printf(
2156 " REMOTE LOGIN <user> <password> to log in to the server if necessary.\r\n");
2157                 printf(" SEND and GET for file transfer.\r\n");
2158                 printf(" REMOTE commands for file management.\r\n");
2159                 printf(" FINISH to terminate Client/Server mode.\r\n");
2160                 printf(" BYE to terminate and close connection.\r\n");
2161                 printf(" REMOTE HELP for additional information.\r\n\r\n");
2162             }
2163             quiet = qsave;
2164             return(0);      /* Failure */
2165         }
2166     }
2167
2168     /* Let our peer know our state. */
2169 #ifdef CK_AUTODL
2170     if (is_tn && TELOPT_ME(TELOPT_KERMIT)
2171 #ifdef OS2
2172         && !viewonly
2173 #endif /* OS2 */
2174          ) {
2175         if (autodl && !TELOPT_SB(TELOPT_KERMIT).kermit.me_start) {
2176             tn_siks(KERMIT_START);      /* Send Kermit-Server Start */
2177         } else if (!autodl && TELOPT_SB(TELOPT_KERMIT).kermit.me_start) {
2178             tn_siks(KERMIT_STOP);
2179         }
2180     }
2181 #else /* CK_AUTODL */
2182     if (is_tn && TELOPT_ME(TELOPT_KERMIT) &&
2183         TELOPT_SB(TELOPT_KERMIT).kermit.me_start)
2184         tn_siks(KERMIT_STOP);
2185 #endif /* CK_AUTODL */
2186 #endif /* IKS_OPTION */
2187
2188     debug(F101,"doconect flow","",flow);
2189 #ifdef OS2
2190     debug(F101,"doconect async","",async);
2191 #ifdef KUI
2192     if (kui_async)
2193       async = 1;;
2194 #endif /* KUI */
2195     x = conect(async);                  /* Connect the first time */
2196 #else /* OS2 */
2197     x = conect();
2198 #endif /* OS2 */
2199     debok = 1;
2200
2201 #ifdef IKS_OPTION
2202     if (TELOPT_U(TELOPT_KERMIT) &&
2203         TELOPT_SB(TELOPT_KERMIT).kermit.u_start &&
2204         !tcp_incoming && !quiet && ttchk() >= 0
2205         ) {
2206         printf("\r\nEnter Client/Server Mode...  Use:\r\n\r\n");
2207         printf(
2208 " REMOTE LOGIN <user> <password> to log in to the server if necessary.\r\n");
2209         printf(" SEND and GET for file transfer.\r\n");
2210         printf(" REMOTE commands for file management.\r\n");
2211         printf(" FINISH to terminate Client/Server mode.\r\n");
2212         printf(" BYE to terminate and close connection.\r\n");
2213         printf(" REMOTE HELP for additional information.\r\n\r\n");
2214     }
2215 #endif /* IKS_OPTION */
2216
2217     quiet = qsave;                      /* Restore "quiet" value */
2218     debug(F101,"doconect justone 2","",justone);
2219
2220 #ifdef NETCONN
2221     if (network && tn_exit && ttchk() < 0)
2222       doexit(GOOD_EXIT,xitsta);         /* Exit with good status */
2223 #endif /* NETCONN */
2224
2225 #ifdef OS2ORUNIX
2226     /* Exit on disconnect if the port is not open or carrier detect */
2227     if (exitonclose && (ttchk() < 0))
2228       doexit(GOOD_EXIT,xitsta);
2229 #endif /* OS2ORUNIX */
2230
2231 #ifdef CKCONINTB4CB
2232     /* The order makes a difference in HP-UX 8.00. */
2233     /* The other order makes it think it's in the background when it */
2234     /* returns from CONNECT (Apr 1999). */
2235     setint();
2236     concb((char)escape);                /* Restore console for commands */
2237 #else
2238     /* This is how it has always been so better leave it */
2239     /* this way for all non-HP-UX-8.00 builds. */
2240     concb((char)escape);                /* Restore console for commands */
2241     setint();
2242 #endif /* CKCONINTB4CB */
2243
2244 #ifdef OS2
2245     if (!async) {
2246         term_io_save = term_io;         /* Disable I/O by emulator */
2247         term_io = 0;
2248 #endif /* OS2 */
2249
2250 #ifdef CK_APC
2251 /*
2252   If an APC command was received during CONNECT mode, we define it now
2253   as a macro, execute the macro, and then return to CONNECT mode.
2254   We do this in a WHILE loop in case additional APCs come during subsequent
2255   CONNECT sessions.
2256 */
2257         debug(F101,"doconect apcactive","",apcactive);
2258         debug(F101,"doconect success","",success);
2259
2260         while (x > 0 && (apcactive == APC_LOCAL ||
2261                          (apcactive == APC_REMOTE && apcstatus != APC_OFF))) {
2262             debug(F101,"doconect justone 3","",justone);
2263             if (mlook(mactab,"_apc_commands",nmac) == -1) {
2264                 debug(F110,"doconect about to execute APC",apcbuf,0);
2265                 domac("_apc_commands",apcbuf,cmdstk[cmdlvl].ccflgs|CF_APC);
2266                 delmac("_apc_commands",1);
2267 #ifdef DEBUG
2268             } else {
2269                 debug(F100,"doconect APC in progress","",0);
2270 #endif /* DEBUG */
2271             }
2272             debug(F101,"doconect apcactive after domac","",apcactive);
2273             if (!apcactive) {               /* In case CLEAR APC was in APC */
2274                 debug(F101,"doconect quit APC loop: apcactive","",apcactive);
2275                 break;
2276             }
2277             /* Also don't reconnect if autodownload failed - very confusing! */
2278             /* Let them view the local screen to see what happened. - fdc */
2279
2280             /* This should be conditional.  If someone is relying on the */
2281             /* connect mode autodownload for the kermit server to use with */
2282             /* a remotely executed script we should be able to return to */
2283             /* connect mode on the failure.  What we really need to do is */
2284             /* report the status of the transfer and then return to CONNECT. */
2285             /* In unix this would simply be a printf(), but in K95 it could */
2286             /* use a popup dialog to report the status. - Jeff */
2287
2288 #ifndef NOXFER
2289             debug(F101,"doconect xferstat","",xferstat);
2290             if (apcactive == APC_LOCAL && !xferstat && adl_err != 0) {
2291                 debug(F101,"doconect quit APC loop: xferstat","",xferstat);
2292                 apcactive = APC_INACTIVE;
2293                 break;
2294             }
2295 #endif /* NOXFER */
2296 #ifdef OS2
2297             msleep(250);
2298 #endif /* OS2 */
2299             debug(F101,"doconect justone 4","",justone);
2300             qsave = quiet;              /* Do this again... */
2301             if (!quiet && q > -1)
2302               quiet = q;
2303 #ifdef CK_AUTODL
2304             ksbuf[0] = NUL;
2305 #endif /* CK_AUTODL */
2306 #ifdef IKS_OPTION
2307 #ifdef CK_AUTODL
2308             if (is_tn &&
2309                 TELOPT_ME(TELOPT_KERMIT) &&
2310                 !TELOPT_SB(TELOPT_KERMIT).kermit.me_start &&
2311                 autodl
2312 #ifdef CK_APC
2313                 && !apcactive
2314 #endif /* CK_APC */
2315 #ifdef OS2
2316                 && !viewonly
2317 #endif /* OS2 */
2318                 ) {
2319                 tn_siks(KERMIT_START);  /* Send Kermit-Server Start */
2320             }
2321 #endif /* CK_AUTODL */
2322 #endif /* IKS_OPTION */
2323 #ifndef OS2
2324             x = conect();               /* Re-CONNECT. */
2325 #else /* OS2 */
2326             x = conect(0);
2327             term_io = term_io_save;
2328 #endif /* OS2 */
2329             debok = 1;
2330             quiet = qsave;
2331             debug(F101,"doconect justone 5","",justone);
2332 #ifdef NETCONN
2333             if (network && ttchk() < 0) {
2334                 if (tn_exit || exitonclose)
2335                   doexit(GOOD_EXIT,xitsta);
2336                 else
2337                   break;
2338             }
2339 #endif /* NETCONN */
2340
2341 #ifdef OS2ORUNIX
2342             /* If connection dropped */
2343             if (ttchk() < 0) {
2344                 concb((char)escape);    /* Restore console. */
2345                 if (exitonclose)
2346                   doexit(GOOD_EXIT,xitsta);
2347                 else
2348                   break;
2349             }
2350 #endif /* OS2ORUNIX */
2351         } /* Loop back for more. */
2352 #endif /* CK_APC */
2353
2354 #ifndef NOKVERBS
2355         if ((keymac > 0) && (keymacx > -1)) { /* Executing a keyboard macro? */
2356             /* Set up the macro and return */
2357             /* Do not clear the keymac flag */
2358 #ifdef OS2
2359             term_io = term_io_save;
2360 #endif /* OS2 */
2361             return(dodo(keymacx,NULL,CF_KMAC|cmdstk[cmdlvl].ccflgs));
2362         }
2363 #endif /* NOKVERBS */
2364 #ifdef OS2
2365         term_io = term_io_save;
2366     } /* if (!async) */
2367 #endif /* OS2 */
2368
2369 #ifdef CKCONINTB4CB
2370     /* The order makes a difference in HP-UX 8.00. */
2371     /* The other order makes it think it's in the background when it */
2372     /* returns from CONNECT (Apr 1999). */
2373     setint();
2374     concb((char)escape);                /* Restore console for commands */
2375 #else
2376     /* This is how it has always been so better leave it */
2377     /* this way for all non-HP-UX-8.00 builds. */
2378     concb((char)escape);                /* Restore console for commands */
2379     setint();
2380 #endif /* CKCONINTB4CB */
2381 #ifdef OS2
2382     if (!async)
2383 #endif /* OS2 */
2384       what = W_COMMAND;                 /* Back in command mode. */
2385     return(x);                          /* Done. */
2386 }
2387 #endif /* NOLOCAL */
2388
2389 #ifndef NOICP
2390 #ifdef COMMENT
2391 /*
2392   It seemed that this was needed for OS/2, in which \v(cmdfile) and other
2393   file-oriented variables or functions can return filenames containing
2394   backslashes, which are subsequently interpreted as quotes rather than
2395   directory separators (e.g. see commented section for VN_CMDF below).
2396   But the problem can't be cured at this level.  Example:
2397
2398     type \v(cmdfile)
2399
2400   Without doubling, the filename is parsed correctly, but then when passed
2401   to UNIX 'cat' through the shell, the backslash is removed, and then cat
2402   can't open the file.  With doubling, the filename is not parsed correctly
2403   and the TYPE command fails immediately with a "file not found" error.
2404 */
2405 /*
2406   Utility routine to double all backslashes in a string.
2407   s1 is pointer to source string, s2 is pointer to destination string,
2408   n is length of destination string, both NUL-terminated.
2409   Returns 0 if OK, -1 if not OK (destination string too short).
2410 */
2411 int
2412 dblbs(s1,s2,n) char *s1, *s2; int n; {
2413     int i = 0;
2414     while (*s1) {
2415         if (*s1 == '\\') {
2416             if (++i > n) return(-1);
2417             *s2++ = '\\';
2418         }
2419         if (++i > n) return(-1);
2420         *s2++ = *s1++;
2421     }
2422     *s2 = NUL;
2423     return(0);
2424 }
2425 #endif /* COMMENT */
2426
2427 char *
2428 gmdmtyp() {                             /* Get modem type */
2429 #ifndef NODIAL
2430     int i, x;
2431
2432     debug(F111,"gmdmtyp","mdmtyp",mdmtyp);
2433     debug(F111,"gmdmtyp","mdmsav",mdmsav);
2434
2435     x = mdmtyp;
2436     if (x < 0)                          /* In case of network dialing */
2437       x = mdmsav;
2438     if (x < 1)
2439       return("none");
2440     else
2441       for (i = 0; i < nmdm; i++)
2442         if ((mdmtab[i].kwval == x) && (mdmtab[i].flgs == 0))
2443           return(mdmtab[i].kwd);
2444 #endif /* NODIAL */
2445     return("none");
2446 }
2447
2448 #ifndef NOXMIT
2449 #ifndef NOLOCAL
2450 /*  T R A N S M I T  --  Raw upload  */
2451
2452 /*  Obey current line, duplex, parity, flow, text/binary settings. */
2453 /*  Returns 0 upon apparent success, 1 on obvious failure.  */
2454
2455 /***
2456  Things to add:
2457  . Make both text and binary mode obey set file bytesize.
2458  . Maybe allow user to specify terminators other than CR?
2459  . Maybe allow user to specify prompts other than single characters?
2460  . Make STATISTICS also work for TRANSMIT.
2461  . If TRANSMIT is done without echo, make some kind of (optional) display.
2462  . Make the same optimization for binary-mode transmit that was done for
2463    text-mode (in the no-echo / no-prompt / no-pause case).
2464 ***/
2465
2466 /*  T R A N S M I T  --  Raw upload  */
2467
2468 /*  s is the filename, t is the turnaround (prompt) character  */
2469
2470 /*
2471   Maximum number of characters to buffer.
2472   Must be less than LINBUFSIZ
2473 */
2474 #ifdef OS2
2475 #define XMBUFS 4096                     /* For compatibility with XYZmodem */
2476 #else /* OS2 */
2477 #define XMBUFS 1024
2478 #endif /* OS2 */
2479
2480 #ifdef TNCODE
2481 #ifndef IAC
2482 #define IAC 255
2483 #endif /* IAC */
2484 #endif /* TNCODE */
2485
2486 #define OUTXBUFSIZ 15
2487 static CHAR inxbuf[OUTXBUFSIZ+1];       /* Host-to-screen expansion buffer */
2488 static int inxcount = 0;                /* and count */
2489 static CHAR outxbuf[OUTXBUFSIZ+1];      /* Keyboard-to-host expansion buf */
2490 static int outxcount = 0;               /* and count */
2491
2492 /*  T R A N S M I T  --  Unguarded non-protocol file transmission  */
2493 /*
2494   Call with:
2495     char * s:   Name of file to transmit.
2496     char t:     Turnaround char for text-mode transmission (normally LF).
2497     int xlate:  nonzero = charset translation for text-mode xfer, 0 = skip.
2498     int binary: nonzero = transmit in binary mode, 0 = in text mode.
2499 */
2500 #define XBBUFSIZ 508                    /* For binary blasting */
2501 static CHAR xbbuf[XBBUFSIZ+4];
2502
2503 int
2504 #ifdef CK_ANSIC
2505 transmit(char * s, char t, int xlate, int binary, int xxecho)
2506 #else
2507 transmit(s,t,xlate,binary,xxecho) char *s; char t; int xlate, binary, xxecho;
2508 #endif /* CK_ANSIC */
2509 /* transmit */ {
2510 #ifdef MAC
2511     extern char sstate;
2512     int count = 100;
2513 #else
2514     int count = 0;
2515 #ifdef OS2
2516 #ifdef NT
2517     SIGTYP (* oldsig)(int);             /* For saving old interrupt trap. */
2518 #else /* NT */
2519     SIGTYP (* volatile oldsig)(int);
2520 #endif /* NT */
2521
2522 #else /* OS2 */
2523     SIGTYP (* oldsig)();
2524 #endif /* OS2 */
2525 #endif /* MAC */
2526     int eof = 0;                        /* End of File flag */
2527     int eol = 0;                        /* End of Line flag */
2528     int rc = 1;                         /* Return code. 0=fail, 1=succeed. */
2529     int myflow;                         /* Local copy of global flow... */
2530     int is_tn = 0;                      /* Do Telnet negotiations */
2531     int xbufsiz = XMBUFS;               /* Size of TRANSMIT buffer */
2532     int x, y, c, i;                     /* Int workers... */
2533     int control = 0;                    /* Echo loop control */
2534     long nbytes = 0;                    /* File byte count */
2535     long zz;                            /* Long worker */
2536     char *p;                            /* Char * worker */
2537
2538 #ifdef PIPESEND
2539     extern int pipesend;
2540 #endif /* PIPESEND */
2541
2542 #ifndef NOCSETS
2543     int tcs = TC_TRANSP;                /* Intermediate (xfer) char set */
2544     int langsv = L_USASCII;             /* Save current language */
2545     int unicode = 0;
2546     int tcssize = 0;
2547
2548 #ifdef CK_ANSIC /* ANSI C prototypes... */
2549     CHAR (*sxo)(CHAR);
2550     CHAR (*rxo)(CHAR);
2551     CHAR (*sxi)(CHAR);
2552     CHAR (*rxi)(CHAR);
2553 #else /* Not ANSI C... */
2554     CHAR (*sxo)();
2555     CHAR (*rxo)();
2556     CHAR (*sxi)();
2557     CHAR (*rxi)();
2558 #endif /* CK_ANSIC */
2559 #ifdef UNICODE
2560     union ck_short uc;
2561     int bomorder = 0;
2562 #ifdef CK_ANSIC
2563     extern int (*xl_ufc[MAXFCSETS+1])(USHORT);  /* Unicode to FCS */
2564     extern USHORT (*xl_fcu[MAXFCSETS+1])(CHAR); /* FCS to Unicode */
2565     extern int (*xuf)(USHORT);
2566     extern USHORT (*xfu)(CHAR);
2567 #else
2568     extern int (*xl_ufc[MAXFCSETS+1])();
2569     extern USHORT (*xl_fcu[MAXFCSETS+1])();
2570     extern int (*xuf)();
2571     extern USHORT (*xfu)();
2572 #endif /* CK_ANSIC */
2573 #endif /* UNICODE */
2574 #endif /* NOCSETS */
2575
2576     debug(F101,"xmit t","",t);
2577     debug(F101,"xmit xlate","",xlate);
2578     debug(F101,"xmit binary","",binary);
2579
2580 #ifdef PIPESEND
2581     if (pipesend) {
2582         if (nopush) return(-2);
2583         if (zxcmd(ZIFILE,s) < 1) {
2584             printf("?Can't start command: %s\n",s);
2585             return(0);
2586         }
2587     } else
2588 #endif /* PIPESEND */
2589     if (zopeni(ZIFILE,s) == 0) {        /* Open the file to be transmitted */
2590         printf("?Can't open file %s\n",s);
2591         return(0);
2592     }
2593     x = -1;                             /* Open the communication channel */
2594     if (ttopen(ttname,&x,mdmtyp,cdtimo) < 0) {  /* (no harm if already open) */
2595         printf("Can't open device %s\n",ttname);
2596         return(0);
2597     }
2598     zz = x ? speed : -1L;
2599     if (binary) {                       /* Binary file transmission */
2600         myflow = (flow == FLO_XONX) ? FLO_NONE : flow;
2601
2602         if (ttvt(zz,myflow) < 0) {      /* So no Xon/Xoff! */
2603             printf("Can't condition line\n");
2604             return(0);
2605         }
2606     } else {
2607         if (ttpkt(zz,flow,parity) < 0) { /* Put the line in "packet mode" */
2608             printf("Can't condition line\n"); /* so Xon/Xoff will work, etc. */
2609             return(0);
2610         }
2611     }
2612     is_tn =
2613 #ifdef TNCODE
2614       (local && network && IS_TELNET()) || (!local && sstelnet)
2615 #else
2616         0
2617 #endif /* TNCODE */
2618           ;
2619
2620 #ifndef NOCSETS
2621 /* Set up character set translations */
2622
2623     tcs = 0;                            /* "Transfer" or "Other" charset */
2624     sxo = rxo = NULL;                   /* Initialize byte-to-byte functions */
2625     sxi = rxi = NULL;
2626     unicode = 0;                        /* Assume Unicode won't be involved */
2627     if (!binary && xlate) {             /* Set up charset translations */
2628 /*
2629   In the SENDING direction, we are converting from the local file's
2630   character-set (fcharset) to the remote terminal charset (tcsr).  In the
2631   RECEIVING direction (echoing) we are converting from the remote end of the
2632   terminal charset (tcsr) to its local end (tcsl), which is not necessarily
2633   the same as the file character-set.  Especially when the file character
2634   set is UCS-2, which is not a valid terminal character set.  The various
2635   combinations are represented in this table:
2636
2637   FCS = File Character Set
2638   RCS = Remote Terminal Character Set
2639   CCS = Console (Local Terminal) Character Set
2640
2641    8   4   2   1
2642   FCS FCS RCS CCS
2643   UCS UTF UTF UTF
2644    0   0   0   0   =   0   =   No translation
2645    0   0   0   1   =   1   =   FCS -> RCS, Echo RCS -> UTF
2646    0   0   1   0   =   2   =   FCS -> UTF, Echo UTF -> CCS
2647    0   0   1   1   =   3   =   FCS -> UTF, Echo no translation
2648
2649    0   1   0   0   =   4   =   UTF -> RCS, Echo RCS -> CCS
2650    0   1   0   1   =   5   =   UTF -> RCS, Echo RCS -> UTF
2651    0   1   1   0   =   6   =   UTF -> UTF, Echo UTF -> CCS
2652    0   1   1   1   =   7   =   No translation
2653
2654    1   0   0   0   =   8   =   UCS -> RCS, Echo RCS -> CCS
2655    1   0   0   1   =   9   =   UCS -> RCS, Echo RCS -> UTF
2656    1   0   1   0   =  10   =   UCS -> UTF, Echo UTF -> CCS
2657    1   0   1   1   =  11   =   UCS -> UTF, Echo no translation
2658 */
2659 #ifdef UNICODE
2660         xfu = NULL;                     /* Unicode translation functions */
2661         xuf = NULL;
2662         bomorder = ucsorder;            /* UCS-2 byte order */
2663
2664         if (fcharset == FC_UCS2)        /* File charset is UCS-2 */
2665           unicode |= 8;
2666         else if (fcharset == FC_UTF8)   /* File charset is UTF-8 */
2667           unicode |= 4;
2668         if (tcsr == FC_UTF8)            /* Remote term charset is UTF-8 */
2669           unicode |= 2;
2670         if (tcsl == FC_UTF8)            /* Local term charset is UTF-8 */
2671           unicode |= 1;
2672 #endif /* UNICODE */
2673 /*
2674   When Unicode not involved -- TCS is the intermediate (xfer) set, and:
2675   sxo = File-to-Intermediate charset function
2676   rxo = Intermediate-to-Remote-Terminal charset function
2677   sxi = Remote-Terminal-to-Intermediate
2678   rxi = Intermediate-to-Local-Terminal
2679 */
2680         tcs = gettcs(tcsr,fcharset);    /* Get intermediate set. */
2681         sxo = xls[tcs][fcharset];       /* translation function */
2682         rxo = xlr[tcs][tcsr];           /* pointers for output functions */
2683         sxi = xls[tcs][tcsr];           /* and for input functions. */
2684         rxi = xlr[tcs][tcsl];
2685 /*
2686   At this point we have unicode nonzero if Unicode is involved in the
2687   conversion, and to 0 if it is not.
2688   The following is to prevent use of zmstuff() and zdstuff() by translation
2689   functions (stuffing works with file i/o, not with communication i/o).
2690 */
2691         langsv = language;              /* Save current SET LANGUAGE */
2692         language = L_USASCII;           /* No language-specific translations */
2693     }
2694 #endif /* NOCSETS */
2695
2696     i = 0;                              /* Beginning of buffer. */
2697 #ifndef MAC
2698 #ifndef AMIGA
2699     oldsig = signal(SIGINT, trtrap);    /* Save current interrupt trap. */
2700 #endif /* AMIGA */
2701 #endif /* MAC */
2702     tr_int = 0;                         /* Have not been interrupted (yet). */
2703     rc = 1;                             /* Return code presumed good. */
2704 #ifdef VMS
2705     conres();
2706 #endif /* VMS */
2707
2708 #ifndef NOCSETS
2709     debug(F101,"XMIT unicode","",unicode);
2710 #ifdef UNICODE
2711     debug(F101,"XMIT bomorder","",bomorder);
2712 #endif /* UNICODE */
2713 #endif /* NOCSETS */
2714
2715     c = 0;                              /* Initial condition */
2716     while (c > -1 && !eof) {            /* Loop for all characters in file */
2717         eol = 0;
2718 #ifdef MAC
2719         /*
2720          * It is expensive to run the miniparser so don't do it for
2721          * every character.
2722          */
2723         if (--count < 0) {
2724             count = 100;
2725             miniparser(1);
2726             if (sstate == 'a') {
2727                 sstate = '\0';
2728                 goto xmitfail;
2729             }
2730         }
2731 #else /* Not MAC */
2732         if (tr_int) {                   /* Interrupted? */
2733             printf("^C...\n");          /* Print message */
2734             goto xmitfail;
2735         }
2736 #endif /* MAC */
2737         c = zminchar();                 /* Get a file character */
2738 #ifdef COMMENT
2739 /* too much */
2740 #ifdef DEBUG
2741         if (deblog) {
2742             if (c < 0)
2743               debug(F101,"XMIT zminchar","",c);
2744             else
2745               debug(F000,"XMIT zminchar","",c);
2746         }
2747 #endif /* DEBUG */
2748 #endif /* COMMENT */
2749         if (c < -1) {                   /* Other error */
2750             printf("?TRANSMIT file read error: %s\n",ck_errstr());
2751             goto xmitfail;
2752         } else if (c > -1) {
2753             nbytes++;
2754             c &= fmask;                 /* Apply SET FILE BYTESIZE mask */
2755         } else if (c == -1) {
2756             eof = 1;
2757             debug(F101,"XMIT eof","",eof);
2758         }
2759         if (binary) {                   /* Binary... */
2760             if (c == -1) {              /* If EOF */
2761                 rc = 1;                 /* Success */
2762                 eof = 1;
2763                 goto xmitexit;          /* Done */
2764             }
2765             if (!xmitw && !xxecho) {    /* Special "blast" mode */
2766                 if (count == XBBUFSIZ) { /* File input buffer full... */
2767                     while (count > 0) {
2768                         errno = 0;
2769                         y = ttol(xbbuf,count);
2770                         if (y < 0) {    /* try to send it. */
2771                             printf("?TRANSMIT output error: %s\n",
2772                                    ck_errstr());
2773                             debug(F111,"XMIT binary ttol error",
2774                                   ck_errstr(),errno);
2775                             rc = 0;
2776                             break;
2777                         }
2778                         if (y < 0) break;
2779                         count -= y;
2780                     }
2781                     count = 0;
2782                 }
2783                 xbbuf[count++] = c;
2784 #ifdef TNCODE
2785                 if (c == IAC && is_tn)  /* Telnet IAC */
2786                   xbbuf[count++] = IAC; /* must be doubled */
2787 #endif /* TNCODE */
2788                 continue;
2789             }
2790             if (ttoc(dopar((char) c)) < 0) { /* else just send the char */
2791                 printf("?Can't transmit character\n");
2792                 goto xmitfail;
2793             }
2794 #ifdef TNCODE
2795             if (c == IAC && is_tn)      /* Quote Telnet IAC */
2796               ttoc((char)IAC);
2797 #endif /* TNCODE */
2798
2799             if (xmitw)                  /* Pause if requested */
2800               msleep(xmitw);
2801
2802             if (xxecho) {               /* SET TRANSMIT ECHO ON? */
2803                 if (duplex) {           /* Yes, for half duplex */
2804 #ifndef NOLOCAL
2805 #ifdef OS2
2806                     /* Echo to emulator */
2807                     scriptwrtbuf((USHORT)(c & cmdmsk));
2808 #endif /* OS2 */
2809 #endif /* NOLOCAL */
2810                     if (conoc((char)(c & cmdmsk)) < 0) /* echo locally. */
2811                       goto xmitfail;
2812                 } else {                /* For full duplex, */
2813                     int i, n;           /* display whatever is there. */
2814                     n = ttchk();        /* See how many chars are waiting */
2815                     if (n < 0) {        /* Connection dropped? */
2816                         printf("?Connection lost\n");
2817                         goto xmitfail;
2818                     }
2819                     for (i = 0; i < n; i++) { /* Read and echo that many. */
2820                         x = ttinc(xmitt); /* Timed read just in case. */
2821                         if (x > -1) {   /* If no timeout */
2822                             if (parity) x &= 0x7f; /* display the char, */
2823 #ifndef NOLOCAL
2824 #ifdef OS2
2825                             /* Echo to emulator */
2826                             scriptwrtbuf((USHORT)x);
2827 #endif /* OS2 */
2828 #endif /* NOLOCAL */
2829                             if (conoc((char)(x & cmdmsk)) < 0) {
2830                                 printf("?Output error\n");
2831                                 goto xmitfail;
2832                             }
2833                         } else if (x == -2) {
2834                             printf("Connection closed.\n");
2835                             ttclos(1);
2836                             goto xmitfail;
2837                         } else if (x == -3) {
2838                             printf(
2839                             "Session Limit exceeded - closing connection.\n"
2840                                    );
2841                             ttclos(1);
2842                             goto xmitfail;
2843                         } else {
2844                             printf("?Communications error\n");
2845                             goto xmitfail;
2846                         }
2847                     }
2848                 }
2849             } else ttflui();            /* Not echoing, just flush input. */
2850
2851         } else {                        /* Text mode, line at a time. */
2852 #ifdef UNICODE
2853             if (fcharset == FC_UCS2 && xlate) { /* Special for UCS-2 */
2854                 char xbuf[8];
2855                 x = 1 - (nbytes & 1);   /* Odd or even byte */
2856                 if (x == 0)             /* Note: 1 = the 1st, 0 = 2nd, etc */
2857                   uc.x_short = 0;
2858                 if (bomorder)           /* Little Endian */
2859                   x = 1 - x;            /* Save byte in appropriate half */
2860                 debug(F101,"XMIT UCS2 x","",x);
2861                 uc.x_char[x] = (CHAR) (c & 0xff);
2862                 if (nbytes & 1)         /* First byte, go back for next */
2863                   continue;
2864                 if (nbytes == 2) {      /* UCS-2 Byte Order Mark */
2865                     if (uc.x_short == (USHORT) 0xfeff) {
2866                         debug(F100,"XMIT UCS2 BOM FEFF","",bomorder);
2867                         continue;
2868                     } else if (uc.x_short == (USHORT) 0xfffe) {
2869                         bomorder = 1 - bomorder;
2870                         debug(F100,"XMIT UCS2 BOM FFFE (swap)","",bomorder);
2871                         continue;
2872                     }
2873                 }
2874                 sprintf(xbuf,"%04X",uc.x_short); /* SAFE */
2875                 debug(F111,"XMIT UCS2",xbuf,uc.x_short);
2876                 if (nbytes & 1)         /* Special eol test for UCS-2 */
2877                   if (uc.x_short == '\n')
2878                     eol = 1;
2879 #ifdef COMMENT
2880                 if (uc.x_short == 0x2028 || uc.x_short == 0x2029)
2881                     eol = 1;
2882 #endif /* COMMENT */
2883             } else
2884 #endif /* UNICODE */
2885               if (c == '\n') {          /* Normal eol test otherwise */
2886                   eol = 1;
2887             }
2888             if (eol) {                  /* End of line? */
2889                 int stuff = -1;
2890                 debug(F101,"XMIT eol length","",i);
2891                 if (i == 0) {           /* Blank line? */
2892                     if (xmitf)          /* Yes, insert fill if asked. */
2893                       line[i++] = dopar((char) xmitf);
2894                 }
2895                 if (i == 0 || ((char) line[i-1]) != ((char) dopar(CR)))
2896                   line[i++] = dopar(CR); /* Terminate it with CR */
2897                 if (xmitl) {
2898                     stuff = LF;
2899 #ifdef TNCODE
2900                 } else if (is_tn && (tn_nlm != TNL_CR)) {
2901                     /* TELNET NEWLINE ON/OFF/RAW */
2902                     stuff = (tn_nlm == TNL_CRLF) ? LF : NUL;
2903 #endif /* TNCODE */
2904                 }
2905                 if (stuff > -1)
2906                   line[i++] = dopar((char)stuff);
2907                 line[i] = NUL;
2908                 debug(F111,"XMIT eol line",line,i);
2909
2910             } else if (c != -1) {       /* Not a newline, regular character */
2911                 int k, x;
2912                 outxbuf[0] = c;         /* In case of no translation */
2913                 outxcount = 1;          /* Assume result is one byte */
2914 #ifndef NOCSETS
2915                 switch (unicode) {
2916                   case 0:               /* No Unicode involved */
2917                   case 1:
2918                     if (xlate) {        /* If not /TRANSPARENT */
2919                         /* Local-to-intermediate */
2920                         if (sxo) c = (*sxo)((char)c);
2921                         /* Intermediate-to-remote */
2922                         if (rxo) c = (*rxo)((char)c);
2923                         outxbuf[0] = c;
2924                     }
2925                     break;
2926 #ifdef UNICODE
2927                   case 2:               /* Local byte to UTF-8 */
2928                   case 3:
2929                     xfu = xl_fcu[fcharset];
2930                     tcssize = fcsinfo[fcharset].size;
2931                     outxcount =
2932                       b_to_u((CHAR)c,outxbuf,OUTXBUFSIZ,tcssize);
2933                     break;
2934                   case 4:               /* Local UTF-8 to remote byte */
2935                   case 5:
2936                     xuf = xl_ufc[tcsr];
2937                     x = u_to_b((CHAR)c); /* Convert to byte */
2938                     if (x == -1) {      /* If more input bytes needed */
2939                         continue;       /* go back and get them */
2940                     } else if (x == -2) { /* LS or PS (shouldn't happen) */
2941                         outxbuf[0] = CR;
2942                     } else if (x == -9) { /* UTF-8 error */
2943                         outxbuf[0] = '?'; /* Insert error char */
2944                         outxbuf[1] = u_to_b2(); /* Insert next char */
2945                         outxcount = 2;
2946                     } else {
2947                         outxbuf[0] =    /* Otherwise store result */
2948                           (unsigned)(x & 0xff);
2949                     }
2950                     break;
2951                   case 6:               /* UTF-8 to UTF-8 */
2952                   case 7:
2953                     break;
2954                   case 8:               /* UCS-2 to byte */
2955                   case 9:
2956                     xuf = xl_ufc[tcsr];
2957                     outxbuf[0] = (*xuf)(uc.x_short);
2958                     break;
2959                   case 10:
2960                   case 11: {            /* UCS-2 to UTF-8 */
2961                       int j;
2962                       CHAR * buf = NULL;
2963                       x = ucs2_to_utf8(uc.x_short,&buf);
2964                       if (x < 0) {
2965                           outxbuf[0] = 0xff; /* (= U+FFFD) */
2966                           outxbuf[1] = 0xbd;
2967                           x = 2;
2968                       }
2969                       for (j = 0; j < x; j++)
2970                         outxbuf[j] = buf[j];
2971                       outxcount = x;
2972                       break;
2973                   }
2974 #endif /* UNICODE */
2975                 }
2976 #endif /* NOCSETS */
2977                 outxbuf[outxcount] = NUL;
2978                 debug(F111,"XMIT outxbuf",outxbuf,outxcount);
2979 /*
2980   Now the input character (1 or more bytes) is translated into the output
2981   expansion buffer (1 or more bytes); outxcount = number of bytes to add to
2982   the TRANSMIT line buffer, which we do here, taking care of parity, SI/SO
2983   processing, and quoting Telnet IACs.
2984 */
2985                 for (k = 0; k < outxcount; k++) {
2986                     c = outxbuf[k];
2987                     if (xmits && parity && (c & 0200)) { /* If shifting */
2988                         line[i++] = dopar(SO); /* needs to be done, */
2989                         line[i++] = dopar((char)c); /* do it here, */
2990                         line[i++] = dopar(SI); /* crudely. */
2991                     } else {
2992                         line[i++] = dopar((char)c);
2993 #ifdef TNCODE
2994                         if (c == (CHAR)IAC && is_tn)
2995                           line[i++] = (CHAR)IAC;
2996 #endif /* TNCODE */
2997                     }
2998                 }
2999             }
3000 /*
3001   Send characters if buffer full, or at end of line, or at end of file.
3002   (End of line only if echoing, waiting for a prompt, or pausing.)
3003 */
3004             debug(F000,"XMIT c",ckitoa(i),c);
3005             if (i >= xbufsiz || eof || (eol && (xxecho || xmitw || t))) {
3006                 p = line;
3007                 line[i] = '\0';
3008                 debug(F111,"transmit buf",p,i);
3009                 if (ttol((CHAR *)p,i) < 0) { /* try to send it. */
3010                     printf("?TRANSMIT output error: %s\n",ck_errstr());
3011                     rc = 0;
3012                     break;
3013                 }
3014                 i = 0;                  /* Reset buffer pointer. */
3015 /*
3016   Now we handle the echo.  If the user wants to see it, or if we have to
3017   wait for the turnaround character, t.  If the echo is being displayed,
3018   and terminal character-set translation is required, we do it here.
3019 */
3020                 if (duplex && xxecho) {  /* If local echo, echo it */
3021                     if (parity || cmdmsk == 0x7f) { /* Strip hi bits */
3022                         char *ss = line;             /* if necessary */
3023                         while (*ss) {
3024                             *ss &= 0x7f;
3025                             ss++;
3026                         }
3027                     }
3028 #ifndef NOLOCAL
3029 #ifdef OS2
3030                     {                   /* Echo to emulator */
3031                         char *ss = p;
3032                         while (*ss) {
3033                             scriptwrtbuf((USHORT)*ss);
3034                             ss++;
3035                         }
3036                     }
3037 #endif /* OS2 */
3038 #endif /* NOLOCAL */
3039                     if (conoll(p) < 0)
3040                       goto xmitfail;
3041                 }
3042                 if (xmitw)              /* Sleep TRANSMIT PAUSE interval */
3043                   msleep(xmitw);
3044
3045                 control = 0;            /* Readback loop control */
3046                 if (t != 0 && eol)      /* TRANSMIT PROMPT given and at EOL */
3047                   control |= 1;
3048                 if (xxecho && !duplex)   /* Echo desired and is remote */
3049                   control |= 2;
3050
3051                 if (control) {          /* Do this if reading back the echo */
3052                     int n;
3053                     x = 0;
3054                     while (1) {
3055                         if (control & 1) { /* Termination criterion */
3056                             if (x == t)    /* for turnaround */
3057                               break;
3058                         } else if (control & 2) { /* And for echoing */
3059                             if ((n = ttchk()) < 1)
3060                               break;
3061                         }
3062                         if ((x = ttinc(xmitt)) < 0) { /* Read with timeout */
3063                             switch (x) {
3064                               case -2:
3065                                 printf("Connection closed.\n");
3066                                 ttclos(1);
3067                                 goto xmitfail;
3068                               case -3:
3069                                 printf(
3070                               "Session Limit exceeded - closing connection.\n"
3071                                        );
3072                                 ttclos(1); /* full thru... */
3073                                 goto xmitfail;
3074                               default:
3075                                 printf("?Timeout\n");
3076                                 goto xmitfail;
3077                             }
3078                         }
3079                         if (x > -1 && (control & 2)) { /* Echo any echoes */
3080                             if (parity)
3081                               x &= 0x7f;
3082                             c = x;
3083 #ifndef NOLOCAL
3084 #ifdef OS2
3085                             scriptwrtbuf((USHORT)x);
3086 #endif /* OS2 */
3087 #endif /* NOLOCAL */
3088                             inxbuf[0] = c;
3089                             inxcount = 1;
3090 #ifndef NOCSETS
3091                             switch (unicode & 3) { /* Remote bits */
3092                               case 0:
3093                                 if (xlate) {
3094                                     if (sxi) c = (*sxi)((CHAR)c);
3095                                     if (rxi) c = (*rxi)((CHAR)c);
3096                                     inxbuf[0] = c;
3097                                 }
3098                                 break;
3099 #ifdef UNICODE
3100                               case 1:   /* Remote Byte to local UTF-8 */
3101                                 xfu = xl_fcu[tcsr];
3102                                 tcssize = fcsinfo[tcsr].size;
3103                                 inxcount =
3104                                   b_to_u((CHAR)c,
3105                                          inxbuf,
3106                                          OUTXBUFSIZ,
3107                                          tcssize
3108                                          );
3109                                 break;
3110                               case 2:   /* Remote UTF-8 to local Byte */
3111                                 xuf = xl_ufc[tcsl];
3112                                 x = u_to_b((CHAR)c);
3113                                 if (x < 0)
3114                                   continue;
3115                                 inxbuf[0] = (unsigned)(x & 0xff);
3116                                 break;
3117                               case 3:   /* UTF-8 to UTF-8 */
3118                                 break;
3119 #endif /* UNICODE */
3120                             }
3121 #endif /* NOCSETS */
3122                             inxbuf[inxcount] = NUL;
3123                             if (conxo(inxcount,(char *)inxbuf) < 0)
3124                               goto xmitfail;
3125                         }
3126                     }
3127                 } else                  /* Not echoing */
3128                   ttflui();             /* Just flush input buffer */
3129             } /* End of buffer-dumping block */
3130         } /* End of text mode */
3131         if (eof) {
3132             rc = 1;
3133             goto xmitexit;
3134         }
3135     } /* End of character-reading loop */
3136
3137   xmitfail:                             /* Failure exit point */
3138     rc = 0;
3139
3140   xmitexit:                             /* General exit point */
3141     if (rc > 0) {
3142         if (binary && !xmitw && !xxecho) { /* "blasting"? */
3143             while (count > 0) {            /* Partial buffer still to go? */
3144                 errno = 0;
3145                 y = ttol(xbbuf,count);
3146                 if (y < 0) {
3147                     printf("?TRANSMIT output error: %s\n",
3148                            ck_errstr());
3149                     debug(F111,"XMIT binary eof ttol error",
3150                           ck_errstr(),errno);
3151                     rc = 0;
3152                     break;
3153                 }
3154                 count -= y;
3155             }
3156         } else if (!binary && *xmitbuf) { /* Anything to send at EOF? */
3157             p = xmitbuf;                /* Yes, point to string. */
3158             while (*p)                  /* Send it. */
3159               ttoc(dopar(*p++));        /* Don't worry about echo here. */
3160         }
3161     }
3162
3163 #ifndef AMIGA
3164 #ifndef MAC
3165     signal(SIGINT,oldsig);              /* Put old signal action back. */
3166 #endif /* MAC */
3167 #endif /* AMIGA */
3168 #ifdef VMS
3169     concb(escape);                      /* Put terminal back, */
3170 #endif /* VMS */
3171     zclose(ZIFILE);                     /* Close file, */
3172 #ifndef NOCSETS
3173     language = langsv;                  /* restore language, */
3174 #endif /* NOCSETS */
3175     ttres();                            /* and terminal modes, */
3176     return(rc);                         /* and return successfully. */
3177 }
3178 #endif /* NOLOCAL */
3179 #endif /* NOXMIT */
3180
3181 #ifndef NOCSETS
3182
3183 _PROTOTYP( CHAR (*sxx), (CHAR) );       /* Local translation function */
3184 _PROTOTYP( CHAR (*rxx), (CHAR) );       /* Local translation function */
3185 _PROTOTYP( CHAR zl1as, (CHAR) );        /* Latin-1 to ascii */
3186 _PROTOTYP( CHAR xl1as, (CHAR) );        /* ditto */
3187
3188 /*  X L A T E  --  Translate a local file from one character set to another */
3189
3190 /*
3191   Translates input file (fin) from character set csin to character set csout
3192   and puts the result in the output file (fout).  The two character sets are
3193   file character sets from fcstab.
3194 */
3195
3196 int
3197 xlate(fin, fout, csin, csout) char *fin, *fout; int csin, csout; {
3198
3199 #ifndef MAC
3200 #ifdef OS2
3201     extern int k95stdout;
3202     extern int wherex[], wherey[];
3203     extern unsigned char colorcmd;
3204 #ifdef NT
3205     SIGTYP (* oldsig)(int);             /* For saving old interrupt trap. */
3206 #else /* NT */
3207     SIGTYP (* volatile oldsig)(int);    /* For saving old interrupt trap. */
3208 #endif /* NT */
3209 #else /* OS2 */
3210     SIGTYP (* oldsig)();
3211 #endif /* OS2 */
3212 #endif /* MAC */
3213 #ifdef CK_ANSIC
3214     int (*fn)(char);                    /* Output function pointer */
3215 #else
3216     int (*fn)();
3217 #endif /* CK_ANSIC */
3218     extern int xlatype;
3219     int filecode;                       /* Code for output file */
3220     int scrnflg = 0;
3221
3222     int z = 1;                          /* Return code. */
3223     int x, c, c2;                       /* Workers */
3224 #ifndef UNICODE
3225     int tcs;
3226 #endif /* UNICODE */
3227
3228     ffc = 0L;
3229
3230     if (zopeni(ZIFILE,fin) == 0) {      /* Open the file to be translated */
3231 #ifdef COMMENT
3232         /* An error message was already printed by zopeni() */
3233         printf("?Can't open input file %s\n",fin);
3234 #endif /* COMMENT */
3235         return(0);
3236     }
3237 #ifdef MAC
3238 /*
3239   If user specified no output file, it goes to the screen.  For the Mac,
3240   this must be done a special way (result goes to a new window); the Mac
3241   doesn't have a "controlling terminal" device name.
3242 */
3243     filecode = !strcmp(fout,CTTNAM) ? ZCTERM : ZOFILE;
3244 #else
3245 #ifdef VMS
3246     filecode = !strcmp(fout,CTTNAM) ? ZCTERM : ZMFILE;
3247 #else
3248 #ifdef OS2
3249     filecode = (!stricmp(fout,"con") || !stricmp(fout,"con:")) ?
3250         ZCTERM : ZMFILE;
3251     if ((filecode == ZCTERM) && !k95stdout && !inserver)
3252         csout = FC_UCS2;
3253 #else /* OS2 */
3254     filecode = ZOFILE;
3255 #endif /* OS2 */
3256 #endif /* VMS */
3257 #endif /* MAC */
3258     if (zopeno(filecode,fout,NULL,NULL) == 0) { /* And the output file */
3259         printf("?Can't open output file %s\n",fout);
3260         return(0);
3261     }
3262 #ifndef AMIGA
3263 #ifndef MAC
3264     oldsig = signal(SIGINT, trtrap);    /* Save current interrupt trap. */
3265 #endif /* MAC */
3266 #endif /* AMIGA */
3267
3268     scrnflg = (filecode == ZCTERM);     /* Set output function */
3269     if (scrnflg)
3270       fn = NULL;
3271     else if (filecode == ZMFILE)
3272       fn = putmfil;
3273     else
3274       fn = putfil;
3275
3276     tr_int = 0;                         /* Have not been interrupted (yet). */
3277     z = 1;                              /* Return code presumed good. */
3278
3279     if (!scrnflg && !quiet)
3280       printf(" %s (%s) => %s (%s)\n",   /* Say what we're doing. */
3281              fin, fcsinfo[csin].keyword,
3282              fout,fcsinfo[csout].keyword
3283              );
3284
3285 #ifndef UNICODE
3286 /*
3287   Non-Unicode picks the "most appropriate" transfer character set as the
3288   intermediate set, which results in loss of any characters that the source
3289   and target sets have in common, but are lacking from the intermediate set.
3290 */
3291 #ifdef KANJI
3292     /* Special handling for Japanese... */
3293
3294     if (fcsinfo[csin].alphabet == AL_JAPAN ||
3295          fcsinfo[csout].alphabet == AL_JAPAN) {
3296         USHORT eu;
3297         int c, x, y;
3298
3299         xpnbyte(-1,0,0,NULL);           /* Reset output machine */
3300         xlatype = XLA_JAPAN;
3301
3302         while ((c = xgnbyte(FC_JEUC,csin,NULL)) > -1) { /* Get an EUC byte */
3303             if (tr_int) {               /* Interrupted? */
3304                 printf("^C...\n");      /* Print message */
3305                 z = 0;
3306                 break;
3307             }
3308             /* Send EUC byte to output machine */
3309             if ((x = xpnbyte(c,TC_JEUC,csout,fn)) < 0) {
3310                 z = -1;
3311                 break;
3312             }
3313         }
3314         goto xxlate;
3315     }
3316 #endif /* KANJI */
3317
3318     /* Regular bytewise conversion... */
3319
3320     tcs = gettcs(csin,csout);           /* Get intermediate set. */
3321     if (csin == csout) {                /* Input and output sets the same? */
3322         sxx = rxx = NULL;               /* If so, no translation. */
3323     } else {                            /* Otherwise, set up */
3324         if (tcs < 0 || tcs > MAXTCSETS ||
3325             csin < 0 || csin > MAXFCSETS ||
3326             csout < 0 || csout > MAXFCSETS) {
3327             debug(F100,"XLATE csets out of range","",0);
3328             sxx = rxx = NULL;
3329         } else {
3330             sxx = xls[tcs][csin];       /* translation function */
3331             rxx = xlr[tcs][csout];      /* pointers. */
3332             if (rxx == zl1as) rxx = xl1as;
3333         }
3334     }
3335     while ((c = zminchar()) != -1) { /* Loop for all characters in file */
3336         if (tr_int) {                   /* Interrupted? */
3337             printf("^C...\n");          /* Print message */
3338             z = 0;
3339             break;
3340         }
3341         if (sxx) c = (*sxx)((CHAR)c);   /* From fcs1 to tcs */
3342         if (rxx) c = (*rxx)((CHAR)c);   /* from tcs to fcs2 */
3343         if (zchout(filecode,(char)c) < 0) { /* Output xlated character */
3344             z = -1;
3345             break;
3346         }
3347     }
3348     goto xxlate;                        /* Done. */
3349
3350 #else  /* UNICODE */
3351 /*
3352    Use Unicode as the intermediate character set.  It's simple and gives
3353    little or no loss, but the overhead is a bit higher.
3354 */
3355     initxlate(csin,csout);              /* Set up translation functions */
3356
3357     if (xlatype == XLA_NONE) {
3358         while ((c = zminchar()) != -1) { /* Loop for all characters in file */
3359             if (tr_int) {               /* Interrupted? */
3360                 printf("^C...\n");      /* Print message */
3361                 z = 0;
3362                 break;
3363             }
3364             if (zchout(filecode,(char)c) < 0) { /* Output xlated character */
3365                 z = -1;
3366                 break;
3367             }
3368         }
3369         goto xxlate;                    /* Done. */
3370     }
3371
3372
3373 #ifndef NOLOCAL
3374 #ifdef OS2
3375     if (csout == FC_UCS2 &&             /* we're translating to UCS-2 */
3376         filecode == ZCTERM &&           /* for the real screen... */
3377         !k95stdout && !inserver
3378         ) {
3379         union {
3380             USHORT ucs2;
3381             UCHAR  bytes[2];
3382         } output;
3383
3384         while (1) {                     /* In this case we go two-by-two. */
3385             if ((c = xgnbyte(FC_UCS2,csin,NULL)) < 0)
3386               break;
3387             output.bytes[0] = c;
3388             if ((c = xgnbyte(FC_UCS2,csin,NULL)) < 0)
3389               break;
3390             output.bytes[1] = c;
3391
3392             if (tr_int) {               /* Interrupted? */
3393                 printf("^C...\n");      /* Print message */
3394                 z = 0;
3395                 break;
3396             }
3397
3398             VscrnWrtUCS2StrAtt(VCMD,
3399                                &output.ucs2,
3400                                1,
3401                                wherey[VCMD],
3402                                wherex[VCMD],
3403                                &colorcmd
3404                                );
3405         }
3406     } else
3407 #endif /* OS2 */
3408 #endif /* NOLOCAL */
3409
3410       /* General case: Get next byte translated from fcs to UCS-2 */
3411
3412 #ifdef COMMENT
3413       while ((c = xgnbyte(FC_UCS2,csin,NULL)) > -1 &&
3414               (c2 = xgnbyte(FC_UCS2,csin,NULL)) > -1) {
3415           extern int fileorder;
3416
3417           if (tr_int) {                 /* Interrupted? */
3418               printf("^C...\n");        /* Print message */
3419               z = 0;
3420               break;
3421           }
3422           debug(F001,"XLATE c","",c);
3423           debug(F001,"XLATE c2","",c2);
3424
3425           /* And then send UCS-2 byte to translate-and-output machine */
3426
3427           if ((x = xpnbyte(fileorder?c2:c,TC_UCS2,csout,fn)) < 0) {
3428               z = -1;
3429               break;
3430           }
3431           if ((x = xpnbyte(fileorder?c:c2,TC_UCS2,csout,fn)) < 0) {
3432               z = -1;
3433               break;
3434           }
3435       }
3436 #else
3437     while ((c = xgnbyte(FC_UCS2,csin,NULL)) > -1) {
3438           if (tr_int) {                 /* Interrupted? */
3439               printf("^C...\n");        /* Print message */
3440               z = 0;
3441               break;
3442           }
3443           if ((x = xpnbyte(c,TC_UCS2,csout,fn)) < 0) {
3444               z = -1;
3445               break;
3446           }
3447       }
3448 #endif /* COMMENT */
3449
3450 #endif /* UNICODE */
3451
3452   xxlate:                               /* Common exit point */
3453
3454 #ifndef AMIGA
3455 #ifndef MAC
3456     signal(SIGINT,oldsig);              /* Put old signal action back. */
3457 #endif /* MAC */
3458 #endif /* AMIGA */
3459     tr_int = 0;
3460     if (z < 0) {
3461         if (z == -1)
3462           printf("?File output error: %s\n",ck_errstr());
3463         z = 0;
3464     }
3465     zclose(ZIFILE);                     /* Close files */
3466     zclose(filecode);                   /* ... */
3467     return(success = z);                /* and return status. */
3468 }
3469
3470 int
3471 doxlate() {
3472 #ifdef OS2ONLY
3473     extern int tt_font;
3474 #endif /* OS2ONLY */
3475 #ifdef UNIX
3476     extern char ** mtchs;               /* zxpand() file list */
3477 #endif /* UNIX */
3478     int x, y, incs, outcs, multiple = 0, wild = 0, fc = 0, len = 0;
3479     int ofisdir = 0;
3480     char * s, * tocs = "";
3481
3482     if ((x = cmifi("File(s) to translate","",&s,&wild,xxstring)) < 0) {
3483         if (x == -3) {
3484             printf("?Name of an existing file\n");
3485             return(-9);
3486         } else
3487           return(x);
3488     }
3489     ckstrncpy(line,s,LINBUFSIZ);        /* Save copy of string just parsed. */
3490
3491     if ((incs = cmkey(fcstab,nfilc,"from character-set","",xxstring)) < 0)
3492       return(incs);
3493
3494 #ifdef OS2
3495     if (isunicode())
3496       tocs = "ucs2";
3497     else
3498 #endif /* OS2 */
3499       tocs = getdcset();
3500
3501     if ((outcs = cmkey(fcstab,nfilc,"to character-set",tocs,xxstring)) < 0)
3502       return(outcs);
3503     if ((x = cmofi("output file",CTTNAM,&s,xxstring)) < 0) return(x);
3504     if (x > 1)
3505       ofisdir = 1;
3506
3507     len = ckstrncpy(tmpbuf,s,TMPBUFSIZ);
3508     if ((y = cmcfm()) < 0) return(y);   /* Confirm the command */
3509
3510     if (len < 1)
3511       return(-2);
3512
3513     if (ofisdir)
3514       multiple = 2;
3515     else if (wild) {
3516         if (isdir(tmpbuf))
3517           multiple = 2;
3518         else if (!strcmp(tmpbuf,CTTNAM))
3519           multiple = 1;
3520 #ifdef OS2
3521         else if (!stricmp(tmpbuf,"con") || !stricmp(tmpbuf,"con:"))
3522           multiple = 1;
3523 #else
3524 #ifdef UNIXOROSK
3525         else if (!strncmp(tmpbuf,"/dev/",4))
3526           multiple = 1;
3527 #endif /* UNIXOROSK */
3528 #endif /* OS2 */
3529         if (!multiple) {
3530             printf("?A single file please\n");
3531             return(-9);
3532         }
3533     }
3534     if (!multiple) {                    /* Just one file */
3535         return(success = xlate(line,tmpbuf,incs,outcs));
3536     } else {                            /* Translate multiple files */
3537         char dirbuf[CKMAXPATH+4];
3538         int k;
3539 #ifndef ZXREWIND
3540         int flags = ZX_FILONLY;
3541 #endif /* ZXREWIND */
3542
3543         if (multiple == 2) {            /* Target is a directory */
3544             k = ckstrncpy(dirbuf,tmpbuf,CKMAXPATH+1) - 1;
3545             if (k < 0)
3546               return(-2);
3547 #ifdef OS2ORUNIX
3548             if (dirbuf[k] != '/') {
3549                 dirbuf[k+1] = '/';
3550                 dirbuf[k+2] = NUL;
3551             }
3552 #else
3553 #ifdef OSK
3554             if (dirbuf[k] != '/') {
3555                 dirbuf[k+1] = '/';
3556                 dirbuf[k+2] = NUL;
3557             }
3558 #else
3559 #ifdef VMS
3560             if (ckmatch("*.DIR;1",s,0,0))
3561               k = cvtdir(tmpbuf,dirbuf,TMPBUFSIZ);
3562             if (dirbuf[k] != ']' &&
3563                 dirbuf[k] != '>' &&
3564                 dirbuf[k] != ':')
3565               return(-2);
3566 #else
3567 #ifdef datageneral
3568             if (dirbuf[k] != ':') {
3569                 dirbuf[k+1] = ':';
3570                 dirbuf[k+2] = NUL;
3571             }
3572 #else
3573 #ifdef STRATUS
3574             if (dirbuf[k] != '>') {
3575                 dirbuf[k+1] = '>';
3576                 dirbuf[k+2] = NUL;
3577             }
3578 #endif /* STRATUS */
3579 #endif /* datageneral */
3580 #endif /* VMS */
3581 #endif /* OSK */
3582 #endif /* OS2ORUNIX */
3583         }
3584
3585 #ifdef ZXREWIND
3586         fc = zxrewind();                /* Rewind the file list */
3587 #else
3588         if (matchdot)  flags |= ZX_MATCHDOT;
3589         fc = nzxpand(line,flags);
3590 #endif /* ZXREWIND */
3591
3592         if (fc < 1) {
3593             printf("?Wildcard expansion error\n");
3594             return(-9);
3595         }
3596 #ifdef UNIX
3597         sh_sort(mtchs,NULL,fc,0,0,filecase); /* Sort the file list */
3598 #endif /* UNIX */
3599
3600         while (1) {                     /* Loop through the files */
3601             znext(line);
3602             if (!line[0])
3603               break;
3604             if (multiple == 2)
3605               ckmakmsg(tmpbuf,TMPBUFSIZ,dirbuf,line,NULL,NULL);
3606             if (xlate(line,tmpbuf,incs,outcs) < 1)
3607               return(success = 0);
3608         }
3609     }
3610     return(success = 1);
3611 }
3612 #endif /* NOCSETS */
3613
3614 static char hompthbuf[CKMAXPATH+1];
3615
3616 char *
3617 homepath() {
3618     int x;
3619     extern char * myhome;
3620     char * h;
3621
3622     h = myhome ? myhome : zhome();
3623     hompthbuf[0] = NUL;
3624 #ifdef UNIXOROSK
3625     x = ckstrncpy(hompthbuf,h,CKMAXPATH+1);
3626     if (x <= 0) {
3627         hompthbuf[0] = '/';
3628         hompthbuf[1] = NUL;
3629     } else if (x < CKMAXPATH - 2 && hompthbuf[x-1] != '/') {
3630         hompthbuf[x] = '/';
3631         hompthbuf[x+1] = NUL;
3632     }
3633     return(hompthbuf);
3634 #else
3635 #ifdef STRATUS
3636     if (strlen(h) < CKMAXPATH)
3637       sprintf(hompthbuf,"%s>",h);       /* SAFE */
3638     return(hompthbuf);
3639 #else
3640     return(h);
3641 #endif /* STRATUS */
3642 #endif /* UNIXOROSK */
3643 }
3644
3645 /*  D O L O G  --  Do the log command  */
3646
3647 int
3648 dolog(x) int x; {
3649     int y, disp; char *s = NULL, * p = NULL, * q = NULL;
3650     extern int isguest;
3651 #ifdef ZFNQFP
3652     struct zfnfp * fnp;
3653 #endif /* ZFNQFP */
3654
3655     if (isguest) {
3656         printf("?Anonymous log creation not allowed\n");
3657         return(-9);
3658     }
3659     switch (x) {                        /* Which log... */
3660
3661 #ifdef DEBUG
3662       case LOGD:
3663         q = "debug.log";
3664         y = cmofi("Name of debugging log file",q,&s,xxstring);
3665         break;
3666 #endif /* DEBUG */
3667
3668       case LOGP:
3669         q = "packet.log";
3670         y = cmofi("Name of packet log file",q,&s,xxstring);
3671         break;
3672
3673 #ifndef NOLOCAL
3674       case LOGS:
3675         q = "session.log";
3676         y = cmofi("Name of session log file",q,&s,xxstring);
3677         break;
3678 #endif /* NOLOCAL */
3679
3680 #ifdef TLOG
3681       case LOGT:
3682         q = "transact.log";
3683         y = cmofi("Name of transaction log file",q,&s,xxstring);
3684         break;
3685 #endif /* TLOG */
3686
3687 #ifdef CKLOGDIAL
3688       case LOGM: {
3689           int m, n;
3690           char mypath[CKMAXPATH+1];
3691           q = CXLOGFILE;
3692           m = ckstrncpy(mypath,homepath(),CKMAXPATH);
3693           n = strlen(CXLOGFILE);
3694           if (m + n < CKMAXPATH)
3695             ckstrncat(mypath,CXLOGFILE,CKMAXPATH);
3696           else
3697             ckstrncpy(mypath,CXLOGFILE,CKMAXPATH);
3698           y = cmofi("Name of connection log file",mypath,&s,xxstring);
3699           break;
3700       }
3701 #endif /* CKLOGDIAL */
3702
3703       default:
3704         printf("\n?Unknown log designator - %d\n",x);
3705         return(-2);
3706     }
3707     if (y < 0) return(y);
3708     if (y == 2) {                       /* If they gave a directory name */
3709         int k;
3710         char * ds = "/";
3711         k = strlen(s);
3712         if (k > 0 && s[k-1] == '/') ds = "";
3713         ckmakmsg(tmpbuf,TMPBUFSIZ,s,ds,q,NULL);
3714         s = tmpbuf;
3715     }
3716 #ifdef ZFNQFP
3717 #ifdef OS2ORUNIX
3718     if (*s != '|')                      /* Allow for pipes */
3719 #else
3720 #ifdef OSK
3721     if (*s != '|')
3722 #endif /* OSK */
3723 #endif /* OS2ORUNIX */
3724       if ((fnp = zfnqfp(s,TMPBUFSIZ - 1,tmpbuf))) {
3725           if (fnp->fpath)
3726             if ((int) strlen(fnp->fpath) > 0)
3727               s = fnp->fpath;
3728       } /* else if error keep original string */
3729 #endif /* ZFNQFP */
3730
3731     ckstrncpy(line,s,LINBUFSIZ);
3732     s = line;
3733 #ifdef MAC
3734     y = 0;
3735 #else
3736
3737     p = "new";
3738 #ifdef TLOG
3739     if ((x == LOGT && tlogfmt == 2) || x == LOGM)
3740       p = "append";
3741 #endif /* TLOG */
3742
3743     if ((y = cmkey(disptb,2,"Disposition",p,xxstring)) < 0)
3744       return(y);
3745 #endif /* MAC */
3746     disp = y;
3747     if ((y = cmcfm()) < 0) return(y);
3748
3749     switch (x) {
3750
3751 #ifdef DEBUG
3752       case LOGD:
3753         return(deblog = debopn(s,disp));
3754 #endif /* DEBUG */
3755
3756 #ifndef NOXFER
3757       case LOGP:
3758         return(pktlog = pktopn(s,disp));
3759 #endif /* NOXFER */
3760
3761 #ifndef NOLOCAL
3762       case LOGS:
3763         setseslog(sesopn(s,disp));
3764         return(seslog);
3765 #endif /* NOLOCAL */
3766
3767 #ifdef TLOG
3768       case LOGT:
3769         return(tralog = traopn(s,disp));
3770 #endif /* TLOG */
3771
3772 #ifdef CKLOGDIAL
3773       case LOGM:
3774         return(dialog = diaopn(s,disp,0));
3775 #endif /* CKLOGDIAL */
3776
3777       default:
3778         return(-2);
3779     }
3780 }
3781
3782 #ifndef NOXFER
3783 int
3784 pktopn(s,disp) char *s; int disp; {
3785     static struct filinfo xx;
3786
3787     if (!s)
3788       s = "";
3789     if (!*s)
3790       return(0);
3791
3792     debug(F111,"pktopn",s,disp);
3793
3794     zclose(ZPFILE);
3795
3796 #ifdef OS2ORUNIX
3797     if (s[0] == '|') {                  /* Pipe */
3798         char * p = s + 1;
3799         debug(F110,"pktopn p",p,0);
3800         while (*p) {
3801             if (*p != ' ')
3802               break;
3803             else
3804               p++;
3805         }
3806         debug(F110,"pktopn pipe",p,0);
3807         pktlog = zxcmd(ZPFILE,p);
3808         debug(F101,"pktopn seslog","",seslog);
3809     } else {                            /* File */
3810 #endif /* OS2ORUNIX */
3811         if (disp) {
3812             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
3813             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
3814             xx.lblopts = 0;
3815             pktlog = zopeno(ZPFILE,s,NULL,&xx);
3816         } else pktlog = zopeno(ZPFILE,s,NULL,NULL);
3817         if (!pktlog && !quiet)
3818           printf("?%s - %s\n",s,ck_errstr());
3819 #ifdef OS2ORUNIX
3820     }
3821 #endif /* OS2ORUNIX */
3822     if (pktlog > 0)
3823       ckstrncpy(pktfil,s,CKMAXPATH+1);
3824     else
3825       *pktfil = '\0';
3826     return(pktlog);
3827 }
3828 #endif /* NOXFER */
3829
3830 int
3831 traopn(s,disp) char *s; int disp; {
3832 #ifdef TLOG
3833     static struct filinfo xx;
3834
3835     if (!s)
3836       s = "";
3837     if (!*s)
3838       return(0);
3839
3840     debug(F111,"traopn",s,disp);
3841     debug(F101,"traopn tlogfmt","",tlogfmt);
3842
3843     zclose(ZTFILE);
3844
3845 #ifdef OS2ORUNIX
3846     if (tlogfmt == 2) {                 /* FTP format is special... */
3847         VOID doiklog();
3848         if (!disp)                      /* Append? */
3849           if (zchki(s) > -1)            /* No - does file exist? */
3850             (VOID) zdelet(s);           /* Yes - delete it. */
3851         xferlog = 1;
3852         ckstrncpy(trafil,s,CKMAXPATH);
3853         makestr(&xferfile,s);
3854         doiklog();
3855         return(1);
3856     }
3857     if (s[0] == '|') {                  /* Pipe */
3858         char * p = s + 1;
3859         debug(F110,"traopn p",p,0);
3860         while (*p) {
3861             if (*p != ' ')
3862               break;
3863             else
3864               p++;
3865         }
3866         debug(F110,"traopn pipe",p,0);
3867         tralog = zxcmd(ZTFILE,p);
3868         debug(F101,"traopn tralog","",tralog);
3869     }
3870 #endif /* OS2ORUNIX */
3871
3872     if (s[0] != '|') {                  /* File */
3873         if (disp) {
3874             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
3875             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
3876             xx.lblopts = 0;
3877             tralog = zopeno(ZTFILE,s,NULL,&xx);
3878         } else tralog = zopeno(ZTFILE,s,NULL,NULL);
3879     }
3880     if (!tralog && !quiet)
3881       printf("?%s - %s\n",s,ck_errstr());
3882     if (tralog > 0 && tlogfmt > 0) {
3883         ckstrncpy(trafil,s,CKMAXPATH);
3884         tlog(F110,"Transaction Log:",versio,0L);
3885 #ifndef MAC
3886         tlog(F100,ckxsys,"",0L);
3887 #endif /* MAC */
3888         ztime(&s);
3889         tlog(F100,s,"",0L);
3890     } else
3891       *trafil = '\0';
3892     return(tralog);
3893 #else
3894     return(0);
3895 #endif /* TLOG */
3896 }
3897
3898 #ifndef NOLOCAL
3899 int
3900 sesopn(s,disp) char * s; int disp; {
3901     static struct filinfo xx;
3902     extern int tsstate;
3903
3904     tsstate = 0;                        /* Session log timestamp state */
3905
3906     if (!s)
3907       s = "";
3908     if (!*s)
3909       return(0);
3910
3911     debug(F111,"sesopn",s,disp);
3912
3913     zclose(ZSFILE);
3914
3915 #ifdef OS2ORUNIX
3916     if (s[0] == '|') {                  /* Pipe */
3917         char * p = s + 1;
3918         debug(F110,"sesopn p",p,0);
3919         while (*p) {
3920             if (*p != ' ')
3921               break;
3922             else
3923               p++;
3924         }
3925         debug(F110,"sesopn pipe",p,0);
3926         setseslog(zxcmd(ZSFILE,p));
3927         debug(F101,"sesopn seslog","",seslog);
3928     } else {                            /* File */
3929 #endif /* OS2ORUNIX */
3930         if (disp) {
3931             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
3932             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
3933             xx.lblopts = 0;
3934             setseslog(zopeno(ZSFILE,s,NULL,&xx));
3935         } else
3936           setseslog(zopeno(ZSFILE,s,NULL,NULL));
3937         if (!seslog && !quiet)
3938           printf("?%s - %s\n",s,ck_errstr());
3939 #ifdef OS2ORUNIX
3940     }
3941 #endif /* OS2ORUNIX */
3942     if (seslog > 0)
3943       ckstrncpy(sesfil,s,CKMAXPATH+1);
3944     else
3945       *sesfil = '\0';
3946     return(seslog);
3947 }
3948 #endif /* NOLOCAL */
3949 #endif /* NOICP */
3950
3951 int
3952 debopn(s,disp) char *s; int disp; {
3953 #ifdef DEBUG
3954 #ifdef CK_UTSNAME
3955     extern char unm_mch[], unm_nam[], unm_rel[], unm_ver[], unm_mod[];
3956 #endif /* CK_UTSNAME */
3957 #ifdef OS2
3958     extern char ckxsystem[];
3959 #endif /* OS2 */
3960     char *tp;
3961     static struct filinfo xx;
3962
3963     if (!s)
3964       s = "";
3965     if (!*s)
3966       return(0);
3967
3968     zclose(ZDFILE);
3969
3970 #ifdef OS2ORUNIX
3971     if (s[0] == '|') {                  /* Pipe */
3972         char * p = s + 1;
3973         debug(F110,"debopn p",p,0);
3974         while (*p) {
3975             if (*p != ' ')
3976               break;
3977             else
3978               p++;
3979         }
3980         debug(F110,"debopn pipe",p,0);
3981         deblog = zxcmd(ZDFILE,p);
3982         debug(F101,"debopn deblog","",deblog);
3983     } else {                            /* File */
3984 #endif /* OS2ORUNIX */
3985         if (disp) {
3986             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
3987             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
3988             xx.lblopts = 0;
3989             deblog = zopeno(ZDFILE,s,NULL,&xx);
3990         } else
3991           deblog = zopeno(ZDFILE,s,NULL,NULL);
3992         if (!deblog && !quiet)
3993           printf("?%s - %s\n",s,ck_errstr());
3994 #ifdef OS2ORUNIX
3995     }
3996 #endif /* OS2ORUNIX */
3997     if (deblog > 0) {
3998         ckstrncpy(debfil,s,CKMAXPATH+1);
3999         debug(F110,"Debug Log ",versio,0);
4000 #ifndef MAC
4001 #ifdef OS2
4002         debug(F110,ckxsys,ckxsystem,0);
4003 #else /* OS2 */
4004         debug(F100,ckxsys,"",0);
4005 #endif /* OS2 */
4006 #endif /* MAC */
4007 #ifdef CK_UTSNAME
4008         if (unm_mch[0]) {
4009             debug(F110,"uname machine",unm_mch,0);
4010             if (unm_mod[0])
4011               debug(F110,"uname model  ",unm_mod,0);
4012             debug(F110,"uname sysname",unm_nam,0);
4013             debug(F110,"uname release",unm_rel,0);
4014             debug(F110,"uname version",unm_ver,0);
4015         }
4016 #ifdef KTARGET
4017         {
4018             char * s;                   /* Makefile target */
4019             s = KTARGET;
4020             if (!s) s = "";
4021             if (!*s) s = "(unknown)";
4022             debug(F110,"build target",s,0);
4023         }
4024 #endif /* KTARGET */
4025         deblog = 0;
4026         ztime(&tp);
4027         deblog = 1;
4028         debug(F100,tp,"",0);
4029 #endif /* UTSNAME */
4030         debug(F101,"byteorder","",byteorder);
4031 #ifndef NOICP
4032 #ifndef NOLOCAL
4033         if (local) {
4034             debug(F110,"Active connection: ",ttname,0);
4035             if (!network) {
4036                 debug(F101,"Speed","",speed);
4037                 if (hwparity)
4038                   debug(F110,"Parity[hardware]",parnam((char)hwparity),0);
4039                 else
4040                   debug(F110,"Parity",parnam((char)parity),0);
4041                 deblog = 0;
4042                 debug(F110,"Modem",gmdmtyp(),0);
4043                 deblog = 1;
4044             }
4045         } else {
4046             debug(F110,"Active connection: ","none",0);
4047         }
4048 #endif /* NOLOCAL */
4049 #endif /* NOICP */
4050     } else *debfil = '\0';
4051     return(deblog);
4052 #else
4053     return(0);
4054 #endif /* MAC */
4055 }
4056
4057
4058 /*  C K D A T E  --  Returns current date/time in standard format  */
4059
4060 static char nowbuf[18];
4061
4062 char *
4063 ckdate() {
4064     extern struct keytab cmonths[];
4065     int x;
4066     char * t;                   /* Substitute today's date */
4067     char dbuf[32];
4068     ztime(&t);
4069
4070 /*  012345678901234567890123 */
4071 /*  Sat Jul  4 12:16:43 1998 */
4072
4073     ckstrncpy(dbuf,t,32);
4074     t = dbuf;
4075     debug(F110,"ckdate dbuf",dbuf,0);
4076     nowbuf[0] = t[20];
4077     nowbuf[1] = t[21];
4078     nowbuf[2] = t[22];
4079     nowbuf[3] = t[23];
4080
4081     nowbuf[4] = NUL;
4082     debug(F110,"ckdate nowbuf",nowbuf,0);
4083
4084     t[7] = NUL;
4085     if ((x = lookup(cmonths,t+4,12,NULL)) < 0) {
4086         debug(F110,"ckdate bad month",t,0);
4087         return("<BAD_MONTH>");
4088     }
4089     sprintf(nowbuf+4,"%02d",x);         /* SAFE */
4090     nowbuf[6] = (t[8] == SP) ? '0' : t[8];
4091     nowbuf[7] = t[9];
4092     nowbuf[8] = ' ';
4093
4094     nowbuf[9] = NUL;
4095     debug(F110,"ckdate nowbuf",nowbuf,0);
4096
4097     for (x = 11; x < 19; x++) nowbuf[x-2] = t[x];
4098     nowbuf[17] = NUL;
4099     debug(F110,"ckdate nowbuf",nowbuf,0);
4100
4101     return((char *)nowbuf);
4102 }
4103
4104 #ifndef NOICP
4105 #ifdef CKLOGDIAL
4106
4107 /*
4108   fc = 0 for initial open, meaning open, then close immediately.
4109   fc > 0 for subsequent opens, meaning open for use, leave open.
4110 */
4111 int
4112 diaopn(s,disp,fc) char *s; int disp, fc; {
4113     static struct filinfo xx;
4114
4115     if (!s)
4116       s = "";
4117     if (!*s)
4118       return(0);
4119
4120     debug(F110,"diaopn log",s,0);
4121     debug(F101,"diaopn fc",s,fc);
4122     debug(F101,"diaopn disp 1",s,disp);
4123     if (fc) disp = 1;                   /* Force append if open for use */
4124     debug(F101,"diaopn disp 2",s,disp);
4125
4126     zclose(ZDIFIL);                     /* In case a log was already open */
4127
4128 #ifdef OS2ORUNIX
4129     if (s[0] == '|') {                  /* Pipe */
4130         char * p = s + 1;
4131         debug(F110,"diaopn p",p,0);
4132         while (*p) {
4133             if (*p != ' ')
4134               break;
4135             else
4136               p++;
4137         }
4138         debug(F110,"diaopn pipe",p,0);
4139         dialog = zxcmd(ZDIFIL,p);
4140         debug(F101,"diaopn dialog","",dialog);
4141     } else {                            /* File */
4142 #endif /* OS2ORUNIX */
4143         if (disp) {
4144             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
4145             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
4146             xx.lblopts = 0;
4147             dialog = zopeno(ZDIFIL,s,NULL,&xx);
4148         } else dialog = zopeno(ZDIFIL,s,NULL,NULL);
4149         if (!dialog)
4150           printf("?%s - %s\n",s,ck_errstr());
4151 #ifdef OS2ORUNIX
4152     }
4153 #endif /* OS2ORUNIX */
4154     if (dialog > 0)
4155       ckstrncpy(diafil,s,CKMAXPATH+1);
4156     else
4157       *diafil = '\0';
4158     if (fc == 0)                        /* Initial open */
4159       zclose(ZDIFIL);                   /* close it */
4160     return(dialog);
4161 }
4162 #endif /* CKLOGDIAL */
4163
4164 #ifndef NOSHOW
4165
4166 /*  SHOW command routines */
4167
4168 char *
4169 shoxm() {
4170     char * s;
4171     switch (binary) {
4172       case XYFT_T: s = "text";         break;
4173 #ifdef VMS
4174       case XYFT_B: s = "binary fixed"; break;
4175       case XYFT_I: s = "image";        break;
4176       case XYFT_L: s = "labeled";      break;
4177       case XYFT_U: s = "binary undef"; break;
4178 #else
4179 #ifdef MAC
4180       case XYFT_B: s = "binary";       break;
4181       case XYFT_M: s = "macbinary";    break;
4182 #else
4183       case XYFT_B: s = "binary";       break;
4184 #ifdef CK_LABELED
4185       case XYFT_L: s = "labeled";      break;
4186 #endif /* CK_LABELED */
4187 #endif /* MAC */
4188 #endif /* VMS */
4189       default: s = "unknown"; break;
4190     }
4191     return(s);
4192 }
4193
4194 #ifndef NOXFER
4195 VOID                                    /* SHOW TRANSFER */
4196 shoxfer() {
4197     extern int docrc, usepipes, xfrxla, whereflg;
4198     extern char * xfrmsg;
4199     printf("\n");
4200     printf(" Transfer Bell: %s\n",showoff(xfrbel));
4201     printf(" Transfer Interruption: %s\n",showoff(xfrint));
4202     printf(" Transfer Cancellation: %s\n",showoff(xfrcan));
4203 #ifndef NOCSETS
4204     printf(" Transfer Translation:  %s\n",showoff(xfrxla));
4205     printf(" Transfer Character-set: ");
4206     if (tcharset == TC_TRANSP)
4207       printf("Transparent\n");
4208     else
4209       printf("%s\n",tcsinfo[tcharset].keyword);
4210 #endif /* NOCSETS */
4211     printf(" Transfer CRC-calculation: %s\n",showoff(docrc));
4212     printf(" Transfer Display: ");
4213     switch (fdispla) {
4214       case XYFD_N: printf("%s\n","none"); break;
4215       case XYFD_R: printf("%s\n","serial"); break;
4216       case XYFD_C: printf("%s\n","fullscreen"); break;
4217       case XYFD_S: printf("%s\n","crt"); break;
4218       case XYFD_B: printf("%s\n","brief"); break;
4219       case XYFD_G: printf("%s\n","gui"); break;
4220     }
4221     printf(" Transfer Message: %s\n", xfrmsg ? xfrmsg : "(none)");
4222     printf(" Transfer Locking-shift: ");
4223     if (lscapu == 2) {
4224         printf("forced");
4225     } else {
4226         printf("%s", (lscapr ? "enabled" : "disabled"));
4227         if (lscapr) printf(",%s%s", (lscapu ? " " : " not "), "used");
4228     }
4229     printf("\n Transfer Mode: %s\n",
4230            xfermode == XMODE_A ?
4231            "automatic" :
4232            "manual"
4233            );
4234     printf(" Transfer Pipes: %s\n", showoff(usepipes));
4235     printf(" Transfer Protocol: %s\n",ptab[protocol].p_name);
4236     printf(" Transfer Report: %s\n",showoff(whereflg));
4237     printf(" Transfer Slow-start: %s\n",showoff(slostart));
4238     printf("\n");
4239 }
4240 #endif /* NOXFER */
4241
4242 VOID
4243 shoflow() {
4244     int i, x;
4245     extern int cxflow[], cxtype, ncxname, nfloname, autoflow;
4246     extern char * cxname[];
4247     printf("\nConnection type:        %s\n",cxname[cxtype]);
4248     if (autoflow) {
4249         printf("Current flow-control:   %s\n", floname[cxflow[cxtype]]);
4250         printf("Switches automatically: yes\n");
4251     } else {
4252         printf("Current flow-control:   %s\n", floname[flow]);
4253         printf("Switches automatically: no\n");
4254     }
4255     printf("\nDefaults by connection type:\n");
4256     debug(F111,"shoflow cxtype",cxname[cxtype],cxtype);
4257     debug(F101,"shoflow flow","",flow);
4258     for (i = 0; i < ncxname; i++) {
4259 #ifdef NOLOCAL
4260         if (i > 0) break;
4261 #endif /* NOLOCAL */
4262 #ifndef NETCONN
4263         if (i > 2) break;
4264 #endif /* NETCONN */
4265 #ifndef DECNET
4266         if (i == CXT_DECNET) continue;
4267 #endif /* DECNET */
4268 #ifndef DECNET
4269 #ifndef SUPERLAT
4270         if (i == CXT_LAT) continue;
4271 #endif /* SUPERLAT */
4272 #endif /* DECNET */
4273 #ifndef CK_NETBIOS
4274         if (i == CXT_NETBIOS) continue;
4275 #endif /* CK_NETBIOS */
4276 #ifndef NPIPE
4277         if (i == CXT_NPIPE) continue;
4278 #endif /* NPIPE */
4279 #ifndef NETCMD
4280         if (i == CXT_PIPE) continue;
4281 #endif /* NETCMD */
4282 #ifndef ANYX25
4283         if (i == CXT_X25) continue;
4284 #endif /* ANYX25 */
4285         x = cxflow[i];
4286         debug(F101,"shoflow x","",x);
4287         if (x < nfloname)
4288           printf("  %-14s: %s\n",cxname[i],floname[x]);
4289         else
4290           printf("  %-14s: (%d)\n",cxname[i],x);
4291     }
4292     printf("\n");
4293 }
4294
4295 #ifndef NOLOCAL
4296 #ifdef ANYX25
4297 int
4298 shox25(n) int n; {
4299     if (nettype == NET_SX25) {
4300         printf("SunLink X.25 V%d.%d",x25ver / 10,x25ver % 10);
4301         if (ttnproto == NP_X3) printf(", PAD X.3, X.28, X.29 protocol,");
4302         printf("\n");
4303         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4304         printf(" Reverse charge call %s",
4305                revcall ? "selected" : "not selected");
4306         printf (", Closed user group ");
4307         if (closgr > -1)
4308           printf ("%d",closgr);
4309         else
4310           printf ("not selected");
4311         printf("\n");
4312         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4313         printf(" Call user data %s.\n", cudata ? udata : "not selected");
4314         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4315     } else if (nettype == NET_VX25) {
4316         if (ttnproto == NP_X3) printf(", PAD X.3, X.28, X.29 protocol,");
4317         printf("\n");
4318         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4319         printf(" Reverse charge call %s",
4320                revcall ? "selected" : "not selected");
4321         printf (", Closed user group [unsupported]");
4322         if (closgr > -1)
4323           printf ("%d",closgr);
4324         else
4325           printf ("not selected");
4326         printf (",");
4327         printf("\n");
4328         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4329         printf(" Call user data %s.\n", cudata ? udata : "not selected");
4330         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4331     } else if (nettype == NET_IX25) {
4332         printf("AIX NPI X.25\n");
4333         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4334         printf("\n Reverse charge call %s",
4335                revcall ? "selected" : "not selected");
4336         printf (", Closed user group [unsupported]");
4337         if (closgr > -1)
4338           printf ("%d",closgr);
4339         else
4340           printf ("not selected");
4341         printf (",");
4342         printf("\n Call user data %s.\n", cudata ? udata : "not selected");
4343     }
4344     return(n);
4345 }
4346
4347 #ifndef IBMX25
4348 int
4349 shopad(n) int n; {
4350     int i;
4351     printf("\nX.3 PAD Parameters:\n");
4352     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4353     for (i = 0; i < npadx3; i++) {
4354         printf(" [%d] %s %d\n",padx3tab[i].kwval,padx3tab[i].kwd,
4355                padparms[padx3tab[i].kwval]);
4356         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4357     }
4358     return(n);
4359 }
4360 #endif /* IBMX25 */
4361 #endif /* ANYX25 */
4362
4363 VOID
4364 shoparc() {
4365     extern int reliable, stopbits, clsondisc;
4366     int i; char *s;
4367     long zz;
4368
4369 #ifdef NEWFTP
4370     if (ftpisconnected()) {
4371         shoftp(1);
4372         printf("\n");
4373     }
4374 #endif /* NEWFTP */
4375
4376     printf("Communications Parameters:\n");
4377
4378     if (network
4379 #ifdef IKSD
4380          || inserver
4381 #endif /* IKSD */
4382          ) {
4383         printf(" Network Host: %s%s",ttname,
4384                (reliable == SET_ON || (reliable == SET_AUTO && !local)
4385 #ifdef TN_COMPORT
4386                && !istncomport()
4387 #endif /* TN_COMPORT */
4388 #ifdef IKSD
4389                || inserver
4390 #endif /* IKSD */
4391                ) ? " (reliable)" : "");
4392 #ifdef TN_COMPORT
4393         if (istncomport()) {
4394             int modemstate;
4395             char * oflow, * iflow = "", * parity, * stopsize, * signature;
4396             int baud = tnc_get_baud();
4397
4398             switch (tnc_get_oflow()) {
4399               case TNC_CTL_OFLOW_NONE:
4400                 oflow = "none";
4401                 break;
4402               case TNC_CTL_OFLOW_XON_XOFF:
4403                 oflow = "xon/xoff";
4404                 break;
4405               case TNC_CTL_OFLOW_RTS_CTS:
4406                 oflow = "rts/cts";
4407                 break;
4408               case TNC_CTL_OFLOW_DCD:
4409                 oflow = "dcd";
4410                 break;
4411               case TNC_CTL_OFLOW_DSR:
4412                 oflow = "dsr";
4413                 break;
4414               default:
4415                 oflow = "(unknown)";
4416             }
4417             switch (tnc_get_iflow()) {
4418               case TNC_CTL_IFLOW_NONE:
4419                 iflow = "none";
4420                 break;
4421               case TNC_CTL_IFLOW_XON_XOFF:
4422                 iflow = "xon/xoff";
4423                 break;
4424               case TNC_CTL_IFLOW_RTS_CTS:
4425                 iflow = "rts/cts";
4426                 break;
4427               case TNC_CTL_IFLOW_DTR:
4428                 break;
4429               default:
4430                 iflow = oflow;
4431             }
4432             switch (tnc_get_parity()) {
4433               case TNC_PAR_NONE:
4434                 parity = "none";
4435                 break;
4436               case TNC_PAR_ODD:
4437                 parity = "odd";
4438                 break;
4439               case TNC_PAR_EVEN:
4440                 parity = "even";
4441                 break;
4442               case TNC_PAR_MARK:
4443                 parity = "mark";
4444                 break;
4445               case TNC_PAR_SPACE:
4446                 parity = "space";
4447                 break;
4448               default:
4449                 parity = "(unknown)";
4450             }
4451             switch (tnc_get_stopsize()) {
4452               case TNC_SB_1:
4453                 stopsize = "1";
4454                 break;
4455               case TNC_SB_1_5:
4456                 stopsize = "1.5";
4457                 break;
4458               case TNC_SB_2:
4459                 stopsize = "2";
4460                 break;
4461               default:
4462                 stopsize = "(unknown)";
4463             }
4464             signature = (char *)tnc_get_signature();
4465             printf("\n  Signature            : %s\n",signature?signature:"");
4466             if (baud <= 0)
4467               printf("  Speed                : (unknown)\n");
4468             else
4469               printf("  Speed                : %d\n", baud);
4470             printf("  Outbound Flow Control: %s\n", oflow);
4471             printf("  Inbound Flow Control : %s\n", iflow);
4472             printf("  Parity               : %s\n", parity);
4473             printf("  Data Size            : %d\n", tnc_get_datasize());
4474             printf("  Stop Bits            : %s\n", stopsize);
4475             printf("  DTR Signal           : %d\n", tnc_get_dtr_state());
4476             printf("  RTS Signal           : %d\n", tnc_get_rts_state());
4477             printf("  Modem State:\n");
4478             modemstate = tnc_get_ms();
4479             if (modemstate & TNC_MS_EDGE_RING)
4480               printf("    Trailing Edge Ring Detector On\n");
4481             else
4482               printf("    Trailing Edge Ring Detector Off\n");
4483             if (modemstate & TNC_MS_CTS_SIG)
4484               printf("    CTS Signal On\n");
4485             else
4486               printf("    CTS Signal Off\n");
4487             if (modemstate & TNC_MS_DSR_SIG)
4488               printf("    DSR Signal On\n");
4489             else
4490               printf("    DSR Signal Off\n");
4491             if (modemstate & TNC_MS_RI_SIG)
4492               printf("    Ring Indicator On\n");
4493             else
4494               printf("    Ring Indicator Off\n");
4495             if (modemstate & TNC_MS_RLSD_SIG)
4496               printf("    RLSD (CD) Signal On\n");
4497             else
4498               printf("    RLSD (CD) Signal Off\n");
4499             printf("\n");
4500         }
4501 #endif /* TN_COMPORT */
4502     } else {
4503
4504         printf(" %s: %s%s, speed: ",
4505 #ifdef OS2
4506                "Port",
4507 #else
4508                "Line",
4509 #endif /* OS2 */
4510                ttname,
4511 #ifdef CK_TTYFD
4512                (local &&
4513 #ifdef VMS
4514                 vmsttyfd() < 0
4515 #else
4516                 ttyfd == -1
4517 #endif /* VMS */
4518                 ) ?
4519                  " (closed)" :
4520                    (reliable == SET_ON ? " (reliable)" : "")
4521 #else
4522                ""
4523 #endif /* CK_TTYFD */
4524                );
4525         if (
4526 #ifdef CK_TTYFD
4527 #ifdef VMS
4528             vmsttyfd() < 0
4529 #else
4530             ttyfd == -1
4531 #endif /* VMS */
4532             ||
4533 #endif /* CK_TTYFD */
4534             (zz = ttgspd()) < 0) {
4535             printf("unknown");
4536         } else {
4537             if (speed == 8880) printf("75/1200");
4538             else if (speed == 134) printf("134.5");
4539             else printf("%ld",zz);
4540         }
4541     }
4542     if (network
4543 #ifdef IKSD
4544          || inserver
4545 #endif /* IKSD */
4546          )
4547       printf("\n Mode: ");
4548     else
4549       printf(", mode: ");
4550     if (local) printf("local"); else printf("remote");
4551     if (network == 0
4552 #ifdef IKSD
4553          && !inserver
4554 #endif/* IKSD */
4555          ) {
4556 #ifdef CK_TAPI
4557         if (tttapi && !tapipass )
4558           printf(", modem: %s","TAPI");
4559         else
4560 #endif /* CK_TAPI */
4561         printf(", modem: %s",gmdmtyp());
4562     } else {
4563 #ifdef NETCONN
4564        if (nettype == NET_TCPA) printf(", TCP/IP");
4565        if (nettype == NET_TCPB) printf(", TCP/IP");
4566        if (nettype == NET_DEC) {
4567            if (ttnproto == NP_LAT) printf(", DECnet LAT");
4568            else if ( ttnproto == NP_CTERM ) printf(", DECnet CTERM");
4569            else printf(", DECnet");
4570        }
4571        if (nettype == NET_SLAT) printf(", Meridian Technologies' SuperLAT");
4572 #ifdef NETFILE
4573        if (nettype == NET_FILE) printf(", local file");
4574 #endif /* NETFILE */
4575 #ifdef NETCMD
4576        if (nettype == NET_CMD) printf(", pipe");
4577 #endif /* NETCMD */
4578 #ifdef NETPTY
4579        if (nettype == NET_PTY) printf(", pseudoterminal");
4580 #endif /* NETPTY */
4581 #ifdef NETDLL
4582        if (nettype == NET_DLL) printf(", dynamic load library");
4583 #endif /* NETDLL */
4584        if (nettype == NET_PIPE) printf(", Named Pipes");
4585 #ifdef SSHBUILTIN
4586        if (nettype == NET_SSH)
4587          printf(", Secure Shell protocol (SECURE)");
4588 #endif /* SSHBUILTIN */
4589 #ifdef ANYX25
4590        if (shox25(0) < 0) return;
4591 #endif /* ANYX25 */
4592        if (IS_TELNET()) {
4593            printf(", telnet protocol");
4594            if (0
4595 #ifdef CK_ENCRYPTION
4596                || ck_tn_encrypting() && ck_tn_decrypting()
4597 #endif /* CK_ENCRYPTION */
4598 #ifdef CK_SSL
4599                || tls_active_flag || ssl_active_flag
4600 #endif /* CK_SSL */
4601                )
4602              printf(" (SECURE)");
4603        }
4604 #ifdef RLOGCODE
4605        else if (ttnproto == NP_RLOGIN || ttnproto == NP_K4LOGIN ||
4606                 ttnproto == NP_K5LOGIN)
4607          printf(", rlogin protocol");
4608        else if (ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN)
4609          printf(", rlogin protocol (SECURE)");
4610 #endif /* RLOGCODE */
4611 #ifdef CK_KERBEROS
4612 #ifdef KRB5
4613        else if (ttnproto == NP_K5U2U)
4614          printf(", Kerberos 5 User to User protocol (SECURE)");
4615 #endif /* KRB5 */
4616 #endif /* CK_KERBEROS */
4617 #endif /* NETCONN */
4618     }
4619     printf("\n");
4620     if (hwparity && local && !network)
4621       s = parnam((char)hwparity);
4622     else
4623       s = parnam((char)parity);
4624     printf(" Parity: %s%s",hwparity ? "hardware " : "", s);
4625 #ifndef NOLOCAL
4626     if (local && !network) {
4627         int sb;
4628         char c;
4629         c = s[0];
4630         if (islower(c)) c = toupper(c);
4631         sb = stopbits;
4632         if (sb < 1) {
4633             sb = (speed > 0 && speed <= 110L) ? 2 : 1;
4634             printf(", stop-bits: (default)");
4635         } else {
4636             printf(", stop-bits: %d",sb);
4637         }
4638         if (hwparity)
4639           printf(" (8%c%d)",c,sb);
4640         else if (parity)
4641           printf(" (7%c%d)",c,sb);
4642         else
4643           printf(" (8N%d)",sb);
4644         printf("\n D");
4645     } else
4646       printf(", d");
4647 #endif /* NOLOCAL */
4648
4649     printf("uplex: %s, ", duplex ? "half" : "full");
4650     debug(F101,"shoparp flow","",flow);
4651     printf("flow: %s", floname[flow]);
4652     printf(", handshake: ");
4653     if (turn) printf("%d\n",turnch); else printf("none\n");
4654 #ifdef COMMENT
4655     if (local && !network) {            /* SET CARRIER-WATCH */
4656 #endif /* COMMENT */
4657         if (carrier == CAR_OFF) s = "off";
4658         else if (carrier == CAR_ON) s = "on";
4659         else if (carrier == CAR_AUT) s = "auto";
4660         else s = "unknown";
4661         printf(" Carrier-watch: %s", s);
4662         if (carrier == CAR_ON) {
4663             if (cdtimo) printf(", timeout: %d sec", cdtimo);
4664             else printf(", timeout: none");
4665         }
4666 #ifdef COMMENT
4667     }
4668 #endif /* COMMENT */
4669     printf(", close-on-disconnect: %s\n",showoff(clsondisc));
4670
4671 #ifdef UNIX                             /* UUCP lockfile, UNIX only */
4672     if (local) {
4673 #ifndef NOUUCP
4674         if (!network && haslock && *flfnam)
4675           printf(" Lockfile: %s",flfnam);
4676 #ifndef USETTYLOCK
4677         if (!network && haslock && lock2[0])
4678           printf("\n Secondary lockfile: %s",lock2);
4679 #endif /* USETTYLOCK */
4680 #else
4681 #ifdef QNX
4682         {
4683             extern int qnxportlock, qnxopencount();
4684             if (local)
4685               printf(" Qnx-port-lock: %s, Open count: %d",
4686                      showoff(qnxportlock),
4687                      qnxopencount()
4688                      );
4689             else
4690               printf(" Qnx-port-lock: %s", showoff(qnxportlock));
4691         }
4692 #endif /* QNX */
4693 #endif /* NOUUCP */
4694         printf("\n");
4695     } else {
4696         char * s;
4697         s = ttglckdir();
4698         if (!s) s = "";
4699         printf(" Lockfile directory: %s\n", *s ? s : "(none)");
4700     }
4701 #endif /* UNIX */
4702 #ifndef MACOSX
4703     if (!local) {
4704         printf(" Typical port device name: %s\n",ttgtpn());
4705     }
4706 #endif  /* MACOSX */
4707     if (local) {
4708         int i;
4709         i = parity ? 7 : 8;
4710         if (i == 8) i = (cmask == 0177) ? 7 : 8;
4711         printf(" Terminal bytesize: %d,",i);
4712         printf(" escape character: %d (^%c)\n",escape,ctl(escape));
4713     }
4714 }
4715
4716 int
4717 shotcp(n) int n; {
4718 #ifdef TCPSOCKET
4719     if (nettype == NET_TCPA || nettype == NET_TCPB) {
4720         printf("SET TCP parameters:\n");
4721         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4722         printf(" Reverse DNS lookup: %s\n", showooa(tcp_rdns));
4723         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4724
4725 #ifdef CK_DNS_SRV
4726         printf(" DNS Service Records lookup: %s\n", showooa(tcp_dns_srv));
4727         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4728 #endif /* CK_DNS_SRV */
4729
4730 #ifndef NOTCPOPTS
4731 #ifdef SOL_SOCKET
4732 #ifdef SO_KEEPALIVE
4733         printf(" Keepalive: %s\n", showoff(tcp_keepalive));
4734         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4735 #endif /* SO_KEEPALIVE */
4736
4737 #ifdef SO_LINGER
4738         printf(" Linger: %s", tcp_linger ? "on, " : "off\n" );
4739         if (tcp_linger) {
4740             if (tcp_linger_tmo)
4741               printf("%d x 10 milliseconds\n",tcp_linger_tmo);
4742             else
4743               printf("no timeout\n");
4744         }
4745         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4746 #endif /* SO_LINGER */
4747
4748 #ifdef SO_DONTROUTE
4749         printf(" DontRoute: %s\n", tcp_dontroute ? "on" : "off" );
4750         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4751 #endif /* SO_DONTROUTE */
4752
4753 #ifdef TCP_NODELAY
4754         printf(" Nodelay: %s\n", showoff(tcp_nodelay));
4755         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4756 #endif /* TCP_NODELAY */
4757
4758 #ifdef SO_SNDBUF
4759         if (tcp_sendbuf <= 0)
4760           printf(" Send buffer: (default size)\n");
4761         else
4762           printf(" Send buffer: %d bytes\n", tcp_sendbuf);
4763         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4764 #endif /* SO_SNDBUF */
4765 #ifdef SO_RCVBUF
4766         if (tcp_recvbuf <= 0)
4767           printf(" Receive buffer: (default size)\n");
4768         else
4769           printf(" Receive buffer: %d bytes\n", tcp_recvbuf);
4770         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4771 #endif /* SO_RCVBUF */
4772 #endif /* SOL_SOCKET */
4773 #endif /* NOTCPOPTS */
4774         printf(" address: %s\n",tcp_address ? tcp_address : "(none)");
4775         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4776 #ifndef NOHTTP
4777         printf(" http-proxy: %s\n",tcp_http_proxy ? tcp_http_proxy : "(none)");
4778         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4779 #endif /* NOHTTP */
4780 #ifdef NT
4781 #ifdef CK_SOCKS
4782         printf(" socks-server: %s\n",tcp_socks_svr ? tcp_socks_svr : "(none)");
4783         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4784 #ifdef CK_SOCKS_NS
4785         printf(" socks-name-server: %s\n",
4786                tcp_socks_ns ? tcp_socks_ns : "(none)");
4787         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4788 #endif /* CK_SOCKS_NS */
4789 #endif /* CK_SOCKS */
4790 #endif /* NT */
4791     }
4792 #endif /* TCPSOCKET */
4793     return(n);
4794 }
4795
4796 #ifdef TNCODE
4797 int
4798 shotopt(n) int n; {
4799     int opt;
4800
4801     printf("%-21s %12s %12s %12s %12s\n\n",
4802            "Telnet Option","Me (client)","U (client)",
4803            "Me (server)","U (server)");
4804     n += 2;
4805     if (n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4806
4807     for ( opt = TELOPT_FIRST; opt <= TELOPT_LAST; opt++) {
4808         switch (opt) {
4809           case TELOPT_AUTHENTICATION:
4810           case TELOPT_ENCRYPTION:
4811           case TELOPT_TTYPE:
4812           case TELOPT_NAWS:
4813           case TELOPT_BINARY:
4814           case TELOPT_NEWENVIRON:
4815           case TELOPT_SNDLOC:
4816           case TELOPT_XDISPLOC:
4817           case TELOPT_SGA:
4818           case TELOPT_ECHO:
4819           case TELOPT_KERMIT:
4820           case TELOPT_START_TLS:
4821           case TELOPT_FORWARD_X:
4822           case TELOPT_COMPORT:
4823             break;
4824           default:
4825             continue;
4826         }
4827         printf("%03d %-17s ",
4828                opt, TELOPT(opt)
4829                );
4830         printf("%12s %12s ",
4831                TELOPT_MODE(TELOPT_DEF_C_ME_MODE(opt)),
4832                TELOPT_MODE(TELOPT_DEF_C_U_MODE(opt))
4833                );
4834         printf("%12s %12s\n",
4835                TELOPT_MODE(TELOPT_DEF_S_ME_MODE(opt)),
4836                TELOPT_MODE(TELOPT_DEF_S_U_MODE(opt))
4837                );
4838
4839         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4840         if (sstelnet)
4841           printf("%21s %12s %12s %12s %12s\n",
4842                  "",
4843                  "",
4844                  "",
4845                  (TELOPT_ME(opt)?"WILL":"WONT"),
4846                  (TELOPT_U(opt)?"DO":"DONT")
4847                  );
4848         else
4849           printf("%21s %12s %12s %12s %12s\n",
4850                  "",
4851                  (TELOPT_ME(opt)?"WILL":"WONT"),
4852                  (TELOPT_U(opt)?"DO":"DONT"),
4853                  "",
4854                  ""
4855                  );
4856         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4857     }
4858     return(n);
4859 }
4860
4861 int
4862 shotel(n) int n; {
4863     extern int tn_duplex;
4864 #ifdef CK_ENVIRONMENT
4865     extern int tn_env_flg;
4866     extern char tn_env_acct[];
4867     extern char tn_env_job[];
4868     extern char tn_env_prnt[];
4869     extern char tn_env_sys[];
4870     extern char * tn_env_uservar[8][2];
4871     int x;
4872 #endif /* CK_ENVIRONMENT */
4873 #ifdef CK_SNDLOC
4874     extern char * tn_loc;
4875 #endif /* CK_SNDLOC */
4876     printf("SET TELNET parameters:\n echo: %s\n NVT newline-mode: ",
4877            tn_duplex ? "local" : "remote");
4878     switch (tn_nlm) {
4879       case TNL_CRNUL: printf("%s\n","off (cr-nul)"); break;
4880       case TNL_CRLF:  printf("%s\n","on (cr-lf)"); break;
4881       case TNL_CR:    printf("%s\n","raw (cr)"); break;
4882       case TNL_LF:    printf("%s\n","(lf)"); break;
4883     }
4884     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4885 #ifdef CK_AUTHENTICATION
4886     {
4887         int type = ck_tn_authenticated();
4888         printf(" authentication: ");
4889         switch (sstelnet ?
4890                 TELOPT_U_MODE(TELOPT_AUTHENTICATION) :
4891                  TELOPT_ME_MODE(TELOPT_AUTHENTICATION)
4892                 ) {
4893           case TN_NG_AC: printf( "accepted " ); break;
4894           case TN_NG_RF: printf( "refused  " ); break;
4895           case TN_NG_RQ: printf( "requested"); break;
4896           case TN_NG_MU: printf( "required "); break;
4897         }
4898
4899 #ifdef CK_SSL
4900         if ((ssl_active_flag || tls_active_flag) &&
4901              ck_tn_auth_valid() == AUTH_VALID &&
4902              (!TELOPT_U(TELOPT_AUTHENTICATION) ||
4903                type == AUTHTYPE_NULL ||
4904                type == AUTHTYPE_AUTO))
4905             printf("   in use: X.509 certificate\n");
4906         else
4907 #endif /* CK_SSL */
4908           printf("   in use: %s\n",AUTHTYPE_NAME(type));
4909         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4910         if (forward_flag)
4911           printf("  credentials forwarding requested %s\n",
4912                  forwarded_tickets ? "and completed" :
4913                  "but not completed");
4914         else
4915           printf("  credentials forwarding disabled\n");
4916         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4917     }
4918 #endif /* CK_AUTHENTICATION */
4919 #ifdef CK_ENCRYPTION
4920     {
4921         int i,x;
4922         int e_type = ck_tn_encrypting();
4923         int d_type = ck_tn_decrypting();
4924         char * e_str = NULL, * d_str = NULL;
4925         static struct keytab * tnetbl = NULL;
4926         static int ntnetbl = 0;
4927
4928         x = ck_get_crypt_table(&tnetbl,&ntnetbl);
4929
4930         for (i = 0; i < ntnetbl; i++) {
4931             if (e_type == tnetbl[i].kwval)
4932               e_str = tnetbl[i].kwd;
4933             if (d_type == tnetbl[i].kwval)
4934               d_str = tnetbl[i].kwd;
4935         }
4936         printf(" encryption: ");
4937         switch (TELOPT_ME_MODE(TELOPT_ENCRYPTION)) {
4938           /* This should be changed to report both ME and U modes */
4939           case TN_NG_AC: printf( "accepted " ); break;
4940           case TN_NG_RF: printf( "refused  " ); break;
4941           case TN_NG_RQ: printf( "requested"); break;
4942           case TN_NG_MU: printf( "required "); break;
4943         }
4944         printf("       in use: ");
4945         switch ((e_type ? 1 : 0) | (d_type ? 2 : 0)) {
4946           case 0:
4947             printf("plain text in both directions");
4948             break;
4949           case 1:
4950             printf("%s output, plain text input",e_str);
4951             break;
4952           case 2:
4953             printf("plain text output, %s input",d_str);
4954             break;
4955           case 3:
4956             printf("%s output, %s input",e_str,d_str);
4957             break;
4958         }
4959         printf("\n");
4960         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4961     }
4962 #endif /* CK_ENCRYPTION */
4963 #ifdef IKS_OPTION
4964     printf(" kermit: ");
4965     switch (TELOPT_U_MODE(TELOPT_KERMIT)) {
4966       case TN_NG_AC: printf( "u, accepted;  " ); break;
4967       case TN_NG_RF: printf( "u, refused;   " ); break;
4968       case TN_NG_RQ: printf( "u, requested; "); break;
4969       case TN_NG_MU: printf( "u, required;  "); break;
4970     }
4971     switch (TELOPT_ME_MODE(TELOPT_KERMIT)) {
4972       case TN_NG_AC: printf( "me, accepted;  " ); break;
4973       case TN_NG_RF: printf( "me, refused;   " ); break;
4974       case TN_NG_RQ: printf( "me, requested; "); break;
4975       case TN_NG_MU: printf( "me, required;  "); break;
4976     }
4977     if (TELOPT_U(TELOPT_KERMIT))
4978       printf(" u, %s",
4979              TELOPT_SB(TELOPT_KERMIT).kermit.u_start ?
4980              "started" :
4981              "stopped"
4982              );
4983     else
4984       printf(" u, n/a");
4985     if (TELOPT_ME(TELOPT_KERMIT))
4986       printf(" me, %s;",
4987              TELOPT_SB(TELOPT_KERMIT).kermit.me_start ?
4988              "started" :
4989              "stopped"
4990              );
4991     else
4992       printf(" me, n/a;");
4993     printf("\n");
4994     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4995 #endif /* IKS_OPTION */
4996     printf(" BINARY newline-mode: ");
4997     switch (tn_b_nlm) {
4998       case TNL_CRNUL: printf("%s\n","off (cr-nul)"); break;
4999       case TNL_CRLF:  printf("%s\n","on (cr-lf)"); break;
5000       case TNL_CR:    printf("%s\n","raw (cr)"); break;
5001       case TNL_LF:    printf("%s\n","(lf)"); break;
5002     }
5003     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5004     printf(" binary-mode: ");
5005     switch (TELOPT_U_MODE(TELOPT_BINARY)) {
5006       case TN_NG_AC: printf( "u, accepted;  " ); break;
5007       case TN_NG_RF: printf( "u, refused;   " ); break;
5008       case TN_NG_RQ: printf( "u, requested; "); break;
5009       case TN_NG_MU: printf( "u, required;  "); break;
5010     }
5011     switch (TELOPT_ME_MODE(TELOPT_BINARY)) {
5012       case TN_NG_AC: printf( "me, accepted; " ); break ;
5013       case TN_NG_RF: printf( "me, refused; " ); break;
5014       case TN_NG_RQ: printf( "me, requested; "); break;
5015       case TN_NG_MU: printf( "me, required;  "); break;
5016     }
5017     printf("u, %s; me, %s\n",
5018            TELOPT_U(TELOPT_BINARY) ? "BINARY" : "NVT",
5019            TELOPT_ME(TELOPT_BINARY) ? "BINARY" : "NVT"
5020            );
5021     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5022     printf(" binary-transfer-mode: %s\n",showoff(tn_b_xfer));
5023     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5024     printf(" bug binary-me-means-u-too: %s\n",showoff(tn_b_meu));
5025     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5026     printf(" bug binary-u-means-me-too: %s\n",showoff(tn_b_ume));
5027     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5028     printf(" bug sb-implies-will-do: %s\n",showoff(tn_sb_bug));
5029     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5030     printf(" bug auth-krb5-des: %s\n",showoff(tn_auth_krb5_des_bug));
5031     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5032     printf(" terminal-type: ");
5033     if (tn_term) {
5034         printf("%s\n",tn_term);
5035     } else {
5036         char *p;
5037 #ifdef OS2
5038         p = (tt_type >= 0 && tt_type <= max_tt) ?
5039           tt_info[tt_type].x_name :
5040             "UNKNOWN";
5041 #else
5042         p = getenv("TERM");
5043 #endif /* OS2 */
5044         if (p)
5045           printf("none (%s will be used)\n",p);
5046         else printf("none\n");
5047     }
5048     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5049 #ifdef CK_ENVIRONMENT
5050     printf(" environment: %s\n", showoff(tn_env_flg));
5051     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5052     printf("   ACCOUNT: %s\n",tn_env_acct);
5053     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5054     printf("   DISPLAY: %s\n",(char *)tn_get_display() ?
5055             (char *)tn_get_display() : "");
5056     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5057     printf("   JOB    : %s\n",tn_env_job);
5058     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5059     printf("   PRINTER: %s\n",tn_env_prnt);
5060     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5061 #ifndef NOSPL
5062     printf("   USER   : %s\n",uidbuf);
5063     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5064 #endif /* NOSPL */
5065     printf("   SYSTEM : %s\n",tn_env_sys);
5066     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5067     for (x = 0; x < 8; x++) {
5068         if (tn_env_uservar[x][0] && tn_env_uservar[x][1]) {
5069             printf("   %-7s: %s\n",tn_env_uservar[x][0],
5070                    tn_env_uservar[x][1]);
5071             if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5072         }
5073     }
5074 #endif /* CK_ENVIRONMENT */
5075 #ifdef CK_SNDLOC
5076     printf("  LOCATION: %s\n", tn_loc ? tn_loc : "");
5077     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5078 #endif /* CK_SNDLOC */
5079 #ifdef CK_FORWARD_X
5080     printf(" .Xauthority-file: %s\n", (char *)XauFileName() ?
5081             (char *)XauFileName() : "(none)");
5082     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5083 #endif /* CK_FORWARD_X */
5084     return(n);
5085 }
5086 #endif /* TNCODE */
5087
5088 #ifdef CK_NETBIOS
5089 static int
5090 shonb(n) int n; {
5091     printf("NETBIOS parameters:\n");
5092     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5093     printf(" API       : %s\n",
5094            NetbeuiAPI ?
5095            "NETAPI.DLL - IBM Extended Services or Novell Netware Requester"
5096            : "ACSNETB.DLL - IBM Network Transport Services/2" ) ;
5097     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5098     printf(" Local Name: [%s]\n", NetBiosName);
5099     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5100     printf(" Adapter   : %d\n", NetBiosAdapter);
5101     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5102     if (NetBiosLSN > 0xFF) {
5103         printf(" Session   : %d\n", NetBiosLSN);
5104     } else {
5105         printf(" Session   : none active\n");
5106     }
5107     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5108     return(n);
5109 }
5110 #endif /* CK_NETBIOS */
5111
5112 #ifndef NONET
5113 int
5114 shonet() {
5115
5116 #ifndef NETCONN
5117     printf("\nNo networks are supported in this version of C-Kermit\n");
5118
5119 #else
5120 #ifdef NOLOCAL
5121     printf("\nNo networks are supported in this version of C-Kermit\n");
5122
5123 #else /* rest of this routine */
5124
5125     int i, n = 4;
5126
5127 #ifndef NODIAL
5128     if (nnetdir <= 1) {
5129         printf("\nNetwork directory: %s\n",netdir[0] ? netdir[0] : "(none)");
5130         n++;
5131     } else {
5132         int i;
5133         printf("\nNetwork directories:\n");
5134         for (i = 0; i < nnetdir; i++) {
5135             printf("%2d. %s\n",i,netdir[i]);
5136             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5137         }
5138     }
5139 #endif /* NODIAL */
5140
5141 #ifdef SSHCMD
5142     {
5143         extern char * sshcmd;
5144         printf("SSH COMMAND: %s\n",sshcmd ? sshcmd : "ssh -e none");
5145         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5146     }
5147 #endif /* SSHCMD */
5148
5149 #ifdef OS2
5150     printf("\nNetwork availability:\n");
5151 #else
5152     printf("\nSupported networks:\n");
5153 #endif /* OS2 */
5154     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5155
5156 #ifdef VMS
5157
5158 #ifdef TCPWARE
5159     printf(" Process Software Corporation TCPware for OpenVMS");
5160 #else
5161 #ifdef MULTINET
5162     printf(" TGV MultiNet TCP/IP");
5163 #else
5164 #ifdef WINTCP
5165     printf(" WOLLONGONG WIN/TCP");
5166 #else
5167 #ifdef DEC_TCPIP
5168     {
5169         static $DESCRIPTOR(tcp_desc,"_TCP0:");
5170         int status;
5171         long devclass;
5172         static int itmcod = DVI$_DEVCLASS;
5173
5174 #ifdef COMMENT
5175         status = LIB$GETDVI(&itmcod, 0, &tcp_desc, &devclass);
5176 #else
5177         /* Martin Zinser 9/96 */
5178         status = lib$getdvi(&itmcod, 0, &tcp_desc, &devclass);
5179 #endif /* COMMENT */
5180         if ((status & 1) && (devclass == DC$_SCOM))
5181           printf(" Process Software Corporation TCPware for OpenVMS");
5182         else
5183 #ifdef UCX50
5184           printf(" DEC TCP/IP Services for (Open)VMS 5.0");
5185 #else
5186           printf(" DEC TCP/IP Services for (Open)VMS");
5187 #endif /* UCX50 */
5188     }
5189 #else
5190 #ifdef CMU_TCPIP
5191     printf(" CMU-OpenVMS/IP");
5192 #else
5193     printf(" None");
5194 #endif /* CMU_TCPIP */
5195 #endif /* DEC_TCPIP */
5196 #endif /* WINTCP */
5197 #endif /* MULTINET */
5198 #endif /* TCPWARE */
5199     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5200 #ifdef TNCODE
5201     printf(", TELNET protocol\n\n");
5202     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5203     n = shotel(n);
5204     if (n < 0) return(0);
5205     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5206 #endif /* TNCODE */
5207     printf("\n");
5208     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5209     printf("\n");
5210     n = shotcp(++n);
5211     if (n < 0) return(0);
5212 #else /* Not VMS */
5213
5214 #ifdef SUNX25
5215     printf(" SunLink X.25\n");
5216     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5217 #endif /* SUNX25 */
5218
5219 #ifdef STRATUSX25
5220     printf(" Stratus VOS X.25\n");
5221     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5222 #endif /* STRATUSX25 */
5223
5224 #ifdef IBMX25
5225     printf(" IBM AIX X.25\n");
5226     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5227 #endif /* IBMX25 */
5228
5229 #ifdef HPX25
5230     printf(" HP-UX X.25\n");
5231     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5232 #endif /* HPX25 */
5233
5234 #ifdef SSHBUILTIN
5235     if (ck_ssleay_is_installed())
5236         printf(" SSH V1 and V2 protocols\n");
5237     else
5238         printf(" SSH V1 and V2 protocols - not available\n");
5239 #endif /* SSHBUILTIN */
5240
5241 #ifdef DECNET
5242 #ifdef OS2
5243 #ifdef NT
5244     if (dnet_avail)
5245       printf(" DECnet, LAT and CTERM protocols\n");
5246     else
5247       printf(" DECnet, LAT and CTERM protocols - not available\n");
5248     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5249 #else /* NT */
5250     if (dnet_avail)
5251       printf(" DECnet, LAT protocol\n");
5252     else
5253       printf(" DECnet, LAT protocol - not available\n");
5254     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5255 #endif /* NT */
5256 #else
5257     printf(" DECnet\n");
5258     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5259 #endif /* OS2 */
5260 #endif /* DECNET */
5261
5262 #ifdef NPIPE
5263     printf(" Named Pipes\n");
5264     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5265 #endif /* NPIPE */
5266
5267 #ifdef CK_NETBIOS
5268     if (netbiosAvail)
5269       printf(" NETBIOS\n");
5270     else
5271       printf(" NETBIOS - not available\n");
5272     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5273 #endif /* CK_NETBIOS */
5274
5275 #ifdef SUPERLAT
5276     if (slat_avail)
5277       printf(" SuperLAT\n");
5278     else
5279       printf(" SuperLAT - not available\n") ;
5280     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5281 #endif /* SUPERLAT */
5282
5283 #ifdef TCPSOCKET
5284     if (
5285 #ifdef OS2
5286         tcp_avail
5287 #else
5288         1
5289 #endif /* OS2 */
5290         ) {
5291         char ipaddr[16];
5292
5293         if (getlocalipaddrs(ipaddr,16,0) < 0) {
5294 #ifdef OS2ONLY
5295             printf(" TCP/IP via %s\n", tcpname);
5296 #else
5297             printf(" TCP/IP\n");
5298 #endif /* OS2ONLY */
5299             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5300         } else {
5301             int i = 1;
5302 #ifdef OS2ONLY
5303           printf(" TCP/IP [%16s] via %s\n", ipaddr, tcpname);
5304 #else
5305           printf(" TCP/IP [%16s]\n",ipaddr);
5306 #endif /* OS2ONLY */
5307             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5308
5309             while (getlocalipaddrs(ipaddr,16,i++) >= 0) {
5310                 printf("        [%16s]\n",ipaddr);
5311                 if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5312             }
5313         }
5314         if (nettype == NET_TCPB) {
5315             printf("\n");
5316             n = shotcp(++n);
5317             if (n < 0) return(0);
5318 #ifdef TNCODE
5319             printf("\n");
5320             n = shotel(++n);
5321             if (n < 0) return(0);
5322 #endif /* TNCODE */
5323             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5324         }
5325 #ifdef OS2
5326     } else {
5327         printf(" TCP/IP - not available%s\n",tcpname[0] ? tcpname : "" );
5328         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5329 #endif /* OS2 */
5330     }
5331 #endif /* TCPSOCKET */
5332
5333 #ifdef CK_NETBIOS
5334     if (netbiosAvail && nettype == NET_BIOS) {
5335        printf("\n") ;
5336        if ((n = shonb(++n)) < 0) return(0);
5337        if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5338     }
5339 #endif /* CK_NETBIOS */
5340
5341 #endif /* VMS */
5342
5343     printf("\nActive network connection:\n");
5344     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5345
5346     if (network) {
5347         printf(" Host: %s",ttname);
5348         if ((nettype == NET_TCPA || nettype == NET_TCPB) && *ipaddr)
5349           printf(" [%s]",ipaddr);
5350     } else
5351       printf(" Host: none");
5352     printf(", via: ");
5353     if (nettype == NET_TCPA || nettype == NET_TCPB)
5354       printf("tcp/ip\n");
5355     else if (nettype == NET_SX25)
5356       printf("SunLink X.25\n");
5357     else if (nettype == NET_VX25)
5358       printf("Stratus VOS X.25\n");
5359     else if (nettype == NET_IX25)
5360       printf("IBM AIX X.25\n");
5361     else if (nettype == NET_HX25)
5362       printf("HP-UX X.25\n");
5363     else if (nettype == NET_DEC) {
5364         if ( ttnproto == NP_LAT )
5365           printf("DECnet LAT\n");
5366         else if ( ttnproto == NP_CTERM )
5367           printf("DECnet CTERM\n");
5368         else
5369           printf("DECnet\n");
5370     } else if (nettype == NET_PIPE)
5371       printf("Named Pipes\n");
5372     else if (nettype == NET_BIOS)
5373       printf("NetBIOS\n");
5374     else if (nettype == NET_SLAT)
5375       printf("SuperLAT\n");
5376
5377 #ifdef NETFILE
5378     else if ( nettype == NET_FILE )
5379       printf("local file\n");
5380 #endif /* NETFILE */
5381 #ifdef NETCMD
5382     else if ( nettype == NET_CMD )
5383       printf("pipe\n");
5384 #endif /* NETCMD */
5385 #ifdef NETPTY
5386     else if ( nettype == NET_PTY )
5387         printf("pseudoterminal\n");
5388 #endif /* NETPTY */
5389 #ifdef NETDLL
5390     else if ( nettype == NET_DLL )
5391       printf("dynamic link library\n");
5392 #endif /* NETDLL */
5393     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5394
5395 #ifdef ANYX25
5396     if ((nettype == NET_SX25) ||
5397         (nettype == NET_VX25) ||
5398         (nettype == NET_IX25))
5399       if ((n = shox25(n)) < 0) return(0);
5400 #endif /* ANYX25 */
5401
5402 #ifdef SSHBUILTIN
5403     if (nettype == NET_SSH) {
5404         printf("Secure Shell protocol\n");
5405         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5406     }
5407 #endif /* SSHBUILTIN */
5408
5409     if (nettype == NET_TCPA || nettype == NET_TCPB) {
5410 #ifdef RLOGCODE
5411         if (ttnproto == NP_RLOGIN) {
5412             printf(" LOGIN (rlogin) protocol\n");
5413             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5414         }
5415 #ifdef CK_KERBEROS
5416         else if (ttnproto == NP_K4LOGIN) {
5417             printf(" Kerberos 4 LOGIN (klogin) protocol\n");
5418             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5419         }
5420         else if (ttnproto == NP_EK4LOGIN) {
5421             printf(" Encrypted Kerberos 4 LOGIN (eklogin) protocol\n");
5422             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5423         }
5424         else if (ttnproto == NP_K5LOGIN) {
5425             printf(" Kerberos 5 LOGIN (klogin) protocol\n");
5426             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5427         }
5428         else if (ttnproto == NP_EK5LOGIN) {
5429             printf(" Encrypted Kerberos 5 LOGIN (eklogin) protocol\n");
5430             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5431         }
5432 #endif /* CK_KERBEROS */
5433 #endif /* RLOGCODE */
5434 #ifdef CK_KERBEROS
5435         if (ttnproto == NP_K5U2U) {
5436             printf(" Kerberos 5 User to User protocol\n");
5437             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5438         }
5439 #endif /* CK_KERBEROS */
5440
5441 #ifdef TNCODE
5442         if (IS_TELNET()) {
5443             printf(" TELNET protocol\n");
5444             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5445             printf(" Echoing is currently %s\n",duplex ? "local" : "remote");
5446             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5447         }
5448 #endif /* TNCODE */
5449         if (ttnproto == NP_TCPRAW) {
5450             printf(" Raw TCP socket\n");
5451             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5452         }
5453     }
5454     printf("\n");
5455 #endif /* NOLOCAL */
5456 #endif /* NETCONN */
5457     return(0);
5458 }
5459 #endif /* NONET */
5460
5461 #ifndef NODIAL
5462 VOID
5463 shodial() {
5464     if (mdmtyp >= 0 || local != 0) doshodial();
5465 }
5466
5467 VOID
5468 shods(s) char *s; {                     /* Show a dial-related string */
5469     char c;
5470     if (s == NULL || !(*s)) {           /* Empty? */
5471         printf("(none)\n");
5472     } else {                            /* Not empty. */
5473         while ((c = *s++))              /* Can contain controls */
5474           if (c == '\\')                /* a backslash */
5475             printf("\\\\");
5476           else if (c > 31 && c < 127) {
5477               putchar(c);
5478           } else
5479             printf("\\{%d}",c);
5480         printf("\n");
5481     }
5482 }
5483
5484 int
5485 doshodial() {
5486
5487     int i, n = 2;
5488
5489     printf(" Dial status:  %d", dialsta);
5490
5491 #ifdef BIGBUFOK
5492     if (dialsta > 90)
5493       printf(" = Unknown error");
5494     else if (dialsta < 0)
5495       printf(" = (none)");
5496     else if (dialsta < 35 && dialmsg[dialsta])
5497       printf(" = %s", dialmsg[dialsta]);
5498 #endif /* BIGBUFOK */
5499     n++;
5500     if (ndialdir <= 1) {
5501         printf("\n Dial directory: %s\n",dialdir[0] ? dialdir[0] : "(none)");
5502     } else {
5503         int i;
5504         printf("\n Dial directories:\n");
5505         for (i = 0; i < ndialdir; i++)
5506           printf("%2d. %s\n",i+1,dialdir[i]);
5507         n += ndialdir;
5508     }
5509     printf(" Dial method:  ");
5510     if      (dialmauto)         printf("auto   ");
5511     else if (dialmth == XYDM_D) printf("default");
5512     else if (dialmth == XYDM_P) printf("pulse  ");
5513     else if (dialmth == XYDM_T) printf("tone   ");
5514     printf("         Dial sort: %s\n",dialsrt ? "on" : "off");
5515     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5516     printf(" Dial hangup:  %s             Dial display: %s\n",
5517            dialhng ? "on " : "off", dialdpy ? "on" : "off");
5518     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5519     if (dialrtr > 0) {
5520         printf(" Dial retries: %-12d    Dial interval: %d\n",
5521                dialrtr, dialint);
5522     } else {
5523         printf(" Dial retries: (auto)          Dial interval: %d\n", dialint);
5524     }
5525     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5526     printf(" Dial timeout: ");
5527 #ifdef CK_TAPI
5528     if (tttapi && !tapipass)
5529         printf("(tapi)");
5530     else
5531 #endif /* CK_TAPI */
5532     if (dialtmo > 0)
5533       printf("%4d sec", dialtmo);
5534     else
5535       printf("0 (auto)");
5536     printf("        Redial number: %s\n",dialnum ? dialnum : "(none)");
5537     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5538     printf(" Dial confirmation: %s        Dial convert-directory: %s\n",
5539            dialcnf ? "on " : "off",
5540            dialcvt ? ((dialcvt == 1) ? "on" : "ask") : "off");
5541     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5542     printf(" Dial ignore-dialtone: %s", dialidt ? "on " : "off");
5543     printf("     Dial pacing: %d\n",dialpace);
5544     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5545     printf(
5546 " Dial prefix:                  %s\n", dialnpr ? dialnpr : "(none)");
5547     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5548     printf(
5549 " Dial suffix:                  %s\n", dialsfx ? dialsfx : "(none)");
5550     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5551     printf(
5552 " Dial country-code:            %-12s", diallcc ? diallcc : "(none)");
5553     printf("Dial connect:  %s", dialcon ? ((dialcon == 1) ? "on" : "auto")
5554            : "off");
5555     if (dialcon != CAR_OFF)
5556       printf(" %s", dialcq ? "quiet" : "verbose");
5557     printf(
5558 "\n Dial area-code:               %-12s", diallac ? diallac : "(none)");
5559     n++;
5560     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5561     printf("Dial restrict: ");
5562     if (dialrstr == 5) printf("international\n");
5563     else if (dialrstr == 4) printf("long-distance\n");
5564     else if (dialrstr == 2) printf("local\n");
5565     else if (dialrstr == 6) printf("none\n");
5566     else printf("?\n");
5567     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5568     printf(" Dial lc-area-codes:           ");
5569     if (nlocalac == 0)
5570       printf("(none)");
5571     else
5572       for (i = 0; i < nlocalac; i++)
5573         printf("%s ", diallcac[i]);
5574     printf(
5575 "\n Dial lc-prefix:               %s\n", diallcp ? diallcp : "(none)");
5576     n++;
5577     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5578     printf(
5579 " Dial lc-suffix:               %s\n", diallcs ? diallcs : "(none)");
5580     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5581     printf(
5582 " Dial ld-prefix:               %s\n", dialldp ? dialldp : "(none)");
5583     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5584     printf(
5585 " Dial ld-suffix:               %s\n", diallds ? diallds : "(none)");
5586     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5587     printf(
5588 " Dial force-long-distance      %s\n", showoff(dialfld));
5589     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5590     printf(
5591 " Dial intl-prefix:             %s\n", dialixp ? dialixp : "(none)");
5592     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5593     printf(
5594 " Dial intl-suffix:             %s\n", dialixs ? dialixs : "(none)");
5595     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5596     printf(
5597 " Dial toll-free-area-code:     ");
5598     if (ntollfree == 0)
5599       printf("(none)");
5600     else
5601       for (i = 0; i < ntollfree; i++)
5602         printf("%s ", dialtfc[i]);
5603     printf("\n");
5604     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5605
5606     printf(
5607 " Dial pulse-countries:         ");
5608     if (ndialpucc == 0)
5609       printf("(none)");
5610     else
5611       for (i = 0; i < ndialpucc; i++)
5612         printf("%s ", dialpucc[i]);
5613     printf("\n");
5614     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5615
5616     printf(
5617 " Dial tone-countries:          ");
5618     if (ndialtocc == 0)
5619       printf("(none)");
5620     else
5621       for (i = 0; i < ndialtocc; i++)
5622         printf("%s ", dialtocc[i]);
5623     printf("\n");
5624     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5625
5626     printf(
5627         " Dial toll-free-prefix:        %s\n",
5628         dialtfp ? dialtfp :
5629         (dialldp ? dialldp : "(none)")
5630         );
5631     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5632     printf(" Dial pbx-exchange:            ");
5633     if (ndialpxx == 0)
5634       printf("(none)");
5635     else
5636       for (i = 0; i < ndialpxx; i++)
5637         printf("%s ", dialpxx[i]);
5638     printf("\n");
5639
5640     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5641     printf(
5642 " Dial pbx-inside-prefix:       %s\n", dialpxi ? dialpxi : "(none)");
5643     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5644     printf(
5645 " Dial pbx-outside-prefix:      %s\n", dialpxo ? dialpxo : "(none)");
5646     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5647     printf(
5648 " Dial macro:                   %s\n", dialmac ? dialmac : "(none)");
5649     return(0);
5650 }
5651 #endif /* NODIAL */
5652 #endif /* NOLOCAL */
5653
5654 /*  Show File Parameters */
5655
5656 static char *
5657 pathval(x) int x; {
5658     switch (x) {
5659       case PATH_OFF:  return("off");
5660       case PATH_ABS:  return("absolute");
5661       case PATH_REL:  return("relative");
5662       case PATH_AUTO: return("auto");
5663       default: return("unknown");
5664     }
5665 }
5666
5667 VOID
5668 shofil() {
5669     char *s; int i = 0, n = 1;
5670     extern char * ifdnam[];
5671     extern int wildena;
5672 #ifdef UNIX
5673     extern int wildxpand;
5674 #endif /* UNIX */
5675     extern char * snd_move, * snd_rename, * rcv_move, * rcv_rename;
5676 #ifdef PATTERNS
5677     extern int patterns;
5678 #endif /* PATTERNS */
5679     extern char * rfspec, * sfspec;
5680 #ifdef UNIX
5681     extern int zobufsize, zofbuffer, zofblock;
5682 #endif /* UNIX */
5683 #ifdef CK_CTRLZ
5684     extern int eofmethod;
5685 #endif /* CK_CTRLZ */
5686
5687     printf("\n");
5688
5689 #ifdef VMS
5690     printf(" File record-Length:      %5d\n",frecl);
5691     n++;
5692 #endif /* VMS */
5693
5694 #ifndef NOXFER
5695     printf(" Transfer mode:           %s\n",
5696            xfermode == XMODE_A ?
5697            "automatic" :
5698            "manual"
5699            );
5700     n++;
5701 #ifdef PATTERNS
5702     printf(" File patterns:           %s", showooa(patterns));
5703     if (xfermode == XMODE_M && patterns)
5704       printf(" (but disabled by TRANSFER-MODE MANUAL)");
5705     else if (patterns)
5706       printf(" (SHOW PATTERNS for list)");
5707     printf("\n");
5708     n++;
5709 #endif /* PATTERNS */
5710     if (filepeek)
5711       printf(" File scan:               on %d\n", nscanfile);
5712     else
5713       printf(" File scan:               off\n");
5714     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5715     if (xfermode == XMODE_A)
5716       printf(" Default file type:       %s\n",shoxm());
5717     else
5718       printf(" File type:               %s\n",shoxm());
5719     n++;
5720     if (fncnv == XYFN_L)
5721       s = "literal";
5722     else if (fncnv == XYFN_C)
5723       s = "converted";
5724     else
5725       s = "(unknown)";
5726     printf(" File names:              %s\n",s);
5727     n++;
5728     printf(" Send pathnames:          %s\n", pathval(fnspath));
5729     n++;
5730     printf(" Receive pathnames:       %s\n", pathval(fnrpath));
5731     n++;
5732 #ifdef UNIXOROSK
5733     printf(" Match dot files:         %s\n", matchdot ? "yes" : "no");
5734     n++;
5735 #ifdef UNIX
5736     printf(" Wildcard-expansion:      %s (%s)\n", showoff(wildena),
5737            wildxpand ? "shell" : "kermit");
5738     n++;
5739 #endif /* UNIX */
5740 #endif /* UNIXOROSK */
5741     printf(" File collision:          ");
5742     for (i = 0; i < ncolx; i++)
5743       if (colxtab[i].kwval == fncact) break;
5744     printf("%s\n", (i == ncolx) ? "unknown" : colxtab[i].kwd);
5745     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5746     printf(" File destination:        %s\n",
5747            (dest == DEST_D) ? "disk" :
5748            ((dest == DEST_S) ? "screen" :
5749             ((dest == DEST_N) ? "nowhere" :
5750             "printer"))
5751            );
5752     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5753     s = (keep >= 0 && keep <= 2) ? ifdnam[keep] : "keep";
5754     printf(" File incomplete:         %s\n",s);
5755     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5756     printf(" File bytesize:           %d\n",(fmask == 0177) ? 7 : 8);
5757     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5758 #ifndef NOCSETS
5759     printf(" File character-set:      %s\n",fcsinfo[fcharset].keyword);
5760     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5761     printf(" File default 7-bit:      %s\n",fcsinfo[dcset7].keyword);
5762     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5763     printf(" File default 8-bit:      %s\n",fcsinfo[dcset8].keyword);
5764     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5765 #ifdef UNICODE
5766     printf(" File UCS bom:            %s\n",showoff(ucsbom));
5767     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5768     printf(" File UCS byte-order:     %s-endian\n",
5769            ucsorder ? "little" : "big");
5770     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5771     printf(" Computer byteorder:      %s-endian\n",
5772            byteorder ? "little" : "big");
5773     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5774 #endif /* UNICODE */
5775 #endif /* NOCSETS */
5776
5777     printf(" File end-of-line:        ");
5778     i = feol;
5779     switch (feol) {
5780       case XYFA_C: printf("%s\n","cr"); break;
5781       case XYFA_L: printf("%s\n","lf"); break;
5782       case XYFA_2: printf("%s\n","crlf"); break;
5783       default: printf("%d\n",i);
5784     }
5785     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5786 #endif /* NOXFER */
5787
5788 #ifdef CK_CTRLZ
5789     printf(" File eof:                %s\n", eofmethod ? "ctrl-z" : "length");
5790     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5791 #endif /* CK_CTRLZ */
5792 #ifndef NOXFER
5793 #ifdef CK_TMPDIR
5794     printf(" File download-directory: %s\n", dldir ? dldir : "(none)");
5795     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5796 #ifdef COMMENT
5797     i = 256;
5798     s = line;
5799     zzstring("\\v(tmpdir)",&s,&i);
5800     printf(" Temporary directory:     %s\n", line);
5801     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5802 #endif /* COMMENT */
5803 #endif /* CK_TMPDIR */
5804 #ifdef VMS
5805     {
5806         extern int vmssversions, vmsrversions;
5807         printf(" Send version-numbers:    %s\n",showoff(vmssversions));
5808         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5809         printf(" Receive version-numbers: %s\n",showoff(vmsrversions));
5810         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5811     }
5812 #endif /* VMS */
5813     printf(" Send move-to:            %s\n",
5814            snd_move ? snd_move : "(none)");
5815     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5816     printf(" Send rename-to:          %s\n",
5817            snd_rename ? snd_rename : "(none)");
5818     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5819     printf(" Receive move-to:         %s\n",
5820            rcv_move ? rcv_move : "(none)");
5821     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5822     printf(" Receive rename-to:       %s\n",
5823            rcv_rename ? rcv_rename : "(none)");
5824     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5825 #endif /* NOXFER */
5826 #ifdef KERMRC
5827     printf(" Initialization file:     %s\n", noinit ? "(none)" :
5828 #ifdef CK_SYSINI
5829            CK_SYSINI
5830 #else
5831            kermrc
5832 #endif /* CK_SYSINI */
5833            );
5834 #endif /* KERMRC */
5835     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5836
5837     if (k_info_dir) {
5838         printf(" Kermit doc files:        %s\n", k_info_dir);
5839         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5840     }
5841
5842 #ifdef CKROOT
5843     s = zgetroot();
5844     printf(" Root set:                %s\n", s ? s : "(none)");
5845 #endif /* CKROOT */
5846
5847 #ifdef UNIX
5848     printf(" Disk output buffer:      %d (writes are %s, %s)\n",
5849            zobufsize,
5850            zofbuffer ? "buffered" : "unbuffered",
5851            zofblock ? "blocking" : "nonblocking"
5852            );
5853     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5854 #ifdef DYNAMIC
5855     printf(" Stringspace:             %d\n", zsetfil(0,2));
5856     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5857     printf(" Listsize:                %d\n", zsetfil(0,4));
5858     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5859 #endif /* DYNAMIC */
5860 #endif /* UNIX */
5861 #ifdef OS2ORUNIX
5862     printf(" Longest filename:        %d\n", maxnam);
5863     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5864     printf(" Longest pathname:        %d\n", maxpath);
5865     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5866 #endif /* OS2ORUNIX */
5867
5868     printf(" Last file sent:          %s\n", sfspec ? sfspec : "(none)");
5869     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5870     printf(" Last file received:      %s\n", rfspec ? rfspec : "(none)");
5871     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5872     printf("\n Also see:\n");
5873     n++;
5874     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5875     printf(" SHOW PROTOCOL, SHOW XFER");
5876 #ifdef CK_LABELED
5877     printf(", SHOW LABELED");
5878 #endif /* CK_LABELED */
5879 #ifdef PATTERNS
5880     printf(", SHOW PATTERNS");
5881 #endif /* PATTERNS */
5882 #ifdef STREAMING
5883     printf(", SHOW STREAMING");
5884 #endif /* STREAMING */
5885 #ifndef NOCSETS
5886     printf(", SHOW CHARACTER-SETS");
5887 #endif /* NOCSETS */
5888     printf("\n\n");
5889 }
5890
5891 #ifndef NOXFER
5892 VOID
5893 shoparp() {                             /* Protocol */
5894     extern int docrc, skipbup;
5895     char *s;
5896
5897 #ifdef CK_TIMERS
5898     extern int rttflg;
5899 #endif /* CK_TIMERS */
5900
5901     printf("Protocol: %s\n",ptab[protocol].p_name);
5902
5903     if (protocol == PROTO_K) {
5904         printf("\nProtocol Parameters:   Send    Receive");
5905         if (timef)
5906           printf("\n Timeout (used=%2d):%7d*%8d ", timint, rtimo, pkttim);
5907         else
5908           printf("\n Timeout (used=%2d):%7d%9d ",  timint, rtimo, pkttim);
5909 #ifdef XFRCAN
5910         printf("       Cancellation:    %s",showoff(xfrcan));
5911         if (xfrcan)
5912           printf(" %d %d", xfrchr, xfrnum);
5913 #endif /* XFRCAN */
5914         printf("\n Padding:      %11d%9d", npad,   mypadn);
5915         if (bctr == 4)
5916           printf("        Block Check: blank-free-2\n");
5917         else
5918           printf("        Block Check: %6d\n",bctr);
5919         printf(  " Pad Character:%11d%9d", padch,  mypadc);
5920         printf("        Delay:       %6d\n",ckdelay);
5921         printf(  " Pause:        %11d%9d", pktpaus, pktpaus);
5922         printf("        Attributes:      %s\n",showoff(atcapr));
5923         printf(  " Packet Start: %11d%9d", mystch, stchr);
5924         printf("        Max Retries: %6d%s\n",
5925                maxtry,
5926                (maxtry == 0) ? " (unlimited)" : ""
5927                );
5928         printf(  " Packet End:   %11d%9d", seol,   eol);
5929         if (ebqflg)
5930           printf("        8th-Bit Prefix: '%c'",ebq);
5931         else
5932           printf("        8th-Bit Prefix: ('%c' but not used)",ebq);
5933         printf(  "\n Packet Length:%11d ", spmax);
5934         printf("%8d     ",  urpsiz);
5935         if (rptflg)
5936           printf("   Repeat Prefix:  '%c'",rptq);
5937         else
5938           printf("   Repeat Prefix:  ('%c' but not used)",rptq);
5939         printf(  "\n Maximum Length: %9d%9d", maxsps, maxrps);
5940         printf("        Window Size:%7d set, %d used\n",wslotr,wmax);
5941         printf(    " Buffer Size:  %11d%9d", bigsbsiz, bigrbsiz);
5942         printf("        Locking-Shift:    ");
5943         if (lscapu == 2) {
5944             printf("forced");
5945         } else {
5946             printf("%s", (lscapr ? "enabled" : "disabled"));
5947             if (lscapr) printf(",%s%s", (lscapu ? " " : " not "), "used");
5948         }
5949         printf("\n\n");
5950
5951         if (!(s = ptab[protocol].h_b_init)) s = "";
5952         printf(" Auto-upload command (binary): ");
5953         if (*s) {
5954             shostrdef((CHAR *)s);
5955             printf("\n");
5956         } else {
5957             printf("(none)\n");
5958         }
5959         if (!(s = ptab[protocol].h_t_init)) s = "";
5960         printf(" Auto-upload command (text):   ");
5961         if (*s) {
5962             shostrdef((CHAR *)s);
5963             printf("\n");
5964         } else {
5965             printf("(none)\n");
5966         }
5967         if (!(s = ptab[protocol].h_x_init)) s = "";
5968         printf(" Auto-server command:          ");
5969         if (*s) {
5970             shostrdef((CHAR *)s);
5971             printf("\n");
5972         } else {
5973             printf("(none)\n");
5974         }
5975         tmpbuf[0] = NUL;
5976 #ifdef CK_TIMERS
5977         if (rttflg) {
5978             extern int mintime, maxtime;
5979             sprintf(tmpbuf," Packet timeouts: dynamic %d:%d", /* SAFE */
5980                     mintime,
5981                     maxtime);
5982         } else {
5983             sprintf(tmpbuf," Packet timeouts: fixed"); /* SAFE */
5984         }
5985 #endif /* CK_TIMERS */
5986         if (tmpbuf[0])
5987           printf("%-31s",tmpbuf);
5988         printf("Send backup: %s\n",showoff(!skipbup));
5989
5990         printf(" Transfer mode:   %s", xfermode == XMODE_A ?
5991                "automatic   " :
5992                "manual      "
5993                );
5994         printf(" Transfer slow-start: %s, crc: %s\n",
5995                showoff(slostart),
5996                showoff(docrc)
5997                );
5998 #ifdef PIPESEND
5999         {
6000             extern int usepipes;
6001             printf(" Transfer pipes:  %s         ",usepipes ? "on " : "off");
6002         }
6003 #endif /* PIPESEND */
6004 #ifndef NOCSETS
6005         printf(" Transfer character-set: ");
6006         if (tcharset == TC_TRANSP)
6007           printf("transparent\n");
6008         else
6009           printf("%s\n", tcsinfo[tcharset].keyword );
6010 #endif /* NOCSETS */
6011 #ifdef PIPESEND
6012         {
6013             extern char * sndfilter, * rcvfilter;
6014             printf(" Send filter:     %s\n", sndfilter ? sndfilter : "(none)");
6015             printf(" Receive filter:  %s\n", rcvfilter ? rcvfilter : "(none)");
6016         }
6017 #endif /* PIPESEND */
6018         printf("\nAlso see:\n");
6019         printf(" SHOW FILE, SHOW XFER");
6020
6021 #ifdef CK_LABELED
6022         printf(", SHOW LABELED");
6023 #endif /* CK_LABELED */
6024 #ifdef PATTERNS
6025         printf(", SHOW PATTERNS");
6026 #endif /* PATTERNS */
6027 #ifdef STREAMING
6028         printf(", SHOW STREAMING");
6029 #endif /* STREAMING */
6030 #ifndef NOCSETS
6031         printf(", SHOW CHARACTER-SETS");
6032 #endif /* NOCSETS */
6033     }
6034
6035 #ifdef CK_XYZ
6036 #ifdef XYZ_INTERNAL
6037     if (protocol != PROTO_K) {
6038         int i;
6039         int x;
6040         printf(" File type: %s\n", binary ? "binary" : "text");
6041         if (protocol == PROTO_Z) {              /* Zmodem */
6042             printf(" Window size:   ");
6043             if (ptab[protocol].winsize < 1)
6044               printf("none\n");
6045             else
6046               printf("%d\n",wslotr);
6047 #ifdef COMMENT
6048             printf(" Packet (frame) length: ");
6049             if (ptab[protocol].spktlen < 0)
6050               printf("none\n");
6051             else
6052               printf("%d\n",spmax);
6053 #endif /* COMMENT */
6054         } else {
6055             if (ptab[protocol].spktlen >= 1000)
6056               printf(" 1K packets\n");
6057             else
6058               printf(" 128-byte packets\n");
6059         }
6060         printf(" Pathname stripping when sending:   %s\n",
6061                showoff(ptab[protocol].fnsp)
6062                );
6063         printf(" Pathname stripping when receiving: %s\n",
6064                showoff(ptab[protocol].fnrp)
6065                );
6066         printf(" Filename collision action:         ");
6067         for (i = 0; i < ncolx; i++)
6068           if (colxtab[i].kwval == fncact) break;
6069         printf("%-12s", (i == ncolx) ? "unknown" : colxtab[i].kwd);
6070
6071         printf("\n Escape control characters:          ");
6072         x = ptab[protocol].prefix;
6073         if (x == PX_ALL)
6074           printf("all\n");
6075         else if (x == PX_CAU || x==PX_WIL)
6076           printf("minimal\n");
6077         else
6078           printf("none\n");
6079         if (!(s = ptab[protocol].h_b_init))
6080           s = "";
6081         printf(" Autoreceive command (binary): %s\n", *s ? s : "(none)");
6082         if (!(s = ptab[protocol].h_t_init))
6083           s = "";
6084         printf(" Autoreceive command (text):   %s\n", *s ? s : "(none)");
6085     }
6086 #else
6087 #ifndef NOPUSH
6088     if (protocol != PROTO_K) {
6089         _PROTOTYP( VOID shoextern, (void) );
6090         printf("\nExecuted by external commands:\n\n");
6091         s = ptab[protocol].p_b_scmd;
6092         if (!s) s = "";
6093         printf(" SEND command (binary):        %s\n", *s ? s : "(none)");
6094         s = ptab[protocol].p_t_scmd;
6095         if (!s) s = "";
6096         printf(" SEND command (text):          %s\n", *s ? s : "(none)");
6097         s = ptab[protocol].p_b_rcmd;
6098         if (!s) s = "";
6099         printf(" RECEIVE command (binary):     %s\n", *s ? s : "(none)");
6100         s = ptab[protocol].p_t_rcmd;
6101         if (!s) s = "";
6102         printf(" RECEIVE command (text):       %s\n", *s ? s : "(none)");
6103         s = ptab[protocol].h_b_init;
6104         if (!s) s = "";
6105         printf(" Autoreceive command (binary): %s\n", *s ? s : "(none)");
6106         s = ptab[protocol].h_t_init;
6107         if (!s) s = "";
6108         printf(" Autoreceive command (text):   %s\n", *s ? s : "(none)");
6109         (VOID) shoextern();
6110     }
6111 #endif /* NOPUSH */
6112 #endif /* XYZ_INTERNAL */
6113 #endif /* CK_XYZ */
6114 }
6115 #endif /* NOXFER */
6116
6117 #ifndef NOCSETS
6118 /* Character-set items */
6119
6120 extern int s_cset, r_cset, axcset[], afcset[];
6121 extern struct keytab xfrmtab[];
6122
6123 VOID
6124 shoparl() {
6125 #ifdef COMMENT
6126     int i;
6127 /* Misleading... */
6128     printf("\nAvailable Languages:\n");
6129     for (i = 0; i < MAXLANG; i++) {
6130         printf(" %s\n",langs[i].description);
6131     }
6132 #else
6133     printf("\nLanguage-specific translation rules: %s\n",
6134            language == L_USASCII ? "none" : langs[language].description);
6135     shocharset();
6136     printf("\n\n");
6137 #endif /* COMMENT */
6138 }
6139
6140 VOID
6141 shocharset() {
6142     int x;
6143 #ifdef COMMENT
6144     char * s = "Unknown";
6145     extern int xlatype;
6146 #endif /* COMMENT */
6147
6148 #ifndef NOXFER
6149     extern int xfrxla;
6150 #endif /* NOXFER */
6151
6152     debug(F101,"SHOW FILE CHAR","",fcharset);
6153     printf("\n");
6154 #ifndef NOXFER
6155     printf(" Transfer Translation: %s\n", showoff(xfrxla));
6156     if (!xfrxla) {
6157         printf(
6158       " Because transfer translation is off, the following are ignored:\n\n");
6159     }
6160 #endif /* NOXFER */
6161     printf(" File Character-Set: %s (%s), ",
6162            fcsinfo[fcharset].keyword,
6163            fcsinfo[fcharset].name
6164            );
6165     if ((x = fcsinfo[fcharset].size) == 128)
6166       printf("7-bit");
6167     else if (x == 256)
6168       printf("8-bit");
6169     else
6170       printf("multibyte");
6171     printf("\n");
6172     printf(" File Scan: %s\n",showoff(filepeek));
6173     printf("   Default 7bit-Character-Set: %s\n",fcsinfo[dcset7].keyword);
6174     printf("   Default 8bit-Character-Set: %s\n",fcsinfo[dcset8].keyword);
6175     printf(" Transfer Character-Set");
6176 #ifdef COMMENT
6177     if (tslevel == TS_L2)
6178       printf(": (international)");
6179     else
6180 #endif /* COMMENT */
6181     if (tcharset == TC_TRANSP)
6182       printf(": Transparent");
6183     else
6184       printf(": %s (%s)",tcsinfo[tcharset].keyword, tcsinfo[tcharset].name);
6185     printf("\n");
6186 #ifdef COMMENT
6187     switch (xlatype) {
6188       case XLA_NONE: s = "None"; break;
6189       case XLA_BYTE: s = "Byte"; break;
6190       case XLA_JAPAN: s = "Japanese"; break;
6191       case XLA_UNICODE: s = "Unicode"; break;
6192     }
6193     printf("\n Translation type: %s\n",s);
6194 #endif /* COMMENT */
6195     printf(" SEND character-set-selection: %s\n",xfrmtab[s_cset].kwd);
6196     printf(" RECEIVE character-set-selection: %s\n",xfrmtab[r_cset].kwd);
6197     if (s_cset == XMODE_A || r_cset == XMODE_A)
6198       printf(
6199       " (Use SHOW ASSOCIATIONS to list automatic character-set selections.)\n"
6200              );
6201 }
6202
6203 VOID
6204 showassoc() {
6205     int i, k, n = 4;
6206     char * s;
6207     printf("\nFor incoming files:\n\n");
6208     printf("Transfer Character-Set   File Character-Set\n");
6209     for (i = 1; i <= MAXTCSETS; i++) {
6210         k = axcset[i];
6211         if (k < 0 || k > MAXFCSETS)
6212           s = "(none)";
6213         else
6214           s = fcsinfo[k].keyword;
6215         if (!s) s = "";
6216         if (!*s) s = "(none)";
6217         printf(" %-25s%s\n",tcsinfo[i].keyword,s);
6218         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
6219     }
6220     printf("\nFor outbound files:\n\n");
6221     n += 2;
6222     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
6223     printf("File Character-Set       Transfer Character-Set\n");
6224     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
6225     for (i = 0; i <= MAXFCSETS; i++) {
6226         k = afcset[i];
6227         if (k < 0 || k > MAXTCSETS)
6228           s = "(none)";
6229         else
6230           s = tcsinfo[k].keyword;
6231         if (!s) s = "";
6232         if (!*s) s = "(none)";
6233         printf(" %-25s%s\n",fcsinfo[i].keyword,s);
6234         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
6235     }
6236 }
6237 #endif /* NOCSETS */
6238
6239 VOID
6240 shopar() {
6241     printf("Show what?  (Type \"show ?\" for a list of possibilities.)\n");
6242 }
6243 #endif /* NOSHOW */
6244
6245 #ifndef NOXFER
6246 /*  D O S T A T  --  Display file transfer statistics.  */
6247
6248 int
6249 dostat(brief) int brief; {
6250     extern long filrej, peakcps;
6251     extern int lastspmax, streamed, cleared, streamok;
6252     extern char whoareu[];
6253     int n = 0, ftp = 0;
6254     extern int docrc, interrupted, fatalio;
6255
6256     ftp = lastxfer & W_FTP;
6257
6258 #ifdef CK_TTGWSIZ
6259 #ifdef OS2
6260     if (tt_cols[VTERM] < 0 || tt_rows[VTERM] < 0)
6261       ttgwsiz();
6262 #else /* OS2 */
6263     if (ttgwsiz() > 0) {
6264         if (tt_rows > 0 && tt_cols > 0) {
6265             cmd_rows = tt_rows;
6266             cmd_cols = tt_cols;
6267         }
6268     }
6269 #endif /* OS2 */
6270 #endif /* CK_TTGWSIZ */
6271
6272     debug(F101,"dostat xferstat","",xferstat);
6273     if (xferstat < 0) {
6274         printf(" No file transfers yet.\n");
6275         return(1);
6276     }
6277     n = 0;
6278     if (brief) { printf("\n"); n++; };
6279     printf(" protocol               : %s\n",
6280            ftp ? "ftp" : ptab[protocol].p_name);
6281     n++;
6282     printf(" status                 : ");
6283     if (xferstat) printf("SUCCESS\n");
6284     else if (interrupted) printf("FAILURE (interrupted)\n");
6285     else if (fatalio) printf("FAILURE (i/o error)\n");
6286     else printf("FAILURE\n");
6287 #ifndef XYZ_INTERNAL
6288     if (!ftp && protocol != PROTO_K) {
6289         printf("\n external protocol statistics not available\n");
6290         return(1);
6291     }
6292 #endif /* XYZ_INTERNAL */
6293     n++;
6294     if (!ftp) {
6295         if (!xferstat > 0) {
6296             if (docrc)
6297               printf(" crc-16 of file(s)      : %ld\n", crc16);
6298             else
6299               printf(" crc-16 of file(s)      : (disabled)\n");
6300             n++;
6301         }
6302         if (!xferstat && *epktmsg) {
6303             printf(" reason                 : %s\n", epktmsg);
6304             n++;
6305         }
6306     }
6307     if (!brief) {
6308 #ifdef NEWFTP
6309         if (ftp) {
6310             extern char ftp_srvtyp[];
6311             printf(" remote system type     : %s\n",ftp_srvtyp);
6312         } else
6313 #endif /* NEWFTP */
6314           if (whoareu[0]) {
6315             printf(" remote system type     : %s\n",
6316                    getsysid((char *)whoareu));
6317             n++;
6318         }
6319         printf(" files transferred      : %ld\n",filcnt - filrej);
6320         if (!ftp)
6321           printf(" files not transferred  : %ld\n",filrej);
6322         printf(" characters last file   : %s\n",ckfstoa(ffc));
6323         printf(" total file characters  : %s\n",ckfstoa(tfc));
6324         n += ftp ? 3 : 4;
6325         if (!ftp) {
6326             printf(" communication line in  : %s\n",ckfstoa(tlci));
6327             printf(" communication line out : %s\n",ckfstoa(tlco));
6328             printf(" packets sent           : %d\n", spackets);
6329             printf(" packets received       : %d\n", rpackets);
6330             n += 4;
6331         }
6332     }
6333     if (ftp) goto dotimes;
6334
6335     printf(" damaged packets rec'd  : %d\n", crunched);
6336     printf(" timeouts               : %d\n", timeouts);
6337     printf(" retransmissions        : %d\n", retrans);
6338     n += 3;
6339
6340     if (!brief) {
6341         if (filcnt > 0) {
6342             printf(" parity                 : %s",parnam((char)parity));
6343             n++;
6344             if (autopar) { printf(" (detected automatically)"); n++; }
6345             printf(
6346                  "\n control characters     : %ld prefixed, %ld unprefixed\n",
6347                    ccp, ccu);
6348             n++;
6349             printf(" 8th bit prefixing      : ");
6350             n++;
6351             if (ebqflg) printf("yes [%c]\n",ebq); else printf("no\n");
6352             n++;
6353             printf(" locking shifts         : %s\n", lscapu ? "yes" : "no");
6354             n++;
6355         }
6356     }
6357     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6358     if (streamed > 0)
6359       printf(" window slots used      : (streaming)\n");
6360     else
6361       printf(" window slots used      : %d of %d\n", wmax, wslotr);
6362     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6363     printf(" reliable:              : %s%s\n",
6364            streamok ? "" : "not ", "negotiated");
6365     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6366     printf(" clearchannel:          : %s%s\n",
6367            cleared  ? "" : "not ", "negotiated");
6368     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6369
6370     if (!brief) {
6371         printf(" packet length          : %d (send), %d (receive)\n",
6372                lastspmax, urpsiz);
6373         if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6374         printf(" compression            : ");
6375         if (rptflg)
6376           printf("yes [%c] (%ld)\n",(char) rptq,rptn);
6377         else
6378           printf("no\n");
6379         if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6380         if (bctu == 4)
6381           printf(" block check type used  : blank-free-2\n");
6382         else
6383           printf(" block check type used  : %d\n",bctu);
6384         if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6385     }
6386
6387   dotimes:
6388
6389 #ifdef GFTIMER
6390 #ifdef COMMENT
6391     printf(" elapsed time           : %0.3f sec, %s\n", fptsecs,hhmmss(tsecs));
6392 #endif /* COMMENT */
6393     printf(" elapsed time           : %s (%0.3f sec)\n",
6394            hhmmss((long)(fptsecs + 0.5)),fptsecs);
6395 #else
6396 #ifdef COMMENT
6397     printf(" elapsed time           : %s (%d sec)\n",hhmmss(tsecs),tsecs);
6398 #endif /* COMMENT */
6399     printf(" elapsed time           : %d sec, %s\n",tsecs,hhmmss(tsecs));
6400 #endif /* GFTIMER */
6401     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6402     if (!ftp && local && !network && !brief) {
6403         if (speed <= 0L) speed = ttgspd();
6404         if (speed > 0L) {
6405             if (speed == 8880)
6406               printf(" transmission rate      : 75/1200 bps\n");
6407             else
6408               printf(" transmission rate      : %ld bps\n",speed);
6409             if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6410         }
6411     }
6412     if (!ftp && local && !network &&    /* Only makes sense for */
6413         mdmtyp == 0 &&                  /* direct serial connections */
6414         speed > 99L &&                  /* when we really know the speed */
6415         speed != 8880L
6416         ) {
6417         int eff;
6418         eff = (((tfcps * 100L) / (speed / 100L)) + 5L) / 10L;
6419         printf(" effective data rate    : %ld cps (%d%%)\n",tfcps,eff);
6420     } else
6421       printf(" effective data rate    : %ld cps\n", tfcps);
6422     if (!ftp && peakcps > 0L && peakcps > tfcps)
6423       printf(" peak data rate         : %ld cps\n", peakcps);
6424     if (brief)
6425       printf("\nUse STATISTICS /VERBOSE for greater detail.\n\n");
6426     return(1);
6427 }
6428 #endif /* NOXFER */
6429
6430 #ifndef NOSPL
6431
6432 /* The INPUT command */
6433
6434 /*
6435   NOTE: An INPUT timeout of 0 means to perform a nonblocking read of the
6436   material that has already arrived and is waiting to be read, and perform
6437   matches against it, without doing any further reads.  It should succeed
6438   or fail instantaneously.
6439 */
6440
6441 /* Output buffering for "doinput" */
6442
6443 #ifdef pdp11
6444 #define MAXBURST 16             /* Maximum size of input burst */
6445 #else
6446 #define MAXBURST 1024
6447 #endif /* pdp11 */
6448 #ifdef OSK
6449 static CHAR *conbuf;            /* Buffer to hold output for console */
6450 #else
6451 static CHAR conbuf[MAXBURST];   /* Buffer to hold output for console */
6452 #endif /* OSK */
6453 static int concnt = 0;          /* Number of characters buffered */
6454 #ifdef OSK
6455 static CHAR *sesbuf;            /* Buffer to hold output for session log */
6456 #else
6457 static CHAR sesbuf[MAXBURST];   /* Buffer to hold output for session log */
6458 #endif /* OSK */
6459 static int sescnt = 0;          /* Number of characters buffered */
6460
6461 extern int debses;                      /* TERMINAL DEBUG ON/OFF */
6462
6463 static VOID                             /* Flush INPUT echoing */
6464 myflsh() {                              /* and session log output. */
6465     if (concnt > 0) {
6466         if (debses) {                   /* Terminal debugging? */
6467             int i;
6468             for (i = 0; i < concnt; i++)
6469               conol(dbchr(conbuf[i]));
6470         } else
6471           conxo(concnt, (char *) conbuf);
6472         concnt = 0;
6473     }
6474     if (sescnt > 0) {
6475         logstr((char *) sesbuf, sescnt);
6476         sescnt = 0;
6477     }
6478 }
6479
6480 /* Execute the INPUT and MINPUT commands */
6481
6482 int instatus = -1;
6483 long inetime = -1L;
6484 int inwait = 0;
6485 int nowrap = 0;
6486
6487 /* For returning the input sequence that matched */
6488
6489 #ifdef BIGBUFOK
6490 #define MATCHBUFSIZ 8191
6491 #else
6492 #define MATCHBUFSIZ 1023
6493 #endif /* BIGBUFOK */
6494 static char * matchbuf = NULL;
6495 static int matchindex = 0;
6496 static int burst = 0;                      /* Chars remaining in input burst */
6497 /*
6498   timo = How long to wait:
6499          < 0 = Wait forever
6500            0 = Don't wait at all - material must already have arrived
6501          > 0 = Wait this many seconds
6502   ms   = Array of strings to wait for.
6503   mp   = Array of flags.
6504          If mp[i] == 0, ms[i] is literal, else it's a pattern.
6505   flags = bit mask
6506     INPSW_NOM = /NOMATCH = 1
6507     INPSW_CLR = /CLEAR   = 2
6508     INPSW_NOW = /NOWRAP  = 4
6509     INPSW_COU = /COUNT   = 8
6510   count = /COUNT: value if a /COUNT switch was given.
6511
6512  Returns:
6513     0 on failure, 1 on success.
6514 */
6515 #ifndef ES_NORMAL
6516 #define ES_NORMAL 0
6517 #endif  /* ES_NORMAL */
6518 extern int inesc[], oldesc[];
6519
6520 int
6521 doinput(timo,ms,mp,flags,count)
6522     int timo; char *ms[]; int mp[]; int flags; int count; {
6523     extern int inintr;
6524 #ifdef CK_AUTODL
6525     extern int inautodl;
6526 #endif /* CK_AUTODL */
6527     int x, y, i, t, rt, icn, anychar = 0, mi[MINPMAX];
6528 #ifdef GFTIMER
6529     CKFLOAT fpt = 0.0;
6530 #endif /* GFTIMER */
6531     int savecount = 0;
6532     int nomatch = 0;
6533     int clearfirst = 0;
6534     int lastchar = 0;
6535     int waiting = 0;
6536     int imask = 0;
6537     char ch, *xp, *s;
6538     CHAR c;
6539 #ifndef NOLOCAL
6540 #ifdef OS2
6541     extern int term_io;
6542     int term_io_save;
6543 #endif /* OS2 */
6544 #endif /* NOLOCAL */
6545 #ifdef TNCODE
6546     static int cr = 0;
6547 #endif /* TNCODE */
6548     int is_tn = 0;
6549 #ifdef SSHBUILTIN
6550     extern int ssh_cas;
6551     extern char * ssh_cmd;
6552 #endif /* SSHBUILTIN */
6553     int noescseq = 0;                   /* Filter escape sequences */
6554
6555     debug(F101,"input count","",count);
6556     debug(F101,"input flags","",flags);
6557
6558 /*
6559   CK_BURST enables the INPUT speedup code, which depends on ttchk() returning
6560   accurate information.  If INPUT fails with this code enabled, change the
6561   above "#define" to "#undef".
6562 */
6563 #define CK_BURST
6564
6565 /***** CHANGE THIS TO A SET INPUT PARAMETER *****/
6566
6567     noescseq = (sessft == XYFT_T);      /* Filter escape sequences */
6568
6569     imask = cmask;
6570     if (parity) imask = 0x7f;
6571     inwait = timo;                      /* For \v(inwait) */
6572
6573     /* Options from command switches */
6574
6575     nowrap = flags & INPSW_NOW;         /* 4 = /NOWRAP */
6576     nomatch = flags & INPSW_NOM;        /* 1 = /NOMATCH */
6577     clearfirst = flags & INPSW_CLR;     /* 2 = /CLEAR */
6578     savecount = count;
6579
6580     makestr(&inpmatch,NULL);
6581     if (!matchbuf) {
6582         matchbuf = malloc(MATCHBUFSIZ+1);
6583         matchbuf[0] = NUL;
6584     }
6585     matchindex = 0;
6586
6587     /* If last time through we returned because of /NOWRAP and buffer full */
6588     /* now we have to clear the buffer to make room for another load. */
6589
6590     if (nowrap && instatus == INP_BF)
6591       clearfirst = 1;
6592
6593     if (clearfirst) {                   /* INPUT /CLEAR */
6594         int i;
6595         myflsh();                       /* Flush screen and log buffers */
6596         for (i = 0; i < inbufsize; i++)
6597           inpbuf[i] = NUL;
6598         inpbp = inpbuf;
6599     }
6600     is_tn =
6601 #ifdef TNCODE
6602         (local && network && IS_TELNET()) || (!local && sstelnet)
6603 #else
6604          0
6605 #endif /* TNCODE */
6606           ;
6607
6608 #ifdef CK_SSL
6609     if (is_tn) if (ssl_raw_flag || tls_raw_flag) is_tn = 0;
6610 #endif  /* CK_SSL */
6611
6612     instatus = INP_IE;                  /* 3 = internal error */
6613     kbchar = 0;
6614
6615 #ifdef OSK
6616     if (conbuf == NULL) {
6617         if ((conbuf = (CHAR *)malloc(MAXBURST*2)) == NULL) {
6618             return(0);
6619         }
6620         sesbuf = conbuf + MAXBURST;
6621     }
6622 #endif /* OSK */
6623
6624 #ifndef NOLOCAL
6625     if (local) {                        /* In local mode... */
6626         if ((waiting = ttchk()) < 0) {  /* check that connection is open */
6627             if (!quiet) {
6628                 if ((!network 
6629 #ifdef TN_COMPORT
6630                       || istncomport()
6631 #endif /* TN_COMPORT */
6632                       ) && carrier != CAR_OFF)
6633                     printf("?Carrier detect failure on %s.\n", ttname);
6634                 else
6635                     printf("?Connection %s %s is not open.\n",
6636                        network ? "to" : "on",
6637                        ttname
6638                        );
6639             }
6640             instatus = INP_IO;
6641             return(0);
6642         }
6643         debug(F101,"doinput waiting","",waiting);
6644         y = ttvt(speed,flow);           /* Put line in "ttvt" mode */
6645         if (y < 0) {
6646             printf("?INPUT initialization error\n");
6647             instatus = INP_IO;
6648             return(0);                  /* Watch out for failure. */
6649         }
6650     }
6651 #endif /* NOLOCAL */
6652
6653 #ifdef SSHBUILTIN
6654     if ( network && nettype == NET_SSH && ssh_cas && ssh_cmd && 
6655          !(strcmp(ssh_cmd,"kermit") && strcmp(ssh_cmd,"sftp"))) {
6656         if (!quiet)
6657           printf("?SSH Subsystem active: %s\n", ssh_cmd);
6658         instatus = INP_IKS;
6659         return(0);
6660     }
6661 #endif /* SSHBUILTIN */
6662
6663     debug(F111,"doinput ms[0]",ms[0],waiting);
6664
6665     if (!ms[0] || isemptystring(ms[0])) { /* No search string was given nor */
6666         if (count < 2)                    /* a /COUNT: switch so we just */
6667           anychar = 1;                    /* wait for the first character */
6668     }
6669     if (nomatch) anychar = 0;           /* Don't match anything */
6670
6671     if (!anychar && waiting == 0 && timo == 0)
6672       return(0);
6673
6674 #ifndef NODEBUG
6675     if (deblog) {
6676         char xbuf[24];
6677         debug(F101,"doinput anychar","",anychar);
6678         debug(F101,"doinput timo","",timo);
6679         debug(F101,"doinput echo","",inecho);
6680 #ifdef CK_BURST
6681         debug(F101,"doinput burst","",burst);
6682 #endif  /* CK_BURST */
6683         y = -1;
6684         while (ms[++y]) {
6685             sprintf(xbuf,"doinput string %2d",y); /* SAFE (24) */
6686             debug(F111,xbuf,ms[y],mp[y]);
6687         }
6688     }
6689 #endif /* NODEBUG */
6690
6691 #ifdef IKS_OPTION
6692     if (is_tn) {
6693         /* If the remote side is in a state of IKS START-SERVER    */
6694         /* we request that the state be changed.  We will detect   */
6695         /* a failure to adhere to the request when we call ttinc() */
6696         if (TELOPT_U(TELOPT_KERMIT) &&
6697             TELOPT_SB(TELOPT_KERMIT).kermit.u_start)
6698           iks_wait(KERMIT_REQ_STOP,0);  /* Send Request-Stop */
6699 #ifdef CK_AUTODL
6700         /* If we are processing packets during INPUT and we have not */
6701         /* sent a START message, do so now.                          */
6702         if (inautodl && TELOPT_ME(TELOPT_KERMIT) &&
6703                         !TELOPT_SB(TELOPT_KERMIT).kermit.me_start) {
6704             tn_siks(KERMIT_START);      /* Send Kermit-Server Start */
6705         }
6706 #endif /* CK_AUTODL */
6707     }
6708 #endif /* IKS_OPTION */
6709     x = 0;                              /* Return code, assume failure */
6710     instatus = INP_TO;                  /* Status, assume timeout */
6711
6712     for (y = 0; y < MINPMAX; y++)       /* Initialize... */
6713       mi[y] = 0;                        /*  ..string pattern match position */
6714
6715     if (!inpcas[cmdlvl]) {              /* INPUT CASE = IGNORE?  */
6716         y = -1;
6717         while ((xp = ms[++y])) {        /* Convert each target to lowercase */
6718             while (*xp) {
6719                 if (isupper(*xp)) *xp = (char) tolower(*xp);
6720                 xp++;
6721             }
6722         }
6723     }
6724     rtimer();                           /* Reset timer. */
6725 #ifdef GFTIMER
6726     rftimer();                          /* Floating-point timer too. */
6727 #endif /* GFTIMER */
6728     inetime = -1L;                      /* Initialize elapsed time. */
6729     t = 0;                              /* Time now is 0. */
6730     m_found = 0;                        /* Default to timed-out */
6731     incount = 0;                        /* Character counter */
6732     rt = (timo == 0) ? 0 : 1;           /* Character-read timeout interval */
6733
6734 #ifndef NOLOCAL
6735 #ifdef OS2
6736     term_io_save = term_io;             /* Disable I/O by emulator */
6737     term_io = 0;
6738 #endif /* OS2 */
6739 #endif /* NOLOCAL */
6740
6741     while (1) {                         /* Character-getting loop */
6742 #ifdef CK_APC
6743         /* Check to see if there is an Autodown or other APC command */
6744         if (apcactive == APC_LOCAL ||
6745             (apcactive == APC_REMOTE && apcstatus != APC_OFF)) {
6746             if (mlook(mactab,"_apc_commands",nmac) == -1) {
6747                 debug(F110,"doinput about to execute APC",apcbuf,0);
6748                 domac("_apc_commands",apcbuf,cmdstk[cmdlvl].ccflgs|CF_APC);
6749                 delmac("_apc_commands",1);
6750                 apcactive = APC_INACTIVE;
6751 #ifdef DEBUG
6752             } else {
6753                 debug(F100,"doinput APC in progress","",0);
6754 #endif /* DEBUG */
6755             }
6756         }
6757 #endif /* CK_APC */
6758
6759         if (timo == 0 && waiting < 1) { /* Special exit criterion */
6760             instatus = INP_TO;          /* for timeout == 0 */
6761             break;
6762         }
6763         if (local) {                    /* One case for local */
6764             y = ttinc(rt);              /* Get character from comm device */
6765             debug(F101,"doinput ttinc(rt) returns","",y);
6766             if (y < -1) {               /* Connection failed. */
6767                 instatus = INP_IO;      /* Status = i/o error */
6768 #ifndef NOLOCAL
6769 #ifdef OS2
6770                 term_io = term_io_save;
6771 #endif /* OS2 */
6772 #endif /* NOLOCAL */
6773                 switch (y) {
6774                   case -2:              /* Connection lost */
6775                     if (local && !network && carrier != CAR_OFF) {
6776                         dologend();
6777                         printf("Connection closed.\n");
6778                         ttclos(1);
6779                     }
6780                     break;
6781                   case -3:
6782                     dologend();
6783                     printf("Session Limit exceeded - closing connection.\n");
6784                     ttclos(1);
6785                   default:
6786                     break;
6787                 }
6788                 debug(F111,"doinput Connection failed","returning 0",y);
6789                 return(0);
6790             }
6791             if (inintr) {
6792                 debug(F111,"doinput","inintr",inintr);
6793                 if ((icn = conchk()) > 0) { /* Interrupted from keyboard? */
6794                     debug(F101,"input interrupted from keyboard","",icn);
6795                     kbchar = coninc(0);
6796                     if (kbchar >= 0) {
6797                         while (--icn > 0) {
6798                             debug(F110,"doinput","absorbing",0);
6799                             coninc(0);      /* Yes, absorb what was typed. */
6800                         }
6801                         instatus = INP_UI;  /* Fail and remember why. */
6802                         break;
6803                     }
6804                 }
6805             }
6806         } else {                        /* Another for remote */
6807             y = coninc(rt);
6808             debug(F101,"doinput coninc(rt) returns","",y);
6809         }
6810         if (y > -1) {                   /* A character arrived */
6811             debug(F111,"doinput","a character arrived",y);
6812             if (timo == 0)
6813               waiting--;
6814 #ifndef OS2
6815 #define TN_NOLO
6816 #endif /* OS2 */
6817 #ifdef NOLOCAL
6818 #define TN_NOLO
6819 #endif /* NOLOCAL */
6820
6821 #ifdef TN_NOLO
6822             debug(F100,"doinput TN_NOLO","",0);
6823 #ifdef TNCODE
6824             /* Check for telnet protocol negotiation */
6825             if (is_tn) {
6826                 switch (y & 0xff) {
6827                   case IAC:
6828                     cr = 0;
6829                     myflsh();   /* Break from input burst for tn_doop() */
6830 #ifdef CK_BURST
6831                     burst = 0;
6832 #endif /* CK_BURST */
6833                     waiting -= 2;       /* (not necessarily...) */
6834                     switch (tn_doop((CHAR)(y & 0xff),duplex,ttinc)) {
6835                       case 2: duplex = 0; continue;
6836                       case 1: duplex = 1; continue;
6837 #ifdef IKS_OPTION
6838                       case 4:
6839                         if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start &&
6840                              !tcp_incoming) {
6841                             instatus = INP_IKS;
6842                             printf(
6843  " Internet Kermit Service in SERVER mode.\n Please use REMOTE commands.\n"
6844                                    );
6845                             break;
6846                         }
6847                         continue;
6848 #endif /* IKS_OPTION */
6849                       case 6:           /* TELNET DO LOGOUT received */
6850                         continue;
6851                       case 7:
6852                       case 3:           /* A quoted IAC */
6853                         break;
6854                       default:
6855                         continue;
6856                     }
6857                   case CR:
6858                     cr = 1;
6859                     break;
6860                   case NUL:
6861                     if (!TELOPT_U(TELOPT_BINARY) && cr) {
6862                         cr = 0;
6863                         continue;
6864                     }
6865                     cr = 0;
6866                     break;
6867                   default:
6868                     cr = 0;
6869                 }
6870                 /* I'm echoing remote chars */
6871                 if (TELOPT_ME(TELOPT_ECHO) && tn_rem_echo)
6872                   ttoc((char)y);
6873             }
6874 #endif /* TNCODE */
6875 #ifdef CK_AUTODL
6876             /* Check for file transfer packets */
6877             if (inautodl) autodown(y);
6878 #endif /* CK_AUTODL */
6879 #else  /* TN_NOLO */
6880             debug(F100,"doinput !TN_NOLO","",0);
6881 #ifdef TNCODE
6882             /* Check for telnet protocol negotiation */
6883             if (is_tn) {
6884                 int tx;
6885                 switch (y & 0xff) {
6886                   case IAC:
6887                     myflsh();   /* Break from input burst for tn_doop() */
6888 #ifdef CK_BURST
6889                     burst = 0;
6890 #endif /* CK_BURST */
6891 #ifdef IKS_OPTION
6892                     tx = scriptwrtbuf((USHORT)y);
6893                     if (tx == 4) {
6894                         if (TELOPT_U(TELOPT_KERMIT) && 
6895                             TELOPT_SB(TELOPT_KERMIT).kermit.u_start &&
6896                             !tcp_incoming
6897                             ) {
6898                             instatus = INP_IKS;
6899                             printf(
6900   " Internet Kermit Service in SERVER mode.\n Please use REMOTE commands.\n"
6901                                    );
6902                             break;
6903                         }
6904                     } else if (tx == 6) {
6905                         /* TELNET DO LOGOUT received */
6906
6907                     }
6908 #else /* IKS_OPTION */
6909                     /* Handles Telnet negotiations */
6910                     tx = scriptwrtbuf((USHORT)y);
6911                     if (tx == 6) {
6912                         /* TELNET DO LOGOUT received */
6913                     }
6914 #endif /* IKS_OPTION */
6915                     waiting -= 2;       /* (not necessarily...) */
6916                     cr = 0;
6917                     continue;           /* and autodownload check */
6918                   case CR:
6919                     cr = 1;
6920                     tx = scriptwrtbuf((USHORT)y);
6921                     if (tx == 6) {
6922                         /* TELNET DO LOGOUT received */
6923                     }
6924                     break;
6925                   case NUL:
6926                     cr = 0;
6927                     if (!TELOPT_U(TELOPT_BINARY) && cr)
6928                       continue;
6929                     tx = scriptwrtbuf((USHORT)y);
6930                     if (tx == 6) {
6931                         /* TELNET DO LOGOUT received */
6932                     }
6933                     break;
6934                   default:
6935                     cr = 0;
6936                     tx = scriptwrtbuf((USHORT)y);
6937                     if (tx == 6) {
6938                         /* TELNET DO LOGOUT received */
6939                     }
6940                 }
6941                 /* I'm echoing remote chars */
6942                 if (TELOPT_ME(TELOPT_ECHO) && tn_rem_echo)
6943                   ttoc((CHAR)y);
6944             } else
6945 #endif /* TNCODE */
6946               /* Handles terminal emulation responses */
6947               scriptwrtbuf((USHORT)y);
6948 #endif /* TN_NOLO */
6949
6950             /* Real input character to be checked */
6951
6952 #ifdef CK_BURST
6953             burst--;                    /* One less character waiting */
6954             debug(F101,"doinput burst","",burst);
6955 #endif /* CK_BURST */
6956             c = (CHAR) (imask & (CHAR) y); /* Mask off any parity */
6957             inchar[0] = c;              /* Remember character for \v(inchar) */
6958 #ifdef COMMENT
6959 #ifdef CK_BURST
6960             /* Update "lastchar" time only once during input burst */
6961             if (burst <= 0)
6962 #endif /* CK_BURST */
6963 #endif /* COMMENT */
6964               lastchar = gtimer();      /* Remember when it came */
6965
6966             if (c == '\0') {            /* NUL, we can't use it */
6967                 if (anychar) {          /* Except if any character will do? */
6968                     x = 1;              /* Yes, done. */
6969                     instatus = INP_OK;
6970                     incount = 1;        /* This must be the first and only. */
6971                     break;
6972                 } else goto refill;     /* Otherwise continue INPUTting */
6973             }
6974             *inpbp++ = c;               /* Store char in circular buffer */
6975             incount++;                  /* Count it for \v(incount) */
6976
6977             if (flags & INPSW_COU) {    /* INPUT /COUNT */
6978                 if (--count < 1) {
6979                     x = 1;
6980                     instatus = INP_OK;
6981                     incount = savecount;
6982                     break;
6983                 }
6984             }
6985             if (matchbuf) {
6986                 if (matchindex < MATCHBUFSIZ) {
6987                     matchbuf[matchindex++] = c;
6988                     matchbuf[matchindex] = NUL;
6989                 }
6990             }
6991 #ifdef MAC
6992             {
6993                 extern char *ttermw;    /* fake pointer cast */
6994                 if (inecho) {
6995                     outchar(ttermw, c); /* echo to terminal window */
6996                     /* this might be too much overhead to do here ? */
6997                     updatecommand(ttermw);
6998                 }
6999             }
7000 #else /* Not MAC */
7001             if (inecho) {               /* Buffer console output */
7002                 conbuf[concnt++] = c;
7003             }
7004 #endif /* MAC */
7005 #ifndef OS2
7006             if (seslog) {
7007                 int dummy = 0, skip = 0;
7008 #ifndef NOLOCAL
7009                 if (noescseq) {
7010                     dummy = chkaes(c,0);
7011                     if (inesc[0] != ES_NORMAL || oldesc[0] != ES_NORMAL)
7012                       skip = 1;
7013                 }
7014 #endif  /* NOLOCAL */
7015 #ifdef UNIXOROSK
7016                 if (sessft == XYFT_T) {
7017 #ifdef UNIX
7018                     if (c == '\r')
7019 #else
7020 #ifdef OSK
7021                     if (c == '\012')
7022 #endif /* OSK */
7023 #endif /* UNIX */
7024                       skip = 1;
7025                 }
7026 #endif  /* UNIXOROSK */
7027                 if (!skip)
7028                   sesbuf[sescnt++] = c; /* Buffer session log output */
7029             }
7030 #endif /* OS2 */
7031             if (anychar) {              /* Any character will do? */
7032                 x = 1;
7033                 instatus = INP_OK;
7034                 break;
7035             }
7036             if (!inpcas[cmdlvl]) {      /* Ignore alphabetic case? */
7037                 if (isupper(c))         /* Yes, convert input char to lower */
7038                   c = (CHAR) tolower(c);
7039             }
7040             debug(F000,"doinput char","",c);
7041
7042             /* Here is the matching section */
7043
7044             y = -1;                     /* Loop thru search strings */
7045             while (!nomatch && (s = ms[++y])) { /* ...as many as we have. */
7046                 if (mp[y]) {            /* Pattern match? */
7047 #ifdef COMMENT
7048                     int j;
7049                     /* This is gross but it works... */
7050                     /* We could just as easily have prepended '*' to the  */
7051                     /* pattern and skipped the loop, except then we would */
7052                     /* not have any way to identify the matching string.  */
7053                     for (j = 0; j < matchindex; j++) {
7054                         if (ckmatch(s,&matchbuf[j],1,1)) {
7055                             matchindex = j;
7056                             instatus = INP_OK;
7057                             x = 1;
7058                             break;
7059                         }
7060                     }
7061                     if (x > 0)
7062                       break;
7063 #else
7064                     /* July 2001 - ckmatch() returns match position. */
7065                     /* It works and it's not gross. */
7066                     /* (4 = floating pattern) */
7067                     x = ckmatch(s,matchbuf,inpcas[cmdlvl],1+4);
7068                     if (x > 0) {
7069                         matchindex = x - 1;
7070                         instatus = INP_OK;
7071                         x = 1;
7072                         break;
7073                     }
7074 #endif /* COMMENT */
7075                     continue;
7076                 }                       /* Literal match. */
7077                 i = mi[y];              /* Match-position in search string. */
7078                 debug(F000,"compare char","",(CHAR)s[i]);
7079                 if (c == (CHAR) s[i]) { /* Check for match */
7080                     i++;                /* Got one, go to next character */
7081                 } else {                /* Don't have a match */
7082                     int j;
7083                     for (j = i; i > 0; ) { /* Back up in search string */
7084                         i--; /* (Do this here to prevent compiler foulup) */
7085                         /* j is the length of the substring that matched */
7086                         if (c == (CHAR) s[i]) {
7087                             if (!strncmp(s,&s[j-i],i)) {
7088                                 i++;          /* c actually matches -- cfk */
7089                                 break;
7090                             }
7091                         }
7092                     }
7093                 }
7094                 if ((CHAR) s[i] == (CHAR) '\0') { /* Matched to end? */
7095                     ckstrncpy(matchbuf,ms[y],MATCHBUFSIZ);
7096                     matchindex = 0;
7097                     instatus = INP_OK;  /* Yes, */
7098                     x = 1;            
7099                     break;              /* done. */
7100                 }
7101                 mi[y] = i;              /* No, remember match-position */
7102             }
7103             if (x == 1) {               /* Set \v(minput) result */
7104                 instatus = INP_OK;
7105                 m_found = y + 1;
7106                 break;
7107             }
7108             if (inpbp >= inpbuf + inbufsize) { /* Reached end of buffer? */
7109                 if (nowrap) {           /* If /NOWRAP...*/
7110                     instatus = INP_BF;  /* ...return indicating buffer full. */
7111                     *inpbp = NUL;
7112                     goto xinput;
7113                 }
7114                 *inpbp = NUL;           /* Make it null-terminated */
7115                 inpbp = inpbuf;         /* Yes. */
7116             }
7117         }
7118 #ifdef CK_BURST
7119         else if (y <= -1 && burst > 0) {
7120             debug(F111,"doinput (y<=-1&&burst>0)","burst",burst);
7121                                         /* A timeout occurred so there can't */
7122             burst = 0;                  /* be data waiting; must check timo */
7123         }
7124       refill:
7125         if (burst <= 0) {               /* No buffered chars remaining... */
7126             myflsh();                   /* Flush buffered output */
7127             if (local) {                /* Get size of next input burst */
7128                 burst = ttchk();
7129                 if (burst < 0) {        /* ttchk() says connection is closed */
7130                     instatus = INP_IO;  /* Status = i/o error */
7131 #ifndef NOLOCAL
7132 #ifdef OS2
7133                     term_io = term_io_save;
7134 #endif /* OS2 */
7135 #endif /* NOLOCAL */
7136
7137                     if ((!network 
7138 #ifdef TN_COMPORT
7139                          || istncomport()
7140 #endif /* TN_COMPORT */
7141                          ) && carrier != CAR_OFF) {
7142         /* The test is written this way because the Microsoft compiler
7143          * is producing bad code if written:
7144          *
7145          *  if (network && (!istncomport() || carrier == CAR_OFF) )
7146          */
7147                         break;
7148                      } else {
7149                          printf("Fatal error - disconnected.\n");
7150                          ttclos(1);
7151                          break;
7152                      }
7153                 }
7154                 if (inintr) {
7155                     if ((icn = conchk()) > 0) { /* Interrupt from keyboard? */
7156                         kbchar = coninc(0);
7157                         debug(F101,"input interrupted from keyboard","",icn);
7158                         while (--icn > 0) coninc(0); /* Yes, absorb chars. */
7159                         break;          /* And fail. */
7160                     }
7161                 }
7162             } else {
7163                 burst = conchk();
7164             }
7165             debug(F101,"doinput burst","",burst);
7166             /* Prevent overflow of "conbuf" and "sesbuf" */
7167             if (burst > MAXBURST)
7168               burst = MAXBURST;
7169
7170             /* Did not match, timer exceeded? */
7171             t = gtimer();
7172             debug(F111,"doinput gtimer","burst",t);
7173             debug(F101,"doinput timo","",timo);
7174             if ((t >= timo) && (timo > 0))
7175               break;
7176             else if (insilence > 0 && (t - lastchar) > insilence)
7177               break;
7178         } else {
7179             debug(F111,"doinput (burst > 0)","burst",burst);
7180         }
7181 #else  /* CK_BURST */
7182       refill:
7183         myflsh();                       /* Flush buffered output */
7184         /* Did not match, timer exceeded? */
7185         t = gtimer();
7186         debug(F111,"doinput gtimer","no burst",t);
7187         debug(F101,"doinput timo","",timo);
7188         if ((t >= timo) && (timo > -1))
7189           break;
7190         else if (insilence > 0 && (t - lastchar) > insilence)
7191           break;
7192 #endif /* CK_BURST */
7193     }                                   /* Still have time left, continue. */
7194   xinput:
7195     myflsh();                           /* Flush buffered output */
7196     if (instatus == INP_BF) {           /* Buffer full and /NOWAIT */
7197         x = 0;                          /* Must not succeed */
7198     } else {                            /* Buffer full and /NOWAIT */
7199         if (nomatch) x = 1;             /* Succeed if nomatch and timed out */
7200         if (x > 0 && !nomatch)
7201           instatus = 0;
7202     }
7203 #ifndef NOLOCAL
7204 #ifdef OS2
7205     term_io = term_io_save;
7206 #endif /* OS2 */
7207 #endif /* NOLOCAL */
7208 #ifdef COMMENT
7209 #ifdef IKS_OPTION
7210 #ifdef CK_AUTODL
7211     if (is_tn && TELOPT_ME(TELOPT_KERMIT) && inautodl) {
7212         tn_siks(KERMIT_STOP);           /* Send Kermit-Server Stop */
7213     }
7214 #endif /* CK_AUTODL */
7215 #endif /* IKS_OPTION */
7216 #endif /* COMMENT */
7217
7218 #ifdef GFTIMER
7219     fpt = gftimer();                    /* Get elapsed time */
7220
7221 /* If a long is 32 bits, it would take about 50 days for this to overflow. */
7222
7223     inetime = (int)(fpt * (CKFLOAT)1000.0);
7224 #else
7225     inetime = (int)(gtimer() * 1000);
7226 #endif /* GFTIMER */
7227
7228     if (x > 0)
7229       makestr(&inpmatch,&matchbuf[matchindex]); /* \v(inmatch) */
7230     return(x);                          /* Return the return code. */
7231 }
7232 #endif /* NOSPL */
7233
7234 #ifndef NOSPL
7235 /* REINPUT Command */
7236
7237 /*
7238   Note, the timeout parameter is required, but ignored.  Syntax is compatible
7239   with MS-DOS Kermit except timeout can't be omitted.  This function only
7240   looks at the characters already received and does not read any new
7241   characters from the connection.
7242 */
7243 int
7244 doreinp(timo,s,pat) int timo; char *s; int pat; {
7245     int x, y, i;
7246     char *xx, *xp, *xq = (char *)0;
7247     CHAR c;
7248
7249     if (!s) s = "";
7250     debug(F101,"doreinput pat","",pat);
7251
7252     y = (int)strlen(s);
7253     debug(F111,"doreinput search",s,y);
7254
7255     if (y > inbufsize) {                /* If search string longer than */
7256         debug(F101,"doreinput inbufsize","",inbufsize);
7257         return(0);                      /* input buffer, fail. */
7258     }
7259     makestr(&inpmatch,NULL);
7260     if (!matchbuf)
7261       matchbuf = malloc(MATCHBUFSIZ+1);
7262     matchindex = 0;
7263
7264     x = 0;                              /* Return code, assume failure */
7265     i = 0;                              /* String pattern match position */
7266
7267     if (!inpcas[cmdlvl]) {              /* INPUT CASE = IGNORE?  */
7268         xp = malloc(y+2);               /* Make a separate copy of the */
7269         if (!xp) {                      /* search string. */
7270             printf("?malloc error 6\n");
7271             return(x);
7272         } else xq = xp;                 /* Keep pointer to beginning. */
7273         while (*s) {                    /* Yes, convert to lowercase */
7274             *xp = *s;
7275             if (isupper(*xp)) *xp = (char) tolower(*xp);
7276             xp++; s++;
7277         }
7278         *xp = NUL;                      /* Terminate it! */
7279         s = xq;                         /* Move search pointer to it. */
7280     }
7281     xx = *inpbp ? inpbp : inpbuf;       /* Current INPUT buffer pointer */
7282     do {
7283         c = *xx++;                      /* Get next character */
7284         if (!c) break;
7285         if (xx >= inpbuf + inbufsize)   /* Wrap around if necessary */
7286           xx = inpbuf;
7287         if (!inpcas[cmdlvl]) {          /* Ignore alphabetic case? */
7288             if (isupper(c)) c = (CHAR) tolower(c); /* Yes */
7289         }
7290         if (pat) {
7291             int j;
7292             if (matchbuf) {
7293                 if (matchindex < MATCHBUFSIZ) {
7294                     matchbuf[matchindex++] = c;
7295                     matchbuf[matchindex] = NUL;
7296                 }
7297                 for (j = 0; j < matchindex; j++) { /* Gross but effective */
7298                     if (ckmatch(s,&matchbuf[j],1,1)) {
7299                         debug(F101,"GOT IT","",j);
7300                         matchindex = j;
7301                         x = 1;
7302                         break;
7303                     }
7304                 }
7305             }
7306             if (x > 0)
7307               break;
7308             continue;
7309         }
7310         debug(F000,"doreinp char","",c);
7311         debug(F000,"compare char","",(CHAR) s[i]);
7312         if (((char) c) == ((char) s[i])) { /* Check for match */
7313             i++;                        /* Got one, go to next character */
7314         } else {                        /* Don't have a match */
7315             int j;
7316             for (j = i; i > 0; ) {      /* [jrs] search backwards for it  */
7317                 i--;
7318                 if (((char) c) == ((char) s[i])) {
7319                     if (!strncmp(s,&s[j-i],i)) {
7320                         i++;
7321                         break;
7322                     }
7323                 }
7324             }
7325         }                               /* [jrs] or return to zero from -1 */
7326         if (s[i] == '\0') {             /* Matched all the way to end? */
7327             ckstrncpy(matchbuf,s,MATCHBUFSIZ);
7328             matchindex = 0;
7329             x = 1;                      /* Yes, */
7330             break;                      /* done. */
7331         }
7332     } while (xx != inpbp && x < 1);     /* Until back where we started. */
7333
7334     if (!inpcas[cmdlvl]) if (xq) free(xq); /* Free this if it was malloc'd. */
7335     makestr(&inpmatch,&matchbuf[matchindex]); /* \v(inmatch) */
7336     return(x);                          /* Return search result. */
7337 }
7338
7339 /*  X X S T R I N G  --  Interpret strings containing backslash escapes  */
7340 /*  Z Z S T R I N G  --  (new name...)  */
7341 /*
7342  Copies result to new string.
7343   strips enclosing braces or doublequotes.
7344   interprets backslash escapes.
7345   returns 0 on success, nonzero on failure.
7346   tries to be compatible with MS-DOS Kermit.
7347
7348  Syntax of input string:
7349   string = chars | "chars" | {chars}
7350   chars = (c*e*)*
7351   where c = any printable character, ascii 32-126
7352   and e = a backslash escape
7353   and * means 0 or more repetitions of preceding quantity
7354   backslash escape = \operand
7355   operand = {number} | number | fname(operand) | v(name) | $(name) | m(name)
7356   number = [r]n[n[n]]], i.e. an optional radix code followed by 1-3 digits
7357   radix code is oO (octal), xX (hex), dD or none (decimal) (see xxesc()).
7358 */
7359
7360 #ifndef NOFRILLS
7361 int
7362 yystring(s,s2) char *s; char **s2; {    /* Reverse a string */
7363     int x;
7364     static char *new;
7365     new = *s2;
7366     if (!s || !new) return(-1);         /* Watch out for null pointers. */
7367     if ((x = (int)strlen(s)) == 0) {    /* Recursion done. */
7368         *new = '\0';
7369         return(0);
7370     }
7371     x--;                                /* Otherwise, call self */
7372     *new++ = s[x];                      /* to reverse rest of string. */
7373     s[x] = 0;
7374     return(yystring(s,&new));
7375 }
7376 #endif /* NOFRILLS */
7377
7378 static char ipabuf[16] = { NUL };       /* IP address buffer */
7379
7380 static char *
7381 getip(s) char *s; {
7382     char c=NUL;                         /* Workers... */
7383     int i=0, p=0, d=0;
7384     int state = 0;                      /* State of 2-state FSA */
7385
7386     while ((c = *s++)) {
7387         switch(state) {
7388           case 0:                       /* Find first digit */
7389             i = 0;                      /* Output buffer index */
7390             ipabuf[i] = NUL;            /* Initialize output buffer */
7391             p = 0;                      /* Period counter */
7392             d = 0;                      /* Digit counter */
7393             if (isdigit(c)) {           /* Have first digit */
7394                 d = 1;                  /* Count it */
7395                 ipabuf[i++] = c;        /* Copy it */
7396                 state = 1;              /* Change state */
7397             }
7398             break;
7399
7400           case 1:                       /* In numeric field */
7401             if (isdigit(c)) {           /* Have digit */
7402                 if (++d > 3)            /* Too many */
7403                   state = 0;            /* Start over */
7404                 else                    /* Not too many */
7405                   ipabuf[i++] = c;      /* Keep it */
7406             } else if (c == '.' && p < 3) { /* Have a period */
7407                 p++;                    /* Count it */
7408                 if (d == 0)             /* Not preceded by a digit */
7409                   state = 0;            /* Start over */
7410                 else                    /* OK */
7411                   ipabuf[i++] = c;      /* Keep it */
7412                 d = 0;                  /* Reset digit counter */
7413             } else if (p == 3 && d > 0) { /* Not part of address */
7414                 ipabuf[i] = NUL;        /* If we have full IP address */
7415                 return((char *)ipabuf); /* Return it */
7416             } else {                    /* Otherwise */
7417                 state = 0;              /* Start over */
7418                 ipabuf[0] = NUL;        /* (in case no more chars left) */
7419             }
7420         }
7421     }                                   /* Fall thru at end of string */
7422     ipabuf[i] = NUL;                    /* Maybe we have one */
7423     return((p == 3 && d > 0) ? (char *)ipabuf : "");
7424 }
7425 #endif /* NOSPL */
7426
7427 /* Date Routines */
7428
7429 /* Z J D A T E  --  Convert yyyymmdd date to Day of Year */
7430
7431 static int jdays[12] = {  0,31,59,90,120,151,181,212,243,273,304,334 };
7432 static int ldays[12] = {  0,31,60,91,121,152,182,213,244,274,305,335 };
7433 static char zjdbuf[12] = { NUL, NUL };
7434 /*
7435   Deinde, ne in posterum a XII kalendas aprilis aequinoctium recedat,
7436   statuimus bissextum quarto quoque anno (uti mos est) continuari debere,
7437   praeterquam in centesimis annis; qui, quamvis bissextiles antea semper
7438   fuerint, qualem etiam esse volumus annum MDC, post eum tamen qui deinceps
7439   consequentur centesimi non omnes bissextiles sint, sed in quadringentis
7440   quibusque annis primi quique tres centesimi sine bissexto transigantur,
7441   quartus vero quisque centesimus bissextilis sit, ita ut annus MDCC, MDCCC,
7442   MDCCCC bissextiles non sint. Anno vero MM, more consueto dies bissextus
7443   intercaletur, februario dies XXIX continente, idemque ordo intermittendi
7444   intercalandique bissextum diem in quadringentis quibusque annis perpetuo
7445   conservetur.  - Gregorius XIII, Anno Domini MDLXXXII.
7446 */
7447 char *
7448 zjdate(date) char * date; {             /* date = yyyymmdd */
7449     char year[5];
7450     char month[3];
7451     char day[3];
7452     int d, m, x, y;
7453     int leapday, j;
7454     char * time = NULL;
7455
7456     if (!date) date = "";               /* Validate arg */
7457     x = strlen(date);
7458     if (x < 1) return("0");
7459     if (x < 8) return("-1");
7460     for (x = 0; x < 8; x++)
7461       if (!isdigit(date[x]))
7462         return("-1");
7463
7464     if (date[8]) if (date[9])
7465       time = date + 9;
7466
7467     year[0] = date[0];                  /* Isolate year */
7468     year[1] = date[1];
7469     year[2] = date[2];
7470     year[3] = date[3];
7471     year[4] = '\0';
7472
7473     month[0] = date[4];                 /* Month */
7474     month[1] = date[5];
7475     month[2] = '\0';;
7476
7477     day[0] = date[6];                   /* And day */
7478     day[1] = date[7];
7479     day[2] = '\0';
7480
7481     leapday = 0;                        /* Assume no leap day */
7482     y = atoi(year);
7483     m = atoi(month);
7484     d = atoi(day);
7485     if (m > 2) {                        /* No Leap day before March */
7486         if (y % 4 == 0) {               /* If year is divisible by 4 */
7487             leapday = 1;                /* It's a Leap year */
7488             if (y % 100 == 0) {         /* Except if divisible by 100 */
7489                 if (y % 400 != 0)       /* but not by 400 */
7490                   leapday = 0;
7491             }
7492         }
7493     }
7494     j = jdays[m - 1] + d + leapday;     /* Day of year */
7495     if (time)
7496       sprintf(zjdbuf,"%04d%03d %s",y,j,time); /* SAFE */
7497     else
7498       sprintf(zjdbuf,"%04d%03d",y,j);   /* SAFE */
7499     return((char *)zjdbuf);
7500 }
7501
7502 static char jzdbuf[32];
7503
7504 /* J Z D A T E  --  Convert Day of Year to yyyyddmm date */
7505
7506 char *
7507 jzdate(date) char * date; {             /* date = yyyyddd */
7508     char year[5];                       /* with optional time */
7509     char day[4];
7510     char * time = NULL, * p;
7511     int d, m, x, y;
7512     int leapday, j;
7513     int * zz;
7514
7515     if (!date) date = "";               /* Validate arg */
7516     x = strlen(date);
7517
7518     debug(F111,"jzdate len",date,x);
7519
7520     if (x < 1) return("0");
7521     if (x < 7) return("-1");
7522     if (x > 8) time = date + 8;
7523
7524     for (x = 0; x < 7; x++)
7525       if (!isdigit(date[x]))
7526         return("-1");
7527
7528     year[0] = date[0];                  /* Isolate year */
7529     year[1] = date[1];
7530     year[2] = date[2];
7531     year[3] = date[3];
7532     year[4] = '\0';
7533
7534     debug(F110,"jzdate year",year,0);
7535
7536     day[0] = date[4];                   /* And day */
7537     day[1] = date[5];
7538     day[2] = date[6];
7539     day[3] = '\0';
7540
7541     debug(F110,"jzdate day",day,0);
7542
7543     j = atoi(day);
7544     if (j > 366)
7545       return("-1");
7546
7547     leapday = 0;                        /* Assume no leap day */
7548     y = atoi(year);
7549     if (y % 4 == 0) {                   /* If year is divisible by 4 */
7550         leapday = 1;                    /* It's a Leap year */
7551         if (y % 100 == 0) {             /* Except if divisible by 100 */
7552             if (y % 400 != 0)           /* but not by 400 */
7553               leapday = 0;
7554         }
7555     }
7556     debug(F101,"jzdate leapday","",leapday);
7557     zz = leapday ? ldays : jdays;
7558
7559     for (x = 0; x < 11; x++)
7560       if (j > zz[x] && j <= zz[x+1])
7561         break;
7562     m = x + 1;
7563
7564     debug(F101,"jzdate m","",m);
7565
7566     d = j - zz[x];
7567
7568     debug(F101,"jzdate d","",d);
7569
7570     if (time)
7571       sprintf(jzdbuf,"%04d%02d%02d %s",y,m,d,time); /* SAFE */
7572     else
7573       sprintf(jzdbuf,"%04d%02d%02d",y,m,d); /* SAFE */
7574
7575     debug(F101,"jzdate jzdbuf",jzdbuf,0);
7576
7577     p = ckcvtdate((char *)jzdbuf, 0);   /* Convert to standard form */
7578     ckstrncpy(jzdbuf,p,32);
7579     if (!time) jzdbuf[8] = NUL;         /* Remove time if not wanted */
7580     return((char *)jzdbuf);
7581 }
7582
7583 /* M J D  --  Modified Julian Date */
7584 /*
7585   Call with:
7586     Standard-format date-time string: yyyymmdd[ hh:mm:ss].
7587     The time, if any, is ignored.
7588
7589   Returns:
7590     -1L on error, otherwise:
7591     The number of days since 17 Nov 1858 as a whole number:
7592     16 Nov 1858 = -1, 17 Nov 1858 = 0, 18 Nov 1858 = 1, 19 Nov 1858 = 2, ...
7593
7594   The Modified Julian Date is defined by the International Astronomical
7595   Union as the true Julian date minus 2400000.5 days.  The true Julian
7596   date is the number days since since noon of 1 January 4713 BCE of the
7597   Julian proleptic calendar.  Conversions between calendar dates and
7598   Julian dates, however, assume Gregorian dating.
7599 */
7600 long
7601 mjd(date) char * date; {
7602     char year[5];
7603     char month[3];
7604     char day[3];
7605     int x, a, d, m, y;
7606     long z;
7607
7608     if (!date) date = "";               /* Validate arg */
7609     x = strlen(date);
7610     if (x < 1) return(0L);
7611     if (x < 8) return(-1L);
7612     for (x = 0; x < 8; x++)
7613       if (!isdigit(date[x]))
7614         return(-1L);
7615
7616     year[0] = date[0];                  /* Isolate year */
7617     year[1] = date[1];
7618     year[2] = date[2];
7619     year[3] = date[3];
7620     year[4] = '\0';
7621
7622     month[0] = date[4];                 /* Month */
7623     month[1] = date[5];
7624     month[2] = '\0';;
7625     m = atoi(month);
7626
7627     day[0] = date[6];                   /* And day */
7628     day[1] = date[7];
7629     day[2] = '\0';
7630     d = atoi(day);
7631
7632     a = (14-m)/12;                      /* Calculate true Julian date */
7633     y = atoi(year) + 4800 - a;
7634     m = m + 12 * a - 3;
7635     z = d + (long)(306*m+5)/10 + (long)(y*365) + y/4 - y/100 + y/400 - 32045L;
7636
7637     z -= 2400001L;                      /* Convert JD to MJD */
7638
7639     return(z);
7640 }
7641
7642 static char mjd2dbuf[32];
7643
7644 /*  M J D 2 D A T E  --  Converts MJD to yyyymmdd  */
7645
7646 char *
7647 #ifdef CK_ANSIC
7648 mjd2date(long mjd)
7649 #else
7650 mjd2date(mjd) long mjd;
7651 #endif /* CK_ANSIC */
7652 /* mjd2date */ {
7653     long jd, l, n;
7654     int d, m, y;
7655     jd = (long)(mjd + 2400001L);
7656     l = jd + 68569;
7657     n = 4 * l / 146097L;
7658     l = l - (146097 * n + 3) / 4;
7659     y = 4000 * (l + 1) / 1461001L;
7660     l = l - 1461 * y / 4 + 31;
7661     m = 80 * l / 2447;
7662     d = l - 2447 * m / 80;
7663     l = m / 11;
7664     m = m + 2 - 12 * l;
7665     y = 100 * (n - 49) + y + l;
7666     sprintf(mjd2dbuf,"%04d%02d%02d",y,m,d); /* SAFE */
7667     return((char *)mjd2dbuf);
7668 }
7669
7670 #ifndef NOSPL
7671 static char ** flist = (char **) NULL;  /* File list for \fnextfile() */
7672 static int flistn = 0;                  /* Number of items in file list */
7673
7674 /*
7675   The function return-value buffer must be global, since fneval() returns a
7676   pointer to it.  fneval() is called only by zzstring(), which always copies
7677   the result out of this buffer to somewhere else, so it's OK to have only
7678   one buffer for this in most cases.  However, since function calls can be
7679   nested -- e.g. functions whose arguments are functions, or recursive
7680   functions, at some point we should convert this to an array of buffers,
7681   indexed by function depth (which might or might not be the same as the
7682   "depth" variable).  Also, since function results are potentially quite big,
7683   we'd need to allocate and deallocate dynamically as we descend and ascend
7684   function depth.  Left for a future release...
7685 */
7686 char fnval[FNVALL+2];                   /* Function return value  */
7687 static int fndepth = 0;                 /* (we don't actually use this yet) */
7688 int fnsuccess = 1;
7689 extern int fnerror;
7690
7691 /* f p f o r m a t  --  Floating-point number nicely formatted.  */
7692 /*
7693    Returns results from a circular 1K buffer.
7694    Don't count on too many results remaining available at once; it could
7695    be anywhere from 5 to maybe 100, depending on the sizes of the results.
7696 */
7697 #ifdef CKFLOAT
7698 #define FPFMTSIZ 1024
7699 static char fpfmtbuf[FPFMTSIZ] = { NUL, NUL };
7700 static int fpfbufpos = 0;               /* (why was this char before?) */
7701
7702 char *
7703 fpformat(fpresult,places,round) CKFLOAT fpresult; int places, round; {
7704     char fbuf[16];                      /* For creating printf format */
7705     int nines = 0, sign = 0, x, y, i, j, size = 0;
7706     char * buf;
7707     CKFLOAT ftmp;
7708
7709     x = places ? places : (fp_digits ? fp_digits : 6);
7710
7711     debug(F101,"fpformat fpresult","",fpresult);
7712     debug(F101,"fpformat places","",places);
7713     debug(F101,"fpformat fpfbufpos 1","",fpfbufpos);
7714
7715     ftmp = fpresult;
7716     if (ftmp < 0.0) ftmp = 0.0 - fpresult;
7717
7718 #ifdef FNFLOAT
7719     if (!fp_rounding &&                 /* If printf doesn't round, */
7720         (places > 0 ||                  /* round result to decimal places. */
7721          (places == 0 && round)))
7722       fpresult += (0.5 / pow(10.0,(CKFLOAT)places));
7723     y = (ftmp == 0.0) ? 1 : (int)log10(ftmp);
7724     size = y + x + 3;                   /* Estimated length of result */
7725     if (fpresult < 0.0) size++;
7726 #else
7727     size = 200;                         /* No way to estimate, be generous */
7728 #endif /* FNFLOAT */
7729
7730     debug(F101,"fpformat size","",size);
7731
7732     if (fpfbufpos > (FPFMTSIZ - size))  /* Wrap around if necessary */
7733       fpfbufpos = 0;
7734     debug(F101,"fpformat fpfbufpos 1","",fpfbufpos);
7735
7736     buf = &fpfmtbuf[fpfbufpos];
7737
7738     if (places > 0) {                   /* If places specified */
7739         /* use specified places to write given number of digits */
7740         sprintf(fbuf,"%%0.%df",places); /* SAFE */
7741         sprintf(buf,fbuf,fpresult);     /* SAFE */
7742     } else {                            /* Otherwise... */
7743         /* Go for max precision */
7744         sprintf(fbuf,"%%0.%df",fp_digits); /* SAFE */
7745         sprintf(buf,fbuf,fpresult);     /* SAFE */
7746     }
7747     if (buf[0] == '-') sign = 1;
7748     debug(F111,"fpresult 1 errno",buf,errno); /* Check for over/underflow */
7749     debug(F111,"fpresult 1 fpfbufpos",buf,fpfbufpos);
7750     /* Give requested decimal places */
7751     for (i = sign; i < FPFMTSIZ && buf[i]; i++) {
7752         if (buf[i] == '.')              /* First find the decimal point */
7753           break;
7754         else if (i > fp_digits + sign - 1) /* replacing garbage */
7755           buf[i] = '0';                 /* digits with 0... */
7756     }
7757     if (buf[i] == '.') {                /* Have decimal point */
7758         int gotend = 0;
7759         /* places < 0 so truncate fraction */
7760         if (places < 0 || (places == 0 && round)) {
7761             buf[i] = NUL;
7762         } else if (places > 0) {        /* d > 0 so this many decimal places */
7763             i++;                           /* First digit after decimal */
7764             for (j = 0; j < places; j++) { /* Truncate after d decimal */
7765                 if (!buf[j+i])        /* places or extend to d  */
7766                   gotend = 1;              /* decimal places. */
7767                 if (gotend || j+i+sign > fp_digits)
7768                   buf[j+i] = '0';
7769             }
7770             buf[j+i] = NUL;
7771         } else {                        /* places == 0 so Do The Right Thing */
7772             for (j = (int)strlen(buf) - 1; j > i+1; j--) {
7773                 if ((j - sign) > fp_digits)
7774                   buf[j] = '0';
7775                 if (buf[j] == '0')
7776                   buf[j] = NUL; /* Strip useless trailing 0's. */
7777                 else
7778                   break;
7779             }
7780         }
7781     }
7782     fpfmtbuf[FPFMTSIZ-1] = NUL;
7783     j = strlen(buf);
7784     sign = 0;
7785     for (i = j-1; i >= 0; i--) {
7786         if (buf[i] == '9')
7787           nines++;
7788         else
7789           break;
7790     }
7791     /* Do something about xx.xx99999999... */
7792     if (nines > 5) {
7793         if (isdigit(buf[i]) && i < FPFMTSIZ - 2) {
7794             buf[i] = buf[i] + 1;
7795             buf[i+1] = '0';
7796             buf[i+2] = '\0';
7797         }
7798     }
7799     if (!strncmp(buf,"-0.0",FPFMTSIZ))
7800       ckstrncpy(buf,"0.0",FPFMTSIZ);
7801     fpfbufpos += (int)strlen(buf) + 1;
7802     return((char *)buf);
7803 }
7804 #endif /* CKFLOAT */
7805
7806 static VOID
7807 evalerr(fn) char * fn; {
7808     if (fndiags) {
7809         if (divbyzero)
7810           ckmakmsg(fnval,FNVALL,"<ERROR:DIVIDE_BY_ZERO:\\f",fn,"()>",NULL);
7811         else
7812           ckmakmsg(fnval,FNVALL,"<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
7813     }
7814 }
7815
7816
7817 static int
7818 ckcindex(c,s) char c, *s; {
7819     int rc;
7820     if (!c || !s) return(0);
7821     for (rc = 0; s[rc]; rc++) {
7822         if (c == s[rc]) return(rc+1);
7823     }
7824     return(0);
7825 }
7826
7827 static char *
7828 dokwval(s,sep) char * s, * sep; {
7829     char c = '\0', * p, * kw = NULL, * vp = NULL;
7830     char * rc = "0";                    /* Return code */
7831     int x = 0;
7832     if (!s) return(rc);
7833     if (!*s) return(rc);
7834     debug(F110,"kwval arg",s,0);
7835     debug(F110,"kwval sep",sep,0);
7836     p = (char *)malloc((int)strlen(s)+1);
7837     if (!p) goto xdokwval;
7838     strcpy(p,s);                        /* SAFE */
7839     s = p;
7840     while (*s < '!' && *s > '\0')       /* Get first nonblank */
7841       s++;
7842     if (!*s) goto xdokwval;
7843     if (ckcindex(*s,sep))               /* Separator but no keyword */
7844       goto xdokwval;
7845     kw = s;                             /* Keyword */
7846     while (*s > ' ') {
7847         if (ckcindex(*s,sep)) {         /* keyword=... */
7848             c = *s;
7849             break;
7850         }
7851         s++;
7852     }
7853     if (*kw) rc = "1";                  /* Have keyword, promote return code */
7854     *s++ = NUL;                         /* Terminate keyword */
7855     while (*s < '!' && *s > '\0')       /* Skip blanks */
7856       s++;
7857     if (!c && ckcindex(*s,sep)) {
7858         c = *s++;                       /* Have separator */
7859         while (*s < '!' && *s > '\0')   /* Skip blanks */
7860           s++;
7861     }
7862     if (c) {
7863         vp = s;
7864         if (*vp) rc = "2";              /* Have value, another promotion */
7865 #ifdef COMMENT
7866         while (*s > ' ')                /* Skip to end */
7867           s++;
7868         *s = NUL;                       /* Terminate value */
7869 #endif  /* COMMENT */
7870     }
7871     debug(F110,"kwval c",ckctoa(c),0);
7872     debug(F110,"kwval keyword",kw,0);
7873     debug(F110,"kwval value",vp,0);
7874     makestr(&lastkwval,kw);
7875     vp = brstrip(vp);
7876     debug(F110,"kwval value",vp,0);
7877     x = addmac(kw,vp);
7878     debug(F111,"kwval addmac",kw,x);
7879   xdokwval: 
7880     if (p) free(p);
7881     return((x < 0) ? "-1" : rc);
7882 }
7883
7884 static int
7885 isaarray(s) char * s; {                 /* Is s an associative array element */
7886     int state = 0;
7887     CHAR c;
7888     if (!s) return(0);
7889     while ((c = *s++)) {
7890         if (!isprint(c)) {
7891             return(0);
7892         } else if (c == '<') {
7893             if (state != 0)
7894               return(0);
7895             state = 1;
7896         } else if (c == '>') {
7897             return((state != 1 || *s) ? 0 : 1);
7898         }
7899     }
7900     return(0);
7901 }
7902
7903 static char *                           /* Evaluate builtin functions */
7904 fneval(fn,argp,argn,xp) char *fn, *argp[]; int argn; char * xp; {
7905     int i=0, j=0, k=0, len1=0, len2=0, len3=0, n=0, t=0, x=0, y=0;
7906     int cx, failed = 0;                 /* Return code, 0 = ok */
7907     long z = 0L;
7908     char *bp[FNARGS + 1];               /* Pointers to malloc'd strings */
7909     char c = NUL;
7910     char *p = NULL, *s = NULL;
7911     char *val1 = NULL, *val2 = NULL;    /* Pointers to numeric string values */
7912
7913 #ifdef RECURSIVE
7914     int rsave = recursive;
7915 #endif /* RECURSIVE */
7916 #ifdef OS2
7917     int zsave = zxpn;
7918 #endif /* OS2 */
7919
7920     if (!fn) fn = "";                   /* Protect against null pointers */
7921     if (!*fn) return("");
7922
7923     for (i = 0; i < FNARGS; i++)        /* Initialize argument pointers */
7924       bp[i] = NULL;
7925 /*
7926   IMPORTANT: Note that argn is not an accurate count of the number of
7927   arguments.  We can't really tell if an argument is null until after we
7928   execute the code below.  So argn is really the maximum number of arguments
7929   we might have.  Argn should always be at least 1, even if the function is
7930   called with empty parentheses (but don't count on it).
7931 */
7932     debug(F111,"fneval",fn,argn);
7933     debug(F110,"fneval",argp[0],0);
7934     if (argn > FNARGS)                  /* Discard excess arguments */
7935       argn = FNARGS;
7936
7937     fndepth++;
7938     debug(F101,"fneval fndepth","",fndepth);
7939     p = fnval;
7940     fnval[0] = NUL;
7941     y = lookup(fnctab,fn,nfuncs,&x);    /* Look up the function name */
7942     cx = y;                             /* Because y is too generic... */
7943     if (cx < 0) {                        /* Not found */
7944         failed = 1;
7945         if (fndiags) {                  /* FUNCTION DIAGNOSTIC ON */
7946             int x;
7947             x = strlen(fn);
7948             /* The following sprintf's are safe */
7949             switch (cx) {
7950               case -1:
7951                 if (x + 32 < FNVALL)
7952                   sprintf(fnval,"<ERROR:NO_SUCH_FUNCTION:\\f%s()>",fn);
7953                 else
7954                   sprintf(fnval,"<ERROR:NO_SUCH_FUNCTION>");
7955                 break;
7956               case -2:
7957                 if (x + 26 < FNVALL)
7958                   sprintf(fnval,"<ERROR:NAME_AMBIGUOUS:\\f%s()>",fn);
7959                 else
7960                   sprintf(fnval,"<ERROR:NAME_AMBIGUOUS>");
7961                 break;
7962               case -3:
7963                 sprintf(fnval,"<ERROR:FUNCTION_NAME_MISSING:\\f()>");
7964                 break;
7965               default:
7966                 if (x + 26 < FNVALL)
7967                   sprintf(fnval,"<ERROR:LOOKUP_FAILURE:\\f%s()>",fn);
7968                 else
7969                   sprintf(fnval,"<ERROR:LOOKUP_FAILURE>");
7970                 break;
7971             }
7972         }
7973         goto fnend;                     /* Always leave via common exit */
7974     }
7975     fn = fnctab[x].kwd;                 /* Full name of function */
7976
7977     if (argn < 0) {
7978         failed = 1;
7979         p = fnval;
7980         if (fndiags)
7981           sprintf(fnval,"<ERROR:MISSING_ARG:\\f%s()>",fn);
7982         goto fnend;
7983     }
7984     if (cx == FN_LIT) {                 /* literal(arg1) */
7985         debug(F010,"flit",xp,0);
7986         p = xp ? xp : "";               /* Return a pointer to arg itself */
7987         goto fnend;
7988     }
7989
7990 #ifdef DEBUG
7991     if (deblog) {
7992         int j;
7993         for (j = 0; j < argn; j++)
7994           debug(F111,"fneval arg",argp[j],j);
7995     }
7996 #endif /* DEBUG */
7997     for (j = argn-1; j >= 0; j--) {     /* Uncount empty trailing args */
7998         if (!argp[j])
7999           argn--;
8000         else if (!*(argp[j]))
8001           argn--;
8002         else break;
8003     }
8004     debug(F111,"fneval argn",fn,argn);
8005 /*
8006   \fliteral() and \fcontents() are special functions that do not evaluate
8007   their arguments, and are treated specially here.  After these come the
8008   functions whose arguments are evaluated in the normal way.
8009 */
8010 #ifdef COMMENT
8011     /* (moved up) */
8012     if (cx == FN_LIT) {                 /* literal(arg1) */
8013         debug(F110,"flit",xp,0);
8014         p = xp ? xp : "";               /* Return a pointer to arg itself */
8015         goto fnend;
8016     }
8017 #endif /* COMMENT */
8018     if (cx == FN_CON) {                 /* Contents of variable, unexpanded. */
8019         char c;
8020         int subscript = 0;        
8021         if (!(p = argp[0]) || !*p) {
8022             failed = 1;
8023             p = fnval;
8024             if (fndiags)
8025               sprintf(fnval,"<ERROR:MISSING_ARG:\\fcontents()>");
8026             goto fnend;
8027         }
8028         p = brstrip(p);
8029         if (*p == CMDQ) p++;
8030         if ((c = *p) == '%') {          /* Scalar variable. */
8031             c = *++p;                   /* Get ID character. */
8032             p = "";                     /* Assume definition is empty */
8033             if (!c) {                   /* Double paranoia */
8034                 failed = 1;
8035                 p = fnval;
8036                 if (fndiags)
8037                   sprintf(fnval,"<ERROR:ARG_BAD_VARIABLE:\\fcontents()>");
8038                 goto fnend;
8039             }
8040             if (c >= '0' && c <= '9') { /* Digit for macro arg */
8041                 if (maclvl < 0)         /* Digit variables are global */
8042                   p = g_var[c];         /* if no macro is active */
8043                 else                    /* otherwise */
8044                   p = m_arg[maclvl][c - '0']; /* they're on the stack */
8045             } else if (c == '*') {
8046 #ifdef COMMENT
8047                 p = (maclvl > -1) ? m_line[maclvl] : topline;
8048                 if (!p) p = "";
8049 #else
8050                 int nx = FNVALL;
8051                 char * sx = fnval;
8052                 p = fnval;
8053 #ifdef COMMENT
8054                 if (cmdsrc() == 0 && topline)
8055                   p = topline;
8056                 else
8057 #endif /* COMMENT */
8058                   if (zzstring("\\fjoin(&_[],{ },1)",&sx,&nx) < 0) {
8059                     failed = 1;
8060                     p = fnval;
8061                     if (fndiags)
8062                       sprintf(fnval,"<ERROR:OVERFLOW:\\fcontents()>");
8063                     debug(F110,"zzstring fcontents(\\%*)",p,0);
8064                 }
8065 #endif /* COMMENT */
8066             } else {
8067                 if (isupper(c)) c -= ('a'-'A');
8068                 p = g_var[c];           /* Letter for global variable */
8069             }
8070             if (!p) p = "";
8071             goto fnend;
8072         } else if (c == '&') {          /* Array reference. */
8073             int vbi, d;
8074             if (arraynam(p,&vbi,&d) < 0) { /* Get name and subscript */
8075                 failed = 1;
8076                 p = fnval;
8077                 if (fndiags)
8078                   sprintf(fnval,"<ERROR:ARG_BAD_ARRAY:\\fcontents()>");
8079                 goto fnend;
8080             }
8081             subscript = chkarray(vbi,d); /* Check the array */
8082             if (subscript >= 0) {       /* Array is declared? */
8083                 vbi -= ARRAYBASE;       /* Convert name to index */
8084                 if (a_dim[vbi] >= d) {  /* If subscript in range */
8085                     char **ap;
8086                     ap = a_ptr[vbi];    /* get data pointer */
8087                     if (ap) {           /* and if there is one */
8088                         p = ap[d];      /* return it */
8089                         goto fnend;
8090                     }
8091                 }
8092             } else {                    /* Array not declared or element */
8093                 fnval[0] = NUL;         /* out of range - return null string */
8094                 p = fnval;              /* fdc 2010-12-30 */
8095                 goto fnend;     
8096             }
8097         } else {
8098             failed = 1;
8099             p = fnval;
8100             if (fndiags)
8101               sprintf(fnval,"<ERROR:ARG_NOT_VARIABLE:\\fcontents()>");
8102             goto fnend;
8103         }
8104     }
8105     p = fnval;                          /* Default result pointer */
8106     fnval[0] = NUL;                     /* Default result = empty string */
8107
8108     for (i = 0; i < argn; i++) {        /* Loop to expand each argument */
8109         n = MAXARGLEN;                  /* Allow plenty of space */
8110         bp[i] = s = malloc(n+1);        /* Allocate space for this argument */
8111         if (bp[i] == NULL) {            /* Handle failure to get space */
8112             failed = 1;
8113             if (fndiags)
8114               ckmakmsg(fnval,FNVALL,"<ERROR:MALLOC_FAILURE:\\f",fn,"()>",NULL);
8115             goto fnend;
8116         }
8117         p = argp[i] ? argp[i] : "";     /* Point to this argument */
8118
8119 /*
8120   Trim leading and trailing spaces from the original argument, before
8121   evaluation.  This code new to edit 184.  Fixed in edit 199 to trim
8122   blanks BEFORE stripping braces.
8123
8124 */
8125         {
8126             int x, j;
8127             x = strlen(p);
8128             j = x - 1;                  /* Trim trailing whitespace */
8129             while (j > 0 && (*(p + j) == SP || *(p + j) == HT))
8130               *(p + j--) = NUL;
8131             while (*p == SP || *p == HT) /* Strip leading whitespace */
8132               p++;
8133             x = strlen(p);
8134             if (*p == '{' && *(p+x-1) == '}') { /* NOW strip braces */
8135                 p[x-1] = NUL;
8136                 p++;
8137                 x -= 2;
8138             }
8139         }
8140
8141 /* Now evaluate the argument */
8142
8143         debug(F111,"fneval calling zzstring",p,n);
8144         t = zzstring(p,&s,&n);          /* Expand arg into new space */
8145         debug(F101,"fneval zzstring","",t);
8146         debug(F101,"fneval zzstring","",n);
8147         if (t < 0) {
8148             debug(F101,"fneval zzstring fails, arg","",i);
8149             failed = 1;
8150             if (fndiags) {
8151                 if (n == 0)
8152                   ckmakmsg(fnval,FNVALL,
8153                            "<ERROR:ARG_TOO_LONG:\\f",fn,"()>",NULL);
8154                 else
8155                   ckmakmsg(fnval,FNVALL,
8156                            "<ERROR:ARG_EVAL_FAILURE:\\f",fn,"()>",NULL);
8157             }
8158             goto fnend;
8159         }
8160         debug(F111,"fneval arg",bp[i],i);
8161     }
8162
8163 #ifdef DEBUG
8164     if (deblog) {
8165         int j;
8166         for (j = 0; j < argn; j++) {
8167             debug(F111,"fneval arg post eval",argp[j],j);
8168             debug(F111,"fneval evaluated arg",bp[j],j);
8169         }
8170     }
8171 #endif /* DEBUG */
8172     {
8173         /* Adjust argn for empty trailing arguments. */
8174         /* For example when an arg is a variable name but the */
8175         /* variable has no value.   July 2006. */
8176         int j, old; char *p;
8177         old = argn;
8178         for (j = argn - 1; j >= 0; j--) {
8179             p = bp[j];
8180             if (!p)
8181               argn--;
8182             else if (!*p)
8183               argn--;
8184             else
8185               break;
8186         }
8187 #ifdef DEBUG
8188         if (argn != old)
8189           debug(F101,"fneval adjusted argn","",argn);
8190 #endif  /* DEBUG */
8191     }   
8192
8193 /*
8194   From this point on, bp[0..argn-1] are not NULL and all must be freed
8195   before returning.
8196 */
8197     if (argn < 1) {                     /* Catch required args missing */
8198         switch (cx) {
8199           case FN_DEF:
8200           case FN_EVA:
8201           case FN_EXE:
8202           case FN_CHR:
8203           case FN_COD:
8204           case FN_MAX:
8205           case FN_MIN:
8206           case FN_MOD:
8207           case FN_FD:
8208           case FN_FS:
8209           case FN_TOD:
8210           case FN_FFN:
8211           case FN_BSN:
8212           case FN_RAW:
8213           case FN_CMD:
8214           case FN_2HEX:
8215           case FN_2OCT:
8216           case FN_DNAM:
8217 #ifdef FN_ERRMSG
8218           case FN_ERRMSG:
8219 #endif /* FN_ERRMSG */
8220 #ifdef CK_KERBEROS
8221           case FN_KRB_TK:
8222           case FN_KRB_NX:
8223           case FN_KRB_IV:
8224           case FN_KRB_TT:
8225           case FN_KRB_FG:
8226 #endif /* CK_KERBEROS */
8227           case FN_MJD2:
8228           case FN_N2TIM:
8229           case FN_DIM:
8230           case FN_DATEJ:
8231           case FN_PNCVT:
8232           case FN_PERM:
8233           case FN_ALOOK:
8234           case FN_TLOOK:
8235           case FN_ABS:
8236           case FN_AADUMP:
8237           case FN_JOIN:
8238 #ifdef CKFLOAT
8239           case FN_FPABS:
8240           case FN_FPEXP:
8241           case FN_FPLOG:
8242           case FN_FPLN:
8243           case FN_FPMOD:
8244           case FN_FPSQR:
8245           case FN_FPADD:
8246           case FN_FPDIV:
8247           case FN_FPMUL:
8248           case FN_FPPOW:
8249           case FN_FPSUB:
8250           case FN_FPINT:
8251           case FN_FPROU:
8252           case FN_FPSIN:
8253           case FN_FPCOS:
8254           case FN_FPTAN:
8255 #endif /* CKFLOAT */
8256 #ifdef TCPSOCKET
8257           case FN_HSTADD:
8258           case FN_HSTNAM:
8259 #endif /* TCPSOCKET */
8260           case FN_DELSEC:
8261 #ifdef COMMENT
8262           case FN_KWVAL:
8263           case FN_SLEEP:
8264           case FN_MSLEEP:
8265 #endif /* COMMENT */
8266 #ifdef NT
8267           case FN_SNAME:
8268           case FN_LNAME:
8269 #endif /* NT */
8270             failed = 1;
8271             p = fnval;
8272             if (fndiags)
8273               ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
8274             goto fnend;
8275         }
8276     }
8277     p = fnval;                          /* Reset these again. */
8278     fnval[0] = NUL;
8279
8280     switch (cx) {                       /* Do function on expanded args. */
8281 #ifdef TCPSOCKET
8282       case FN_HSTADD:
8283         p = ckaddr2name(bp[0]);
8284         goto fnend;
8285       case FN_HSTNAM:
8286         p = ckname2addr(bp[0]);
8287         goto fnend;
8288 #endif /* TCPSOCKET */
8289
8290       case FN_DEF:                      /* \fdefinition(arg1) */
8291         k = isaarray(bp[0]) ?
8292             mxxlook(mactab,bp[0],nmac) :
8293                 mxlook(mactab,bp[0],nmac);
8294         p = (k > -1) ? mactab[k].mval : "";
8295         goto fnend;
8296
8297       case FN_EVA:                      /* \fevaluate(arg1) */
8298         p = *(bp[0]) ? evalx(bp[0]) : "";
8299         if (!*p && fndiags) {
8300             failed = 1;
8301             p = fnval;
8302             evalerr(fn);
8303         }
8304         goto fnend;
8305
8306       case FN_EXE:                      /* \fexecute(arg1) */
8307         j = (int)strlen(s = bp[0]);     /* Length of macro invocation */
8308         p = "";                         /* Initialize return value to null */
8309         if (j) {                        /* If there is a macro to execute */
8310             while (*s == SP) s++,j--;   /* strip leading spaces */
8311             p = s;                      /* remember beginning of macro name */
8312             for (i = 0; i < j; i++) {   /* find end of macro name */
8313                 if (*s == SP)
8314                   break;
8315                 s++;
8316             }
8317             if (*s == SP)       {       /* if there was a space after */
8318                 *s++ = NUL;             /* terminate the macro name */
8319                 while (*s == SP) s++;   /* skip past any extra spaces */
8320             } else
8321               s = "";                   /* maybe there are no arguments */
8322             if (p && *p) {
8323                 k = mlook(mactab,p,nmac); /* Look up the macro name */
8324                 debug(F111,"fexec mlook",p,k);
8325             } else
8326               k = -1;
8327             if (k < 0) {
8328                 char * p2 = p;
8329                 failed = 1;
8330                 p = fnval;
8331                 if (fndiags)
8332                   ckmakxmsg(fnval,FNVALL,
8333                             "<ERROR:NO_SUCH_MACRO:\\f",fn,"(",p2,")>",
8334                             NULL,NULL,NULL,NULL,NULL,NULL,NULL);
8335                 goto fnend;
8336             }
8337 /*
8338   This is just a WEE bit dangerous because we are copying up to 9 arguments
8339   into the space reserved for one.  It won't overrun the buffer, but if there
8340   are lots of long arguments we might lose some.  The other problem is that if
8341   the macro has more than 3 arguments, the 4th through last are all
8342   concatenated onto the third.  (The workaround is to use spaces rather than
8343   commas to separate them.)  Leaving it like this to avoid having to allocate
8344   tons more buffers.
8345 */
8346             if (argn > 1) {             /* Commas used instead of spaces */
8347                 int i;
8348                 char *p = bp[0];        /* Reuse this space */
8349                 *p = NUL;               /* Make into dodo() arg list */
8350                 for (i = 1; i < argn; i++) {
8351                     ckstrncat(p,bp[i],MAXARGLEN);
8352                     ckstrncat(p," ",MAXARGLEN);
8353                 }
8354                 s = bp[0];              /* Point to new list */
8355             }
8356             p = "";                     /* Initialize return value */
8357             if (k >= 0) {               /* If macro found in table */
8358                 /* Go set it up (like DO cmd) */
8359                 if ((j = dodo(k,s,cmdstk[cmdlvl].ccflgs)) > 0) {
8360                     if (cmpush() > -1) { /* Push command parser state */
8361                         extern int ifc;
8362                         int ifcsav = ifc; /* Push IF condition on stack */
8363                         k = parser(1);  /* Call parser to execute the macro */
8364                         cmpop();        /* Pop command parser */
8365                         ifc = ifcsav;   /* Restore IF condition */
8366                         if (k == 0) {   /* No errors, ignore action cmds. */
8367                             p = mrval[maclvl+1]; /* If OK, set return value. */
8368                             if (p == NULL) p = "";
8369                         }
8370                     } else {            /* Can't push any more */
8371                         debug(F100,"zzstring fneval fexec failure","",0);
8372                         printf("\n?\\fexec() too deeply nested\n");
8373                         while (cmpop() > -1) ;
8374                         p = "";
8375                     }
8376                 }
8377             }
8378         }
8379         debug(F110,"zzstring fneval fexecute final p",p,0);
8380         goto fnend;
8381
8382 #ifdef RECURSIVE
8383       case FN_RDIR:                     /* \frdir..() - Recursive dir count */
8384       case FN_RFIL:                     /* \frfiles() - Recursive file count */
8385         /* recursive = 2; */            /* fall thru... */
8386 #endif /* RECURSIVE */
8387       case FN_FC:                       /* \ffiles() - File count. */
8388       case FN_DIR: {                    /* \ffdir.() - Directory count. */
8389           char abuf[16], *s;
8390           char ** ap = NULL;
8391           int x, xflags = 0;
8392           if (matchdot)
8393             xflags |= ZX_MATCHDOT;
8394           if (cx == FN_RDIR || cx == FN_RFIL) {
8395               xflags |= ZX_RECURSE;
8396 #ifdef CKSYMLINK
8397               /* Recursive - don't follow symlinks */
8398               xflags |= ZX_NOLINKS;
8399 #endif /* CKSYMLINK */
8400           }
8401           failed = 0;
8402           if (argn < 1) {
8403               p = "0";
8404               goto fnend;
8405           }
8406           if (cx == FN_DIR || cx == FN_RDIR) { /* Only list directories */
8407               debug(F100,"FN_DIR or FN_RDIR","",0);
8408               xflags |= ZX_DIRONLY;
8409 #ifdef OS2
8410               zxpn = 1;                 /* Use the alternate list */
8411 #endif /* OS2 */
8412           } else {                      /* List only files */
8413               debug(F100,"Not FN_DIR or FN_RDIR","",0);
8414               xflags |= ZX_FILONLY;
8415 #ifdef OS2
8416               zxpn = 1;                 /* Use the alternate list */
8417 #endif /* OS2 */
8418           }
8419           if (*(bp[0])) {
8420               k = nzxpand(bp[0],xflags);
8421               if (k < 0) k = 0;
8422               sprintf(fnval,"%d",k);    /* SAFE */
8423               p = fnval;
8424           } else
8425             p = "0";
8426
8427           if (argn > 1) {               /* Assign list to array */
8428               fnval[0] = NUL;           /* Initial return value */
8429               ckstrncpy(abuf,bp[1],16); /* Get array reference */
8430               s = abuf;
8431               if (*s == CMDQ) s++;
8432               failed = 1;               /* Assume it's bad */
8433               p = fnval;                /* Point to result */
8434               if (fndiags)              /* Default is this error message */
8435                 ckmakmsg(fnval,FNVALL,
8436                          "<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
8437               if (s[0] != '&')          /* "Address" of array */
8438                 goto fnend;
8439               if (s[2])
8440                 if (s[2] != '[' || s[3] != ']')
8441                   goto fnend;
8442               if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */
8443                 s[1] += 32;
8444               if ((x = dclarray(s[1],k)) < 0) /* File list plus count */
8445                 goto fnend;
8446               failed = 0;               /* Unset failure flag */
8447               ap = a_ptr[x];            /* Point to array we just declared */
8448               sprintf(fnval,"%d",k);    /* SAFE */
8449           }
8450 #ifdef OS2
8451           if (ap) {                     /* We are making an array */
8452               int i;
8453               char tmp[16];
8454               ap[0] = NULL;             /* Containing number of files    */
8455               makestr(&(ap[0]),ckitoa(k));
8456
8457               ckstrncpy(tmp,fnval,16);  /* Save return value */
8458
8459               for (i = 1; i <= k; i++) { /* Fill it */
8460                   ap[i] = NULL;
8461                   znext(fnval);         /* Next filename */
8462                   if (!*fnval)          /* No more, done */
8463                     break;              /* In case a premature end */
8464                   makestr(&(ap[i]),fnval);
8465               }
8466 #ifdef ZXREWIND
8467               k = zxrewind();           /* Reset the file expansion */
8468 #else
8469               k = nzxpand(bp[0],xflags);
8470 #endif /* ZXREWIND */
8471               ckstrncpy(fnval,tmp,FNVALL); /* Restore return value */
8472           }
8473 #else /* OS2 */
8474           {                             /* Make copies of the list */
8475               int i; char tmp[16];
8476               if (flist) {              /* Free old file list, if any */
8477                   for (i = 0; flist[i]; i++) { /* and each string */
8478                       free(flist[i]);
8479                       flist[i] = NULL;
8480                   }
8481                   free((char *)flist);
8482               }
8483               ckstrncpy(tmp,fnval,16);  /* Save our return value */
8484               flist = (char **) malloc((k+1) * sizeof(char *)); /* New array */
8485               if (flist) {
8486                   for (i = 0; i <= k; i++) { /* Fill it */
8487                       flist[i] = NULL;
8488                       znext(fnval);     /* Next filename */
8489                       if (!*fnval)      /* No more, done */
8490                         break;
8491                       makestr(&(flist[i]),fnval);
8492                   }
8493                   if (ap) {             /* If array pointer given */
8494                       ap[0] = NULL;
8495                       makestr(&(ap[0]),ckitoa(k));
8496                       for (i = 0; i < k; i++) { /* Copy file list to array */
8497                           ap[i+1] = NULL;
8498                           makestr(&(ap[i+1]),flist[i]);
8499                       }
8500                   }
8501               }
8502               ckstrncpy(fnval,tmp,FNVALL); /* Restore return value */
8503               flistn = 0;               /* Reset global list pointer */
8504           }
8505 #endif /* OS2 */
8506 #ifdef RECURSIVE
8507           recursive = rsave;
8508 #endif /* RECURSIVE */
8509 #ifdef OS2
8510           zxpn = zsave;
8511 #endif /* OS2 */
8512       }
8513       goto fnend;
8514
8515       case FN_FIL:                      /* \fnextfile() - Next file in list. */
8516         p = fnval;                      /* (no args) */
8517         *p = NUL;
8518 #ifdef OS2
8519         zxpn = 1;                       /* OS/2 - use the alternate list */
8520         znext(p);                       /* Call system-dependent function */
8521         zxpn = zsave;                   /* Restore original list */
8522 #else
8523         if (flist)                      /* Others, use our own list. */
8524           if (flist[flistn])
8525             p = flist[flistn++];
8526 #endif /* OS2 */
8527         goto fnend;
8528
8529     } /* Break up big switch... */
8530
8531     switch (cx) {
8532       case FN_IND:                      /* \findex(s1,s2,start,occurrence) */
8533       case FN_RIX:                      /* \frindex(s1,s2,start,occurrence) */
8534       case FN_SEARCH:                   /* \fsearch(pat,string,start,occ) */
8535       case FN_RSEARCH:                  /* \frsearch(pat,string,start,occ) */
8536       case FN_COUNT: {                  /* \fcount(s1,s2,start) */
8537         int i = 0, right = 0, search = 0, count = 0;
8538         int desired = 1;
8539         right = (cx == FN_RIX || cx == FN_RSEARCH);
8540         search = (cx == FN_SEARCH || cx == FN_RSEARCH);
8541         count = (cx == FN_COUNT);
8542         p = "0";
8543         if (argn > 1) {                 /* Only works if we have 2 or 3 args */
8544             int start = 0;
8545             char * pat = NULL;
8546             len1 = (int)strlen(pat = bp[0]); /* length of string to look for */
8547             len2 = (int)strlen(s = bp[1]); /* length of string to look in */
8548             if (len1 < 1 || len2 < 1)   /* Watch out for empty strings */
8549               goto fnend;
8550             start = right ? -1 : 0;     /* Default starting position */
8551             if (argn > 2) {
8552                 val1 = *(bp[2]) ? evalx(bp[2]) : "1";
8553                 if (argn > 3) {
8554                     val2 = *(bp[3]) ? evalx(bp[3]) : "1";
8555                     if (chknum(val2)) desired = atoi(val2);
8556                     if (desired * len1 > len2) goto fnend;
8557                 }
8558                 if (chknum(val1)) {
8559                     int t;
8560                     t = atoi(val1);
8561                     if (!search) {      /* Index or Rindex */
8562                         j = len2 - len1; /* Length difference */
8563                         t--;             /* Convert position to 0-based */
8564                         if (t < 0) t = 0;
8565                         start = t;
8566                         if (!right && start < 0) start = 0;
8567                     } else {            /* Search or Rsearch */
8568                         int x;
8569                         if (t < 0) t = 0;
8570                         if (right) {    /* Right to left */
8571                             if (t > len2) t = len2;
8572                             start = len2 - t - 1;
8573                             if (start < 0)
8574                               goto fnend;
8575                             x = len2 - t;
8576                             s[x] = NUL;
8577                         } else {        /* Left to right */
8578                             start = t - 1;
8579                             if (start < 0) start = 0;
8580                             if (start >= len2)
8581                               goto fnend;
8582                         }
8583                     }
8584                 } else {
8585                     failed = 1;
8586                     evalerr(fn);
8587                     p = fnval;
8588                     goto fnend;
8589                 }
8590             }
8591             if (count) {                /* \fcount() */
8592                 int j;
8593                 for (i = 0; start < len2; i++) {
8594                     j = ckindex(pat,bp[1],start,0,inpcas[cmdlvl]);
8595                     if (j == 0) break;
8596                     start = j;
8597                 }
8598
8599             } else if (search) {        /* \fsearch() or \frsearch() */
8600
8601                 if (right && pat[0] == '^') {
8602                     right = 0;
8603                     start = 0;
8604                 }
8605                 if (right) {            /* From right */
8606                     int k, j = 1;
8607                     if (start < 0)
8608                       start = len2 - 1;
8609                     i = 0;
8610                     while (start >= 0 && j <= desired) {
8611                         for (i = start;
8612                              (i >= 0) && 
8613                                  !(k = ckmatch(pat,s+i,inpcas[cmdlvl],1+4));
8614                              i--) ;
8615                         if (k < 1) {    /* No match */
8616                             i = 0;
8617                             break;
8618                         }
8619                         if (j == desired) { /* The match we want? */
8620                             i += k;     /* Yes, return string index */
8621                             break;
8622                         }
8623                         j++;            /* No, count this match */
8624                         s[i] = NUL;     /* null it out */
8625                         start = i-1;    /* move left and look again */
8626                     }
8627
8628                 } else {                /* From left */
8629                     int j;
8630                     i = 0;
8631                     for (j = 1; j <= desired && start < len2; j++) {
8632                         i = ckmatch(pat,&s[start],inpcas[cmdlvl],1+4);
8633                         if (i == 0 || j == desired) break;
8634                         start += i + 1;
8635                     }                   
8636                     if (j == desired && i != 0)
8637                       i += start;
8638                     else
8639                       i = 0;
8640                 }
8641             } else {                    /* index or rindex */
8642                 int j = 0;
8643                 i = 0;
8644                 for (j = 1; j <= desired && start < len2; j++) {
8645                     i = ckindex(pat,bp[1],start,right,inpcas[cmdlvl]);
8646                     if (i == 0 || j == desired) break;
8647                     start = (right) ? len2 - i + 1 : i;
8648                 }
8649                 if (j != desired)
8650                   i = 0;
8651             }
8652             sprintf(fnval,"%d",i);      /* SAFE */
8653             p = fnval;
8654         }
8655         goto fnend;
8656       }
8657
8658       case FN_RPL:                      /* \freplace(s1,s2,s3) */
8659       /*
8660         s = bp[0] = source string
8661             bp[1] = match string
8662             bp[2] = replacement string
8663             bp[3] = which occurrence (default = all);
8664         p = fnval = destination (result) string
8665       */
8666         if (argn < 1)                   /* Nothing */
8667           goto fnend;
8668         if (argn < 2) {                 /* Only works if we have 2 or 3 args */
8669             ckstrncpy(p,bp[0],FNVALL);
8670         } else {
8671             int occur = 0, xx = 0, j2;
8672             len1 = (int)strlen(bp[0]);  /* length of string to look in */
8673             len2 = (int)strlen(bp[1]);  /* length of string to look for */
8674             len3 = (argn < 3) ? 0 : (int)strlen(bp[2]); /* Len of replacemnt */
8675             j = len1 - len2 + 1;
8676             j2 = j;
8677             if (argn > 3) {
8678                 if (chknum(bp[3])) {
8679                     occur = atoi(bp[3]);
8680                 } else {
8681                     failed = 1;
8682                     if (fndiags)
8683                       ckmakmsg(fnval,FNVALL,
8684                                "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
8685                     goto fnend;
8686                 }
8687             }
8688             /* If args out of whack... */
8689             if (j < 1 || len1 == 0 || len2 == 0) {
8690                 ckstrncpy(p,bp[0],FNVALL); /* just return original string */
8691                 p[FNVALL] = NUL;
8692             } else {
8693               ragain:
8694                 s = bp[0];              /* Point to beginning of string */
8695                 while (j-- > 0) {       /* For each character */
8696                     if (!ckstrcmp(bp[1],s,len2,inpcas[cmdlvl]) &&
8697                         (occur == 0 || occur == ++xx)) {
8698                         if (len3) {
8699                             ckstrncpy(p,bp[2],FNVALL);
8700                             p += len3;
8701                         }
8702                         s += len2;      /* and skip past it. */
8703                     } else {            /* No, */
8704                         *p++ = *s++;    /* just copy this character */
8705                     }
8706                 }
8707                 *p = NUL;
8708                 while ((*p++ = *s++));
8709                 if (occur < 0) {        /* cheap... */
8710                     occur = xx + occur + 1;
8711                     xx = 0;
8712                     p = fnval;
8713                     j = j2;
8714                     if (occur > 0)
8715                       goto ragain;
8716                 }
8717             }
8718         }
8719         p = fnval;
8720         goto fnend;
8721
8722       case FN_CHR:                      /* \fcharacter(arg1) */
8723         val1 = *(bp[0]) ? evalx(bp[0]) : "";
8724         if (chknum(val1)) {             /* Must be numeric */
8725             i = atoi(val1);
8726             if (i >= 0 && i < 256) {    /* Must be an 8-bit value */
8727                 p = fnval;
8728                 *p++ = (char) i;
8729                 *p = NUL;
8730                 p = fnval;
8731             } else {
8732                 failed = 1;
8733                 if (fndiags)
8734                   ckmakmsg(fnval,FNVALL,
8735                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
8736             }
8737         } else {
8738             failed = 1;
8739             evalerr(fn);
8740         }
8741         goto fnend;
8742
8743       case FN_COD:                      /* \fcode(char) */
8744         if ((int)strlen(bp[0]) > 0) {
8745             p = fnval;
8746             i = *bp[0];
8747             sprintf(p,"%d",(i & 0xff)); /* SAFE */
8748         } else p = "0";                 /* Can't happen */
8749         goto fnend;
8750
8751       case FN_LEN:                      /* \flength(arg1) */
8752         if (argn > 0) {
8753             p = fnval;
8754             sprintf(p,"%d",(int)strlen(bp[0])); /* SAFE */
8755         } else p = "0";
8756         goto fnend;
8757
8758       case FN_LOW:                      /* \flower(arg1) */
8759         s = bp[0] ? bp[0] : "";
8760         p = fnval;
8761         while (*s) {
8762             if (isupper(*s))
8763               *p = (char) tolower(*s);
8764             else
8765               *p = *s;
8766             p++; s++;
8767         }
8768         *p = NUL;
8769         p = fnval;
8770         goto fnend;
8771
8772       case FN_MAX:                      /* \fmax(arg1,arg2) */
8773       case FN_MIN:                      /* \fmin(arg1,arg2) */
8774       case FN_MOD:                      /* \fmod(arg1,arg2) */
8775         val1 = *(bp[0]) ? evalx(bp[0]) : "";
8776 #ifdef COMMENT
8777         /* No longer necessary because evalx() no longer overwrites its */
8778         /* result every time it's called (2000/09/23). */
8779         free(bp[0]);
8780         bp[0] = NULL;
8781 #endif /* COMMENT */
8782         if (argn > 1) {
8783 #ifdef COMMENT
8784             /* Ditto... */
8785             bp[0] = malloc((int)strlen(val1)+1);
8786             if (bp[0])
8787               strcpy(bp[0],val1);       /* safe */
8788             val1 = bp[0];
8789 #endif /* COMMENT */
8790             val2 = *(bp[1]) ? evalx(bp[1]) : "";
8791             if (chknum(val1) && chknum(val2)) {
8792                 i = atoi(val1);
8793                 j = atoi(val2);
8794                 switch (y) {
8795                   case FN_MAX:
8796                     if (j < i) j = i;
8797                     break;
8798                   case FN_MIN:
8799                     if (j > i) j = i;
8800                     break;
8801                   case FN_MOD:
8802                     if (j == 0) {
8803                         failed = 1;
8804                         if (fndiags)
8805                           ckmakmsg(fnval,FNVALL,
8806                                    "<ERROR:DIVIDE_BY_ZERO:\\f",fn,"()>",NULL);
8807                         else
8808                           fnval[0] = NUL;
8809                         goto fnend;
8810                     } else
8811                       j = i % j;
8812                 }
8813                 p = fnval;
8814                 sprintf(p,"%d",j);      /* SAFE */
8815             } else {
8816                 failed = 1;
8817                 evalerr(fn);
8818             }
8819         } else p = val1;
8820         goto fnend;
8821     } /* Break up big switch... */
8822
8823     switch (y) {
8824       case FN_SUB:                      /* \fsubstr(arg1,arg2,arg3) */
8825       case FN_RIG:                      /* \fright(arg1,arg2) */
8826       case FN_LEF:                      /* \fleft(arg1,arg2) */
8827         if (argn < 1)
8828           goto fnend;
8829         val1 = "";
8830         if (argn > 1)
8831           if (*(bp[1]))
8832             val1 =  evalx(bp[1]);
8833 #ifdef COMMENT
8834         if (bp[1]) free(bp[1]);         /* Have to copy this */
8835         bp[1] = malloc((int)strlen(val1)+1);
8836         if (!bp[1]) {
8837             failed = 1;
8838             if (fndiags) {
8839                 p = fnval;
8840                 ckmakmsg(fnval,FNVALL,
8841                          "<ERROR:MALLOC_FAILURE:\\f",fn,"()>",NULL);
8842             }
8843             goto fnend;
8844         }
8845         strcpy(bp[1],val1);             /* safe */
8846         val1 = bp[1];
8847 #endif /* COMMENT */
8848         val2 = "";
8849         if (argn > 2)
8850           if (*(bp[2]))
8851             val2 = evalx(bp[2]);
8852         if (
8853             ((argn > 1) && (int)strlen(val1) && !rdigits(val1)) ||
8854             ((cx == FN_SUB) &&
8855               ((argn > 2) && (int)strlen(val2) && !rdigits(val2)))
8856             ) {
8857             failed = 1;
8858             evalerr(fn);
8859         } else {
8860             int lx;
8861             p = fnval;                  /* pointer to result */
8862             lx = strlen(bp[0]);         /* length of arg1 */
8863             if (cx == FN_SUB) {         /* substring */
8864                 k = (argn > 2) ? atoi(val2) : MAXARGLEN; /* length */
8865                 j = (argn > 1) ? atoi(val1) : 1; /* start pos for substr */
8866             } else if (cx == FN_LEF) {  /* left */
8867                 k = (argn > 1) ? atoi(val1) : lx;
8868                 j = 1;
8869             } else {                             /* right */
8870                 k = (argn > 1) ? atoi(val1) : lx; /* length */
8871                 j = lx - k + 1;                  /* start pos for right */
8872                 if (j < 1) j = 1;
8873             }
8874             if (k > 0 && j <= lx) {              /* if start pos in range */
8875                 s = bp[0]+j-1;                   /* point to source string */
8876                 for (i = 0; (i < k) && (*p++ = *s++); i++) ;  /* copy */
8877             }
8878             *p = NUL;                   /* terminate the result */
8879             p = fnval;                  /* and point to it. */
8880         }
8881         goto fnend;
8882
8883       case FN_UPP:                      /* \fupper(arg1) */
8884         s = bp[0] ? bp[0] : "";
8885         p = fnval;
8886         while (*s) {
8887             if (islower(*s))
8888               *p = (char) toupper(*s);
8889             else
8890               *p = *s;
8891             p++; s++;
8892         }
8893         *p = NUL;
8894         p = fnval;
8895         goto fnend;
8896
8897       case FN_REP:                      /* \frepeat(text,number) */
8898         if (argn < 1)
8899           goto fnend;
8900         val1 = "1";
8901         if (argn > 1)
8902           if (*(bp[1]))
8903             val1 = evalx(bp[1]);
8904         if (chknum(val1)) {             /* Repeat count */
8905             n = atoi(val1);
8906             debug(F111,"SUNDAY frepeat n",val1,n);
8907             if (n > 0) {                /* Make n copies */
8908                 p = fnval;
8909                 *p = '\0';
8910                 k = (int)strlen(bp[0]); /* Make sure string has some length */
8911                 debug(F111,"SUNDAY frepeat k","",k);
8912                 debug(F111,"SUNDAY frepeat FNVALL","",FNVALL);
8913                 if (k * n >= FNVALL) {  /* But not too much... */
8914                     failed = 1;
8915                     if (fndiags)
8916                       ckmakmsg(fnval,FNVALL,
8917                                "<ERROR:RESULT_TOO_LONG:\\f",fn,"()>",NULL);
8918                     else
8919                       fnval[0] = NUL;
8920                     p = fnval;
8921                     goto fnend;
8922                 }
8923                 if (k > 0) {            /* If there is something to copy */
8924                     for (i = 0; i < n; i++) { /* Copy loop */
8925                         s = bp[0];
8926                         for (j = 0; j < k; j++) {
8927                             if ((p - fnval) >= FNVALL)
8928                               break;    /* shouldn't happen... */
8929                             else
8930                               *p++ = *s++;
8931                         }
8932                     }
8933                     *p = NUL;
8934                 }
8935             }
8936         } else {
8937             failed = 1;
8938             evalerr(fn);
8939         }
8940         p = fnval;
8941         goto fnend;
8942
8943 #ifndef NOFRILLS
8944       case FN_REV:                      /* \freverse() */
8945         if (argn < 1)
8946           goto fnend;
8947         yystring(bp[0],&p);
8948         goto fnend;
8949 #endif /* NOFRILLS */
8950
8951       case FN_RPA:                      /* \frpad() and \flpad() */
8952       case FN_LPA:
8953         if (argn < 1)
8954           goto fnend;
8955         val1 = "";
8956         if (argn > 1)
8957           if (*(bp[1]))
8958             val1 = evalx(bp[1]);
8959         if (argn == 1) {                /* If a number wasn't given */
8960             p = fnval;                  /* just return the original string */
8961             ckstrncpy(p,bp[0],FNVALL);
8962         } else if (argn > 1 &&  !*val1) {
8963             failed = 1;
8964             evalerr(fn);
8965         } else /* if (chknum(val1)) */ { /* Repeat count */
8966             char pc;
8967             n = atoi(val1);
8968             if (n >= 0) {
8969                 p = fnval;
8970                 k = (int)strlen(bp[0]); /* Length of string to be padded */
8971                 if (k >= n) {           /* It's already long enough */
8972                     ckstrncpy(p,bp[0],FNVALL);
8973                 } else {
8974                     if (n + k <= FNVALL) {
8975                         pc = (char) ((argn < 3) ? SP : *bp[2]);
8976                         if (!pc) pc = SP;
8977                         if (cx == FN_RPA) { /* RPAD */
8978                             strncpy(p,bp[0],k); /* (leave it like this) */
8979                             p[k] = NUL;
8980                             p += k;
8981                             for (i = k; i < n; i++)
8982                               *p++ = pc;
8983                         } else {        /* LPAD */
8984                             n -= k;
8985                             for (i = 0; i < n; i++)
8986                               *p++ = pc;
8987                             strncpy(p,bp[0],k); /* (leave it like this) */
8988                             p[k] = NUL;
8989                             p += k;
8990                         }
8991                     }
8992                     *p = NUL;
8993                 }
8994             }
8995         }
8996         p = fnval;
8997         goto fnend;
8998
8999 #ifdef ZFCDAT
9000       case FN_FD:                       /* \fdate(filename) */
9001         p = fnval;
9002         s = zfcdat(bp[0]);
9003         if (!s) s = "";
9004         if (!*s) {
9005             failed = 1;
9006             if (fndiags)
9007               ckmakmsg(fnval,FNVALL,"<ERROR:FILE_NOT_FOUND:\\f",fn,"()>",NULL);
9008             goto fnend;
9009         }
9010         ckstrncpy(fnval,s,FNVALL);
9011 #endif /* ZFCDAT */
9012         goto fnend;
9013
9014     } /* Break up big switch... */
9015
9016     switch (y) {
9017       case FN_FS: {                     /* \fsize(filename) */
9018           CK_OFF_T z;
9019           p = fnval;
9020           z = zchki(bp[0]);
9021           if (z < (CK_OFF_T)0) {
9022               failed = 1;
9023               if (fndiags) {
9024                   if (z == (CK_OFF_T)-1)
9025                     ckmakmsg(fnval,FNVALL,
9026                              "<ERROR:FILE_NOT_FOUND:\\f",fn,"()>",NULL);
9027                   else if (z == (CK_OFF_T)-2)
9028                     ckmakmsg(fnval,FNVALL,
9029                              "<ERROR:FILE_NOT_READABLE:\\f",fn,"()>",NULL);
9030                   else if (z == (CK_OFF_T)-3)
9031                     ckmakmsg(fnval,FNVALL,
9032                              "<ERROR:FILE_NOT_ACCESSIBLE:\\f",fn,"()>",NULL);
9033                   else
9034                     ckmakmsg(fnval,FNVALL,
9035                              "<ERROR:FILE_ERROR:\\f",fn,"()>",NULL);
9036               }
9037               goto fnend;
9038           }
9039           ckstrncpy(fnval,ckfstoa(z),FNVALL);
9040           goto fnend;
9041       }
9042       case FN_VER:                      /* \fverify() */
9043         p = "-1";
9044         if (argn == 1)                  /* No second arg */
9045           goto fnend;
9046         else if (!bp[1])                /* Or second arg null */
9047           goto fnend;
9048         else if (!*(bp[1]))             /* or empty. */
9049           goto fnend;
9050         p = "0";
9051         if (argn > 1) {                 /* Only works if we have 2 or 3 args */
9052             int start;
9053             char *s2, ch1, ch2;
9054             start = 0;
9055             if (argn > 2) {             /* Starting position specified */
9056                 val1 = *(bp[2]) ? evalx(bp[2]) : "0";
9057                 if (chknum(val1)) {
9058                     start = atoi(val1) /* - 1 */;
9059                     if (start < 0) start = 0;
9060                     if (start > (int)strlen(bp[1]))
9061                       goto verfin;
9062                 } else {
9063                     failed = 1;
9064                     evalerr(fn);
9065                     goto fnend;
9066                 }
9067             }
9068             i = start;
9069             p = "0";
9070             for (s = bp[1] + start; *s; s++,i++) {
9071                 ch1 = *s;
9072                 if (!inpcas[cmdlvl]) if (islower(ch1)) ch1 = toupper(ch1);
9073                 j = 0;
9074                 for (s2 = bp[0]; *s2; s2++) {
9075                     ch2 = *s2;
9076                     if (!inpcas[cmdlvl]) if (islower(ch2)) ch2 = toupper(ch2);
9077                     if (ch1 == ch2) {
9078                         j = 1;
9079                         break;
9080                     }
9081                 }
9082                 if (j == 0) {
9083                     sprintf(fnval,"%d",i+1); /* SAFE */
9084                     p = fnval;
9085                     break;
9086                 }
9087             }
9088         }
9089       verfin:
9090         goto fnend;
9091
9092       case FN_IPA:                      /* Find and return IP address */
9093         if (argn > 0) {                 /* in argument string. */
9094             int start = 0;
9095             if (argn > 1) {             /* Starting position specified */
9096                 if (chknum(bp[1])) {
9097                     start = atoi(bp[1]) - 1;
9098                     if (start < 0) start = 0;
9099                 } else {
9100                     failed = 1;
9101                     if (fndiags)
9102                       ckmakmsg(fnval,FNVALL,
9103                                "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9104                     goto fnend;
9105                 }
9106             }
9107             p = getip(bp[0]+start);
9108         } else p = "";
9109         goto fnend;
9110
9111 #ifdef OS2
9112       case FN_CRY:
9113         p = "";
9114         if (argn > 0) {
9115             p = fnval;
9116             ckstrncpy(p,bp[0],FNVALL);
9117             ck_encrypt(p);
9118         }
9119         goto fnend;
9120
9121       case FN_OOX:
9122         p = "";
9123         if (argn > 0)
9124           p = (char *) ck_oox(bp[0], (argn > 1) ? bp[1] : "");
9125         goto fnend;
9126 #endif /* OS2 */
9127
9128       case FN_HEX:                      /* \fhexify(arg1) */
9129         if (argn < 1)
9130           goto fnend;
9131         if ((int)strlen(bp[0]) < (FNVALL / 2)) {
9132             s = bp[0];
9133             p = fnval;
9134             while (*s) {
9135                 x = (*s >> 4) & 0x0f;
9136                 *p++ = hexdigits[x];
9137                 x = *s++ & 0x0f;
9138                 *p++ = hexdigits[x];
9139             }
9140             *p = NUL;
9141             p = fnval;
9142         }
9143         goto fnend;
9144
9145       case FN_UNTAB:                    /* \funtab(arg1) */
9146         if (argn < 1)
9147           goto fnend;
9148         if ((int)strlen(bp[0]) < (FNVALL * 2)) {
9149             s = bp[0];
9150             p = fnval;
9151             if (untabify(bp[0],p,FNVALL) < 0) {
9152                 failed = 1;
9153                 if (fndiags)
9154                   ckmakmsg(fnval,FNVALL,
9155                            "<ERROR:OVERFLOW:\\f",fn,"()>",NULL);
9156             }
9157             goto fnend;
9158         }
9159
9160       case FN_UNH: {                    /* \funhex(arg1) */
9161           int c[2], i;
9162           if (argn < 1)
9163             goto fnend;
9164           if ((int)strlen(bp[0]) < (FNVALL * 2)) {
9165               s = bp[0];
9166               p = fnval;
9167               while (*s) {
9168                   for (i = 0; i < 2; i++) {
9169                       c[i] = *s++;
9170                       if (!c[i]) { p = ""; goto unhexfin; }
9171                       if (islower(c[i])) c[i] = toupper(c[i]);
9172                       if (c[i] >= '0' && c[i] <= '9') {
9173                           c[i] -= 0x30;
9174                       } else if (c[i] >= 'A' && c[i] <= 'F') {
9175                           c[i] -= 0x37;
9176                       } else {
9177                           failed = 1;
9178                           if (fndiags)
9179                             ckmakmsg(fnval,
9180                                      FNVALL,
9181                                      "<ERROR:ARG_OUT_OF_RANGE:\\f",
9182                                      fn,
9183                                      "()>",
9184                                      NULL
9185                                      );
9186                           goto fnend;
9187                       }
9188                   }
9189                   *p++ = ((c[0] << 4) & 0xf0) | (c[1] & 0x0f);
9190               }
9191               *p = NUL;
9192               p = fnval;
9193           }
9194         unhexfin:
9195           goto fnend;
9196       }
9197
9198       case FN_BRK: {                    /* \fbreak() */
9199           char * c;                     /* Characters to break on */
9200           char c2, s2;
9201           int start = 0;
9202           int done = 0;
9203           if (argn < 1)
9204             goto fnend;
9205           if (argn > 2) {
9206               s = bp[2] ? bp[2] : "0";
9207               if (chknum(s)) {
9208                   start = atoi(s);
9209                   if (start < 0) start = 0;
9210                   if (start > (int)strlen(bp[0]))
9211                     goto brkfin;
9212               } else {
9213                   failed = 1;
9214                   if (fndiags)
9215                     ckmakmsg(fnval,FNVALL,
9216                              "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9217                   goto fnend;
9218               }
9219           }
9220           s = bp[0] + start;            /* Source pointer */
9221
9222           while (*s && !done) {
9223               s2 = *s;
9224               if (!inpcas[cmdlvl] && islower(s2)) s2 = toupper(s2);
9225               c = bp[1] ? bp[1] : "";   /* Character to break on */
9226               while (*c) {
9227                   c2 = *c;
9228                   if (!inpcas[cmdlvl] && islower(c2)) c2 = toupper(c2);
9229                   if (c2 == s2) {
9230                       done = 1;
9231                       break;
9232                   }
9233                   c++;
9234               }
9235               if (done) break;
9236               *p++ = *s++;
9237           }
9238           *p = NUL;                     /* terminate the result */
9239           p = fnval;                    /* and point to it. */
9240         brkfin:
9241           goto fnend;
9242       }
9243
9244       case FN_SPN: {                    /* \fspan() */
9245           char *q;
9246           char c1, c2;
9247           int start = 0;
9248           if (argn < 1)
9249             goto fnend;
9250           if (argn > 2) {               /* Starting position */
9251               s = bp[2] ? bp[2] : "0";
9252               if (chknum(s)) {
9253                   start = atoi(s);
9254                   if (start < 0) start = 0;
9255               } else {
9256                   failed = 1;
9257                   if (fndiags)
9258                     ckmakmsg(fnval,FNVALL,
9259                              "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9260                   goto fnend;
9261               }
9262           }
9263           s = bp[0] + start;            /* Source pointer */
9264           if (argn > 1 &&
9265               (int)strlen(bp[1]) > 0 &&
9266               start <= (int)strlen(bp[0])) {
9267               while (*s) {              /* Loop thru source string */
9268                   q = bp[1];            /* Span string */
9269                   c1 = *s;
9270                   if (!inpcas[cmdlvl])
9271                     if (islower(c1)) c1 = toupper(c1);
9272                   x = 0;
9273                   while ((c2 = *q++)) {
9274                       if (!inpcas[cmdlvl])
9275                         if (islower(c2)) c2 = toupper(c2);
9276                       if (c1 == c2) { x = 1; break; }
9277                   }
9278                   if (!x) break;
9279                   *p++ = *s++;
9280               }
9281               *p = NUL;                 /* Terminate and return the result */
9282               p = fnval;
9283           }
9284           goto fnend;
9285       }
9286     } /* Break up big switch... */
9287
9288     switch (y) {
9289       case FN_TRM:                      /* \ftrim(s1[,s2]) */
9290       case FN_LTR:                      /* \fltrim(s1[,s2]) */
9291         if (argn < 1)
9292           goto fnend;
9293         if ((len1 = (int)strlen(bp[0])) > 0) {
9294             if (len1 > FNVALL)
9295               len1 = FNVALL;
9296             s = " \t\r\n";
9297             if (argn > 1)               /* Trim list given */
9298               s = bp[1];
9299             len2 = (int)strlen(s);
9300             if (len2 < 1) {             /* or not... */
9301                 s = " \t\r\n";          /* Default is to trim whitespace */
9302                 len2 = 2;
9303             }
9304             if (cx == FN_TRM) {         /* Trim from right */
9305                 char * q, p2, q2;
9306                 ckstrncpy(fnval,bp[0],FNVALL); /* Copy string to output */
9307                 p = fnval + len1 - 1;   /* Point to last character */
9308
9309                 while (p >= (char *)fnval) { /* Go backwards */
9310                     q = s;              /* Point to trim list */
9311                     p2 = *p;
9312                     if (!inpcas[cmdlvl])
9313                       if (islower(p2)) p2 = toupper(p2);
9314                     while (*q) {        /* Is this char in trim list? */
9315                         q2 = *q;
9316                         if (!inpcas[cmdlvl])
9317                           if (islower(q2)) q2 = toupper(q2);
9318                         if (p2 == q2) { /* Yes, null it out */
9319                             *p = NUL;
9320                             break;
9321                         }
9322                         q++;
9323                     }
9324                     if (!*q)            /* Trim list exhausted */
9325                       break;            /* So we're done. */
9326                     p--;                /* Else keep trimming */
9327                 }
9328             } else {                    /* Trim from left */
9329                 char * q, p2, q2;
9330                 p = bp[0];              /* Source */
9331                 while (*p) {
9332                     p2 = *p;
9333                     if (!inpcas[cmdlvl])
9334                       if (islower(p2)) p2 = toupper(p2);
9335                     q = s;
9336                     while (*q) {        /* Is this char in trim list? */
9337                         q2 = *q;
9338                         if (!inpcas[cmdlvl])
9339                           if (islower(q2)) q2 = toupper(q2);
9340                         if (p2 == q2) { /* Yes, point past it */
9341                             p++;        /* and try next source character */
9342                             break;
9343                         }
9344                         q++;            /* No, try next trim character */
9345                     }
9346                     if (!*q)            /* Trim list exhausted */
9347                       break;            /* So we're done. */
9348                 }
9349                 ckstrncpy(fnval,p,FNVALL);
9350             }
9351             p = fnval;
9352         } else p = "";
9353         goto fnend;
9354
9355       case FN_CAP:                      /* \fcapitalize(arg1) */
9356         if (argn < 1)
9357           goto fnend;
9358         s = bp[0];
9359         p = fnval;
9360         x = 0;
9361         while ((c = *s++)) {
9362             if (isalpha(c)) {
9363                 if (x == 0) {
9364                     x = 1;
9365                     if (islower(c))
9366                       c = toupper(c);
9367                 } else if (isupper(c))
9368                   c = tolower(c);
9369             }
9370             *p++ = c;
9371         }
9372         *p = NUL;
9373         p = fnval;
9374         goto fnend;
9375
9376 #ifdef COMMENT
9377       case FN_TOD:                      /* Time of day to secs since midnite */
9378         sprintf(fnval,"%ld",tod2sec(bp[0])); /* SAFE */
9379         goto fnend;
9380 #endif /* COMMENT */
9381
9382       case FN_FFN:                      /* Full pathname of file */
9383         zfnqfp(bp[0],FNVALL,p);
9384         if (!p) p = "";
9385         goto fnend;
9386
9387       case FN_CHK: {                    /* \fchecksum() */
9388           long chk = 0;
9389           p = (argn > 0) ? bp[0] : "";
9390           while (*p) chk += *p++;
9391           sprintf(fnval,"%lu",chk);     /* SAFE */
9392           p = fnval;
9393           goto fnend;
9394       }
9395
9396 #ifndef NOXFER
9397       case FN_CRC:                      /* \fcrc16() */
9398         if (argn > 0)
9399           sprintf(fnval,"%u",           /* SAFE */
9400                   chk3((CHAR *)bp[0],(int)strlen(bp[0])));
9401         else
9402           p = "0";
9403         goto fnend;
9404 #endif /* NOXFER */
9405
9406       case FN_BSN:                      /* \fbasename() */
9407         zstrip(bp[0],&p);
9408         goto fnend;
9409
9410 #ifndef NOLOCAL
9411 #ifdef OS2
9412       case FN_SCRN_CX:                  /* \fscrncurx() */
9413         p = fnval;
9414         sprintf(p,"%d",(int)VscrnGetCurPos(VTERM)->x); /* SAFE */
9415         goto fnend;
9416
9417       case FN_SCRN_CY:                  /* \fscrncury() */
9418         p = fnval;
9419         sprintf(p,"%d",(int)VscrnGetCurPos(VTERM)->y); /* SAFE */
9420         goto fnend;
9421
9422       case FN_SCRN_STR: {               /* \fscrnstr() */
9423           videoline * line = NULL;
9424           viocell * cells = NULL;
9425           int row = 0, col = 0, len = 0;
9426           /* NOTE: On Unicode systems, the screen contents are stored in */
9427           /* in Unicode.  Therefore, we should really be performing a    */
9428           /* conversion to the local character set.                      */
9429
9430           /* 6/18/2000 - added the translation to lcs */
9431
9432           if (bp[0] == NULL || bp[0][0] == '\0') {
9433               row = 0;
9434           } else {
9435               if (chknum(bp[0])) {
9436                   row = atoi(bp[0]);
9437                   if (row < 0)
9438                     row = 0;
9439               } else {
9440                   failed = 1;
9441                   if (fndiags)
9442                     ckmakmsg(fnval,FNVALL,
9443                              "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9444                   goto fnend;
9445               }
9446           }
9447           line = VscrnGetLineFromTop( VTERM, (USHORT) row );
9448           if (line != NULL) {
9449               if (bp[1] == NULL || bp[1][0] == '\0')
9450                 col = 0;
9451               else {
9452                   if (chknum(bp[0])) {
9453                       col = atoi(bp[1]);
9454                       if (col < 0 || col >= line->width)
9455                         col = 0;
9456                   } else {
9457                       failed = 1;
9458                       if (fndiags)
9459                         ckmakmsg(fnval,FNVALL,
9460                                  "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9461                       goto fnend;
9462                   }
9463               }
9464               if (bp[2] == NULL || bp[2][0] == '\0') {
9465                   len = line->width - (col+1);
9466               } else {
9467                   if (!chknum(bp[2])) {
9468                       failed = 1;
9469                       if (fndiags)
9470                         ckmakmsg(fnval,FNVALL,
9471                                  "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9472                       goto fnend;
9473                   }
9474                   len = atoi(bp[2]);
9475                   if (len < 0 || len > line->width)
9476                     len = line->width;
9477               }
9478               cells = line->cells;
9479               for (i = 0; i < len; i++) {
9480                   int pos = i + col;
9481                   if (pos < line->width) {
9482                       if (isunicode())
9483                         fnval[i] = (CHAR) utolxlat(cells[pos].c);
9484                       else
9485                         fnval[i] = (CHAR) (cells[pos].c & 0xFF);
9486                       if (fnval[i] == 0)
9487                         fnval[i] = SP;
9488                   } else
9489                     fnval[i] = SP;
9490               }
9491               fnval[i] = '\0';
9492           } else {
9493               fnval[0] = '\0';
9494           }
9495           p = fnval;
9496           goto fnend;
9497       }
9498 #endif /* OS2 */
9499 #endif /* NOLOCAL */
9500
9501 #ifndef NOPUSH
9502       case FN_RAW:                      /* \frawcommand() */
9503       case FN_CMD: {                    /* \fcommand() */
9504           int x, c, n = FNVALL;
9505           x = 0;                        /* Completion flag */
9506 /*
9507   ZIFILE can be safely used because we can't possibly be transferring a file
9508   while executing this function.
9509 */
9510           if (!nopush && zxcmd(ZIFILE,bp[0]) > 0) { /* Open the command */
9511               while (n-- > -1) {        /* Read from it */
9512                   if ((c = zminchar()) < 0) {
9513                       x = 1;             /* EOF - set completion flag */
9514                       if (cx == FN_CMD) { /* If not "rawcommand" */
9515                           p--;           /* remove trailing newlines */
9516                           while (*p == CR || *p == LF)
9517                             p--;
9518                           p++;
9519                       }
9520                       *p = NUL;         /* Terminate the string */
9521                       break;
9522                   } else                /* Command still running */
9523                     *p++ = c;           /* Copy the bytes */
9524               }
9525               zclose(ZIFILE);           /* Close the command */
9526           }
9527           /* Return null string if command's output was too long. */
9528           p = fnval;
9529           if (!x) {
9530               failed = 1;
9531               if (fndiags)
9532                 ckmakmsg(fnval,FNVALL,
9533                          "<ERROR:RESULT_TOO_LONG:\\f",fn,"()>",NULL);
9534           }
9535           goto fnend;
9536       }
9537 #endif /* NOPUSH */
9538     } /* Break up big switch... */
9539
9540     switch (y) {
9541       case FN_STX:                      /* \fstripx(string,c) */
9542         if (!(s = bp[0]))               /* Make sure there is a string */
9543           goto fnend;
9544         c = '.';                        /* Character to strip from */
9545         if (argn > 1) if (*bp[1]) c = *bp[1];
9546         n = ckstrncpy(fnval,bp[0],FNVALL);
9547         while (--n >= 0) {
9548             if (fnval[n] == c) {
9549                 fnval[n] = NUL;
9550                 break;
9551             }
9552         }
9553         p = fnval;
9554         goto fnend;
9555
9556       case FN_STL:                      /* \flop(string,c) */
9557       case FN_LOPX: {                   /* \flopx(string,c) */
9558           int n = 1;
9559           if (!(s = bp[0]))             /* Make sure there is a string */
9560             goto fnend;
9561           c = '.';                      /* Character to strip to */
9562           if (argn > 1) if (*bp[1]) c = *bp[1];
9563           if (argn > 2) if (*bp[2]) {
9564 #ifndef NOFLOAT
9565               n = 0;
9566               if (isfloat(bp[2],0)) {
9567                   n = (int)floatval;
9568                   if (n < 0) n = 0;
9569               } else
9570 #endif  /* NOFLOAT */
9571                 n = atoi(bp[2]);
9572           }
9573           x = 0;
9574           if (cx == FN_LOPX) {          /* Lopx (from right) */
9575               if (n == 0)
9576                 goto fnend;
9577               s += strlen(s) - 1;       /* We already know it's > 0 */
9578               while (s-- >= bp[0]) {
9579                   if (*s == c) {
9580                       n--;
9581                       if (n == 0) {
9582                           s++;
9583                           x = 1;
9584                           break;
9585                       }
9586                   }
9587               }
9588               if (!x) s = "";
9589           } else {                      /* Lop (from left) */
9590               if (n == 0) {
9591                   p = bp[0];
9592                   goto fnend;
9593               }
9594               while (*s++) {
9595                   if (*(s-1) == c) {
9596                       if (--n == 0) {
9597                           x = 1;
9598                           break;
9599                       }
9600                   }
9601               }
9602               if (!x) s = bp[0];
9603           }
9604           ckstrncpy(fnval,s,FNVALL);
9605           p = fnval;
9606           goto fnend;
9607       }
9608       case FN_STN:                      /* \fstripn(string,n) */
9609         if (argn < 1)                   /* Remove n chars from right */
9610           goto fnend;
9611         val1 = "0";
9612         if (argn > 1)
9613           if (*(bp[1]))
9614             val1 = evalx(bp[1]);
9615         if (!chknum(val1)) {
9616             failed = 1;
9617             evalerr(fn);
9618             goto fnend;
9619         }
9620         n = atoi(val1);
9621         if (n < 0) n = 0;
9622         k = (int)strlen(s = bp[0]) - n;
9623         if (k < 0) k = 0;
9624         p = fnval;
9625         while (k-- > 0)
9626           *p++ = *s++;
9627         *p = NUL;
9628         p = fnval;
9629         goto fnend;
9630
9631       case FN_STB: {                    /* \fstripb(string,c) */
9632           char c2 = NUL;
9633           int i, k = 0;
9634           char * gr_opn = "\"{'([<";    /* Group open brackets */
9635           char * gr_cls = "\"}')]>";    /* Group close brackets */
9636
9637           p = fnval;
9638           *p = NUL;
9639           if (!(s = bp[0]))             /* Make sure there is a string */
9640             goto fnend;
9641           if ((x = strlen(s)) < 1)
9642             goto fnend;
9643           c = NUL;                      /* Brace/bracket kind */
9644           if (argn > 1) {
9645               if (*bp[1]) {
9646                   if (chknum(bp[1])) {
9647                       k = atoi(bp[1]);
9648                       if (k < 0) k = 63;
9649                       for (i = 0; i < 6; i++) {
9650                           if (k & (1<<i)) {
9651                               if (s[0] == gr_opn[i] && s[x-1] == gr_cls[i]) {
9652                                   ckstrncpy(fnval,s+1,FNVALL);
9653                                   fnval[x-2] = NUL;
9654                                   goto fnend;
9655                               }
9656                           }
9657                       }
9658                       ckstrncpy(fnval,s,FNVALL); /* No match */
9659                       goto fnend;
9660                   }
9661               }
9662           }
9663           c = !bp[1] ? 0 : *bp[1];
9664           if (!c) c = s[0];
9665           if (argn > 2) if (*bp[2]) c2 = *bp[2];
9666           if (*s == c) {
9667               if (!c2) {
9668                   switch (c) {
9669                     case '(': c2 = ')'; break;
9670                     case '[': c2 = ']'; break;
9671                     case '{': c2 = '}'; break;
9672                     case '<': c2 = '>'; break;
9673                     case '"': c2 = '"'; break;
9674                     case 39:  c2 = 39;  break;
9675                     case 96:  c2 = 39;  break;
9676                     default:
9677                       if (argn == 2) {
9678                           c2 = c;
9679                       } else {
9680                           strncpy(fnval,s,x); /* Leave it like this */
9681                           fnval[x] = NUL;
9682                           goto fnend;
9683                       }
9684                   }
9685               }
9686               if (s[x-1] == c2) {
9687                   strncpy(fnval,s+1,x-2); /* Leave it like this */
9688                   fnval[x-2] = NUL;
9689                   goto fnend;
9690               }
9691           }
9692           strncpy(fnval,s,x);
9693           fnval[x] = NUL;
9694           goto fnend;
9695       }
9696
9697       case FN_2HEX:                     /* Number to hex */
9698       case FN_2OCT:                     /* Number to octal */
9699         val1 = evalx(bp[0]);
9700         if (!*val1) {
9701             failed = 1;
9702             evalerr(fn);
9703             goto fnend;
9704         }
9705         sprintf(fnval, cx == FN_2HEX ? "%lx" : "%lo", atol(val1)); /* SAFE */
9706         if (cx == FN_2HEX && (int)(strlen(fnval)&1))
9707           sprintf(fnval,"0%lx",atol(val1)); /* SAFE */
9708         p = fnval;
9709         goto fnend;
9710
9711       case FN_DNAM: {                   /* Directory part of file name */
9712           char *s;
9713           zfnqfp(bp[0],FNVALL,p);       /* Get full name */
9714           if (!isdir(p)) {              /* Is it already a directory? */
9715               zstrip(p,&s);             /* No get basename */
9716               if (*s) {
9717                   x = ckindex(s,p,0,0,0); /* Pos of latter in former */
9718                   if (x > 0) p[x-1] = NUL;
9719               }
9720           }
9721           if (!p) p = "";
9722           goto fnend;
9723       }
9724
9725 #ifndef NORANDOM
9726       case FN_RAND:                     /* Random number */
9727 #ifdef CK_SSL
9728         if (RAND_bytes((unsigned char *)&k,sizeof(k)) < 0)
9729 #endif /* CK_SSL */
9730           k = rand();
9731         x = 0;
9732         if (argn > 0) {
9733             if (!chknum(bp[0])) {
9734                 failed = 1;
9735                 if (fndiags)
9736                   ckmakmsg(fnval,FNVALL,
9737                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9738                 goto fnend;
9739             }
9740             x = atoi(bp[0]);
9741         }
9742 #ifdef COMMENT
9743         sprintf(fnval,"%d", (x > 0 && k > 0) || (x < 0 && k < 0) ? k % x : 
9744                 (x == 0 ? 0 : (0 - (k % (-x)))));
9745 #else
9746         debug(F111,"rand",ckitoa(x),k);
9747 #ifdef SUNOS4
9748 /* This is really strange but on SunOS, if we are requesting random numbers */
9749 /* between 0 and 4 or less, they always come out in sequence: 0 1 2 3 0 1 2 */
9750 /* Shifting the result of rand() in this case gives a more random result.   */
9751         if (x < 5)
9752           k = k >> 5;
9753 #endif /* SUNOS4 */
9754         if ((x > 0 && k > 0) || (x < 0 && k < 0))
9755           x = k % x;
9756         else if (x == 0)
9757           x = 0;
9758         else
9759           x = 0 - (k % (-x));
9760         debug(F101,"rand x","",x);
9761         sprintf(fnval,"%d", x);         /* SAFE */
9762 #endif /* COMMENT */
9763         p = fnval;
9764         goto fnend;
9765 #endif /* NORANDOM */
9766     } /* Break up big switch... */
9767
9768     switch (y) {
9769       case FN_SPLIT:                    /* \fsplit(s1,a,s2,s3,mask) */
9770       case FN_WORD: {                   /* \fword(s1,n,s2,s3,mask) */
9771           int wordnum = 0;
9772           int splitting = 0;
9773           int x;
9774           int array = 0;
9775           int grouping = 0;
9776           int nocollapse = 0;
9777           char * sep = "";
9778           char * notsep = "";
9779           char * bp0 = NULL;
9780           char * bp1 = NULL;
9781           char   abuf[16];
9782           struct stringarray * q = NULL;
9783
9784           splitting = (cx == FN_SPLIT); /* Our job */
9785           debug(F101,"FN_SPLIT splitting","",splitting);
9786
9787           fnval[0] = splitting ? '0' : NUL; /* Initial return value */
9788           fnval[1] = NUL;
9789           p = fnval;
9790           bp0 = bp[0];                  /* Source string */
9791           if (!bp0) bp0 = "";
9792           debug(F111,"fsplit bp[0]",bp0,argn);
9793           if (argn < 1 || !*bp0)        /* If none, return default value */
9794             goto fnend;
9795
9796           bp1 = bp[1];                  /* Function-dependent arg */
9797           if (!bp1) bp1 = "";           /* (array or number) */
9798           debug(F110,"fsplit bp[1]",bp1,0);
9799           if (bp[5]) {
9800               if (!chknum(bp[5])) {
9801                   failed = 1;
9802                   ckmakmsg(fnval,FNVALL,
9803                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9804                   goto fnend;
9805               }
9806               x = atoi(bp[5]);
9807               nocollapse = x;
9808           }
9809           if (!splitting) {             /* \fword(): n = desired word number */
9810               val1 = "1";               /* Default is first word */
9811               if (argn > 1)             /* Word number supplied */
9812                 if (*bp1)
9813                   val1 = evalx(bp1);
9814               if (!chknum(val1)) {
9815                   failed = 1;
9816                   ckmakmsg(fnval,FNVALL,
9817                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9818                   goto fnend;
9819               }
9820               n = atoi(val1);
9821           } else if (argn > 1 && *bp1) { /* \fsplit(): n = word count */
9822               ckstrncpy(abuf,bp1,16);   /* Get array reference */
9823               debug(F110,"fsplit abuf 1",abuf,0);
9824               failed = 1;               /* Assume it's bad */
9825               if (fndiags)              /* Default is this error message */
9826                 ckmakmsg(fnval,FNVALL,
9827                          "<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
9828               if (abuf[0] != '&')       /* "Address" of array */
9829                 goto fnend;             /* It's bad */
9830               if (abuf[2]) {            /* Check for brackets */
9831                   if (abuf[2] != '[' || abuf[3] != ']') {
9832                       goto fnend;       /* Bad */
9833                   }
9834               }
9835               debug(F110,"fsplit abuf 2",abuf,0);
9836               if (abuf[1] > 64 && abuf[1] < 91) /* Convert upper to lower */
9837                 abuf[1] += 32;
9838               if (abuf[1] < 97 || abuf[1] > 122) { /* Check for a-z */
9839                   goto fnend;
9840               }
9841               debug(F110,"fsplit abuf 3",abuf,0);
9842               array = 1;
9843               fnval[0] = NUL;           /* No error, erase message */
9844               failed = 0;               /* Unset failure flag */
9845               n = 0;                    /* Initialize word counter */
9846           }
9847           if (argn > 2)                 /* Have break set? */
9848             sep = bp[2];
9849           debug(F111,"fsplit sep",sep,argn);
9850           if (argn > 3)                 /* Have include set? */
9851             notsep = bp[3];
9852           debug(F111,"fsplit notsep",notsep,argn);
9853           if (argn > 4) {               /* Have grouping set? */
9854               char * bp4 = bp[4];
9855               debug(F111,"fsplit bp4",bp4,argn);
9856               if (!bp4) bp4 = "0";
9857               if (!*bp4) bp4 = "0";
9858               if (chknum(bp4)) {
9859                   grouping = atoi(bp4);
9860                   if (grouping == -1)
9861                     grouping = 127;
9862               } else {
9863                   failed = 1;
9864                   if (fndiags)
9865                     ckmakmsg(fnval,FNVALL,
9866                              "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9867                   goto fnend;
9868               }
9869           }
9870           /* Args parsed, now do the work */
9871
9872           debug(F111,"fsplit bp0",bp0,n);
9873           q = cksplit(splitting,n,bp0,sep,notsep,grouping,0,nocollapse);
9874
9875           wordnum = q ? q->a_size : -1; /* Check result */
9876           if (wordnum < 0) {
9877               failed = 1;               /* Failure */
9878               if (fndiags)
9879                 ckmakmsg(fnval,FNVALL,
9880                          (wordnum == -1) ?
9881                          "<ERROR:MALLOC_FAILURE:\\f" :
9882                          "<ERROR:TOO_MANY_WORDS:\\f",
9883                          fn,
9884                          "()>",
9885                          NULL
9886                          );
9887               goto fnend;
9888           }
9889           if (splitting) {              /* \fsplit() result */
9890               ckstrncpy(fnval,ckitoa(wordnum),FNVALL);
9891               if (array) {              /* Array was not declared. */
9892                   int i;
9893                   if ((x = dclarray(abuf[1],wordnum)) < 0) { /* Declare it. */
9894                       failed = 1;
9895                       if (fndiags)
9896                         ckmakmsg(fnval,FNVALL,
9897                                  "<ERROR:MALLOC_FAILURE:\\f",fn,"()>",NULL);
9898                       goto fnend;
9899                   }
9900                   for (i = 1; i <= wordnum; i++) { /* Copy results */
9901                       makestr(&(a_ptr[x][i]),q->a_head[i]);
9902                   }
9903                   a_ptr[x][0] = NULL;   /* Array is 1-based */
9904                   makestr(&(a_ptr[x][0]),fnval); /* Element = size */
9905               }
9906           } else {                      /* \fword() result */
9907               char * s;
9908               s = q->a_head[1];
9909               if (!s) s = "";
9910               ckstrncpy(fnval,s,FNVALL);
9911           }
9912           goto fnend;                   /* Done */
9913       }
9914
9915     } /* Break up big switch... */
9916
9917     switch (y) {
9918
9919 #ifdef CK_KERBEROS
9920       case FN_KRB_TK:                   /* Kerberos tickets */
9921       case FN_KRB_NX:                   /* Kerberos next ticket */
9922       case FN_KRB_IV:                   /* Kerberos ticket is valid */
9923       case FN_KRB_FG:                   /* Kerberos Ticket flags */
9924       case FN_KRB_TT: {                 /* Kerberos ticket time */
9925           int kv = 0;                   /* Kerberos version */
9926           int n = 0;
9927           char * s = NULL;
9928           if (rdigits(bp[0])) {
9929               kv = atoi(bp[0]);
9930           } else {
9931               failed = 1;
9932               if (fndiags)
9933                 ckmakmsg(fnval,FNVALL,
9934                          "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9935               goto fnend;
9936           }
9937           if (kv != 4 && kv != 5) {
9938               failed = 1;
9939               if (fndiags)
9940                 ckmakmsg(fnval,FNVALL,
9941                          "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
9942               goto fnend;
9943           }
9944           if ((cx == FN_KRB_IV || cx == FN_KRB_TT || cx == FN_KRB_FG) &&
9945                argn < 2) {
9946               failed = 1;
9947               if (fndiags)
9948                 ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
9949               goto fnend;
9950           }
9951           switch (y) {
9952             case FN_KRB_TK:             /* Number of Kerberos tickets */
9953 #ifdef CK_AUTHENTICATION
9954               switch (kv) {
9955                 case 4:
9956                   n = ck_krb4_get_tkts();
9957                   sprintf(fnval, "%d", (n >= 0) ? n : 0); /* SAFE */
9958                   goto fnend;
9959                 case 5: {
9960                     extern char * krb5_d_cc;
9961                     n = ck_krb5_get_tkts(krb5_d_cc);
9962                     sprintf(fnval, "%d", (n >= 0) ? n : 0); /* SAFE */
9963                     goto fnend;
9964                 }
9965               }
9966 #else
9967               sprintf(fnval,"%d",0);    /* SAFE */
9968 #endif /* CK_AUTHENTICATION */
9969               goto fnend;
9970
9971             case FN_KRB_NX:             /* Kerberos next ticket */
9972 #ifdef CK_AUTHENTICATION
9973               switch (kv) {
9974                 case 4:
9975                   s = ck_krb4_get_next_tkt();
9976                   ckstrncpy(fnval, s ? s : "",FNVALL);
9977                   goto fnend;
9978                 case 5:
9979                   s = ck_krb5_get_next_tkt();
9980                   ckstrncpy(fnval, s ? s : "",FNVALL);
9981                   goto fnend;
9982               }
9983 #else
9984               sprintf(fnval,"k%d next-ticket-string",kv); /* SAFE */
9985 #endif /* CK_AUTHENTICATION */
9986               goto fnend;
9987
9988             case FN_KRB_IV:             /* Kerberos ticket is valid */
9989 #ifdef CK_AUTHENTICATION
9990               /* Return 1 if valid, 0 if not */
9991               switch (kv) {
9992                 case 4:
9993                   n = ck_krb4_tkt_isvalid(bp[1]);
9994                   sprintf(fnval, "%d", n > 0 ? 1 : 0); /* SAVE */
9995                   goto fnend;
9996                 case 5: {
9997                     extern char * krb5_d_cc;
9998                     n = ck_krb5_tkt_isvalid(krb5_d_cc,bp[1]);
9999                     sprintf(fnval,"%d", n > 0 ? 1 : 0); /* SAFE */
10000                     goto fnend;
10001                 }
10002               }
10003 #else
10004               sprintf(fnval,"%d",0);    /* SAFE */
10005 #endif /* CK_AUTHENTICATION */
10006               goto fnend;
10007
10008             case FN_KRB_TT:             /* Kerberos ticket time */
10009 #ifdef CK_AUTHENTICATION
10010               switch (kv) {
10011                 case 4:
10012                   n = ck_krb4_tkt_time(bp[1]);
10013                   sprintf(fnval,"%d", n >= 0 ? n : 0); /* SAFE */
10014                   goto fnend;
10015                 case 5: {
10016                     extern char * krb5_d_cc;
10017                     n = ck_krb5_tkt_time(krb5_d_cc,bp[1]);
10018                     sprintf(fnval,"%d", n >= 0 ? n : 0); /* SAFE */
10019                     goto fnend;
10020                 }
10021               }
10022 #else
10023               ckstrncpy(fnval,"600",FNVALL); /* Some time */
10024 #endif /* CK_AUTHENTICATION */
10025               goto fnend;
10026
10027             case FN_KRB_FG:             /* Kerberos ticket flags */
10028 #ifdef CK_AUTHENTICATION
10029               switch (kv) {
10030                 case 4:
10031                   fnval[0] = '\0';
10032                   goto fnend;
10033                 case 5: {
10034                     extern char * krb5_d_cc;
10035                     ckstrncpy(fnval,ck_krb5_tkt_flags(krb5_d_cc,bp[1]),FNVALL);
10036                     goto fnend;
10037                 }
10038               }
10039 #else
10040               fnval[0] = '\0';
10041 #endif /* CK_AUTHENTICATION */
10042               goto fnend;
10043           }
10044           p = fnval;
10045           goto fnend;
10046       }
10047 #endif /* CK_KERBEROS */
10048
10049 #ifdef FN_ERRMSG
10050       case FN_ERRMSG:
10051         if (rdigits(bp[0])) {
10052             k = atoi(bp[0]);
10053         } else {
10054             failed = 1;
10055             if (fndiags)
10056              ckmakmsg(fnval,FNVALL,"<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10057             goto fnend;
10058         }
10059 #ifdef VMS
10060         ckstrncpy(fnval,ckvmserrstr(k),FNVALL);
10061 #else
10062         x = errno;
10063         errno = k;
10064         ckstrncpy(fnval,ck_errstr(),FNVALL);
10065         errno = x;
10066 #endif /* VMS */
10067         p = fnval;
10068         goto fnend;
10069 #endif /* FN_ERRMSG */
10070
10071       case FN_DIM: {
10072           int max;
10073           char abuf[16], *s;
10074           fnval[0] = NUL;               /* Initial return value */
10075           ckstrncpy(abuf,bp[0],16);     /* Get array reference */
10076           s = abuf;
10077           if (*s == CMDQ) s++;
10078           failed = 1;                   /* Assume it's bad */
10079           p = fnval;                    /* Point to result */
10080           if (fndiags)                  /* Default is this error message */
10081             ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
10082           if (s[0] != '&') {            /* "Address" of array */
10083               goto fnend;
10084           }
10085           if (s[2]) {
10086               if (s[2] != '[' || s[3] != ']') {
10087                   goto fnend;
10088               }
10089           }
10090           if (s[1] >= 64 && s[1] < 91)  /* Convert upper to lower */
10091             s[1] += 32;
10092           if (s[1] < 95 || s[1] > 122) { /* Check for a-z */
10093               goto fnend;                /* Bad */
10094           }
10095           if ((max = chkarray(s[1],1)) < 1) /* (second arg was 1) */
10096             max = 0;
10097           failed = 0;                   /* Unset failure flag */
10098           sprintf(fnval,"%d",max);      /* SAFE */
10099           goto fnend;
10100       }
10101
10102     } /* Break up big switch... */
10103
10104     switch (y) {
10105       case FN_JDATE:
10106         if (argn < 1)                   /* Check number of args */
10107           p = ckdate();                 /* None, get today's date-time */
10108         else                            /* Some */
10109           p = bp[0];                    /* Use first */
10110         p = ckcvtdate(p,0);             /* Convert to standard form */
10111         ckstrncpy(fnval,zjdate(p),FNVALL); /* Convert to Julian */
10112         p = fnval;                      /* Point to result */
10113         failed = 0;
10114         if (*p == '-') {
10115             failed = 1;
10116             if (fndiags)                /* Default is this error message */
10117               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_DATE:\\f",fn,"()>",NULL);
10118         }
10119         goto fnend;
10120
10121       case FN_DATEJ:
10122         ckstrncpy(fnval,jzdate(bp[0]),FNVALL); /* Convert to yyyy<dayofyear> */
10123         p = fnval;                      /* Point to result */
10124         failed = 0;
10125         if (*p == '-') {
10126             failed = 1;
10127             if (fndiags)                /* Default is this error message */
10128               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_DATE:\\f",fn,"()>",NULL);
10129         }
10130         goto fnend;
10131
10132       case FN_DTIM:                     /* \fcvtdate() */
10133       case FN_TIME:                     /* Free-format time to hh:mm:ss */
10134       case FN_NTIM:                     /* Time to sec since midnight */
10135         s = (argn > 0) ? bp[0] : "";
10136         if (!s) s = "";
10137         if (!*s)
10138           p = ckdate();                 /* None, get today's date */
10139         else                            /* Some */
10140           p = bp[0];                    /* Use first */
10141         {
10142             char * s;
10143             s = p;
10144             while (*s) {
10145                 if (*s < 32) {
10146                     *s = NUL;
10147                     break;
10148                 }
10149                 s++;
10150             }
10151             /* do { if (*s < '!') *s = NUL; break; } while (*s++); */
10152         }
10153         p = ckcvtdate(p,2);             /* Convert to standard form */
10154         if (*p == '<') {
10155             failed = 1;
10156             if (fndiags)                /* Default is this error message */
10157               ckmakmsg(fnval,FNVALL,
10158                        "<ERROR:ARG_BAD_DATE_OR_TIME:\\f",fn,"()>",NULL);
10159             p = fnval;
10160             goto fnend;
10161         }
10162         if (argn > 1) {                 /* Format code */
10163             s = bp[1];
10164             if (!s) s = "";
10165             if (!*s) s = "0";
10166             if (!chknum(s)) {
10167                 failed = 1;
10168                 if (fndiags)
10169                   ckmakmsg(fnval,FNVALL,
10170                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10171                 p = fnval;
10172                 goto fnend;
10173             }
10174             x = atoi(s);
10175             /* if (x) */ p = shuffledate(p,x);
10176         }
10177         if (cx == FN_TIME) {
10178             p += 9;
10179         } else if (cx == FN_NTIM) {
10180             long sec = 0L;
10181             p[11] = NUL;
10182             p[14] = NUL;
10183             sec = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15);
10184             sprintf(fnval,"%ld",sec);   /* SAFE */
10185             p = fnval;
10186         }
10187         goto fnend;
10188
10189       case FN_MJD:                      /* Modified Julian Date */
10190         if (argn < 1)                   /* Check number of args */
10191           p = zzndate();                /* None, get today's date-time */
10192         else                            /* Some */
10193           p = bp[0];                    /* Use first */
10194         p = ckcvtdate(p,0);             /* Convert to standard form */
10195         if (*p == '-') {
10196             failed = 1;
10197             if (fndiags)                /* Default is this error message */
10198               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_DATE:\\f",fn,"()>",NULL);
10199             goto fnend;
10200         }
10201         /* Convert to modified Julian date */
10202         sprintf(fnval,"%ld",mjd(p));    /* SAFE */
10203         p = fnval;                      /* Point to result */
10204         goto fnend;
10205
10206       case FN_MJD2: {
10207           long k = 0L;
10208           int n = 0;
10209           p = evalx(bp[0]);
10210           if (*p == '-') {
10211               p++;
10212               n = 1;
10213           }
10214           if (!rdigits(p)) {
10215               failed = 1;
10216               evalerr(fn);
10217               p = fnval;
10218               goto fnend;
10219           } else {
10220               k = atol(p);
10221               if (n) k = -k;
10222           }
10223           ckstrncpy(fnval,mjd2date(k),FNVALL); /* Convert to Date */
10224           p = fnval;                    /* Point to result */
10225           failed = 0;
10226           goto fnend;
10227       }
10228
10229 #ifndef NODIAL
10230       case FN_PNCVT: {                  /* Convert phone number */
10231           extern char * pncvt();
10232           failed = 0;
10233           p = pncvt(bp[0]);
10234           if (!p) p = "";
10235           if (!*p) {
10236             failed = 1;
10237             if (fndiags)                /* Default is this error message */
10238               ckmakmsg(fnval,FNVALL,
10239                        "<ERROR:ARG_BAD_PHONENUM:\\f",fn,"()>",NULL);
10240         }
10241         goto fnend;
10242       }
10243 #endif /* NODIAL */
10244
10245       case FN_DAY:
10246       case FN_NDAY:
10247         if (argn < 1)                   /* Check number of args */
10248           p = zzndate();                /* None, get today's date-time */
10249         else                            /* Some */
10250           p = bp[0];                    /* Use first */
10251         p = ckcvtdate(p,0);             /* Convert to standard form */
10252         if (*p == '-') {
10253             failed = 1;
10254             if (fndiags)                /* Default is this error message */
10255               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_DATE:\\f",fn,"()>",NULL);
10256             goto fnend;
10257         }
10258         failed = 0;
10259         z = mjd(p);                     /* Convert to modified Julian date */
10260         z = z % 7L;
10261         if (z < 0) {
10262             z = 0 - z;
10263             k = 6 - ((int)z + 3) % 7;
10264         } else {
10265             k = ((int)z + 3) % 7;       /* Day of week */
10266         }
10267         p = fnval;                      /* Point to result */
10268         if (cx == FN_NDAY)
10269           sprintf(fnval,"%d",k);        /* SAFE */
10270         else
10271           ckstrncpy(fnval,wkdays[k],FNVALL);
10272         goto fnend;
10273
10274       case FN_N2TIM: {                  /* Sec since midnight to hh:mm:ss */
10275           long k = 0L;
10276           int n = 0, hh, mm, ss;
10277           char * s = bp[0];
10278           if (argn < 1)                 /* If no arg substitute 0 */
10279             s = "0";
10280           p = evalx(s);                 /* Evaluate expression silently */
10281           if (*p == '-') {              /* Check result for minus sign */
10282               p++;
10283               n = 1;
10284           }
10285           if (!rdigits(p)) { /* Check for numeric */
10286               failed = 1;
10287               ckmakmsg(fnval,FNVALL,
10288                        "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10289               p = fnval;
10290               goto fnend;
10291           } else {
10292               k = atol(p);
10293               if (n) k = -k;
10294           }
10295           if (k < 0) {                  /* Check for negative */
10296               failed = 1;
10297               if (fndiags)
10298                 ckmakmsg(fnval,FNVALL,
10299                          "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
10300               p = fnval;
10301               goto fnend;
10302           }
10303           hh = k / 3600L;               /* Have positive number */
10304           mm = (k % 3600L) / 60L;       /* break it down... */
10305           ss = ((k % 3600L) % 60L);
10306
10307           sprintf(fnval,"%02d:%02d:%02d",hh,mm,ss); /* SAFE */
10308           p = fnval;
10309           failed = 0;
10310           goto fnend;
10311       }
10312
10313       case FN_PERM: {                   /* File permissions */
10314           p = fnval;
10315           z = zchki(bp[0]);
10316           if (z < 0) {
10317               failed = 1;
10318               if (fndiags) {
10319                   if (z == -1)
10320                     ckmakmsg(fnval,FNVALL,
10321                              "<ERROR:FILE_NOT_FOUND:\\f",fn,"()>",NULL);
10322                   else if (z == -2)
10323                     ckmakmsg(fnval,FNVALL,
10324                              "<ERROR:FILE_NOT_READABLE:\\f",fn,"()>",NULL);
10325                   else if (z == -3)
10326                     ckmakmsg(fnval,FNVALL,
10327                              "<ERROR:FILE_NOT_ACCESSIBLE:\\f",fn,"()>",NULL);
10328                   else
10329                     ckmakmsg(fnval,FNVALL,
10330                              "<ERROR:FILE_ERROR:\\f",fn,"()>",NULL);
10331               }
10332               goto fnend;
10333           }
10334 #ifdef CK_PERMS
10335           ckstrncpy(fnval,ziperm(bp[0]),FNVALL);
10336 #else
10337           ckstrncpy(fnval,"(unknown)",FNVALL);
10338 #endif /* CK_PERMS */
10339           goto fnend;
10340       }
10341       case FN_TLOOK:                    /* tablelook() */
10342       case FN_ALOOK: {                  /* arraylook() */
10343           int i, x, hi, lo, max, cmdlen;
10344           char abuf[16], *s, *pat;
10345           char kwbuf[256];
10346           char delim = ':';
10347           failed = 1;                   /* Assume failure */
10348           ckstrncpy(fnval,"-1",FNVALL);
10349           pat = bp[0];                  /* Point to search pattern */
10350           if (!pat) pat = "";           /* Watch out for NULL pointer */
10351           cmdlen = strlen(pat);         /* Get pattern length */
10352           if (argn < 2 /* || cmdlen < 1 */ ) { /* Need two args */
10353               if (fndiags)
10354                 ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
10355               goto fnend;
10356           }
10357           ckstrncpy(abuf,bp[1],16);     /* Get array reference */
10358           if (argn > 2)
10359             delim = *(bp[2]);
10360           s = abuf;
10361           if ((x = arraybounds(s,&lo,&hi)) < 0) { /* Get index and bounds */
10362               if (fndiags)
10363                ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
10364               goto fnend;
10365           }
10366           p = fnval;                    /* Point to result */
10367           max = a_dim[x];               /* Size of array */
10368           if (lo < 0) lo = 0;           /* Use given range if any */
10369           if (lo > max) lo = max;
10370           if (hi < 0) hi = max;
10371           if (hi > max) hi = max;
10372           failed = 0;                   /* Unset failure flag */
10373           if (max < 1)
10374             goto fnend;
10375           kwbuf[255] = NUL;
10376           for (i = lo; i <= hi; i++) {
10377               if (!a_ptr[x][i])
10378                 continue;
10379               if (cx == FN_ALOOK) {
10380                   if (ckmatch(pat,a_ptr[x][i],inpcas[cmdlvl],1+4)) {
10381                       sprintf(fnval,"%d",i); /* SAFE */
10382                       goto fnend;
10383                   }
10384               } else if (cx == FN_TLOOK) {
10385                   char * aa;
10386                   int j = 0, v = 0, len;
10387                   if (i == hi)
10388                     break;
10389                   aa = a_ptr[x][i];     /* Point to this array element */
10390                   if (!aa) aa = "";
10391                   while (j < 254 && *aa) { /* Isolate keyword */
10392                       if (*aa == delim)
10393                         break;
10394                       kwbuf[j++] = *aa++;
10395                   }
10396                   kwbuf[j] = NUL;
10397                   len = j;
10398                   v = 0;
10399                   if ((len == cmdlen && !ckstrcmp(kwbuf,pat,len,0)) ||
10400                       ((v = !ckstrcmp(kwbuf,pat,cmdlen,0)) &&
10401                        ckstrcmp(a_ptr[x][i+1],pat,cmdlen,0))) {
10402                       sprintf(fnval,"%d",i); /* SAFE */
10403                       goto fnend;
10404                   }
10405                   if (v) {              /* Ambiguous */
10406                       ckstrncpy(fnval,"-2",FNVALL);
10407                       goto fnend;
10408                   }
10409               }
10410           }
10411           if (cx == FN_TLOOK) {         /* tablelook() last element */
10412               ckstrncpy(fnval,"-1",FNVALL);
10413               if (!ckstrcmp(a_ptr[x][hi],pat,cmdlen,0))
10414                 sprintf(fnval,"%d",hi); /* SAFE */
10415           }
10416           goto fnend;
10417       }
10418       case FN_TOB64:                    /* Base-64 conversion */
10419       case FN_FMB64:
10420         p = fnval;
10421         *p = NUL;
10422         if (argn < 1)
10423           goto fnend;
10424         if (cx == FN_TOB64) {
10425             x = b8tob64(bp[0],-1,fnval,FNVALL);
10426         } else {
10427             x = strlen(bp[0]);
10428             if (x % 4) {                /* length must be multiple of 4 */
10429                 failed = 1;
10430                 ckmakmsg(fnval,FNVALL,
10431                          "<ERROR:ARG_INCOMPLETE:\\f",fn,"()>",NULL);
10432                 goto fnend;
10433             }
10434             b64tob8(NULL,0,NULL,0);     /* Reset */
10435             x = b64tob8(bp[0],-1,fnval,FNVALL);
10436             b64tob8(NULL,0,NULL,0);     /* Reset again */
10437         }
10438         if (x < 0) {
10439             failed = 1;
10440             if (fndiags) {
10441                 char * m = "INTERNAL_ERROR";
10442                 switch (x) {
10443                   case -1: m = "ARG_TOO_LONG"; break;
10444                   case -2: m = "ARG_OUT_OF_RANGE"; break;
10445                 }
10446                 if (ckmakmsg(fnval,FNVALL,"<ERROR:",m,"\\f",fn) > 0)
10447                   ckstrncat(fnval,"()>",FNVALL);
10448             }
10449         }
10450         goto fnend;
10451
10452       case FN_ABS: {
10453           char * s;
10454           s = bp[0];
10455           if (*s == '-' || *s == '+')
10456             s++;
10457           if (!rdigits(s)) {
10458               if (fndiags)
10459                 ckmakmsg(fnval,FNVALL,
10460                          "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10461               goto fnend;
10462           }
10463           ckstrncpy(fnval,s,FNVALL);
10464           goto fnend;
10465       }
10466
10467       case FN_AADUMP: {
10468           char abuf[16], *s = NULL, **ap = NULL, **vp = NULL;
10469           char pattern[VNAML];
10470           int slen, i, j, k, first = -1;
10471           extern int xdelmac();
10472           p = fnval;
10473           if (argn < 2) {
10474               if (fndiags)
10475                 ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG2:\\f",fn,"()>",NULL);
10476               goto fnend;
10477           }
10478           debug(F101,"aaconvert argn","",argn);
10479           s = bp[0];
10480           slen = strlen(s);
10481
10482           /* Count elements so we can create the array */
10483
10484           ckmakmsg(pattern,VNAML,s,"<*>",NULL,NULL);
10485           for (k = 0, i = 0; i < nmac; i++) {
10486               if (ckmatch(pattern,mactab[i].kwd,0,1)) {
10487                   if (first < 0)        /* Remember location of first match */
10488                     first = i;
10489                   k++;
10490               }
10491           }
10492           debug(F101,"aaconvert matches","",k);
10493           debug(F101,"aaconvert first","",first);
10494           fnval[0] = NUL;               /* Initial return value */
10495           ckstrncpy(abuf,bp[1],16);     /* Get array reference */
10496           s = abuf;
10497           if (*s == CMDQ) s++;
10498           p = fnval;                    /* Point to result */
10499           if (fndiags)                  /* Default is this error message */
10500             ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
10501           if (s[0] != '&')              /* Address of array */
10502             goto fnend;
10503           if (s[2])
10504             if (s[2] != '[' || s[3] != ']')
10505               goto fnend;
10506           if (s[1] >= 64 && s[1] < 91)  /* Convert upper to lower */
10507             s[1] += 32;
10508           if ((x = dclarray(s[1],k)) < 0) /* Declare array to size */
10509             goto fnend;
10510           ap = a_ptr[x];                /* Point to array we just declared */
10511           /* debug(F111,"aaconvert array 1",abuf,ap); */
10512           abuf[0] = NUL;
10513           if (argn > 2) {
10514               ckstrncpy(abuf,bp[2],16); /* Get value array reference */
10515               s = abuf;
10516               if (*s == CMDQ) s++;
10517               if (s[0] != '&')          /* Address of array */
10518                 goto fnend;
10519               if (s[2])
10520                 if (s[2] != '[' || s[3] != ']')
10521                   goto fnend;
10522               if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */
10523                 s[1] += 32;
10524               if ((x = dclarray(s[1],k)) < 0)
10525                 goto fnend;
10526               vp = a_ptr[x];            /* Point to array we just declared */
10527           }
10528           /* debug(F111,"aaconvert array 2",abuf,vp); */
10529           makestr(&ap[0],ckitoa(k));
10530           if (vp) makestr(&vp[0],ckitoa(k));
10531           if (fndiags)
10532            ckmakmsg(fnval,FNVALL,"<ERROR:ASSOCIATIVE_ARRAY:\\f",fn,"()>",NULL);
10533
10534           /* Copy macro index & value to the arrays and then remove the */
10535           /* macro, so the 'first' pointer keeps indicating the next one. */
10536           /* We could combine the initial counting loop with this one but */
10537           /* then it would be harder to create the array and anyway this */
10538           /* function is plenty fast as it is. */
10539
10540           for (i = 1; i <= k; ) {
10541               if (!ckmatch(pattern,mactab[first].kwd,0,1)) {
10542                   debug(F111,"aaconvert oddball",mactab[first].kwd,first);
10543                   first++;
10544                   continue;
10545               }
10546               ckstrncpy(tmpbuf,mactab[first].kwd,TMPBUFSIZ); /* Macro name */
10547               s = tmpbuf;                       /* Make writeable copy */
10548               s += slen;                        /* Isolate "index" */
10549               j = strlen(s) - 1;
10550               if (*s != '<' || *(s+j) != '>') { /* Check syntax */
10551                   /* This shouldn't happen */
10552                   debug(F111,"aaconvert ERROR",mactab[first].kwd,first);
10553                   goto fnend;
10554               }
10555               *(s+j) = NUL;             /* Remove final '>' */
10556               debug(F111,"aaconvert",s+1,i);
10557               makestr(&(ap[i]),s+1);    /* Set first array to index */
10558               if (vp)
10559                 makestr(&(vp[i]),mactab[first].mval); /* 2nd to value */
10560               if (xdelmac(first) < 0)
10561                 goto fnend;
10562               i++;
10563           }
10564           sprintf(fnval,"%d",k);        /* SAFE */
10565           p = fnval;                    /* Return size of array */
10566           debug(F110,"aaconvert return",p,0);
10567           failed = 0;                   /* Unset failure flag */
10568           goto fnend;
10569       }
10570
10571     } /* End of switch() */
10572
10573 #ifdef FNFLOAT
10574 /*
10575   Floating-point functions.  To be included only if FNFLOAT is defined, which
10576   should happen only if CKFLOAT is also defined, and if the math library is
10577   linked in.  Even then, we might have float-vs-double confusion as well as
10578   confusion about what the final "%f" format effector is supposed to reference
10579   (32 bits, 64 bits, etc).  Expect trouble if CKFLOAT does not match the data
10580   type of math library functions or args.
10581 */
10582     if (cx == FN_FPABS ||               /* Floating-point functions */
10583         cx == FN_FPADD ||
10584         cx == FN_FPDIV ||
10585         cx == FN_FPEXP ||
10586         cx == FN_FPLOG ||
10587         cx == FN_FPLN  ||
10588         cx == FN_FPMOD ||
10589         cx == FN_FPMAX ||
10590         cx == FN_FPMIN ||
10591         cx == FN_FPMUL ||
10592         cx == FN_FPPOW ||
10593         cx == FN_FPSQR ||
10594         cx == FN_FPINT ||
10595         cx == FN_FPSUB ||
10596         cx == FN_FPROU ||
10597         cx == FN_FPSIN ||
10598         cx == FN_FPCOS ||
10599         cx == FN_FPTAN) {
10600         CKFLOAT farg[2], fpresult = 0.0;
10601         char fpbuf[64], * bp0;
10602         double dummy;
10603         /* int sign = 0; */
10604         int i, j, places = 0;
10605         int argcount = 1;
10606
10607         failed = 1;
10608         p = fnval;
10609         bp0 = bp[0];
10610         if (!bp0)
10611           bp0 = "0";
10612         else if (!*bp0)
10613           bp0 = "0";
10614         if (!isfloat(bp0,0)) {
10615             k = mxlook(mactab,bp0,nmac);
10616             bp0 = (k > -1) ? mactab[k].mval : NULL;
10617             if (bp0) {
10618                 if (!isfloat(bp0,0)) {
10619                     if (fndiags)
10620                       ckmakmsg(fnval,FNVALL,
10621                                "<ERROR:ARG_NOT_FLOAT:\\f",fn,"()>",NULL);
10622                     goto fnend;
10623                 }
10624             }
10625         }
10626         if (cx == FN_FPINT) {           /* Float to int */
10627             failed = 0;
10628             ckstrncpy(fnval,bp0,FNVALL);
10629             for (i = 0; fnval[i]; i++) {
10630                 if (fnval[i] == '.') {
10631                     fnval[i] = NUL;
10632                     break;
10633                 }
10634             }
10635             goto fnend;
10636         }
10637         switch (y) {                    /* These need 2 args */
10638           case FN_FPADD:
10639           case FN_FPDIV:
10640           case FN_FPMOD:
10641           case FN_FPMAX:
10642           case FN_FPMIN:
10643           case FN_FPMUL:
10644           case FN_FPPOW:
10645           case FN_FPSUB:
10646             argcount = 2;
10647         }
10648         /* Missing arguments are supplied as 0.0 */
10649
10650         debug(F111,fn,"argcount",argcount);
10651         for (i = 0; i < argcount; i++) { /* Get floating-point args */
10652 #ifdef DEBUG
10653             if (deblog) {
10654                 ckmakmsg(fpbuf,
10655                          64,
10656                          "bp[",
10657                          ckitoa(i),
10658                          bp[i] ? bp[i] : "(null)",
10659                          "]"
10660                          );
10661                 debug(F100,fpbuf,"",0);
10662             }
10663 #endif /* DEBUG */
10664             if (!bp[i]) {
10665                 farg[i] = 0.0;
10666             } else if (!*(bp[i])) {
10667                 farg[i] = 0.0;
10668             } else if (!isfloat(bp[i],0)) {
10669                 char * tmp;
10670                 k = mxlook(mactab,bp[i],nmac);
10671                 tmp = (k > -1) ? mactab[k].mval : NULL;
10672                 if (tmp) {
10673                     if (!isfloat(tmp,0)) {
10674                         if (fndiags)
10675                           ckmakmsg(fnval,FNVALL,
10676                                    "<ERROR:ARG_NOT_FLOAT:\\f",fn,"()>",NULL);
10677                         goto fnend;
10678                     }
10679                 }
10680             }
10681             farg[i] = floatval;
10682
10683 #ifdef DEBUG
10684             if (deblog) {
10685                 sprintf(fpbuf,"farg[%d]=%f",i,farg[i]); /* SAFE */
10686                 debug(F100,fpbuf,"",0);
10687             }
10688 #endif /* DEBUG */
10689         }
10690         if (bp[argcount]) {             /* Get decimal places */
10691             char * s;
10692             s = bp[argcount];
10693             if (!s) s = "";
10694             if (!*s) s = "0";
10695             s = evalx(s);
10696             if (!s) s = "";
10697             if (!*s) {
10698                 evalerr(fn);
10699                 goto fnend;
10700             }
10701             places = atoi(s);
10702         }
10703         errno = 0;
10704         failed = 0;
10705         switch (y) {                    /* Now do the requested function */
10706           case FN_FPABS:                /* Floating-point absolute value */
10707 #ifndef COMMENT
10708             fpresult = fabs(farg[0]);
10709 #else
10710             if (farg[0] < 0.0)
10711               fpresult = 0.0 - farg[0];
10712 #endif /* COMMENT */
10713             break;
10714           case FN_FPADD:                /* FP add */
10715             fpresult = farg[0] + farg[1];
10716             break;
10717           case FN_FPDIV:                /* FP divide */
10718           case FN_FPMOD:                /* FP modulus */
10719             if (!farg[1]) {
10720                 failed = 1;
10721                 if (fndiags)
10722                   ckmakmsg(fnval,FNVALL,
10723                            "<ERROR:DIVIDE_BY_ZERO:\\f",fn,"()>",NULL);
10724             } else
10725               fpresult = (cx == FN_FPDIV) ?
10726                 (farg[0] / farg[1]) :
10727                   fmod(farg[0],farg[1]);
10728             break;
10729           case FN_FPEXP:                /* FP e to the x */
10730             fpresult = (CKFLOAT) exp(farg[0]);
10731             break;
10732           case FN_FPLOG:                /* FP base-10 logarithm */
10733           case FN_FPLN:                 /* FP natural logarithm */
10734             if (farg[0] < 0.0) {
10735                 failed = 1;
10736                 if (fndiags)
10737                   ckmakmsg(fnval,FNVALL,
10738                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
10739             } else
10740               fpresult = (cx == FN_FPLOG) ? log10(farg[0]) : log(farg[0]);
10741             break;
10742           case FN_FPMUL:                /* FP multiply */
10743             fpresult = farg[0] * farg[1];
10744             break;
10745           case FN_FPPOW:                /* FP raise to a power */
10746             fpresult = modf(farg[1],&dummy);
10747             if ((!farg[0] && farg[1] <= 0.0) ||
10748                 (farg[0] < 0.0 && fpresult)) {
10749                 failed = 1;
10750                 if (fndiags)
10751                   ckmakmsg(fnval,FNVALL,
10752                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
10753             } else
10754               fpresult = pow(farg[0],farg[1]);
10755             break;
10756           case FN_FPSQR:                /* FP square root */
10757             if (farg[0] < 0.0) {
10758                 failed = 1;
10759                 if (fndiags)
10760                   ckmakmsg(fnval,FNVALL,
10761                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
10762             } else
10763               fpresult = sqrt(farg[0]);
10764             break;
10765           case FN_FPSUB:                /* FP subtract */
10766             fpresult = farg[0] - farg[1];
10767             break;
10768           case FN_FPROU:                /* FP round */
10769             fpresult = farg[0];
10770             break;
10771           case FN_FPSIN:                /* FP sine */
10772             fpresult = (CKFLOAT) sin(farg[0]);
10773             break;
10774           case FN_FPCOS:                /* FP cosine */
10775             fpresult = (CKFLOAT) cos(farg[0]);
10776             break;
10777           case FN_FPTAN:                /* FP tangent */
10778             fpresult = (CKFLOAT) tan(farg[0]);
10779             break;
10780           case FN_FPMAX:
10781             fpresult = (farg[0] > farg[1]) ? farg[0] : farg[1];
10782             break;
10783           case FN_FPMIN:
10784             fpresult = (farg[0] < farg[1]) ? farg[0] : farg[1];
10785             break;
10786         }
10787
10788         /* Get here with fpresult = function result */
10789
10790         if (errno) {                    /* If range or domain error */
10791             failed = 1;
10792             if (fndiags)
10793               ckmakmsg(fnval,FNVALL,
10794                        "<ERROR:FLOATING-POINT-OP:\\f",fn,"()>",NULL);
10795         }
10796         if (failed)                     /* and/or any other kind of error, */
10797           goto fnend;                   /* fail. */
10798 #ifndef COMMENT
10799         /* Call routine containing code that was formerly inline */
10800         ckstrncpy(fnval,fpformat(fpresult,places,cx == FN_FPROU),FNVALL);
10801 #else
10802         {
10803             char fbuf[16];              /* For creating printf format */
10804             if (!fp_rounding &&         /* If printf doesn't round, */
10805                 (places > 0 ||          /* round result to decimal places. */
10806                  (places == 0 && cx == FN_FPROU)))
10807               fpresult += (0.5 / pow(10.0,(CKFLOAT)places));
10808             if (places > 0) {                   /* If places specified */
10809                 /* use specified places to write given number of digits */
10810                 sprintf(fbuf,"%%0.%df",places); /* SAFE */
10811                 sprintf(fnval,fbuf,fpresult);   /* SAFE */
10812             } else {                            /* Otherwise... */
10813 #ifdef COMMENT
10814 /*
10815   Here we want to print exactly fp_digits significant digits, no matter which
10816   side of the decimal point they are on.  That is, we want want the default
10817   format to show the maximum number of non-garbage digits, AND we want the last
10818   such digit to be rounded.  Of course there is no way to do that, since the
10819   digit after the last non-garbage digit is, well, garbage.  So the following
10820   clever ruse does no good.
10821 */
10822                 int sign = 0, m = 0;
10823                 sprintf(fnval,"%f",fpresult);
10824                 if (fnval[0] == '-') sign = 1;
10825                 for (i = sign; i < FNVALL; i++) {
10826                     if (isdigit(fnval[i]))
10827                       m++;
10828                     else
10829                       break;
10830                 }
10831                 if (m > 1) {
10832                     int d = fp_digits - m;
10833                     if (d < 1) d = 1;
10834                     sprintf(fbuf,"%%%d.%df",fp_digits+sign+1,d);
10835                 } else {
10836                     sprintf(fbuf,"%%0.%df",fp_digits);
10837                 }
10838                 sprintf(fnval,fbuf,fpresult);
10839 #else
10840                 /* Go for max precision */
10841                 sprintf(fbuf,"%%0.%df",fp_digits); /* SAFE */
10842                 sprintf(fnval,fbuf,fpresult); /* SAFE */
10843
10844 #endif /* COMMENT */
10845             }
10846             if (fnval[0] == '-') sign = 1;
10847         }
10848         debug(F111,"fpresult 1",fnval,errno); /* Check for over/underflow */
10849         for (i = sign; fnval[i]; i++) { /* Give requested decimal places */
10850             if (fnval[i] == '.')        /* First find the decimal point */
10851               break;
10852             else if (i > fp_digits + sign - 1) /* replacing garbage */
10853               fnval[i] = '0';           /* digits with 0... */
10854         }
10855         if (fnval[i] == '.') {          /* Have decimal point */
10856             int gotend = 0;
10857             /* d < 0 so truncate fraction */
10858             if (places < 0 || (places == 0 && cx == FN_FPROU)) {
10859                 fnval[i] = NUL;
10860             } else if (places > 0) {    /* d > 0 so this many decimal places */
10861                 i++;                           /* First digit after decimal */
10862                 for (j = 0; j < places; j++) { /* Truncate after d decimal */
10863                     if (!fnval[j+i])           /* places or extend to d  */
10864                       gotend = 1;              /* decimal places. */
10865                     if (gotend || j+i+sign > fp_digits)
10866                       fnval[j+i] = '0';
10867                 }
10868                 fnval[j+i] = NUL;
10869             } else {                    /* d == 0 so Do The Right Thing */
10870                 for (j = (int)strlen(fnval) - 1; j > i+1; j--) {
10871                     if ((j - sign) > fp_digits)
10872                       fnval[j] = '0';
10873                     if (fnval[j] == '0')
10874                       fnval[j] = NUL;   /* Strip useless trailing 0's. */
10875                     else
10876                       break;
10877                 }
10878             }
10879         }
10880 #endif /* COMMENT */
10881         debug(F111,"fpresult 2",fnval,errno);
10882         goto fnend;
10883
10884     }
10885 #endif /* FNFLOAT */
10886
10887 #ifdef CKCHANNELIO
10888     if (cx == FN_FSTAT  ||              /* File functions */
10889         cx == FN_FPOS   ||
10890         cx == FN_FEOF   ||
10891         cx == FN_FGCHAR ||
10892         cx == FN_FGLINE ||
10893         cx == FN_FGBLK  ||
10894         cx == FN_FPCHAR ||
10895         cx == FN_FPLINE ||
10896         cx == FN_FPBLK  ||
10897         cx == FN_NLINE  ||
10898         cx == FN_FERMSG ||
10899         cx == FN_FILNO) {
10900         int x = 0, t = 0, channel;
10901         long z;
10902         extern int z_maxchan;
10903
10904         failed = 1;                     /* Assume failure */
10905         p = fnval;                      /* until we validate args */
10906         if (cx == FN_FERMSG) {
10907             extern int z_error;
10908             if (argn < 1) {
10909                 x = z_error;
10910             } else if (chknum(bp[0])) {
10911                 x = atoi(bp[0]);
10912             } else if (fndiags)
10913               ckmakmsg(fnval,FNVALL,
10914                        "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10915             failed = 0;
10916             ckstrncpy(fnval,ckferror(x),FNVALL);
10917             goto fnend;
10918         }
10919         if (argn < 1) {                 /* All file functions need channel */
10920             if (cx == FN_FSTAT) {       /* Except f_status(), e.g. when */
10921                 fnval[0] = '0';         /* called with a variable that */
10922                 fnval[1] = NUL;         /* hasn't been defined yet. */
10923                 failed = 0;
10924             } else {
10925                 if (fndiags)
10926                  ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
10927             }
10928             goto fnend;
10929         }
10930         if (rdigits(bp[0])) {           /* Channel must be numeric */
10931             channel = atoi(bp[0]);
10932         } else {                        /* Fail if it isn't */
10933             if (fndiags)
10934               ckmakmsg(fnval,FNVALL,
10935                        "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10936             goto fnend;
10937         }
10938         if (channel < 0 || channel > z_maxchan) { /* Check channel range */
10939             if (fndiags)
10940               ckmakmsg(fnval,FNVALL,
10941                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
10942             goto fnend;
10943         }
10944         x = z_getmode(channel);         /* Find out about the channel */
10945
10946         failed = 0;                     /* Assume success from here down */
10947         if (cx == FN_FSTAT) {           /* Status / modes of channel */
10948             if (x > -1)
10949               x &= FM_RWB;              /* Mask out irrelevant bits */
10950             else                        /* In this case not open is OK */
10951               x = 0;                    /* 0 if not open, 1-7 if open */
10952             sprintf(fnval,"%d",x);      /* SAFE */
10953             goto fnend;
10954         } else if (x < 1) {             /* Not \f_status() so must be open */
10955             failed = 1;
10956             if (fndiags)
10957               ckmakmsg(fnval,FNVALL,"<ERROR:FILE_NOT_OPEN:\\f",fn,"()>",NULL);
10958             goto fnend;
10959         }
10960         switch (y) {                    /* Do the requested function */
10961           case FN_FPOS:                 /* Get position */
10962             z = z_getpos(channel);      /* FIX THIS */
10963             sprintf(fnval,"%ld",z);     /* SAFE */
10964             goto fnend;
10965
10966           case FN_NLINE:                /* Get line number */
10967             z = z_getline(channel);     /* FIX THIS */
10968             sprintf(fnval,"%ld",z);     /* SAFE */
10969             goto fnend;
10970
10971           case FN_FEOF:                 /* Check EOF */
10972             t = 0;
10973             if (x & FM_EOF) t = 1;
10974             sprintf(fnval,"%d",t);      /* SAFE */
10975             goto fnend;
10976
10977           case FN_FILNO:                /* Get file handle */
10978             x = z_getfnum(channel);
10979             sprintf(fnval,"%d",x);      /* SAFE */
10980             goto fnend;
10981
10982           case FN_FPBLK:                /* Read or write block */
10983           case FN_FGBLK:
10984             if (argn < 2) {
10985                 if (fndiags)
10986                   ckmakmsg(fnval,FNVALL,
10987                            "<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
10988                 goto fnend;
10989             }
10990             if (rdigits(bp[1])) {
10991                 t = atoi(bp[1]);
10992             } else {
10993                 if (fndiags)
10994                   ckmakmsg(fnval,FNVALL,
10995                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10996                 goto fnend;
10997             }
10998           case FN_FGCHAR:               /* Read or write character or line */
10999           case FN_FPCHAR:
11000           case FN_FGLINE:
11001           case FN_FPLINE:
11002             fnval[0] = NUL;
11003             switch (y) {
11004               case FN_FGCHAR: t = z_in(channel,fnval,FNVALL,1,1); break;
11005               case FN_FGLINE: t = z_in(channel,fnval,FNVALL,FNVALL-1,0); break;
11006               case FN_FGBLK:
11007                 if (t >= FNVALL) t = FNVALL - 1;
11008                 t = z_in(channel,fnval,FNVALL,t,1);
11009                 break;
11010               case FN_FPCHAR: t = z_out(channel,bp[1],1,1);  break;
11011               case FN_FPLINE: t = z_out(channel,bp[1],-1,0); break;
11012               case FN_FPBLK:  t = z_out(channel,bp[1],-1,1); break;
11013             }
11014             if (t < 0) {                /* Handle read/write error */
11015                 failed = 1;
11016                 if (fndiags && t != FX_EOF)
11017                   ckmakmsg(fnval,FNVALL,
11018                            "<ERROR:FILE_ERROR_%d:\\f",fn,"()>",NULL);
11019                 goto fnend;
11020             }
11021             if (cx == FN_FGCHAR)        /* Null terminate char */
11022               fnval[1] = NUL;
11023             /* Write (put) functions return numeric status code */
11024             if (cx == FN_FPCHAR || cx == FN_FPLINE || cx == FN_FPBLK)
11025               sprintf(fnval,"%d",t);    /* SAFE */
11026             goto fnend;
11027         }
11028     }
11029 #endif /* CKCHANNELIO */
11030
11031     if (cx == FN_SQUEEZE) {             /* String function \fsqueeze() */
11032         /* Squeeze out whitespace */
11033         /* Add options later for whether to trim leading and trailing blanks */
11034         /* and what to do about control characters, 8-bit whitespace, etc */
11035         int started = 0;                /* Flag for first non-whitespace */
11036         int n = 0;                      /* Blank/Tab counter */
11037         s = bp[0] ? bp[0] : "";
11038         p = fnval;                      /* Result buffer */
11039         while (*s) {                    /* While there is input */
11040             if (!started && (*s == ' ' || *s == '\011')) {
11041                 s++;                    /* Skip past leading whitespace */
11042                 continue;
11043             }
11044             started++;                  /* Leading whitespace was skipped */
11045             if (*s != ' ' && *s != '\011') { /* Have a nonspace char */
11046                 n = 0;                  /* reset space counter */
11047                 *p++ = *s++;            /* copy char to destination */
11048                 continue;
11049             }               
11050             if (n++ > 0) {              /* Have blank or tab */
11051                 s++;                    /* don't copy more than one */
11052                 continue;
11053             }
11054             *p++ = ' ';                 /* Deposit one space */
11055             s++;                        /* and go to next source char */
11056         }
11057         if (*(p-1) == ' ') p--;         /* Remove trailing space */
11058         *p = NUL;                       /* Terminate string */
11059         p = fnval;                      /* point to beginning */
11060         goto fnend;                     /* Done. */
11061     }
11062     if (cx == FN_PATTERN) {             /* \fpattern() for INPUT */
11063         itsapattern = 1;
11064         if (argn > 0) {
11065             p = fnval;
11066             ckstrncpy(fnval,bp[0],FNVALL);
11067         } else p = "";
11068         goto fnend;
11069     }
11070     if (cx == FN_HEX2N || cx == FN_OCT2N) { /* \fhex2n(), \foct2n() */
11071         p = "0";
11072         if (argn < 1)
11073           goto fnend;
11074         p = ckradix(bp[0], ((cx == FN_HEX2N) ? 16 : 8), 10);
11075         if (!p) {
11076             if (fndiags)
11077               ckmakmsg(fnval,FNVALL,
11078                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11079             goto fnend;
11080         }
11081         failed = 0;
11082         ckstrncpy(fnval,p,FNVALL);
11083         p = fnval;
11084         goto fnend;
11085     }
11086
11087     if (cx == FN_HEX2IP) {
11088         int c[2], ip[4], i, k;
11089         p = "0";
11090         if (argn < 1)
11091           goto fnend;
11092         s = bp[0];
11093         if ((int)strlen(s) != 8) {
11094             failed = 1;
11095             if (fndiags)
11096               ckmakmsg(fnval,FNVALL,
11097                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11098             goto fnend;
11099         }
11100         p = fnval;
11101         for (k = 0; k < 8; k += 2) {
11102             for (i = 0; i < 2; i++) {
11103                 c[i] = *s++;
11104                 if (islower(c[i])) c[i] = toupper(c[i]);
11105                 if (c[i] >= '0' && c[i] <= '9') {
11106                     c[i] -= 0x30;
11107                 } else if (c[i] >= 'A' && c[i] <= 'F') {
11108                     c[i] -= 0x37;
11109                 } else {
11110                     failed = 1;
11111                     if (fndiags)
11112                       ckmakmsg(fnval,FNVALL,
11113                                "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11114                     goto fnend;
11115                 }
11116                 ip[k/2] = c[0] << 4 | c[1];
11117             }
11118             sprintf(p,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]); /* SAFE */
11119         }
11120         goto fnend;
11121     }
11122     if (cx == FN_IP2HEX) {
11123         int ip[4], i;
11124         char * q;
11125         p = "00000000";
11126         if (argn < 1)
11127           goto fnend;
11128         s = bp[0];
11129         p = fnval;
11130         for (i = 0; i < 3; i++) {
11131             q = ckstrchr(s,'.');
11132             if (q) {
11133                 *q++ = NUL;
11134                 ip[i] = atoi(s);
11135                 s = q;
11136             } else {
11137                 failed = 1;
11138                 if (fndiags)
11139                   ckmakmsg(fnval,FNVALL,
11140                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11141                 goto fnend;
11142             }
11143         }
11144         ip[3] = atoi(s);
11145         sprintf(p,"%02x%02x%02x%02x",ip[0],ip[1],ip[2],ip[3]); /* SAFE */
11146         goto fnend;
11147     }
11148     if (cx == FN_RADIX) {
11149         failed = 1;
11150         p = fnval;
11151         if (argn < 3) {
11152             if (fndiags)
11153               ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
11154             goto fnend;
11155         }
11156         if (!rdigits(bp[1]) || !rdigits(bp[2])) {
11157             if (fndiags)
11158               ckmakmsg(fnval,FNVALL,
11159                        "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11160             goto fnend;
11161         }
11162         p = ckradix(bp[0],atoi(bp[1]),atoi(bp[2]));
11163         if (!p) {
11164             if (fndiags)
11165               ckmakmsg(fnval,FNVALL,
11166                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11167             goto fnend;
11168         }
11169         failed = 0;
11170         ckstrncpy(fnval,p,FNVALL);
11171         p = fnval;
11172         goto fnend;
11173     }
11174     if (cx == FN_JOIN) {
11175         int i, x, y, z, flag, flag2, hi, lo, max, seplen, grouping = 0;
11176         char abuf[16], c, *s, *q, *sep = NULL;
11177         char * gr_opn = "\"{'([<";      /* Group open brackets */
11178         char * gr_cls = "\"}')]>";      /* Group close brackets */
11179         char lb[2], rb[2];              /* Selected left and right brackets */
11180         int csv = 0, tsv = 0;           /* Function flags */
11181         char specialchar = 0;           /* Field char that triggers grouping */
11182         char *s2 = NULL;                /* Address of malloc'd storage */
11183
11184         failed = 1;                     /* Assume failure */
11185         fnval[0] = NUL;
11186         debug(F101,"FNJOIN ARGN","",argn);
11187
11188         ckstrncpy(abuf,bp[0],16);       /* Get array reference */
11189         s = abuf;
11190         if ((x = arraybounds(s,&lo,&hi)) < 0) {  /* Get index and bounds */
11191             if (fndiags)
11192               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
11193             goto fnend;
11194         }
11195         p = fnval;                      /* Point to result */
11196         max = a_dim[x];                 /* Size of array */
11197         if (lo < 0) lo = 1;             /* Use given range if any */
11198         if (lo > max) lo = max;
11199 #ifdef COMMENT
11200         hi = max;
11201 #else
11202 /*
11203   This is a workaround for the problem in which the dimension of the \&_[]
11204   array (but not its contents) grows upon entry to a SWITCH block.  But this
11205   code prevents the dimension from growing.  Go figure.
11206 */
11207         if (hi < 0) {                   /* Bounds not given */
11208             if (x)                      /* Regular array */
11209               hi = max;
11210             else                        /* Argument vector array */
11211               for (hi = max; hi >= lo; hi--) { /* ignore any trailing */
11212                   if (!a_ptr[x][hi]) continue; /* empty elements */
11213                   if (!*(a_ptr[x][hi])) continue;
11214                   break;
11215               }
11216         }
11217 #endif /* COMMENT */
11218         if (hi > max) hi = max;
11219         failed = 0;                     /* Unset failure flag */
11220         if (max < 1)
11221           goto fnend;
11222         sep = " ";                      /* Separator */
11223         lb[0] = NUL;                    /* Group start char (as string) */
11224         rb[0] = NUL;
11225         lb[1] = NUL;                    /* Group end char as string */
11226         rb[1] = NUL;
11227
11228         if (argn > 1) {
11229             if (bp[1]) if (*bp[1]) {    /* If arg1 given and not empty */
11230                 if (!strcmp(bp[1],"CSV")) { /* Special "CSV" symbolic arg */
11231                     csv++;              /* Make a comma separated list */
11232                     sep = ",";          /* Comma */
11233                     specialchar = *sep; /* Separator is special character */
11234                     grouping = 1;       /* Group with doublequotes */
11235                     lb[0] = '"';        /* and here */
11236                     rb[0] = '"';        /* they are */
11237                 } else if (!strcmp(bp[1],"TSV")) { /* "TSV" symbolic arg */
11238                     tsv++;              /* Make a Tab separated list */
11239                     sep = "\011";       /* Tab */
11240                     specialchar = *sep;
11241                     grouping = 0;       /* No grouping */
11242                 } else                  /* Normal case */
11243                   sep = bp[1];          /* use the separator char specified */
11244             }
11245         }
11246         if (argn > 2 && !csv && !tsv) { /* Grouping? */
11247             char * bp2 = bp[2];
11248             if (!bp2) bp2 = "0";
11249             if (!*bp2) bp2 = "0";
11250             if (chknum(bp2)) {
11251                 grouping = atoi(bp2);
11252                 if (grouping < 0 || grouping > 63)
11253                   grouping = 1;
11254             } else {
11255                 failed = 1;
11256                 if (fndiags)
11257                   ckmakmsg(fnval,FNVALL,
11258                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11259                 goto fnend;
11260             }
11261             if (grouping) {             /* Take lowest-order one */
11262                 int j, k;               /* and set the others to 0 */
11263                 for (k = 0; k < 6; k++) {
11264                     j = 1 << k;
11265                     if (grouping & j) {
11266                         lb[0] = gr_opn[k];
11267                         rb[0] = gr_cls[k];
11268                         break;
11269                     }
11270                 }
11271             }
11272         }
11273         if (!csv && !tsv) {             /* Normal case, not CSV or TSV */
11274             specialchar = SP;           /* Special character is space */
11275             if (argn > 3)               /* Nonzero 4th arg for no separator */
11276               if (chknum(bp[3]))
11277                 if (atoi(bp[3]) > 0)
11278                   sep = NULL;
11279             if (!sep) {
11280                 sep = "";
11281                 seplen = 0;
11282             } else
11283               seplen = strlen(sep);
11284         }
11285         for (i = lo; i <= hi; i++) {    /* Loop thru selected array elements */
11286             s = a_ptr[x][i];            /* Get next element */
11287             if (!s)
11288               s = "";
11289             flag = 0;                   /* Flag to indicate grouping needed */
11290             flag2 = 0;                  /* Flag for internal doublequotes */
11291             if (grouping) {             /* Does this element need quoting? */
11292                 q = s;                  /* Look for special character */
11293                 while ((c = *q++)) {    /* If found */
11294                     if (c == specialchar) /* grouping is required */
11295                       flag++;
11296                     if (csv && (c == '"')) /* Character that needs doubling */
11297                       flag2++;             /* in comma-separated list */
11298                     if (flag && !csv)   /* Exit early if no more to do */
11299                       break;
11300                 }
11301             }
11302             y = strlen(s);              /* Get length of this element */
11303             if ((y > 0) && csv && !flag) { /* CSV item needs grouping */
11304                 if (s[0] == SP || s[y-1] == SP || /* if it has leading */
11305                     s[0] == HT || s[y-1] == HT) /* or trailing whitespace */
11306                   flag++;               /* then it needs grouping */
11307             }
11308             if (flag || flag2) {        /* String needs grouping or quoting */
11309                 char *ss = s;
11310                 q = (char *)malloc(y + flag2 + 3); /* Make new buffer */
11311                 if (q) {
11312                     s2 = q;             /* and this is what to free */
11313                     if (flag)           /* If grouping */
11314                       *q++ = lb[0];     /* put opening group quote */
11315                     while (*ss) {       /* Loop through string */
11316                         if (flag2 && (*ss == '"')) /* If CSV and this a '"' */
11317                           *q++ = *ss;              /* double it. */
11318                         *q++ = *ss++;   /* Copy the character */
11319                     }
11320                     if (flag)           /* If grouping */
11321                       *q++ = rb[0];     /* add closing group quote */
11322                     *q = NUL;           /* terminate the result. */
11323                     s = s2;
11324                     y = strlen(s);
11325                 }
11326             }
11327             z = 0;                      /* Number of chars copied */
11328             flag = 0;                   /* flag is now buffer-overrun flag */
11329             if (y > 0)                  /* If this string is not empty */
11330               z = ckstrncat(fnval,s,FNVALL); /* copy it. */
11331             if (s2) free(s2);           /* Free temp storage */
11332             if (z < y)                  /* Now check for buffer overrun. */
11333               flag++;
11334             if (!flag && *sep && i < hi) { /* If buffer still has room */
11335                 z = ckstrncat(fnval,sep,FNVALL); /* copy delimiter */
11336                 if (z < seplen)
11337                   flag++;
11338             }
11339             if (flag) {
11340                 failed = 1;
11341                 if (fndiags)
11342                   ckmakmsg(fnval,FNVALL,
11343                            "<ERROR:RESULT_TOO_LONG:\\f",fn,"()>",NULL);
11344                 goto fnend;
11345             }
11346         }
11347         isjoin = 1;
11348         goto fnend;
11349     }
11350     if (cx == FN_SUBST) {               /* \fsubstitute() */
11351         CHAR c, * s, * r, * tp[2], buf1[256], buf2[256], buf3[256];
11352         int len, i, j, state = 0, lo = 0, hi = 0;
11353
11354         failed = 0;
11355         p = fnval;                      /* Result pointer */
11356         *p = NUL;
11357         if (!bp[0])                     /* No target, no result*/
11358           goto fnend;
11359
11360         len = strlen(bp[0]);            /* Length of source */
11361         if (len == 0)
11362           goto fnend;
11363         if (len > FNVALL) {
11364             failed = 1;
11365             if (fndiags)
11366               ckmakmsg(fnval,FNVALL,
11367                        "<ERROR:RESULT_TOO_LONG:\\f",fn,"()>",NULL);
11368             goto fnend;
11369         }
11370         if (!bp[1]) {
11371             ckstrncpy(bp[0],fnval,FNVALL);
11372             goto fnend;
11373         }
11374         tp[0] = buf1;                   /* For s2-s3 interpretation loop */
11375         tp[1] = buf2;
11376
11377         for (i = 0; i < 256; i++) {     /* Initialize working buffers */
11378             buf1[i] = 0;                /* s2 expansion buffer */
11379             buf2[i] = 0;                /* s3 expansion buffer */
11380             buf3[i] = i;                /* Translation table */
11381         }
11382         for (i = 0; i < 2; i++) {       /* Interpret s2 and s3 */
11383             s = (CHAR *)bp[i+1];        /* Arg pointer */
11384             if (!s) s = (CHAR *)"";
11385             r = tp[i];                  /* To construct interpreted arg */
11386             j = 0;                      /* Output buf pointer */
11387             state = 0;                  /* Initial state */
11388             while (c = *s++) {          /* Loop thru arg chars */
11389                 if (j > 255)            /* Output buf full */
11390                   break;
11391                 switch (state) {
11392                   case 0:               /* Normal state */
11393                     switch (c) {
11394                       case '\\':        /* Have quote */
11395                         state = 1;
11396                         break;
11397                       case '[':         /* Have range starter */
11398                         state = 2;
11399                         break;
11400                       default:          /* Anything else */
11401                         r[j++] = c;
11402                         break;
11403                     }
11404                     continue;
11405                   case 1:               /* Quoted char */
11406                     r[j++] = c;
11407                     state = 0;
11408                     continue;
11409                   case 2:               /* Range bottom */
11410                     lo = c;
11411                     state++;
11412                     continue;
11413                   case 3:               /* Range separater */
11414                     if (c != '-') {
11415                         failed = 1;
11416                         if (fndiags)
11417                           ckmakmsg(fnval,FNVALL,
11418                                    "<ERROR:BAD_RANGE:\\f",fn,"()>",NULL);
11419                         goto fnend;
11420                     }
11421                     state++;
11422                     continue;
11423                   case 4:               /* Range top */
11424                     hi = c;
11425                     state++;
11426                     continue;
11427                   case 5:               /* Range end */
11428                     if (c != ']') {
11429                         failed = 1;
11430                         if (fndiags)
11431                           ckmakmsg(fnval,FNVALL,
11432                                    "<ERROR:BAD_RANGE:\\f",fn,"()>",NULL);
11433                         goto fnend;
11434                     }
11435                     for (k = lo; k <= hi && j < 255; k++) /* Fill in */
11436                       r[j++] = k;
11437                     lo = 0; hi = 0;     /* Reset */
11438                     state = 0;
11439                     continue;
11440                 }
11441             }
11442         }
11443         for (i = 0; i < 256 && buf1[i]; i++) {  /* Create translation table */
11444             k = (unsigned)buf1[i];
11445             buf3[k] = buf2[i];
11446         }
11447         s = (CHAR *)bp[0];              /* Point to source string */
11448         for (i = 0; i < len; i++) {     /* Translation loop */
11449             k = (unsigned)s[i];         /* Get next char */
11450             if (!buf3[k])               /* Remove this char */
11451               continue;
11452             *p++ = buf3[k];             /* Substitute this char */
11453         }
11454         *p = NUL;
11455         p = fnval;
11456         goto fnend;
11457     }
11458
11459 #ifndef NOSEXP
11460     if (cx == FN_SEXP) {                /* \fsexpression(arg1) */
11461         char * p2;
11462         fsexpflag++;
11463         p = (argn > 0) ? dosexp(bp[0]) : "";
11464         fsexpflag--;
11465         p2 = fnval;
11466         while ((*p2++ = *p++)) ;
11467         p = fnval;
11468         goto fnend;
11469     }
11470 #endif /* NOSEXP */
11471
11472     if (cx == FN_CMDSTK) {              /* \fcmdstack(n1,n2) */
11473         int i, j, k;
11474         char * s;
11475
11476         if (bp[0])
11477           val1 = *(bp[0]) ? evalx(bp[0]) : ckitoa(cmdlvl);
11478         else
11479           val1 = ckitoa(cmdlvl);
11480 #ifdef COMMENT
11481         free(bp[0]);                    /* (evalx() always uses same buffer) */
11482         bp[0] = NULL;                   /* (not any more!) */
11483 #endif /* COMMENT */
11484         failed = 1;
11485         if (argn > 1) {
11486 #ifdef COMMENT
11487             makestr(&(bp[0]),val1);
11488             val1 = bp[0];
11489 #endif /* COMMENT */
11490             val2 = *(bp[1]) ? evalx(bp[1]) : "0";
11491             if (!(chknum(val1) && chknum(val2))) {
11492                 if (fndiags)
11493                   ckmakmsg(fnval,FNVALL,
11494                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11495                 goto fnend;
11496             }
11497         } else {
11498             val1 = ckitoa(cmdlvl);
11499             val2 = "0";
11500         }
11501         i = atoi(val1);                 /* Level */
11502         j = atoi(val2);                 /* Flags */
11503         if (i < 0 || i > cmdlvl) {
11504             if (fndiags)
11505               ckmakmsg(fnval,FNVALL,
11506                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11507             goto fnend;
11508         }
11509         failed = 0;
11510         p = fnval;
11511         k = cmdstk[i].src;              /* What (prompt, file, macro) */
11512         if (j) {
11513             ckstrncpy(fnval,ckitoa(k),FNVALL);
11514             goto fnend;
11515         }
11516         switch (k) {
11517           case CMD_KB:
11518             ckstrncpy(fnval,"(prompt)",FNVALL);
11519             break;
11520           case CMD_TF:
11521             s = tfnam[cmdstk[i].lvl];
11522             if (!zfnqfp(s,FNVALL,fnval))
11523               ckstrncpy(fnval,s,FNVALL);
11524             break;
11525           case CMD_MD:
11526             ckstrncpy(fnval,m_arg[cmdstk[i].lvl][0],FNVALL);
11527             break;
11528         }
11529         goto fnend;
11530     }
11531 #ifdef CKFLOAT
11532     if (cx == FN_DIFDATE) {             /* \fdiffdates(d1,d2) */
11533         char * d1, * d2;
11534         d1 = bp[0] ? bp[0] : ckdate();
11535         d2 = bp[1] ? bp[1] : ckdate();
11536         p = (char *)cmdiffdate(d1,d2);
11537         if (!p) {
11538             failed = 1;
11539             if (fndiags) {
11540                 ckmakmsg(fnval,FNVALL,"<ERROR:BAD_DATE:\\f",fn,"()>",NULL);
11541                 p = fnval;
11542             }
11543         }
11544         goto fnend;
11545     }
11546 #endif /* CKFLOAT */
11547     if (cx == FN_CMPDATE) {             /* \fcmddates(d1,d2) */
11548         int x = 0;
11549         char d1[18], d2[18], * dp;
11550         failed = 0;
11551         d1[0] = NUL;
11552         d2[0] = NUL;
11553         p = fnval;
11554         dp = cmcvtdate(bp[0],1);
11555         if (dp) {
11556             ckstrncpy(d1,dp,18);
11557             if ((dp = cmcvtdate(bp[1],1))) {
11558                 ckstrncpy(d2,dp,18);
11559                 x = 1;
11560             }
11561         }
11562         if (x == 0) {
11563             failed = 1;
11564             if (fndiags)
11565               ckmakmsg(fnval,FNVALL,"<ERROR:BAD_DATE:\\f",fn,"()>",NULL);
11566         } else {
11567             x = strcmp(d1,d2);
11568             if (x > 0)
11569               x = 1;
11570             else if (x < 0)
11571               x = -1;
11572             sprintf(fnval,"%d",x);
11573         }
11574         goto fnend;
11575     }
11576     if (cx == FN_TOGMT) {               /* \futcdate(d1) */
11577         char * d1, * dp;
11578         char datebuf[32];
11579         char d2[32];
11580         p = fnval;
11581         failed = 1;
11582         if ((dp = cmcvtdate(bp[0],1))) { /* The given date */
11583             ckstrncpy(datebuf,dp,18);
11584             ckstrncpy(d2,dp,18);        /* local time */
11585             ckstrncat(datebuf,"Z",19);  /* Same time GMT */
11586             if ((dp = cmcvtdate(datebuf,1))) /* converted to local time */
11587               ckstrncpy(datebuf,dp,18);
11588             if ((p = (char *)cmdiffdate(d2,datebuf))) { /* Get offset */
11589                 ckstrncat(d2,p,32);     /* Append offset to local time */
11590                 if ((dp = cmcvtdate(d2,1))) {
11591                     failed = 0;
11592                     ckstrncpy(fnval,dp,FNVALL);
11593                     p = fnval;
11594                 }
11595             }
11596         }
11597         if (failed && fndiags)
11598           ckmakmsg(fnval,FNVALL,"<ERROR:BAD_DATE:\\f",fn,"()>",NULL);
11599         goto fnend;
11600     }
11601     if (cx == FN_DELSEC) {              /* \fdelta2secs(delta-time) */
11602         long secs;
11603         p = fnval;
11604         if ((x = delta2sec(bp[0],&secs)) < 0) {
11605             failed = 1;
11606             if (fndiags)
11607               ckmakmsg(fnval,FNVALL,
11608                        (x == -1) ?
11609                          "<ERROR:BAD_DELTA_TIME:\\f" :
11610                          "<ERROR:OVERFLOW:\\f",
11611                        fn,
11612                        "()>",
11613                        NULL
11614                        );
11615             goto fnend;
11616         }
11617         sprintf(p,"%ld",secs);
11618         goto fnend;
11619     }
11620     if (cx == FN_PC_DU) {
11621         char c, * s = bp[0];
11622         if (!s) s = "";
11623         p = fnval;
11624         while ((c = *s++)) {
11625             if (c == ':') {
11626                 if (*s != '\\')
11627                   *p++ = '/';
11628             } else if (c == '\\') {
11629                 *p++ = '/';
11630             } else {
11631                 *p++ = c;
11632             }
11633         }
11634         *p = NUL;
11635         p = fnval;
11636         goto fnend;
11637     }
11638     if (cx == FN_PC_UD) {               /* Unix to DOS path */
11639         char c, * s = bp[0];
11640         if (!s) s = "";
11641         if (*s == '~') {                /* Skip leading tilde */
11642             s++;
11643             if (*s == '/')
11644               s++;
11645         }
11646         p = fnval;
11647         while ((c = *s++))
11648           *p ++ = (c == '/') ? '\\' : c;
11649         *p = NUL;
11650         p = fnval;
11651         goto fnend;
11652     }
11653     if (cx == FN_KWVAL) {               /* Keyword=Value */
11654         p = dokwval(bp[0],bp[1]?bp[1]:"=");
11655         goto fnend;
11656     }
11657 #ifdef COMMENT
11658 /* Cute idea but doesn't work */
11659     if (cx == FN_SLEEP || cx == FN_MSLEEP) {
11660         p = "";
11661         if (chknum(bp[0])) {
11662             x = atoi(bp[0]);
11663         } else {
11664             failed = 1;
11665             if (fndiags) {
11666                 ckmakmsg(fnval,FNVALL,
11667                          "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11668                 p = fnval;
11669             }
11670             goto fnend;
11671         }
11672         if (cx == FN_SLEEP)
11673           x *= 1000;
11674         msleep(x);
11675         goto fnend;
11676     }
11677 #endif /* COMMENT */
11678
11679 #ifdef NT
11680     if (cx == FN_SNAME) {
11681         GetShortPathName(bp[0],fnval,FNVALL);
11682         goto fnend;
11683     }
11684     if (cx == FN_LNAME) {
11685         ckGetLongPathName(bp[0],fnval,FNVALL);
11686         goto fnend;
11687     }
11688 #endif /* NT */
11689
11690 /*
11691   \femailaddress():
11692   Picks the email address out of an RFC 2822 From: or Sender: header.
11693   Added 26 Nov 2005.  Handles all common, and some uncommon, cases but
11694   doesn't totally bother about nested comments.  Needed this for fetching
11695   email from a POP server and then constructing the BSD "From " line.
11696   Works with or without the "From: " or "Sender: " tag.
11697 */
11698     if (cx == FN_EMAIL) {
11699         char c, * s = bp[0], * s2, * s3, * ap = "";
11700         int k, state = 0, quote = 0, infield = 0;
11701         int pc = 0;                     /* For nested comments */
11702         if (!s) s = "";
11703         if (!*s) goto xemail;
11704
11705         if (ckindex("From: ",s,0,0,0) == 1) s += 5;
11706         if (ckindex("Sender: ",s,0,0,0) == 1) s += 7;
11707
11708         k = strlen(s);                  /* Strip junk from end */
11709         if (k < 1) goto xemail;
11710         k--;
11711         while (k >= 0 && s[k] == CR || s[k] == LF)
11712           s[k--] = NUL;
11713         while (k >= 0 && s[k] == SP || s[k] == HT)
11714           s[k--] = NUL;
11715         if (k == 0)
11716           goto xemail;
11717
11718 #ifndef COMMENT                      /* Simple method if not 100% foolproof */
11719         k = 0;
11720         for (s2 = s; *s2; s2++) {       /* Find at-sign */
11721             if (*s2 == '@') {
11722                 k++;                    /* If more than one use rightmost */
11723                 s3 = s2;
11724             }
11725         }
11726         if (k < 1)                      /* No at-sign */
11727           goto xemail;
11728
11729         for (ap = s3-1; ap >= s; ap--) { /* Back up to beginning of address */
11730             if (isspace(*ap) || *ap == '<') {
11731                 ap++;
11732                 break;
11733             }
11734             if (ap == s)
11735               break;
11736         }
11737         for (s2 = s3+1; *s2; s2++) {    /* Find end of address */
11738             if (isspace(*s2) || *s2 == '>')
11739               break;
11740         }
11741         *s2-- = NUL;
11742         if (*ap == '[' && *s2 == ']') { /* Handle [blah@blah.blah] */
11743             ap++;
11744             *s2 = NUL;
11745         }
11746         if (!ckstrcmp(ap,"mailto:",7,0)) /* Handle mailto: URLs */
11747           ap += 7;
11748
11749 #else  /* Too complicated and error-prone */
11750
11751         k = 0;
11752         for (s2 = s; *s2; s2++) {       /* Strip leading whitespace */
11753             if (*s2 == SP || *s2 == HT) {
11754                 k = 1;
11755                 break;
11756             }
11757         }
11758         if (!k) {                       /* Simple address */
11759             ap = s;
11760             goto xemail;
11761         }
11762         do {                            /* Not simple, have to extract it */
11763             if (quote) {
11764                 quote = 0;
11765                 continue;
11766             } else if (*s == '\\') {
11767                 quote = 1;
11768                 continue;
11769             }
11770             switch (state) {
11771               case 0:
11772                 if (!infield && *s == '"') { /* Quoted string */
11773                     infield = 1;
11774                     c = '"';
11775                     state = 1;
11776                 } else if (!infield && *s == '(') { /* Comment in parens */
11777                     pc++;
11778                     infield = 1;
11779                     c = ')';
11780                     if (*ap) *s = NUL;
11781                     state = 1;
11782                 } else if (!infield && *s == '<') { /* Address */
11783                     infield = 1;
11784                     c = '>';
11785                     ap = s+1;
11786                     state = 2;
11787                 } else if (infield && (*s == SP || *s == HT)) {
11788                     infield = 0;
11789                 } else {                /* One or more bare words */
11790                     infield = 1;        /* Could be an address */
11791                     if (!*ap) ap = s;   /* Could be comments */
11792                 }
11793                 continue;
11794               case 1:                   /* In Quoted string or Comment */
11795                 if (infield && *s == c) { /* Look for end */
11796                     infield = 0;
11797                     *s++ = NUL;
11798                     while (*s == SP || *s == HT) s++;
11799                     if (!*ap)
11800                       ap = s;
11801                     state = 0;
11802                 }
11803                 continue;
11804               case 2:                   /* In address */
11805                 if (infield && *s == c) { /* Looking for end */
11806                     infield = 0;
11807                     *s = NUL;
11808                     break;
11809                 }
11810             }
11811         } while (*s++);
11812
11813       xemail:
11814         if (*ap) {
11815             while (*ap == SP || *ap == HT) ap++;
11816         }
11817         k = strlen(ap) - 1;
11818         while (k >= 0 && (ap[k] == SP || ap[k] == HT))
11819           ap[k--] = NUL;
11820         if (*ap) {
11821             failed = 0;
11822             if (*ap == '<') {
11823                 k = strlen(ap);
11824                 if (*(ap+k-1) == '>') {
11825                     ap[k-1] = NUL;
11826                     ap++;
11827                 }
11828             }
11829         } else
11830           failed = 1;
11831         /* Here we might also want check against "*@*.*" */
11832 #endif  /* COMMENt */
11833       xemail:
11834         ckstrncpy(fnval,ap,FNVALL);
11835         goto fnend;
11836     }
11837
11838 #ifdef SEEK_CUR
11839 /*
11840    \fpicture():   Get dimensions of GIF or JPG image.
11841    fdc June 2006
11842 */
11843     if (cx == FN_PICTURE) {
11844         FILE *fp = NULL;
11845         int c, x, w = 0, h = 0, eof = 0;
11846         unsigned int i, j, k;
11847         unsigned char buf[1024];
11848         char abuf[16], * p, * s;
11849         char ** ap = NULL;
11850
11851         p = fnval;                      /* Point to result */
11852         failed = 1;                     /* Assume failure */
11853         if (argn > 1) {
11854             int xi;
11855             ckstrncpy(abuf,bp[1],16);   /* Get array reference */
11856             s = abuf;
11857             if (*s == CMDQ) s++;
11858             if (fndiags)                /* Default is this error message */
11859               ckmakmsg(fnval,FNVALL,
11860                        "<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
11861             if (s[0] != '&')            /* "Address" of array */
11862               goto fnend;
11863             if (s[2])
11864               if (s[2] != '[' || s[3] != ']')
11865                 goto fnend;
11866             if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */
11867               s[1] += 32;
11868             if ((xi = dclarray(s[1],2)) < 0) /* Two elements */
11869               goto fnend;
11870             ap = a_ptr[xi];             /* Point to array we just declared */
11871         }
11872         s = bp[0];                      /* Filename */
11873         failed = 0;                     /* From here on we don't fail */
11874         p[0] = '0';                     /* Default return value */
11875         p[1] = NUL;
11876         if (!ckmatch("*.{jpg,jpeg,gif}$",s,0,1+4)) /* Appropriate name? */
11877           goto fnend;                   /* No, fail */
11878         fp = fopen(s, "r");             /* Open it */
11879         if (fp == NULL) {               /* Can't, fail */
11880             p[0] = '-';
11881             p[1] = '1';
11882             p[2] = NUL;                 /* Return -1 */
11883             goto fnend;
11884         }
11885         k = strlen(s);
11886         if (!ckstrcmp(&s[k-4],".gif",4,0)) { /* GIF file */
11887             if (fread(buf,1,10,fp) != 10) {
11888                 fclose(fp);
11889                 goto fnend;
11890             }
11891             /* Check signature */
11892             if (ckstrcmp((char *)buf,"GIF87a",6,0) &&
11893                 ckstrcmp((char *)buf,"GIF89a",6,0)) {
11894                 fclose(fp);
11895                 goto fnend;
11896             }
11897             w = buf[6] + 256 * buf[7];
11898             h = buf[8] + 256 * buf[9];
11899             goto picend;
11900         } else if (!ckstrcmp(&s[k-4],".jpg",4,0) || /* JPEG file */
11901                    !ckstrcmp(&s[k-5],".jpeg",5,0)) {
11902             if (fread(buf,1,2,fp) != 2) {
11903                 fclose(fp);
11904                 goto fnend;
11905             }
11906             if (buf[0] != 0xff || buf[1] != 0xd8) { /* Check signature */
11907                 fclose(fp);
11908                 goto fnend;
11909             }
11910             eof = 0;
11911             while (!eof) {              /* Loop for each marker */
11912                 while (!eof) {          /* Find next marker */
11913                     c = getc(fp);
11914                     if (c == (unsigned int)EOF) {
11915                         eof++;
11916                         break;
11917                     }
11918                     if (c == 0xff) {
11919                         buf[0] = c;
11920                         c = getc(fp);
11921                         if (c == (unsigned int)EOF) {
11922                             eof++;
11923                             break;
11924                         }
11925                         buf[1] = c;
11926                         if (c == 0xd9)
11927                           eof++;
11928                         if (c >= 0xc0 && c <= 0xfe)
11929                           break;
11930                     }
11931                 }
11932                 if (eof) break;
11933                 x = buf[1];
11934                 if (x == 0xc0 || x == 0xc1 || x == 0xc2 || x == 0xc3 ||
11935                     x == 0xc9 || x == 0xca || x == 0xcb) {
11936                     if (fread(buf,1,7,fp) != 7) {
11937                         fclose(fp);
11938                         goto fnend;
11939                     }
11940                     h = buf[3] * 256 + buf[4];
11941                     w = buf[5] * 256 + buf[6];
11942                     goto picend;
11943                 } else {                /* Not a desired field */
11944                     if (feof(fp)) {
11945                         eof++;
11946                         break;
11947                     }
11948                     if (fread(buf,1,2,fp) != 2) { /* Length of this field */
11949                         fclose(fp);
11950                         goto fnend;
11951                     }
11952                     j = 256 * buf[0] + buf[1] - 2; /* Skip next field */
11953                     if (CKFSEEK(fp,(CK_OFF_T)j,SEEK_CUR) != 0) {
11954                         fclose(fp);
11955                         goto fnend;
11956                     }
11957                 }
11958             } 
11959         }
11960       picend:
11961         fclose(fp);
11962         if (ap) {
11963             makestr(&(ap[0]),"2");
11964             makestr(&(ap[1]),ckitoa(w));
11965             makestr(&(ap[2]),ckitoa(h));
11966         }
11967         if (w > 0 && h > 0) {
11968             if (w > h) p[0] = '1';
11969             if (h >= w) p[0] = '2';
11970         }
11971         goto fnend;
11972     }
11973 #endif  /* SEEK_CUR */
11974
11975     if (cx == FN_PID) {
11976         int x = -1;
11977         if (chknum(bp[0])) {            /* Need numeric argument */
11978             int pid;
11979             pid = atoi(bp[0]);          /* Convert to int */
11980 #ifdef UNIX
11981             if (kill(pid,0) < 0) {      /* Test it */
11982                 if (errno ==
11983 #ifdef ESRCH
11984                     ESRCH               /* No such process */
11985 #else
11986                     3
11987 #endif  /* ESRCH */
11988                     )
11989                   x = 0;
11990             } else                      /* Process exists */
11991               x = 1;
11992 #endif  /* UNIX */
11993         }
11994         sprintf(fnval,"%d",x);          /* SAFE */
11995         goto fnend;
11996     }
11997
11998     if (cx == FN_FUNC) {
11999         char * s = bp[0];
12000         p = "0";
12001         debug(F111,"ffunc",s,argn);
12002         if (argn > 0) {
12003             int x, y;
12004             for (p = s; *p; p++) {      /* Chop off trailing parens if any */
12005                 if (*p == '(') {
12006                     *p = NUL;
12007                     break;
12008                 }
12009             }
12010             /* Chop off leading "\\f" or "\f" or "f" */
12011             p = s;
12012             if (*p == CMDQ)             /* Allow for \\f... */
12013               p++;
12014             if (*p == CMDQ && (*(p+1) == 'f' || *(p+1) == 'F')) { /* or \f */
12015                 p += 2;
12016             } else if (*p == 'f' || *p == 'F') { /* or just f */
12017                 p++;
12018             }
12019             y = lookup(fnctab,p,nfuncs,&x); /* Look up the result */
12020             debug(F111,"ffunc",p,y);
12021             p = (y > -1) ? "1" : "0";
12022         }
12023         goto fnend;
12024     }
12025     if (cx == FN_RECURSE) {
12026         int t, n;
12027         char * s;
12028         fnval[0] = NUL;                 /* Default result is empty string */
12029         s = bp[0];                      /* Check for null argument */
12030         if (!s) s = "";                 /* or empty argument */
12031         if (!*s) goto fnend;            /* in which case return empty string */
12032         n = FNVALL;                     /* Not empty, max size for result */
12033         s = fnval;                      /* Location of result */
12034         {
12035             /* Force VARIABLE-EVALUATION SIMPLE RECURSIVE */
12036             /* NOTE: This is vulnerable to SIGINT and whatnot... */
12037             int tmp = vareval;          /* Save VARIABLE-EVALUATION setting */
12038             vareval = 1;                /* Force it to RECURSIVE */
12039             zzstring(bp[0],&s,&n);      /* Expand arg into result space */ 
12040             vareval = tmp;              /* Restore VARIABLE-EVALUATION */
12041         }
12042         goto fnend;
12043     }
12044
12045     if (cx == FN_XLATE) {               /* f_cvtcset() */
12046 #ifdef NOFRILLS
12047         ckstrncpy(fnval,bp[0],FNVALL);  
12048 #else
12049 #ifndef NOUNICODE
12050         _PROTOTYP( char * cvtstring, (char *, int, int) );
12051         char * string, * cset1, * cset2;
12052         int id1, id2;
12053 #endif  /* NOUNICODE */
12054         fnval[0] = NUL; 
12055 #ifdef NOUNICODE
12056         ckstrncpy(fnval,bp[0],FNVALL);  
12057 #else
12058         string = bp[0] ? bp[0] : "";    /* String to convert */
12059         if (!*string) goto fnend;       /* It's empty */
12060
12061         cset1 = bp[1] ? bp[1] : "ascii"; /* Current charset of string */
12062         cset2 = bp[2] ? bp[2] : "ascii"; /* Charset to convert to */
12063
12064         id1 = lookup(fcstab,cset1,nfilc,NULL); /* Lookup 'from' set */
12065         if (id1 < 0) {
12066             failed = 1;
12067             ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN_CHARSET:\\f",fn,"()>",NULL);
12068             goto fnend;
12069         }
12070         id2 = lookup(fcstab,cset2,nfilc,NULL); /* Lookup 'to' set */
12071         if (id2 < 0) {
12072             failed = 1;
12073             ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN_CHARSET:\\f",fn,"()>",NULL);
12074             goto fnend;
12075         }
12076         string = cvtstring(string,id1,id2);
12077         ckstrncpy(fnval,string,FNVALL);
12078 #endif  /* NOUNICODE */
12079 #endif  /* NOFRILLS */
12080         goto fnend;
12081     }
12082
12083 /* Decode strings containing hex escapes */
12084
12085     if (cx == FN_UNPCT) {               /* \fdecodehex() */
12086         char *s1, *s2;
12087         char *prefix;                   /* Can be 1 or 2 chars */
12088         char buf[3];
12089         int n = 0, k;
12090
12091         p = fnval;
12092         *p = NUL;
12093         if (argn < 1) goto fnend;       /* Empty string */
12094
12095         s1 = bp[0] ? bp[0] : "";        /* Original string */
12096         prefix = bp[1] ? bp[1] : "%%";  /* Hex byte prefix */
12097         n = (int)strlen(prefix);        /* Length of prefix */
12098         if (n < 1 || n > 2) {           /* must be 1 or 2 */
12099             ckmakmsg(fnval,FNVALL,
12100                        "<ERROR:INVALID_HEX_PREFIX:\\f",fn,"()>",NULL);
12101             goto xunpct;
12102         }
12103         while (*s1) {
12104             if (!ckstrcmp(s1,prefix,n,0)) { /* Case-independent */
12105                 if (!*(s1+n)) {
12106                     ckmakmsg(fnval,FNVALL,
12107                              "<ERROR:INCOMPLETE_SEQUENCE:\\f",fn,"()>",NULL);
12108                     goto xunpct;
12109                 }
12110                 buf[0] = *(s1+n);       /* First hex character */
12111                 buf[1] = *(s1+n+1);     /* Second hex character */
12112                 buf[2] = NUL;
12113                 if ((k = ckhexbytetoint((char *)buf)) > -1) {
12114                     *p++ = (char) k;    /* Deposit decoded result */
12115                     s1 += 2+n;          /* and advance the source pointer */
12116                 } else {                /* Fail on conversion error */
12117                     ckmakmsg(fnval,FNVALL,
12118                              "<ERROR:NON_HEX_CHARS:\\f",fn,"()>",NULL);
12119                     goto xunpct;
12120                 }
12121             } else {                    /* Not a hex escape sequence */
12122                 *p++ = *s1++;           /* Just copy the character */
12123             }
12124         }
12125         *p = NUL;                       /* Terminate the result string */
12126         failed = 0;                     /* Say we didn't fail */
12127         p = fnval;                      /* Set up result pointer */
12128         goto fnend;                     /* and finish */
12129
12130       xunpct:                           /* Error exit */
12131         p = fnval;
12132         failed = 1;
12133         goto fnend;
12134     }
12135
12136 /* Check a string for encoding family */
12137
12138     if (cx == FN_STRINGT) {             /* \fstringtype() */
12139         p = "UNK";
12140         switch (scanstring(bp[0])) {
12141           case FT_7BIT: p = "7BIT"; break;
12142           case FT_8BIT: p = "8BIT"; break;
12143           case FT_UTF8: p = "UTF8"; break;
12144           case FT_UCS2: p = "UCS2"; break;
12145           case FT_TEXT: p = "TEXT"; break;
12146           case FT_BIN:  p = "BINARY"; break;
12147         }
12148         ckstrncpy(fnval,p,FNVALL);
12149         p = fnval;
12150         goto fnend;
12151     }
12152
12153 /* String compare s1, s2, [ case ], [ start ] , [ len ] */
12154
12155     if (cx == FN_STRCMP) {
12156         int docase = 0;                 /* Case matters or not */
12157         int start = 0;                  /* Start of substring */
12158         int len = -1;                   /* Length of substring to compare */
12159         int x; char * s1, * s2;         /* workers */
12160
12161         p = "0";                        /* Return value */
12162         if (argn == 0) {                /* Two null strings are equal */
12163             ckstrncpy(fnval,p,FNVALL);
12164             p = fnval;
12165             goto fnend;
12166         }
12167         if (argn == 1) {                /* Non-null string > null string */
12168             p = "1";
12169             ckstrncpy(fnval,p,FNVALL);
12170             p = fnval;
12171             goto fnend;
12172         }
12173         if (argn > 2) {
12174             s = *(bp[2]) ? evalx(bp[2]) : "0"; /* 0 = caseless */
12175             if (chknum(s)) docase = atoi(s);
12176             if (argn > 3) {
12177                 s = *(bp[3]) ? evalx(bp[3]) : "1"; /* start is 1-based */
12178                 if (chknum(s)) start = atoi(s);
12179                 if (argn > 4) {
12180                     s = *(bp[4]) ? evalx(bp[4]) : "-1"; /* -1 = whole thing */
12181                     if (chknum(s)) len = atoi(s);
12182                 }               
12183             }        
12184         }
12185         if (start > 0) start--;         /* start is 0-based internally */
12186         s1 = bp[0];                     /* Get length of first arg */
12187         x = (int)strlen(s1);
12188         if (x > start)                  /* Point to start position of s1 */
12189           s1 += start;
12190         else
12191           s1 = "";
12192         s2 = bp[1];                     /* Get length of second arg */
12193         x = (int)strlen(s2);
12194         if (x > start)                  /* Point to start position of s2 */
12195           s2 += start;
12196         else
12197           s2 = "";
12198         x = ckstrcmp(s,s2,len,docase);
12199         p = ckitoa(x);
12200         ckstrncpy(fnval,p,FNVALL);
12201         p = fnval;
12202         goto fnend;
12203     }
12204
12205 /* Note: when adding new functions remember to update dohfunc in ckuus2.c. */
12206
12207     failed = 1;
12208     if (fndiags)
12209       ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN_FUNCTION:\\f",fn,"()>",NULL);
12210
12211   fnend:
12212     /* Free temporary storage for aguments */
12213     for (k = 0; k < argn; k++) if (bp[k]) free(bp[k]);
12214     fndepth--;
12215     if (failed) {                       /* Handle failure */
12216         debug(F111,"fnend",fnval,errno);
12217         if (!p) p = "";
12218         if (p[0]) {
12219             /* In case this wasn't caught above... */
12220             k = strlen(p);
12221             if (p[0] != '<' && p[k-1] != '>') {
12222                 ckmakmsg(fnval,FNVALL,"<ERROR:BAD_ARG:\\f",fn,"()>",NULL);
12223                 p = fnval;
12224             }
12225         } else {
12226             ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN:\\f",fn,"()>",NULL);
12227             p = fnval;
12228         }
12229         if (fnerror)                    /* SET FUNCTION ERROR ON */
12230           fnsuccess = 0;                /* Make command fail (see ckuus5.c) */
12231         debug(F111,"fneval failed",p,fnsuccess);
12232         if (fndiags)                    /* SET FUNCTION DIAGNOSTICS ON */
12233           printf("?%s\n",p);            /* Print error message now. */
12234         else
12235           return("");                   /* Return nothing. */
12236     }
12237     return(p);
12238 }
12239 #endif /* NOSPL */
12240
12241 static char ckpidbuf[32] = "????";
12242
12243 #ifdef VMS
12244 _PROTOTYP(long zgpid,(void));
12245 #endif /* VMS */
12246
12247 char *
12248 ckgetpid() {                            /* Return pid as string */
12249 #ifdef CK_PID
12250 #ifdef OS2
12251 #define getpid _getpid
12252     unsigned long zz;
12253 #else
12254     long zz;
12255 #endif /* OS2 */
12256 #ifdef VMS
12257     zz = zgpid();
12258 #else
12259     zz = getpid();
12260 #endif /* VMS */
12261     sprintf(ckpidbuf,"%ld",zz);         /* SAFE */
12262 #endif /* CK_PID */
12263     return((char *)ckpidbuf);
12264 }
12265
12266 #ifndef NOSPL
12267 #define EMBUFLEN 128                    /* Error message buffer length */
12268
12269 static char embuf[EMBUFLEN+1];
12270
12271 char *                                  /* Evaluate builtin variable */
12272 nvlook(s) char *s; {
12273     int x, y, cx;
12274     long z;
12275     char *p;
12276 #ifndef NODIAL
12277     MDMINF * m;
12278 #endif /* NODIAL */
12279 #ifndef NOKVERBS                        /* Keyboard macro material */
12280     extern int keymac, keymacx;
12281 #endif /* NOKVERBS */
12282 #ifdef CK_LOGIN
12283     extern int isguest;
12284 #endif /* CK_LOGIN */
12285     if (!s) s = "";
12286     x = strlen(s);
12287     if (fndiags) {                      /* FUNCTION DIAGNOSTIC ON */
12288         if (x + 32 < EMBUFLEN)
12289           sprintf(embuf,"<ERROR:NO_SUCH_VARIABLE:\\v(%s)>",s); /* SAFE */
12290         else
12291           sprintf(embuf,"<ERROR:NO_SUCH_VARIABLE>"); /* SAFE */
12292     } else                              /* FUNCTION DIAGNOSTIC OFF */
12293       embuf[0] = NUL;
12294     x = VVBUFL;
12295     p = vvbuf;
12296     if (zzstring(s,&p,&x) < 0) {        /* e.g. for \v(\%a) */
12297         y = -1;
12298     } else {
12299         s = vvbuf;
12300         y = lookup(vartab,s,nvars,&x);
12301     }
12302     cx = y;                             /* y is too generic */
12303 #ifndef NODIAL
12304     m = (mdmtyp > 0) ? modemp[mdmtyp] : NULL; /* For \v(m_xxx) variables */
12305 #endif /* NODIAL */
12306
12307     debug(F101,"nvlook y","",y);
12308
12309     switch (y) {
12310       case VN_ARGC:                     /* ARGC */
12311         sprintf(vvbuf,"%d",maclvl < 0 ? topargc : macargc[maclvl]); /* SAFE */
12312         return(vvbuf);
12313
12314       case VN_ARGS:                     /* ARGS */
12315         sprintf(vvbuf,"%d",xargs);      /* SAFE */
12316         return(vvbuf);
12317
12318       case VN_COUN:                     /* COUNT */
12319         sprintf(vvbuf,"%d",count[cmdlvl]); /* SAFE */
12320         return(vvbuf);
12321
12322       case VN_DATE:                     /* DATE */
12323         ztime(&p);                      /* Get "asctime" string */
12324         if (p == NULL || *p == NUL) return(NULL);
12325         vvbuf[0] = p[8];                /* dd */
12326         vvbuf[1] = p[9];
12327         vvbuf[2] = SP;
12328         vvbuf[3] = p[4];                /* mmm */
12329         vvbuf[4] = p[5];
12330         vvbuf[5] = p[6];
12331         vvbuf[6] = SP;
12332         for (x = 20; x < 24; x++)       /* yyyy */
12333           vvbuf[x - 13] = p[x];
12334         vvbuf[11] = NUL;
12335         return(vvbuf);
12336
12337       case VN_NDAT:                     /* Numeric date */
12338         ckstrncpy(vvbuf,zzndate(),VVBUFL);
12339         return(vvbuf);
12340
12341       case VN_DIRE:                     /* DIRECTORY */
12342         s = zgtdir();                   /* Get current directory */
12343         if (!s)
12344 #ifdef UNIXOROSK
12345           s = "./";
12346 #else
12347 #ifdef VMS
12348           s = "[]";
12349 #else
12350           s = "";
12351 #endif /* VMS */
12352 #endif /* UNIXOROSK */
12353         ckstrncpy(vvbuf,s,VVBUFL);
12354         s = vvbuf;
12355 #ifdef UNIXOROSK
12356         x = strlen(s);
12357         if (x < VVBUFL - 1) {
12358             if (s[x-1] != '/') {
12359                 s[x] = '/';
12360                 s[x+1] = NUL;
12361             }
12362         }
12363 #endif /* UNIXOROSK */
12364         return(s);
12365
12366       case VN_FILE:                     /* filespec */
12367         return(fspec);
12368
12369       case VN_HOST:                     /* host name */
12370         if (*myhost) {                  /* If known */
12371             return(myhost);             /* return it. */
12372         } else {                        /* Otherwise */
12373             ckstrncpy(vvbuf,"unknown",VVBUFL); /* just say "unknown" */
12374             return(vvbuf);
12375         }
12376
12377       case VN_SYST:                     /* System type */
12378 #ifdef UNIX
12379         ckstrncpy(vvbuf,"UNIX",VVBUFL);
12380 #else
12381 #ifdef VMS
12382         ckstrncpy(vvbuf,"VMS",VVBUFL);
12383 #else
12384 #ifdef OSK
12385         ckstrncpy(vvbuf,"OS9/68K",VVBUFL);
12386 #else
12387 #ifdef AMIGA
12388         ckstrncpy(vvbuf,"Amiga",VVBUFL);
12389 #else
12390 #ifdef MAC
12391         ckstrncpy(vvbuf,"Macintosh",VVBUFL);
12392 #else
12393 #ifdef OS2
12394 #ifdef NT
12395         ckstrncpy(vvbuf,"WIN32",VVBUFL) ;
12396 #else /* NT */
12397         ckstrncpy(vvbuf,"OS/2",VVBUFL);
12398 #endif /* NT */
12399 #else
12400 #ifdef datageneral
12401         ckstrncpy(vvbuf,"AOS/VS",VVBUFL);
12402 #else
12403 #ifdef GEMDOS
12404         ckstrncpy(vvbuf,"Atari_ST",VVBUFL);
12405 #else
12406 #ifdef STRATUS
12407         ckstrncpy(vvbuf,"Stratus_VOS",VVBUFL);
12408 #else
12409         ckstrncpy(vvbuf,"unknown",VVBUFL);
12410 #endif /* STRATUS */
12411 #endif /* GEMDOS */
12412 #endif /* datageneral */
12413 #endif /* OS2 */
12414 #endif /* MAC */
12415 #endif /* AMIGA */
12416 #endif /* OSK */
12417 #endif /* VMS */
12418 #endif /* UNIX */
12419         return(vvbuf);
12420
12421       case VN_SYSV:                     /* System herald */
12422 #ifdef IKSD
12423 #ifdef CK_LOGIN
12424         if (inserver && isguest)
12425           return("");
12426 #endif /* CK_LOGIN */
12427 #endif /* IKSD */
12428         for (x = y = 0; x < VVBUFL; x++) {
12429             if (ckxsys[x] == SP && y == 0) continue;
12430             vvbuf[y++] = (char) ((ckxsys[x] == SP) ? '_' : ckxsys[x]);
12431         }
12432         vvbuf[y] = NUL;
12433         return(vvbuf);
12434     } /* Break up long switch statements... */
12435
12436     switch(y) {
12437       case VN_TIME:                     /* TIME. Assumes that ztime returns */
12438         ztime(&p);                      /* "Thu Feb  8 12:00:00 1990" */
12439         if (p == NULL || *p == NUL)     /* like asctime()! */
12440           return("");
12441         for (x = 11; x < 19; x++)       /* copy hh:mm:ss */
12442           vvbuf[x - 11] = p[x];         /* to vvbuf */
12443         vvbuf[8] = NUL;                 /* terminate */
12444         return(vvbuf);                  /* and return it */
12445
12446       case VN_NTIM:                     /* Numeric time */
12447         ztime(&p);                      /* "Thu Feb  8 12:00:00 1990" */
12448         if (p == NULL || *p == NUL)     /* like asctime()! */
12449           return(NULL);
12450         z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
12451         sprintf(vvbuf,"%ld",z);         /* SAFE */
12452         return(vvbuf);
12453
12454 #ifdef CK_TTYFD
12455       case VN_TTYF:                     /* TTY file descriptor */
12456         sprintf(vvbuf,"%d",             /* SAFE */
12457 #ifdef VMS
12458                 vmsttyfd()
12459 #else
12460                 ttyfd
12461 #endif /* VMS */
12462                 );
12463         return(vvbuf);
12464 #endif /* CK_TTYFD */
12465
12466       case VN_VERS:                     /* Numeric Kermit version number */
12467         sprintf(vvbuf,"%ld",vernum);    /* SAFE */
12468         return(vvbuf);
12469
12470       case VN_XVNUM:                    /* Product-specific version number */
12471         sprintf(vvbuf,"%ld",xvernum);   /* SAFE */
12472         return(vvbuf);
12473
12474       case VN_HOME:                     /* Home directory */
12475         return(homepath());
12476
12477       case VN_IBUF:                     /* INPUT buffer */
12478         return((char *)inpbuf);
12479
12480       case VN_ICHR:                     /* INPUT character */
12481         inchar[1] = NUL;
12482         return((char *)inchar);
12483
12484       case VN_ICNT:                     /* INPUT character count */
12485         sprintf(vvbuf,"%d",incount);    /* SAFE */
12486         return(vvbuf);
12487
12488       case VN_SPEE: {                   /* Transmission SPEED */
12489           long t;
12490           t = ttgspd();
12491           if (t < 0L)
12492             sprintf(vvbuf,"unknown");   /* SAFE */
12493           else
12494             sprintf(vvbuf,"%ld",t);     /* SAFE */
12495           return(vvbuf);
12496       }
12497
12498       case VN_SUCC:                     /* SUCCESS flag */
12499         /* Note inverted sense */
12500         sprintf(vvbuf,"%d",(success == 0) ? 1 : 0); /* SAFE */
12501         return(vvbuf);
12502
12503       case VN_LINE: {                   /* LINE */
12504 #ifdef DEBUG
12505           if (deblog) {
12506               debug(F111,"\\v(line) local",ttname,local);
12507               debug(F111,"\\v(line) inserver","",inserver);
12508 #ifdef TNCODE
12509               debug(F111,"\\v(line) tcp_incoming","",tcp_incoming);
12510 #endif /* TNCODE */
12511 #ifdef CK_TAPI
12512               debug(F111,"\\v(line) tttapi","",tttapi);
12513 #endif /* CK_TAPI */
12514           }
12515 #endif /* DEBUG */
12516
12517 #ifdef CK_TAPI
12518           if (tttapi) {                 /* If I have made a TAPI connection */
12519               int i;                    /* return the TAPI device name */
12520               for (i = 0; i < ntapiline; i++) {
12521                   if (!strcmp(ttname,tapilinetab[i].kwd)) {
12522                       p = _tapilinetab[i].kwd;
12523                       return(p);
12524                   }
12525               }
12526           }
12527 #endif /* CK_TAPI */
12528 #ifndef NOXFER
12529           if (inserver                  /* If I am a TCP server */
12530 #ifdef TNCODE
12531               || tcp_incoming
12532 #endif /* TNCODE */
12533               )
12534 #ifdef TCPSOCKET
12535             p = ckgetpeer();            /* return peer name */
12536           else
12537 #endif /* TCPSOCKET */
12538 #endif /* NOXFER */
12539           if (local)                    /* Otherwise if in local mode */
12540             p = (char *) ttname;        /* return SET LINE / SET HOST name */
12541           else                          /* Otherwise */
12542             p = "";                     /* return empty string */
12543           if (!p)                       /* In case ckgetpeer() returns */
12544             p = "";                     /* null pointer... */
12545           debug(F110,"\\v(line) p",p,0);
12546           if (!*p)
12547             p = (char *) ttname;
12548           return(p);
12549       }
12550       case VN_PROG:                     /* Program name */
12551         return("C-Kermit");
12552
12553     } /* Break up long switch statements... */
12554
12555     switch(y) {
12556       case VN_RET:                      /* Value of most recent RETURN */
12557         debug(F111,"\\v(return)",mrval[maclvl+1],maclvl+1);
12558         p = mrval[maclvl+1];
12559         if (p == NULL) p = "";
12560         return(p);
12561
12562       case VN_FFC:                      /* Size of most recent file */
12563         sprintf(vvbuf, "%s", ckfstoa(ffc)); /* SAFE */
12564         return(vvbuf);
12565
12566       case VN_TFC:                      /* Size of most recent file group */
12567         sprintf(vvbuf, "%s", ckfstoa(tfc)); /* SAFE */
12568         return(vvbuf);
12569
12570       case VN_CPU:                      /* CPU type */
12571 #ifdef IKSD
12572 #ifdef CK_LOGIN
12573         if (inserver && isguest)
12574           return("");
12575 #endif /* CK_LOGIN */
12576 #endif /* IKSD */
12577 #ifdef OS2
12578          {
12579             char * getcpu(void) ;
12580             return getcpu();
12581          }
12582 #else /* OS2 */
12583 #ifdef CKCPU
12584         return(CKCPU);                  /* Traditionally, compile-time value */
12585 #else
12586 #ifdef CK_UTSNAME
12587         {                               /* But if none, try runtime value */
12588             extern char unm_mch[];
12589             return((char *)unm_mch);
12590         }
12591 #else
12592         p = getenv("HOSTTYPE");         /* 20091116 */
12593         if (p) if (*p) return(p);
12594         return("unknown");
12595 #endif /* CK_UTSNAME */
12596 #endif /* CKCPU */
12597 #endif /* OS2 */
12598
12599       case VN_CMDL:                     /* Command level */
12600         sprintf(vvbuf, "%d", cmdlvl);   /* SAFE */
12601         return(vvbuf);
12602
12603       case VN_DAY:                      /* Day of week */
12604         ztime(&p);
12605         if (p != NULL && *p != NUL)     /* ztime() succeeded. */
12606           ckstrncpy(vvbuf,p,4);
12607         else
12608           vvbuf[0] = NUL;               /* ztime() failed. */
12609         return(vvbuf);                  /* Return what we got. */
12610
12611       case VN_NDAY: {                   /* Numeric day of week */
12612           long k;
12613           z = mjd(zzndate());           /* Get modified Julian date */
12614           k = (((int)(z % 7L)) + 3) % 7; /* Get day number */
12615           sprintf(vvbuf,"%ld",k);       /* SAFE */
12616           return(vvbuf);
12617       }
12618
12619       case VN_LCL:                      /* Local (vs remote) mode */
12620         ckstrncpy(vvbuf, local ? "1" : "0",VVBUFL);
12621         return(vvbuf);
12622
12623       case VN_CMDS:                     /* Command source */
12624         if (cmdstk[cmdlvl].src == CMD_KB)
12625           ckstrncpy(vvbuf,"prompt",VVBUFL);
12626         else if (cmdstk[cmdlvl].src == CMD_MD)
12627           ckstrncpy(vvbuf,"macro",VVBUFL);
12628         else if (cmdstk[cmdlvl].src == CMD_TF)
12629           ckstrncpy(vvbuf,"file",VVBUFL);
12630         else
12631           ckstrncpy(vvbuf,"unknown",VVBUFL);
12632         return(vvbuf);
12633
12634       case VN_CMDF:                     /* Current command file name */
12635 #ifdef COMMENT                          /* (see comments above) */
12636         if (tfnam[tlevel]) {            /* (near dblbs declaration) */
12637             dblbs(tfnam[tlevel],vvbuf,VVBUFL);
12638             return(vvbuf);
12639         } else return("");
12640 #else
12641         if (tlevel < 0)
12642           return("");
12643         else
12644           return(tfnam[tlevel] ? tfnam[tlevel] : "");
12645 #endif /* COMMENT */
12646
12647       case VN_MAC:                      /* Current macro name */
12648         return((maclvl > -1) ? m_arg[maclvl][0] : "");
12649
12650       case VN_EXIT:
12651         sprintf(vvbuf,"%d",xitsta);     /* SAFE */
12652         return(vvbuf);
12653
12654     } /* Break up long switch statements... */
12655
12656     switch(y) {
12657       case VN_PRTY: {                   /* Parity */
12658           char *ss;
12659           switch (parity) {
12660             case 0:   ss = "none";  break;
12661             case 'e': ss = "even";  break;
12662             case 'm': ss = "mark";  break;
12663             case 'o': ss = "odd";   break;
12664             case 's': ss = "space"; break;
12665             default:  ss = "unknown"; break;
12666           }
12667           ckstrncpy(vvbuf,ss,VVBUFL);
12668           return(vvbuf);
12669       }
12670
12671       case VN_DIAL:
12672         sprintf(vvbuf,"%d",             /* SAFE */
12673 #ifndef NODIAL
12674                 dialsta
12675 #else
12676                 -1
12677 #endif /* NODIAL */
12678                 );
12679         return(vvbuf);
12680
12681 #ifndef NODIAL
12682       case VN_DMSG:
12683 #ifdef BIGBUFOK
12684         ckstrncpy(vvbuf,
12685               ((dialsta < 0) ? "(none)" : dialmsg[dialsta]),
12686               VVBUFL); /* Safe if src == NULL */
12687 #endif  /* BIGBUFOK */
12688         return((char *)vvbuf);
12689 #endif  /* NODIAL */
12690
12691 #ifdef OS2
12692       case VN_KEYB:
12693         ckstrncpy(vvbuf,conkbg(),VVBUFL);
12694         return(vvbuf);
12695       case VN_SELCT: {
12696 #ifndef NOLOCAL
12697           const char * selection = GetSelection();
12698           return( (char *) (selection ? selection : "" )) ;
12699 #else
12700           return("");
12701 #endif /* NOLOCAL */
12702       }
12703 #endif /* OS2 */
12704
12705 #ifndef NOXFER
12706       case VN_CPS:
12707         sprintf(vvbuf,"%ld",tfcps);     /* SAFE */
12708         return(vvbuf);
12709 #endif /* NOXFER */
12710
12711       case VN_MODE:                     /* File transfer mode */
12712         switch (binary) {
12713           case XYFT_T: ckstrncpy(vvbuf,"text",VVBUFL); break;
12714           case XYFT_B:
12715           case XYFT_U: ckstrncpy(vvbuf,"binary",VVBUFL); break;
12716           case XYFT_I: ckstrncpy(vvbuf,"image",VVBUFL); break;
12717           case XYFT_L: ckstrncpy(vvbuf,"labeled",VVBUFL); break;
12718           case XYFT_M: ckstrncpy(vvbuf,"macbinary",VVBUFL); break;
12719           default:     ckstrncpy(vvbuf,"unknown",VVBUFL);
12720         }
12721         return(vvbuf);
12722
12723 #ifdef CK_REXX
12724       case VN_REXX:
12725         return(rexxbuf);
12726 #endif /* CK_REXX */
12727
12728       case VN_NEWL:                     /* System newline char or sequence */
12729 #ifdef UNIX
12730         ckstrncpy(vvbuf,"\n",VVBUFL);
12731 #else
12732 #ifdef datageneral
12733         ckstrncpy(vvbuf,"\n",VVBUFL);
12734 #else
12735 #ifdef OSK
12736         ckstrncpy(vvbuf,"\15",VVBUFL);  /* Remember, these are octal... */
12737 #else
12738 #ifdef MAC
12739         ckstrncpy(vvbuf,"\15",VVBUFL);
12740 #else
12741 #ifdef OS2
12742         ckstrncpy(vvbuf,"\15\12",VVBUFL);
12743 #else
12744 #ifdef STRATUS
12745         ckstrncpy(vvbuf,"\n",VVBUFL);
12746 #else
12747 #ifdef VMS
12748         ckstrncpy(vvbuf,"\15\12",VVBUFL);
12749 #else
12750 #ifdef AMIGA
12751         ckstrncpy(vvbuf,"\n",VVBUFL);
12752 #else
12753 #ifdef GEMDOS
12754         ckstrncpy(vvbuf,"\n",VVBUFL);
12755 #else
12756         ckstrncpy(vvbuf,"\n",VVBUFL);
12757 #endif /* GEMDOS */
12758 #endif /* AMIGA */
12759 #endif /* VMS */
12760 #endif /* STRATUS */
12761 #endif /* OS2 */
12762 #endif /* MAC */
12763 #endif /* OSK */
12764 #endif /* datageneral */
12765 #endif /* UNIX */
12766         return(vvbuf);
12767
12768       case VN_ROWS:                     /* ROWS */
12769       case VN_COLS:                     /* COLS */
12770         ckstrncpy(vvbuf,(cx == VN_ROWS) ? "24" : "80",VVBUFL); /* Default */
12771 #ifdef CK_TTGWSIZ
12772 #ifdef OS2
12773         if (tt_cols[VTERM] < 0 || tt_rows[VTERM] < 0)
12774           ttgwsiz();
12775         sprintf(vvbuf,"%d",             /* SAFE */
12776                 (cx == VN_ROWS) ? tt_rows[VTERM] : tt_cols[VTERM]);
12777 #else /* OS2 */
12778         if (ttgwsiz() > 0)              /* Get window size */
12779           if (tt_cols > 0 && tt_rows > 0) /* sets tt_rows, tt_cols */
12780             sprintf(vvbuf,"%d",         /* SAFE */
12781                     (cx == VN_ROWS) ? tt_rows : tt_cols);
12782 #endif /* OS2 */
12783 #endif /* CK_TTGWSIZ */
12784         return(vvbuf);
12785
12786       case VN_TTYP:
12787 #ifdef NOTERM
12788         ckstrncpy(vvbuf,"unknown",VVBUFL);
12789 #else
12790 #ifdef OS2
12791         sprintf(vvbuf, "%s",            /* SAFE */
12792                 (tt_type >= 0 && tt_type <= max_tt) ?
12793                 tt_info[tt_type].x_name :
12794                 "unknown"
12795                 );
12796 #else
12797 #ifdef MAC
12798         ckstrncpy(vvbuf,"vt320",VVBUFL);
12799 #else
12800         p = getenv("TERM");
12801         ckstrncpy(vvbuf,p ? p : "unknown",VVBUFL+1);
12802 #endif /* MAC */
12803 #endif /* OS2 */
12804 #endif /* NOTERM */
12805         return(vvbuf);
12806
12807       case VN_MINP:                     /* MINPUT */
12808         sprintf(vvbuf, "%d", m_found);  /* SAFE */
12809         return(vvbuf);
12810     } /* Break up long switch statements... */
12811
12812     switch(y) {
12813       case VN_CONN:                     /* CONNECTION */
12814         if (!local) {
12815           ckstrncpy(vvbuf,"remote",VVBUFL);
12816         } else {
12817             if (!network)
12818               ckstrncpy(vvbuf,"serial",VVBUFL);
12819 #ifdef TCPSOCKET
12820             else if (nettype == NET_TCPB || nettype == NET_TCPA) {
12821                 if (ttnproto == NP_TELNET)
12822                   ckstrncpy(vvbuf,"tcp/ip_telnet",VVBUFL);
12823 #ifdef CK_SSL
12824                 else if (ttnproto == NP_SSL || ttnproto == NP_SSL_RAW)
12825                   ckstrncpy(vvbuf,"tcp/ip_ssl",VVBUFL);
12826                 else if (ttnproto == NP_TLS || ttnproto == NP_SSL_RAW)
12827                   ckstrncpy(vvbuf,"tcp/ip_tls",VVBUFL);
12828 #endif /* CK_SSL */
12829                 else
12830                   ckstrncpy(vvbuf,"tcp/ip",VVBUFL);
12831             }
12832 #endif /* TCPSOCKET */
12833 #ifdef SSHBUILTIN
12834             else if (nettype == NET_SSH)
12835                   ckstrncpy(vvbuf,"tcp/ip_ssh",VVBUFL);
12836 #endif /* SSHBUILTIN */
12837 #ifdef ANYX25
12838             else if (nettype == NET_SX25 ||
12839                      nettype == NET_VX25 ||
12840                      nettype == NET_IX25
12841                      )
12842               ckstrncpy(vvbuf,"x.25",VVBUFL);
12843 #endif /* ANYX25 */
12844 #ifdef DECNET
12845             else if (nettype == NET_DEC) {
12846                 if (ttnproto == NP_LAT)
12847                   ckstrncpy(vvbuf,"decnet_lat",VVBUFL);
12848                 else if ( ttnproto == NP_CTERM )
12849                   ckstrncpy(vvbuf,"decnet_cterm",VVBUFL);
12850                 else
12851                   ckstrncpy(vvbuf,"decnet",VVBUFL);
12852             }
12853 #endif /* DECNET */
12854 #ifdef SUPERLAT
12855             else if (nettype == NET_SLAT)
12856               ckstrncpy(vvbuf,"superlat",VVBUFL);
12857 #endif /* SUPERLAT */
12858 #ifdef NETFILE
12859             else if (nettype == NET_FILE)
12860               ckstrncpy(vvbuf,"local_file",VVBUFL);
12861 #endif /* NETFILE */
12862 #ifdef NETCMD
12863             else if (nettype == NET_CMD)
12864               ckstrncpy(vvbuf,"pipe",VVBUFL);
12865 #endif /* NETCMD */
12866 #ifdef NETPTY
12867             else if (nettype == NET_PTY)
12868               ckstrncpy(vvbuf,"pseudoterminal",VVBUFL);
12869 #endif /* NETPTY */
12870 #ifdef NETDLL
12871             else if (nettype == NET_DLL)
12872               ckstrncpy(vvbuf,"dynamic_link_library",VVBUFL);
12873 #endif /* NETDLL */
12874
12875 #ifdef NPIPE
12876             else if (nettype == NET_PIPE)
12877               ckstrncpy(vvbuf,"named_pipe",VVBUFL);
12878 #endif /* NPIPE */
12879 #ifdef CK_NETBIOS
12880             else if (nettype == NET_BIOS)
12881               ckstrncpy(vvbuf,"netbios",VVBUFL);
12882 #endif /* CK_NETBIOS */
12883             else
12884               ckstrncpy(vvbuf,"unknown",VVBUFL);
12885         }
12886         return(vvbuf);
12887
12888 #ifndef NOXFER
12889       case VN_SYSI:                     /* System ID, Kermit code */
12890         return((char *)cksysid);
12891 #endif /* NOXFER */
12892
12893 #ifdef OS2
12894       case VN_SPA: {
12895           unsigned long space = zdskspace(0);
12896           if (space > 0 && space < 1024)
12897             sprintf(vvbuf,"-1");
12898           else
12899             sprintf(vvbuf,"%lu",space); /* SAFE */
12900           return(vvbuf);
12901       }
12902 #endif /* OS2 */
12903
12904 #ifndef NOXFER
12905       case VN_QUE: {
12906           extern char querybuf[];
12907           return(querybuf);
12908       }
12909 #endif /* NOXFER */
12910
12911 #ifndef NOCSETS
12912       case VN_CSET:
12913 #ifdef OS2
12914         sprintf(vvbuf,"cp%d",os2getcp()); /* SAFE */
12915 #else
12916         ckstrncpy(vvbuf,fcsinfo[fcharset].keyword,VVBUFL+1);
12917 #endif /* OS2 */
12918         return(vvbuf);
12919 #endif /* NOCSETS */
12920
12921 #ifdef OS2
12922       case VN_EXEDIR:
12923         return(exedir);
12924       case VN_STAR:
12925         return(startupdir);
12926 #else
12927       case VN_EXEDIR:
12928         return(exedir ? exedir : "");
12929 #ifdef VMSORUNIX
12930       case VN_STAR:
12931         return(startupdir);
12932 #endif /* VMSORUNIX */
12933 #endif /* OS2 */
12934
12935       case VN_INI:
12936         return(inidir);
12937
12938       case VN_MDM:
12939         return(gmdmtyp());
12940
12941       case VN_EVAL:
12942         return(evalbuf);
12943
12944 #ifndef NODIAL
12945       case VN_D_CC:                     /* DIAL COUNTRY-CODE */
12946         return(diallcc ? diallcc : "");
12947
12948       case VN_D_AC:                     /* DIAL AREA-CODE */
12949         return(diallac ? diallac : "");
12950
12951       case VN_D_IP:                     /* DIAL INTERNATIONAL-PREFIX */
12952         return(dialixp ? dialixp : "");
12953
12954       case VN_D_LP:                     /* DIAL LD-PREFIX */
12955         return(dialldp ? dialldp : "");
12956
12957       case VN_D_LCP:                    /* DIAL LOCAL-PREFIX */
12958         return(diallcp ? diallcp : "");
12959
12960       case VN_D_PXX:                    /* DIAL PBX-EXCHANGE that matched */
12961         return(matchpxx ? matchpxx : "");
12962 #else
12963       case VN_D_CC:                     /* DIAL COUNTRY-CODE */
12964       case VN_D_AC:                     /* DIAL AREA-CODE */
12965       case VN_D_IP:                     /* DIAL INTERNATIONAL-PREFIX */
12966       case VN_D_LP:                     /* DIAL LD-PREFIX */
12967       case VN_D_LCP:                    /* DIAL LOCAL-PREFIX */
12968       case VN_D_PXX:                    /* DIAL PBX-EXCHANGE */
12969         return("");
12970 #endif /* NODIAL */
12971       case VN_UID:
12972 #ifdef UNIX
12973         {
12974 #ifdef IKSD
12975             if (inserver)
12976               return((char *)uidbuf);
12977             else
12978 #endif /* IKSD */
12979               if (uidbuf[0])
12980                 return((char *)uidbuf);
12981               else
12982                 return(whoami());
12983         }
12984 #else
12985         return((char *)uidbuf);
12986 #endif /* UNIX */
12987     } /* Break up long switch statements... */
12988
12989     switch(y) {
12990       case VN_PWD:
12991 #ifdef OS2
12992         if (activecmd == XXOUT || activecmd == XXLNOUT) {
12993             ckstrncpy(vvbuf,pwbuf,VVBUFL);
12994             ck_encrypt((char *)vvbuf);
12995             return((char *)vvbuf);
12996         } else
12997 #endif /* OS2 */
12998           return((char *)pwbuf);
12999
13000       case VN_PRM:
13001         return((char *)prmbuf);
13002
13003       case VN_PROTO:
13004 #ifdef NOXFER
13005         return("none");
13006 #else
13007 #ifdef CK_XYZ
13008         return(ptab[protocol].p_name);
13009 #else
13010         return("kermit");
13011 #endif /* CK_XYZ */
13012 #endif /* NOXFER */
13013
13014 #ifndef NOXFER
13015 #ifdef CK_TMPDIR
13016       case VN_DLDIR:
13017         return(dldir ? dldir : "");
13018 #endif /* CK_TMPDIR */
13019 #endif /* NOXFER */
13020
13021 #ifndef NODIAL
13022       case VN_M_INI:                    /* Modem init string */
13023         return(dialini ? dialini : (m ? m->wake_str : ""));
13024
13025       case VN_M_DCM:                    /* Modem dial command */
13026         return(dialcmd ? dialcmd : (m ? m->dial_str : ""));
13027
13028       case VN_M_DCO:                    /* Modem data compression on */
13029         return(dialdcon ? dialdcon : (m ? m->dc_on_str : ""));
13030
13031       case VN_M_DCX:                    /* Modem data compression off */
13032         return(dialdcoff ? dialdcoff : (m ? m->dc_off_str : ""));
13033
13034       case VN_M_ECO:                    /* Modem error correction on */
13035         return(dialecon ? dialecon : (m ? m->ec_on_str : ""));
13036
13037       case VN_M_ECX:                    /* Modem error correction off */
13038         return(dialecoff ? dialecoff : (m ? m->ec_off_str : ""));
13039
13040       case VN_M_AAO:                    /* Modem autoanswer on */
13041         return(dialaaon ? dialaaon : (m ? m->aa_on_str : ""));
13042
13043       case VN_M_AAX:                    /* Modem autoanswer off */
13044         return(dialaaoff ? dialaaoff : (m ? m->aa_off_str : ""));
13045
13046       case VN_M_HUP:                    /* Modem hangup command */
13047         return(dialhcmd ? dialhcmd : (m ? m->hup_str : ""));
13048
13049       case VN_M_HWF:                    /* Modem hardware flow command */
13050         return(dialhwfc ? dialhwfc : (m ? m->hwfc_str : ""));
13051
13052       case VN_M_SWF:                    /* Modem software flow command */
13053         return(dialswfc ? dialswfc : (m ? m->swfc_str : ""));
13054
13055       case VN_M_NFC:                    /* Modem no flow-control command */
13056         return(dialnofc ? dialnofc : (m ? m->nofc_str : ""));
13057
13058       case VN_M_PDM:                    /* Modem pulse dialing mode */
13059         return(dialpulse ? dialpulse : (m ? m->pulse : ""));
13060
13061       case VN_M_TDM:                    /* Modem tone dialing mode */
13062         return(dialtone ? dialtone : (m ? m->tone : ""));
13063
13064       case VN_M_NAM:                    /* Modem full name */
13065         return(dialname ? dialname : (m ? m->name : ""));
13066 #else
13067       case VN_M_INI:                    /* Modem init string */
13068       case VN_M_DCM:                    /* Modem dial command */
13069       case VN_M_DCO:                    /* Modem data compression on */
13070       case VN_M_DCX:                    /* Modem data compression off */
13071       case VN_M_ECO:                    /* Modem error correction on */
13072       case VN_M_ECX:                    /* Modem error correction off */
13073       case VN_M_AAO:                    /* Modem autoanswer on */
13074       case VN_M_AAX:                    /* Modem autoanswer off */
13075       case VN_M_HUP:                    /* Modem hangup command */
13076       case VN_M_HWF:                    /* Modem hardware flow command */
13077       case VN_M_SWF:                    /* Modem software flow command */
13078       case VN_M_NFC:                    /* Modem no flow-control command */
13079       case VN_M_PDM:                    /* Modem pulse dialing mode */
13080       case VN_M_TDM:                    /* Modem tone dialing mode */
13081       case VN_M_NAM:
13082         return("");
13083 #endif /* NODIAL */
13084
13085       case VN_ISTAT:                    /* INPUT status */
13086         sprintf(vvbuf, "%d", instatus); /* SAFE */
13087         return(vvbuf);
13088
13089       case VN_TEMP:                     /* Temporary directory */
13090         if (tempdir) {
13091             p = tempdir;
13092         } else {
13093 #ifdef OS2
13094 #ifdef NT
13095             p = getenv("K95TMP");
13096 #else
13097             p = getenv("K2TMP");
13098 #endif /* NT */
13099             if ( !p )
13100 #endif /* OS2 */
13101               p = getenv("CK_TMP");
13102             if (!p) p = getenv("TMPDIR");
13103             if (!p) p = getenv("TEMP");
13104             if (!p) p = getenv("TMP");
13105
13106 #ifdef OS2ORUNIX
13107             if (p) {
13108                 int len = strlen(p);
13109                 if (p[len-1] != '/'
13110 #ifdef OS2
13111                     && p[len-1] != '\\'
13112 #endif /* OS2 */
13113                      ) {
13114                     static char foo[CKMAXPATH];
13115                     ckstrncpy(foo,p,CKMAXPATH);
13116                     ckstrncat(foo,"/",CKMAXPATH);
13117                     p = foo;
13118                 }
13119             } else
13120 #else /* OS2ORUNIX */
13121             if (!p)
13122 #endif /* OS2ORUNIX */
13123 #ifdef UNIX                             /* Systems that have a standard */
13124               p = "/tmp/";              /* temporary directory... */
13125 #else
13126 #ifdef datageneral
13127               p = ":TMP:";
13128 #else
13129               p = "";
13130 #endif /* datageneral */
13131 #endif /* UNIX */
13132         }
13133         ckstrncpy(vvbuf,p,VVBUFL);
13134         p = vvbuf;
13135
13136 /* This needs generalizing for VOS, AOS/VS, etc... */
13137
13138         while (*p) {
13139 #ifdef OS2
13140             if (*p == '\\') *p = '/';
13141 #endif /* OS2 */
13142             p++;
13143         }
13144 #ifndef VMS
13145         if (p > vvbuf) {
13146             char c =                    /* Directory termination character */
13147 #ifdef MAC
13148               ':'
13149 #else
13150 #ifdef datageneral
13151               ':'
13152 #else
13153 #ifdef STRATUS
13154               '>'
13155 #else
13156               '/'
13157 #endif /* STRATUS */
13158 #endif /* datageneral */
13159 #endif /* MAC */
13160                 ;
13161
13162             if (*(p-1) != c) {
13163                 *p++ = c;
13164                 *p = NUL;
13165             }
13166         }
13167 #endif /* VMS */
13168         return(vvbuf);
13169     } /* Break up long switch statements... */
13170
13171     switch(y) {
13172       case VN_ERRNO:                    /* Error number */
13173 #ifdef VMS
13174         {
13175             extern int vms_lasterr;
13176             sprintf(vvbuf, "%d", vms_lasterr); /* SAFE */
13177         }
13178 #else
13179         sprintf(vvbuf, "%d", errno);    /* SAFE */
13180 #endif /* VMS */
13181         return(vvbuf);
13182
13183       case VN_ERSTR:                    /* Error string */
13184         ckstrncpy(vvbuf,ck_errstr(),VVBUFL);
13185         return(vvbuf);
13186
13187 #ifndef NOXFER
13188       case VN_RPSIZ:                    /* RECEIVE packet-length */
13189         sprintf(vvbuf,"%d",urpsiz);     /* SAFE */
13190         return(vvbuf);
13191
13192       case VN_WINDO:                    /* WINDOW slots */
13193         sprintf(vvbuf,"%d",wslotr);     /* SAFE */
13194         return(vvbuf);
13195 #endif /* NOXFER */
13196
13197       case VN_TFLN:                     /* TAKE-file line number */
13198         if (tlevel > -1) {
13199             sprintf(vvbuf, "%d", tfline[tlevel]); /* SAFE */
13200             return(vvbuf);
13201         } else
13202           return("0");
13203
13204       case VN_MDMSG:                    /* DIALRESULT */
13205 #ifndef NODIAL
13206         return((char *)modemmsg);
13207 #else
13208         return("");
13209 #endif /* NODIAL */
13210
13211       case VN_DNUM:                     /* DIALNUMBER */
13212 #ifndef NODIAL
13213         return(dialnum ? (char *) dialnum : "");
13214 #else
13215         return("");
13216 #endif /* NODIAL */
13217
13218       case VN_APC:
13219         sprintf(vvbuf, "%d",            /* SAFE */
13220 #ifdef CK_APC
13221                 apcactive
13222 #else
13223                 0
13224 #endif /* CK_APC */
13225                 );
13226         return((char *)vvbuf);
13227
13228 #ifdef OS2
13229 #ifndef NOKVERBS
13230       case VN_TRMK:
13231           sprintf(vvbuf, "%d", keymac); /* SAFE */
13232         return((char *)vvbuf);
13233 #endif /* NOKVERBS */
13234 #endif /* OS2 */
13235
13236       case VN_IPADDR:
13237 #ifdef TCPSOCKET
13238 #ifndef OSK
13239       /* This dumps core on OS-9 for some reason, but only if executed */
13240       /* before we have made a TCP connection.  This is obviously not */
13241       /* the ideal fix. */
13242         if (!myipaddr[0])
13243           getlocalipaddr();
13244 #endif /* OSK */
13245 #endif /* TCPSOCKET */
13246         ckstrncpy(vvbuf,
13247 #ifdef TCPSOCKET
13248                 (char *)myipaddr,
13249 #else
13250                 "",
13251 #endif /* TCPSOCKET */
13252                 VVBUFL);
13253         return((char *)vvbuf);
13254
13255 #ifndef NOXFER
13256       case VN_CRC16:                    /* CRC-16 of most recent transfer */
13257         sprintf(vvbuf,"%d",crc16);      /* SAFE */
13258         return(vvbuf);
13259 #endif /* NOXFER */
13260
13261 #ifdef CK_PID
13262       case VN_PID:
13263 #ifdef IKSD
13264 #ifdef CK_LOGIN
13265         if (inserver && isguest)
13266           return("");
13267 #endif /* CK_LOGIN */
13268 #endif /* IKSD */
13269         return(ckgetpid());
13270 #endif /* CK_PID */
13271
13272 #ifndef NOXFER
13273       case VN_FNAM: {                   /* \v(filename) */
13274           extern char filnam[], ofn1[], *sfspec, *rrfspec;
13275           char * tmp;
13276           switch (what) {               /* File transfer is in progress */
13277 #ifdef NEWFTP
13278             case (W_FTP|W_RECV):
13279             case (W_FTP|W_SEND):
13280               return((char *)filnam);
13281 #endif /* NEWFTP */
13282             case W_RECV:
13283             case W_REMO:
13284               return((char *)ofn1);
13285             default:                    /* Most recent file transferred */
13286               if (filnam[0]) {          /* (if any) */
13287                   return((char *)filnam);
13288               } else if (lastxfer & W_SEND && sfspec) {
13289                   if (fnspath == PATH_OFF)
13290                     zstrip(sfspec,&tmp);
13291                   else
13292                     tmp = sfspec;
13293                   return(tmp);
13294               } else if (lastxfer & W_RECV && rrfspec) {
13295                   if (fnrpath == PATH_OFF)
13296                     zstrip(rrfspec,&tmp);
13297                   else
13298                     tmp = rrfspec;
13299                   return(tmp);
13300               } else
13301                 return("");
13302           }
13303       }
13304       case VN_FNUM:                     /* \v(filenum) */
13305         sprintf(vvbuf,"%ld",filcnt);    /* SAFE */
13306         return((char *)vvbuf);
13307 #endif /* NOXFER */
13308
13309 #ifdef PEXITSTAT
13310       case VN_PEXIT: {
13311           extern int pexitstat;
13312           sprintf(vvbuf,"%d",pexitstat); /* SAFE */
13313           return((char *)vvbuf);
13314       }
13315 #endif /* PEXITSTAT */
13316
13317 #ifndef NOXFER
13318       case VN_P_8BIT:
13319         vvbuf[0] = parity ? ebq : NUL;
13320         vvbuf[1] = NUL;
13321         return((char *)vvbuf);
13322
13323       case VN_P_CTL: {
13324           extern CHAR myctlq;
13325           vvbuf[0] = myctlq;
13326           vvbuf[1] = NUL;
13327           return((char *)vvbuf);
13328       }
13329       case VN_P_RPT: {
13330           extern int rptena;
13331           vvbuf[0] = rptena ? rptq : NUL;
13332           vvbuf[1] = NUL;
13333           return((char *)vvbuf);
13334       }
13335 #endif /* NOXFER */
13336
13337 #ifdef OS2
13338       case VN_REGN:
13339         return(get_reg_name());
13340       case VN_REGO:
13341         return(get_reg_corp());
13342       case VN_REGS:
13343         return(get_reg_sn());
13344 #endif /* OS2 */
13345     } /* Break up long switch statements... */
13346
13347     switch(y) {
13348       case VN_XPROG:
13349 #ifdef OS2
13350 #ifdef NT
13351 #ifdef KUI
13352         return("K-95G");
13353 #else
13354         return("K-95");
13355 #endif /* KUI */
13356 #else
13357         return("K/2");
13358 #endif /* NT */
13359 #else
13360         return("C-Kermit");
13361 #endif /* OS2 */
13362
13363       case VN_EDITOR:
13364 #ifdef NOFRILLS
13365         return("");
13366 #else
13367 #ifdef NOPUSH
13368         return("");
13369 #else
13370         {
13371             extern char editor[];
13372             char *ss;
13373             if (!editor[0]) {
13374                 ss = getenv("EDITOR");
13375                 if (ss) {
13376                     ckstrncpy(editor,ss,CKMAXPATH);
13377                 }
13378             }
13379             debug(F110,"\\v(editor)",editor,0);
13380             return(editor[0] ? (char *)editor : "");
13381         }
13382 #endif /* NOPUSH */
13383 #endif /* NOFRILLS */
13384
13385       case VN_EDOPT:
13386 #ifdef NOFRILLS
13387         return("");
13388 #else
13389 #ifdef NOPUSH
13390         return("");
13391 #else
13392         {
13393             extern char editopts[];
13394             return(editopts[0] ? (char *)editopts : "");
13395         }
13396 #endif /* NOPUSH */
13397 #endif /* NOFRILLS */
13398
13399       case VN_EDFILE:
13400 #ifdef NOFRILLS
13401         return("");
13402 #else
13403 #ifdef NOPUSH
13404         return("");
13405 #else
13406         {
13407             extern char editfile[];
13408             return(editfile[0] ? (char *)editfile : "");
13409         }
13410 #endif /* NOPUSH */
13411 #endif /* NOFRILLS */
13412
13413 #ifdef BROWSER
13414       case VN_BROWSR: {
13415           extern char browser[];
13416           if (!browser[0]) {
13417               s = getenv("BROWSER");
13418               if (s) ckstrncpy(browser,s,CKMAXPATH);
13419           }
13420           return(browser[0] ? (char *)browser : "");
13421       }
13422       case VN_BROPT: {
13423           extern char browsopts[];
13424           return(browsopts[0] ? (char *)browsopts : "");
13425       }
13426       case VN_URL: {
13427           extern char browsurl[];
13428           return(browsurl[0] ? (char *)browsurl : "");
13429       }
13430 #endif /* BROWSER */
13431       case VN_HERALD:
13432         return((char *)versio);
13433
13434       case VN_TEST: {                   /* test */
13435           extern char * ck_s_test, * ck_s_tver;
13436           if (!ck_s_test) ck_s_test = "";
13437           if (!ck_s_tver) ck_s_tver = "";
13438           if (*ck_s_test) {
13439               ckstrncpy(vvbuf,ck_s_test,VVBUFL);
13440               if (*ck_s_tver) {
13441                   ckstrncat(vvbuf,".",VVBUFL);
13442                   ckstrncat(vvbuf,ck_s_tver,VVBUFL);
13443               }
13444           } else
13445             ckstrncpy(vvbuf,"0",VVBUFL);
13446           return((char *)vvbuf);
13447       }
13448
13449 #ifndef NOXFER
13450       case VN_XFSTAT:                   /* xferstatus */
13451         x = xferstat;                   /* Like success */
13452         if (x > -1) x = (x == 0) ? 1 : 0; /* External value is reversed */
13453         sprintf(vvbuf,"%d",x);          /* SAFE */
13454         return((char *)vvbuf);
13455
13456       case VN_XFMSG:                    /* xfermsg */
13457         return((char *)epktmsg);
13458
13459 #ifndef NOMSEND
13460       case VN_SNDL: {                   /* sendlist */
13461           extern int filesinlist;
13462           sprintf(vvbuf,"%d",filesinlist); /* SAFE */
13463           return((char *)vvbuf);
13464       }
13465 #endif /* NOMSEND */
13466 #endif /* NOXFER */
13467
13468 #ifdef CK_TRIGGER
13469       case VN_TRIG: {
13470           extern char * triggerval;
13471           return(triggerval ? triggerval : "");
13472       }
13473 #endif /* CK_TRIGGER */
13474 #ifdef OS2MOUSE
13475 #ifdef OS2
13476       case VN_MOU_X: {
13477           extern int MouseCurX;
13478           sprintf(vvbuf,"%d",MouseCurX); /* SAFE */
13479           return((char *)vvbuf);
13480       }
13481       case VN_MOU_Y: {
13482           extern int MouseCurY;
13483           sprintf(vvbuf,"%d",MouseCurY); /* SAFE */
13484           return((char *)vvbuf);
13485       }
13486 #endif /* OS2 */
13487 #endif /* OS2MOUSE */
13488       case VN_PRINT: {
13489           extern int printpipe;
13490           extern char * printername;
13491 #ifdef PRINTSWI
13492           extern int noprinter;
13493           if (noprinter) return("");
13494 #endif /* PRINTSWI */
13495           ckmakmsg(vvbuf,VVBUFL,
13496                    printpipe ? "|" : "",
13497                    printername ? printername :
13498 #ifdef OS2
13499                    "PRN",
13500 #else
13501                    "(default)",
13502 #endif /* OS2 */
13503                    NULL,
13504                    NULL
13505                    );
13506           return((char *)vvbuf);
13507       }
13508     } /* Break up long switch statements... */
13509
13510     switch(y) {
13511       case VN_ESC:                      /* Escape character */
13512         sprintf(vvbuf,"%d",escape);     /* SAFE */
13513         return((char *)vvbuf);
13514
13515       case VN_INTIME:
13516         sprintf(vvbuf,"%ld",inetime);   /* SAFE */
13517         return((char *)vvbuf);
13518
13519       case VN_INTMO:
13520         sprintf(vvbuf,"%d",inwait);     /* SAFE */
13521         return((char *)vvbuf);
13522
13523       case VN_SECURE:
13524         if (0
13525 #ifdef SSHBUILTIN
13526             || IS_SSH()
13527 #endif /* SSHBUILTIN */
13528 #ifdef CK_ENCRYPTION
13529             || ck_tn_encrypting() && ck_tn_decrypting()
13530 #endif /* CK_ENCRYPTION */
13531 #ifdef CK_SSL
13532             || tls_active_flag || ssl_active_flag
13533 #endif /* CK_SSL */
13534             )
13535           return("1");
13536         else
13537           return("0");
13538
13539       case VN_AUTHN:
13540 #ifdef CK_AUTHENTICATION
13541         {
13542             extern char szUserNameAuthenticated[];
13543             return((char *)szUserNameAuthenticated);
13544         }
13545 #else /* CK_AUTHENTICATION */
13546         return((char *)"");
13547 #endif /* CK_AUTHENTICATION */
13548
13549       case VN_AUTHS:
13550 #ifdef CK_AUTHENTICATION
13551         switch (ck_tn_auth_valid()) {
13552           case AUTH_UNKNOWN:
13553             return((char *)"unknown");
13554           case AUTH_OTHER:
13555             return((char *)"other");
13556           case AUTH_USER:
13557             return((char *)"user");
13558           case AUTH_VALID:
13559             return((char *)"valid");
13560           case AUTH_REJECT:
13561           default:
13562             return((char *)"rejected");
13563         }
13564 #else /* CK_AUTHENTICATION */
13565         return((char *)"rejected");
13566 #endif /* CK_AUTHENTICATION */
13567
13568       case VN_AUTHT:
13569 #ifdef CK_AUTHENTICATION
13570 #ifdef CK_SSL
13571         if ((ssl_active_flag || tls_active_flag) &&
13572             ck_tn_auth_valid() == AUTH_VALID &&
13573             (sstelnet ? (!TELOPT_U(TELOPT_AUTHENTICATION)) :
13574                         (!TELOPT_ME(TELOPT_AUTHENTICATION))) ||
13575              ck_tn_authenticated() == AUTHTYPE_NULL ||
13576              ck_tn_authenticated() == AUTHTYPE_AUTO)
13577           return("X_509_CERTIFICATE");
13578         else
13579 #endif /* CK_SSL */
13580           return(AUTHTYPE_NAME(ck_tn_authenticated()));
13581 #else /* CK_AUTHENTICATION */
13582         return((char *)"NULL");
13583 #endif /* CK_AUTHENTICATION */
13584
13585 #ifdef CK_KERBEROS
13586       case VN_K4PRN: {
13587           extern char * krb4_d_principal;
13588           if (krb4_d_principal)
13589             ckstrncpy(vvbuf,krb4_d_principal,VVBUFL+1);
13590           else
13591             *vvbuf = NUL;
13592           return((char *)vvbuf);
13593       }
13594       case VN_K5PRN: {
13595           extern char * krb5_d_principal;
13596           if (krb5_d_principal)
13597             ckstrncpy(vvbuf,krb5_d_principal,VVBUFL+1);
13598           else
13599             *vvbuf = NUL;
13600           return((char *)vvbuf);
13601       }
13602       case VN_K4RLM: {
13603           extern char * krb4_d_realm;
13604           if (krb4_d_realm) {
13605               ckstrncpy(vvbuf,krb4_d_realm,VVBUFL+1);
13606           } else {
13607               char * s = ck_krb4_getrealm();
13608               ckstrncpy(vvbuf,s ? s : "",VVBUFL+1);
13609           }
13610           return((char *)vvbuf);
13611       }
13612       case VN_K4SRV: {
13613           extern char * krb4_d_srv;
13614           if (krb4_d_srv)
13615             ckstrncpy(vvbuf,krb4_d_srv,VVBUFL+1);
13616           else
13617             ckstrncpy(vvbuf,"rcmd",VVBUFL);
13618           return((char *)vvbuf);
13619       }
13620       case VN_K5RLM: {
13621           extern char * krb5_d_realm;
13622           extern char * krb5_d_cc;
13623           if (krb5_d_realm) {
13624               ckstrncpy(vvbuf,krb5_d_realm,VVBUFL+1);
13625           } else {
13626               char * s = ck_krb5_getrealm(krb5_d_cc);
13627               ckstrncpy(vvbuf,s,VVBUFL+1);
13628           }
13629           return((char *)vvbuf);
13630       }
13631       case VN_K5CC: {
13632           extern char * krb5_d_cc;
13633           if (krb5_d_cc)
13634             ckstrncpy(vvbuf,krb5_d_cc,VVBUFL+1);
13635           else
13636             ckstrncpy(vvbuf,ck_krb5_get_cc_name(),VVBUFL+1);
13637           return((char *)vvbuf);
13638       }
13639       case VN_K5SRV: {
13640           extern char * krb5_d_srv;
13641           if (krb5_d_srv)
13642             ckstrncpy(vvbuf,krb5_d_srv,VVBUFL+1);
13643           else
13644             ckstrncpy(vvbuf,"host",VVBUFL);
13645           return((char *)vvbuf);
13646       }
13647       case VN_K4ENO: {
13648         extern int krb4_errno;
13649         sprintf(vvbuf,"%d",krb4_errno); /* SAFE */
13650         return((char *)vvbuf);
13651       }
13652       case VN_K5ENO: {
13653         extern int krb5_errno;
13654         sprintf(vvbuf,"%d",krb5_errno); /* SAFE */
13655         return((char *)vvbuf);
13656       }
13657       case VN_K4EMSG: {
13658         extern char * krb4_errmsg;
13659         ckstrncpy(vvbuf,krb4_errmsg?krb4_errmsg:"",VVBUFL+1);
13660         return((char *)vvbuf);
13661       }
13662       case VN_K5EMSG: {
13663         extern char * krb5_errmsg;
13664         ckstrncpy(vvbuf,krb5_errmsg,VVBUFL+1);
13665         return((char *)vvbuf);
13666       }
13667 #endif /* CK_KERBEROS */
13668 #ifdef CK_SSL
13669       case VN_X509_S:
13670         if (ssl_active_flag)
13671           ckstrncpy(vvbuf,ssl_get_subject_name(ssl_con),VVBUFL+1);
13672         else if (tls_active_flag)
13673           ckstrncpy(vvbuf,ssl_get_subject_name(tls_con),VVBUFL+1);
13674         else
13675           ckstrncpy(vvbuf,"",VVBUFL+1);
13676         return((char *)vvbuf);
13677       case VN_X509_I:
13678         if (ssl_active_flag)
13679           ckstrncpy(vvbuf,ssl_get_issuer_name(ssl_con),VVBUFL+1);
13680         else if (tls_active_flag)
13681           ckstrncpy(vvbuf,ssl_get_issuer_name(tls_con),VVBUFL+1);
13682         else
13683           ckstrncpy(vvbuf,"",VVBUFL+1);
13684         return((char *)vvbuf);
13685 #endif /* CK_SSL */
13686
13687       case VN_OSNAM:
13688 #ifdef IKSD
13689 #ifdef CK_LOGIN
13690         if (inserver && isguest)
13691           return("");
13692 #endif /* CK_LOGIN */
13693 #endif /* IKSD */
13694 #ifdef CK_UTSNAME
13695         {
13696             extern char unm_nam[];
13697             return((char *)unm_nam);
13698         }
13699 #else
13700         for (x = y = 0; x < VVBUFL; x++) {
13701             if (ckxsys[x] == SP && cx == 0) continue;
13702             vvbuf[y++] = (char) ((ckxsys[x] == SP) ? '_' : ckxsys[x]);
13703         }
13704         vvbuf[y] = NUL;
13705         return(vvbuf);
13706 #endif /* CK_UTSNAME */
13707
13708       case VN_OSVER: {
13709 #ifdef CK_UTSNAME
13710           extern char unm_ver[];
13711 #ifdef IKSD
13712 #ifdef CK_LOGIN
13713           if (inserver && isguest)
13714             return("");
13715 #endif /* CK_LOGIN */
13716 #endif /* IKSD */
13717           return((char *)unm_ver);
13718 #else
13719           return("");
13720 #endif /* CK_UTSNAME */
13721       }
13722
13723       case VN_OSREL: {
13724 #ifdef CK_UTSNAME
13725           extern char unm_rel[];
13726 #ifdef IKSD
13727 #ifdef CK_LOGIN
13728           if (inserver && isguest)
13729             return("");
13730 #endif /* CK_LOGIN */
13731 #endif /* IKSD */
13732           return((char *)unm_rel);
13733 #else
13734           return("");
13735 #endif /* CK_UTSNAME */
13736       }
13737     } /* Break up long switch statements... */
13738
13739     switch(y) {
13740       case VN_NAME: {
13741           extern char * myname;
13742           return(myname);
13743       }
13744
13745       case VN_MODL: {
13746 #ifdef CK_UTSNAME
13747           extern char unm_mod[], unm_mch[];
13748           int y = VVBUFL - 1;
13749           char * s = unm_mod;
13750 #endif /* CK_UTSNAME */
13751 #ifdef IKSD
13752 #ifdef CK_LOGIN
13753           if (inserver && isguest)
13754             return("");
13755 #endif /* CK_LOGIN */
13756 #endif /* IKSD */
13757
13758 #ifdef COMMENT                          /* was HPUX */
13759           if (!unm_mod[0] && !nopush)
13760             zzstring("\\fcommand(model)",&s,&y);
13761 /*
13762    Another possibility would be:
13763      "\\fcommand(ksh -c 'whence model 1>&- && model || uname -m')"
13764    But that would depend on having ksh.
13765 */
13766 #else
13767 #ifdef OSF32                            /* Digital UNIX 3.2 and higher... */
13768 /* Note: Ultrix has /etc/sizer, but it is not publicly executable. */
13769 /* sizer -c outputs 'cpu:<tab><tab>"DECxxxx"' */
13770           if (!unm_mod[0]) {
13771               char * p;
13772               int flag = 0;
13773               zzstring("\\fcommand(/usr/sbin/sizer -c)",&s,&y);
13774               debug(F110,"DU model",unm_mod,0);
13775               s = unm_mod;
13776               p = unm_mod;
13777               while (*p) {              /* Extract the part in quotes */
13778                   if (*p == '"') {
13779                       if (flag)
13780                         break;
13781                       flag = 1;
13782                       p++;
13783                       continue;
13784                   }
13785                   if (flag)
13786                     *s++ = *p;
13787                   p++;
13788               }
13789               *s = NUL;
13790           }
13791 #endif /* OSF32 */
13792 #endif /* COMMENT */
13793
13794 #ifdef CK_UTSNAME
13795           if (unm_mod[0])
13796             return((char *)unm_mod);
13797           else
13798             return((char *)unm_mch);
13799 #else
13800           return("");
13801 #endif /* CK_UTSNAME */
13802       }
13803
13804 #ifdef IBMX25
13805       /* X.25 variables (local and remote address) */
13806       case VN_X25LA:
13807         if (!local_nua[0] && !x25local_nua(local_nua))
13808           *vvbuf = NULL;
13809         else
13810           ckstrncpy(vvbuf,local_nua,VVBUFL+1);
13811         return((char *)vvbuf);
13812
13813       case VN_X25RA:
13814         if (!remote_nua[0])
13815           *vvbuf = NULL;
13816         else
13817           ckstrncpy(vvbuf,remote_nua,VVBUFL+1);
13818         return((char *)vvbuf);
13819 #endif /* IBMX25 */
13820
13821 #ifndef NODIAL
13822       case VN_PDSFX: {
13823           extern char pdsfx[];
13824           return((char *)pdsfx);
13825       }
13826       case VN_DTYPE: {
13827           extern int dialtype;
13828           sprintf(vvbuf,"%d",dialtype); /* SAFE */
13829           return((char *)vvbuf);
13830       }
13831 #endif /* NODIAL */
13832
13833 #ifdef UNIX
13834       case VN_LCKPID: {
13835           extern char lockpid[];
13836           return((char *)lockpid);
13837       }
13838 #endif /* UNIX */
13839
13840 #ifndef NOXFER
13841       case VN_BLK:
13842         sprintf(vvbuf,"%d",bctr);       /* SAFE */
13843         return((char *)vvbuf);
13844
13845       case VN_TFTIM:
13846         sprintf(vvbuf,                  /* SAFE */
13847 #ifdef GFTIMER
13848                 "%ld", (long)(fptsecs + 0.5)
13849 #else
13850                 "%d", tsecs
13851 #endif /* GFTIMER */
13852                 );
13853         return((char *)vvbuf);
13854 #endif /* NOXFER */
13855
13856       case VN_HWPAR:
13857       case VN_SERIAL: {
13858           int sb;
13859           char c, * ss;
13860           extern int stopbits;
13861           vvbuf[0] = NUL;
13862           if (hwparity && local && !network)
13863             ss = parnam((char)hwparity);
13864           else
13865             ss = parnam((char)parity);
13866           if (cx == VN_HWPAR) {
13867               ckstrncpy(vvbuf,ss,VVBUFL);
13868               return((char *)vvbuf);
13869           }
13870           c = ss[0];
13871           if (islower(c)) c = toupper(c);
13872           sb = stopbits;
13873           if (sb < 1)
13874             sb = (speed > 0 && speed <= 110L) ? 2 : 1;
13875           if (hwparity)
13876             sprintf(vvbuf," 8%c%d",c,sb); /* SAFE */
13877           else if (parity)
13878             sprintf(vvbuf," 7%c%d",c,sb); /* SAFE */
13879           else
13880             sprintf(vvbuf," 8N%d",sb);  /* SAFE */
13881           return((char *)vvbuf);
13882       }
13883
13884 #ifdef UNIX
13885       case VN_LCKDIR: {
13886 #ifndef NOUUCP
13887           extern char * uucplockdir;
13888           ckstrncpy(vvbuf,uucplockdir,VVBUFL);
13889           x = strlen(vvbuf);
13890           if (x > 0) {
13891               if (vvbuf[x-1] != '/') {
13892                   vvbuf[x] = '/';
13893                   vvbuf[x+1] = NUL;
13894               }
13895           }
13896 #else
13897           vvbuf[0] = NUL;
13898 #endif /* NOUUCP */
13899           return((char *)vvbuf);
13900       }
13901 #endif /* UNIX */
13902     } /* Break up long switch statements... */
13903
13904     switch(y) {
13905 #ifndef NODIAL
13906       case VN_DM_LP:
13907       case VN_DM_SP:
13908       case VN_DM_PD:
13909       case VN_DM_TD:
13910       case VN_DM_WA:
13911       case VN_DM_WD:
13912       case VN_DM_HF:
13913       case VN_DM_WB:
13914       case VN_DM_RC: {
13915           extern char * getdm();
13916           ckstrncpy(vvbuf,getdm(y),VVBUFL);
13917           return((char *)vvbuf);
13918       }
13919 #endif /* NODIAL */
13920
13921       case VN_TY_LN:
13922       case VN_TY_LC: {
13923           extern int typ_lines;
13924           sprintf(vvbuf,"%d",typ_lines); /* SAFE */
13925           return((char *)vvbuf);
13926       }
13927       case VN_TY_LM: {
13928           extern int typ_mtchs;
13929           sprintf(vvbuf,"%d",typ_mtchs); /* SAFE */
13930           return((char *)vvbuf);
13931       }
13932       case VN_MACLVL:
13933         sprintf(vvbuf,"%d",maclvl);     /* SAFE */
13934         return((char *)vvbuf);
13935     } /* Break up long switch statements... */
13936
13937     switch(y) {
13938 #ifndef NOLASTFILE
13939       case VN_LASTFIL: {
13940           extern char * lastfile;
13941           return(lastfile ? lastfile : "");
13942       }
13943 #endif  /* NOLASTFILE */
13944 #ifndef NOXFER
13945       case VN_XF_BC:
13946         sprintf(vvbuf,"%d",crunched);   /* SAFE */
13947         return((char *)vvbuf);
13948
13949       case VN_XF_TM:
13950         sprintf(vvbuf,"%d",timeouts);   /* SAFE */
13951         return((char *)vvbuf);
13952
13953       case VN_XF_RX:
13954         sprintf(vvbuf,"%d",retrans);    /* SAFE */
13955         return((char *)vvbuf);
13956 #endif /* NOXFER */
13957
13958       case VN_MS_CD:                    /* Modem signals */
13959       case VN_MS_CTS:
13960       case VN_MS_DSR:
13961       case VN_MS_DTR:
13962       case VN_MS_RI:
13963       case VN_MS_RTS: {
13964           int x, z = -1;
13965           x = ttgmdm();                 /* Try to get them */
13966           if (x > -1) {
13967               switch (y) {
13968                 case VN_MS_CD:  z = (x & BM_DCD) ? 1 : 0; break;
13969                 case VN_MS_DSR: z = (x & BM_DSR) ? 1 : 0; break;
13970                 case VN_MS_CTS: z = (x & BM_CTS) ? 1 : 0; break;
13971 #ifdef MAC
13972                 case VN_MS_DTR: z = (x & BM_DTR) ? 1 : 0; break;
13973 #else
13974 #ifndef STRATUS
13975                 case VN_MS_RI:  z = (x & BM_RNG) ? 1 : 0; break;
13976 #ifndef NT
13977                 case VN_MS_DTR: z = (x & BM_DTR) ? 1 : 0; break;
13978                 case VN_MS_RTS: z = (x & BM_RTS) ? 1 : 0; break;
13979 #endif /* NT */
13980 #endif /* STRATUS */
13981 #endif /* MAC */
13982               }
13983           }
13984           sprintf(vvbuf,"%d",z);        /* SAFE */
13985           return((char *)vvbuf);
13986       }
13987       case VN_MATCH:                    /* INPUT MATCH */
13988         return(inpmatch ? inpmatch : "");
13989
13990 #ifdef CKFLOAT
13991       case VN_ISCALE:                   /* INPUT SCALE-FACTOR */
13992         return(inpscale ? inpscale : "1.0");
13993 #endif  /* CKFLOAT */
13994
13995       case VN_SLMSG: {                  /* SET LINE / HOST message */
13996           extern char * slmsg;
13997           vvbuf[0] = NUL;
13998           if (slmsg)
13999             ckstrncpy(vvbuf,slmsg,VVBUFL);
14000          return(vvbuf);
14001       }
14002
14003       case VN_TXTDIR:                   /* TEXTDIR */
14004         return(k_info_dir ? k_info_dir : "");
14005
14006 #ifdef FNFLOAT
14007       case VN_MA_PI:
14008         return(math_pi);
14009
14010       case VN_MA_E:
14011         return(math_e);
14012
14013       case VN_MA_PR:
14014         sprintf(vvbuf,"%d",fp_digits);  /* SAFE */
14015         return(vvbuf);
14016 #endif /* FNFLOAT */
14017
14018       case VN_CMDBL:
14019         sprintf(vvbuf,"%d",CMDBL);      /* SAFE */
14020         return(vvbuf);
14021
14022 #ifdef CKCHANNELIO
14023       case VN_FERR: {
14024           extern int z_error;
14025           sprintf(vvbuf,"%d",z_error);  /* SAFE */
14026           return(vvbuf);
14027       }
14028       case VN_FMAX: {
14029           extern int z_maxchan;
14030           sprintf(vvbuf,"%d",z_maxchan); /* SAFE */
14031           return(vvbuf);
14032       }
14033       case VN_FCOU: {
14034           extern int z_filcount;
14035           sprintf(vvbuf,"%d",z_filcount); /* SAFE */
14036           return(vvbuf);
14037       }
14038 #endif /* CKCHANNELIO */
14039
14040 #ifndef NODIAL
14041       case VN_DRTR: {
14042           extern int dialcount;
14043           sprintf(vvbuf,"%d",dialcount); /* SAFE */
14044           return(vvbuf);
14045       }
14046 #endif /* NODIAL */
14047
14048 #ifndef NOLOGDIAL
14049 #ifndef NOLOCAL
14050       case VN_CXTIME:
14051         sprintf(vvbuf,"%ld",dologshow(0)); /* SAFE */
14052         return(vvbuf);
14053 #endif /* NOLOCAL */
14054 #endif /* NOLOGDIAL */
14055
14056       case VN_BYTE:
14057         sprintf(vvbuf,"%d",byteorder);  /* SAFE */
14058         return(vvbuf);
14059
14060       case VN_KBCHAR:
14061         vvbuf[0] = NUL;
14062         vvbuf[1] = NUL;
14063         if (kbchar > 0)
14064           vvbuf[0] = (kbchar & 0xff);
14065         return(vvbuf);
14066
14067       case VN_TTYNAM: {
14068 #ifdef HAVECTTNAM
14069           extern char cttnam[];
14070           return((char *)cttnam);
14071 #else
14072           return(CTTNAM);
14073 #endif /* HAVECTTNAM */
14074       }
14075
14076       case VN_PROMPT:
14077         return(cmgetp());
14078
14079       case VN_BUILD: {
14080           extern char * buildid;
14081           return(buildid);
14082       }
14083
14084 #ifndef NOSEXP
14085       case VN_SEXP: {
14086           extern char * lastsexp;
14087           return(lastsexp ? lastsexp : "");
14088       }
14089       case VN_VSEXP: {
14090           extern char * sexpval;
14091           return(sexpval ? sexpval : "");
14092       }
14093       case VN_LSEXP: {
14094           extern int sexpdep;
14095           ckstrncpy(vvbuf,ckitoa(sexpdep),VVBUFL);
14096           return(vvbuf);
14097       }
14098 #endif /* NOSEXP */
14099
14100 #ifdef GFTIMER
14101       case VN_FTIME: {
14102           CKFLOAT f;
14103           ztime(&p);
14104           if (p == NULL || *p == NUL)
14105             return(NULL);
14106           z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
14107           f = (CKFLOAT)z + ((CKFLOAT)ztusec) / 1000000.0;
14108           sprintf(vvbuf,"%f",f);        /* SAFE */
14109           return(vvbuf);
14110       }
14111 #endif /* GFTIMER */
14112
14113 #ifndef NOHTTP
14114       case VN_HTTP_C: {                 /* HTTP Code */
14115           extern int http_code;
14116           return(ckitoa(http_code));
14117       }
14118       case VN_HTTP_N:                   /* HTTP Connected */
14119         return( http_isconnected() ? "1" : "0");
14120       case VN_HTTP_H:                   /* HTTP Host */
14121         return( (char *)http_host() );
14122       case VN_HTTP_M: {                 /* HTTP Message */
14123           extern char http_reply_str[];
14124           return((char *)http_reply_str);
14125       }
14126       case VN_HTTP_S:                   /* HTTP Security */
14127         return((char *)http_security());
14128 #endif /* NOHTTP */
14129
14130 #ifdef NEWFTP
14131       case VN_FTP_B:
14132         return((char *)ftp_cpl_mode());
14133       case VN_FTP_D:
14134         return((char *)ftp_dpl_mode());
14135       case VN_FTP_Z:
14136         return((char *)ftp_authtype());
14137       case VN_FTP_C: {
14138           extern int ftpcode;
14139           return(ckitoa(ftpcode));
14140       }
14141       case VN_FTP_M: {
14142           extern char ftp_reply_str[];
14143           if (isdigit(ftp_reply_str[0]) &&
14144               isdigit(ftp_reply_str[1]) &&
14145               isdigit(ftp_reply_str[2]) &&
14146               ftp_reply_str[3] == ' ')
14147             return(&ftp_reply_str[4]);
14148           else
14149             return(ftp_reply_str);
14150       }
14151       case VN_FTP_S: {
14152           extern char ftp_srvtyp[];
14153           return((char *)ftp_srvtyp);
14154       }
14155       case VN_FTP_H: {
14156           extern char * ftp_host;
14157           return(ftp_host ? ftp_host : "");
14158       }
14159       case VN_FTP_X: {                  /* FTP Connected */
14160           return(ftpisconnected() ? "1" : "0");
14161       }
14162       case VN_FTP_L: {                  /* FTP Logged in */
14163           return(ftpisloggedin() ? "1" : "0");
14164       }
14165       case VN_FTP_G: {                  /* FTP GET-PUT-REMOTE */
14166           extern int ftpget;
14167           char * s = "";
14168           switch (ftpget) {
14169             case 0: s = "kermit"; break;
14170             case 1: s = "ftp"; break;
14171             case 2: s = "auto"; break;
14172           }
14173           return(s);
14174       }
14175 #endif /* NEWFTP */
14176
14177 #ifndef NOLOCAL
14178       case VN_CX_STA: {                 /* CONNECT status */
14179           extern int cx_status;
14180           return(ckitoa(cx_status));
14181       }
14182 #endif /* NOLOCAL */
14183       case VN_NOW:                      /* Timestamp */
14184         return(ckcvtdate(p,0));
14185
14186       case VN_HOUR:                     /* Hour of the day */
14187         ztime(&p);                      /* "Thu Feb  8 12:00:00 1990" */
14188         if (!p) p = "";
14189         if (!*p) return(p);
14190         vvbuf[0] = p[11];
14191         vvbuf[1] = p[12];
14192         vvbuf[2] = NUL;
14193         return(vvbuf);                  /* and return it */
14194
14195       case VN_BITS:                     /* Bits (16, 32, 64) */
14196         if (sizeof(long) > 4)
14197           return(ckitoa(8*sizeof(long)));
14198         else
14199           return(ckitoa(8*sizeof(int)));
14200
14201       case VN_LASTKWV:                  /* 212 */
14202         return(lastkwval ? lastkwval : "");
14203
14204       case VN_HOSTIP: {                 /* 212 */
14205 #ifdef TCPSOCKET
14206           extern char hostipaddr[];
14207           return((char *)hostipaddr);
14208 #else
14209           return("");
14210 #endif  /* TCPSOCKET */
14211       }
14212       case VN_INPMSG:
14213         switch (instatus) {
14214           case INP_OK:  return("SUCCESS");
14215           case INP_TO:  return("Timed out");
14216           case INP_UI:  return("Keyboard interrupt");
14217           case INP_IE:  return("Internal error");
14218           case INP_IO:  return("I/O error or connection lost");
14219           case INP_IKS: return("INPUT disabled");
14220           case INP_BF:  return("Buffer filled and /NOWRAP set");
14221           default:      return("Unknown");
14222         }
14223
14224       case VN_VAREVAL:                  /* 212 */
14225         return(vareval ? "recursive" : "simple");
14226
14227       case VN_LOG_CON:                  /* \v(...) for log files */
14228 #ifdef CKLOGDIAL
14229         return(diafil);
14230 #else 
14231         return("");
14232 #endif
14233       case VN_LOG_PKT:
14234 #ifndef NOXFER
14235         return(pktfil);
14236 #else
14237         return("");
14238 #endif
14239       case VN_LOG_SES:
14240 #ifndef NOLOCAL
14241         return(sesfil);
14242 #else
14243         return("");
14244 #endif
14245       case VN_LOG_TRA:
14246 #ifdef TLOG
14247         return(trafil);
14248 #else
14249         return("");
14250 #endif
14251       case VN_LOG_DEB:
14252 #ifdef DEBUG
14253         return(debfil);
14254 #else
14255         return("");
14256 #endif
14257       case VN_PREVCMD: {
14258           extern char * prevcmd;
14259           return(prevcmd ?  prevcmd : "");
14260       }
14261     }
14262
14263 #ifndef NODIAL
14264     switch (y) {                        /* Caller ID values */
14265       extern char
14266         * callid_date, * callid_time, * callid_name,
14267         * callid_nmbr, * callid_mesg;
14268
14269       case VN_CI_DA:
14270         return(callid_date ? callid_date : "");
14271
14272       case VN_CI_TI:
14273         return(callid_time ? callid_time : "");
14274
14275       case VN_CI_NA:
14276         return(callid_name ? callid_name : "");
14277
14278       case VN_CI_NU:
14279         return(callid_nmbr ? callid_nmbr : "");
14280
14281       case VN_CI_ME:
14282         return(callid_mesg ? callid_mesg : "");
14283
14284     } /* End of variable-name switches */
14285 #endif /* NODIAL */
14286
14287 #ifdef NT
14288     switch (y) {
14289       case VN_PERSONAL:
14290         p = (char *)GetPersonal();
14291         if (p) {
14292             GetShortPathName(p,vvbuf,VVBUFL);
14293             return(vvbuf);
14294         }
14295         return("");
14296       case VN_DESKTOP:
14297           p = (char *)GetDesktop();
14298           if (p) {
14299               GetShortPathName(p,vvbuf,VVBUFL);
14300               return(vvbuf);
14301           }
14302           return("");
14303       case VN_COMMON:
14304         p = (char *)GetAppData(1);
14305         if (p) {
14306             ckmakmsg(vvbuf,VVBUFL,p,"Kermit 95/",NULL,NULL);
14307             GetShortPathName(vvbuf,vvbuf,VVBUFL);
14308             return(vvbuf);
14309         }
14310         return("");
14311       case VN_APPDATA:
14312         p = (char *)GetAppData(0);
14313         if (p) {
14314             ckmakmsg(vvbuf,VVBUFL,p,"Kermit 95/",NULL,NULL);
14315             GetShortPathName(vvbuf,vvbuf,VVBUFL);
14316             return(vvbuf);
14317         }
14318         return("");
14319     }
14320 #endif /* NT */
14321
14322 #ifdef TN_COMPORT
14323     switch (y) {
14324       case VN_TNC_SIG: {
14325         p = (char *) tnc_get_signature();
14326         ckstrncpy(vvbuf,p ? p : "",VVBUFL);
14327         return(vvbuf);
14328       }
14329     }
14330 #endif /* TN_COMPORT */
14331
14332 #ifdef KUI
14333     switch (y) {
14334       case VN_GUI_RUN: {
14335           extern HWND getHwndKUI();
14336           if ( IsIconic(getHwndKUI()) )
14337             return("minimized");
14338           if ( IsZoomed(getHwndKUI()) )
14339             return("maximized");
14340           return("restored");
14341       }
14342       case VN_GUI_XP:
14343         sprintf(vvbuf,"%d",get_gui_window_pos_x());  /* SAFE */
14344         return(vvbuf);
14345       case VN_GUI_YP:
14346         sprintf(vvbuf,"%d",get_gui_window_pos_y());  /* SAFE */
14347         return(vvbuf);
14348       case VN_GUI_XR:
14349         sprintf(vvbuf,"%d",GetSystemMetrics(SM_CXSCREEN));  /* SAFE */
14350         return(vvbuf);
14351       case VN_GUI_YR:
14352         sprintf(vvbuf,"%d",GetSystemMetrics(SM_CYSCREEN));  /* SAFE */
14353         return(vvbuf);
14354       case VN_GUI_FNM:
14355           if ( ntermfont > 0 ) {
14356               int i;
14357               for (i = 0; i < ntermfont; i++) {
14358                   if (tt_font == term_font[i].kwval) {
14359                       ckstrncpy(vvbuf,term_font[i].kwd,VVBUFL);
14360                       return(vvbuf);
14361                   }
14362               }
14363           }
14364           return("(unknown)");
14365       case VN_GUI_FSZ:
14366           ckstrncpy(vvbuf,ckitoa(tt_font_size/2),VVBUFL);
14367           if ( tt_font_size % 2 )
14368               ckstrncat(vvbuf,".5",VVBUFL);
14369           return(vvbuf);
14370     }
14371 #endif /* KUI */
14372
14373     fnsuccess = 0;
14374     if (fnerror) {
14375         fnsuccess = 0;
14376     }
14377     if (fndiags) {
14378         if (!embuf[0])
14379           ckstrncpy(embuf,"<ERROR:NO_SUCH_VARIABLE>",EMBUFLEN);
14380         printf("?%s\n",embuf);
14381         return((char *)embuf);
14382     } else
14383       return("");
14384 }
14385 #endif /* NOSPL */
14386
14387
14388 /*
14389   X X S T R I N G  --  Expand variables and backslash codes.
14390
14391     int xxtstring(s,&s2,&n);
14392
14393   Expands \ escapes via recursive descent.
14394   Argument s is a pointer to string to expand (source).
14395   Argument s2 is the address of where to put result (destination).
14396   Argument n is the length of the destination string (to prevent overruns).
14397   Returns -1 on failure, 0 on success,
14398     with destination string null-terminated and s2 pointing to the
14399     terminating null, so that subsequent characters can be added.
14400     Failure reasons include destination buffer is filled up.
14401 */
14402
14403 #define XXDEPLIM 100                    /* Recursion depth limit */
14404 /*
14405   In Windows the stack is limited to 256K so big character arrays like
14406   vnambuf can't be on the stack in recursive functions like zzstring().
14407   But that's no reason use malloc() in Unix or VMS, which don't have
14408   this kind of restriction.
14409 */
14410 #ifdef DVNAMBUF                         /* Dynamic vnambuf[] */
14411 #undef DVNAMBUF                         /* Clean slate */
14412 #endif /* DVNAMBUF */
14413
14414 #ifndef NOSPL                           /* Only if SPL included */
14415 #ifdef OS2                              /* Only for K95 */
14416 #define DVNAMBUF
14417 #endif /* OS2 */
14418 #endif /* NOSPL */
14419
14420 int
14421 zzstring(s,s2,n) char *s; char **s2; int *n; {
14422     int x,                              /* Current character */
14423         y,                              /* Worker */
14424         pp,                             /* Paren level */
14425         kp,                             /* Brace level */
14426         argn,                           /* Function argument counter */
14427         n2,                             /* Local copy of n */
14428         d,                              /* Array dimension */
14429         vbi,                            /* Variable id (integer form) */
14430         argl,                           /* String argument length */
14431         nx,                             /* Save original length */
14432         quoting = 0;                    /* 299 */
14433
14434     char vb,                            /* Variable id (char form) */
14435         *vp,                            /* Pointer to variable definition */
14436         *new,                           /* Local pointer to target string */
14437 #ifdef COMMENT
14438         *old,                           /* Save original target pointer */
14439 #endif /* COMMENT */
14440         *p,                             /* Worker */
14441         *q,                             /* Worker */
14442         *s3;                            /* Worker */
14443     int  x3;                            /* Worker */
14444     char *r  = (char *)0;               /* For holding function args */
14445     char *r2 = (char *)0;
14446     char *r3p;
14447
14448 #ifndef NOSPL
14449 #ifdef DVNAMBUF
14450     char * vnambuf = NULL;              /* Buffer for variable/function name */
14451 #else /* DVNAMBUF */
14452     char vnambuf[VNAML];                /* Buffer for variable/function name */
14453 #endif /* DVNAMBUF */
14454     char *argp[FNARGS];                 /* Pointers to function args */
14455 #endif /* NOSPL */
14456
14457     static int depth = 0;               /* Call depth, avoid overflow */
14458
14459     n2 = *n;                            /* Make local copies of args */
14460     nx = n2;
14461
14462 #ifdef COMMENT
14463     /* This is always 32K in BIGBUFOK builds */
14464     if (depth == 0)
14465       debug(F101,"zzstring top-level n","",n2);
14466 #endif  /* COMMENT */
14467
14468     new = *s2;                          /* for one less level of indirection */
14469 #ifdef COMMENT
14470     old = new;
14471 #endif /* COMMENT */
14472
14473 #ifndef NOSPL
14474     itsapattern = 0;                    /* For \fpattern() */
14475     isjoin = 0;                         /* For \fjoin() */
14476 #endif /* NOSPL */
14477     depth++;                            /* Sink to a new depth */
14478     if (depth > XXDEPLIM) {             /* Too deep? */
14479         printf("?definition is circular or too deep\n");
14480         debug(F101,"zzstring fail","",depth);
14481         depth = 0;
14482         *new = NUL;
14483         return(-1);
14484     }
14485     if (!s || !new) {                   /* Watch out for null pointers */
14486         debug(F101,"zzstring fail 2","",depth);
14487         if (new)
14488           *new = NUL;
14489         depth = 0;
14490         return(-1);
14491     }
14492     s3 = s;
14493     argl = 0;
14494     while (*s3++) argl++;              /* Get length of source string */
14495     debug(F010,"zzstring entry",s,0);
14496     if (argl == 0) {                    /* Empty string */
14497         debug(F111,"zzstring empty arg",s,argl);
14498         depth = 0;
14499         *new = NUL;
14500         return(0);
14501     }
14502     if (argl < 0) {                     /* Watch out for garbage */
14503         debug(F101,"zzstring fail 3","",depth);
14504         *new = NUL;
14505         depth = 0;
14506         return(-1);
14507     }
14508 #ifdef DVNAMBUF
14509     debug(F100,"vnambuf malloc...","",0);
14510     vnambuf = malloc(VNAML);
14511     if (vnambuf == NULL) {
14512         printf("?Out of memory");
14513         return(-1);
14514     }
14515     debug(F100,"vnambuf malloc ok","",0);
14516 #endif /* DVNAMBUF */
14517
14518     while ((x = *s)) {                  /* Loop for all characters */
14519         if (x != CMDQ) {                /* Is it the command-quote char? */
14520             *new++ = *s++;              /* No, normal char, just copy */
14521             if (--n2 < 0) {             /* and count it, careful of overflow */
14522                 debug(F101,"zzstring overflow 1","",depth);
14523                 depth = 0;
14524 #ifdef DVNAMBUF
14525                 if (vnambuf) free(vnambuf);
14526 #endif /* DVNAMBUF */
14527                 return(-1);
14528             }
14529             continue;
14530         }
14531
14532 /* We have the command-quote character. */
14533
14534         x = *(s+1);                     /* Get the following character. */
14535         if (isupper(x)) x = tolower(x);
14536         switch (x) {                    /* Act according to variable type */
14537 #ifndef NOSPL
14538           case 0:                       /* It's a lone backslash */
14539             *new++ = *s++;
14540             if (--n2 < 0) {
14541                 debug(F101,"zzstring overflow 2","",0);
14542 #ifdef DVNAMBUF
14543                 if (vnambuf) free(vnambuf);
14544 #endif /* DVNAMBUF */
14545                 return(-1);
14546             }
14547             break;
14548           case '%':                     /* Variable */
14549             s += 2;                     /* Get the letter or digit */
14550             vb = *s++;                  /* and move source pointer past it */
14551             vp = NULL;                  /* Assume definition is empty */
14552             if (vb >= '0' && vb <= '9') { /* Digit for macro arg */
14553                 if (maclvl < 0)         /* Digit variables are global */
14554                   vp = g_var[vb];       /* if no macro is active */
14555                 else                    /* otherwise */
14556                   vp = m_arg[maclvl][vb - '0']; /* they're on the stack */
14557             } else if (vb == '*') {     /* Macro args string */
14558 #ifdef COMMENT
14559                 /* This doesn't take changes into account */
14560                 vp = (maclvl >= 0) ? m_line[maclvl] : topline;
14561                 if (!vp) vp = "";
14562 #else
14563                 char * ss = new;
14564                 if (zzstring("\\fjoin(&_[],,1)",&new,&n2) < 0) {
14565 #ifdef DVNAMBUF
14566                     if (vnambuf) free(vnambuf);
14567 #endif /* DVNAMBUF */
14568                     return(-1);
14569                 }
14570                 debug(F110,"zzstring \\%*",ss,0);
14571                 break;
14572 #endif /* COMMENT */
14573             } else {
14574                 if (isupper(vb)) vb += ('a'-'A');
14575                 vp = g_var[vb];         /* Letter for global variable */
14576             }
14577             if (!vp) vp = "";
14578 #ifdef COMMENT
14579             if (vp) {                   /* If definition not empty */
14580 #endif /* COMMENT */
14581                 if (vareval) {
14582                     debug(F010,"zzstring %n vp",vp,0);
14583                     /* call self to evaluate it */
14584                     if (zzstring(vp,&new,&n2) < 0) {
14585                         debug(F101,"zzstring fail 6","",depth);
14586 #ifdef DVNAMBUF
14587                         if (vnambuf) free(vnambuf);
14588 #endif /* DVNAMBUF */
14589                         return(-1);     /* Pass along failure */
14590                     }
14591                 } else {
14592                     while ((*new++ = *vp++)) /* copy it to output string. */
14593                       if (--n2 < 0) {
14594                           if (q) free(q);
14595                           debug(F101,"zzstring overflow 4.5","",depth);
14596 #ifdef DVNAMBUF
14597                           if (vnambuf) free(vnambuf);
14598 #endif /* DVNAMBUF */
14599                           return(-1);
14600                       }
14601                     new--;              /* Back up over terminating null */
14602                     n2++;               /* to allow for further deposits. */
14603                 }
14604 #ifdef COMMENT
14605             } else {
14606                 debug(F110,"zzstring %n vp","(NULL)",0);
14607                 n2 = nx;
14608                 new = old;
14609                 *new = NUL;
14610             }
14611 #endif /* COMMENT */
14612             break;
14613           case '&':                     /* An array reference */
14614             x = arraynam(s,&vbi,&d);    /* Get name and subscript */
14615             debug(F111,"zzstring arraynam",s,x);
14616             if (x < 0) {
14617                 debug(F101,"zzstring fail 7","",depth);
14618 #ifdef DVNAMBUF
14619                 if (vnambuf) free(vnambuf);
14620 #endif /* DVNAMBUF */
14621                 return(-1);
14622             }
14623             pp = 0;                     /* Bracket counter */
14624             while (*s) {                /* Advance source pointer... */
14625                 if (*s == '[') pp++;
14626                 if (*s == ']' && --pp == 0) break;
14627                 s++;
14628             }
14629             if (*s == ']') s++;         /* ...past the closing bracket. */
14630
14631             x = chkarray(vbi,d);        /* Array is declared? */
14632             debug(F101,"zzstring chkarray","",x);
14633             if (x > -1) {
14634 #ifdef COMMENT
14635                 char * s1 = NULL;
14636 #endif /* COMMENT */
14637                 vbi -= ARRAYBASE;       /* Convert name to index */
14638
14639                   if (a_dim[vbi] >= d) { /* If subscript in range */
14640                     char **ap;
14641                     ap = a_ptr[vbi];    /* get data pointer */
14642                     if (ap) {           /* and if there is one */
14643                         if (ap[d]) {    /* If definition not empty */
14644                             debug(F111,"zzstring ap[d]",ap[d],d);
14645                             if (vareval) {
14646                                 if (zzstring(ap[d],&new,&n2) < 0) {
14647                                     debug(F101,"zzstring fail 8","",depth);
14648 #ifdef DVNAMBUF
14649                                     if (vnambuf) free(vnambuf);
14650 #endif /* DVNAMBUF */
14651                                     return(-1); /* Pass along failure */
14652                                 }
14653                             } else {
14654                                 vp = ap[d];
14655                                 while ((*new++ = *vp++)) /* copy to result */
14656                                   if (--n2 < 0) {
14657                                       if (q) free(q);
14658                                       debug(F101,
14659                                             "zzstring overflow 8.5","",depth);
14660 #ifdef DVNAMBUF
14661                                       if (vnambuf) free(vnambuf);
14662 #endif /* DVNAMBUF */
14663                                       return(-1);
14664                                   }
14665                                 new--;  /* Back up over terminating null */
14666                                 n2++;   /* to allow for further deposits. */
14667                             }
14668                         }
14669
14670                     } else {
14671                         /* old = new; */
14672                         n2 = nx;
14673                     }
14674                 }
14675             }
14676             break;
14677
14678           case 'f':                     /* A builtin function */
14679             q = vnambuf;                /* Copy the name */
14680             y = 0;                      /* into a separate buffer */
14681             s += 2;                     /* point past 'F' */
14682             while (y++ < VNAML) {
14683                 if (*s == '(') { s++; break; } /* Look for open paren */
14684                 if ((*q = *s) == NUL) break;   /* or end of string */
14685                 s++; q++;
14686             }
14687             *q = NUL;                   /* Terminate function name */
14688             if (y >= VNAML) {           /* Handle pathological case */
14689                 while (*s && (*s != '(')) /* of very long string entered */
14690                   s++;                    /* as function name. */
14691                 if (*s == ')') s++;       /* Skip past it. */
14692             }
14693             r = r2 = malloc(argl+2);    /* And make a place to copy args */
14694             /* debug(F101,"zzstring r2","",r2); */
14695             if (!r2) {                  /* Watch out for malloc failure */
14696                 debug(F101,"zzstring fail 9","",depth);
14697                 *new = NUL;
14698                 depth = 0;
14699 #ifdef DVNAMBUF
14700                 if (vnambuf) free(vnambuf);
14701 #endif /* DVNAMBUF */
14702                 return(-1);
14703             }
14704             if (r3) free(r3); /* And another to copy literal arg string */
14705             r3 = malloc(argl+2);
14706             /* debug(F101,"zzstring r3","",r3); */
14707             if (!r3) {
14708                 debug(F101,"zzstring fail 10","",depth);
14709                 depth = 0;
14710                 *new = NUL;
14711                 if (r2) free(r2);
14712 #ifdef DVNAMBUF
14713                 if (vnambuf) free(vnambuf);
14714 #endif /* DVNAMBUF */
14715                 return(-1);
14716             } else
14717               r3p = r3;
14718             argn = 0;                   /* Argument counter */
14719             argp[argn++] = r;           /* Point to first argument */
14720             y = 0;                      /* Completion flag */
14721             pp = 1;                     /* Paren level (already have one). */
14722             kp = 0;
14723             while (1) {                 /* Copy each argument, char by char. */
14724                 *r3p++ = *s;            /* This is a literal copy for \flit */
14725                 if (!*s) break;
14726
14727                 if (*s == '{') {        /* Left brace */
14728                     kp++;
14729                 }
14730                 if (*s == '}') {        /* Right brace */
14731                     kp--;
14732                 }
14733                 if (*s == '(' && kp <= 0) { /* Open paren not in brace */
14734                     pp++;               /* Count it */
14735                 }
14736                 *r = *s;                /* Now copy resulting byte */
14737                 if (!*r)                /* If NUL, done. */
14738                   break;
14739                 if (*r == ')' && kp <= 0) { /* Closing paren, count it. */
14740                     if (--pp == 0) {    /* Final one? */
14741                         *r = NUL;       /* Make it a terminating null */
14742                         *(r3p - 1) = NUL;
14743                         s++;            /* Point past it in source string */
14744                         y = 1;          /* Flag we've got all the args */
14745                         break;          /* Done with while loop */
14746                     }
14747                 }
14748                 if (*r == ',' && kp <= 0) { /* Comma */
14749                     if (pp == 1) {          /* If not within ()'s, */
14750                         if (argn >= FNARGS) { /* Too many args */
14751                             s++; r++;   /* Keep collecting flit() string */
14752                             continue;
14753                         }
14754                         *r = NUL;           /* New arg, skip past comma */
14755                         argp[argn++] = r+1; /* In range, point to new arg */
14756                     }                   /* Otherwise just skip past  */
14757                 }
14758                 s++; r++;               /* Advance pointers */
14759             }
14760             if (!y)                     /* If we didn't find closing paren */
14761               argn = -1;
14762 #ifdef DEBUG
14763             if (deblog) {
14764                 char buf[24];
14765                 debug(F111,"zzstring function name",vnambuf,y);
14766                 debug(F010,"zzstring function r3",r3,0);
14767                 for (y = 0; y < argn; y++) {
14768                     sprintf(buf,"arg %2d ",y);
14769                     debug(F010,buf,argp[y],0);
14770                 }
14771             }
14772 #endif /* DEBUG */
14773             {
14774              /* In case the function name itself is constructed */
14775                 char buf[64]; char * p = buf; int n = 64; 
14776                 if (zzstring(vnambuf,&p,&n) > -1)
14777                   ckstrncpy(vnambuf,buf,64);
14778             }
14779             vp = fneval(vnambuf,argp,argn,r3); /* Evaluate the function. */
14780             if (vp) {                      /* If definition not empty */
14781                 while ((*new++ = *vp++)) { /* copy it to output string */
14782                     if (--n2 < 0) {        /* watch out for overflow */
14783                         debug(F101,"zzstring fail 12","",depth);
14784                         if (r2) { free(r2); r2 = NULL; }
14785                         if (r3) { free(r3); r3 = NULL; }
14786 #ifdef DVNAMBUF
14787                         if (vnambuf) free(vnambuf);
14788 #endif /* DVNAMBUF */
14789                         return(-1);
14790                     }
14791                 }
14792                 new--;                  /* Back up over terminating null */
14793                 n2++;                   /* to allow for further deposits. */
14794             }
14795             if (r2) { free(r2); r2 = NULL; }
14796             if (r3) { free(r3); r3 = NULL; }
14797             break;
14798           case 'q':                     /* 299 String to be take literally */
14799             quoting = 1;                /* 299 */
14800           case '$':                     /* An environment variable */
14801           case 'v':                     /* Or a named builtin variable. */
14802           case 'm':                     /* Or a macro /long variable */
14803           case 's':                     /* 196 Macro substring */
14804           case ':':                     /* 196 \-variable substring */
14805             pp = 0;
14806             p = s+2;                    /* $/V/M must be followed by (name) */
14807             if (*p != '(') {            /* as in \$(HOME) or \V(count) */
14808                 *new++ = *s++;          /* If not, just copy it */
14809                 if (--n2 < 0) {
14810                     debug(F101,"zzstring overflow 3","",depth);
14811 #ifdef DVNAMBUF
14812                     if (vnambuf) free(vnambuf);
14813 #endif /* DVNAMBUF */
14814                     return(-1);
14815                 }
14816                 break;
14817             }
14818             pp++;
14819             p++;                        /* Point to 1st char of name */
14820             q = vnambuf;                /* Copy the name */
14821             y = 0;                      /* into a separate buffer */
14822             debug(F110,">>>> \\q(ARG)",p,0);
14823             while (y++ < VNAML) {       /* Watch out for name too long */
14824                 if (*p == '(') {        /* Parens can be nested... */
14825                     if (*(p-1) != CMDQ) /* 299 */
14826                       pp++;
14827                 } else if (*p == ')') { /* Name properly terminated with ')' */
14828                     if (*(p-1) != CMDQ) /* 299 */
14829                       pp--;
14830                     if (pp == 0) {
14831                         p++;            /* Move source pointer past ')' */
14832                         break;
14833                     }
14834                 }
14835                 if ((*q = *p) == NUL)   /* String ends before ')' */
14836                   break;
14837                 p++; q++;               /* Advance pointers */
14838             }
14839             *q = NUL;                   /* Terminate the variable name */
14840             if (y >= VNAML) {           /* Handle pathological case */
14841                 while (*p && (*p != ')')) /* of very long string entered */
14842                   p++;                    /* as variable name. */
14843                 if (*p == ')') p++;       /* Skip ahead to the end of it. */
14844             }
14845             s = p;                      /* Adjust global source pointer */
14846             s3 = vnambuf;
14847             x3 = 0;
14848             while (*s3++) x3++;
14849             p = malloc(x3 + 1);         /* Make temporary space */
14850             if (p && !quoting) {        /* If we got the space */
14851                 vp = vnambuf;           /* Point to original */
14852                 strcpy(p,vp);           /* (safe) Make a copy of it */
14853                 y = VNAML;              /* Length of name buffer */
14854                 zzstring(p,&vp,&y);     /* Evaluate the copy */
14855                 free(p);                /* Free the temporary space */
14856                 p = NULL;
14857             }
14858             debug(F110,"zzstring vname",vnambuf,0);
14859             q = NULL;
14860             if (x == 'q') {             /* 299 Quoting this string */
14861                 vp = vnambuf;           /* 299 */
14862                 debug(F110,">>> VP",vp,0);
14863             } else if (x == '$') {      /* Look up its value */
14864                 vp = getenv(vnambuf);   /* This way for environment variable */
14865             } else if (x == 'm' || x == 's' || x == ':') { /* Macro / substr */
14866                 int k, x1 = -1, x2 = -1;
14867                 char c = NUL; 
14868                 k = strlen(vnambuf);
14869                 /* \s(name[n:m]) -- Compact substring notation */
14870                 if ((x == 's' || x == ':') && (k > 1)) { /* Substring wanted */
14871                     int bprc;
14872                     if (vnambuf[k-1] == ']') {
14873                         int i;
14874                         for (i = 0; i < k-1; i++) {
14875                             if (vnambuf[i] == '[') {
14876                                 bprc = boundspair(vnambuf,":_.",&x1,&x2,&c);
14877                                 debug(F111,"zzstring boundspair",vnambuf,bprc);
14878                                 debug(F000,"zzstring boundspair c","",c);
14879                                 if (bprc > -1) {
14880                                     vnambuf[i] = NUL;
14881                                     if (x1 < 1)
14882                                       x1 = 1;
14883                                     x1--;       /* Adjust to 0-base */
14884                                 }
14885                                 break;
14886                             }
14887                         }
14888                     }
14889                 }
14890                 if (x == ':') {         /* Variable type (s or :) */
14891                     vp = vnambuf;
14892                 } else {
14893                     y = isaarray(vnambuf) ?
14894                         mxxlook(mactab,vnambuf,nmac) :
14895                         mxlook(mactab,vnambuf,nmac);
14896                     if (y > -1) {       /* Got definition */
14897                         vp = mactab[y].mval;
14898                     } else {
14899                         vp = NULL;
14900                     }
14901                 }
14902                 debug(F111,"zzstring vp",vp,(vp==NULL)?0:strlen(vp));
14903
14904                 if (vp) {
14905                     if ((x == 's' || x == ':') && (k > 1)) {
14906                         /* Compact substring notation */
14907                         if (x2 == 0) {  /* Length */
14908                             vp = NULL;
14909                         } else if (x1 > -1) { /* Start */
14910                             k = strlen(vp);
14911                             debug(F101,">>> k","",k);
14912                             /* If it's off the end, result is empty */
14913                             if (x1 > k) {
14914                                 vp = NULL;
14915                             } else if (k > 0) {
14916                                 /* Stay in bounds */
14917                                 if (c == '_' && x2 > k) /* startpos_endpos */
14918                                   x2 = k;
14919                                 if (c == ':' && x1 + x2 > k) /* start:length */
14920                                   x2 = -1;
14921                                 debug(F101,">>> x2","",x2);
14922                                 debug(F000,">>> c","",c);
14923                                 if ((q = malloc(k+1))) {
14924                                     strcpy(q,vp); /* safe */
14925                                     if (c == '.') {
14926                                         q[x1+1] = NUL;
14927                                         debug(F000,"XXX. q",q,c);
14928                                     }
14929                                     if (c == ':') { /* start:length */
14930                                         if ((x2 > -1) && ((x1 + x2) <= k)) {
14931                                             q[x1+x2] = NUL;
14932                                         }
14933                                         debug(F000,"XXX: q",q,c);
14934                                     } else if (c == '_') { /* start_endpos */
14935                                         if (x1 >= x2) {
14936                                             q[x1 = 0] = NUL;
14937                                         } else if (x2 < k && x2 > -1) {
14938                                             q[x2] = NUL;
14939                                         }
14940                                         debug(F000,"XXX_ q",q,c);
14941                                     }
14942                                     vp = q+x1;
14943                                 } else vp = NULL;
14944                             } else vp = NULL;
14945                         }
14946
14947                         debug(F110,"XXX vnambuf",vnambuf,0);
14948                         debug(F000,"XXX c","",c);
14949                         debug(F101,"XXX x1","",x1);
14950                         debug(F101,"XXX x2","",x2);
14951                         debug(F110,"XXX result",vp,0);
14952 #ifdef DEBUG
14953                         if (deblog) {
14954                             if (!vp) {
14955                             } else {
14956                                 k = strlen(vp);
14957                             }
14958                         }
14959 #endif /* DEBUG */
14960                     }
14961                 }
14962             } else {                    /* or */
14963                 vp = nvlook(vnambuf);   /* this way for builtin variable */
14964             }
14965             if (vp) {                   /* If definition not empty */
14966                 while ((*new++ = *vp++)) /* copy it to output string. */
14967                   if (--n2 < 0) {
14968                       if (q) free(q);
14969                       debug(F101,"zzstring overflow 4","",depth);
14970 #ifdef DVNAMBUF
14971                       if (vnambuf) free(vnambuf);
14972 #endif /* DVNAMBUF */
14973                       return(-1);
14974                   }
14975                 new--;                  /* Back up over terminating null */
14976                 n2++;                   /* to allow for further deposits. */
14977             }
14978             if (q) {
14979                 free(q);
14980                 q = NULL;
14981             }
14982             break;
14983 #endif /* NOSPL */                      /* Handle \nnn even if NOSPL. */
14984
14985 #ifndef NOKVERBS
14986         case 'K':
14987         case 'k': {
14988             extern struct keytab kverbs[];
14989             extern int nkverbs;
14990 #define K_BUFLEN 30
14991             char kbuf[K_BUFLEN + 1];    /* Key verb name buffer */
14992             int x, y, z, brace = 0;
14993             s += 2;
14994 /*
14995   We assume that the verb name is {braced}, or it extends to the end of the
14996   string, s, or it ends with a space, control character, or backslash.
14997 */
14998             p = kbuf;                   /* Copy verb name into local buffer */
14999             x = 0;
15000             if (*s == '{')  {
15001                 s++;
15002                 brace++;
15003             }
15004             while ((x++ < K_BUFLEN) && (*s > SP) && (*s != CMDQ)) {
15005                 if (brace && *s == '}') {
15006                     s++;
15007                     break;
15008                 }
15009                 *p++ = *s++;
15010             }
15011             brace = 0;
15012             *p = NUL;                   /* Terminate. */
15013             p = kbuf;                   /* Point back to beginning */
15014             debug(F110,"zzstring kverb",p,0);
15015             y = xlookup(kverbs,p,nkverbs,&x); /* Look it up */
15016             debug(F101,"zzstring lookup",0,y);
15017             if (y > -1) {
15018                 dokverb(VCMD,y);
15019 #ifndef NOSPL
15020             } else {                    /* Is it a macro? */
15021                 y = mxlook(mactab,p,nmac);
15022                 if (y > -1) {
15023                     debug(F111,"zzstring mxlook",p,y);
15024                     if ((z = dodo(y,NULL,cmdstk[cmdlvl].ccflgs)) > 0) {
15025                         if (cmpush() > -1) {  /* Push command parser state */
15026                             extern int ifc;
15027                             int ifcsav = ifc; /* Push IF condition on stack */
15028                             y = parser(1);    /* New parser to execute macro */
15029                             cmpop();          /* Pop command parser */
15030                             ifc = ifcsav;     /* Restore IF condition */
15031                             if (y == 0) {     /* No errors, ignore actions */
15032                                 p = mrval[maclvl+1]; /* If OK set return val */
15033                                 if (p == NULL) p = "";
15034                             }
15035                         } else {                /* Can't push any more */
15036                             debug(F101,"zzstring pushed too deep","",depth);
15037                             printf(
15038                                "\n?Internal error: zzstring stack overflow\n"
15039                                    );
15040                             while (cmpop() > -1);
15041                             p = "";
15042                         }
15043                     }
15044                 }
15045 #endif /* NOSPL */
15046             }
15047             break;
15048         }
15049 #endif /* NOKVERBS */
15050
15051         default:                        /* Maybe it's a backslash code */
15052           y = xxesc(&s);                /* Go interpret it */
15053           if (y < 0) {                  /* Upon failure */
15054               *new++ = (char) x;        /* Just quote the next character */
15055               s += 2;                   /* Move past the pair */
15056               n2 -= 2;
15057               if (n2 < 0) {
15058                   debug(F101,"zzstring overflow 5","",depth);
15059 #ifdef DVNAMBUF
15060                   if (vnambuf) free(vnambuf);
15061 #endif /* DVNAMBUF */
15062                   return(-1);
15063               }
15064               continue;                 /* and go back for more */
15065           } else {
15066               *new++ = (char) y;        /* else deposit interpreted value */
15067               if (--n2 < 0) {
15068                   debug(F101,"zzstring overflow 6","",depth);
15069 #ifdef DVNAMBUF
15070                   if (vnambuf) free(vnambuf);
15071 #endif /* DVNAMBUF */
15072                   return(-1);
15073               }
15074           }
15075         }
15076     }
15077     *new = NUL;                         /* Terminate the new string */
15078     debug(F010,"zzstring while exit",*s2,0);
15079
15080     depth--;                            /* Adjust stack depth gauge */
15081     *s2 = new;                          /* Copy results back into */
15082     *n = n2;                            /* the argument addresses */
15083     debug(F101,"zzstring ok","",depth);
15084 #ifdef DVNAMBUF
15085     if (vnambuf) free(vnambuf);
15086 #endif /* DVNAMBUF */
15087     return(0);                          /* and return. */
15088 }
15089 #endif /* NOICP */