makefile patch
[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     { "pictureinfo",FN_PICTURE, 0 },    /* Picture orientation/dimensions */
1114     { "radix",      FN_RADIX, 0 },      /* Radix conversion */
1115 #ifndef NORANDOM
1116     { "random",     FN_RAND, 0},        /* Random number */
1117 #endif /* NORANDOM */
1118 #ifndef NOPUSH
1119     { "rawcommand", FN_RAW,  0},        /* Output from a command (raw) */
1120 #endif /* NOPUSH */
1121 #ifdef RECURSIVE
1122     { "rdirectories", FN_RDIR, 0},      /* Recursive directory list */
1123 #endif /* RECURSIVE */
1124     { "recurse",    FN_RECURSE, 0},     /* Recursive variable evaluation */
1125 #ifdef RECURSIVE
1126     { "rfiles",       FN_RFIL, 0},      /* Recursive file list */
1127 #endif /* RECURSIVE */
1128     { "rep",        FN_REP, CM_INV|CM_ABR},
1129     { "repeat",     FN_REP,  0},        /* Repeat argument given # of times */
1130     { "replace",    FN_RPL,  0},        /* Replace characters in string */
1131     { "reverse",    FN_REV,  0},        /* Reverse the argument string */
1132     { "right",      FN_RIG,  0},        /* Rightmost n characters of string */
1133     { "rindex",     FN_RIX,  0},        /* Right index */
1134     { "rpad",       FN_RPA,  0},        /* Right-pad the argument */
1135     { "rsearch",    FN_RSEARCH, 0},     /* R-L Search for pattern in string */
1136 #ifdef OS2
1137     { "scrncurx",   FN_SCRN_CX,  0},    /* Screen Cursor X Pos */
1138     { "scrncury",   FN_SCRN_CY,  0},    /* Screen Cursor Y Pos */
1139     { "scrnstr",    FN_SCRN_STR, 0},    /* Screen String */
1140 #endif /* OS2 */
1141     { "search",     FN_SEARCH, 0},      /* L-R Search for pattern in string */
1142 #ifndef NOSEXP
1143     { "sexpression",FN_SEXP, 0},        /* S-Expression */
1144 #endif /* NOSEXP */
1145 #ifdef NT
1146     { "shortpathname",FN_SNAME,0},      /* GetShortPathName() */
1147 #else
1148     { "shortpathname",FN_FFN,CM_INV},
1149 #endif /* NT */
1150     { "size",       FN_FS,   0},        /* File size */
1151 #ifdef COMMENT
1152     { "sleep",      FN_SLEEP,0},        /* Sleep for n seconds */
1153 #endif /* COMMENT */
1154     { "span",       FN_SPN,  0},        /* Span - like Snobol */
1155     { "split",      FN_SPLIT,0},        /* Split string into words */
1156     { "squeeze",    FN_SQUEEZE,0},      /* Squeeze whitespace in string */
1157     { "strcmp",     FN_STRCMP,0},       /* String comparison */
1158     { "stringtype", FN_STRINGT,0},      /* String type (7-bit, 8-bit, UTF-8) */
1159     { "stripb",     FN_STB,  0},        /* Strip enclosing braces/brackets */
1160     { "stripn",     FN_STN,  0},        /* Strip n chars */
1161     { "stripx",     FN_STX,  0},        /* Strip suffix */
1162     { "su",         FN_SUB,  CM_INV|CM_ABR},
1163     { "sub",        FN_SUB,  CM_INV|CM_ABR},
1164     { "subs",       FN_SUB,  CM_INV|CM_ABR},
1165     { "subst",      FN_SUB,  CM_INV|CM_ABR},
1166     { "substitute", FN_SUBST,0},        /* Substitute chars */
1167     { "substring",  FN_SUB,  0},        /* Extract substring from argument */
1168     { "tablelook",  FN_TLOOK,0},        /* Table lookup */
1169     { "time",       FN_TIME, 0},        /* Free-format time to hh:mm:ss */
1170     { "tod2secs",   FN_NTIM, CM_INV},   /* Time-of-day-to-secs-since-midnite */
1171     { "todtosecs",  FN_NTIM, CM_INV},   /* Time-of-day-to-secs-since-midnite */
1172     { "trim",       FN_TRM,  0},        /* Trim */
1173     { "unhexify",   FN_UNH,  0},        /* Unhexify */
1174     { "unix2dospath",FN_PC_UD, 0},      /* UNIX to DOS path */
1175     { "unixtodospath",FN_PC_UD, CM_INV}, /* UNIX to DOS path */
1176     { "untabify",   FN_UNTAB,0},        /* Untabify */
1177     { "upper",      FN_UPP,  0},        /* Return uppercased argument */
1178     { "utcdate",    FN_TOGMT,0},        /* Date-time to UTC (GMT) */
1179     { "verify",     FN_VER,  0},        /* Verify */
1180     { "word",       FN_WORD, 0},        /* Extract a word */
1181     { "", 0, 0}
1182 };
1183 int nfuncs = (sizeof(fnctab) / sizeof(struct keytab)) - 1;
1184 #endif /* NOSPL */
1185
1186 #ifndef NOSPL                           /* Buffer for expansion of */
1187 #ifdef BIGBUFOK                         /* built-in variables. */
1188 #define VVBUFL 1024
1189 #else
1190 #define VVBUFL 256
1191 #endif /* BIGBUFOK */
1192 char vvbuf[VVBUFL+1];
1193 #endif /* NOSPL */
1194
1195 struct keytab disptb[] = {              /* Log file disposition */
1196     { "append",    1,  0},
1197     { "new",       0,  0}
1198 };
1199
1200 #ifdef CKFLOAT
1201
1202 /* I N I T F L O A T  --  Deduce floating-point precision by inspection */
1203
1204 int fp_rounding = 0;                /* Nonzero if printf("%f") rounds */
1205 int fp_digits = 0;                  /* Digits of floating point precision */
1206
1207 #ifdef COMMENT
1208 /* For looking at internal floating-point representations */
1209 static char fp_xbuf[128];
1210 static char *
1211 tohex(s, n) CHAR * s; int n; {
1212     int x;
1213     char * p = fp_xbuf;
1214     while (n-- > 0) {
1215         x = (*s >> 4) & 0x0f;
1216         *p++ = hexdigits[x];
1217         x = *s++ & 0x0f;
1218         *p++ = hexdigits[x];
1219     }
1220     *p = NUL;
1221     return((char *)fp_xbuf);
1222 }
1223 #endif /* COMMENT */
1224
1225 char math_pi[] = "3.1415926535897932384626433832795";
1226 char math_e[] =  "2.7182818284590452353602874713527";
1227
1228 VOID
1229 initfloat() {
1230     char * buf = NULL;
1231     int i, x, y;
1232 /*
1233   We malloc a big temporary buffer for sprintf() to minimize likelihood of
1234   (and damage from) sprintf buffer overflows.  In any case, the only way this
1235   could happen would be if sprintf() itself had bugs, since the format
1236   descriptor says to cut it off at 250 decimal places.
1237 */
1238     if ((buf = (char *)malloc(4096))) {
1239         sprintf(buf,"%0.250f",(10.0 / 3.0));
1240         for (i = 2; i < 250 && buf[i] == '3'; i++) ;
1241         x = i - 1;
1242         debug(F111,"initfloat 10.0/3.0",buf,x);
1243         sprintf(buf,"%0.250f",(4.0 / 9.0));
1244         for (i = 2; i < 250 && buf[i] == '4'; i++) ;
1245         y = i - 1;
1246         debug(F111,"initfloat 4.0/9.0",buf,y);
1247         fp_digits = (x < y) ? x : y;
1248         if (fp_digits < sizeof(math_pi) - 1) {
1249             math_pi[fp_digits+1] = NUL;
1250             math_e[fp_digits+1] = NUL;
1251         }
1252         sprintf(buf,"%0.6f",(7.0 / 9.0));
1253         if (buf[7] == '8') fp_rounding = 1;
1254         debug(F111,"initfloat 7.0/9.0",buf,fp_rounding);
1255         debug(F101,"initfloat precision","",fp_digits);
1256         free(buf);
1257     }
1258 }
1259 #endif /* CKFLOAT */
1260
1261 /*
1262   P R E S C A N -- A quick look through the command-line options for
1263   items that must be handled before the initialization file is executed.
1264 */
1265 #ifdef NT
1266 extern int StartedFromDialer;
1267 #endif /* NT */
1268 #ifdef OS2
1269 extern int k95stdio;
1270 unsigned long startflags = 0L;
1271 #endif /* OS2 */
1272
1273 static char *
1274 findinpath(arg) char * arg; {
1275 #ifdef OS2
1276     char * scriptenv, * keymapenv;
1277     int len;
1278 #endif /* OS2 */
1279 #ifdef DCMDBUF
1280     extern char * cmdbuf;
1281 #else
1282     extern char cmdbuf[];
1283 #endif /* DCMDBUF */
1284     char takepath[4096];
1285     char * s;
1286     int x, z;
1287
1288     /* Set up search path... */
1289 #ifdef OS2
1290     char * appdata0 = NULL, *appdata1 = NULL;
1291 #ifdef NT
1292     scriptenv = getenv("K95SCRIPTS");
1293     keymapenv = getenv("K95KEYMAPS");
1294     makestr(&appdata0,(char *)GetAppData(0));
1295     makestr(&appdata1,(char *)GetAppData(1));
1296 #else /* NT */
1297     scriptenv = getenv("K2SCRIPTS");
1298     keymapenv = getenv("K2KEYMAPS");
1299 #endif /* NT */
1300     if (!scriptenv)
1301       scriptenv = getenv("CK_SCRIPTS");
1302     if (!scriptenv)
1303       scriptenv = "";
1304     if (!keymapenv)
1305       keymapenv = getenv("CK_KEYMAPS");
1306     if (!keymapenv)
1307       keymapenv = "";
1308
1309     debug(F110,"startupdir",startupdir,0);
1310     debug(F110,"common appdata directory",appdata1,0);
1311     debug(F110,"appdata directory",appdata0,0);
1312     debug(F110,"inidir",inidir,0);
1313     debug(F110,"home",zhome(),0);
1314     debug(F110,"exedir",exedir,0);
1315
1316     len = strlen(scriptenv) + strlen(keymapenv) + 3*strlen(startupdir)
1317         + 3*strlen(inidir) + 3*strlen(zhome()) + 3*strlen(exedir)
1318         + (appdata0 ? 3*strlen(appdata0) : 0) 
1319         + (appdata1 ? 3*strlen(appdata1) : 0)
1320         + 6*strlen("SCRIPTS/") + 6*strlen("KEYMAPS/") + 16;
1321
1322     if (len >= 4096) {                  /* SAFE (length is checked) */
1323         takepath[0] = '\0';
1324         debug(F111,"findinpath error - path length too long","len",len);
1325     } else
1326       sprintf(takepath,
1327               /* semicolon-separated path list */
1328     "%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",
1329               scriptenv,
1330               (scriptenv[0] && scriptenv[strlen(scriptenv)-1]==';')?"":";",
1331               keymapenv,
1332               (keymapenv[0] && keymapenv[strlen(keymapenv)-1]==';')?"":";",
1333               startupdir,
1334               startupdir, "SCRIPTS/",
1335               startupdir, "KEYMAPS/",
1336               appdata1 ? appdata1 : "", 
1337               appdata1 ? "Kermit 95;" : "",
1338               appdata1 ? appdata1 : "",
1339               appdata1 ? "Kermit 95/SCRIPTS/;" : "",
1340               appdata1 ? appdata1 : "",
1341               appdata1 ? "Kermit 95/KEYMAPS/;" : "",
1342               appdata0 ? appdata0 : "",
1343               appdata0 ? "Kermit 95;" : "",
1344               appdata0 ? appdata0 : "",
1345               appdata0 ? "Kermit 95/SCRIPTS/;" : "",
1346               appdata0 ? appdata0 : "",
1347               appdata0 ? "Kermit 95/KEYMAPS/;" : "",
1348               inidir,
1349               inidir, "SCRIPTS/",
1350               inidir, "KEYMAPS/",
1351               zhome(),
1352               zhome(), "SCRIPTS/",
1353               zhome(), "KEYMAPS/",
1354               exedir,
1355               exedir, "SCRIPTS/",
1356               exedir, "KEYMAPS/"
1357               );
1358     debug(F110,"findinpath takepath",takepath,0);
1359 #ifdef NT
1360     makestr(&appdata0,NULL);
1361     makestr(&appdata1,NULL);
1362 #endif /* NT */
1363 #else /* not OS2 */
1364 #ifndef NOSPL
1365     z = 1024;                           /* Look in home directory */
1366     s = takepath;
1367     zzstring("\\v(home)",&s,&z);
1368 #else
1369     takepath[0] = '\0';
1370 #endif /* NOSPL */
1371 #endif /* OS2 */
1372 /*
1373   All the logic for searching the take path is in the command parser.
1374   So even though we aren't parsing commands, we initialize and call the
1375   parser from here, with the purported filename stuffed into the command
1376   buffer, followed by some carriage returns to make the parser return.
1377   If the file is not found, or otherwise not accessible, the parser prints
1378   an appropriate message, and then we just exit.
1379 */
1380     cmdini();                           /* Allocate command buffers etc */
1381     cmini(0);                           /* Initialize them */
1382     /* Stuff filename into command buf with braces in case of spaces */
1383     ckmakmsg(cmdbuf,CMDBL,"{",arg,"}",NULL);
1384     debug(F110,"findinpath cmdbuf",cmdbuf,0);
1385     ckstrncat(cmdbuf,"\r\r",CMDBL);     /* And some carriage returns */
1386     if (cmifip("","",&s,&x,0,takepath,xxstring) < 0)
1387       return(NULL);
1388     cmres();
1389     return(s);
1390 }
1391
1392 static int tr_int;                      /* Flag if TRANSMIT interrupted */
1393
1394 #ifndef MAC
1395 SIGTYP
1396 #ifdef CK_ANSIC
1397 trtrap(int foo)                         /* TRANSMIT interrupt trap */
1398 #else
1399 trtrap(foo) int foo;                    /* TRANSMIT interrupt trap */
1400 #endif /* CK_ANSIC */
1401 /* trtrap */ {
1402 #ifdef __EMX__
1403     signal(SIGINT, SIG_ACK);
1404 #endif
1405     tr_int = 1;                         /* (Need arg for ANSI C) */
1406     SIGRETURN;
1407 }
1408 #endif /* MAC */
1409 #endif /* NOICP */
1410
1411 #ifdef UNIX
1412 VOID
1413 getexedir() {
1414     extern char * xarg0;
1415     long xx;
1416   /*
1417     Unix provides no standard service for this.  We look in argv[0], and if
1418     we're lucky there's a full pathname.  If not we do a PATH search.
1419   */
1420     if (ckstrchr(xarg0,'/')) {          /* Global copy of argv[0] */
1421         int i, k;
1422         char * p = NULL;
1423         if ((k = ckstrncpy(tmpbuf,xarg0,TMPBUFSIZ-2)) > 0) {
1424             p = tmpbuf;
1425             /* Convert to fully qualified pathname */
1426             if (tmpbuf[0]) if (tmpbuf[0] != '/') {
1427                 line[0] = NUL;
1428                 zfnqfp(tmpbuf,LINBUFSIZ-2,(char *)line);
1429                 if (line[0])
1430                   p = line;
1431             }
1432             xx = zchki(p);
1433             if (xx > -1) {              /* Is the result an existing file? */
1434                 k = strlen(p);
1435                 for (i = k-1; i > 0; i--) { /* Yes, strip name part */
1436                     if (p[i] == '/') {
1437                         if (i < k-1)
1438                           p[i+1] = NUL;
1439                         break;
1440                     }
1441                 }
1442             }
1443             makestr(&exedir,p);         /* Save the result */
1444         }
1445     }
1446     if (!exedir && xarg0) {             /* Not found? */
1447         char * p;
1448         p = getenv("PATH");             /* Search the PATH */
1449         if (p) {                        /* If there is one... */
1450             char * q, * PATH = NULL;
1451             int k;
1452             makestr(&PATH,p);           /* Pokeable copy of PATH string */
1453             if (PATH) {                 /* If malloc succeeded... */
1454                 p = PATH;
1455                 while (p && *p) {        /* Loop through segments */
1456                     q = ckstrchr(p,':'); /* End of this segment */
1457                     if (q == p) {       /* Null PATH segment */
1458                         p++;            /* Skip over colon */
1459                         continue;
1460                     }
1461                     if (q)              /* If not at end of PATH string */
1462                       *q++ = NUL;       /* zero out the colon */
1463                     if ((k = ckstrncpy(tmpbuf,p,TMPBUFSIZ)) > 0) {
1464                         if (tmpbuf[k-1] != '/') { /* Copy this PATH segment */
1465                             tmpbuf[k++] = '/';    /* Append '/' if needed */
1466                             tmpbuf[k] = NUL;
1467                         }
1468                         /* Append the argv[0] value */
1469                         if (ckstrncpy(&tmpbuf[k],xarg0,TMPBUFSIZ) > 0) {
1470                             if (zchki(tmpbuf) > -1) { /* File exists? */
1471                                 tmpbuf[k] = NUL;      /* Yes, we're done */
1472                                 zfnqfp(tmpbuf,LINBUFSIZ,(char *)line);
1473                                 makestr(&exedir,line);
1474                                 break;
1475                             }
1476                         } else break;
1477                     } else break;
1478                     p = q;              /* Not found, go to next segment  */
1479                 } /* while */
1480                 free(PATH);             /* Free PATH copy */
1481             }
1482         }
1483         if (!exedir) {                  /* Still nothing? */
1484             if (zchki(xarg0) > -1) {    /* Maybe it's in the current dir */
1485                 zfnqfp(zgtdir(),LINBUFSIZ,(char *)line);
1486                 makestr(&exedir,line);
1487             }
1488         }
1489     }
1490     if (!exedir) {                      /* Still nothing? */
1491         makestr(&exedir,"/");           /* Fake it with with root. */
1492     }
1493 }
1494 #endif /* UNIX */
1495
1496 int arg_x = 0;
1497 static int x_prescan = 0;
1498
1499 /*
1500   The argument y once meant something but I can't imagine what so now
1501   it's ignored.  (Prior to 22 Aug 98, prescan() was called twice by main(),
1502   and the arg differentiated the two calls.  But this caused all sorts of
1503   problems & confusion, so I commented out the second call.  This issue might
1504   need to be revisited.)
1505 */
1506 VOID
1507 prescan(dummy) int dummy; {             /* Arg is ignored. */
1508     extern int howcalled;
1509     int yargc; char **yargv;
1510     char x;
1511     char *yp, *yy;
1512 #ifdef DEBUG
1513     int debcount = 0;
1514 #endif /* DEBUG */
1515     int z;
1516
1517     if (x_prescan)                      /* Only run once */
1518       return;
1519     x_prescan = 1;
1520
1521     yargc = xargc;                      /* Make copy of arg vector */
1522     yargv = xargv;
1523
1524 #ifndef NOICP
1525 #ifdef DCMDBUF
1526     if (!kermrc)
1527       if (!(kermrc = (char *) malloc(KERMRCL+1)))
1528         fatal("prescan: no memory for kermrc");
1529 #endif /* DCMDBUF */
1530     ckstrncpy(kermrc,KERMRC,KERMRCL);   /* Default init file name */
1531 #endif /* NOICP */
1532
1533 #ifdef OS2
1534     yp = getenv("K95STARTFLAGS");
1535     if (yp) {
1536         startflags = atoi(yp);
1537     }
1538 #endif /* OS2 */
1539
1540 #ifdef IKSD
1541     if (howcalled == I_AM_IKSD)         /* Internet Kermit Service daemon */
1542       inserver = 1;                     /* (See inserver section of ckcmai) */
1543 #endif /* IKSD */
1544
1545 /* Command line options for Kermit */
1546
1547 #ifndef NOCMDL
1548     if (yargc > 1
1549         && *yargv[1] != '-'
1550         && (yargv[1][0] != '=')
1551 #ifdef KERBANG
1552         && (yargv[1][0] != '+')
1553 #endif /* KERBANG */
1554 #ifdef IKSD
1555         && (howcalled != I_AM_IKSD)
1556 #endif /* IKSD */
1557         ) {                             /* Filename as 1st argument */
1558 #ifndef NOICP
1559         char *s;
1560 #endif /* NOICP */
1561 #ifndef NOURL
1562         extern int haveurl;
1563         extern struct urldata g_url;
1564         if (urlparse(yargv[1],&g_url)) {
1565             if (!ckstrcmp(g_url.svc,"ftp",-1,0) ||
1566                 !ckstrcmp(g_url.svc,"ftps",-1,0)) {
1567                 haveurl = 1;
1568                 howcalled = I_AM_FTP;
1569             } else if (!ckstrcmp(g_url.svc,"telnet",-1,0) ||
1570                        !ckstrcmp(g_url.svc,"telnets",-1,0)) {
1571                 haveurl = 1;
1572                 howcalled = I_AM_TELNET;
1573             } else if (!ckstrcmp(g_url.svc,"ssh",-1,0)) {
1574                 haveurl = 1;
1575                 howcalled = I_AM_SSH;
1576             } else if (!ckstrcmp(g_url.svc,"iksd",-1,0) ||
1577                        !ckstrcmp(g_url.svc,"kermit",-1,0)) {
1578                 haveurl = 1;
1579                 howcalled = I_AM_KERMIT;
1580             } else if (!ckstrcmp(g_url.svc,"http",-1,0) ||
1581                        !ckstrcmp(g_url.svc,"https",-1,0)) {
1582                 haveurl = 1;
1583                 howcalled = I_AM_HTTP;
1584             }
1585             if (haveurl) {
1586                 while (--yargc > 0) {   /* Go through command-line args */
1587                     yargv++;            /* looking for -Y and -d */
1588                     yp = *yargv+1;
1589                     if (**yargv == '-') {
1590                         x = *(*yargv+1);
1591                         while (x) {
1592                             switch (x) {
1593 #ifndef NOICP
1594                               case '+':
1595                               case '-':
1596                                 if (doxarg(yargv,1) < 0) {
1597                                     fatal("Extended argument error");
1598                                 }
1599                                 yp = "";
1600                                 break;
1601 #endif /* NOICP */
1602                               case 'Y':
1603                                 noinit++;
1604                                 break;
1605
1606                               case 'q':
1607                                 quiet = 1;
1608                                 break;
1609
1610                               case 'h':
1611                                   noinit = 1;
1612 #ifdef OS2
1613                                   startflags |= 2;    /* No network DLLs */
1614                                   startflags |= 4;    /* No TAPI DLLs */
1615                                   startflags |= 8;    /* No Security DLLs */
1616                                   startflags |= 16;   /* No Zmodem DLLs */
1617                                   startflags |= 32;   /* Stdin */
1618                                   startflags |= 64;   /* Stdout */
1619 #endif /* OS2 */
1620                                   break;
1621                               case 'd': /* = SET DEBUG ON */
1622 #ifdef DEBUG
1623                                 if (debcount++ > 0)
1624                                   debtim = 1;
1625                                 if (!deblog)
1626                                   deblog = debopn("debug.log",0);
1627 #endif /* DEBUG */
1628                                 break;
1629 #ifdef OS2
1630                               case 'W':
1631                                 if (*(yp+1))
1632                                   fatal("invalid argument bundling after -W");
1633                                 yargv++, yargc--;
1634                                 if (yargc < 1)
1635                                   fatal("Window handle missing");
1636                                 hwndDialer = (HWND) atol(*yargv);
1637                                 StartedFromDialer = 1;
1638                                 yargv++, yargc--;
1639                                 KermitDialerID = atol(*yargv) ;
1640                                 break;
1641                               case '#': /* K95 initialization options */
1642                                 if (*(yp+1)) {
1643                                     fatal("invalid argument bundling");
1644                                 }
1645                                 yargv++, yargc--;
1646                                 if (yargc < 1)
1647                                   fatal("-# argument missing");
1648                                 startflags |= atol(*yargv);
1649                                 break;
1650 #endif /* OS2 */
1651                             }
1652                             if (!yp)
1653                               break;
1654                             x = *++yp;
1655                         }
1656                     }
1657                 }
1658                 return;
1659             }
1660         }
1661         /* after this point non-Kermit personalities must return */
1662         switch (howcalled) {
1663           case I_AM_KERMIT:
1664           case I_AM_IKSD:
1665           case I_AM_SSHSUB:
1666             break;
1667           default:
1668             return;
1669         }
1670 #endif /* NOURL */
1671
1672 #ifndef NOICP
1673         /* If it is not a URL that we recognize, try to treat it as a file */
1674
1675         if (!isabsolute(yargv[1]))      /* If not absolute */
1676           s = findinpath(yargv[1]);     /* Look in PATH */
1677         else
1678           s = yargv[1];
1679         if (!s)
1680           doexit(BAD_EXIT,xitsta);
1681         zfnqfp(s,CKMAXPATH,cmdfil);     /* In case of CD in file */
1682         yargc -= 1;                     /* Skip past the filename */
1683         yargv += 1;                     /* Otherwise we'll get an error */
1684 #endif /* NOICP */
1685     }
1686
1687 #ifndef NOCMDL
1688 #ifdef NEWFTP
1689     if (howcalled == I_AM_FTP) {        /* Kermit's FTP client personality */
1690         while (--yargc > 0) {           /* Go through command-line args */
1691             yargv++;                    /* looking for -Y and -d */
1692             yp = *yargv+1;
1693             if (**yargv == '-') {
1694                 x = *(*yargv+1);
1695                 while (x) {
1696                     switch (x) {
1697 #ifndef NOICP
1698                       case '+':
1699                       case '-':
1700                         if (doxarg(yargv,1) < 0) {
1701                             fatal("Extended argument error");
1702                         }
1703                         yp = "";
1704                         break;
1705 #endif /* NOICP */
1706                       case 'Y':
1707                         noinit++;
1708                         break;
1709
1710                       case 'q':
1711                           quiet = 1;
1712                           break;
1713
1714                       case 'h':
1715                         noinit = 1;
1716 #ifdef OS2
1717                         startflags |= 2;    /* No network DLLs */
1718                         startflags |= 4;    /* No TAPI DLLs */
1719                         startflags |= 8;    /* No Security DLLs */
1720                         startflags |= 16;   /* No Zmodem DLLs */
1721                         startflags |= 32;   /* Stdin */
1722                         startflags |= 64;   /* Stdout */
1723 #endif /* OS2 */
1724                         break;
1725                       case 'd':             /* = SET DEBUG ON */
1726 #ifdef DEBUG
1727                         if (debcount++ > 0)
1728                           debtim = 1;
1729                         if (!deblog)
1730                           deblog = debopn("debug.log",0);
1731 #endif /* DEBUG */
1732                         break;
1733 #ifdef OS2
1734                       case 'W':
1735                         if (*(yp+1))
1736                           fatal("invalid argument bundling after -W");
1737                         yargv++, yargc--;
1738                         if (yargc < 1)
1739                           fatal("Window handle missing");
1740                         hwndDialer = (HWND) atol(*yargv);
1741                         StartedFromDialer = 1;
1742                         yargv++, yargc--;
1743                         KermitDialerID = atol(*yargv) ;
1744                         break;
1745                       case '#':         /* K95 initialization options */
1746                         if (*(yp+1)) {
1747                             fatal("invalid argument bundling");
1748                         }
1749                         yargv++, yargc--;
1750                         if (yargc < 1)
1751                           fatal("-# argument missing");
1752                         startflags |= atol(*yargv);
1753                         break;
1754 #endif /* OS2 */
1755                     }
1756                     if (!yp)
1757                       break;
1758                     x = *++yp;
1759                 }
1760             }
1761         }
1762         return;
1763     }
1764 #endif /* NEWFTP */
1765 #endif /* NOCMDL */
1766
1767     while (--yargc > 0) {               /* Go through command-line args */
1768         yargv++;
1769         yp = *yargv+1;                  /* Pointer for bundled args */
1770         if (**yargv == '=')             /* Same rules as cmdlin()... */
1771           return;
1772         debug(F110,"prescan *yargv",*yargv,0);
1773
1774 #ifndef NOICP
1775 #ifdef KERBANG
1776         yy = *yargv;
1777         if (!strcmp(yy,"+") || (*yy == '+' && *(yy+1) < (char)33)) {
1778             char * s;
1779             yargv++;
1780             noinit = 1;
1781             if (!*yargv)
1782               return;
1783             cfilef = 1;
1784             s = findinpath(*yargv);
1785             if (s) {
1786                 zfnqfp(s,CKMAXPATH,cmdfil);
1787                 return;
1788             } else
1789               doexit(BAD_EXIT,xitsta);
1790         }
1791 #endif /* KERBANG */
1792 #endif /* NOICP */
1793         if (!strcmp(*yargv,"--"))       /* getopt() conformance */
1794           return;
1795 #ifdef VMS
1796         else if (**yargv == '/')
1797           continue;
1798 #endif /* VMS */
1799         else if (**yargv == '-') {      /* Got an option (begins with dash) */
1800             x = *(*yargv+1);            /* Get option letter */
1801             while (x) {                 /* Allow for bundled options */
1802                 debug(F000,"prescan arg","",x);
1803                 switch (x) {
1804 #ifndef NOICP
1805                   case '+':
1806                   case '-':
1807                     if (doxarg(yargv,1) < 0) {
1808                         fatal("Extended argument error");
1809                     }
1810 #ifndef COMMENT                         /* Jeff 28 Apr 2003 */
1811                     yp = NULL;          /* (not "") */
1812 #else
1813                     yargv++, yargc--;
1814                     yp = *yargv;
1815 #endif /* COMMENT */
1816                     break;
1817 #endif /* NOICP */
1818
1819                   case '7':             /* Undocumented... */
1820                     sstelnet = 1;       /* (because it doesn't work) */
1821                     break;
1822 #ifdef IKSD
1823                   case 'A': {
1824                       char * p;
1825                       inserver = 1;     /* Flag that we are doing this */
1826                       srvcdmsg = 2;     /* Preset this */
1827                       /* See inserver section of ckcmai.c for more settings */
1828 #ifdef OS2
1829                       if (*(yp+1)) {
1830                           fatal("invalid argument bundling after -A");
1831                       }
1832 #ifdef NT
1833                       /* Support for Pragma Systems Telnet/Terminal Servers */
1834                       p = getenv("PRAGMASYS_INETD_SOCK");
1835                       if (p && atoi(p) != 0) {
1836                           ttname[0] = '$';
1837                           ckstrncpy(&ttname[1],p,TTNAMLEN-1);
1838                           break;
1839                       }
1840 #endif /* NT */
1841                       yargv++, yargc--;
1842                       if (yargc < 1 || **yargv == '-') {
1843                           fatal("-A argument missing");
1844                       } else {
1845                           ttname[0] = '$';
1846                           ckstrncpy(&ttname[1],*yargv,TTNAMLEN-1);
1847                       }
1848 #endif /* OS2 */
1849                       break;
1850                   }
1851 #endif /* IKSD */
1852
1853 #ifdef OS2
1854                   case 'W':
1855                     if (*(yp+1))
1856                       fatal("invalid argument bundling after -W");
1857                     yargv++, yargc--;
1858                     if (yargc < 1)
1859                       fatal("Window handle missing");
1860 #ifdef COMMENT
1861                     if (dummy) {
1862                         yargv++, yargc--;
1863                         break;
1864                     } else {
1865 #endif /* COMMENT */
1866                         hwndDialer = (HWND) atol(*yargv);
1867                         StartedFromDialer = 1;
1868                         yargv++, yargc--;
1869                         KermitDialerID = atol(*yargv) ;
1870 #ifdef COMMENT
1871                     }
1872 #endif /* COMMENT */
1873                     break;
1874
1875                   case '#':             /* K95 initialization options */
1876                     if (*(yp+1)) {
1877                         fatal("invalid argument bundling");
1878                     }
1879                     yargv++, yargc--;
1880                     if (yargc < 1)
1881                       fatal("-# argument missing");
1882                     startflags |= atol(*yargv);
1883                     break;
1884 #endif /* OS2 */
1885
1886 #ifndef NOSPL
1887                   case 'M':                             /* My User Name */
1888                     if (*(yp+1)) {
1889                         fatal("invalid argument bundling");
1890                     }
1891                     yargv++, yargc--;
1892                     if ((yargc < 1) || (**yargv == '-')) {
1893                         fatal("missing username");
1894                     }
1895                     if ((int)strlen(*yargv) > UIDBUFLEN) {
1896                         fatal("username too long");
1897                     }
1898 #ifdef COMMENT
1899 /*
1900   This can't work.  uidbuf is overwritten in sysinit() which has yet to be
1901   called.  This cannot be set in prescan().
1902 */
1903 #ifdef IKSD
1904                     if (!inserver)
1905 #endif /* IKSD */
1906                       ckstrncpy(uidbuf,*yargv,UIDBUFLEN);
1907 #endif /* COMMENT */
1908                     break;
1909 #endif /* NOSPL */
1910                   case 'R':             /* Remote-only advisory */
1911 #ifdef CK_IFRO
1912                     remonly = 1;
1913 #endif /* CK_IFRO */
1914                     break;
1915                   case 'S':             /* STAY */
1916                     stayflg = 1;
1917                     break;
1918                   case 'h':
1919                     noinit = 1;
1920 #ifdef OS2
1921                     startflags |= 2;    /* No network DLLs */
1922                     startflags |= 4;    /* No TAPI DLLs */
1923                     startflags |= 8;    /* No Security DLLs */
1924                     startflags |= 16;   /* No Zmodem DLLs */
1925                     startflags |= 32;   /* Stdin */
1926                     startflags |= 64;   /* Stdout */
1927 #endif /* OS2 */
1928                     break;
1929 #ifndef NOICP
1930                   case 'Y':             /* No init file */
1931                     noinit = 1;
1932                     break;
1933 #endif /* NOICP */
1934
1935                   case 'q':
1936                       quiet = 1;
1937                       break;
1938
1939                   case 'd':             /* = SET DEBUG ON */
1940 #ifdef DEBUG
1941                     if (debcount++ > 0)
1942                       debtim = 1;
1943                     if (!deblog)
1944                       deblog = debopn("debug.log",0);
1945 #endif /* DEBUG */
1946                     break;
1947
1948                   case 'x':             /* Server */
1949                     arg_x = 1;          /* Note in advance */
1950                     break;
1951 #ifndef NOICP
1952                   case 'y':             /* Alternative init file */
1953                     noinit = 0;
1954                     yargv++, yargc--;
1955                     if (yargc < 1) fatal("missing name in -y");
1956                     /* Replace init file name */
1957                     ckstrncpy(kermrc,*yargv,KERMRCL);
1958                     rcflag = 1;         /* Flag that this has been done */
1959                     debug(F111,"prescan kermrc",kermrc,rcflag);
1960                     break;
1961 #endif /* NOICP */
1962                   case 'z':             /* = SET BACKGROUND OFF */
1963                     bgset = 0;
1964                     backgrd = 0;
1965 #ifdef VMS
1966                     batch = 0;
1967 #endif /* VMS */
1968                     break;
1969
1970                   case 'B':             /* Force background (batch) */
1971                     bgset = 1;
1972                     backgrd = 1;
1973 #ifdef VMS
1974                     batch = 1;
1975 #endif /* VMS */
1976                     break;
1977
1978 #ifdef CK_NETBIOS
1979                   case 'N':
1980                     {
1981                         int n ;
1982                         yargv++, yargc--;
1983 #ifdef COMMENT
1984                         if (y)
1985                           break;
1986 #endif /* COMMENT */
1987                         if (strlen(*yargv) != 1 || (*yargv)[0] == 'X') {
1988                             NetBiosAdapter = -1;
1989                         } else {
1990                             n = atoi(*yargv);
1991                             if (n >= 0 && n <= 9)
1992                               NetBiosAdapter = n;
1993                             else
1994                               NetBiosAdapter = -1;
1995                         }
1996                     }
1997                     break;
1998 #endif /* CK_NETBIOS */
1999                   default:
2000                     break;
2001                 }
2002                 if (!yp)
2003                   break;
2004                 x = *++yp;              /* See if options are bundled */
2005             }
2006         }
2007     }
2008 #endif /* NOCMDL */
2009 }
2010
2011 /*  G E T T C S  --  Get Transfer (Intermediate) Character Set  */
2012
2013 /*
2014   Given two file character sets, this routine picks out the appropriate
2015   "transfer" character set to use for translating between them.
2016   The transfer character set number is returned.
2017
2018   Translation between two file character sets is done, for example,
2019   by the CONNECT, TRANSMIT, and TRANSLATE commands.
2020
2021   Translation between Kanji character sets is not yet supported.
2022 */
2023 int
2024 gettcs(cs1,cs2) int cs1, cs2; {
2025 #ifdef NOCSETS                          /* No character-set support */
2026     return(0);                          /* so no translation */
2027 #else
2028     int tcs = TC_TRANSP;
2029 #ifdef KANJI
2030 /* Kanji not supported yet */
2031     if (fcsinfo[cs1].alphabet == AL_JAPAN ||
2032         fcsinfo[cs2].alphabet == AL_JAPAN )
2033       tcs = TC_TRANSP;
2034     else
2035 #endif /* KANJI */
2036 #ifdef CYRILLIC
2037 /*
2038   I can't remember why we don't test both sets here, but I think there
2039   must have been a reason...
2040 */
2041       if (fcsinfo[cs2].alphabet == AL_CYRIL)
2042         tcs = TC_CYRILL;
2043       else
2044 #endif /* CYRILLIC */
2045 #ifdef HEBREW
2046           if (fcsinfo[cs1].alphabet == AL_HEBREW ||
2047               fcsinfo[cs2].alphabet == AL_HEBREW )
2048             tcs = TC_HEBREW;
2049           else
2050 #endif /* HEBREW */
2051 #ifdef GREEK
2052           if (fcsinfo[cs1].alphabet == AL_GREEK ||
2053               fcsinfo[cs2].alphabet == AL_GREEK )
2054             tcs = TC_GREEK;
2055           else
2056 #endif /* GREEK */
2057
2058             /* Roman sets ... */
2059
2060 #ifdef LATIN2                           /* East European */
2061         if (cs1 == FC_2LATIN  || cs2 == FC_2LATIN || /* Latin-2 */
2062             cs1 == FC_CP852   || cs2 == FC_CP852  || /* CP852 */
2063             cs1 == FC_CP1250  || cs2 == FC_CP1250 || /* Windows Latin-2 */
2064             cs1 == FC_MAZOVIA || cs2 == FC_MAZOVIA)  /* Polish Mazovia */
2065           tcs = TC_2LATIN;
2066         else
2067 #endif /* LATIN2 */
2068                                         /* West European Euro-aware */
2069           if (cs1 == FC_CP858 || cs1 == FC_9LATIN ||
2070               cs2 == FC_CP858 || cs2 == FC_9LATIN)
2071             tcs = TC_9LATIN;
2072           else                          /* Traditional West European */
2073             tcs = TC_1LATIN;
2074     return(tcs);
2075 #endif /* NOCSETS */
2076 }
2077
2078 #ifndef NOLOCAL
2079 /*  D O C O N E C T  --  Do the connect command  */
2080 /*
2081   q = 0 means issue normal informational message about how to get back, etc.
2082   q != 0 means to skip the message.
2083 */
2084
2085 int
2086 doconect(q,async) int q, async; {
2087     int x;                              /* Return code */
2088 #ifdef CK_AUTODL
2089     extern CHAR ksbuf[];
2090 #endif /* CK_AUTODL */
2091 #ifndef NOKVERBS                        /* Keyboard macro material */
2092     extern int keymac, keymacx;
2093 #endif /* NOKVERBS */
2094     extern int justone, adl_err;
2095     int qsave;                          /* For remembering "quiet" value */
2096 #ifdef OS2
2097     extern int term_io;
2098     extern int display_demo;
2099     int term_io_save;
2100 #ifdef KUI
2101     extern int kui_async;
2102 #endif /* KUI */
2103 #endif /* OS2 */
2104     int is_tn = 0;
2105
2106 #ifdef IKSD
2107     if (inserver) {
2108         if (!quiet)
2109           printf("?Sorry, IKSD cannot CONNECT.\r\n");
2110         return(success = 0);
2111     }
2112 #endif /* IKSD */
2113
2114     is_tn =
2115 #ifdef TNCODE
2116       (local && network && IS_TELNET()) || (!local && sstelnet)
2117 #else
2118         0
2119 #endif /* TNCODE */
2120           ;
2121 /*
2122   Saving, changing, and restoring the global "quiet" variable around calls
2123   to conect() to control whether the verbose CONNECT message is printed is
2124   obviously less elegant than passing a parameter to conect(), but we do it
2125   this way to avoid the need to change all of the ck?con.c modules.  NOTE:
2126   it is important to restore the value immediately upon return in case there
2127   is an autodownload or APC.
2128 */
2129     qsave = quiet;                      /* Save it */
2130     if (!quiet && q > -1)
2131       quiet = q;                        /* Use argument temporarily */
2132     conres();                           /* Put console back to normal */
2133     debug(F101,"doconect justone 1","",justone);
2134 #ifdef CK_AUTODL
2135     ksbuf[0] = NUL;                     /* Autodownload packet buffer */
2136 #endif /* CK_AUTODL */
2137 #ifdef OS2
2138     display_demo = 1;                   /* Remember to display demo */
2139 #endif /* OS2 */
2140
2141 #ifdef IKS_OPTION
2142     if (is_tn && TELOPT_U(TELOPT_KERMIT) && ttchk() >= 0
2143 #ifdef OS2
2144        && !viewonly
2145 #endif /* OS2 */
2146         ) {
2147         /* If the remote side is in a state of IKS START-SERVER    */
2148         /* we request that the state be changed.  We will detect   */
2149         /* a failure to adhere to the request when we call ttinc() */
2150         if (!iks_wait(KERMIT_REQ_STOP,0) && !tcp_incoming) {
2151             if (!quiet) {
2152                 printf("\r\nEnter Client/Server Mode...  Use:\r\n\r\n");
2153                 printf(
2154 " REMOTE LOGIN <user> <password> to log in to the server if necessary.\r\n");
2155                 printf(" SEND and GET for file transfer.\r\n");
2156                 printf(" REMOTE commands for file management.\r\n");
2157                 printf(" FINISH to terminate Client/Server mode.\r\n");
2158                 printf(" BYE to terminate and close connection.\r\n");
2159                 printf(" REMOTE HELP for additional information.\r\n\r\n");
2160             }
2161             quiet = qsave;
2162             return(0);      /* Failure */
2163         }
2164     }
2165
2166     /* Let our peer know our state. */
2167 #ifdef CK_AUTODL
2168     if (is_tn && TELOPT_ME(TELOPT_KERMIT)
2169 #ifdef OS2
2170         && !viewonly
2171 #endif /* OS2 */
2172          ) {
2173         if (autodl && !TELOPT_SB(TELOPT_KERMIT).kermit.me_start) {
2174             tn_siks(KERMIT_START);      /* Send Kermit-Server Start */
2175         } else if (!autodl && TELOPT_SB(TELOPT_KERMIT).kermit.me_start) {
2176             tn_siks(KERMIT_STOP);
2177         }
2178     }
2179 #else /* CK_AUTODL */
2180     if (is_tn && TELOPT_ME(TELOPT_KERMIT) &&
2181         TELOPT_SB(TELOPT_KERMIT).kermit.me_start)
2182         tn_siks(KERMIT_STOP);
2183 #endif /* CK_AUTODL */
2184 #endif /* IKS_OPTION */
2185
2186     debug(F101,"doconect flow","",flow);
2187 #ifdef OS2
2188     debug(F101,"doconect async","",async);
2189 #ifdef KUI
2190     if (kui_async)
2191       async = 1;;
2192 #endif /* KUI */
2193     x = conect(async);                  /* Connect the first time */
2194 #else /* OS2 */
2195     x = conect();
2196 #endif /* OS2 */
2197     debok = 1;
2198
2199 #ifdef IKS_OPTION
2200     if (TELOPT_U(TELOPT_KERMIT) &&
2201         TELOPT_SB(TELOPT_KERMIT).kermit.u_start &&
2202         !tcp_incoming && !quiet && ttchk() >= 0
2203         ) {
2204         printf("\r\nEnter Client/Server Mode...  Use:\r\n\r\n");
2205         printf(
2206 " REMOTE LOGIN <user> <password> to log in to the server if necessary.\r\n");
2207         printf(" SEND and GET for file transfer.\r\n");
2208         printf(" REMOTE commands for file management.\r\n");
2209         printf(" FINISH to terminate Client/Server mode.\r\n");
2210         printf(" BYE to terminate and close connection.\r\n");
2211         printf(" REMOTE HELP for additional information.\r\n\r\n");
2212     }
2213 #endif /* IKS_OPTION */
2214
2215     quiet = qsave;                      /* Restore "quiet" value */
2216     debug(F101,"doconect justone 2","",justone);
2217
2218 #ifdef NETCONN
2219     if (network && tn_exit && ttchk() < 0)
2220       doexit(GOOD_EXIT,xitsta);         /* Exit with good status */
2221 #endif /* NETCONN */
2222
2223 #ifdef OS2ORUNIX
2224     /* Exit on disconnect if the port is not open or carrier detect */
2225     if (exitonclose && (ttchk() < 0))
2226       doexit(GOOD_EXIT,xitsta);
2227 #endif /* OS2ORUNIX */
2228
2229 #ifdef CKCONINTB4CB
2230     /* The order makes a difference in HP-UX 8.00. */
2231     /* The other order makes it think it's in the background when it */
2232     /* returns from CONNECT (Apr 1999). */
2233     setint();
2234     concb((char)escape);                /* Restore console for commands */
2235 #else
2236     /* This is how it has always been so better leave it */
2237     /* this way for all non-HP-UX-8.00 builds. */
2238     concb((char)escape);                /* Restore console for commands */
2239     setint();
2240 #endif /* CKCONINTB4CB */
2241
2242 #ifdef OS2
2243     if (!async) {
2244         term_io_save = term_io;         /* Disable I/O by emulator */
2245         term_io = 0;
2246 #endif /* OS2 */
2247
2248 #ifdef CK_APC
2249 /*
2250   If an APC command was received during CONNECT mode, we define it now
2251   as a macro, execute the macro, and then return to CONNECT mode.
2252   We do this in a WHILE loop in case additional APCs come during subsequent
2253   CONNECT sessions.
2254 */
2255         debug(F101,"doconect apcactive","",apcactive);
2256         debug(F101,"doconect success","",success);
2257
2258         while (x > 0 && (apcactive == APC_LOCAL ||
2259                          (apcactive == APC_REMOTE && apcstatus != APC_OFF))) {
2260             debug(F101,"doconect justone 3","",justone);
2261             if (mlook(mactab,"_apc_commands",nmac) == -1) {
2262                 debug(F110,"doconect about to execute APC",apcbuf,0);
2263                 domac("_apc_commands",apcbuf,cmdstk[cmdlvl].ccflgs|CF_APC);
2264                 delmac("_apc_commands",1);
2265 #ifdef DEBUG
2266             } else {
2267                 debug(F100,"doconect APC in progress","",0);
2268 #endif /* DEBUG */
2269             }
2270             debug(F101,"doconect apcactive after domac","",apcactive);
2271             if (!apcactive) {               /* In case CLEAR APC was in APC */
2272                 debug(F101,"doconect quit APC loop: apcactive","",apcactive);
2273                 break;
2274             }
2275             /* Also don't reconnect if autodownload failed - very confusing! */
2276             /* Let them view the local screen to see what happened. - fdc */
2277
2278             /* This should be conditional.  If someone is relying on the */
2279             /* connect mode autodownload for the kermit server to use with */
2280             /* a remotely executed script we should be able to return to */
2281             /* connect mode on the failure.  What we really need to do is */
2282             /* report the status of the transfer and then return to CONNECT. */
2283             /* In unix this would simply be a printf(), but in K95 it could */
2284             /* use a popup dialog to report the status. - Jeff */
2285
2286 #ifndef NOXFER
2287             debug(F101,"doconect xferstat","",xferstat);
2288             if (apcactive == APC_LOCAL && !xferstat && adl_err != 0) {
2289                 debug(F101,"doconect quit APC loop: xferstat","",xferstat);
2290                 apcactive = APC_INACTIVE;
2291                 break;
2292             }
2293 #endif /* NOXFER */
2294 #ifdef OS2
2295             msleep(250);
2296 #endif /* OS2 */
2297             debug(F101,"doconect justone 4","",justone);
2298             qsave = quiet;              /* Do this again... */
2299             if (!quiet && q > -1)
2300               quiet = q;
2301 #ifdef CK_AUTODL
2302             ksbuf[0] = NUL;
2303 #endif /* CK_AUTODL */
2304 #ifdef IKS_OPTION
2305 #ifdef CK_AUTODL
2306             if (is_tn &&
2307                 TELOPT_ME(TELOPT_KERMIT) &&
2308                 !TELOPT_SB(TELOPT_KERMIT).kermit.me_start &&
2309                 autodl
2310 #ifdef CK_APC
2311                 && !apcactive
2312 #endif /* CK_APC */
2313 #ifdef OS2
2314                 && !viewonly
2315 #endif /* OS2 */
2316                 ) {
2317                 tn_siks(KERMIT_START);  /* Send Kermit-Server Start */
2318             }
2319 #endif /* CK_AUTODL */
2320 #endif /* IKS_OPTION */
2321 #ifndef OS2
2322             x = conect();               /* Re-CONNECT. */
2323 #else /* OS2 */
2324             x = conect(0);
2325             term_io = term_io_save;
2326 #endif /* OS2 */
2327             debok = 1;
2328             quiet = qsave;
2329             debug(F101,"doconect justone 5","",justone);
2330 #ifdef NETCONN
2331             if (network && ttchk() < 0) {
2332                 if (tn_exit || exitonclose)
2333                   doexit(GOOD_EXIT,xitsta);
2334                 else
2335                   break;
2336             }
2337 #endif /* NETCONN */
2338
2339 #ifdef OS2ORUNIX
2340             /* If connection dropped */
2341             if (ttchk() < 0) {
2342                 concb((char)escape);    /* Restore console. */
2343                 if (exitonclose)
2344                   doexit(GOOD_EXIT,xitsta);
2345                 else
2346                   break;
2347             }
2348 #endif /* OS2ORUNIX */
2349         } /* Loop back for more. */
2350 #endif /* CK_APC */
2351
2352 #ifndef NOKVERBS
2353         if ((keymac > 0) && (keymacx > -1)) { /* Executing a keyboard macro? */
2354             /* Set up the macro and return */
2355             /* Do not clear the keymac flag */
2356 #ifdef OS2
2357             term_io = term_io_save;
2358 #endif /* OS2 */
2359             return(dodo(keymacx,NULL,CF_KMAC|cmdstk[cmdlvl].ccflgs));
2360         }
2361 #endif /* NOKVERBS */
2362 #ifdef OS2
2363         term_io = term_io_save;
2364     } /* if (!async) */
2365 #endif /* OS2 */
2366
2367 #ifdef CKCONINTB4CB
2368     /* The order makes a difference in HP-UX 8.00. */
2369     /* The other order makes it think it's in the background when it */
2370     /* returns from CONNECT (Apr 1999). */
2371     setint();
2372     concb((char)escape);                /* Restore console for commands */
2373 #else
2374     /* This is how it has always been so better leave it */
2375     /* this way for all non-HP-UX-8.00 builds. */
2376     concb((char)escape);                /* Restore console for commands */
2377     setint();
2378 #endif /* CKCONINTB4CB */
2379 #ifdef OS2
2380     if (!async)
2381 #endif /* OS2 */
2382       what = W_COMMAND;                 /* Back in command mode. */
2383     return(x);                          /* Done. */
2384 }
2385 #endif /* NOLOCAL */
2386
2387 #ifndef NOICP
2388 #ifdef COMMENT
2389 /*
2390   It seemed that this was needed for OS/2, in which \v(cmdfile) and other
2391   file-oriented variables or functions can return filenames containing
2392   backslashes, which are subsequently interpreted as quotes rather than
2393   directory separators (e.g. see commented section for VN_CMDF below).
2394   But the problem can't be cured at this level.  Example:
2395
2396     type \v(cmdfile)
2397
2398   Without doubling, the filename is parsed correctly, but then when passed
2399   to UNIX 'cat' through the shell, the backslash is removed, and then cat
2400   can't open the file.  With doubling, the filename is not parsed correctly
2401   and the TYPE command fails immediately with a "file not found" error.
2402 */
2403 /*
2404   Utility routine to double all backslashes in a string.
2405   s1 is pointer to source string, s2 is pointer to destination string,
2406   n is length of destination string, both NUL-terminated.
2407   Returns 0 if OK, -1 if not OK (destination string too short).
2408 */
2409 int
2410 dblbs(s1,s2,n) char *s1, *s2; int n; {
2411     int i = 0;
2412     while (*s1) {
2413         if (*s1 == '\\') {
2414             if (++i > n) return(-1);
2415             *s2++ = '\\';
2416         }
2417         if (++i > n) return(-1);
2418         *s2++ = *s1++;
2419     }
2420     *s2 = NUL;
2421     return(0);
2422 }
2423 #endif /* COMMENT */
2424
2425 char *
2426 gmdmtyp() {                             /* Get modem type */
2427 #ifndef NODIAL
2428     int i, x;
2429
2430     debug(F111,"gmdmtyp","mdmtyp",mdmtyp);
2431     debug(F111,"gmdmtyp","mdmsav",mdmsav);
2432
2433     x = mdmtyp;
2434     if (x < 0)                          /* In case of network dialing */
2435       x = mdmsav;
2436     if (x < 1)
2437       return("none");
2438     else
2439       for (i = 0; i < nmdm; i++)
2440         if ((mdmtab[i].kwval == x) && (mdmtab[i].flgs == 0))
2441           return(mdmtab[i].kwd);
2442 #endif /* NODIAL */
2443     return("none");
2444 }
2445
2446 #ifndef NOXMIT
2447 #ifndef NOLOCAL
2448 /*  T R A N S M I T  --  Raw upload  */
2449
2450 /*  Obey current line, duplex, parity, flow, text/binary settings. */
2451 /*  Returns 0 upon apparent success, 1 on obvious failure.  */
2452
2453 /***
2454  Things to add:
2455  . Make both text and binary mode obey set file bytesize.
2456  . Maybe allow user to specify terminators other than CR?
2457  . Maybe allow user to specify prompts other than single characters?
2458  . Make STATISTICS also work for TRANSMIT.
2459  . If TRANSMIT is done without echo, make some kind of (optional) display.
2460  . Make the same optimization for binary-mode transmit that was done for
2461    text-mode (in the no-echo / no-prompt / no-pause case).
2462 ***/
2463
2464 /*  T R A N S M I T  --  Raw upload  */
2465
2466 /*  s is the filename, t is the turnaround (prompt) character  */
2467
2468 /*
2469   Maximum number of characters to buffer.
2470   Must be less than LINBUFSIZ
2471 */
2472 #ifdef OS2
2473 #define XMBUFS 4096                     /* For compatibility with XYZmodem */
2474 #else /* OS2 */
2475 #define XMBUFS 1024
2476 #endif /* OS2 */
2477
2478 #ifdef TNCODE
2479 #ifndef IAC
2480 #define IAC 255
2481 #endif /* IAC */
2482 #endif /* TNCODE */
2483
2484 #define OUTXBUFSIZ 15
2485 static CHAR inxbuf[OUTXBUFSIZ+1];       /* Host-to-screen expansion buffer */
2486 static int inxcount = 0;                /* and count */
2487 static CHAR outxbuf[OUTXBUFSIZ+1];      /* Keyboard-to-host expansion buf */
2488 static int outxcount = 0;               /* and count */
2489
2490 /*  T R A N S M I T  --  Unguarded non-protocol file transmission  */
2491 /*
2492   Call with:
2493     char * s:   Name of file to transmit.
2494     char t:     Turnaround char for text-mode transmission (normally LF).
2495     int xlate:  nonzero = charset translation for text-mode xfer, 0 = skip.
2496     int binary: nonzero = transmit in binary mode, 0 = in text mode.
2497 */
2498 #define XBBUFSIZ 508                    /* For binary blasting */
2499 static CHAR xbbuf[XBBUFSIZ+4];
2500
2501 int
2502 #ifdef CK_ANSIC
2503 transmit(char * s, char t, int xlate, int binary, int xxecho)
2504 #else
2505 transmit(s,t,xlate,binary,xxecho) char *s; char t; int xlate, binary, xxecho;
2506 #endif /* CK_ANSIC */
2507 /* transmit */ {
2508 #ifdef MAC
2509     extern char sstate;
2510     int count = 100;
2511 #else
2512     int count = 0;
2513 #ifdef OS2
2514 #ifdef NT
2515     SIGTYP (* oldsig)(int);             /* For saving old interrupt trap. */
2516 #else /* NT */
2517     SIGTYP (* volatile oldsig)(int);
2518 #endif /* NT */
2519
2520 #else /* OS2 */
2521     SIGTYP (* oldsig)();
2522 #endif /* OS2 */
2523 #endif /* MAC */
2524     int eof = 0;                        /* End of File flag */
2525     int eol = 0;                        /* End of Line flag */
2526     int rc = 1;                         /* Return code. 0=fail, 1=succeed. */
2527     int myflow;                         /* Local copy of global flow... */
2528     int is_tn = 0;                      /* Do Telnet negotiations */
2529     int xbufsiz = XMBUFS;               /* Size of TRANSMIT buffer */
2530     int x, y, c, i;                     /* Int workers... */
2531     int control = 0;                    /* Echo loop control */
2532     long nbytes = 0;                    /* File byte count */
2533     long zz;                            /* Long worker */
2534     char *p;                            /* Char * worker */
2535
2536 #ifdef PIPESEND
2537     extern int pipesend;
2538 #endif /* PIPESEND */
2539
2540 #ifndef NOCSETS
2541     int tcs = TC_TRANSP;                /* Intermediate (xfer) char set */
2542     int langsv = L_USASCII;             /* Save current language */
2543     int unicode = 0;
2544     int tcssize = 0;
2545
2546 #ifdef CK_ANSIC /* ANSI C prototypes... */
2547     CHAR (*sxo)(CHAR);
2548     CHAR (*rxo)(CHAR);
2549     CHAR (*sxi)(CHAR);
2550     CHAR (*rxi)(CHAR);
2551 #else /* Not ANSI C... */
2552     CHAR (*sxo)();
2553     CHAR (*rxo)();
2554     CHAR (*sxi)();
2555     CHAR (*rxi)();
2556 #endif /* CK_ANSIC */
2557 #ifdef UNICODE
2558     union ck_short uc;
2559     int bomorder = 0;
2560 #ifdef CK_ANSIC
2561     extern int (*xl_ufc[MAXFCSETS+1])(USHORT);  /* Unicode to FCS */
2562     extern USHORT (*xl_fcu[MAXFCSETS+1])(CHAR); /* FCS to Unicode */
2563     extern int (*xuf)(USHORT);
2564     extern USHORT (*xfu)(CHAR);
2565 #else
2566     extern int (*xl_ufc[MAXFCSETS+1])();
2567     extern USHORT (*xl_fcu[MAXFCSETS+1])();
2568     extern int (*xuf)();
2569     extern USHORT (*xfu)();
2570 #endif /* CK_ANSIC */
2571 #endif /* UNICODE */
2572 #endif /* NOCSETS */
2573
2574     debug(F101,"xmit t","",t);
2575     debug(F101,"xmit xlate","",xlate);
2576     debug(F101,"xmit binary","",binary);
2577
2578 #ifdef PIPESEND
2579     if (pipesend) {
2580         if (nopush) return(-2);
2581         if (zxcmd(ZIFILE,s) < 1) {
2582             printf("?Can't start command: %s\n",s);
2583             return(0);
2584         }
2585     } else
2586 #endif /* PIPESEND */
2587     if (zopeni(ZIFILE,s) == 0) {        /* Open the file to be transmitted */
2588         printf("?Can't open file %s\n",s);
2589         return(0);
2590     }
2591     x = -1;                             /* Open the communication channel */
2592     if (ttopen(ttname,&x,mdmtyp,cdtimo) < 0) {  /* (no harm if already open) */
2593         printf("Can't open device %s\n",ttname);
2594         return(0);
2595     }
2596     zz = x ? speed : -1L;
2597     if (binary) {                       /* Binary file transmission */
2598         myflow = (flow == FLO_XONX) ? FLO_NONE : flow;
2599
2600         if (ttvt(zz,myflow) < 0) {      /* So no Xon/Xoff! */
2601             printf("Can't condition line\n");
2602             return(0);
2603         }
2604     } else {
2605         if (ttpkt(zz,flow,parity) < 0) { /* Put the line in "packet mode" */
2606             printf("Can't condition line\n"); /* so Xon/Xoff will work, etc. */
2607             return(0);
2608         }
2609     }
2610     is_tn =
2611 #ifdef TNCODE
2612       (local && network && IS_TELNET()) || (!local && sstelnet)
2613 #else
2614         0
2615 #endif /* TNCODE */
2616           ;
2617
2618 #ifndef NOCSETS
2619 /* Set up character set translations */
2620
2621     tcs = 0;                            /* "Transfer" or "Other" charset */
2622     sxo = rxo = NULL;                   /* Initialize byte-to-byte functions */
2623     sxi = rxi = NULL;
2624     unicode = 0;                        /* Assume Unicode won't be involved */
2625     if (!binary && xlate) {             /* Set up charset translations */
2626 /*
2627   In the SENDING direction, we are converting from the local file's
2628   character-set (fcharset) to the remote terminal charset (tcsr).  In the
2629   RECEIVING direction (echoing) we are converting from the remote end of the
2630   terminal charset (tcsr) to its local end (tcsl), which is not necessarily
2631   the same as the file character-set.  Especially when the file character
2632   set is UCS-2, which is not a valid terminal character set.  The various
2633   combinations are represented in this table:
2634
2635   FCS = File Character Set
2636   RCS = Remote Terminal Character Set
2637   CCS = Console (Local Terminal) Character Set
2638
2639    8   4   2   1
2640   FCS FCS RCS CCS
2641   UCS UTF UTF UTF
2642    0   0   0   0   =   0   =   No translation
2643    0   0   0   1   =   1   =   FCS -> RCS, Echo RCS -> UTF
2644    0   0   1   0   =   2   =   FCS -> UTF, Echo UTF -> CCS
2645    0   0   1   1   =   3   =   FCS -> UTF, Echo no translation
2646
2647    0   1   0   0   =   4   =   UTF -> RCS, Echo RCS -> CCS
2648    0   1   0   1   =   5   =   UTF -> RCS, Echo RCS -> UTF
2649    0   1   1   0   =   6   =   UTF -> UTF, Echo UTF -> CCS
2650    0   1   1   1   =   7   =   No translation
2651
2652    1   0   0   0   =   8   =   UCS -> RCS, Echo RCS -> CCS
2653    1   0   0   1   =   9   =   UCS -> RCS, Echo RCS -> UTF
2654    1   0   1   0   =  10   =   UCS -> UTF, Echo UTF -> CCS
2655    1   0   1   1   =  11   =   UCS -> UTF, Echo no translation
2656 */
2657 #ifdef UNICODE
2658         xfu = NULL;                     /* Unicode translation functions */
2659         xuf = NULL;
2660         bomorder = ucsorder;            /* UCS-2 byte order */
2661
2662         if (fcharset == FC_UCS2)        /* File charset is UCS-2 */
2663           unicode |= 8;
2664         else if (fcharset == FC_UTF8)   /* File charset is UTF-8 */
2665           unicode |= 4;
2666         if (tcsr == FC_UTF8)            /* Remote term charset is UTF-8 */
2667           unicode |= 2;
2668         if (tcsl == FC_UTF8)            /* Local term charset is UTF-8 */
2669           unicode |= 1;
2670 #endif /* UNICODE */
2671 /*
2672   When Unicode not involved -- TCS is the intermediate (xfer) set, and:
2673   sxo = File-to-Intermediate charset function
2674   rxo = Intermediate-to-Remote-Terminal charset function
2675   sxi = Remote-Terminal-to-Intermediate
2676   rxi = Intermediate-to-Local-Terminal
2677 */
2678         tcs = gettcs(tcsr,fcharset);    /* Get intermediate set. */
2679         sxo = xls[tcs][fcharset];       /* translation function */
2680         rxo = xlr[tcs][tcsr];           /* pointers for output functions */
2681         sxi = xls[tcs][tcsr];           /* and for input functions. */
2682         rxi = xlr[tcs][tcsl];
2683 /*
2684   At this point we have unicode nonzero if Unicode is involved in the
2685   conversion, and to 0 if it is not.
2686   The following is to prevent use of zmstuff() and zdstuff() by translation
2687   functions (stuffing works with file i/o, not with communication i/o).
2688 */
2689         langsv = language;              /* Save current SET LANGUAGE */
2690         language = L_USASCII;           /* No language-specific translations */
2691     }
2692 #endif /* NOCSETS */
2693
2694     i = 0;                              /* Beginning of buffer. */
2695 #ifndef MAC
2696 #ifndef AMIGA
2697     oldsig = signal(SIGINT, trtrap);    /* Save current interrupt trap. */
2698 #endif /* AMIGA */
2699 #endif /* MAC */
2700     tr_int = 0;                         /* Have not been interrupted (yet). */
2701     rc = 1;                             /* Return code presumed good. */
2702 #ifdef VMS
2703     conres();
2704 #endif /* VMS */
2705
2706 #ifndef NOCSETS
2707     debug(F101,"XMIT unicode","",unicode);
2708 #ifdef UNICODE
2709     debug(F101,"XMIT bomorder","",bomorder);
2710 #endif /* UNICODE */
2711 #endif /* NOCSETS */
2712
2713     c = 0;                              /* Initial condition */
2714     while (c > -1 && !eof) {            /* Loop for all characters in file */
2715         eol = 0;
2716 #ifdef MAC
2717         /*
2718          * It is expensive to run the miniparser so don't do it for
2719          * every character.
2720          */
2721         if (--count < 0) {
2722             count = 100;
2723             miniparser(1);
2724             if (sstate == 'a') {
2725                 sstate = '\0';
2726                 goto xmitfail;
2727             }
2728         }
2729 #else /* Not MAC */
2730         if (tr_int) {                   /* Interrupted? */
2731             printf("^C...\n");          /* Print message */
2732             goto xmitfail;
2733         }
2734 #endif /* MAC */
2735         c = zminchar();                 /* Get a file character */
2736 #ifdef COMMENT
2737 /* too much */
2738 #ifdef DEBUG
2739         if (deblog) {
2740             if (c < 0)
2741               debug(F101,"XMIT zminchar","",c);
2742             else
2743               debug(F000,"XMIT zminchar","",c);
2744         }
2745 #endif /* DEBUG */
2746 #endif /* COMMENT */
2747         if (c < -1) {                   /* Other error */
2748             printf("?TRANSMIT file read error: %s\n",ck_errstr());
2749             goto xmitfail;
2750         } else if (c > -1) {
2751             nbytes++;
2752             c &= fmask;                 /* Apply SET FILE BYTESIZE mask */
2753         } else if (c == -1) {
2754             eof = 1;
2755             debug(F101,"XMIT eof","",eof);
2756         }
2757         if (binary) {                   /* Binary... */
2758             if (c == -1) {              /* If EOF */
2759                 rc = 1;                 /* Success */
2760                 eof = 1;
2761                 goto xmitexit;          /* Done */
2762             }
2763             if (!xmitw && !xxecho) {    /* Special "blast" mode */
2764                 if (count == XBBUFSIZ) { /* File input buffer full... */
2765                     while (count > 0) {
2766                         errno = 0;
2767                         y = ttol(xbbuf,count);
2768                         if (y < 0) {    /* try to send it. */
2769                             printf("?TRANSMIT output error: %s\n",
2770                                    ck_errstr());
2771                             debug(F111,"XMIT binary ttol error",
2772                                   ck_errstr(),errno);
2773                             rc = 0;
2774                             break;
2775                         }
2776                         if (y < 0) break;
2777                         count -= y;
2778                     }
2779                     count = 0;
2780                 }
2781                 xbbuf[count++] = c;
2782 #ifdef TNCODE
2783                 if (c == IAC && is_tn)  /* Telnet IAC */
2784                   xbbuf[count++] = IAC; /* must be doubled */
2785 #endif /* TNCODE */
2786                 continue;
2787             }
2788             if (ttoc(dopar((char) c)) < 0) { /* else just send the char */
2789                 printf("?Can't transmit character\n");
2790                 goto xmitfail;
2791             }
2792 #ifdef TNCODE
2793             if (c == IAC && is_tn)      /* Quote Telnet IAC */
2794               ttoc((char)IAC);
2795 #endif /* TNCODE */
2796
2797             if (xmitw)                  /* Pause if requested */
2798               msleep(xmitw);
2799
2800             if (xxecho) {               /* SET TRANSMIT ECHO ON? */
2801                 if (duplex) {           /* Yes, for half duplex */
2802 #ifndef NOLOCAL
2803 #ifdef OS2
2804                     /* Echo to emulator */
2805                     scriptwrtbuf((USHORT)(c & cmdmsk));
2806 #endif /* OS2 */
2807 #endif /* NOLOCAL */
2808                     if (conoc((char)(c & cmdmsk)) < 0) /* echo locally. */
2809                       goto xmitfail;
2810                 } else {                /* For full duplex, */
2811                     int i, n;           /* display whatever is there. */
2812                     n = ttchk();        /* See how many chars are waiting */
2813                     if (n < 0) {        /* Connection dropped? */
2814                         printf("?Connection lost\n");
2815                         goto xmitfail;
2816                     }
2817                     for (i = 0; i < n; i++) { /* Read and echo that many. */
2818                         x = ttinc(xmitt); /* Timed read just in case. */
2819                         if (x > -1) {   /* If no timeout */
2820                             if (parity) x &= 0x7f; /* display the char, */
2821 #ifndef NOLOCAL
2822 #ifdef OS2
2823                             /* Echo to emulator */
2824                             scriptwrtbuf((USHORT)x);
2825 #endif /* OS2 */
2826 #endif /* NOLOCAL */
2827                             if (conoc((char)(x & cmdmsk)) < 0) {
2828                                 printf("?Output error\n");
2829                                 goto xmitfail;
2830                             }
2831                         } else if (x == -2) {
2832                             printf("Connection closed.\n");
2833                             ttclos(1);
2834                             goto xmitfail;
2835                         } else if (x == -3) {
2836                             printf(
2837                             "Session Limit exceeded - closing connection.\n"
2838                                    );
2839                             ttclos(1);
2840                             goto xmitfail;
2841                         } else {
2842                             printf("?Communications error\n");
2843                             goto xmitfail;
2844                         }
2845                     }
2846                 }
2847             } else ttflui();            /* Not echoing, just flush input. */
2848
2849         } else {                        /* Text mode, line at a time. */
2850 #ifdef UNICODE
2851             if (fcharset == FC_UCS2 && xlate) { /* Special for UCS-2 */
2852                 char xbuf[8];
2853                 x = 1 - (nbytes & 1);   /* Odd or even byte */
2854                 if (x == 0)             /* Note: 1 = the 1st, 0 = 2nd, etc */
2855                   uc.x_short = 0;
2856                 if (bomorder)           /* Little Endian */
2857                   x = 1 - x;            /* Save byte in appropriate half */
2858                 debug(F101,"XMIT UCS2 x","",x);
2859                 uc.x_char[x] = (CHAR) (c & 0xff);
2860                 if (nbytes & 1)         /* First byte, go back for next */
2861                   continue;
2862                 if (nbytes == 2) {      /* UCS-2 Byte Order Mark */
2863                     if (uc.x_short == (USHORT) 0xfeff) {
2864                         debug(F100,"XMIT UCS2 BOM FEFF","",bomorder);
2865                         continue;
2866                     } else if (uc.x_short == (USHORT) 0xfffe) {
2867                         bomorder = 1 - bomorder;
2868                         debug(F100,"XMIT UCS2 BOM FFFE (swap)","",bomorder);
2869                         continue;
2870                     }
2871                 }
2872                 sprintf(xbuf,"%04X",uc.x_short); /* SAFE */
2873                 debug(F111,"XMIT UCS2",xbuf,uc.x_short);
2874                 if (nbytes & 1)         /* Special eol test for UCS-2 */
2875                   if (uc.x_short == '\n')
2876                     eol = 1;
2877 #ifdef COMMENT
2878                 if (uc.x_short == 0x2028 || uc.x_short == 0x2029)
2879                     eol = 1;
2880 #endif /* COMMENT */
2881             } else
2882 #endif /* UNICODE */
2883               if (c == '\n') {          /* Normal eol test otherwise */
2884                   eol = 1;
2885             }
2886             if (eol) {                  /* End of line? */
2887                 int stuff = -1;
2888                 debug(F101,"XMIT eol length","",i);
2889                 if (i == 0) {           /* Blank line? */
2890                     if (xmitf)          /* Yes, insert fill if asked. */
2891                       line[i++] = dopar((char) xmitf);
2892                 }
2893                 if (i == 0 || ((char) line[i-1]) != ((char) dopar(CR)))
2894                   line[i++] = dopar(CR); /* Terminate it with CR */
2895                 if (xmitl) {
2896                     stuff = LF;
2897 #ifdef TNCODE
2898                 } else if (is_tn && (tn_nlm != TNL_CR)) {
2899                     /* TELNET NEWLINE ON/OFF/RAW */
2900                     stuff = (tn_nlm == TNL_CRLF) ? LF : NUL;
2901 #endif /* TNCODE */
2902                 }
2903                 if (stuff > -1)
2904                   line[i++] = dopar((char)stuff);
2905                 line[i] = NUL;
2906                 debug(F111,"XMIT eol line",line,i);
2907
2908             } else if (c != -1) {       /* Not a newline, regular character */
2909                 int k, x;
2910                 outxbuf[0] = c;         /* In case of no translation */
2911                 outxcount = 1;          /* Assume result is one byte */
2912 #ifndef NOCSETS
2913                 switch (unicode) {
2914                   case 0:               /* No Unicode involved */
2915                   case 1:
2916                     if (xlate) {        /* If not /TRANSPARENT */
2917                         /* Local-to-intermediate */
2918                         if (sxo) c = (*sxo)((char)c);
2919                         /* Intermediate-to-remote */
2920                         if (rxo) c = (*rxo)((char)c);
2921                         outxbuf[0] = c;
2922                     }
2923                     break;
2924 #ifdef UNICODE
2925                   case 2:               /* Local byte to UTF-8 */
2926                   case 3:
2927                     xfu = xl_fcu[fcharset];
2928                     tcssize = fcsinfo[fcharset].size;
2929                     outxcount =
2930                       b_to_u((CHAR)c,outxbuf,OUTXBUFSIZ,tcssize);
2931                     break;
2932                   case 4:               /* Local UTF-8 to remote byte */
2933                   case 5:
2934                     xuf = xl_ufc[tcsr];
2935                     x = u_to_b((CHAR)c); /* Convert to byte */
2936                     if (x == -1) {      /* If more input bytes needed */
2937                         continue;       /* go back and get them */
2938                     } else if (x == -2) { /* LS or PS (shouldn't happen) */
2939                         outxbuf[0] = CR;
2940                     } else if (x == -9) { /* UTF-8 error */
2941                         outxbuf[0] = '?'; /* Insert error char */
2942                         outxbuf[1] = u_to_b2(); /* Insert next char */
2943                         outxcount = 2;
2944                     } else {
2945                         outxbuf[0] =    /* Otherwise store result */
2946                           (unsigned)(x & 0xff);
2947                     }
2948                     break;
2949                   case 6:               /* UTF-8 to UTF-8 */
2950                   case 7:
2951                     break;
2952                   case 8:               /* UCS-2 to byte */
2953                   case 9:
2954                     xuf = xl_ufc[tcsr];
2955                     outxbuf[0] = (*xuf)(uc.x_short);
2956                     break;
2957                   case 10:
2958                   case 11: {            /* UCS-2 to UTF-8 */
2959                       int j;
2960                       CHAR * buf = NULL;
2961                       x = ucs2_to_utf8(uc.x_short,&buf);
2962                       if (x < 0) {
2963                           outxbuf[0] = 0xff; /* (= U+FFFD) */
2964                           outxbuf[1] = 0xbd;
2965                           x = 2;
2966                       }
2967                       for (j = 0; j < x; j++)
2968                         outxbuf[j] = buf[j];
2969                       outxcount = x;
2970                       break;
2971                   }
2972 #endif /* UNICODE */
2973                 }
2974 #endif /* NOCSETS */
2975                 outxbuf[outxcount] = NUL;
2976                 debug(F111,"XMIT outxbuf",outxbuf,outxcount);
2977 /*
2978   Now the input character (1 or more bytes) is translated into the output
2979   expansion buffer (1 or more bytes); outxcount = number of bytes to add to
2980   the TRANSMIT line buffer, which we do here, taking care of parity, SI/SO
2981   processing, and quoting Telnet IACs.
2982 */
2983                 for (k = 0; k < outxcount; k++) {
2984                     c = outxbuf[k];
2985                     if (xmits && parity && (c & 0200)) { /* If shifting */
2986                         line[i++] = dopar(SO); /* needs to be done, */
2987                         line[i++] = dopar((char)c); /* do it here, */
2988                         line[i++] = dopar(SI); /* crudely. */
2989                     } else {
2990                         line[i++] = dopar((char)c);
2991 #ifdef TNCODE
2992                         if (c == (CHAR)IAC && is_tn)
2993                           line[i++] = (CHAR)IAC;
2994 #endif /* TNCODE */
2995                     }
2996                 }
2997             }
2998 /*
2999   Send characters if buffer full, or at end of line, or at end of file.
3000   (End of line only if echoing, waiting for a prompt, or pausing.)
3001 */
3002             debug(F000,"XMIT c",ckitoa(i),c);
3003             if (i >= xbufsiz || eof || (eol && (xxecho || xmitw || t))) {
3004                 p = line;
3005                 line[i] = '\0';
3006                 debug(F111,"transmit buf",p,i);
3007                 if (ttol((CHAR *)p,i) < 0) { /* try to send it. */
3008                     printf("?TRANSMIT output error: %s\n",ck_errstr());
3009                     rc = 0;
3010                     break;
3011                 }
3012                 i = 0;                  /* Reset buffer pointer. */
3013 /*
3014   Now we handle the echo.  If the user wants to see it, or if we have to
3015   wait for the turnaround character, t.  If the echo is being displayed,
3016   and terminal character-set translation is required, we do it here.
3017 */
3018                 if (duplex && xxecho) {  /* If local echo, echo it */
3019                     if (parity || cmdmsk == 0x7f) { /* Strip hi bits */
3020                         char *ss = line;             /* if necessary */
3021                         while (*ss) {
3022                             *ss &= 0x7f;
3023                             ss++;
3024                         }
3025                     }
3026 #ifndef NOLOCAL
3027 #ifdef OS2
3028                     {                   /* Echo to emulator */
3029                         char *ss = p;
3030                         while (*ss) {
3031                             scriptwrtbuf((USHORT)*ss);
3032                             ss++;
3033                         }
3034                     }
3035 #endif /* OS2 */
3036 #endif /* NOLOCAL */
3037                     if (conoll(p) < 0)
3038                       goto xmitfail;
3039                 }
3040                 if (xmitw)              /* Sleep TRANSMIT PAUSE interval */
3041                   msleep(xmitw);
3042
3043                 control = 0;            /* Readback loop control */
3044                 if (t != 0 && eol)      /* TRANSMIT PROMPT given and at EOL */
3045                   control |= 1;
3046                 if (xxecho && !duplex)   /* Echo desired and is remote */
3047                   control |= 2;
3048
3049                 if (control) {          /* Do this if reading back the echo */
3050                     int n;
3051                     x = 0;
3052                     while (1) {
3053                         if (control & 1) { /* Termination criterion */
3054                             if (x == t)    /* for turnaround */
3055                               break;
3056                         } else if (control & 2) { /* And for echoing */
3057                             if ((n = ttchk()) < 1)
3058                               break;
3059                         }
3060                         if ((x = ttinc(xmitt)) < 0) { /* Read with timeout */
3061                             switch (x) {
3062                               case -2:
3063                                 printf("Connection closed.\n");
3064                                 ttclos(1);
3065                                 goto xmitfail;
3066                               case -3:
3067                                 printf(
3068                               "Session Limit exceeded - closing connection.\n"
3069                                        );
3070                                 ttclos(1); /* full thru... */
3071                                 goto xmitfail;
3072                               default:
3073                                 printf("?Timeout\n");
3074                                 goto xmitfail;
3075                             }
3076                         }
3077                         if (x > -1 && (control & 2)) { /* Echo any echoes */
3078                             if (parity)
3079                               x &= 0x7f;
3080                             c = x;
3081 #ifndef NOLOCAL
3082 #ifdef OS2
3083                             scriptwrtbuf((USHORT)x);
3084 #endif /* OS2 */
3085 #endif /* NOLOCAL */
3086                             inxbuf[0] = c;
3087                             inxcount = 1;
3088 #ifndef NOCSETS
3089                             switch (unicode & 3) { /* Remote bits */
3090                               case 0:
3091                                 if (xlate) {
3092                                     if (sxi) c = (*sxi)((CHAR)c);
3093                                     if (rxi) c = (*rxi)((CHAR)c);
3094                                     inxbuf[0] = c;
3095                                 }
3096                                 break;
3097 #ifdef UNICODE
3098                               case 1:   /* Remote Byte to local UTF-8 */
3099                                 xfu = xl_fcu[tcsr];
3100                                 tcssize = fcsinfo[tcsr].size;
3101                                 inxcount =
3102                                   b_to_u((CHAR)c,
3103                                          inxbuf,
3104                                          OUTXBUFSIZ,
3105                                          tcssize
3106                                          );
3107                                 break;
3108                               case 2:   /* Remote UTF-8 to local Byte */
3109                                 xuf = xl_ufc[tcsl];
3110                                 x = u_to_b((CHAR)c);
3111                                 if (x < 0)
3112                                   continue;
3113                                 inxbuf[0] = (unsigned)(x & 0xff);
3114                                 break;
3115                               case 3:   /* UTF-8 to UTF-8 */
3116                                 break;
3117 #endif /* UNICODE */
3118                             }
3119 #endif /* NOCSETS */
3120                             inxbuf[inxcount] = NUL;
3121                             if (conxo(inxcount,(char *)inxbuf) < 0)
3122                               goto xmitfail;
3123                         }
3124                     }
3125                 } else                  /* Not echoing */
3126                   ttflui();             /* Just flush input buffer */
3127             } /* End of buffer-dumping block */
3128         } /* End of text mode */
3129         if (eof) {
3130             rc = 1;
3131             goto xmitexit;
3132         }
3133     } /* End of character-reading loop */
3134
3135   xmitfail:                             /* Failure exit point */
3136     rc = 0;
3137
3138   xmitexit:                             /* General exit point */
3139     if (rc > 0) {
3140         if (binary && !xmitw && !xxecho) { /* "blasting"? */
3141             while (count > 0) {            /* Partial buffer still to go? */
3142                 errno = 0;
3143                 y = ttol(xbbuf,count);
3144                 if (y < 0) {
3145                     printf("?TRANSMIT output error: %s\n",
3146                            ck_errstr());
3147                     debug(F111,"XMIT binary eof ttol error",
3148                           ck_errstr(),errno);
3149                     rc = 0;
3150                     break;
3151                 }
3152                 count -= y;
3153             }
3154         } else if (!binary && *xmitbuf) { /* Anything to send at EOF? */
3155             p = xmitbuf;                /* Yes, point to string. */
3156             while (*p)                  /* Send it. */
3157               ttoc(dopar(*p++));        /* Don't worry about echo here. */
3158         }
3159     }
3160
3161 #ifndef AMIGA
3162 #ifndef MAC
3163     signal(SIGINT,oldsig);              /* Put old signal action back. */
3164 #endif /* MAC */
3165 #endif /* AMIGA */
3166 #ifdef VMS
3167     concb(escape);                      /* Put terminal back, */
3168 #endif /* VMS */
3169     zclose(ZIFILE);                     /* Close file, */
3170 #ifndef NOCSETS
3171     language = langsv;                  /* restore language, */
3172 #endif /* NOCSETS */
3173     ttres();                            /* and terminal modes, */
3174     return(rc);                         /* and return successfully. */
3175 }
3176 #endif /* NOLOCAL */
3177 #endif /* NOXMIT */
3178
3179 #ifndef NOCSETS
3180
3181 _PROTOTYP( CHAR (*sxx), (CHAR) );       /* Local translation function */
3182 _PROTOTYP( CHAR (*rxx), (CHAR) );       /* Local translation function */
3183 _PROTOTYP( CHAR zl1as, (CHAR) );        /* Latin-1 to ascii */
3184 _PROTOTYP( CHAR xl1as, (CHAR) );        /* ditto */
3185
3186 /*  X L A T E  --  Translate a local file from one character set to another */
3187
3188 /*
3189   Translates input file (fin) from character set csin to character set csout
3190   and puts the result in the output file (fout).  The two character sets are
3191   file character sets from fcstab.
3192 */
3193
3194 int
3195 xlate(fin, fout, csin, csout) char *fin, *fout; int csin, csout; {
3196
3197 #ifndef MAC
3198 #ifdef OS2
3199     extern int k95stdout;
3200     extern int wherex[], wherey[];
3201     extern unsigned char colorcmd;
3202 #ifdef NT
3203     SIGTYP (* oldsig)(int);             /* For saving old interrupt trap. */
3204 #else /* NT */
3205     SIGTYP (* volatile oldsig)(int);    /* For saving old interrupt trap. */
3206 #endif /* NT */
3207 #else /* OS2 */
3208     SIGTYP (* oldsig)();
3209 #endif /* OS2 */
3210 #endif /* MAC */
3211 #ifdef CK_ANSIC
3212     int (*fn)(char);                    /* Output function pointer */
3213 #else
3214     int (*fn)();
3215 #endif /* CK_ANSIC */
3216     extern int xlatype;
3217     int filecode;                       /* Code for output file */
3218     int scrnflg = 0;
3219
3220     int z = 1;                          /* Return code. */
3221     int x, c, c2;                       /* Workers */
3222 #ifndef UNICODE
3223     int tcs;
3224 #endif /* UNICODE */
3225
3226     ffc = 0L;
3227
3228     if (zopeni(ZIFILE,fin) == 0) {      /* Open the file to be translated */
3229 #ifdef COMMENT
3230         /* An error message was already printed by zopeni() */
3231         printf("?Can't open input file %s\n",fin);
3232 #endif /* COMMENT */
3233         return(0);
3234     }
3235 #ifdef MAC
3236 /*
3237   If user specified no output file, it goes to the screen.  For the Mac,
3238   this must be done a special way (result goes to a new window); the Mac
3239   doesn't have a "controlling terminal" device name.
3240 */
3241     filecode = !strcmp(fout,CTTNAM) ? ZCTERM : ZOFILE;
3242 #else
3243 #ifdef VMS
3244     filecode = !strcmp(fout,CTTNAM) ? ZCTERM : ZMFILE;
3245 #else
3246 #ifdef OS2
3247     filecode = (!stricmp(fout,"con") || !stricmp(fout,"con:")) ?
3248         ZCTERM : ZMFILE;
3249     if ((filecode == ZCTERM) && !k95stdout && !inserver)
3250         csout = FC_UCS2;
3251 #else /* OS2 */
3252     filecode = ZOFILE;
3253 #endif /* OS2 */
3254 #endif /* VMS */
3255 #endif /* MAC */
3256     if (zopeno(filecode,fout,NULL,NULL) == 0) { /* And the output file */
3257         printf("?Can't open output file %s\n",fout);
3258         return(0);
3259     }
3260 #ifndef AMIGA
3261 #ifndef MAC
3262     oldsig = signal(SIGINT, trtrap);    /* Save current interrupt trap. */
3263 #endif /* MAC */
3264 #endif /* AMIGA */
3265
3266     scrnflg = (filecode == ZCTERM);     /* Set output function */
3267     if (scrnflg)
3268       fn = NULL;
3269     else if (filecode == ZMFILE)
3270       fn = putmfil;
3271     else
3272       fn = putfil;
3273
3274     tr_int = 0;                         /* Have not been interrupted (yet). */
3275     z = 1;                              /* Return code presumed good. */
3276
3277     if (!scrnflg && !quiet)
3278       printf(" %s (%s) => %s (%s)\n",   /* Say what we're doing. */
3279              fin, fcsinfo[csin].keyword,
3280              fout,fcsinfo[csout].keyword
3281              );
3282
3283 #ifndef UNICODE
3284 /*
3285   Non-Unicode picks the "most appropriate" transfer character set as the
3286   intermediate set, which results in loss of any characters that the source
3287   and target sets have in common, but are lacking from the intermediate set.
3288 */
3289 #ifdef KANJI
3290     /* Special handling for Japanese... */
3291
3292     if (fcsinfo[csin].alphabet == AL_JAPAN ||
3293          fcsinfo[csout].alphabet == AL_JAPAN) {
3294         USHORT eu;
3295         int c, x, y;
3296
3297         xpnbyte(-1,0,0,NULL);           /* Reset output machine */
3298         xlatype = XLA_JAPAN;
3299
3300         while ((c = xgnbyte(FC_JEUC,csin,NULL)) > -1) { /* Get an EUC byte */
3301             if (tr_int) {               /* Interrupted? */
3302                 printf("^C...\n");      /* Print message */
3303                 z = 0;
3304                 break;
3305             }
3306             /* Send EUC byte to output machine */
3307             if ((x = xpnbyte(c,TC_JEUC,csout,fn)) < 0) {
3308                 z = -1;
3309                 break;
3310             }
3311         }
3312         goto xxlate;
3313     }
3314 #endif /* KANJI */
3315
3316     /* Regular bytewise conversion... */
3317
3318     tcs = gettcs(csin,csout);           /* Get intermediate set. */
3319     if (csin == csout) {                /* Input and output sets the same? */
3320         sxx = rxx = NULL;               /* If so, no translation. */
3321     } else {                            /* Otherwise, set up */
3322         if (tcs < 0 || tcs > MAXTCSETS ||
3323             csin < 0 || csin > MAXFCSETS ||
3324             csout < 0 || csout > MAXFCSETS) {
3325             debug(F100,"XLATE csets out of range","",0);
3326             sxx = rxx = NULL;
3327         } else {
3328             sxx = xls[tcs][csin];       /* translation function */
3329             rxx = xlr[tcs][csout];      /* pointers. */
3330             if (rxx == zl1as) rxx = xl1as;
3331         }
3332     }
3333     while ((c = zminchar()) != -1) { /* Loop for all characters in file */
3334         if (tr_int) {                   /* Interrupted? */
3335             printf("^C...\n");          /* Print message */
3336             z = 0;
3337             break;
3338         }
3339         if (sxx) c = (*sxx)((CHAR)c);   /* From fcs1 to tcs */
3340         if (rxx) c = (*rxx)((CHAR)c);   /* from tcs to fcs2 */
3341         if (zchout(filecode,(char)c) < 0) { /* Output xlated character */
3342             z = -1;
3343             break;
3344         }
3345     }
3346     goto xxlate;                        /* Done. */
3347
3348 #else  /* UNICODE */
3349 /*
3350    Use Unicode as the intermediate character set.  It's simple and gives
3351    little or no loss, but the overhead is a bit higher.
3352 */
3353     initxlate(csin,csout);              /* Set up translation functions */
3354
3355     if (xlatype == XLA_NONE) {
3356         while ((c = zminchar()) != -1) { /* Loop for all characters in file */
3357             if (tr_int) {               /* Interrupted? */
3358                 printf("^C...\n");      /* Print message */
3359                 z = 0;
3360                 break;
3361             }
3362             if (zchout(filecode,(char)c) < 0) { /* Output xlated character */
3363                 z = -1;
3364                 break;
3365             }
3366         }
3367         goto xxlate;                    /* Done. */
3368     }
3369
3370
3371 #ifndef NOLOCAL
3372 #ifdef OS2
3373     if (csout == FC_UCS2 &&             /* we're translating to UCS-2 */
3374         filecode == ZCTERM &&           /* for the real screen... */
3375         !k95stdout && !inserver
3376         ) {
3377         union {
3378             USHORT ucs2;
3379             UCHAR  bytes[2];
3380         } output;
3381
3382         while (1) {                     /* In this case we go two-by-two. */
3383             if ((c = xgnbyte(FC_UCS2,csin,NULL)) < 0)
3384               break;
3385             output.bytes[0] = c;
3386             if ((c = xgnbyte(FC_UCS2,csin,NULL)) < 0)
3387               break;
3388             output.bytes[1] = c;
3389
3390             if (tr_int) {               /* Interrupted? */
3391                 printf("^C...\n");      /* Print message */
3392                 z = 0;
3393                 break;
3394             }
3395
3396             VscrnWrtUCS2StrAtt(VCMD,
3397                                &output.ucs2,
3398                                1,
3399                                wherey[VCMD],
3400                                wherex[VCMD],
3401                                &colorcmd
3402                                );
3403         }
3404     } else
3405 #endif /* OS2 */
3406 #endif /* NOLOCAL */
3407
3408       /* General case: Get next byte translated from fcs to UCS-2 */
3409
3410 #ifdef COMMENT
3411       while ((c = xgnbyte(FC_UCS2,csin,NULL)) > -1 &&
3412               (c2 = xgnbyte(FC_UCS2,csin,NULL)) > -1) {
3413           extern int fileorder;
3414
3415           if (tr_int) {                 /* Interrupted? */
3416               printf("^C...\n");        /* Print message */
3417               z = 0;
3418               break;
3419           }
3420           debug(F001,"XLATE c","",c);
3421           debug(F001,"XLATE c2","",c2);
3422
3423           /* And then send UCS-2 byte to translate-and-output machine */
3424
3425           if ((x = xpnbyte(fileorder?c2:c,TC_UCS2,csout,fn)) < 0) {
3426               z = -1;
3427               break;
3428           }
3429           if ((x = xpnbyte(fileorder?c:c2,TC_UCS2,csout,fn)) < 0) {
3430               z = -1;
3431               break;
3432           }
3433       }
3434 #else
3435     while ((c = xgnbyte(FC_UCS2,csin,NULL)) > -1) {
3436           if (tr_int) {                 /* Interrupted? */
3437               printf("^C...\n");        /* Print message */
3438               z = 0;
3439               break;
3440           }
3441           if ((x = xpnbyte(c,TC_UCS2,csout,fn)) < 0) {
3442               z = -1;
3443               break;
3444           }
3445       }
3446 #endif /* COMMENT */
3447
3448 #endif /* UNICODE */
3449
3450   xxlate:                               /* Common exit point */
3451
3452 #ifndef AMIGA
3453 #ifndef MAC
3454     signal(SIGINT,oldsig);              /* Put old signal action back. */
3455 #endif /* MAC */
3456 #endif /* AMIGA */
3457     tr_int = 0;
3458     if (z < 0) {
3459         if (z == -1)
3460           printf("?File output error: %s\n",ck_errstr());
3461         z = 0;
3462     }
3463     zclose(ZIFILE);                     /* Close files */
3464     zclose(filecode);                   /* ... */
3465     return(success = z);                /* and return status. */
3466 }
3467
3468 int
3469 doxlate() {
3470 #ifdef OS2ONLY
3471     extern int tt_font;
3472 #endif /* OS2ONLY */
3473 #ifdef UNIX
3474     extern char ** mtchs;               /* zxpand() file list */
3475 #endif /* UNIX */
3476     int x, y, incs, outcs, multiple = 0, wild = 0, fc = 0, len = 0;
3477     int ofisdir = 0;
3478     char * s, * tocs = "";
3479
3480     if ((x = cmifi("File(s) to translate","",&s,&wild,xxstring)) < 0) {
3481         if (x == -3) {
3482             printf("?Name of an existing file\n");
3483             return(-9);
3484         } else
3485           return(x);
3486     }
3487     ckstrncpy(line,s,LINBUFSIZ);        /* Save copy of string just parsed. */
3488
3489     if ((incs = cmkey(fcstab,nfilc,"from character-set","",xxstring)) < 0)
3490       return(incs);
3491
3492 #ifdef OS2
3493     if (isunicode())
3494       tocs = "ucs2";
3495     else
3496 #endif /* OS2 */
3497       tocs = getdcset();
3498
3499     if ((outcs = cmkey(fcstab,nfilc,"to character-set",tocs,xxstring)) < 0)
3500       return(outcs);
3501     if ((x = cmofi("output file",CTTNAM,&s,xxstring)) < 0) return(x);
3502     if (x > 1)
3503       ofisdir = 1;
3504
3505     len = ckstrncpy(tmpbuf,s,TMPBUFSIZ);
3506     if ((y = cmcfm()) < 0) return(y);   /* Confirm the command */
3507
3508     if (len < 1)
3509       return(-2);
3510
3511     if (ofisdir)
3512       multiple = 2;
3513     else if (wild) {
3514         if (isdir(tmpbuf))
3515           multiple = 2;
3516         else if (!strcmp(tmpbuf,CTTNAM))
3517           multiple = 1;
3518 #ifdef OS2
3519         else if (!stricmp(tmpbuf,"con") || !stricmp(tmpbuf,"con:"))
3520           multiple = 1;
3521 #else
3522 #ifdef UNIXOROSK
3523         else if (!strncmp(tmpbuf,"/dev/",4))
3524           multiple = 1;
3525 #endif /* UNIXOROSK */
3526 #endif /* OS2 */
3527         if (!multiple) {
3528             printf("?A single file please\n");
3529             return(-9);
3530         }
3531     }
3532     if (!multiple) {                    /* Just one file */
3533         return(success = xlate(line,tmpbuf,incs,outcs));
3534     } else {                            /* Translate multiple files */
3535         char dirbuf[CKMAXPATH+4];
3536         int k;
3537 #ifndef ZXREWIND
3538         int flags = ZX_FILONLY;
3539 #endif /* ZXREWIND */
3540
3541         if (multiple == 2) {            /* Target is a directory */
3542             k = ckstrncpy(dirbuf,tmpbuf,CKMAXPATH+1) - 1;
3543             if (k < 0)
3544               return(-2);
3545 #ifdef OS2ORUNIX
3546             if (dirbuf[k] != '/') {
3547                 dirbuf[k+1] = '/';
3548                 dirbuf[k+2] = NUL;
3549             }
3550 #else
3551 #ifdef OSK
3552             if (dirbuf[k] != '/') {
3553                 dirbuf[k+1] = '/';
3554                 dirbuf[k+2] = NUL;
3555             }
3556 #else
3557 #ifdef VMS
3558             if (ckmatch("*.DIR;1",s,0,0))
3559               k = cvtdir(tmpbuf,dirbuf,TMPBUFSIZ);
3560             if (dirbuf[k] != ']' &&
3561                 dirbuf[k] != '>' &&
3562                 dirbuf[k] != ':')
3563               return(-2);
3564 #else
3565 #ifdef datageneral
3566             if (dirbuf[k] != ':') {
3567                 dirbuf[k+1] = ':';
3568                 dirbuf[k+2] = NUL;
3569             }
3570 #else
3571 #ifdef STRATUS
3572             if (dirbuf[k] != '>') {
3573                 dirbuf[k+1] = '>';
3574                 dirbuf[k+2] = NUL;
3575             }
3576 #endif /* STRATUS */
3577 #endif /* datageneral */
3578 #endif /* VMS */
3579 #endif /* OSK */
3580 #endif /* OS2ORUNIX */
3581         }
3582
3583 #ifdef ZXREWIND
3584         fc = zxrewind();                /* Rewind the file list */
3585 #else
3586         if (matchdot)  flags |= ZX_MATCHDOT;
3587         fc = nzxpand(line,flags);
3588 #endif /* ZXREWIND */
3589
3590         if (fc < 1) {
3591             printf("?Wildcard expansion error\n");
3592             return(-9);
3593         }
3594 #ifdef UNIX
3595         sh_sort(mtchs,NULL,fc,0,0,filecase); /* Sort the file list */
3596 #endif /* UNIX */
3597
3598         while (1) {                     /* Loop through the files */
3599             znext(line);
3600             if (!line[0])
3601               break;
3602             if (multiple == 2)
3603               ckmakmsg(tmpbuf,TMPBUFSIZ,dirbuf,line,NULL,NULL);
3604             if (xlate(line,tmpbuf,incs,outcs) < 1)
3605               return(success = 0);
3606         }
3607     }
3608     return(success = 1);
3609 }
3610 #endif /* NOCSETS */
3611
3612 static char hompthbuf[CKMAXPATH+1];
3613
3614 char *
3615 homepath() {
3616     int x;
3617     extern char * myhome;
3618     char * h;
3619
3620     h = myhome ? myhome : zhome();
3621     hompthbuf[0] = NUL;
3622 #ifdef UNIXOROSK
3623     x = ckstrncpy(hompthbuf,h,CKMAXPATH+1);
3624     if (x <= 0) {
3625         hompthbuf[0] = '/';
3626         hompthbuf[1] = NUL;
3627     } else if (x < CKMAXPATH - 2 && hompthbuf[x-1] != '/') {
3628         hompthbuf[x] = '/';
3629         hompthbuf[x+1] = NUL;
3630     }
3631     return(hompthbuf);
3632 #else
3633 #ifdef STRATUS
3634     if (strlen(h) < CKMAXPATH)
3635       sprintf(hompthbuf,"%s>",h);       /* SAFE */
3636     return(hompthbuf);
3637 #else
3638     return(h);
3639 #endif /* STRATUS */
3640 #endif /* UNIXOROSK */
3641 }
3642
3643 /*  D O L O G  --  Do the log command  */
3644
3645 int
3646 dolog(x) int x; {
3647     int y, disp; char *s = NULL, * p = NULL, * q = NULL;
3648     extern int isguest;
3649 #ifdef ZFNQFP
3650     struct zfnfp * fnp;
3651 #endif /* ZFNQFP */
3652
3653     if (isguest) {
3654         printf("?Anonymous log creation not allowed\n");
3655         return(-9);
3656     }
3657     switch (x) {                        /* Which log... */
3658
3659 #ifdef DEBUG
3660       case LOGD:
3661         q = "debug.log";
3662         y = cmofi("Name of debugging log file",q,&s,xxstring);
3663         break;
3664 #endif /* DEBUG */
3665
3666       case LOGP:
3667         q = "packet.log";
3668         y = cmofi("Name of packet log file",q,&s,xxstring);
3669         break;
3670
3671 #ifndef NOLOCAL
3672       case LOGS:
3673         q = "session.log";
3674         y = cmofi("Name of session log file",q,&s,xxstring);
3675         break;
3676 #endif /* NOLOCAL */
3677
3678 #ifdef TLOG
3679       case LOGT:
3680         q = "transact.log";
3681         y = cmofi("Name of transaction log file",q,&s,xxstring);
3682         break;
3683 #endif /* TLOG */
3684
3685 #ifdef CKLOGDIAL
3686       case LOGM: {
3687           int m, n;
3688           char mypath[CKMAXPATH+1];
3689           q = CXLOGFILE;
3690           m = ckstrncpy(mypath,homepath(),CKMAXPATH);
3691           n = strlen(CXLOGFILE);
3692           if (m + n < CKMAXPATH)
3693             ckstrncat(mypath,CXLOGFILE,CKMAXPATH);
3694           else
3695             ckstrncpy(mypath,CXLOGFILE,CKMAXPATH);
3696           y = cmofi("Name of connection log file",mypath,&s,xxstring);
3697           break;
3698       }
3699 #endif /* CKLOGDIAL */
3700
3701       default:
3702         printf("\n?Unknown log designator - %d\n",x);
3703         return(-2);
3704     }
3705     if (y < 0) return(y);
3706     if (y == 2) {                       /* If they gave a directory name */
3707         int k;
3708         char * ds = "/";
3709         k = strlen(s);
3710         if (k > 0 && s[k-1] == '/') ds = "";
3711         ckmakmsg(tmpbuf,TMPBUFSIZ,s,ds,q,NULL);
3712         s = tmpbuf;
3713     }
3714 #ifdef ZFNQFP
3715 #ifdef OS2ORUNIX
3716     if (*s != '|')                      /* Allow for pipes */
3717 #else
3718 #ifdef OSK
3719     if (*s != '|')
3720 #endif /* OSK */
3721 #endif /* OS2ORUNIX */
3722       if ((fnp = zfnqfp(s,TMPBUFSIZ - 1,tmpbuf))) {
3723           if (fnp->fpath)
3724             if ((int) strlen(fnp->fpath) > 0)
3725               s = fnp->fpath;
3726       } /* else if error keep original string */
3727 #endif /* ZFNQFP */
3728
3729     ckstrncpy(line,s,LINBUFSIZ);
3730     s = line;
3731 #ifdef MAC
3732     y = 0;
3733 #else
3734
3735     p = "new";
3736 #ifdef TLOG
3737     if ((x == LOGT && tlogfmt == 2) || x == LOGM)
3738       p = "append";
3739 #endif /* TLOG */
3740
3741     if ((y = cmkey(disptb,2,"Disposition",p,xxstring)) < 0)
3742       return(y);
3743 #endif /* MAC */
3744     disp = y;
3745     if ((y = cmcfm()) < 0) return(y);
3746
3747     switch (x) {
3748
3749 #ifdef DEBUG
3750       case LOGD:
3751         return(deblog = debopn(s,disp));
3752 #endif /* DEBUG */
3753
3754 #ifndef NOXFER
3755       case LOGP:
3756         return(pktlog = pktopn(s,disp));
3757 #endif /* NOXFER */
3758
3759 #ifndef NOLOCAL
3760       case LOGS:
3761         setseslog(sesopn(s,disp));
3762         return(seslog);
3763 #endif /* NOLOCAL */
3764
3765 #ifdef TLOG
3766       case LOGT:
3767         return(tralog = traopn(s,disp));
3768 #endif /* TLOG */
3769
3770 #ifdef CKLOGDIAL
3771       case LOGM:
3772         return(dialog = diaopn(s,disp,0));
3773 #endif /* CKLOGDIAL */
3774
3775       default:
3776         return(-2);
3777     }
3778 }
3779
3780 #ifndef NOXFER
3781 int
3782 pktopn(s,disp) char *s; int disp; {
3783     static struct filinfo xx;
3784
3785     if (!s)
3786       s = "";
3787     if (!*s)
3788       return(0);
3789
3790     debug(F111,"pktopn",s,disp);
3791
3792     zclose(ZPFILE);
3793
3794 #ifdef OS2ORUNIX
3795     if (s[0] == '|') {                  /* Pipe */
3796         char * p = s + 1;
3797         debug(F110,"pktopn p",p,0);
3798         while (*p) {
3799             if (*p != ' ')
3800               break;
3801             else
3802               p++;
3803         }
3804         debug(F110,"pktopn pipe",p,0);
3805         pktlog = zxcmd(ZPFILE,p);
3806         debug(F101,"pktopn seslog","",seslog);
3807     } else {                            /* File */
3808 #endif /* OS2ORUNIX */
3809         if (disp) {
3810             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
3811             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
3812             xx.lblopts = 0;
3813             pktlog = zopeno(ZPFILE,s,NULL,&xx);
3814         } else pktlog = zopeno(ZPFILE,s,NULL,NULL);
3815         if (!pktlog && !quiet)
3816           printf("?%s - %s\n",s,ck_errstr());
3817 #ifdef OS2ORUNIX
3818     }
3819 #endif /* OS2ORUNIX */
3820     if (pktlog > 0)
3821       ckstrncpy(pktfil,s,CKMAXPATH+1);
3822     else
3823       *pktfil = '\0';
3824     return(pktlog);
3825 }
3826 #endif /* NOXFER */
3827
3828 int
3829 traopn(s,disp) char *s; int disp; {
3830 #ifdef TLOG
3831     static struct filinfo xx;
3832
3833     if (!s)
3834       s = "";
3835     if (!*s)
3836       return(0);
3837
3838     debug(F111,"traopn",s,disp);
3839     debug(F101,"traopn tlogfmt","",tlogfmt);
3840
3841     zclose(ZTFILE);
3842
3843 #ifdef OS2ORUNIX
3844     if (tlogfmt == 2) {                 /* FTP format is special... */
3845         VOID doiklog();
3846         if (!disp)                      /* Append? */
3847           if (zchki(s) > -1)            /* No - does file exist? */
3848             (VOID) zdelet(s);           /* Yes - delete it. */
3849         xferlog = 1;
3850         ckstrncpy(trafil,s,CKMAXPATH);
3851         makestr(&xferfile,s);
3852         doiklog();
3853         return(1);
3854     }
3855     if (s[0] == '|') {                  /* Pipe */
3856         char * p = s + 1;
3857         debug(F110,"traopn p",p,0);
3858         while (*p) {
3859             if (*p != ' ')
3860               break;
3861             else
3862               p++;
3863         }
3864         debug(F110,"traopn pipe",p,0);
3865         tralog = zxcmd(ZTFILE,p);
3866         debug(F101,"traopn tralog","",tralog);
3867     }
3868 #endif /* OS2ORUNIX */
3869
3870     if (s[0] != '|') {                  /* File */
3871         if (disp) {
3872             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
3873             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
3874             xx.lblopts = 0;
3875             tralog = zopeno(ZTFILE,s,NULL,&xx);
3876         } else tralog = zopeno(ZTFILE,s,NULL,NULL);
3877     }
3878     if (!tralog && !quiet)
3879       printf("?%s - %s\n",s,ck_errstr());
3880     if (tralog > 0 && tlogfmt > 0) {
3881         ckstrncpy(trafil,s,CKMAXPATH);
3882         tlog(F110,"Transaction Log:",versio,0L);
3883 #ifndef MAC
3884         tlog(F100,ckxsys,"",0L);
3885 #endif /* MAC */
3886         ztime(&s);
3887         tlog(F100,s,"",0L);
3888     } else
3889       *trafil = '\0';
3890     return(tralog);
3891 #else
3892     return(0);
3893 #endif /* TLOG */
3894 }
3895
3896 #ifndef NOLOCAL
3897 int
3898 sesopn(s,disp) char * s; int disp; {
3899     static struct filinfo xx;
3900     extern int tsstate;
3901
3902     tsstate = 0;                        /* Session log timestamp state */
3903
3904     if (!s)
3905       s = "";
3906     if (!*s)
3907       return(0);
3908
3909     debug(F111,"sesopn",s,disp);
3910
3911     zclose(ZSFILE);
3912
3913 #ifdef OS2ORUNIX
3914     if (s[0] == '|') {                  /* Pipe */
3915         char * p = s + 1;
3916         debug(F110,"sesopn p",p,0);
3917         while (*p) {
3918             if (*p != ' ')
3919               break;
3920             else
3921               p++;
3922         }
3923         debug(F110,"sesopn pipe",p,0);
3924         setseslog(zxcmd(ZSFILE,p));
3925         debug(F101,"sesopn seslog","",seslog);
3926     } else {                            /* File */
3927 #endif /* OS2ORUNIX */
3928         if (disp) {
3929             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
3930             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
3931             xx.lblopts = 0;
3932             setseslog(zopeno(ZSFILE,s,NULL,&xx));
3933         } else
3934           setseslog(zopeno(ZSFILE,s,NULL,NULL));
3935         if (!seslog && !quiet)
3936           printf("?%s - %s\n",s,ck_errstr());
3937 #ifdef OS2ORUNIX
3938     }
3939 #endif /* OS2ORUNIX */
3940     if (seslog > 0)
3941       ckstrncpy(sesfil,s,CKMAXPATH+1);
3942     else
3943       *sesfil = '\0';
3944     return(seslog);
3945 }
3946 #endif /* NOLOCAL */
3947 #endif /* NOICP */
3948
3949 int
3950 debopn(s,disp) char *s; int disp; {
3951 #ifdef DEBUG
3952 #ifdef CK_UTSNAME
3953     extern char unm_mch[], unm_nam[], unm_rel[], unm_ver[], unm_mod[];
3954 #endif /* CK_UTSNAME */
3955 #ifdef OS2
3956     extern char ckxsystem[];
3957 #endif /* OS2 */
3958     char *tp;
3959     static struct filinfo xx;
3960
3961     if (!s)
3962       s = "";
3963     if (!*s)
3964       return(0);
3965
3966     zclose(ZDFILE);
3967
3968 #ifdef OS2ORUNIX
3969     if (s[0] == '|') {                  /* Pipe */
3970         char * p = s + 1;
3971         debug(F110,"debopn p",p,0);
3972         while (*p) {
3973             if (*p != ' ')
3974               break;
3975             else
3976               p++;
3977         }
3978         debug(F110,"debopn pipe",p,0);
3979         deblog = zxcmd(ZDFILE,p);
3980         debug(F101,"debopn deblog","",deblog);
3981     } else {                            /* File */
3982 #endif /* OS2ORUNIX */
3983         if (disp) {
3984             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
3985             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
3986             xx.lblopts = 0;
3987             deblog = zopeno(ZDFILE,s,NULL,&xx);
3988         } else
3989           deblog = zopeno(ZDFILE,s,NULL,NULL);
3990         if (!deblog && !quiet)
3991           printf("?%s - %s\n",s,ck_errstr());
3992 #ifdef OS2ORUNIX
3993     }
3994 #endif /* OS2ORUNIX */
3995     if (deblog > 0) {
3996         ckstrncpy(debfil,s,CKMAXPATH+1);
3997         debug(F110,"Debug Log ",versio,0);
3998 #ifndef MAC
3999 #ifdef OS2
4000         debug(F110,ckxsys,ckxsystem,0);
4001 #else /* OS2 */
4002         debug(F100,ckxsys,"",0);
4003 #endif /* OS2 */
4004 #endif /* MAC */
4005 #ifdef CK_UTSNAME
4006         if (unm_mch[0]) {
4007             debug(F110,"uname machine",unm_mch,0);
4008             if (unm_mod[0])
4009               debug(F110,"uname model  ",unm_mod,0);
4010             debug(F110,"uname sysname",unm_nam,0);
4011             debug(F110,"uname release",unm_rel,0);
4012             debug(F110,"uname version",unm_ver,0);
4013         }
4014 #ifdef KTARGET
4015         {
4016             char * s;                   /* Makefile target */
4017             s = KTARGET;
4018             if (!s) s = "";
4019             if (!*s) s = "(unknown)";
4020             debug(F110,"build target",s,0);
4021         }
4022 #endif /* KTARGET */
4023         deblog = 0;
4024         ztime(&tp);
4025         deblog = 1;
4026         debug(F100,tp,"",0);
4027 #endif /* UTSNAME */
4028         debug(F101,"byteorder","",byteorder);
4029 #ifndef NOICP
4030 #ifndef NOLOCAL
4031         if (local) {
4032             debug(F110,"Active connection: ",ttname,0);
4033             if (!network) {
4034                 debug(F101,"Speed","",speed);
4035                 if (hwparity)
4036                   debug(F110,"Parity[hardware]",parnam((char)hwparity),0);
4037                 else
4038                   debug(F110,"Parity",parnam((char)parity),0);
4039                 deblog = 0;
4040                 debug(F110,"Modem",gmdmtyp(),0);
4041                 deblog = 1;
4042             }
4043         } else {
4044             debug(F110,"Active connection: ","none",0);
4045         }
4046 #endif /* NOLOCAL */
4047 #endif /* NOICP */
4048     } else *debfil = '\0';
4049     return(deblog);
4050 #else
4051     return(0);
4052 #endif /* MAC */
4053 }
4054
4055
4056 /*  C K D A T E  --  Returns current date/time in standard format  */
4057
4058 static char nowbuf[18];
4059
4060 char *
4061 ckdate() {
4062     extern struct keytab cmonths[];
4063     int x;
4064     char * t;                   /* Substitute today's date */
4065     char dbuf[32];
4066     ztime(&t);
4067
4068 /*  012345678901234567890123 */
4069 /*  Sat Jul  4 12:16:43 1998 */
4070
4071     ckstrncpy(dbuf,t,32);
4072     t = dbuf;
4073     debug(F110,"ckdate dbuf",dbuf,0);
4074     nowbuf[0] = t[20];
4075     nowbuf[1] = t[21];
4076     nowbuf[2] = t[22];
4077     nowbuf[3] = t[23];
4078
4079     nowbuf[4] = NUL;
4080     debug(F110,"ckdate nowbuf",nowbuf,0);
4081
4082     t[7] = NUL;
4083     if ((x = lookup(cmonths,t+4,12,NULL)) < 0) {
4084         debug(F110,"ckdate bad month",t,0);
4085         return("<BAD_MONTH>");
4086     }
4087     sprintf(nowbuf+4,"%02d",x);         /* SAFE */
4088     nowbuf[6] = (t[8] == SP) ? '0' : t[8];
4089     nowbuf[7] = t[9];
4090     nowbuf[8] = ' ';
4091
4092     nowbuf[9] = NUL;
4093     debug(F110,"ckdate nowbuf",nowbuf,0);
4094
4095     for (x = 11; x < 19; x++) nowbuf[x-2] = t[x];
4096     nowbuf[17] = NUL;
4097     debug(F110,"ckdate nowbuf",nowbuf,0);
4098
4099     return((char *)nowbuf);
4100 }
4101
4102 #ifndef NOICP
4103 #ifdef CKLOGDIAL
4104
4105 /*
4106   fc = 0 for initial open, meaning open, then close immediately.
4107   fc > 0 for subsequent opens, meaning open for use, leave open.
4108 */
4109 int
4110 diaopn(s,disp,fc) char *s; int disp, fc; {
4111     static struct filinfo xx;
4112
4113     if (!s)
4114       s = "";
4115     if (!*s)
4116       return(0);
4117
4118     debug(F110,"diaopn log",s,0);
4119     debug(F101,"diaopn fc",s,fc);
4120     debug(F101,"diaopn disp 1",s,disp);
4121     if (fc) disp = 1;                   /* Force append if open for use */
4122     debug(F101,"diaopn disp 2",s,disp);
4123
4124     zclose(ZDIFIL);                     /* In case a log was already open */
4125
4126 #ifdef OS2ORUNIX
4127     if (s[0] == '|') {                  /* Pipe */
4128         char * p = s + 1;
4129         debug(F110,"diaopn p",p,0);
4130         while (*p) {
4131             if (*p != ' ')
4132               break;
4133             else
4134               p++;
4135         }
4136         debug(F110,"diaopn pipe",p,0);
4137         dialog = zxcmd(ZDIFIL,p);
4138         debug(F101,"diaopn dialog","",dialog);
4139     } else {                            /* File */
4140 #endif /* OS2ORUNIX */
4141         if (disp) {
4142             xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
4143             xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = "";
4144             xx.lblopts = 0;
4145             dialog = zopeno(ZDIFIL,s,NULL,&xx);
4146         } else dialog = zopeno(ZDIFIL,s,NULL,NULL);
4147         if (!dialog)
4148           printf("?%s - %s\n",s,ck_errstr());
4149 #ifdef OS2ORUNIX
4150     }
4151 #endif /* OS2ORUNIX */
4152     if (dialog > 0)
4153       ckstrncpy(diafil,s,CKMAXPATH+1);
4154     else
4155       *diafil = '\0';
4156     if (fc == 0)                        /* Initial open */
4157       zclose(ZDIFIL);                   /* close it */
4158     return(dialog);
4159 }
4160 #endif /* CKLOGDIAL */
4161
4162 #ifndef NOSHOW
4163
4164 /*  SHOW command routines */
4165
4166 char *
4167 shoxm() {
4168     char * s;
4169     switch (binary) {
4170       case XYFT_T: s = "text";         break;
4171 #ifdef VMS
4172       case XYFT_B: s = "binary fixed"; break;
4173       case XYFT_I: s = "image";        break;
4174       case XYFT_L: s = "labeled";      break;
4175       case XYFT_U: s = "binary undef"; break;
4176 #else
4177 #ifdef MAC
4178       case XYFT_B: s = "binary";       break;
4179       case XYFT_M: s = "macbinary";    break;
4180 #else
4181       case XYFT_B: s = "binary";       break;
4182 #ifdef CK_LABELED
4183       case XYFT_L: s = "labeled";      break;
4184 #endif /* CK_LABELED */
4185 #endif /* MAC */
4186 #endif /* VMS */
4187       default: s = "unknown"; break;
4188     }
4189     return(s);
4190 }
4191
4192 #ifndef NOXFER
4193 VOID                                    /* SHOW TRANSFER */
4194 shoxfer() {
4195     extern int docrc, usepipes, xfrxla, whereflg;
4196     extern char * xfrmsg;
4197     printf("\n");
4198     printf(" Transfer Bell: %s\n",showoff(xfrbel));
4199     printf(" Transfer Interruption: %s\n",showoff(xfrint));
4200     printf(" Transfer Cancellation: %s\n",showoff(xfrcan));
4201 #ifndef NOCSETS
4202     printf(" Transfer Translation:  %s\n",showoff(xfrxla));
4203     printf(" Transfer Character-set: ");
4204     if (tcharset == TC_TRANSP)
4205       printf("Transparent\n");
4206     else
4207       printf("%s\n",tcsinfo[tcharset].keyword);
4208 #endif /* NOCSETS */
4209     printf(" Transfer CRC-calculation: %s\n",showoff(docrc));
4210     printf(" Transfer Display: ");
4211     switch (fdispla) {
4212       case XYFD_N: printf("%s\n","none"); break;
4213       case XYFD_R: printf("%s\n","serial"); break;
4214       case XYFD_C: printf("%s\n","fullscreen"); break;
4215       case XYFD_S: printf("%s\n","crt"); break;
4216       case XYFD_B: printf("%s\n","brief"); break;
4217       case XYFD_G: printf("%s\n","gui"); break;
4218     }
4219     printf(" Transfer Message: %s\n", xfrmsg ? xfrmsg : "(none)");
4220     printf(" Transfer Locking-shift: ");
4221     if (lscapu == 2) {
4222         printf("forced");
4223     } else {
4224         printf("%s", (lscapr ? "enabled" : "disabled"));
4225         if (lscapr) printf(",%s%s", (lscapu ? " " : " not "), "used");
4226     }
4227     printf("\n Transfer Mode: %s\n",
4228            xfermode == XMODE_A ?
4229            "automatic" :
4230            "manual"
4231            );
4232     printf(" Transfer Pipes: %s\n", showoff(usepipes));
4233     printf(" Transfer Protocol: %s\n",ptab[protocol].p_name);
4234     printf(" Transfer Report: %s\n",showoff(whereflg));
4235     printf(" Transfer Slow-start: %s\n",showoff(slostart));
4236     printf("\n");
4237 }
4238 #endif /* NOXFER */
4239
4240 VOID
4241 shoflow() {
4242     int i, x;
4243     extern int cxflow[], cxtype, ncxname, nfloname, autoflow;
4244     extern char * cxname[];
4245     printf("\nConnection type:        %s\n",cxname[cxtype]);
4246     if (autoflow) {
4247         printf("Current flow-control:   %s\n", floname[cxflow[cxtype]]);
4248         printf("Switches automatically: yes\n");
4249     } else {
4250         printf("Current flow-control:   %s\n", floname[flow]);
4251         printf("Switches automatically: no\n");
4252     }
4253     printf("\nDefaults by connection type:\n");
4254     debug(F111,"shoflow cxtype",cxname[cxtype],cxtype);
4255     debug(F101,"shoflow flow","",flow);
4256     for (i = 0; i < ncxname; i++) {
4257 #ifdef NOLOCAL
4258         if (i > 0) break;
4259 #endif /* NOLOCAL */
4260 #ifndef NETCONN
4261         if (i > 2) break;
4262 #endif /* NETCONN */
4263 #ifndef DECNET
4264         if (i == CXT_DECNET) continue;
4265 #endif /* DECNET */
4266 #ifndef DECNET
4267 #ifndef SUPERLAT
4268         if (i == CXT_LAT) continue;
4269 #endif /* SUPERLAT */
4270 #endif /* DECNET */
4271 #ifndef CK_NETBIOS
4272         if (i == CXT_NETBIOS) continue;
4273 #endif /* CK_NETBIOS */
4274 #ifndef NPIPE
4275         if (i == CXT_NPIPE) continue;
4276 #endif /* NPIPE */
4277 #ifndef NETCMD
4278         if (i == CXT_PIPE) continue;
4279 #endif /* NETCMD */
4280 #ifndef ANYX25
4281         if (i == CXT_X25) continue;
4282 #endif /* ANYX25 */
4283         x = cxflow[i];
4284         debug(F101,"shoflow x","",x);
4285         if (x < nfloname)
4286           printf("  %-14s: %s\n",cxname[i],floname[x]);
4287         else
4288           printf("  %-14s: (%d)\n",cxname[i],x);
4289     }
4290     printf("\n");
4291 }
4292
4293 #ifndef NOLOCAL
4294 #ifdef ANYX25
4295 int
4296 shox25(n) int n; {
4297     if (nettype == NET_SX25) {
4298         printf("SunLink X.25 V%d.%d",x25ver / 10,x25ver % 10);
4299         if (ttnproto == NP_X3) printf(", PAD X.3, X.28, X.29 protocol,");
4300         printf("\n");
4301         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4302         printf(" Reverse charge call %s",
4303                revcall ? "selected" : "not selected");
4304         printf (", Closed user group ");
4305         if (closgr > -1)
4306           printf ("%d",closgr);
4307         else
4308           printf ("not selected");
4309         printf("\n");
4310         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4311         printf(" Call user data %s.\n", cudata ? udata : "not selected");
4312         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4313     } else if (nettype == NET_VX25) {
4314         if (ttnproto == NP_X3) printf(", PAD X.3, X.28, X.29 protocol,");
4315         printf("\n");
4316         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4317         printf(" Reverse charge call %s",
4318                revcall ? "selected" : "not selected");
4319         printf (", Closed user group [unsupported]");
4320         if (closgr > -1)
4321           printf ("%d",closgr);
4322         else
4323           printf ("not selected");
4324         printf (",");
4325         printf("\n");
4326         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4327         printf(" Call user data %s.\n", cudata ? udata : "not selected");
4328         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4329     } else if (nettype == NET_IX25) {
4330         printf("AIX NPI X.25\n");
4331         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4332         printf("\n Reverse charge call %s",
4333                revcall ? "selected" : "not selected");
4334         printf (", Closed user group [unsupported]");
4335         if (closgr > -1)
4336           printf ("%d",closgr);
4337         else
4338           printf ("not selected");
4339         printf (",");
4340         printf("\n Call user data %s.\n", cudata ? udata : "not selected");
4341     }
4342     return(n);
4343 }
4344
4345 #ifndef IBMX25
4346 int
4347 shopad(n) int n; {
4348     int i;
4349     printf("\nX.3 PAD Parameters:\n");
4350     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4351     for (i = 0; i < npadx3; i++) {
4352         printf(" [%d] %s %d\n",padx3tab[i].kwval,padx3tab[i].kwd,
4353                padparms[padx3tab[i].kwval]);
4354         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4355     }
4356     return(n);
4357 }
4358 #endif /* IBMX25 */
4359 #endif /* ANYX25 */
4360
4361 VOID
4362 shoparc() {
4363     extern int reliable, stopbits, clsondisc;
4364     int i; char *s;
4365     long zz;
4366
4367 #ifdef NEWFTP
4368     if (ftpisconnected()) {
4369         shoftp(1);
4370         printf("\n");
4371     }
4372 #endif /* NEWFTP */
4373
4374     printf("Communications Parameters:\n");
4375
4376     if (network
4377 #ifdef IKSD
4378          || inserver
4379 #endif /* IKSD */
4380          ) {
4381         printf(" Network Host: %s%s",ttname,
4382                (reliable == SET_ON || (reliable == SET_AUTO && !local)
4383 #ifdef TN_COMPORT
4384                && !istncomport()
4385 #endif /* TN_COMPORT */
4386 #ifdef IKSD
4387                || inserver
4388 #endif /* IKSD */
4389                ) ? " (reliable)" : "");
4390 #ifdef TN_COMPORT
4391         if (istncomport()) {
4392             int modemstate;
4393             char * oflow, * iflow = "", * parity, * stopsize, * signature;
4394             int baud = tnc_get_baud();
4395
4396             switch (tnc_get_oflow()) {
4397               case TNC_CTL_OFLOW_NONE:
4398                 oflow = "none";
4399                 break;
4400               case TNC_CTL_OFLOW_XON_XOFF:
4401                 oflow = "xon/xoff";
4402                 break;
4403               case TNC_CTL_OFLOW_RTS_CTS:
4404                 oflow = "rts/cts";
4405                 break;
4406               case TNC_CTL_OFLOW_DCD:
4407                 oflow = "dcd";
4408                 break;
4409               case TNC_CTL_OFLOW_DSR:
4410                 oflow = "dsr";
4411                 break;
4412               default:
4413                 oflow = "(unknown)";
4414             }
4415             switch (tnc_get_iflow()) {
4416               case TNC_CTL_IFLOW_NONE:
4417                 iflow = "none";
4418                 break;
4419               case TNC_CTL_IFLOW_XON_XOFF:
4420                 iflow = "xon/xoff";
4421                 break;
4422               case TNC_CTL_IFLOW_RTS_CTS:
4423                 iflow = "rts/cts";
4424                 break;
4425               case TNC_CTL_IFLOW_DTR:
4426                 break;
4427               default:
4428                 iflow = oflow;
4429             }
4430             switch (tnc_get_parity()) {
4431               case TNC_PAR_NONE:
4432                 parity = "none";
4433                 break;
4434               case TNC_PAR_ODD:
4435                 parity = "odd";
4436                 break;
4437               case TNC_PAR_EVEN:
4438                 parity = "even";
4439                 break;
4440               case TNC_PAR_MARK:
4441                 parity = "mark";
4442                 break;
4443               case TNC_PAR_SPACE:
4444                 parity = "space";
4445                 break;
4446               default:
4447                 parity = "(unknown)";
4448             }
4449             switch (tnc_get_stopsize()) {
4450               case TNC_SB_1:
4451                 stopsize = "1";
4452                 break;
4453               case TNC_SB_1_5:
4454                 stopsize = "1.5";
4455                 break;
4456               case TNC_SB_2:
4457                 stopsize = "2";
4458                 break;
4459               default:
4460                 stopsize = "(unknown)";
4461             }
4462             signature = (char *)tnc_get_signature();
4463             printf("\n  Signature            : %s\n",signature?signature:"");
4464             if (baud <= 0)
4465               printf("  Speed                : (unknown)\n");
4466             else
4467               printf("  Speed                : %d\n", baud);
4468             printf("  Outbound Flow Control: %s\n", oflow);
4469             printf("  Inbound Flow Control : %s\n", iflow);
4470             printf("  Parity               : %s\n", parity);
4471             printf("  Data Size            : %d\n", tnc_get_datasize());
4472             printf("  Stop Bits            : %s\n", stopsize);
4473             printf("  DTR Signal           : %d\n", tnc_get_dtr_state());
4474             printf("  RTS Signal           : %d\n", tnc_get_rts_state());
4475             printf("  Modem State:\n");
4476             modemstate = tnc_get_ms();
4477             if (modemstate & TNC_MS_EDGE_RING)
4478               printf("    Trailing Edge Ring Detector On\n");
4479             else
4480               printf("    Trailing Edge Ring Detector Off\n");
4481             if (modemstate & TNC_MS_CTS_SIG)
4482               printf("    CTS Signal On\n");
4483             else
4484               printf("    CTS Signal Off\n");
4485             if (modemstate & TNC_MS_DSR_SIG)
4486               printf("    DSR Signal On\n");
4487             else
4488               printf("    DSR Signal Off\n");
4489             if (modemstate & TNC_MS_RI_SIG)
4490               printf("    Ring Indicator On\n");
4491             else
4492               printf("    Ring Indicator Off\n");
4493             if (modemstate & TNC_MS_RLSD_SIG)
4494               printf("    RLSD (CD) Signal On\n");
4495             else
4496               printf("    RLSD (CD) Signal Off\n");
4497             printf("\n");
4498         }
4499 #endif /* TN_COMPORT */
4500     } else {
4501
4502         printf(" %s: %s%s, speed: ",
4503 #ifdef OS2
4504                "Port",
4505 #else
4506                "Line",
4507 #endif /* OS2 */
4508                ttname,
4509 #ifdef CK_TTYFD
4510                (local &&
4511 #ifdef VMS
4512                 vmsttyfd() < 0
4513 #else
4514                 ttyfd == -1
4515 #endif /* VMS */
4516                 ) ?
4517                  " (closed)" :
4518                    (reliable == SET_ON ? " (reliable)" : "")
4519 #else
4520                ""
4521 #endif /* CK_TTYFD */
4522                );
4523         if (
4524 #ifdef CK_TTYFD
4525 #ifdef VMS
4526             vmsttyfd() < 0
4527 #else
4528             ttyfd == -1
4529 #endif /* VMS */
4530             ||
4531 #endif /* CK_TTYFD */
4532             (zz = ttgspd()) < 0) {
4533             printf("unknown");
4534         } else {
4535             if (speed == 8880) printf("75/1200");
4536             else if (speed == 134) printf("134.5");
4537             else printf("%ld",zz);
4538         }
4539     }
4540     if (network
4541 #ifdef IKSD
4542          || inserver
4543 #endif /* IKSD */
4544          )
4545       printf("\n Mode: ");
4546     else
4547       printf(", mode: ");
4548     if (local) printf("local"); else printf("remote");
4549     if (network == 0
4550 #ifdef IKSD
4551          && !inserver
4552 #endif/* IKSD */
4553          ) {
4554 #ifdef CK_TAPI
4555         if (tttapi && !tapipass )
4556           printf(", modem: %s","TAPI");
4557         else
4558 #endif /* CK_TAPI */
4559         printf(", modem: %s",gmdmtyp());
4560     } else {
4561 #ifdef NETCONN
4562        if (nettype == NET_TCPA) printf(", TCP/IP");
4563        if (nettype == NET_TCPB) printf(", TCP/IP");
4564        if (nettype == NET_DEC) {
4565            if (ttnproto == NP_LAT) printf(", DECnet LAT");
4566            else if ( ttnproto == NP_CTERM ) printf(", DECnet CTERM");
4567            else printf(", DECnet");
4568        }
4569        if (nettype == NET_SLAT) printf(", Meridian Technologies' SuperLAT");
4570 #ifdef NETFILE
4571        if (nettype == NET_FILE) printf(", local file");
4572 #endif /* NETFILE */
4573 #ifdef NETCMD
4574        if (nettype == NET_CMD) printf(", pipe");
4575 #endif /* NETCMD */
4576 #ifdef NETPTY
4577        if (nettype == NET_PTY) printf(", pseudoterminal");
4578 #endif /* NETPTY */
4579 #ifdef NETDLL
4580        if (nettype == NET_DLL) printf(", dynamic load library");
4581 #endif /* NETDLL */
4582        if (nettype == NET_PIPE) printf(", Named Pipes");
4583 #ifdef SSHBUILTIN
4584        if (nettype == NET_SSH)
4585          printf(", Secure Shell protocol (SECURE)");
4586 #endif /* SSHBUILTIN */
4587 #ifdef ANYX25
4588        if (shox25(0) < 0) return;
4589 #endif /* ANYX25 */
4590        if (IS_TELNET()) {
4591            printf(", telnet protocol");
4592            if (0
4593 #ifdef CK_ENCRYPTION
4594                || ck_tn_encrypting() && ck_tn_decrypting()
4595 #endif /* CK_ENCRYPTION */
4596 #ifdef CK_SSL
4597                || tls_active_flag || ssl_active_flag
4598 #endif /* CK_SSL */
4599                )
4600              printf(" (SECURE)");
4601        }
4602 #ifdef RLOGCODE
4603        else if (ttnproto == NP_RLOGIN || ttnproto == NP_K4LOGIN ||
4604                 ttnproto == NP_K5LOGIN)
4605          printf(", rlogin protocol");
4606        else if (ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN)
4607          printf(", rlogin protocol (SECURE)");
4608 #endif /* RLOGCODE */
4609 #ifdef CK_KERBEROS
4610 #ifdef KRB5
4611        else if (ttnproto == NP_K5U2U)
4612          printf(", Kerberos 5 User to User protocol (SECURE)");
4613 #endif /* KRB5 */
4614 #endif /* CK_KERBEROS */
4615 #endif /* NETCONN */
4616     }
4617     printf("\n");
4618     if (hwparity && local && !network)
4619       s = parnam((char)hwparity);
4620     else
4621       s = parnam((char)parity);
4622     printf(" Parity: %s%s",hwparity ? "hardware " : "", s);
4623 #ifndef NOLOCAL
4624     if (local && !network) {
4625         int sb;
4626         char c;
4627         c = s[0];
4628         if (islower(c)) c = toupper(c);
4629         sb = stopbits;
4630         if (sb < 1) {
4631             sb = (speed > 0 && speed <= 110L) ? 2 : 1;
4632             printf(", stop-bits: (default)");
4633         } else {
4634             printf(", stop-bits: %d",sb);
4635         }
4636         if (hwparity)
4637           printf(" (8%c%d)",c,sb);
4638         else if (parity)
4639           printf(" (7%c%d)",c,sb);
4640         else
4641           printf(" (8N%d)",sb);
4642         printf("\n D");
4643     } else
4644       printf(", d");
4645 #endif /* NOLOCAL */
4646
4647     printf("uplex: %s, ", duplex ? "half" : "full");
4648     debug(F101,"shoparp flow","",flow);
4649     printf("flow: %s", floname[flow]);
4650     printf(", handshake: ");
4651     if (turn) printf("%d\n",turnch); else printf("none\n");
4652 #ifdef COMMENT
4653     if (local && !network) {            /* SET CARRIER-WATCH */
4654 #endif /* COMMENT */
4655         if (carrier == CAR_OFF) s = "off";
4656         else if (carrier == CAR_ON) s = "on";
4657         else if (carrier == CAR_AUT) s = "auto";
4658         else s = "unknown";
4659         printf(" Carrier-watch: %s", s);
4660         if (carrier == CAR_ON) {
4661             if (cdtimo) printf(", timeout: %d sec", cdtimo);
4662             else printf(", timeout: none");
4663         }
4664 #ifdef COMMENT
4665     }
4666 #endif /* COMMENT */
4667     printf(", close-on-disconnect: %s\n",showoff(clsondisc));
4668
4669 #ifdef UNIX                             /* UUCP lockfile, UNIX only */
4670     if (local) {
4671 #ifndef NOUUCP
4672         if (!network && haslock && *flfnam)
4673           printf(" Lockfile: %s",flfnam);
4674 #ifndef USETTYLOCK
4675         if (!network && haslock && lock2[0])
4676           printf("\n Secondary lockfile: %s",lock2);
4677 #endif /* USETTYLOCK */
4678 #else
4679 #ifdef QNX
4680         {
4681             extern int qnxportlock, qnxopencount();
4682             if (local)
4683               printf(" Qnx-port-lock: %s, Open count: %d",
4684                      showoff(qnxportlock),
4685                      qnxopencount()
4686                      );
4687             else
4688               printf(" Qnx-port-lock: %s", showoff(qnxportlock));
4689         }
4690 #endif /* QNX */
4691 #endif /* NOUUCP */
4692         printf("\n");
4693     } else {
4694         char * s;
4695         s = ttglckdir();
4696         if (!s) s = "";
4697         printf(" Lockfile directory: %s\n", *s ? s : "(none)");
4698     }
4699 #endif /* UNIX */
4700 #ifndef MACOSX
4701     if (!local) {
4702         printf(" Typical port device name: %s\n",ttgtpn());
4703     }
4704 #endif  /* MACOSX */
4705     if (local) {
4706         int i;
4707         i = parity ? 7 : 8;
4708         if (i == 8) i = (cmask == 0177) ? 7 : 8;
4709         printf(" Terminal bytesize: %d,",i);
4710         printf(" escape character: %d (^%c)\n",escape,ctl(escape));
4711     }
4712 }
4713
4714 int
4715 shotcp(n) int n; {
4716 #ifdef TCPSOCKET
4717     if (nettype == NET_TCPA || nettype == NET_TCPB) {
4718         printf("SET TCP parameters:\n");
4719         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4720         printf(" Reverse DNS lookup: %s\n", showooa(tcp_rdns));
4721         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4722
4723 #ifdef CK_DNS_SRV
4724         printf(" DNS Service Records lookup: %s\n", showooa(tcp_dns_srv));
4725         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4726 #endif /* CK_DNS_SRV */
4727
4728 #ifndef NOTCPOPTS
4729 #ifdef SOL_SOCKET
4730 #ifdef SO_KEEPALIVE
4731         printf(" Keepalive: %s\n", showoff(tcp_keepalive));
4732         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4733 #endif /* SO_KEEPALIVE */
4734
4735 #ifdef SO_LINGER
4736         printf(" Linger: %s", tcp_linger ? "on, " : "off\n" );
4737         if (tcp_linger) {
4738             if (tcp_linger_tmo)
4739               printf("%d x 10 milliseconds\n",tcp_linger_tmo);
4740             else
4741               printf("no timeout\n");
4742         }
4743         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4744 #endif /* SO_LINGER */
4745
4746 #ifdef SO_DONTROUTE
4747         printf(" DontRoute: %s\n", tcp_dontroute ? "on" : "off" );
4748         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4749 #endif /* SO_DONTROUTE */
4750
4751 #ifdef TCP_NODELAY
4752         printf(" Nodelay: %s\n", showoff(tcp_nodelay));
4753         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4754 #endif /* TCP_NODELAY */
4755
4756 #ifdef SO_SNDBUF
4757         if (tcp_sendbuf <= 0)
4758           printf(" Send buffer: (default size)\n");
4759         else
4760           printf(" Send buffer: %d bytes\n", tcp_sendbuf);
4761         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4762 #endif /* SO_SNDBUF */
4763 #ifdef SO_RCVBUF
4764         if (tcp_recvbuf <= 0)
4765           printf(" Receive buffer: (default size)\n");
4766         else
4767           printf(" Receive buffer: %d bytes\n", tcp_recvbuf);
4768         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
4769 #endif /* SO_RCVBUF */
4770 #endif /* SOL_SOCKET */
4771 #endif /* NOTCPOPTS */
4772         printf(" address: %s\n",tcp_address ? tcp_address : "(none)");
4773         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4774 #ifndef NOHTTP
4775         printf(" http-proxy: %s\n",tcp_http_proxy ? tcp_http_proxy : "(none)");
4776         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4777 #endif /* NOHTTP */
4778 #ifdef NT
4779 #ifdef CK_SOCKS
4780         printf(" socks-server: %s\n",tcp_socks_svr ? tcp_socks_svr : "(none)");
4781         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4782 #ifdef CK_SOCKS_NS
4783         printf(" socks-name-server: %s\n",
4784                tcp_socks_ns ? tcp_socks_ns : "(none)");
4785         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4786 #endif /* CK_SOCKS_NS */
4787 #endif /* CK_SOCKS */
4788 #endif /* NT */
4789     }
4790 #endif /* TCPSOCKET */
4791     return(n);
4792 }
4793
4794 #ifdef TNCODE
4795 int
4796 shotopt(n) int n; {
4797     int opt;
4798
4799     printf("%-21s %12s %12s %12s %12s\n\n",
4800            "Telnet Option","Me (client)","U (client)",
4801            "Me (server)","U (server)");
4802     n += 2;
4803     if (n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4804
4805     for ( opt = TELOPT_FIRST; opt <= TELOPT_LAST; opt++) {
4806         switch (opt) {
4807           case TELOPT_AUTHENTICATION:
4808           case TELOPT_ENCRYPTION:
4809           case TELOPT_TTYPE:
4810           case TELOPT_NAWS:
4811           case TELOPT_BINARY:
4812           case TELOPT_NEWENVIRON:
4813           case TELOPT_SNDLOC:
4814           case TELOPT_XDISPLOC:
4815           case TELOPT_SGA:
4816           case TELOPT_ECHO:
4817           case TELOPT_KERMIT:
4818           case TELOPT_START_TLS:
4819           case TELOPT_FORWARD_X:
4820           case TELOPT_COMPORT:
4821             break;
4822           default:
4823             continue;
4824         }
4825         printf("%03d %-17s ",
4826                opt, TELOPT(opt)
4827                );
4828         printf("%12s %12s ",
4829                TELOPT_MODE(TELOPT_DEF_C_ME_MODE(opt)),
4830                TELOPT_MODE(TELOPT_DEF_C_U_MODE(opt))
4831                );
4832         printf("%12s %12s\n",
4833                TELOPT_MODE(TELOPT_DEF_S_ME_MODE(opt)),
4834                TELOPT_MODE(TELOPT_DEF_S_U_MODE(opt))
4835                );
4836
4837         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4838         if (sstelnet)
4839           printf("%21s %12s %12s %12s %12s\n",
4840                  "",
4841                  "",
4842                  "",
4843                  (TELOPT_ME(opt)?"WILL":"WONT"),
4844                  (TELOPT_U(opt)?"DO":"DONT")
4845                  );
4846         else
4847           printf("%21s %12s %12s %12s %12s\n",
4848                  "",
4849                  (TELOPT_ME(opt)?"WILL":"WONT"),
4850                  (TELOPT_U(opt)?"DO":"DONT"),
4851                  "",
4852                  ""
4853                  );
4854         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4855     }
4856     return(n);
4857 }
4858
4859 int
4860 shotel(n) int n; {
4861     extern int tn_duplex;
4862 #ifdef CK_ENVIRONMENT
4863     extern int tn_env_flg;
4864     extern char tn_env_acct[];
4865     extern char tn_env_job[];
4866     extern char tn_env_prnt[];
4867     extern char tn_env_sys[];
4868     extern char * tn_env_uservar[8][2];
4869     int x;
4870 #endif /* CK_ENVIRONMENT */
4871 #ifdef CK_SNDLOC
4872     extern char * tn_loc;
4873 #endif /* CK_SNDLOC */
4874     printf("SET TELNET parameters:\n echo: %s\n NVT newline-mode: ",
4875            tn_duplex ? "local" : "remote");
4876     switch (tn_nlm) {
4877       case TNL_CRNUL: printf("%s\n","off (cr-nul)"); break;
4878       case TNL_CRLF:  printf("%s\n","on (cr-lf)"); break;
4879       case TNL_CR:    printf("%s\n","raw (cr)"); break;
4880       case TNL_LF:    printf("%s\n","(lf)"); break;
4881     }
4882     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4883 #ifdef CK_AUTHENTICATION
4884     {
4885         int type = ck_tn_authenticated();
4886         printf(" authentication: ");
4887         switch (sstelnet ?
4888                 TELOPT_U_MODE(TELOPT_AUTHENTICATION) :
4889                  TELOPT_ME_MODE(TELOPT_AUTHENTICATION)
4890                 ) {
4891           case TN_NG_AC: printf( "accepted " ); break;
4892           case TN_NG_RF: printf( "refused  " ); break;
4893           case TN_NG_RQ: printf( "requested"); break;
4894           case TN_NG_MU: printf( "required "); break;
4895         }
4896
4897 #ifdef CK_SSL
4898         if ((ssl_active_flag || tls_active_flag) &&
4899              ck_tn_auth_valid() == AUTH_VALID &&
4900              (!TELOPT_U(TELOPT_AUTHENTICATION) ||
4901                type == AUTHTYPE_NULL ||
4902                type == AUTHTYPE_AUTO))
4903             printf("   in use: X.509 certificate\n");
4904         else
4905 #endif /* CK_SSL */
4906           printf("   in use: %s\n",AUTHTYPE_NAME(type));
4907         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4908         if (forward_flag)
4909           printf("  credentials forwarding requested %s\n",
4910                  forwarded_tickets ? "and completed" :
4911                  "but not completed");
4912         else
4913           printf("  credentials forwarding disabled\n");
4914         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4915     }
4916 #endif /* CK_AUTHENTICATION */
4917 #ifdef CK_ENCRYPTION
4918     {
4919         int i,x;
4920         int e_type = ck_tn_encrypting();
4921         int d_type = ck_tn_decrypting();
4922         char * e_str = NULL, * d_str = NULL;
4923         static struct keytab * tnetbl = NULL;
4924         static int ntnetbl = 0;
4925
4926         x = ck_get_crypt_table(&tnetbl,&ntnetbl);
4927
4928         for (i = 0; i < ntnetbl; i++) {
4929             if (e_type == tnetbl[i].kwval)
4930               e_str = tnetbl[i].kwd;
4931             if (d_type == tnetbl[i].kwval)
4932               d_str = tnetbl[i].kwd;
4933         }
4934         printf(" encryption: ");
4935         switch (TELOPT_ME_MODE(TELOPT_ENCRYPTION)) {
4936           /* This should be changed to report both ME and U modes */
4937           case TN_NG_AC: printf( "accepted " ); break;
4938           case TN_NG_RF: printf( "refused  " ); break;
4939           case TN_NG_RQ: printf( "requested"); break;
4940           case TN_NG_MU: printf( "required "); break;
4941         }
4942         printf("       in use: ");
4943         switch ((e_type ? 1 : 0) | (d_type ? 2 : 0)) {
4944           case 0:
4945             printf("plain text in both directions");
4946             break;
4947           case 1:
4948             printf("%s output, plain text input",e_str);
4949             break;
4950           case 2:
4951             printf("plain text output, %s input",d_str);
4952             break;
4953           case 3:
4954             printf("%s output, %s input",e_str,d_str);
4955             break;
4956         }
4957         printf("\n");
4958         if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4959     }
4960 #endif /* CK_ENCRYPTION */
4961 #ifdef IKS_OPTION
4962     printf(" kermit: ");
4963     switch (TELOPT_U_MODE(TELOPT_KERMIT)) {
4964       case TN_NG_AC: printf( "u, accepted;  " ); break;
4965       case TN_NG_RF: printf( "u, refused;   " ); break;
4966       case TN_NG_RQ: printf( "u, requested; "); break;
4967       case TN_NG_MU: printf( "u, required;  "); break;
4968     }
4969     switch (TELOPT_ME_MODE(TELOPT_KERMIT)) {
4970       case TN_NG_AC: printf( "me, accepted;  " ); break;
4971       case TN_NG_RF: printf( "me, refused;   " ); break;
4972       case TN_NG_RQ: printf( "me, requested; "); break;
4973       case TN_NG_MU: printf( "me, required;  "); break;
4974     }
4975     if (TELOPT_U(TELOPT_KERMIT))
4976       printf(" u, %s",
4977              TELOPT_SB(TELOPT_KERMIT).kermit.u_start ?
4978              "started" :
4979              "stopped"
4980              );
4981     else
4982       printf(" u, n/a");
4983     if (TELOPT_ME(TELOPT_KERMIT))
4984       printf(" me, %s;",
4985              TELOPT_SB(TELOPT_KERMIT).kermit.me_start ?
4986              "started" :
4987              "stopped"
4988              );
4989     else
4990       printf(" me, n/a;");
4991     printf("\n");
4992     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
4993 #endif /* IKS_OPTION */
4994     printf(" BINARY newline-mode: ");
4995     switch (tn_b_nlm) {
4996       case TNL_CRNUL: printf("%s\n","off (cr-nul)"); break;
4997       case TNL_CRLF:  printf("%s\n","on (cr-lf)"); break;
4998       case TNL_CR:    printf("%s\n","raw (cr)"); break;
4999       case TNL_LF:    printf("%s\n","(lf)"); break;
5000     }
5001     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5002     printf(" binary-mode: ");
5003     switch (TELOPT_U_MODE(TELOPT_BINARY)) {
5004       case TN_NG_AC: printf( "u, accepted;  " ); break;
5005       case TN_NG_RF: printf( "u, refused;   " ); break;
5006       case TN_NG_RQ: printf( "u, requested; "); break;
5007       case TN_NG_MU: printf( "u, required;  "); break;
5008     }
5009     switch (TELOPT_ME_MODE(TELOPT_BINARY)) {
5010       case TN_NG_AC: printf( "me, accepted; " ); break ;
5011       case TN_NG_RF: printf( "me, refused; " ); break;
5012       case TN_NG_RQ: printf( "me, requested; "); break;
5013       case TN_NG_MU: printf( "me, required;  "); break;
5014     }
5015     printf("u, %s; me, %s\n",
5016            TELOPT_U(TELOPT_BINARY) ? "BINARY" : "NVT",
5017            TELOPT_ME(TELOPT_BINARY) ? "BINARY" : "NVT"
5018            );
5019     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5020     printf(" binary-transfer-mode: %s\n",showoff(tn_b_xfer));
5021     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5022     printf(" bug binary-me-means-u-too: %s\n",showoff(tn_b_meu));
5023     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5024     printf(" bug binary-u-means-me-too: %s\n",showoff(tn_b_ume));
5025     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5026     printf(" bug sb-implies-will-do: %s\n",showoff(tn_sb_bug));
5027     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5028     printf(" bug auth-krb5-des: %s\n",showoff(tn_auth_krb5_des_bug));
5029     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5030     printf(" terminal-type: ");
5031     if (tn_term) {
5032         printf("%s\n",tn_term);
5033     } else {
5034         char *p;
5035 #ifdef OS2
5036         p = (tt_type >= 0 && tt_type <= max_tt) ?
5037           tt_info[tt_type].x_name :
5038             "UNKNOWN";
5039 #else
5040         p = getenv("TERM");
5041 #endif /* OS2 */
5042         if (p)
5043           printf("none (%s will be used)\n",p);
5044         else printf("none\n");
5045     }
5046     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5047 #ifdef CK_ENVIRONMENT
5048     printf(" environment: %s\n", showoff(tn_env_flg));
5049     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5050     printf("   ACCOUNT: %s\n",tn_env_acct);
5051     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5052     printf("   DISPLAY: %s\n",(char *)tn_get_display() ?
5053             (char *)tn_get_display() : "");
5054     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5055     printf("   JOB    : %s\n",tn_env_job);
5056     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5057     printf("   PRINTER: %s\n",tn_env_prnt);
5058     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5059 #ifndef NOSPL
5060     printf("   USER   : %s\n",uidbuf);
5061     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5062 #endif /* NOSPL */
5063     printf("   SYSTEM : %s\n",tn_env_sys);
5064     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5065     for (x = 0; x < 8; x++) {
5066         if (tn_env_uservar[x][0] && tn_env_uservar[x][1]) {
5067             printf("   %-7s: %s\n",tn_env_uservar[x][0],
5068                    tn_env_uservar[x][1]);
5069             if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5070         }
5071     }
5072 #endif /* CK_ENVIRONMENT */
5073 #ifdef CK_SNDLOC
5074     printf("  LOCATION: %s\n", tn_loc ? tn_loc : "");
5075     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5076 #endif /* CK_SNDLOC */
5077 #ifdef CK_FORWARD_X
5078     printf(" .Xauthority-file: %s\n", (char *)XauFileName() ?
5079             (char *)XauFileName() : "(none)");
5080     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5081 #endif /* CK_FORWARD_X */
5082     return(n);
5083 }
5084 #endif /* TNCODE */
5085
5086 #ifdef CK_NETBIOS
5087 static int
5088 shonb(n) int n; {
5089     printf("NETBIOS parameters:\n");
5090     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5091     printf(" API       : %s\n",
5092            NetbeuiAPI ?
5093            "NETAPI.DLL - IBM Extended Services or Novell Netware Requester"
5094            : "ACSNETB.DLL - IBM Network Transport Services/2" ) ;
5095     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5096     printf(" Local Name: [%s]\n", NetBiosName);
5097     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5098     printf(" Adapter   : %d\n", NetBiosAdapter);
5099     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5100     if (NetBiosLSN > 0xFF) {
5101         printf(" Session   : %d\n", NetBiosLSN);
5102     } else {
5103         printf(" Session   : none active\n");
5104     }
5105     if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0;
5106     return(n);
5107 }
5108 #endif /* CK_NETBIOS */
5109
5110 #ifndef NONET
5111 int
5112 shonet() {
5113
5114 #ifndef NETCONN
5115     printf("\nNo networks are supported in this version of C-Kermit\n");
5116
5117 #else
5118 #ifdef NOLOCAL
5119     printf("\nNo networks are supported in this version of C-Kermit\n");
5120
5121 #else /* rest of this routine */
5122
5123     int i, n = 4;
5124
5125 #ifndef NODIAL
5126     if (nnetdir <= 1) {
5127         printf("\nNetwork directory: %s\n",netdir[0] ? netdir[0] : "(none)");
5128         n++;
5129     } else {
5130         int i;
5131         printf("\nNetwork directories:\n");
5132         for (i = 0; i < nnetdir; i++) {
5133             printf("%2d. %s\n",i,netdir[i]);
5134             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5135         }
5136     }
5137 #endif /* NODIAL */
5138
5139 #ifdef SSHCMD
5140     {
5141         extern char * sshcmd;
5142         printf("SSH COMMAND: %s\n",sshcmd ? sshcmd : "ssh -e none");
5143         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5144     }
5145 #endif /* SSHCMD */
5146
5147 #ifdef OS2
5148     printf("\nNetwork availability:\n");
5149 #else
5150     printf("\nSupported networks:\n");
5151 #endif /* OS2 */
5152     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5153
5154 #ifdef VMS
5155
5156 #ifdef TCPWARE
5157     printf(" Process Software Corporation TCPware for OpenVMS");
5158 #else
5159 #ifdef MULTINET
5160     printf(" TGV MultiNet TCP/IP");
5161 #else
5162 #ifdef WINTCP
5163     printf(" WOLLONGONG WIN/TCP");
5164 #else
5165 #ifdef DEC_TCPIP
5166     {
5167         static $DESCRIPTOR(tcp_desc,"_TCP0:");
5168         int status;
5169         long devclass;
5170         static int itmcod = DVI$_DEVCLASS;
5171
5172 #ifdef COMMENT
5173         status = LIB$GETDVI(&itmcod, 0, &tcp_desc, &devclass);
5174 #else
5175         /* Martin Zinser 9/96 */
5176         status = lib$getdvi(&itmcod, 0, &tcp_desc, &devclass);
5177 #endif /* COMMENT */
5178         if ((status & 1) && (devclass == DC$_SCOM))
5179           printf(" Process Software Corporation TCPware for OpenVMS");
5180         else
5181 #ifdef UCX50
5182           printf(" DEC TCP/IP Services for (Open)VMS 5.0");
5183 #else
5184           printf(" DEC TCP/IP Services for (Open)VMS");
5185 #endif /* UCX50 */
5186     }
5187 #else
5188 #ifdef CMU_TCPIP
5189     printf(" CMU-OpenVMS/IP");
5190 #else
5191     printf(" None");
5192 #endif /* CMU_TCPIP */
5193 #endif /* DEC_TCPIP */
5194 #endif /* WINTCP */
5195 #endif /* MULTINET */
5196 #endif /* TCPWARE */
5197     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5198 #ifdef TNCODE
5199     printf(", TELNET protocol\n\n");
5200     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5201     n = shotel(n);
5202     if (n < 0) return(0);
5203     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5204 #endif /* TNCODE */
5205     printf("\n");
5206     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5207     printf("\n");
5208     n = shotcp(++n);
5209     if (n < 0) return(0);
5210 #else /* Not VMS */
5211
5212 #ifdef SUNX25
5213     printf(" SunLink X.25\n");
5214     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5215 #endif /* SUNX25 */
5216
5217 #ifdef STRATUSX25
5218     printf(" Stratus VOS X.25\n");
5219     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5220 #endif /* STRATUSX25 */
5221
5222 #ifdef IBMX25
5223     printf(" IBM AIX X.25\n");
5224     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5225 #endif /* IBMX25 */
5226
5227 #ifdef HPX25
5228     printf(" HP-UX X.25\n");
5229     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5230 #endif /* HPX25 */
5231
5232 #ifdef SSHBUILTIN
5233     if (ck_ssleay_is_installed())
5234         printf(" SSH V1 and V2 protocols\n");
5235     else
5236         printf(" SSH V1 and V2 protocols - not available\n");
5237 #endif /* SSHBUILTIN */
5238
5239 #ifdef DECNET
5240 #ifdef OS2
5241 #ifdef NT
5242     if (dnet_avail)
5243       printf(" DECnet, LAT and CTERM protocols\n");
5244     else
5245       printf(" DECnet, LAT and CTERM protocols - not available\n");
5246     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5247 #else /* NT */
5248     if (dnet_avail)
5249       printf(" DECnet, LAT protocol\n");
5250     else
5251       printf(" DECnet, LAT protocol - not available\n");
5252     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5253 #endif /* NT */
5254 #else
5255     printf(" DECnet\n");
5256     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5257 #endif /* OS2 */
5258 #endif /* DECNET */
5259
5260 #ifdef NPIPE
5261     printf(" Named Pipes\n");
5262     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5263 #endif /* NPIPE */
5264
5265 #ifdef CK_NETBIOS
5266     if (netbiosAvail)
5267       printf(" NETBIOS\n");
5268     else
5269       printf(" NETBIOS - not available\n");
5270     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5271 #endif /* CK_NETBIOS */
5272
5273 #ifdef SUPERLAT
5274     if (slat_avail)
5275       printf(" SuperLAT\n");
5276     else
5277       printf(" SuperLAT - not available\n") ;
5278     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5279 #endif /* SUPERLAT */
5280
5281 #ifdef TCPSOCKET
5282     if (
5283 #ifdef OS2
5284         tcp_avail
5285 #else
5286         1
5287 #endif /* OS2 */
5288         ) {
5289         char ipaddr[16];
5290
5291         if (getlocalipaddrs(ipaddr,16,0) < 0) {
5292 #ifdef OS2ONLY
5293             printf(" TCP/IP via %s\n", tcpname);
5294 #else
5295             printf(" TCP/IP\n");
5296 #endif /* OS2ONLY */
5297             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5298         } else {
5299             int i = 1;
5300 #ifdef OS2ONLY
5301           printf(" TCP/IP [%16s] via %s\n", ipaddr, tcpname);
5302 #else
5303           printf(" TCP/IP [%16s]\n",ipaddr);
5304 #endif /* OS2ONLY */
5305             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5306
5307             while (getlocalipaddrs(ipaddr,16,i++) >= 0) {
5308                 printf("        [%16s]\n",ipaddr);
5309                 if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5310             }
5311         }
5312         if (nettype == NET_TCPB) {
5313             printf("\n");
5314             n = shotcp(++n);
5315             if (n < 0) return(0);
5316 #ifdef TNCODE
5317             printf("\n");
5318             n = shotel(++n);
5319             if (n < 0) return(0);
5320 #endif /* TNCODE */
5321             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5322         }
5323 #ifdef OS2
5324     } else {
5325         printf(" TCP/IP - not available%s\n",tcpname[0] ? tcpname : "" );
5326         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5327 #endif /* OS2 */
5328     }
5329 #endif /* TCPSOCKET */
5330
5331 #ifdef CK_NETBIOS
5332     if (netbiosAvail && nettype == NET_BIOS) {
5333        printf("\n") ;
5334        if ((n = shonb(++n)) < 0) return(0);
5335        if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5336     }
5337 #endif /* CK_NETBIOS */
5338
5339 #endif /* VMS */
5340
5341     printf("\nActive network connection:\n");
5342     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5343
5344     if (network) {
5345         printf(" Host: %s",ttname);
5346         if ((nettype == NET_TCPA || nettype == NET_TCPB) && *ipaddr)
5347           printf(" [%s]",ipaddr);
5348     } else
5349       printf(" Host: none");
5350     printf(", via: ");
5351     if (nettype == NET_TCPA || nettype == NET_TCPB)
5352       printf("tcp/ip\n");
5353     else if (nettype == NET_SX25)
5354       printf("SunLink X.25\n");
5355     else if (nettype == NET_VX25)
5356       printf("Stratus VOS X.25\n");
5357     else if (nettype == NET_IX25)
5358       printf("IBM AIX X.25\n");
5359     else if (nettype == NET_HX25)
5360       printf("HP-UX X.25\n");
5361     else if (nettype == NET_DEC) {
5362         if ( ttnproto == NP_LAT )
5363           printf("DECnet LAT\n");
5364         else if ( ttnproto == NP_CTERM )
5365           printf("DECnet CTERM\n");
5366         else
5367           printf("DECnet\n");
5368     } else if (nettype == NET_PIPE)
5369       printf("Named Pipes\n");
5370     else if (nettype == NET_BIOS)
5371       printf("NetBIOS\n");
5372     else if (nettype == NET_SLAT)
5373       printf("SuperLAT\n");
5374
5375 #ifdef NETFILE
5376     else if ( nettype == NET_FILE )
5377       printf("local file\n");
5378 #endif /* NETFILE */
5379 #ifdef NETCMD
5380     else if ( nettype == NET_CMD )
5381       printf("pipe\n");
5382 #endif /* NETCMD */
5383 #ifdef NETPTY
5384     else if ( nettype == NET_PTY )
5385         printf("pseudoterminal\n");
5386 #endif /* NETPTY */
5387 #ifdef NETDLL
5388     else if ( nettype == NET_DLL )
5389       printf("dynamic link library\n");
5390 #endif /* NETDLL */
5391     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5392
5393 #ifdef ANYX25
5394     if ((nettype == NET_SX25) ||
5395         (nettype == NET_VX25) ||
5396         (nettype == NET_IX25))
5397       if ((n = shox25(n)) < 0) return(0);
5398 #endif /* ANYX25 */
5399
5400 #ifdef SSHBUILTIN
5401     if (nettype == NET_SSH) {
5402         printf("Secure Shell protocol\n");
5403         if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5404     }
5405 #endif /* SSHBUILTIN */
5406
5407     if (nettype == NET_TCPA || nettype == NET_TCPB) {
5408 #ifdef RLOGCODE
5409         if (ttnproto == NP_RLOGIN) {
5410             printf(" LOGIN (rlogin) protocol\n");
5411             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5412         }
5413 #ifdef CK_KERBEROS
5414         else if (ttnproto == NP_K4LOGIN) {
5415             printf(" Kerberos 4 LOGIN (klogin) protocol\n");
5416             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5417         }
5418         else if (ttnproto == NP_EK4LOGIN) {
5419             printf(" Encrypted Kerberos 4 LOGIN (eklogin) protocol\n");
5420             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5421         }
5422         else if (ttnproto == NP_K5LOGIN) {
5423             printf(" Kerberos 5 LOGIN (klogin) protocol\n");
5424             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5425         }
5426         else if (ttnproto == NP_EK5LOGIN) {
5427             printf(" Encrypted Kerberos 5 LOGIN (eklogin) protocol\n");
5428             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5429         }
5430 #endif /* CK_KERBEROS */
5431 #endif /* RLOGCODE */
5432 #ifdef CK_KERBEROS
5433         if (ttnproto == NP_K5U2U) {
5434             printf(" Kerberos 5 User to User protocol\n");
5435             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5436         }
5437 #endif /* CK_KERBEROS */
5438
5439 #ifdef TNCODE
5440         if (IS_TELNET()) {
5441             printf(" TELNET protocol\n");
5442             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5443             printf(" Echoing is currently %s\n",duplex ? "local" : "remote");
5444             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5445         }
5446 #endif /* TNCODE */
5447         if (ttnproto == NP_TCPRAW) {
5448             printf(" Raw TCP socket\n");
5449             if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5450         }
5451     }
5452     printf("\n");
5453 #endif /* NOLOCAL */
5454 #endif /* NETCONN */
5455     return(0);
5456 }
5457 #endif /* NONET */
5458
5459 #ifndef NODIAL
5460 VOID
5461 shodial() {
5462     if (mdmtyp >= 0 || local != 0) doshodial();
5463 }
5464
5465 VOID
5466 shods(s) char *s; {                     /* Show a dial-related string */
5467     char c;
5468     if (s == NULL || !(*s)) {           /* Empty? */
5469         printf("(none)\n");
5470     } else {                            /* Not empty. */
5471         while ((c = *s++))              /* Can contain controls */
5472           if (c == '\\')                /* a backslash */
5473             printf("\\\\");
5474           else if (c > 31 && c < 127) {
5475               putchar(c);
5476           } else
5477             printf("\\{%d}",c);
5478         printf("\n");
5479     }
5480 }
5481
5482 int
5483 doshodial() {
5484
5485     int i, n = 2;
5486
5487     printf(" Dial status:  %d", dialsta);
5488
5489 #ifdef BIGBUFOK
5490     if (dialsta > 90)
5491       printf(" = Unknown error");
5492     else if (dialsta < 0)
5493       printf(" = (none)");
5494     else if (dialsta < 35 && dialmsg[dialsta])
5495       printf(" = %s", dialmsg[dialsta]);
5496 #endif /* BIGBUFOK */
5497     n++;
5498     if (ndialdir <= 1) {
5499         printf("\n Dial directory: %s\n",dialdir[0] ? dialdir[0] : "(none)");
5500     } else {
5501         int i;
5502         printf("\n Dial directories:\n");
5503         for (i = 0; i < ndialdir; i++)
5504           printf("%2d. %s\n",i+1,dialdir[i]);
5505         n += ndialdir;
5506     }
5507     printf(" Dial method:  ");
5508     if      (dialmauto)         printf("auto   ");
5509     else if (dialmth == XYDM_D) printf("default");
5510     else if (dialmth == XYDM_P) printf("pulse  ");
5511     else if (dialmth == XYDM_T) printf("tone   ");
5512     printf("         Dial sort: %s\n",dialsrt ? "on" : "off");
5513     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5514     printf(" Dial hangup:  %s             Dial display: %s\n",
5515            dialhng ? "on " : "off", dialdpy ? "on" : "off");
5516     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5517     if (dialrtr > 0) {
5518         printf(" Dial retries: %-12d    Dial interval: %d\n",
5519                dialrtr, dialint);
5520     } else {
5521         printf(" Dial retries: (auto)          Dial interval: %d\n", dialint);
5522     }
5523     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5524     printf(" Dial timeout: ");
5525 #ifdef CK_TAPI
5526     if (tttapi && !tapipass)
5527         printf("(tapi)");
5528     else
5529 #endif /* CK_TAPI */
5530     if (dialtmo > 0)
5531       printf("%4d sec", dialtmo);
5532     else
5533       printf("0 (auto)");
5534     printf("        Redial number: %s\n",dialnum ? dialnum : "(none)");
5535     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5536     printf(" Dial confirmation: %s        Dial convert-directory: %s\n",
5537            dialcnf ? "on " : "off",
5538            dialcvt ? ((dialcvt == 1) ? "on" : "ask") : "off");
5539     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5540     printf(" Dial ignore-dialtone: %s", dialidt ? "on " : "off");
5541     printf("     Dial pacing: %d\n",dialpace);
5542     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5543     printf(
5544 " Dial prefix:                  %s\n", dialnpr ? dialnpr : "(none)");
5545     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5546     printf(
5547 " Dial suffix:                  %s\n", dialsfx ? dialsfx : "(none)");
5548     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5549     printf(
5550 " Dial country-code:            %-12s", diallcc ? diallcc : "(none)");
5551     printf("Dial connect:  %s", dialcon ? ((dialcon == 1) ? "on" : "auto")
5552            : "off");
5553     if (dialcon != CAR_OFF)
5554       printf(" %s", dialcq ? "quiet" : "verbose");
5555     printf(
5556 "\n Dial area-code:               %-12s", diallac ? diallac : "(none)");
5557     n++;
5558     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5559     printf("Dial restrict: ");
5560     if (dialrstr == 5) printf("international\n");
5561     else if (dialrstr == 4) printf("long-distance\n");
5562     else if (dialrstr == 2) printf("local\n");
5563     else if (dialrstr == 6) printf("none\n");
5564     else printf("?\n");
5565     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5566     printf(" Dial lc-area-codes:           ");
5567     if (nlocalac == 0)
5568       printf("(none)");
5569     else
5570       for (i = 0; i < nlocalac; i++)
5571         printf("%s ", diallcac[i]);
5572     printf(
5573 "\n Dial lc-prefix:               %s\n", diallcp ? diallcp : "(none)");
5574     n++;
5575     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5576     printf(
5577 " Dial lc-suffix:               %s\n", diallcs ? diallcs : "(none)");
5578     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5579     printf(
5580 " Dial ld-prefix:               %s\n", dialldp ? dialldp : "(none)");
5581     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5582     printf(
5583 " Dial ld-suffix:               %s\n", diallds ? diallds : "(none)");
5584     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5585     printf(
5586 " Dial force-long-distance      %s\n", showoff(dialfld));
5587     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5588     printf(
5589 " Dial intl-prefix:             %s\n", dialixp ? dialixp : "(none)");
5590     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5591     printf(
5592 " Dial intl-suffix:             %s\n", dialixs ? dialixs : "(none)");
5593     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5594     printf(
5595 " Dial toll-free-area-code:     ");
5596     if (ntollfree == 0)
5597       printf("(none)");
5598     else
5599       for (i = 0; i < ntollfree; i++)
5600         printf("%s ", dialtfc[i]);
5601     printf("\n");
5602     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5603
5604     printf(
5605 " Dial pulse-countries:         ");
5606     if (ndialpucc == 0)
5607       printf("(none)");
5608     else
5609       for (i = 0; i < ndialpucc; i++)
5610         printf("%s ", dialpucc[i]);
5611     printf("\n");
5612     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5613
5614     printf(
5615 " Dial tone-countries:          ");
5616     if (ndialtocc == 0)
5617       printf("(none)");
5618     else
5619       for (i = 0; i < ndialtocc; i++)
5620         printf("%s ", dialtocc[i]);
5621     printf("\n");
5622     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5623
5624     printf(
5625         " Dial toll-free-prefix:        %s\n",
5626         dialtfp ? dialtfp :
5627         (dialldp ? dialldp : "(none)")
5628         );
5629     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5630     printf(" Dial pbx-exchange:            ");
5631     if (ndialpxx == 0)
5632       printf("(none)");
5633     else
5634       for (i = 0; i < ndialpxx; i++)
5635         printf("%s ", dialpxx[i]);
5636     printf("\n");
5637
5638     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5639     printf(
5640 " Dial pbx-inside-prefix:       %s\n", dialpxi ? dialpxi : "(none)");
5641     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5642     printf(
5643 " Dial pbx-outside-prefix:      %s\n", dialpxo ? dialpxo : "(none)");
5644     if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0;
5645     printf(
5646 " Dial macro:                   %s\n", dialmac ? dialmac : "(none)");
5647     return(0);
5648 }
5649 #endif /* NODIAL */
5650 #endif /* NOLOCAL */
5651
5652 /*  Show File Parameters */
5653
5654 static char *
5655 pathval(x) int x; {
5656     switch (x) {
5657       case PATH_OFF:  return("off");
5658       case PATH_ABS:  return("absolute");
5659       case PATH_REL:  return("relative");
5660       case PATH_AUTO: return("auto");
5661       default: return("unknown");
5662     }
5663 }
5664
5665 VOID
5666 shofil() {
5667     char *s; int i = 0, n = 1;
5668     extern char * ifdnam[];
5669     extern int wildena;
5670 #ifdef UNIX
5671     extern int wildxpand;
5672 #endif /* UNIX */
5673     extern char * snd_move, * snd_rename, * rcv_move, * rcv_rename;
5674 #ifdef PATTERNS
5675     extern int patterns;
5676 #endif /* PATTERNS */
5677     extern char * rfspec, * sfspec;
5678 #ifdef UNIX
5679     extern int zobufsize, zofbuffer, zofblock;
5680 #endif /* UNIX */
5681 #ifdef CK_CTRLZ
5682     extern int eofmethod;
5683 #endif /* CK_CTRLZ */
5684
5685     printf("\n");
5686
5687 #ifdef VMS
5688     printf(" File record-Length:      %5d\n",frecl);
5689     n++;
5690 #endif /* VMS */
5691
5692 #ifndef NOXFER
5693     printf(" Transfer mode:           %s\n",
5694            xfermode == XMODE_A ?
5695            "automatic" :
5696            "manual"
5697            );
5698     n++;
5699 #ifdef PATTERNS
5700     printf(" File patterns:           %s", showooa(patterns));
5701     if (xfermode == XMODE_M && patterns)
5702       printf(" (but disabled by TRANSFER-MODE MANUAL)");
5703     else if (patterns)
5704       printf(" (SHOW PATTERNS for list)");
5705     printf("\n");
5706     n++;
5707 #endif /* PATTERNS */
5708     if (filepeek)
5709       printf(" File scan:               on %d\n", nscanfile);
5710     else
5711       printf(" File scan:               off\n");
5712     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5713     if (xfermode == XMODE_A)
5714       printf(" Default file type:       %s\n",shoxm());
5715     else
5716       printf(" File type:               %s\n",shoxm());
5717     n++;
5718     if (fncnv == XYFN_L)
5719       s = "literal";
5720     else if (fncnv == XYFN_C)
5721       s = "converted";
5722     else
5723       s = "(unknown)";
5724     printf(" File names:              %s\n",s);
5725     n++;
5726     printf(" Send pathnames:          %s\n", pathval(fnspath));
5727     n++;
5728     printf(" Receive pathnames:       %s\n", pathval(fnrpath));
5729     n++;
5730 #ifdef UNIXOROSK
5731     printf(" Match dot files:         %s\n", matchdot ? "yes" : "no");
5732     n++;
5733 #ifdef UNIX
5734     printf(" Wildcard-expansion:      %s (%s)\n", showoff(wildena),
5735            wildxpand ? "shell" : "kermit");
5736     n++;
5737 #endif /* UNIX */
5738 #endif /* UNIXOROSK */
5739     printf(" File collision:          ");
5740     for (i = 0; i < ncolx; i++)
5741       if (colxtab[i].kwval == fncact) break;
5742     printf("%s\n", (i == ncolx) ? "unknown" : colxtab[i].kwd);
5743     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5744     printf(" File destination:        %s\n",
5745            (dest == DEST_D) ? "disk" :
5746            ((dest == DEST_S) ? "screen" :
5747             ((dest == DEST_N) ? "nowhere" :
5748             "printer"))
5749            );
5750     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5751     s = (keep >= 0 && keep <= 2) ? ifdnam[keep] : "keep";
5752     printf(" File incomplete:         %s\n",s);
5753     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5754     printf(" File bytesize:           %d\n",(fmask == 0177) ? 7 : 8);
5755     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5756 #ifndef NOCSETS
5757     printf(" File character-set:      %s\n",fcsinfo[fcharset].keyword);
5758     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5759     printf(" File default 7-bit:      %s\n",fcsinfo[dcset7].keyword);
5760     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5761     printf(" File default 8-bit:      %s\n",fcsinfo[dcset8].keyword);
5762     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5763 #ifdef UNICODE
5764     printf(" File UCS bom:            %s\n",showoff(ucsbom));
5765     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5766     printf(" File UCS byte-order:     %s-endian\n",
5767            ucsorder ? "little" : "big");
5768     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5769     printf(" Computer byteorder:      %s-endian\n",
5770            byteorder ? "little" : "big");
5771     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5772 #endif /* UNICODE */
5773 #endif /* NOCSETS */
5774
5775     printf(" File end-of-line:        ");
5776     i = feol;
5777     switch (feol) {
5778       case XYFA_C: printf("%s\n","cr"); break;
5779       case XYFA_L: printf("%s\n","lf"); break;
5780       case XYFA_2: printf("%s\n","crlf"); break;
5781       default: printf("%d\n",i);
5782     }
5783     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5784 #endif /* NOXFER */
5785
5786 #ifdef CK_CTRLZ
5787     printf(" File eof:                %s\n", eofmethod ? "ctrl-z" : "length");
5788     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5789 #endif /* CK_CTRLZ */
5790 #ifndef NOXFER
5791 #ifdef CK_TMPDIR
5792     printf(" File download-directory: %s\n", dldir ? dldir : "(none)");
5793     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5794 #ifdef COMMENT
5795     i = 256;
5796     s = line;
5797     zzstring("\\v(tmpdir)",&s,&i);
5798     printf(" Temporary directory:     %s\n", line);
5799     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5800 #endif /* COMMENT */
5801 #endif /* CK_TMPDIR */
5802 #ifdef VMS
5803     {
5804         extern int vmssversions, vmsrversions;
5805         printf(" Send version-numbers:    %s\n",showoff(vmssversions));
5806         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5807         printf(" Receive version-numbers: %s\n",showoff(vmsrversions));
5808         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5809     }
5810 #endif /* VMS */
5811     printf(" Send move-to:            %s\n",
5812            snd_move ? snd_move : "(none)");
5813     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5814     printf(" Send rename-to:          %s\n",
5815            snd_rename ? snd_rename : "(none)");
5816     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5817     printf(" Receive move-to:         %s\n",
5818            rcv_move ? rcv_move : "(none)");
5819     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5820     printf(" Receive rename-to:       %s\n",
5821            rcv_rename ? rcv_rename : "(none)");
5822     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5823 #endif /* NOXFER */
5824 #ifdef KERMRC
5825     printf(" Initialization file:     %s\n", noinit ? "(none)" :
5826 #ifdef CK_SYSINI
5827            CK_SYSINI
5828 #else
5829            kermrc
5830 #endif /* CK_SYSINI */
5831            );
5832 #endif /* KERMRC */
5833     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5834
5835     if (k_info_dir) {
5836         printf(" Kermit doc files:        %s\n", k_info_dir);
5837         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5838     }
5839
5840 #ifdef CKROOT
5841     s = zgetroot();
5842     printf(" Root set:                %s\n", s ? s : "(none)");
5843 #endif /* CKROOT */
5844
5845 #ifdef UNIX
5846     printf(" Disk output buffer:      %d (writes are %s, %s)\n",
5847            zobufsize,
5848            zofbuffer ? "buffered" : "unbuffered",
5849            zofblock ? "blocking" : "nonblocking"
5850            );
5851     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5852 #ifdef DYNAMIC
5853     printf(" Stringspace:             %d\n", zsetfil(0,2));
5854     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5855     printf(" Listsize:                %d\n", zsetfil(0,4));
5856     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5857 #endif /* DYNAMIC */
5858 #endif /* UNIX */
5859 #ifdef OS2ORUNIX
5860     printf(" Longest filename:        %d\n", maxnam);
5861     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5862     printf(" Longest pathname:        %d\n", maxpath);
5863     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5864 #endif /* OS2ORUNIX */
5865
5866     printf(" Last file sent:          %s\n", sfspec ? sfspec : "(none)");
5867     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5868     printf(" Last file received:      %s\n", rfspec ? rfspec : "(none)");
5869     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5870     printf("\n Also see:\n");
5871     n++;
5872     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
5873     printf(" SHOW PROTOCOL, SHOW XFER");
5874 #ifdef CK_LABELED
5875     printf(", SHOW LABELED");
5876 #endif /* CK_LABELED */
5877 #ifdef PATTERNS
5878     printf(", SHOW PATTERNS");
5879 #endif /* PATTERNS */
5880 #ifdef STREAMING
5881     printf(", SHOW STREAMING");
5882 #endif /* STREAMING */
5883 #ifndef NOCSETS
5884     printf(", SHOW CHARACTER-SETS");
5885 #endif /* NOCSETS */
5886     printf("\n\n");
5887 }
5888
5889 #ifndef NOXFER
5890 VOID
5891 shoparp() {                             /* Protocol */
5892     extern int docrc, skipbup;
5893     char *s;
5894
5895 #ifdef CK_TIMERS
5896     extern int rttflg;
5897 #endif /* CK_TIMERS */
5898
5899     printf("Protocol: %s\n",ptab[protocol].p_name);
5900
5901     if (protocol == PROTO_K) {
5902         printf("\nProtocol Parameters:   Send    Receive");
5903         if (timef)
5904           printf("\n Timeout (used=%2d):%7d*%8d ", timint, rtimo, pkttim);
5905         else
5906           printf("\n Timeout (used=%2d):%7d%9d ",  timint, rtimo, pkttim);
5907 #ifdef XFRCAN
5908         printf("       Cancellation:    %s",showoff(xfrcan));
5909         if (xfrcan)
5910           printf(" %d %d", xfrchr, xfrnum);
5911 #endif /* XFRCAN */
5912         printf("\n Padding:      %11d%9d", npad,   mypadn);
5913         if (bctr == 4)
5914           printf("        Block Check: blank-free-2\n");
5915         else
5916           printf("        Block Check: %6d\n",bctr);
5917         printf(  " Pad Character:%11d%9d", padch,  mypadc);
5918         printf("        Delay:       %6d\n",ckdelay);
5919         printf(  " Pause:        %11d%9d", pktpaus, pktpaus);
5920         printf("        Attributes:      %s\n",showoff(atcapr));
5921         printf(  " Packet Start: %11d%9d", mystch, stchr);
5922         printf("        Max Retries: %6d%s\n",
5923                maxtry,
5924                (maxtry == 0) ? " (unlimited)" : ""
5925                );
5926         printf(  " Packet End:   %11d%9d", seol,   eol);
5927         if (ebqflg)
5928           printf("        8th-Bit Prefix: '%c'",ebq);
5929         else
5930           printf("        8th-Bit Prefix: ('%c' but not used)",ebq);
5931         printf(  "\n Packet Length:%11d ", spmax);
5932         printf("%8d     ",  urpsiz);
5933         if (rptflg)
5934           printf("   Repeat Prefix:  '%c'",rptq);
5935         else
5936           printf("   Repeat Prefix:  ('%c' but not used)",rptq);
5937         printf(  "\n Maximum Length: %9d%9d", maxsps, maxrps);
5938         printf("        Window Size:%7d set, %d used\n",wslotr,wmax);
5939         printf(    " Buffer Size:  %11d%9d", bigsbsiz, bigrbsiz);
5940         printf("        Locking-Shift:    ");
5941         if (lscapu == 2) {
5942             printf("forced");
5943         } else {
5944             printf("%s", (lscapr ? "enabled" : "disabled"));
5945             if (lscapr) printf(",%s%s", (lscapu ? " " : " not "), "used");
5946         }
5947         printf("\n\n");
5948
5949         if (!(s = ptab[protocol].h_b_init)) s = "";
5950         printf(" Auto-upload command (binary): ");
5951         if (*s) {
5952             shostrdef((CHAR *)s);
5953             printf("\n");
5954         } else {
5955             printf("(none)\n");
5956         }
5957         if (!(s = ptab[protocol].h_t_init)) s = "";
5958         printf(" Auto-upload command (text):   ");
5959         if (*s) {
5960             shostrdef((CHAR *)s);
5961             printf("\n");
5962         } else {
5963             printf("(none)\n");
5964         }
5965         if (!(s = ptab[protocol].h_x_init)) s = "";
5966         printf(" Auto-server command:          ");
5967         if (*s) {
5968             shostrdef((CHAR *)s);
5969             printf("\n");
5970         } else {
5971             printf("(none)\n");
5972         }
5973         tmpbuf[0] = NUL;
5974 #ifdef CK_TIMERS
5975         if (rttflg) {
5976             extern int mintime, maxtime;
5977             sprintf(tmpbuf," Packet timeouts: dynamic %d:%d", /* SAFE */
5978                     mintime,
5979                     maxtime);
5980         } else {
5981             sprintf(tmpbuf," Packet timeouts: fixed"); /* SAFE */
5982         }
5983 #endif /* CK_TIMERS */
5984         if (tmpbuf[0])
5985           printf("%-31s",tmpbuf);
5986         printf("Send backup: %s\n",showoff(!skipbup));
5987
5988         printf(" Transfer mode:   %s", xfermode == XMODE_A ?
5989                "automatic   " :
5990                "manual      "
5991                );
5992         printf(" Transfer slow-start: %s, crc: %s\n",
5993                showoff(slostart),
5994                showoff(docrc)
5995                );
5996 #ifdef PIPESEND
5997         {
5998             extern int usepipes;
5999             printf(" Transfer pipes:  %s         ",usepipes ? "on " : "off");
6000         }
6001 #endif /* PIPESEND */
6002 #ifndef NOCSETS
6003         printf(" Transfer character-set: ");
6004         if (tcharset == TC_TRANSP)
6005           printf("transparent\n");
6006         else
6007           printf("%s\n", tcsinfo[tcharset].keyword );
6008 #endif /* NOCSETS */
6009 #ifdef PIPESEND
6010         {
6011             extern char * sndfilter, * rcvfilter;
6012             printf(" Send filter:     %s\n", sndfilter ? sndfilter : "(none)");
6013             printf(" Receive filter:  %s\n", rcvfilter ? rcvfilter : "(none)");
6014         }
6015 #endif /* PIPESEND */
6016         printf("\nAlso see:\n");
6017         printf(" SHOW FILE, SHOW XFER");
6018
6019 #ifdef CK_LABELED
6020         printf(", SHOW LABELED");
6021 #endif /* CK_LABELED */
6022 #ifdef PATTERNS
6023         printf(", SHOW PATTERNS");
6024 #endif /* PATTERNS */
6025 #ifdef STREAMING
6026         printf(", SHOW STREAMING");
6027 #endif /* STREAMING */
6028 #ifndef NOCSETS
6029         printf(", SHOW CHARACTER-SETS");
6030 #endif /* NOCSETS */
6031     }
6032
6033 #ifdef CK_XYZ
6034 #ifdef XYZ_INTERNAL
6035     if (protocol != PROTO_K) {
6036         int i;
6037         int x;
6038         printf(" File type: %s\n", binary ? "binary" : "text");
6039         if (protocol == PROTO_Z) {              /* Zmodem */
6040             printf(" Window size:   ");
6041             if (ptab[protocol].winsize < 1)
6042               printf("none\n");
6043             else
6044               printf("%d\n",wslotr);
6045 #ifdef COMMENT
6046             printf(" Packet (frame) length: ");
6047             if (ptab[protocol].spktlen < 0)
6048               printf("none\n");
6049             else
6050               printf("%d\n",spmax);
6051 #endif /* COMMENT */
6052         } else {
6053             if (ptab[protocol].spktlen >= 1000)
6054               printf(" 1K packets\n");
6055             else
6056               printf(" 128-byte packets\n");
6057         }
6058         printf(" Pathname stripping when sending:   %s\n",
6059                showoff(ptab[protocol].fnsp)
6060                );
6061         printf(" Pathname stripping when receiving: %s\n",
6062                showoff(ptab[protocol].fnrp)
6063                );
6064         printf(" Filename collision action:         ");
6065         for (i = 0; i < ncolx; i++)
6066           if (colxtab[i].kwval == fncact) break;
6067         printf("%-12s", (i == ncolx) ? "unknown" : colxtab[i].kwd);
6068
6069         printf("\n Escape control characters:          ");
6070         x = ptab[protocol].prefix;
6071         if (x == PX_ALL)
6072           printf("all\n");
6073         else if (x == PX_CAU || x==PX_WIL)
6074           printf("minimal\n");
6075         else
6076           printf("none\n");
6077         if (!(s = ptab[protocol].h_b_init))
6078           s = "";
6079         printf(" Autoreceive command (binary): %s\n", *s ? s : "(none)");
6080         if (!(s = ptab[protocol].h_t_init))
6081           s = "";
6082         printf(" Autoreceive command (text):   %s\n", *s ? s : "(none)");
6083     }
6084 #else
6085 #ifndef NOPUSH
6086     if (protocol != PROTO_K) {
6087         _PROTOTYP( VOID shoextern, (void) );
6088         printf("\nExecuted by external commands:\n\n");
6089         s = ptab[protocol].p_b_scmd;
6090         if (!s) s = "";
6091         printf(" SEND command (binary):        %s\n", *s ? s : "(none)");
6092         s = ptab[protocol].p_t_scmd;
6093         if (!s) s = "";
6094         printf(" SEND command (text):          %s\n", *s ? s : "(none)");
6095         s = ptab[protocol].p_b_rcmd;
6096         if (!s) s = "";
6097         printf(" RECEIVE command (binary):     %s\n", *s ? s : "(none)");
6098         s = ptab[protocol].p_t_rcmd;
6099         if (!s) s = "";
6100         printf(" RECEIVE command (text):       %s\n", *s ? s : "(none)");
6101         s = ptab[protocol].h_b_init;
6102         if (!s) s = "";
6103         printf(" Autoreceive command (binary): %s\n", *s ? s : "(none)");
6104         s = ptab[protocol].h_t_init;
6105         if (!s) s = "";
6106         printf(" Autoreceive command (text):   %s\n", *s ? s : "(none)");
6107         (VOID) shoextern();
6108     }
6109 #endif /* NOPUSH */
6110 #endif /* XYZ_INTERNAL */
6111 #endif /* CK_XYZ */
6112 }
6113 #endif /* NOXFER */
6114
6115 #ifndef NOCSETS
6116 /* Character-set items */
6117
6118 extern int s_cset, r_cset, axcset[], afcset[];
6119 extern struct keytab xfrmtab[];
6120
6121 VOID
6122 shoparl() {
6123 #ifdef COMMENT
6124     int i;
6125 /* Misleading... */
6126     printf("\nAvailable Languages:\n");
6127     for (i = 0; i < MAXLANG; i++) {
6128         printf(" %s\n",langs[i].description);
6129     }
6130 #else
6131     printf("\nLanguage-specific translation rules: %s\n",
6132            language == L_USASCII ? "none" : langs[language].description);
6133     shocharset();
6134     printf("\n\n");
6135 #endif /* COMMENT */
6136 }
6137
6138 VOID
6139 shocharset() {
6140     int x;
6141 #ifdef COMMENT
6142     char * s = "Unknown";
6143     extern int xlatype;
6144 #endif /* COMMENT */
6145
6146 #ifndef NOXFER
6147     extern int xfrxla;
6148 #endif /* NOXFER */
6149
6150     debug(F101,"SHOW FILE CHAR","",fcharset);
6151     printf("\n");
6152 #ifndef NOXFER
6153     printf(" Transfer Translation: %s\n", showoff(xfrxla));
6154     if (!xfrxla) {
6155         printf(
6156       " Because transfer translation is off, the following are ignored:\n\n");
6157     }
6158 #endif /* NOXFER */
6159     printf(" File Character-Set: %s (%s), ",
6160            fcsinfo[fcharset].keyword,
6161            fcsinfo[fcharset].name
6162            );
6163     if ((x = fcsinfo[fcharset].size) == 128)
6164       printf("7-bit");
6165     else if (x == 256)
6166       printf("8-bit");
6167     else
6168       printf("multibyte");
6169     printf("\n");
6170     printf(" File Scan: %s\n",showoff(filepeek));
6171     printf("   Default 7bit-Character-Set: %s\n",fcsinfo[dcset7].keyword);
6172     printf("   Default 8bit-Character-Set: %s\n",fcsinfo[dcset8].keyword);
6173     printf(" Transfer Character-Set");
6174 #ifdef COMMENT
6175     if (tslevel == TS_L2)
6176       printf(": (international)");
6177     else
6178 #endif /* COMMENT */
6179     if (tcharset == TC_TRANSP)
6180       printf(": Transparent");
6181     else
6182       printf(": %s (%s)",tcsinfo[tcharset].keyword, tcsinfo[tcharset].name);
6183     printf("\n");
6184 #ifdef COMMENT
6185     switch (xlatype) {
6186       case XLA_NONE: s = "None"; break;
6187       case XLA_BYTE: s = "Byte"; break;
6188       case XLA_JAPAN: s = "Japanese"; break;
6189       case XLA_UNICODE: s = "Unicode"; break;
6190     }
6191     printf("\n Translation type: %s\n",s);
6192 #endif /* COMMENT */
6193     printf(" SEND character-set-selection: %s\n",xfrmtab[s_cset].kwd);
6194     printf(" RECEIVE character-set-selection: %s\n",xfrmtab[r_cset].kwd);
6195     if (s_cset == XMODE_A || r_cset == XMODE_A)
6196       printf(
6197       " (Use SHOW ASSOCIATIONS to list automatic character-set selections.)\n"
6198              );
6199 }
6200
6201 VOID
6202 showassoc() {
6203     int i, k, n = 4;
6204     char * s;
6205     printf("\nFor incoming files:\n\n");
6206     printf("Transfer Character-Set   File Character-Set\n");
6207     for (i = 1; i <= MAXTCSETS; i++) {
6208         k = axcset[i];
6209         if (k < 0 || k > MAXFCSETS)
6210           s = "(none)";
6211         else
6212           s = fcsinfo[k].keyword;
6213         if (!s) s = "";
6214         if (!*s) s = "(none)";
6215         printf(" %-25s%s\n",tcsinfo[i].keyword,s);
6216         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
6217     }
6218     printf("\nFor outbound files:\n\n");
6219     n += 2;
6220     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
6221     printf("File Character-Set       Transfer Character-Set\n");
6222     if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
6223     for (i = 0; i <= MAXFCSETS; i++) {
6224         k = afcset[i];
6225         if (k < 0 || k > MAXTCSETS)
6226           s = "(none)";
6227         else
6228           s = tcsinfo[k].keyword;
6229         if (!s) s = "";
6230         if (!*s) s = "(none)";
6231         printf(" %-25s%s\n",fcsinfo[i].keyword,s);
6232         if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0;
6233     }
6234 }
6235 #endif /* NOCSETS */
6236
6237 VOID
6238 shopar() {
6239     printf("Show what?  (Type \"show ?\" for a list of possibilities.)\n");
6240 }
6241 #endif /* NOSHOW */
6242
6243 #ifndef NOXFER
6244 /*  D O S T A T  --  Display file transfer statistics.  */
6245
6246 int
6247 dostat(brief) int brief; {
6248     extern long filrej, peakcps;
6249     extern int lastspmax, streamed, cleared, streamok;
6250     extern char whoareu[];
6251     int n = 0, ftp = 0;
6252     extern int docrc, interrupted, fatalio;
6253
6254     ftp = lastxfer & W_FTP;
6255
6256 #ifdef CK_TTGWSIZ
6257 #ifdef OS2
6258     if (tt_cols[VTERM] < 0 || tt_rows[VTERM] < 0)
6259       ttgwsiz();
6260 #else /* OS2 */
6261     if (ttgwsiz() > 0) {
6262         if (tt_rows > 0 && tt_cols > 0) {
6263             cmd_rows = tt_rows;
6264             cmd_cols = tt_cols;
6265         }
6266     }
6267 #endif /* OS2 */
6268 #endif /* CK_TTGWSIZ */
6269
6270     debug(F101,"dostat xferstat","",xferstat);
6271     if (xferstat < 0) {
6272         printf(" No file transfers yet.\n");
6273         return(1);
6274     }
6275     n = 0;
6276     if (brief) { printf("\n"); n++; };
6277     printf(" protocol               : %s\n",
6278            ftp ? "ftp" : ptab[protocol].p_name);
6279     n++;
6280     printf(" status                 : ");
6281     if (xferstat) printf("SUCCESS\n");
6282     else if (interrupted) printf("FAILURE (interrupted)\n");
6283     else if (fatalio) printf("FAILURE (i/o error)\n");
6284     else printf("FAILURE\n");
6285 #ifndef XYZ_INTERNAL
6286     if (!ftp && protocol != PROTO_K) {
6287         printf("\n external protocol statistics not available\n");
6288         return(1);
6289     }
6290 #endif /* XYZ_INTERNAL */
6291     n++;
6292     if (!ftp) {
6293         if (!xferstat > 0) {
6294             if (docrc)
6295               printf(" crc-16 of file(s)      : %ld\n", crc16);
6296             else
6297               printf(" crc-16 of file(s)      : (disabled)\n");
6298             n++;
6299         }
6300         if (!xferstat && *epktmsg) {
6301             printf(" reason                 : %s\n", epktmsg);
6302             n++;
6303         }
6304     }
6305     if (!brief) {
6306 #ifdef NEWFTP
6307         if (ftp) {
6308             extern char ftp_srvtyp[];
6309             printf(" remote system type     : %s\n",ftp_srvtyp);
6310         } else
6311 #endif /* NEWFTP */
6312           if (whoareu[0]) {
6313             printf(" remote system type     : %s\n",
6314                    getsysid((char *)whoareu));
6315             n++;
6316         }
6317         printf(" files transferred      : %ld\n",filcnt - filrej);
6318         if (!ftp)
6319           printf(" files not transferred  : %ld\n",filrej);
6320         printf(" characters last file   : %s\n",ckfstoa(ffc));
6321         printf(" total file characters  : %s\n",ckfstoa(tfc));
6322         n += ftp ? 3 : 4;
6323         if (!ftp) {
6324             printf(" communication line in  : %s\n",ckfstoa(tlci));
6325             printf(" communication line out : %s\n",ckfstoa(tlco));
6326             printf(" packets sent           : %d\n", spackets);
6327             printf(" packets received       : %d\n", rpackets);
6328             n += 4;
6329         }
6330     }
6331     if (ftp) goto dotimes;
6332
6333     printf(" damaged packets rec'd  : %d\n", crunched);
6334     printf(" timeouts               : %d\n", timeouts);
6335     printf(" retransmissions        : %d\n", retrans);
6336     n += 3;
6337
6338     if (!brief) {
6339         if (filcnt > 0) {
6340             printf(" parity                 : %s",parnam((char)parity));
6341             n++;
6342             if (autopar) { printf(" (detected automatically)"); n++; }
6343             printf(
6344                  "\n control characters     : %ld prefixed, %ld unprefixed\n",
6345                    ccp, ccu);
6346             n++;
6347             printf(" 8th bit prefixing      : ");
6348             n++;
6349             if (ebqflg) printf("yes [%c]\n",ebq); else printf("no\n");
6350             n++;
6351             printf(" locking shifts         : %s\n", lscapu ? "yes" : "no");
6352             n++;
6353         }
6354     }
6355     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6356     if (streamed > 0)
6357       printf(" window slots used      : (streaming)\n");
6358     else
6359       printf(" window slots used      : %d of %d\n", wmax, wslotr);
6360     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6361     printf(" reliable:              : %s%s\n",
6362            streamok ? "" : "not ", "negotiated");
6363     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6364     printf(" clearchannel:          : %s%s\n",
6365            cleared  ? "" : "not ", "negotiated");
6366     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6367
6368     if (!brief) {
6369         printf(" packet length          : %d (send), %d (receive)\n",
6370                lastspmax, urpsiz);
6371         if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6372         printf(" compression            : ");
6373         if (rptflg)
6374           printf("yes [%c] (%ld)\n",(char) rptq,rptn);
6375         else
6376           printf("no\n");
6377         if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6378         if (bctu == 4)
6379           printf(" block check type used  : blank-free-2\n");
6380         else
6381           printf(" block check type used  : %d\n",bctu);
6382         if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6383     }
6384
6385   dotimes:
6386
6387 #ifdef GFTIMER
6388 #ifdef COMMENT
6389     printf(" elapsed time           : %0.3f sec, %s\n", fptsecs,hhmmss(tsecs));
6390 #endif /* COMMENT */
6391     printf(" elapsed time           : %s (%0.3f sec)\n",
6392            hhmmss((long)(fptsecs + 0.5)),fptsecs);
6393 #else
6394 #ifdef COMMENT
6395     printf(" elapsed time           : %s (%d sec)\n",hhmmss(tsecs),tsecs);
6396 #endif /* COMMENT */
6397     printf(" elapsed time           : %d sec, %s\n",tsecs,hhmmss(tsecs));
6398 #endif /* GFTIMER */
6399     if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6400     if (!ftp && local && !network && !brief) {
6401         if (speed <= 0L) speed = ttgspd();
6402         if (speed > 0L) {
6403             if (speed == 8880)
6404               printf(" transmission rate      : 75/1200 bps\n");
6405             else
6406               printf(" transmission rate      : %ld bps\n",speed);
6407             if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; }
6408         }
6409     }
6410     if (!ftp && local && !network &&    /* Only makes sense for */
6411         mdmtyp == 0 &&                  /* direct serial connections */
6412         speed > 99L &&                  /* when we really know the speed */
6413         speed != 8880L
6414         ) {
6415         int eff;
6416         eff = (((tfcps * 100L) / (speed / 100L)) + 5L) / 10L;
6417         printf(" effective data rate    : %ld cps (%d%%)\n",tfcps,eff);
6418     } else
6419       printf(" effective data rate    : %ld cps\n", tfcps);
6420     if (!ftp && peakcps > 0L && peakcps > tfcps)
6421       printf(" peak data rate         : %ld cps\n", peakcps);
6422     if (brief)
6423       printf("\nUse STATISTICS /VERBOSE for greater detail.\n\n");
6424     return(1);
6425 }
6426 #endif /* NOXFER */
6427
6428 #ifndef NOSPL
6429
6430 /* The INPUT command */
6431
6432 /*
6433   NOTE: An INPUT timeout of 0 means to perform a nonblocking read of the
6434   material that has already arrived and is waiting to be read, and perform
6435   matches against it, without doing any further reads.  It should succeed
6436   or fail instantaneously.
6437 */
6438
6439 /* Output buffering for "doinput" */
6440
6441 #ifdef pdp11
6442 #define MAXBURST 16             /* Maximum size of input burst */
6443 #else
6444 #define MAXBURST 1024
6445 #endif /* pdp11 */
6446 #ifdef OSK
6447 static CHAR *conbuf;            /* Buffer to hold output for console */
6448 #else
6449 static CHAR conbuf[MAXBURST];   /* Buffer to hold output for console */
6450 #endif /* OSK */
6451 static int concnt = 0;          /* Number of characters buffered */
6452 #ifdef OSK
6453 static CHAR *sesbuf;            /* Buffer to hold output for session log */
6454 #else
6455 static CHAR sesbuf[MAXBURST];   /* Buffer to hold output for session log */
6456 #endif /* OSK */
6457 static int sescnt = 0;          /* Number of characters buffered */
6458
6459 extern int debses;                      /* TERMINAL DEBUG ON/OFF */
6460
6461 static VOID                             /* Flush INPUT echoing */
6462 myflsh() {                              /* and session log output. */
6463     if (concnt > 0) {
6464         if (debses) {                   /* Terminal debugging? */
6465             int i;
6466             for (i = 0; i < concnt; i++)
6467               conol(dbchr(conbuf[i]));
6468         } else
6469           conxo(concnt, (char *) conbuf);
6470         concnt = 0;
6471     }
6472     if (sescnt > 0) {
6473         logstr((char *) sesbuf, sescnt);
6474         sescnt = 0;
6475     }
6476 }
6477
6478 /* Execute the INPUT and MINPUT commands */
6479
6480 int instatus = -1;
6481 long inetime = -1L;
6482 int inwait = 0;
6483 int nowrap = 0;
6484
6485 /* For returning the input sequence that matched */
6486
6487 #ifdef BIGBUFOK
6488 #define MATCHBUFSIZ 8191
6489 #else
6490 #define MATCHBUFSIZ 1023
6491 #endif /* BIGBUFOK */
6492 static char * matchbuf = NULL;
6493 static int matchindex = 0;
6494 static int burst = 0;                      /* Chars remaining in input burst */
6495 /*
6496   timo = How long to wait:
6497          < 0 = Wait forever
6498            0 = Don't wait at all - material must already have arrived
6499          > 0 = Wait this many seconds
6500   ms   = Array of strings to wait for.
6501   mp   = Array of flags.
6502          If mp[i] == 0, ms[i] is literal, else it's a pattern.
6503   flags = bit mask
6504     INPSW_NOM = /NOMATCH = 1
6505     INPSW_CLR = /CLEAR   = 2
6506     INPSW_NOW = /NOWRAP  = 4
6507     INPSW_COU = /COUNT   = 8
6508   count = /COUNT: value if a /COUNT switch was given.
6509
6510  Returns:
6511     0 on failure, 1 on success.
6512 */
6513 #ifndef ES_NORMAL
6514 #define ES_NORMAL 0
6515 #endif  /* ES_NORMAL */
6516 extern int inesc[], oldesc[];
6517
6518 int
6519 doinput(timo,ms,mp,flags,count)
6520     int timo; char *ms[]; int mp[]; int flags; int count; {
6521     extern int inintr;
6522 #ifdef CK_AUTODL
6523     extern int inautodl;
6524 #endif /* CK_AUTODL */
6525     int x, y, i, t, rt, icn, anychar = 0, mi[MINPMAX];
6526 #ifdef GFTIMER
6527     CKFLOAT fpt = 0.0;
6528 #endif /* GFTIMER */
6529     int savecount = 0;
6530     int nomatch = 0;
6531     int clearfirst = 0;
6532     int lastchar = 0;
6533     int waiting = 0;
6534     int imask = 0;
6535     char ch, *xp, *s;
6536     CHAR c;
6537 #ifndef NOLOCAL
6538 #ifdef OS2
6539     extern int term_io;
6540     int term_io_save;
6541 #endif /* OS2 */
6542 #endif /* NOLOCAL */
6543 #ifdef TNCODE
6544     static int cr = 0;
6545 #endif /* TNCODE */
6546     int is_tn = 0;
6547 #ifdef SSHBUILTIN
6548     extern int ssh_cas;
6549     extern char * ssh_cmd;
6550 #endif /* SSHBUILTIN */
6551     int noescseq = 0;                   /* Filter escape sequences */
6552
6553     debug(F101,"input count","",count);
6554     debug(F101,"input flags","",flags);
6555
6556 /*
6557   CK_BURST enables the INPUT speedup code, which depends on ttchk() returning
6558   accurate information.  If INPUT fails with this code enabled, change the
6559   above "#define" to "#undef".
6560 */
6561 #define CK_BURST
6562
6563 /***** CHANGE THIS TO A SET INPUT PARAMETER *****/
6564
6565     noescseq = (sessft == XYFT_T);      /* Filter escape sequences */
6566
6567     imask = cmask;
6568     if (parity) imask = 0x7f;
6569     inwait = timo;                      /* For \v(inwait) */
6570
6571     /* Options from command switches */
6572
6573     nowrap = flags & INPSW_NOW;         /* 4 = /NOWRAP */
6574     nomatch = flags & INPSW_NOM;        /* 1 = /NOMATCH */
6575     clearfirst = flags & INPSW_CLR;     /* 2 = /CLEAR */
6576     savecount = count;
6577
6578     makestr(&inpmatch,NULL);
6579     if (!matchbuf) {
6580         matchbuf = malloc(MATCHBUFSIZ+1);
6581         matchbuf[0] = NUL;
6582     }
6583     matchindex = 0;
6584
6585     /* If last time through we returned because of /NOWRAP and buffer full */
6586     /* now we have to clear the buffer to make room for another load. */
6587
6588     if (nowrap && instatus == INP_BF)
6589       clearfirst = 1;
6590
6591     if (clearfirst) {                   /* INPUT /CLEAR */
6592         int i;
6593         myflsh();                       /* Flush screen and log buffers */
6594         for (i = 0; i < inbufsize; i++)
6595           inpbuf[i] = NUL;
6596         inpbp = inpbuf;
6597     }
6598     is_tn =
6599 #ifdef TNCODE
6600         (local && network && IS_TELNET()) || (!local && sstelnet)
6601 #else
6602          0
6603 #endif /* TNCODE */
6604           ;
6605
6606 #ifdef CK_SSL
6607     if (is_tn) if (ssl_raw_flag || tls_raw_flag) is_tn = 0;
6608 #endif  /* CK_SSL */
6609
6610     instatus = INP_IE;                  /* 3 = internal error */
6611     kbchar = 0;
6612
6613 #ifdef OSK
6614     if (conbuf == NULL) {
6615         if ((conbuf = (CHAR *)malloc(MAXBURST*2)) == NULL) {
6616             return(0);
6617         }
6618         sesbuf = conbuf + MAXBURST;
6619     }
6620 #endif /* OSK */
6621
6622 #ifndef NOLOCAL
6623     if (local) {                        /* In local mode... */
6624         if ((waiting = ttchk()) < 0) {  /* check that connection is open */
6625             if (!quiet) {
6626                 if ((!network 
6627 #ifdef TN_COMPORT
6628                       || istncomport()
6629 #endif /* TN_COMPORT */
6630                       ) && carrier != CAR_OFF)
6631                     printf("?Carrier detect failure on %s.\n", ttname);
6632                 else
6633                     printf("?Connection %s %s is not open.\n",
6634                        network ? "to" : "on",
6635                        ttname
6636                        );
6637             }
6638             instatus = INP_IO;
6639             return(0);
6640         }
6641         debug(F101,"doinput waiting","",waiting);
6642         y = ttvt(speed,flow);           /* Put line in "ttvt" mode */
6643         if (y < 0) {
6644             printf("?INPUT initialization error\n");
6645             instatus = INP_IO;
6646             return(0);                  /* Watch out for failure. */
6647         }
6648     }
6649 #endif /* NOLOCAL */
6650
6651 #ifdef SSHBUILTIN
6652     if ( network && nettype == NET_SSH && ssh_cas && ssh_cmd && 
6653          !(strcmp(ssh_cmd,"kermit") && strcmp(ssh_cmd,"sftp"))) {
6654         if (!quiet)
6655           printf("?SSH Subsystem active: %s\n", ssh_cmd);
6656         instatus = INP_IKS;
6657         return(0);
6658     }
6659 #endif /* SSHBUILTIN */
6660
6661     debug(F111,"doinput ms[0]",ms[0],waiting);
6662
6663     if (!ms[0] || isemptystring(ms[0])) { /* No search string was given nor */
6664         if (count < 2)                    /* a /COUNT: switch so we just */
6665           anychar = 1;                    /* wait for the first character */
6666     }
6667     if (nomatch) anychar = 0;           /* Don't match anything */
6668
6669     if (!anychar && waiting == 0 && timo == 0)
6670       return(0);
6671
6672 #ifndef NODEBUG
6673     if (deblog) {
6674         char xbuf[24];
6675         debug(F101,"doinput anychar","",anychar);
6676         debug(F101,"doinput timo","",timo);
6677         debug(F101,"doinput echo","",inecho);
6678 #ifdef CK_BURST
6679         debug(F101,"doinput burst","",burst);
6680 #endif  /* CK_BURST */
6681         y = -1;
6682         while (ms[++y]) {
6683             sprintf(xbuf,"doinput string %2d",y); /* SAFE (24) */
6684             debug(F111,xbuf,ms[y],mp[y]);
6685         }
6686     }
6687 #endif /* NODEBUG */
6688
6689 #ifdef IKS_OPTION
6690     if (is_tn) {
6691         /* If the remote side is in a state of IKS START-SERVER    */
6692         /* we request that the state be changed.  We will detect   */
6693         /* a failure to adhere to the request when we call ttinc() */
6694         if (TELOPT_U(TELOPT_KERMIT) &&
6695             TELOPT_SB(TELOPT_KERMIT).kermit.u_start)
6696           iks_wait(KERMIT_REQ_STOP,0);  /* Send Request-Stop */
6697 #ifdef CK_AUTODL
6698         /* If we are processing packets during INPUT and we have not */
6699         /* sent a START message, do so now.                          */
6700         if (inautodl && TELOPT_ME(TELOPT_KERMIT) &&
6701                         !TELOPT_SB(TELOPT_KERMIT).kermit.me_start) {
6702             tn_siks(KERMIT_START);      /* Send Kermit-Server Start */
6703         }
6704 #endif /* CK_AUTODL */
6705     }
6706 #endif /* IKS_OPTION */
6707     x = 0;                              /* Return code, assume failure */
6708     instatus = INP_TO;                  /* Status, assume timeout */
6709
6710     for (y = 0; y < MINPMAX; y++)       /* Initialize... */
6711       mi[y] = 0;                        /*  ..string pattern match position */
6712
6713     if (!inpcas[cmdlvl]) {              /* INPUT CASE = IGNORE?  */
6714         y = -1;
6715         while ((xp = ms[++y])) {        /* Convert each target to lowercase */
6716             while (*xp) {
6717                 if (isupper(*xp)) *xp = (char) tolower(*xp);
6718                 xp++;
6719             }
6720         }
6721     }
6722     rtimer();                           /* Reset timer. */
6723 #ifdef GFTIMER
6724     rftimer();                          /* Floating-point timer too. */
6725 #endif /* GFTIMER */
6726     inetime = -1L;                      /* Initialize elapsed time. */
6727     t = 0;                              /* Time now is 0. */
6728     m_found = 0;                        /* Default to timed-out */
6729     incount = 0;                        /* Character counter */
6730     rt = (timo == 0) ? 0 : 1;           /* Character-read timeout interval */
6731
6732 #ifndef NOLOCAL
6733 #ifdef OS2
6734     term_io_save = term_io;             /* Disable I/O by emulator */
6735     term_io = 0;
6736 #endif /* OS2 */
6737 #endif /* NOLOCAL */
6738
6739     while (1) {                         /* Character-getting loop */
6740 #ifdef CK_APC
6741         /* Check to see if there is an Autodown or other APC command */
6742         if (apcactive == APC_LOCAL ||
6743             (apcactive == APC_REMOTE && apcstatus != APC_OFF)) {
6744             if (mlook(mactab,"_apc_commands",nmac) == -1) {
6745                 debug(F110,"doinput about to execute APC",apcbuf,0);
6746                 domac("_apc_commands",apcbuf,cmdstk[cmdlvl].ccflgs|CF_APC);
6747                 delmac("_apc_commands",1);
6748                 apcactive = APC_INACTIVE;
6749 #ifdef DEBUG
6750             } else {
6751                 debug(F100,"doinput APC in progress","",0);
6752 #endif /* DEBUG */
6753             }
6754         }
6755 #endif /* CK_APC */
6756
6757         if (timo == 0 && waiting < 1) { /* Special exit criterion */
6758             instatus = INP_TO;          /* for timeout == 0 */
6759             break;
6760         }
6761         if (local) {                    /* One case for local */
6762             y = ttinc(rt);              /* Get character from comm device */
6763             debug(F101,"doinput ttinc(rt) returns","",y);
6764             if (y < -1) {               /* Connection failed. */
6765                 instatus = INP_IO;      /* Status = i/o error */
6766 #ifndef NOLOCAL
6767 #ifdef OS2
6768                 term_io = term_io_save;
6769 #endif /* OS2 */
6770 #endif /* NOLOCAL */
6771                 switch (y) {
6772                   case -2:              /* Connection lost */
6773                     if (local && !network && carrier != CAR_OFF) {
6774                         dologend();
6775                         printf("Connection closed.\n");
6776                         ttclos(1);
6777                     }
6778                     break;
6779                   case -3:
6780                     dologend();
6781                     printf("Session Limit exceeded - closing connection.\n");
6782                     ttclos(1);
6783                   default:
6784                     break;
6785                 }
6786                 debug(F111,"doinput Connection failed","returning 0",y);
6787                 return(0);
6788             }
6789             if (inintr) {
6790                 debug(F111,"doinput","inintr",inintr);
6791                 if ((icn = conchk()) > 0) { /* Interrupted from keyboard? */
6792                     debug(F101,"input interrupted from keyboard","",icn);
6793                     kbchar = coninc(0);
6794                     if (kbchar >= 0) {
6795                         while (--icn > 0) {
6796                             debug(F110,"doinput","absorbing",0);
6797                             coninc(0);      /* Yes, absorb what was typed. */
6798                         }
6799                         instatus = INP_UI;  /* Fail and remember why. */
6800                         break;
6801                     }
6802                 }
6803             }
6804         } else {                        /* Another for remote */
6805             y = coninc(rt);
6806             debug(F101,"doinput coninc(rt) returns","",y);
6807         }
6808         if (y > -1) {                   /* A character arrived */
6809             debug(F111,"doinput","a character arrived",y);
6810             if (timo == 0)
6811               waiting--;
6812 #ifndef OS2
6813 #define TN_NOLO
6814 #endif /* OS2 */
6815 #ifdef NOLOCAL
6816 #define TN_NOLO
6817 #endif /* NOLOCAL */
6818
6819 #ifdef TN_NOLO
6820             debug(F100,"doinput TN_NOLO","",0);
6821 #ifdef TNCODE
6822             /* Check for telnet protocol negotiation */
6823             if (is_tn) {
6824                 switch (y & 0xff) {
6825                   case IAC:
6826                     cr = 0;
6827                     myflsh();   /* Break from input burst for tn_doop() */
6828 #ifdef CK_BURST
6829                     burst = 0;
6830 #endif /* CK_BURST */
6831                     waiting -= 2;       /* (not necessarily...) */
6832                     switch (tn_doop((CHAR)(y & 0xff),duplex,ttinc)) {
6833                       case 2: duplex = 0; continue;
6834                       case 1: duplex = 1; continue;
6835 #ifdef IKS_OPTION
6836                       case 4:
6837                         if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start &&
6838                              !tcp_incoming) {
6839                             instatus = INP_IKS;
6840                             printf(
6841  " Internet Kermit Service in SERVER mode.\n Please use REMOTE commands.\n"
6842                                    );
6843                             break;
6844                         }
6845                         continue;
6846 #endif /* IKS_OPTION */
6847                       case 6:           /* TELNET DO LOGOUT received */
6848                         continue;
6849                       case 7:
6850                       case 3:           /* A quoted IAC */
6851                         break;
6852                       default:
6853                         continue;
6854                     }
6855                   case CR:
6856                     cr = 1;
6857                     break;
6858                   case NUL:
6859                     if (!TELOPT_U(TELOPT_BINARY) && cr) {
6860                         cr = 0;
6861                         continue;
6862                     }
6863                     cr = 0;
6864                     break;
6865                   default:
6866                     cr = 0;
6867                 }
6868                 /* I'm echoing remote chars */
6869                 if (TELOPT_ME(TELOPT_ECHO) && tn_rem_echo)
6870                   ttoc((char)y);
6871             }
6872 #endif /* TNCODE */
6873 #ifdef CK_AUTODL
6874             /* Check for file transfer packets */
6875             if (inautodl) autodown(y);
6876 #endif /* CK_AUTODL */
6877 #else  /* TN_NOLO */
6878             debug(F100,"doinput !TN_NOLO","",0);
6879 #ifdef TNCODE
6880             /* Check for telnet protocol negotiation */
6881             if (is_tn) {
6882                 int tx;
6883                 switch (y & 0xff) {
6884                   case IAC:
6885                     myflsh();   /* Break from input burst for tn_doop() */
6886 #ifdef CK_BURST
6887                     burst = 0;
6888 #endif /* CK_BURST */
6889 #ifdef IKS_OPTION
6890                     tx = scriptwrtbuf((USHORT)y);
6891                     if (tx == 4) {
6892                         if (TELOPT_U(TELOPT_KERMIT) && 
6893                             TELOPT_SB(TELOPT_KERMIT).kermit.u_start &&
6894                             !tcp_incoming
6895                             ) {
6896                             instatus = INP_IKS;
6897                             printf(
6898   " Internet Kermit Service in SERVER mode.\n Please use REMOTE commands.\n"
6899                                    );
6900                             break;
6901                         }
6902                     } else if (tx == 6) {
6903                         /* TELNET DO LOGOUT received */
6904
6905                     }
6906 #else /* IKS_OPTION */
6907                     /* Handles Telnet negotiations */
6908                     tx = scriptwrtbuf((USHORT)y);
6909                     if (tx == 6) {
6910                         /* TELNET DO LOGOUT received */
6911                     }
6912 #endif /* IKS_OPTION */
6913                     waiting -= 2;       /* (not necessarily...) */
6914                     cr = 0;
6915                     continue;           /* and autodownload check */
6916                   case CR:
6917                     cr = 1;
6918                     tx = scriptwrtbuf((USHORT)y);
6919                     if (tx == 6) {
6920                         /* TELNET DO LOGOUT received */
6921                     }
6922                     break;
6923                   case NUL:
6924                     cr = 0;
6925                     if (!TELOPT_U(TELOPT_BINARY) && cr)
6926                       continue;
6927                     tx = scriptwrtbuf((USHORT)y);
6928                     if (tx == 6) {
6929                         /* TELNET DO LOGOUT received */
6930                     }
6931                     break;
6932                   default:
6933                     cr = 0;
6934                     tx = scriptwrtbuf((USHORT)y);
6935                     if (tx == 6) {
6936                         /* TELNET DO LOGOUT received */
6937                     }
6938                 }
6939                 /* I'm echoing remote chars */
6940                 if (TELOPT_ME(TELOPT_ECHO) && tn_rem_echo)
6941                   ttoc((CHAR)y);
6942             } else
6943 #endif /* TNCODE */
6944               /* Handles terminal emulation responses */
6945               scriptwrtbuf((USHORT)y);
6946 #endif /* TN_NOLO */
6947
6948             /* Real input character to be checked */
6949
6950 #ifdef CK_BURST
6951             burst--;                    /* One less character waiting */
6952             debug(F101,"doinput burst","",burst);
6953 #endif /* CK_BURST */
6954             c = (CHAR) (imask & (CHAR) y); /* Mask off any parity */
6955             inchar[0] = c;              /* Remember character for \v(inchar) */
6956 #ifdef COMMENT
6957 #ifdef CK_BURST
6958             /* Update "lastchar" time only once during input burst */
6959             if (burst <= 0)
6960 #endif /* CK_BURST */
6961 #endif /* COMMENT */
6962               lastchar = gtimer();      /* Remember when it came */
6963
6964             if (c == '\0') {            /* NUL, we can't use it */
6965                 if (anychar) {          /* Except if any character will do? */
6966                     x = 1;              /* Yes, done. */
6967                     instatus = INP_OK;
6968                     incount = 1;        /* This must be the first and only. */
6969                     break;
6970                 } else goto refill;     /* Otherwise continue INPUTting */
6971             }
6972             *inpbp++ = c;               /* Store char in circular buffer */
6973             incount++;                  /* Count it for \v(incount) */
6974
6975             if (flags & INPSW_COU) {    /* INPUT /COUNT */
6976                 if (--count < 1) {
6977                     x = 1;
6978                     instatus = INP_OK;
6979                     incount = savecount;
6980                     break;
6981                 }
6982             }
6983             if (matchbuf) {
6984                 if (matchindex < MATCHBUFSIZ) {
6985                     matchbuf[matchindex++] = c;
6986                     matchbuf[matchindex] = NUL;
6987                 }
6988             }
6989 #ifdef MAC
6990             {
6991                 extern char *ttermw;    /* fake pointer cast */
6992                 if (inecho) {
6993                     outchar(ttermw, c); /* echo to terminal window */
6994                     /* this might be too much overhead to do here ? */
6995                     updatecommand(ttermw);
6996                 }
6997             }
6998 #else /* Not MAC */
6999             if (inecho) {               /* Buffer console output */
7000                 conbuf[concnt++] = c;
7001             }
7002 #endif /* MAC */
7003 #ifndef OS2
7004             if (seslog) {
7005                 int dummy = 0, skip = 0;
7006 #ifndef NOLOCAL
7007                 if (noescseq) {
7008                     dummy = chkaes(c,0);
7009                     if (inesc[0] != ES_NORMAL || oldesc[0] != ES_NORMAL)
7010                       skip = 1;
7011                 }
7012 #endif  /* NOLOCAL */
7013 #ifdef UNIXOROSK
7014                 if (sessft == XYFT_T) {
7015 #ifdef UNIX
7016                     if (c == '\r')
7017 #else
7018 #ifdef OSK
7019                     if (c == '\012')
7020 #endif /* OSK */
7021 #endif /* UNIX */
7022                       skip = 1;
7023                 }
7024 #endif  /* UNIXOROSK */
7025                 if (!skip)
7026                   sesbuf[sescnt++] = c; /* Buffer session log output */
7027             }
7028 #endif /* OS2 */
7029             if (anychar) {              /* Any character will do? */
7030                 x = 1;
7031                 instatus = INP_OK;
7032                 break;
7033             }
7034             if (!inpcas[cmdlvl]) {      /* Ignore alphabetic case? */
7035                 if (isupper(c))         /* Yes, convert input char to lower */
7036                   c = (CHAR) tolower(c);
7037             }
7038             debug(F000,"doinput char","",c);
7039
7040             /* Here is the matching section */
7041
7042             y = -1;                     /* Loop thru search strings */
7043             while (!nomatch && (s = ms[++y])) { /* ...as many as we have. */
7044                 if (mp[y]) {            /* Pattern match? */
7045 #ifdef COMMENT
7046                     int j;
7047                     /* This is gross but it works... */
7048                     /* We could just as easily have prepended '*' to the  */
7049                     /* pattern and skipped the loop, except then we would */
7050                     /* not have any way to identify the matching string.  */
7051                     for (j = 0; j < matchindex; j++) {
7052                         if (ckmatch(s,&matchbuf[j],1,1)) {
7053                             matchindex = j;
7054                             instatus = INP_OK;
7055                             x = 1;
7056                             break;
7057                         }
7058                     }
7059                     if (x > 0)
7060                       break;
7061 #else
7062                     /* July 2001 - ckmatch() returns match position. */
7063                     /* It works and it's not gross. */
7064                     /* (4 = floating pattern) */
7065                     x = ckmatch(s,matchbuf,inpcas[cmdlvl],1+4);
7066                     if (x > 0) {
7067                         matchindex = x - 1;
7068                         instatus = INP_OK;
7069                         x = 1;
7070                         break;
7071                     }
7072 #endif /* COMMENT */
7073                     continue;
7074                 }                       /* Literal match. */
7075                 i = mi[y];              /* Match-position in search string. */
7076                 debug(F000,"compare char","",(CHAR)s[i]);
7077                 if (c == (CHAR) s[i]) { /* Check for match */
7078                     i++;                /* Got one, go to next character */
7079                 } else {                /* Don't have a match */
7080                     int j;
7081                     for (j = i; i > 0; ) { /* Back up in search string */
7082                         i--; /* (Do this here to prevent compiler foulup) */
7083                         /* j is the length of the substring that matched */
7084                         if (c == (CHAR) s[i]) {
7085                             if (!strncmp(s,&s[j-i],i)) {
7086                                 i++;          /* c actually matches -- cfk */
7087                                 break;
7088                             }
7089                         }
7090                     }
7091                 }
7092                 if ((CHAR) s[i] == (CHAR) '\0') { /* Matched to end? */
7093                     ckstrncpy(matchbuf,ms[y],MATCHBUFSIZ);
7094                     matchindex = 0;
7095                     instatus = INP_OK;  /* Yes, */
7096                     x = 1;            
7097                     break;              /* done. */
7098                 }
7099                 mi[y] = i;              /* No, remember match-position */
7100             }
7101             if (x == 1) {               /* Set \v(minput) result */
7102                 instatus = INP_OK;
7103                 m_found = y + 1;
7104                 break;
7105             }
7106             if (inpbp >= inpbuf + inbufsize) { /* Reached end of buffer? */
7107                 if (nowrap) {           /* If /NOWRAP...*/
7108                     instatus = INP_BF;  /* ...return indicating buffer full. */
7109                     *inpbp = NUL;
7110                     goto xinput;
7111                 }
7112                 *inpbp = NUL;           /* Make it null-terminated */
7113                 inpbp = inpbuf;         /* Yes. */
7114             }
7115         }
7116 #ifdef CK_BURST
7117         else if (y <= -1 && burst > 0) {
7118             debug(F111,"doinput (y<=-1&&burst>0)","burst",burst);
7119                                         /* A timeout occurred so there can't */
7120             burst = 0;                  /* be data waiting; must check timo */
7121         }
7122       refill:
7123         if (burst <= 0) {               /* No buffered chars remaining... */
7124             myflsh();                   /* Flush buffered output */
7125             if (local) {                /* Get size of next input burst */
7126                 burst = ttchk();
7127                 if (burst < 0) {        /* ttchk() says connection is closed */
7128                     instatus = INP_IO;  /* Status = i/o error */
7129 #ifndef NOLOCAL
7130 #ifdef OS2
7131                     term_io = term_io_save;
7132 #endif /* OS2 */
7133 #endif /* NOLOCAL */
7134
7135                     if ((!network 
7136 #ifdef TN_COMPORT
7137                          || istncomport()
7138 #endif /* TN_COMPORT */
7139                          ) && carrier != CAR_OFF) {
7140         /* The test is written this way because the Microsoft compiler
7141          * is producing bad code if written:
7142          *
7143          *  if (network && (!istncomport() || carrier == CAR_OFF) )
7144          */
7145                         break;
7146                      } else {
7147                          printf("Fatal error - disconnected.\n");
7148                          ttclos(1);
7149                          break;
7150                      }
7151                 }
7152                 if (inintr) {
7153                     if ((icn = conchk()) > 0) { /* Interrupt from keyboard? */
7154                         kbchar = coninc(0);
7155                         debug(F101,"input interrupted from keyboard","",icn);
7156                         while (--icn > 0) coninc(0); /* Yes, absorb chars. */
7157                         break;          /* And fail. */
7158                     }
7159                 }
7160             } else {
7161                 burst = conchk();
7162             }
7163             debug(F101,"doinput burst","",burst);
7164             /* Prevent overflow of "conbuf" and "sesbuf" */
7165             if (burst > MAXBURST)
7166               burst = MAXBURST;
7167
7168             /* Did not match, timer exceeded? */
7169             t = gtimer();
7170             debug(F111,"doinput gtimer","burst",t);
7171             debug(F101,"doinput timo","",timo);
7172             if ((t >= timo) && (timo > 0))
7173               break;
7174             else if (insilence > 0 && (t - lastchar) > insilence)
7175               break;
7176         } else {
7177             debug(F111,"doinput (burst > 0)","burst",burst);
7178         }
7179 #else  /* CK_BURST */
7180       refill:
7181         myflsh();                       /* Flush buffered output */
7182         /* Did not match, timer exceeded? */
7183         t = gtimer();
7184         debug(F111,"doinput gtimer","no burst",t);
7185         debug(F101,"doinput timo","",timo);
7186         if ((t >= timo) && (timo > -1))
7187           break;
7188         else if (insilence > 0 && (t - lastchar) > insilence)
7189           break;
7190 #endif /* CK_BURST */
7191     }                                   /* Still have time left, continue. */
7192   xinput:
7193     myflsh();                           /* Flush buffered output */
7194     if (instatus == INP_BF) {           /* Buffer full and /NOWAIT */
7195         x = 0;                          /* Must not succeed */
7196     } else {                            /* Buffer full and /NOWAIT */
7197         if (nomatch) x = 1;             /* Succeed if nomatch and timed out */
7198         if (x > 0 && !nomatch)
7199           instatus = 0;
7200     }
7201 #ifndef NOLOCAL
7202 #ifdef OS2
7203     term_io = term_io_save;
7204 #endif /* OS2 */
7205 #endif /* NOLOCAL */
7206 #ifdef COMMENT
7207 #ifdef IKS_OPTION
7208 #ifdef CK_AUTODL
7209     if (is_tn && TELOPT_ME(TELOPT_KERMIT) && inautodl) {
7210         tn_siks(KERMIT_STOP);           /* Send Kermit-Server Stop */
7211     }
7212 #endif /* CK_AUTODL */
7213 #endif /* IKS_OPTION */
7214 #endif /* COMMENT */
7215
7216 #ifdef GFTIMER
7217     fpt = gftimer();                    /* Get elapsed time */
7218
7219 /* If a long is 32 bits, it would take about 50 days for this to overflow. */
7220
7221     inetime = (int)(fpt * (CKFLOAT)1000.0);
7222 #else
7223     inetime = (int)(gtimer() * 1000);
7224 #endif /* GFTIMER */
7225
7226     if (x > 0)
7227       makestr(&inpmatch,&matchbuf[matchindex]); /* \v(inmatch) */
7228     return(x);                          /* Return the return code. */
7229 }
7230 #endif /* NOSPL */
7231
7232 #ifndef NOSPL
7233 /* REINPUT Command */
7234
7235 /*
7236   Note, the timeout parameter is required, but ignored.  Syntax is compatible
7237   with MS-DOS Kermit except timeout can't be omitted.  This function only
7238   looks at the characters already received and does not read any new
7239   characters from the connection.
7240 */
7241 int
7242 doreinp(timo,s,pat) int timo; char *s; int pat; {
7243     int x, y, i;
7244     char *xx, *xp, *xq = (char *)0;
7245     CHAR c;
7246
7247     if (!s) s = "";
7248     debug(F101,"doreinput pat","",pat);
7249
7250     y = (int)strlen(s);
7251     debug(F111,"doreinput search",s,y);
7252
7253     if (y > inbufsize) {                /* If search string longer than */
7254         debug(F101,"doreinput inbufsize","",inbufsize);
7255         return(0);                      /* input buffer, fail. */
7256     }
7257     makestr(&inpmatch,NULL);
7258     if (!matchbuf)
7259       matchbuf = malloc(MATCHBUFSIZ+1);
7260     matchindex = 0;
7261
7262     x = 0;                              /* Return code, assume failure */
7263     i = 0;                              /* String pattern match position */
7264
7265     if (!inpcas[cmdlvl]) {              /* INPUT CASE = IGNORE?  */
7266         xp = malloc(y+2);               /* Make a separate copy of the */
7267         if (!xp) {                      /* search string. */
7268             printf("?malloc error 6\n");
7269             return(x);
7270         } else xq = xp;                 /* Keep pointer to beginning. */
7271         while (*s) {                    /* Yes, convert to lowercase */
7272             *xp = *s;
7273             if (isupper(*xp)) *xp = (char) tolower(*xp);
7274             xp++; s++;
7275         }
7276         *xp = NUL;                      /* Terminate it! */
7277         s = xq;                         /* Move search pointer to it. */
7278     }
7279     xx = *inpbp ? inpbp : inpbuf;       /* Current INPUT buffer pointer */
7280     do {
7281         c = *xx++;                      /* Get next character */
7282         if (!c) break;
7283         if (xx >= inpbuf + inbufsize)   /* Wrap around if necessary */
7284           xx = inpbuf;
7285         if (!inpcas[cmdlvl]) {          /* Ignore alphabetic case? */
7286             if (isupper(c)) c = (CHAR) tolower(c); /* Yes */
7287         }
7288         if (pat) {
7289             int j;
7290             if (matchbuf) {
7291                 if (matchindex < MATCHBUFSIZ) {
7292                     matchbuf[matchindex++] = c;
7293                     matchbuf[matchindex] = NUL;
7294                 }
7295                 for (j = 0; j < matchindex; j++) { /* Gross but effective */
7296                     if (ckmatch(s,&matchbuf[j],1,1)) {
7297                         debug(F101,"GOT IT","",j);
7298                         matchindex = j;
7299                         x = 1;
7300                         break;
7301                     }
7302                 }
7303             }
7304             if (x > 0)
7305               break;
7306             continue;
7307         }
7308         debug(F000,"doreinp char","",c);
7309         debug(F000,"compare char","",(CHAR) s[i]);
7310         if (((char) c) == ((char) s[i])) { /* Check for match */
7311             i++;                        /* Got one, go to next character */
7312         } else {                        /* Don't have a match */
7313             int j;
7314             for (j = i; i > 0; ) {      /* [jrs] search backwards for it  */
7315                 i--;
7316                 if (((char) c) == ((char) s[i])) {
7317                     if (!strncmp(s,&s[j-i],i)) {
7318                         i++;
7319                         break;
7320                     }
7321                 }
7322             }
7323         }                               /* [jrs] or return to zero from -1 */
7324         if (s[i] == '\0') {             /* Matched all the way to end? */
7325             ckstrncpy(matchbuf,s,MATCHBUFSIZ);
7326             matchindex = 0;
7327             x = 1;                      /* Yes, */
7328             break;                      /* done. */
7329         }
7330     } while (xx != inpbp && x < 1);     /* Until back where we started. */
7331
7332     if (!inpcas[cmdlvl]) if (xq) free(xq); /* Free this if it was malloc'd. */
7333     makestr(&inpmatch,&matchbuf[matchindex]); /* \v(inmatch) */
7334     return(x);                          /* Return search result. */
7335 }
7336
7337 /*  X X S T R I N G  --  Interpret strings containing backslash escapes  */
7338 /*  Z Z S T R I N G  --  (new name...)  */
7339 /*
7340  Copies result to new string.
7341   strips enclosing braces or doublequotes.
7342   interprets backslash escapes.
7343   returns 0 on success, nonzero on failure.
7344   tries to be compatible with MS-DOS Kermit.
7345
7346  Syntax of input string:
7347   string = chars | "chars" | {chars}
7348   chars = (c*e*)*
7349   where c = any printable character, ascii 32-126
7350   and e = a backslash escape
7351   and * means 0 or more repetitions of preceding quantity
7352   backslash escape = \operand
7353   operand = {number} | number | fname(operand) | v(name) | $(name) | m(name)
7354   number = [r]n[n[n]]], i.e. an optional radix code followed by 1-3 digits
7355   radix code is oO (octal), xX (hex), dD or none (decimal) (see xxesc()).
7356 */
7357
7358 #ifndef NOFRILLS
7359 int
7360 yystring(s,s2) char *s; char **s2; {    /* Reverse a string */
7361     int x;
7362     static char *new;
7363     new = *s2;
7364     if (!s || !new) return(-1);         /* Watch out for null pointers. */
7365     if ((x = (int)strlen(s)) == 0) {    /* Recursion done. */
7366         *new = '\0';
7367         return(0);
7368     }
7369     x--;                                /* Otherwise, call self */
7370     *new++ = s[x];                      /* to reverse rest of string. */
7371     s[x] = 0;
7372     return(yystring(s,&new));
7373 }
7374 #endif /* NOFRILLS */
7375
7376 static char ipabuf[16] = { NUL };       /* IP address buffer */
7377
7378 static char *
7379 getip(s) char *s; {
7380     char c=NUL;                         /* Workers... */
7381     int i=0, p=0, d=0;
7382     int state = 0;                      /* State of 2-state FSA */
7383
7384     while ((c = *s++)) {
7385         switch(state) {
7386           case 0:                       /* Find first digit */
7387             i = 0;                      /* Output buffer index */
7388             ipabuf[i] = NUL;            /* Initialize output buffer */
7389             p = 0;                      /* Period counter */
7390             d = 0;                      /* Digit counter */
7391             if (isdigit(c)) {           /* Have first digit */
7392                 d = 1;                  /* Count it */
7393                 ipabuf[i++] = c;        /* Copy it */
7394                 state = 1;              /* Change state */
7395             }
7396             break;
7397
7398           case 1:                       /* In numeric field */
7399             if (isdigit(c)) {           /* Have digit */
7400                 if (++d > 3)            /* Too many */
7401                   state = 0;            /* Start over */
7402                 else                    /* Not too many */
7403                   ipabuf[i++] = c;      /* Keep it */
7404             } else if (c == '.' && p < 3) { /* Have a period */
7405                 p++;                    /* Count it */
7406                 if (d == 0)             /* Not preceded by a digit */
7407                   state = 0;            /* Start over */
7408                 else                    /* OK */
7409                   ipabuf[i++] = c;      /* Keep it */
7410                 d = 0;                  /* Reset digit counter */
7411             } else if (p == 3 && d > 0) { /* Not part of address */
7412                 ipabuf[i] = NUL;        /* If we have full IP address */
7413                 return((char *)ipabuf); /* Return it */
7414             } else {                    /* Otherwise */
7415                 state = 0;              /* Start over */
7416                 ipabuf[0] = NUL;        /* (in case no more chars left) */
7417             }
7418         }
7419     }                                   /* Fall thru at end of string */
7420     ipabuf[i] = NUL;                    /* Maybe we have one */
7421     return((p == 3 && d > 0) ? (char *)ipabuf : "");
7422 }
7423 #endif /* NOSPL */
7424
7425 /* Date Routines */
7426
7427 /* Z J D A T E  --  Convert yyyymmdd date to Day of Year */
7428
7429 static int jdays[12] = {  0,31,59,90,120,151,181,212,243,273,304,334 };
7430 static int ldays[12] = {  0,31,60,91,121,152,182,213,244,274,305,335 };
7431 static char zjdbuf[12] = { NUL, NUL };
7432 /*
7433   Deinde, ne in posterum a XII kalendas aprilis aequinoctium recedat,
7434   statuimus bissextum quarto quoque anno (uti mos est) continuari debere,
7435   praeterquam in centesimis annis; qui, quamvis bissextiles antea semper
7436   fuerint, qualem etiam esse volumus annum MDC, post eum tamen qui deinceps
7437   consequentur centesimi non omnes bissextiles sint, sed in quadringentis
7438   quibusque annis primi quique tres centesimi sine bissexto transigantur,
7439   quartus vero quisque centesimus bissextilis sit, ita ut annus MDCC, MDCCC,
7440   MDCCCC bissextiles non sint. Anno vero MM, more consueto dies bissextus
7441   intercaletur, februario dies XXIX continente, idemque ordo intermittendi
7442   intercalandique bissextum diem in quadringentis quibusque annis perpetuo
7443   conservetur.  - Gregorius XIII, Anno Domini MDLXXXII.
7444 */
7445 char *
7446 zjdate(date) char * date; {             /* date = yyyymmdd */
7447     char year[5];
7448     char month[3];
7449     char day[3];
7450     int d, m, x, y;
7451     int leapday, j;
7452     char * time = NULL;
7453
7454     if (!date) date = "";               /* Validate arg */
7455     x = strlen(date);
7456     if (x < 1) return("0");
7457     if (x < 8) return("-1");
7458     for (x = 0; x < 8; x++)
7459       if (!isdigit(date[x]))
7460         return("-1");
7461
7462     if (date[8]) if (date[9])
7463       time = date + 9;
7464
7465     year[0] = date[0];                  /* Isolate year */
7466     year[1] = date[1];
7467     year[2] = date[2];
7468     year[3] = date[3];
7469     year[4] = '\0';
7470
7471     month[0] = date[4];                 /* Month */
7472     month[1] = date[5];
7473     month[2] = '\0';;
7474
7475     day[0] = date[6];                   /* And day */
7476     day[1] = date[7];
7477     day[2] = '\0';
7478
7479     leapday = 0;                        /* Assume no leap day */
7480     y = atoi(year);
7481     m = atoi(month);
7482     d = atoi(day);
7483     if (m > 2) {                        /* No Leap day before March */
7484         if (y % 4 == 0) {               /* If year is divisible by 4 */
7485             leapday = 1;                /* It's a Leap year */
7486             if (y % 100 == 0) {         /* Except if divisible by 100 */
7487                 if (y % 400 != 0)       /* but not by 400 */
7488                   leapday = 0;
7489             }
7490         }
7491     }
7492     j = jdays[m - 1] + d + leapday;     /* Day of year */
7493     if (time)
7494       sprintf(zjdbuf,"%04d%03d %s",y,j,time); /* SAFE */
7495     else
7496       sprintf(zjdbuf,"%04d%03d",y,j);   /* SAFE */
7497     return((char *)zjdbuf);
7498 }
7499
7500 static char jzdbuf[32];
7501
7502 /* J Z D A T E  --  Convert Day of Year to yyyyddmm date */
7503
7504 char *
7505 jzdate(date) char * date; {             /* date = yyyyddd */
7506     char year[5];                       /* with optional time */
7507     char day[4];
7508     char * time = NULL, * p;
7509     int d, m, x, y;
7510     int leapday, j;
7511     int * zz;
7512
7513     if (!date) date = "";               /* Validate arg */
7514     x = strlen(date);
7515
7516     debug(F111,"jzdate len",date,x);
7517
7518     if (x < 1) return("0");
7519     if (x < 7) return("-1");
7520     if (x > 8) time = date + 8;
7521
7522     for (x = 0; x < 7; x++)
7523       if (!isdigit(date[x]))
7524         return("-1");
7525
7526     year[0] = date[0];                  /* Isolate year */
7527     year[1] = date[1];
7528     year[2] = date[2];
7529     year[3] = date[3];
7530     year[4] = '\0';
7531
7532     debug(F110,"jzdate year",year,0);
7533
7534     day[0] = date[4];                   /* And day */
7535     day[1] = date[5];
7536     day[2] = date[6];
7537     day[3] = '\0';
7538
7539     debug(F110,"jzdate day",day,0);
7540
7541     j = atoi(day);
7542     if (j > 366)
7543       return("-1");
7544
7545     leapday = 0;                        /* Assume no leap day */
7546     y = atoi(year);
7547     if (y % 4 == 0) {                   /* If year is divisible by 4 */
7548         leapday = 1;                    /* It's a Leap year */
7549         if (y % 100 == 0) {             /* Except if divisible by 100 */
7550             if (y % 400 != 0)           /* but not by 400 */
7551               leapday = 0;
7552         }
7553     }
7554     debug(F101,"jzdate leapday","",leapday);
7555     zz = leapday ? ldays : jdays;
7556
7557     for (x = 0; x < 11; x++)
7558       if (j > zz[x] && j <= zz[x+1])
7559         break;
7560     m = x + 1;
7561
7562     debug(F101,"jzdate m","",m);
7563
7564     d = j - zz[x];
7565
7566     debug(F101,"jzdate d","",d);
7567
7568     if (time)
7569       sprintf(jzdbuf,"%04d%02d%02d %s",y,m,d,time); /* SAFE */
7570     else
7571       sprintf(jzdbuf,"%04d%02d%02d",y,m,d); /* SAFE */
7572
7573     debug(F101,"jzdate jzdbuf",jzdbuf,0);
7574
7575     p = ckcvtdate((char *)jzdbuf, 0);   /* Convert to standard form */
7576     ckstrncpy(jzdbuf,p,32);
7577     if (!time) jzdbuf[8] = NUL;         /* Remove time if not wanted */
7578     return((char *)jzdbuf);
7579 }
7580
7581 /* M J D  --  Modified Julian Date */
7582 /*
7583   Call with:
7584     Standard-format date-time string: yyyymmdd[ hh:mm:ss].
7585     The time, if any, is ignored.
7586
7587   Returns:
7588     -1L on error, otherwise:
7589     The number of days since 17 Nov 1858 as a whole number:
7590     16 Nov 1858 = -1, 17 Nov 1858 = 0, 18 Nov 1858 = 1, 19 Nov 1858 = 2, ...
7591
7592   The Modified Julian Date is defined by the International Astronomical
7593   Union as the true Julian date minus 2400000.5 days.  The true Julian
7594   date is the number days since since noon of 1 January 4713 BCE of the
7595   Julian proleptic calendar.  Conversions between calendar dates and
7596   Julian dates, however, assume Gregorian dating.
7597 */
7598 long
7599 mjd(date) char * date; {
7600     char year[5];
7601     char month[3];
7602     char day[3];
7603     int x, a, d, m, y;
7604     long z;
7605
7606     if (!date) date = "";               /* Validate arg */
7607     x = strlen(date);
7608     if (x < 1) return(0L);
7609     if (x < 8) return(-1L);
7610     for (x = 0; x < 8; x++)
7611       if (!isdigit(date[x]))
7612         return(-1L);
7613
7614     year[0] = date[0];                  /* Isolate year */
7615     year[1] = date[1];
7616     year[2] = date[2];
7617     year[3] = date[3];
7618     year[4] = '\0';
7619
7620     month[0] = date[4];                 /* Month */
7621     month[1] = date[5];
7622     month[2] = '\0';;
7623     m = atoi(month);
7624
7625     day[0] = date[6];                   /* And day */
7626     day[1] = date[7];
7627     day[2] = '\0';
7628     d = atoi(day);
7629
7630     a = (14-m)/12;                      /* Calculate true Julian date */
7631     y = atoi(year) + 4800 - a;
7632     m = m + 12 * a - 3;
7633     z = d + (long)(306*m+5)/10 + (long)(y*365) + y/4 - y/100 + y/400 - 32045L;
7634
7635     z -= 2400001L;                      /* Convert JD to MJD */
7636
7637     return(z);
7638 }
7639
7640 static char mjd2dbuf[32];
7641
7642 /*  M J D 2 D A T E  --  Converts MJD to yyyymmdd  */
7643
7644 char *
7645 #ifdef CK_ANSIC
7646 mjd2date(long mjd)
7647 #else
7648 mjd2date(mjd) long mjd;
7649 #endif /* CK_ANSIC */
7650 /* mjd2date */ {
7651     long jd, l, n;
7652     int d, m, y;
7653     jd = (long)(mjd + 2400001L);
7654     l = jd + 68569;
7655     n = 4 * l / 146097L;
7656     l = l - (146097 * n + 3) / 4;
7657     y = 4000 * (l + 1) / 1461001L;
7658     l = l - 1461 * y / 4 + 31;
7659     m = 80 * l / 2447;
7660     d = l - 2447 * m / 80;
7661     l = m / 11;
7662     m = m + 2 - 12 * l;
7663     y = 100 * (n - 49) + y + l;
7664     sprintf(mjd2dbuf,"%04d%02d%02d",y,m,d); /* SAFE */
7665     return((char *)mjd2dbuf);
7666 }
7667
7668 #ifndef NOSPL
7669 static char ** flist = (char **) NULL;  /* File list for \fnextfile() */
7670 static int flistn = 0;                  /* Number of items in file list */
7671
7672 /*
7673   The function return-value buffer must be global, since fneval() returns a
7674   pointer to it.  fneval() is called only by zzstring(), which always copies
7675   the result out of this buffer to somewhere else, so it's OK to have only
7676   one buffer for this in most cases.  However, since function calls can be
7677   nested -- e.g. functions whose arguments are functions, or recursive
7678   functions, at some point we should convert this to an array of buffers,
7679   indexed by function depth (which might or might not be the same as the
7680   "depth" variable).  Also, since function results are potentially quite big,
7681   we'd need to allocate and deallocate dynamically as we descend and ascend
7682   function depth.  Left for a future release...
7683 */
7684 char fnval[FNVALL+2];                   /* Function return value  */
7685 static int fndepth = 0;                 /* (we don't actually use this yet) */
7686 int fnsuccess = 1;
7687 extern int fnerror;
7688
7689 /* f p f o r m a t  --  Floating-point number nicely formatted.  */
7690 /*
7691    Returns results from a circular 1K buffer.
7692    Don't count on too many results remaining available at once; it could
7693    be anywhere from 5 to maybe 100, depending on the sizes of the results.
7694 */
7695 #ifdef CKFLOAT
7696 #define FPFMTSIZ 1024
7697 static char fpfmtbuf[FPFMTSIZ] = { NUL, NUL };
7698 static int fpfbufpos = 0;               /* (why was this char before?) */
7699
7700 char *
7701 fpformat(fpresult,places,round) CKFLOAT fpresult; int places, round; {
7702     char fbuf[16];                      /* For creating printf format */
7703     int nines = 0, sign = 0, x, y, i, j, size = 0;
7704     char * buf;
7705     CKFLOAT ftmp;
7706
7707     x = places ? places : (fp_digits ? fp_digits : 6);
7708
7709     debug(F101,"fpformat fpresult","",fpresult);
7710     debug(F101,"fpformat places","",places);
7711     debug(F101,"fpformat fpfbufpos 1","",fpfbufpos);
7712
7713     ftmp = fpresult;
7714     if (ftmp < 0.0) ftmp = 0.0 - fpresult;
7715
7716 #ifdef FNFLOAT
7717     if (!fp_rounding &&                 /* If printf doesn't round, */
7718         (places > 0 ||                  /* round result to decimal places. */
7719          (places == 0 && round)))
7720       fpresult += (0.5 / pow(10.0,(CKFLOAT)places));
7721     y = (ftmp == 0.0) ? 1 : (int)log10(ftmp);
7722     size = y + x + 3;                   /* Estimated length of result */
7723     if (fpresult < 0.0) size++;
7724 #else
7725     size = 200;                         /* No way to estimate, be generous */
7726 #endif /* FNFLOAT */
7727
7728     debug(F101,"fpformat size","",size);
7729
7730     if (fpfbufpos > (FPFMTSIZ - size))  /* Wrap around if necessary */
7731       fpfbufpos = 0;
7732     debug(F101,"fpformat fpfbufpos 1","",fpfbufpos);
7733
7734     buf = &fpfmtbuf[fpfbufpos];
7735
7736     if (places > 0) {                   /* If places specified */
7737         /* use specified places to write given number of digits */
7738         sprintf(fbuf,"%%0.%df",places); /* SAFE */
7739         sprintf(buf,fbuf,fpresult);     /* SAFE */
7740     } else {                            /* Otherwise... */
7741         /* Go for max precision */
7742         sprintf(fbuf,"%%0.%df",fp_digits); /* SAFE */
7743         sprintf(buf,fbuf,fpresult);     /* SAFE */
7744     }
7745     if (buf[0] == '-') sign = 1;
7746     debug(F111,"fpresult 1 errno",buf,errno); /* Check for over/underflow */
7747     debug(F111,"fpresult 1 fpfbufpos",buf,fpfbufpos);
7748     /* Give requested decimal places */
7749     for (i = sign; i < FPFMTSIZ && buf[i]; i++) {
7750         if (buf[i] == '.')              /* First find the decimal point */
7751           break;
7752         else if (i > fp_digits + sign - 1) /* replacing garbage */
7753           buf[i] = '0';                 /* digits with 0... */
7754     }
7755     if (buf[i] == '.') {                /* Have decimal point */
7756         int gotend = 0;
7757         /* places < 0 so truncate fraction */
7758         if (places < 0 || (places == 0 && round)) {
7759             buf[i] = NUL;
7760         } else if (places > 0) {        /* d > 0 so this many decimal places */
7761             i++;                           /* First digit after decimal */
7762             for (j = 0; j < places; j++) { /* Truncate after d decimal */
7763                 if (!buf[j+i])        /* places or extend to d  */
7764                   gotend = 1;              /* decimal places. */
7765                 if (gotend || j+i+sign > fp_digits)
7766                   buf[j+i] = '0';
7767             }
7768             buf[j+i] = NUL;
7769         } else {                        /* places == 0 so Do The Right Thing */
7770             for (j = (int)strlen(buf) - 1; j > i+1; j--) {
7771                 if ((j - sign) > fp_digits)
7772                   buf[j] = '0';
7773                 if (buf[j] == '0')
7774                   buf[j] = NUL; /* Strip useless trailing 0's. */
7775                 else
7776                   break;
7777             }
7778         }
7779     }
7780     fpfmtbuf[FPFMTSIZ-1] = NUL;
7781     j = strlen(buf);
7782     sign = 0;
7783     for (i = j-1; i >= 0; i--) {
7784         if (buf[i] == '9')
7785           nines++;
7786         else
7787           break;
7788     }
7789     /* Do something about xx.xx99999999... */
7790     if (nines > 5) {
7791         if (isdigit(buf[i]) && i < FPFMTSIZ - 2) {
7792             buf[i] = buf[i] + 1;
7793             buf[i+1] = '0';
7794             buf[i+2] = '\0';
7795         }
7796     }
7797     if (!strncmp(buf,"-0.0",FPFMTSIZ))
7798       ckstrncpy(buf,"0.0",FPFMTSIZ);
7799     fpfbufpos += (int)strlen(buf) + 1;
7800     return((char *)buf);
7801 }
7802 #endif /* CKFLOAT */
7803
7804 static VOID
7805 evalerr(fn) char * fn; {
7806     if (fndiags) {
7807         if (divbyzero)
7808           ckmakmsg(fnval,FNVALL,"<ERROR:DIVIDE_BY_ZERO:\\f",fn,"()>",NULL);
7809         else
7810           ckmakmsg(fnval,FNVALL,"<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
7811     }
7812 }
7813
7814
7815 static int
7816 ckcindex(c,s) char c, *s; {
7817     int rc;
7818     if (!c || !s) return(0);
7819     for (rc = 0; s[rc]; rc++) {
7820         if (c == s[rc]) return(rc+1);
7821     }
7822     return(0);
7823 }
7824
7825 static char *
7826 dokwval(s,sep) char * s, * sep; {
7827     char c = '\0', * p, * kw = NULL, * vp = NULL;
7828     char * rc = "0";                    /* Return code */
7829     int x = 0;
7830     if (!s) return(rc);
7831     if (!*s) return(rc);
7832     debug(F110,"kwval arg",s,0);
7833     debug(F110,"kwval sep",sep,0);
7834     p = (char *)malloc((int)strlen(s)+1);
7835     if (!p) goto xdokwval;
7836     strcpy(p,s);                        /* SAFE */
7837     s = p;
7838     while (*s < '!' && *s > '\0')       /* Get first nonblank */
7839       s++;
7840     if (!*s) goto xdokwval;
7841     if (ckcindex(*s,sep))               /* Separator but no keyword */
7842       goto xdokwval;
7843     kw = s;                             /* Keyword */
7844     while (*s > ' ') {
7845         if (ckcindex(*s,sep)) {         /* keyword=... */
7846             c = *s;
7847             break;
7848         }
7849         s++;
7850     }
7851     if (*kw) rc = "1";                  /* Have keyword, promote return code */
7852     *s++ = NUL;                         /* Terminate keyword */
7853     while (*s < '!' && *s > '\0')       /* Skip blanks */
7854       s++;
7855     if (!c && ckcindex(*s,sep)) {
7856         c = *s++;                       /* Have separator */
7857         while (*s < '!' && *s > '\0')   /* Skip blanks */
7858           s++;
7859     }
7860     if (c) {
7861         vp = s;
7862         if (*vp) rc = "2";              /* Have value, another promotion */
7863 #ifdef COMMENT
7864         while (*s > ' ')                /* Skip to end */
7865           s++;
7866         *s = NUL;                       /* Terminate value */
7867 #endif  /* COMMENT */
7868     }
7869     debug(F110,"kwval c",ckctoa(c),0);
7870     debug(F110,"kwval keyword",kw,0);
7871     debug(F110,"kwval value",vp,0);
7872     makestr(&lastkwval,kw);
7873     vp = brstrip(vp);
7874     debug(F110,"kwval value",vp,0);
7875     x = addmac(kw,vp);
7876     debug(F111,"kwval addmac",kw,x);
7877   xdokwval: 
7878     if (p) free(p);
7879     return((x < 0) ? "-1" : rc);
7880 }
7881
7882 static int
7883 isaarray(s) char * s; {                 /* Is s an associative array element */
7884     int state = 0;
7885     CHAR c;
7886     if (!s) return(0);
7887     while ((c = *s++)) {
7888         if (!isprint(c)) {
7889             return(0);
7890         } else if (c == '<') {
7891             if (state != 0)
7892               return(0);
7893             state = 1;
7894         } else if (c == '>') {
7895             return((state != 1 || *s) ? 0 : 1);
7896         }
7897     }
7898     return(0);
7899 }
7900
7901 static char *                           /* Evaluate builtin functions */
7902 fneval(fn,argp,argn,xp) char *fn, *argp[]; int argn; char * xp; {
7903     int i=0, j=0, k=0, len1=0, len2=0, len3=0, n=0, t=0, x=0, y=0;
7904     int cx, failed = 0;                 /* Return code, 0 = ok */
7905     long z = 0L;
7906     char *bp[FNARGS + 1];               /* Pointers to malloc'd strings */
7907     char c = NUL;
7908     char *p = NULL, *s = NULL;
7909     char *val1 = NULL, *val2 = NULL;    /* Pointers to numeric string values */
7910
7911 #ifdef RECURSIVE
7912     int rsave = recursive;
7913 #endif /* RECURSIVE */
7914 #ifdef OS2
7915     int zsave = zxpn;
7916 #endif /* OS2 */
7917
7918     if (!fn) fn = "";                   /* Protect against null pointers */
7919     if (!*fn) return("");
7920
7921     for (i = 0; i < FNARGS; i++)        /* Initialize argument pointers */
7922       bp[i] = NULL;
7923 /*
7924   IMPORTANT: Note that argn is not an accurate count of the number of
7925   arguments.  We can't really tell if an argument is null until after we
7926   execute the code below.  So argn is really the maximum number of arguments
7927   we might have.  Argn should always be at least 1, even if the function is
7928   called with empty parentheses (but don't count on it).
7929 */
7930     debug(F111,"fneval",fn,argn);
7931     debug(F110,"fneval",argp[0],0);
7932     if (argn > FNARGS)                  /* Discard excess arguments */
7933       argn = FNARGS;
7934
7935     fndepth++;
7936     debug(F101,"fneval fndepth","",fndepth);
7937     p = fnval;
7938     fnval[0] = NUL;
7939     y = lookup(fnctab,fn,nfuncs,&x);    /* Look up the function name */
7940     cx = y;                             /* Because y is too generic... */
7941     if (cx < 0) {                        /* Not found */
7942         failed = 1;
7943         if (fndiags) {                  /* FUNCTION DIAGNOSTIC ON */
7944             int x;
7945             x = strlen(fn);
7946             /* The following sprintf's are safe */
7947             switch (cx) {
7948               case -1:
7949                 if (x + 32 < FNVALL)
7950                   sprintf(fnval,"<ERROR:NO_SUCH_FUNCTION:\\f%s()>",fn);
7951                 else
7952                   sprintf(fnval,"<ERROR:NO_SUCH_FUNCTION>");
7953                 break;
7954               case -2:
7955                 if (x + 26 < FNVALL)
7956                   sprintf(fnval,"<ERROR:NAME_AMBIGUOUS:\\f%s()>",fn);
7957                 else
7958                   sprintf(fnval,"<ERROR:NAME_AMBIGUOUS>");
7959                 break;
7960               case -3:
7961                 sprintf(fnval,"<ERROR:FUNCTION_NAME_MISSING:\\f()>");
7962                 break;
7963               default:
7964                 if (x + 26 < FNVALL)
7965                   sprintf(fnval,"<ERROR:LOOKUP_FAILURE:\\f%s()>",fn);
7966                 else
7967                   sprintf(fnval,"<ERROR:LOOKUP_FAILURE>");
7968                 break;
7969             }
7970         }
7971         goto fnend;                     /* Always leave via common exit */
7972     }
7973     fn = fnctab[x].kwd;                 /* Full name of function */
7974
7975     if (argn < 0) {
7976         failed = 1;
7977         p = fnval;
7978         if (fndiags)
7979           sprintf(fnval,"<ERROR:MISSING_ARG:\\f%s()>",fn);
7980         goto fnend;
7981     }
7982     if (cx == FN_LIT) {                 /* literal(arg1) */
7983         debug(F010,"flit",xp,0);
7984         p = xp ? xp : "";               /* Return a pointer to arg itself */
7985         goto fnend;
7986     }
7987
7988 #ifdef DEBUG
7989     if (deblog) {
7990         int j;
7991         for (j = 0; j < argn; j++)
7992           debug(F111,"fneval arg",argp[j],j);
7993     }
7994 #endif /* DEBUG */
7995     for (j = argn-1; j >= 0; j--) {     /* Uncount empty trailing args */
7996         if (!argp[j])
7997           argn--;
7998         else if (!*(argp[j]))
7999           argn--;
8000         else break;
8001     }
8002     debug(F111,"fneval argn",fn,argn);
8003 /*
8004   \fliteral() and \fcontents() are special functions that do not evaluate
8005   their arguments, and are treated specially here.  After these come the
8006   functions whose arguments are evaluated in the normal way.
8007 */
8008 #ifdef COMMENT
8009     /* (moved up) */
8010     if (cx == FN_LIT) {                 /* literal(arg1) */
8011         debug(F110,"flit",xp,0);
8012         p = xp ? xp : "";               /* Return a pointer to arg itself */
8013         goto fnend;
8014     }
8015 #endif /* COMMENT */
8016     if (cx == FN_CON) {                 /* Contents of variable, unexpanded. */
8017         char c;
8018         int subscript = 0;        
8019         if (!(p = argp[0]) || !*p) {
8020             failed = 1;
8021             p = fnval;
8022             if (fndiags)
8023               sprintf(fnval,"<ERROR:MISSING_ARG:\\fcontents()>");
8024             goto fnend;
8025         }
8026         p = brstrip(p);
8027         if (*p == CMDQ) p++;
8028         if ((c = *p) == '%') {          /* Scalar variable. */
8029             c = *++p;                   /* Get ID character. */
8030             p = "";                     /* Assume definition is empty */
8031             if (!c) {                   /* Double paranoia */
8032                 failed = 1;
8033                 p = fnval;
8034                 if (fndiags)
8035                   sprintf(fnval,"<ERROR:ARG_BAD_VARIABLE:\\fcontents()>");
8036                 goto fnend;
8037             }
8038             if (c >= '0' && c <= '9') { /* Digit for macro arg */
8039                 if (maclvl < 0)         /* Digit variables are global */
8040                   p = g_var[c];         /* if no macro is active */
8041                 else                    /* otherwise */
8042                   p = m_arg[maclvl][c - '0']; /* they're on the stack */
8043             } else if (c == '*') {
8044 #ifdef COMMENT
8045                 p = (maclvl > -1) ? m_line[maclvl] : topline;
8046                 if (!p) p = "";
8047 #else
8048                 int nx = FNVALL;
8049                 char * sx = fnval;
8050                 p = fnval;
8051 #ifdef COMMENT
8052                 if (cmdsrc() == 0 && topline)
8053                   p = topline;
8054                 else
8055 #endif /* COMMENT */
8056                   if (zzstring("\\fjoin(&_[],{ },1)",&sx,&nx) < 0) {
8057                     failed = 1;
8058                     p = fnval;
8059                     if (fndiags)
8060                       sprintf(fnval,"<ERROR:OVERFLOW:\\fcontents()>");
8061                     debug(F110,"zzstring fcontents(\\%*)",p,0);
8062                 }
8063 #endif /* COMMENT */
8064             } else {
8065                 if (isupper(c)) c -= ('a'-'A');
8066                 p = g_var[c];           /* Letter for global variable */
8067             }
8068             if (!p) p = "";
8069             goto fnend;
8070         } else if (c == '&') {          /* Array reference. */
8071             int vbi, d;
8072             if (arraynam(p,&vbi,&d) < 0) { /* Get name and subscript */
8073                 failed = 1;
8074                 p = fnval;
8075                 if (fndiags)
8076                   sprintf(fnval,"<ERROR:ARG_BAD_ARRAY:\\fcontents()>");
8077                 goto fnend;
8078             }
8079             subscript = chkarray(vbi,d); /* Check the array */
8080             if (subscript >= 0) {       /* Array is declared? */
8081                 vbi -= ARRAYBASE;       /* Convert name to index */
8082                 if (a_dim[vbi] >= d) {  /* If subscript in range */
8083                     char **ap;
8084                     ap = a_ptr[vbi];    /* get data pointer */
8085                     if (ap) {           /* and if there is one */
8086                         p = ap[d];      /* return it */
8087                         goto fnend;
8088                     }
8089                 }
8090             } else {                    /* Array not declared or element */
8091                 fnval[0] = NUL;         /* out of range - return null string */
8092                 p = fnval;              /* fdc 2010-12-30 */
8093                 goto fnend;     
8094             }
8095         } else {
8096             failed = 1;
8097             p = fnval;
8098             if (fndiags)
8099               sprintf(fnval,"<ERROR:ARG_NOT_VARIABLE:\\fcontents()>");
8100             goto fnend;
8101         }
8102     }
8103     p = fnval;                          /* Default result pointer */
8104     fnval[0] = NUL;                     /* Default result = empty string */
8105
8106     for (i = 0; i < argn; i++) {        /* Loop to expand each argument */
8107         n = MAXARGLEN;                  /* Allow plenty of space */
8108         bp[i] = s = malloc(n+1);        /* Allocate space for this argument */
8109         if (bp[i] == NULL) {            /* Handle failure to get space */
8110             failed = 1;
8111             if (fndiags)
8112               ckmakmsg(fnval,FNVALL,"<ERROR:MALLOC_FAILURE:\\f",fn,"()>",NULL);
8113             goto fnend;
8114         }
8115         p = argp[i] ? argp[i] : "";     /* Point to this argument */
8116
8117 /*
8118   Trim leading and trailing spaces from the original argument, before
8119   evaluation.  This code new to edit 184.  Fixed in edit 199 to trim
8120   blanks BEFORE stripping braces.
8121
8122 */
8123         {
8124             int x, j;
8125             x = strlen(p);
8126             j = x - 1;                  /* Trim trailing whitespace */
8127             while (j > 0 && (*(p + j) == SP || *(p + j) == HT))
8128               *(p + j--) = NUL;
8129             while (*p == SP || *p == HT) /* Strip leading whitespace */
8130               p++;
8131             x = strlen(p);
8132             if (*p == '{' && *(p+x-1) == '}') { /* NOW strip braces */
8133                 p[x-1] = NUL;
8134                 p++;
8135                 x -= 2;
8136             }
8137         }
8138
8139 /* Now evaluate the argument */
8140
8141         debug(F111,"fneval calling zzstring",p,n);
8142         t = zzstring(p,&s,&n);          /* Expand arg into new space */
8143         debug(F101,"fneval zzstring","",t);
8144         debug(F101,"fneval zzstring","",n);
8145         if (t < 0) {
8146             debug(F101,"fneval zzstring fails, arg","",i);
8147             failed = 1;
8148             if (fndiags) {
8149                 if (n == 0)
8150                   ckmakmsg(fnval,FNVALL,
8151                            "<ERROR:ARG_TOO_LONG:\\f",fn,"()>",NULL);
8152                 else
8153                   ckmakmsg(fnval,FNVALL,
8154                            "<ERROR:ARG_EVAL_FAILURE:\\f",fn,"()>",NULL);
8155             }
8156             goto fnend;
8157         }
8158         debug(F111,"fneval arg",bp[i],i);
8159     }
8160
8161 #ifdef DEBUG
8162     if (deblog) {
8163         int j;
8164         for (j = 0; j < argn; j++) {
8165             debug(F111,"fneval arg post eval",argp[j],j);
8166             debug(F111,"fneval evaluated arg",bp[j],j);
8167         }
8168     }
8169 #endif /* DEBUG */
8170     {
8171         /* Adjust argn for empty trailing arguments. */
8172         /* For example when an arg is a variable name but the */
8173         /* variable has no value.   July 2006. */
8174         int j, old; char *p;
8175         old = argn;
8176         for (j = argn - 1; j >= 0; j--) {
8177             p = bp[j];
8178             if (!p)
8179               argn--;
8180             else if (!*p)
8181               argn--;
8182             else
8183               break;
8184         }
8185 #ifdef DEBUG
8186         if (argn != old)
8187           debug(F101,"fneval adjusted argn","",argn);
8188 #endif  /* DEBUG */
8189     }   
8190
8191 /*
8192   From this point on, bp[0..argn-1] are not NULL and all must be freed
8193   before returning.
8194 */
8195     if (argn < 1) {                     /* Catch required args missing */
8196         switch (cx) {
8197           case FN_DEF:
8198           case FN_EVA:
8199           case FN_EXE:
8200           case FN_CHR:
8201           case FN_COD:
8202           case FN_MAX:
8203           case FN_MIN:
8204           case FN_MOD:
8205           case FN_FD:
8206           case FN_FS:
8207           case FN_TOD:
8208           case FN_FFN:
8209           case FN_BSN:
8210           case FN_RAW:
8211           case FN_CMD:
8212           case FN_2HEX:
8213           case FN_2OCT:
8214           case FN_DNAM:
8215 #ifdef FN_ERRMSG
8216           case FN_ERRMSG:
8217 #endif /* FN_ERRMSG */
8218 #ifdef CK_KERBEROS
8219           case FN_KRB_TK:
8220           case FN_KRB_NX:
8221           case FN_KRB_IV:
8222           case FN_KRB_TT:
8223           case FN_KRB_FG:
8224 #endif /* CK_KERBEROS */
8225           case FN_MJD2:
8226           case FN_N2TIM:
8227           case FN_DIM:
8228           case FN_DATEJ:
8229           case FN_PNCVT:
8230           case FN_PERM:
8231           case FN_ALOOK:
8232           case FN_TLOOK:
8233           case FN_ABS:
8234           case FN_AADUMP:
8235           case FN_JOIN:
8236 #ifdef CKFLOAT
8237           case FN_FPABS:
8238           case FN_FPEXP:
8239           case FN_FPLOG:
8240           case FN_FPLN:
8241           case FN_FPMOD:
8242           case FN_FPSQR:
8243           case FN_FPADD:
8244           case FN_FPDIV:
8245           case FN_FPMUL:
8246           case FN_FPPOW:
8247           case FN_FPSUB:
8248           case FN_FPINT:
8249           case FN_FPROU:
8250           case FN_FPSIN:
8251           case FN_FPCOS:
8252           case FN_FPTAN:
8253 #endif /* CKFLOAT */
8254 #ifdef TCPSOCKET
8255           case FN_HSTADD:
8256           case FN_HSTNAM:
8257 #endif /* TCPSOCKET */
8258           case FN_DELSEC:
8259 #ifdef COMMENT
8260           case FN_KWVAL:
8261           case FN_SLEEP:
8262           case FN_MSLEEP:
8263 #endif /* COMMENT */
8264 #ifdef NT
8265           case FN_SNAME:
8266           case FN_LNAME:
8267 #endif /* NT */
8268             failed = 1;
8269             p = fnval;
8270             if (fndiags)
8271               ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
8272             goto fnend;
8273         }
8274     }
8275     p = fnval;                          /* Reset these again. */
8276     fnval[0] = NUL;
8277
8278     switch (cx) {                       /* Do function on expanded args. */
8279 #ifdef TCPSOCKET
8280       case FN_HSTADD:
8281         p = ckaddr2name(bp[0]);
8282         goto fnend;
8283       case FN_HSTNAM:
8284         p = ckname2addr(bp[0]);
8285         goto fnend;
8286 #endif /* TCPSOCKET */
8287
8288       case FN_DEF:                      /* \fdefinition(arg1) */
8289         k = isaarray(bp[0]) ?
8290             mxxlook(mactab,bp[0],nmac) :
8291                 mxlook(mactab,bp[0],nmac);
8292         p = (k > -1) ? mactab[k].mval : "";
8293         goto fnend;
8294
8295       case FN_EVA:                      /* \fevaluate(arg1) */
8296         p = *(bp[0]) ? evalx(bp[0]) : "";
8297         if (!*p && fndiags) {
8298             failed = 1;
8299             p = fnval;
8300             evalerr(fn);
8301         }
8302         goto fnend;
8303
8304       case FN_EXE:                      /* \fexecute(arg1) */
8305         j = (int)strlen(s = bp[0]);     /* Length of macro invocation */
8306         p = "";                         /* Initialize return value to null */
8307         if (j) {                        /* If there is a macro to execute */
8308             while (*s == SP) s++,j--;   /* strip leading spaces */
8309             p = s;                      /* remember beginning of macro name */
8310             for (i = 0; i < j; i++) {   /* find end of macro name */
8311                 if (*s == SP)
8312                   break;
8313                 s++;
8314             }
8315             if (*s == SP)       {       /* if there was a space after */
8316                 *s++ = NUL;             /* terminate the macro name */
8317                 while (*s == SP) s++;   /* skip past any extra spaces */
8318             } else
8319               s = "";                   /* maybe there are no arguments */
8320             if (p && *p) {
8321                 k = mlook(mactab,p,nmac); /* Look up the macro name */
8322                 debug(F111,"fexec mlook",p,k);
8323             } else
8324               k = -1;
8325             if (k < 0) {
8326                 char * p2 = p;
8327                 failed = 1;
8328                 p = fnval;
8329                 if (fndiags)
8330                   ckmakxmsg(fnval,FNVALL,
8331                             "<ERROR:NO_SUCH_MACRO:\\f",fn,"(",p2,")>",
8332                             NULL,NULL,NULL,NULL,NULL,NULL,NULL);
8333                 goto fnend;
8334             }
8335 /*
8336   This is just a WEE bit dangerous because we are copying up to 9 arguments
8337   into the space reserved for one.  It won't overrun the buffer, but if there
8338   are lots of long arguments we might lose some.  The other problem is that if
8339   the macro has more than 3 arguments, the 4th through last are all
8340   concatenated onto the third.  (The workaround is to use spaces rather than
8341   commas to separate them.)  Leaving it like this to avoid having to allocate
8342   tons more buffers.
8343 */
8344             if (argn > 1) {             /* Commas used instead of spaces */
8345                 int i;
8346                 char *p = bp[0];        /* Reuse this space */
8347                 *p = NUL;               /* Make into dodo() arg list */
8348                 for (i = 1; i < argn; i++) {
8349                     ckstrncat(p,bp[i],MAXARGLEN);
8350                     ckstrncat(p," ",MAXARGLEN);
8351                 }
8352                 s = bp[0];              /* Point to new list */
8353             }
8354             p = "";                     /* Initialize return value */
8355             if (k >= 0) {               /* If macro found in table */
8356                 /* Go set it up (like DO cmd) */
8357                 if ((j = dodo(k,s,cmdstk[cmdlvl].ccflgs)) > 0) {
8358                     if (cmpush() > -1) { /* Push command parser state */
8359                         extern int ifc;
8360                         int ifcsav = ifc; /* Push IF condition on stack */
8361                         k = parser(1);  /* Call parser to execute the macro */
8362                         cmpop();        /* Pop command parser */
8363                         ifc = ifcsav;   /* Restore IF condition */
8364                         if (k == 0) {   /* No errors, ignore action cmds. */
8365                             p = mrval[maclvl+1]; /* If OK, set return value. */
8366                             if (p == NULL) p = "";
8367                         }
8368                     } else {            /* Can't push any more */
8369                         debug(F100,"zzstring fneval fexec failure","",0);
8370                         printf("\n?\\fexec() too deeply nested\n");
8371                         while (cmpop() > -1) ;
8372                         p = "";
8373                     }
8374                 }
8375             }
8376         }
8377         debug(F110,"zzstring fneval fexecute final p",p,0);
8378         goto fnend;
8379
8380 #ifdef RECURSIVE
8381       case FN_RDIR:                     /* \frdir..() - Recursive dir count */
8382       case FN_RFIL:                     /* \frfiles() - Recursive file count */
8383         /* recursive = 2; */            /* fall thru... */
8384 #endif /* RECURSIVE */
8385       case FN_FC:                       /* \ffiles() - File count. */
8386       case FN_DIR: {                    /* \ffdir.() - Directory count. */
8387           char abuf[16], *s;
8388           char ** ap = NULL;
8389           int x, xflags = 0;
8390           if (matchdot)
8391             xflags |= ZX_MATCHDOT;
8392           if (cx == FN_RDIR || cx == FN_RFIL) {
8393               xflags |= ZX_RECURSE;
8394 #ifdef CKSYMLINK
8395               /* Recursive - don't follow symlinks */
8396               xflags |= ZX_NOLINKS;
8397 #endif /* CKSYMLINK */
8398           }
8399           failed = 0;
8400           if (argn < 1) {
8401               p = "0";
8402               goto fnend;
8403           }
8404           if (cx == FN_DIR || cx == FN_RDIR) { /* Only list directories */
8405               debug(F100,"FN_DIR or FN_RDIR","",0);
8406               xflags |= ZX_DIRONLY;
8407 #ifdef OS2
8408               zxpn = 1;                 /* Use the alternate list */
8409 #endif /* OS2 */
8410           } else {                      /* List only files */
8411               debug(F100,"Not FN_DIR or FN_RDIR","",0);
8412               xflags |= ZX_FILONLY;
8413 #ifdef OS2
8414               zxpn = 1;                 /* Use the alternate list */
8415 #endif /* OS2 */
8416           }
8417           if (*(bp[0])) {
8418               k = nzxpand(bp[0],xflags);
8419               if (k < 0) k = 0;
8420               sprintf(fnval,"%d",k);    /* SAFE */
8421               p = fnval;
8422           } else
8423             p = "0";
8424
8425           if (argn > 1) {               /* Assign list to array */
8426               fnval[0] = NUL;           /* Initial return value */
8427               ckstrncpy(abuf,bp[1],16); /* Get array reference */
8428               s = abuf;
8429               if (*s == CMDQ) s++;
8430               failed = 1;               /* Assume it's bad */
8431               p = fnval;                /* Point to result */
8432               if (fndiags)              /* Default is this error message */
8433                 ckmakmsg(fnval,FNVALL,
8434                          "<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
8435               if (s[0] != '&')          /* "Address" of array */
8436                 goto fnend;
8437               if (s[2])
8438                 if (s[2] != '[' || s[3] != ']')
8439                   goto fnend;
8440               if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */
8441                 s[1] += 32;
8442               if ((x = dclarray(s[1],k)) < 0) /* File list plus count */
8443                 goto fnend;
8444               failed = 0;               /* Unset failure flag */
8445               ap = a_ptr[x];            /* Point to array we just declared */
8446               sprintf(fnval,"%d",k);    /* SAFE */
8447           }
8448 #ifdef OS2
8449           if (ap) {                     /* We are making an array */
8450               int i;
8451               char tmp[16];
8452               ap[0] = NULL;             /* Containing number of files    */
8453               makestr(&(ap[0]),ckitoa(k));
8454
8455               ckstrncpy(tmp,fnval,16);  /* Save return value */
8456
8457               for (i = 1; i <= k; i++) { /* Fill it */
8458                   ap[i] = NULL;
8459                   znext(fnval);         /* Next filename */
8460                   if (!*fnval)          /* No more, done */
8461                     break;              /* In case a premature end */
8462                   makestr(&(ap[i]),fnval);
8463               }
8464 #ifdef ZXREWIND
8465               k = zxrewind();           /* Reset the file expansion */
8466 #else
8467               k = nzxpand(bp[0],xflags);
8468 #endif /* ZXREWIND */
8469               ckstrncpy(fnval,tmp,FNVALL); /* Restore return value */
8470           }
8471 #else /* OS2 */
8472           {                             /* Make copies of the list */
8473               int i; char tmp[16];
8474               if (flist) {              /* Free old file list, if any */
8475                   for (i = 0; flist[i]; i++) { /* and each string */
8476                       free(flist[i]);
8477                       flist[i] = NULL;
8478                   }
8479                   free((char *)flist);
8480               }
8481               ckstrncpy(tmp,fnval,16);  /* Save our return value */
8482               flist = (char **) malloc((k+1) * sizeof(char *)); /* New array */
8483               if (flist) {
8484                   for (i = 0; i <= k; i++) { /* Fill it */
8485                       flist[i] = NULL;
8486                       znext(fnval);     /* Next filename */
8487                       if (!*fnval)      /* No more, done */
8488                         break;
8489                       makestr(&(flist[i]),fnval);
8490                   }
8491                   if (ap) {             /* If array pointer given */
8492                       ap[0] = NULL;
8493                       makestr(&(ap[0]),ckitoa(k));
8494                       for (i = 0; i < k; i++) { /* Copy file list to array */
8495                           ap[i+1] = NULL;
8496                           makestr(&(ap[i+1]),flist[i]);
8497                       }
8498                   }
8499               }
8500               ckstrncpy(fnval,tmp,FNVALL); /* Restore return value */
8501               flistn = 0;               /* Reset global list pointer */
8502           }
8503 #endif /* OS2 */
8504 #ifdef RECURSIVE
8505           recursive = rsave;
8506 #endif /* RECURSIVE */
8507 #ifdef OS2
8508           zxpn = zsave;
8509 #endif /* OS2 */
8510       }
8511       goto fnend;
8512
8513       case FN_FIL:                      /* \fnextfile() - Next file in list. */
8514         p = fnval;                      /* (no args) */
8515         *p = NUL;
8516 #ifdef OS2
8517         zxpn = 1;                       /* OS/2 - use the alternate list */
8518         znext(p);                       /* Call system-dependent function */
8519         zxpn = zsave;                   /* Restore original list */
8520 #else
8521         if (flist)                      /* Others, use our own list. */
8522           if (flist[flistn])
8523             p = flist[flistn++];
8524 #endif /* OS2 */
8525         goto fnend;
8526
8527     } /* Break up big switch... */
8528
8529     switch (cx) {
8530       case FN_IND:                      /* \findex(s1,s2,start,occurrence) */
8531       case FN_RIX:                      /* \frindex(s1,s2,start,occurrence) */
8532       case FN_SEARCH:                   /* \fsearch(pat,string,start,occ) */
8533       case FN_RSEARCH:                  /* \frsearch(pat,string,start,occ) */
8534       case FN_COUNT: {                  /* \fcount(s1,s2,start) */
8535         int i = 0, right = 0, search = 0, count = 0;
8536         int desired = 1;
8537         right = (cx == FN_RIX || cx == FN_RSEARCH);
8538         search = (cx == FN_SEARCH || cx == FN_RSEARCH);
8539         count = (cx == FN_COUNT);
8540         p = "0";
8541         if (argn > 1) {                 /* Only works if we have 2 or 3 args */
8542             int start = 0;
8543             char * pat = NULL;
8544             len1 = (int)strlen(pat = bp[0]); /* length of string to look for */
8545             len2 = (int)strlen(s = bp[1]); /* length of string to look in */
8546             if (len1 < 1 || len2 < 1)   /* Watch out for empty strings */
8547               goto fnend;
8548             start = right ? -1 : 0;     /* Default starting position */
8549             if (argn > 2) {
8550                 val1 = *(bp[2]) ? evalx(bp[2]) : "1";
8551                 if (argn > 3) {
8552                     val2 = *(bp[3]) ? evalx(bp[3]) : "1";
8553                     if (chknum(val2)) desired = atoi(val2);
8554                     if (desired * len1 > len2) goto fnend;
8555                 }
8556                 if (chknum(val1)) {
8557                     int t;
8558                     t = atoi(val1);
8559                     if (!search) {      /* Index or Rindex */
8560                         j = len2 - len1; /* Length difference */
8561                         t--;             /* Convert position to 0-based */
8562                         if (t < 0) t = 0;
8563                         start = t;
8564                         if (!right && start < 0) start = 0;
8565                     } else {            /* Search or Rsearch */
8566                         int x;
8567                         if (t < 0) t = 0;
8568                         if (right) {    /* Right to left */
8569                             if (t > len2) t = len2;
8570                             start = len2 - t - 1;
8571                             if (start < 0)
8572                               goto fnend;
8573                             x = len2 - t;
8574                             s[x] = NUL;
8575                         } else {        /* Left to right */
8576                             start = t - 1;
8577                             if (start < 0) start = 0;
8578                             if (start >= len2)
8579                               goto fnend;
8580                         }
8581                     }
8582                 } else {
8583                     failed = 1;
8584                     evalerr(fn);
8585                     p = fnval;
8586                     goto fnend;
8587                 }
8588             }
8589             if (count) {                /* \fcount() */
8590                 int j;
8591                 for (i = 0; start < len2; i++) {
8592                     j = ckindex(pat,bp[1],start,0,inpcas[cmdlvl]);
8593                     if (j == 0) break;
8594                     start = j;
8595                 }
8596
8597             } else if (search) {        /* \fsearch() or \frsearch() */
8598
8599                 if (right && pat[0] == '^') {
8600                     right = 0;
8601                     start = 0;
8602                 }
8603                 if (right) {            /* From right */
8604                     int k, j = 1;
8605                     if (start < 0)
8606                       start = len2 - 1;
8607                     i = 0;
8608                     while (start >= 0 && j <= desired) {
8609                         for (i = start;
8610                              (i >= 0) && 
8611                                  !(k = ckmatch(pat,s+i,inpcas[cmdlvl],1+4));
8612                              i--) ;
8613                         if (k < 1) {    /* No match */
8614                             i = 0;
8615                             break;
8616                         }
8617                         if (j == desired) { /* The match we want? */
8618                             i += k;     /* Yes, return string index */
8619                             break;
8620                         }
8621                         j++;            /* No, count this match */
8622                         s[i] = NUL;     /* null it out */
8623                         start = i-1;    /* move left and look again */
8624                     }
8625
8626                 } else {                /* From left */
8627                     int j;
8628                     i = 0;
8629                     for (j = 1; j <= desired && start < len2; j++) {
8630                         i = ckmatch(pat,&s[start],inpcas[cmdlvl],1+4);
8631                         if (i == 0 || j == desired) break;
8632                         start += i + 1;
8633                     }                   
8634                     if (j == desired && i != 0)
8635                       i += start;
8636                     else
8637                       i = 0;
8638                 }
8639             } else {                    /* index or rindex */
8640                 int j = 0;
8641                 i = 0;
8642                 for (j = 1; j <= desired && start < len2; j++) {
8643                     i = ckindex(pat,bp[1],start,right,inpcas[cmdlvl]);
8644                     if (i == 0 || j == desired) break;
8645                     start = (right) ? len2 - i + 1 : i;
8646                 }
8647                 if (j != desired)
8648                   i = 0;
8649             }
8650             sprintf(fnval,"%d",i);      /* SAFE */
8651             p = fnval;
8652         }
8653         goto fnend;
8654       }
8655
8656       case FN_RPL:                      /* \freplace(s1,s2,s3) */
8657       /*
8658         s = bp[0] = source string
8659             bp[1] = match string
8660             bp[2] = replacement string
8661             bp[3] = which occurrence (default = all);
8662         p = fnval = destination (result) string
8663       */
8664         if (argn < 1)                   /* Nothing */
8665           goto fnend;
8666         if (argn < 2) {                 /* Only works if we have 2 or 3 args */
8667             ckstrncpy(p,bp[0],FNVALL);
8668         } else {
8669             int occur = 0, xx = 0, j2;
8670             len1 = (int)strlen(bp[0]);  /* length of string to look in */
8671             len2 = (int)strlen(bp[1]);  /* length of string to look for */
8672             len3 = (argn < 3) ? 0 : (int)strlen(bp[2]); /* Len of replacemnt */
8673             j = len1 - len2 + 1;
8674             j2 = j;
8675             if (argn > 3) {
8676                 if (chknum(bp[3])) {
8677                     occur = atoi(bp[3]);
8678                 } else {
8679                     failed = 1;
8680                     if (fndiags)
8681                       ckmakmsg(fnval,FNVALL,
8682                                "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
8683                     goto fnend;
8684                 }
8685             }
8686             /* If args out of whack... */
8687             if (j < 1 || len1 == 0 || len2 == 0) {
8688                 ckstrncpy(p,bp[0],FNVALL); /* just return original string */
8689                 p[FNVALL] = NUL;
8690             } else {
8691               ragain:
8692                 s = bp[0];              /* Point to beginning of string */
8693                 while (j-- > 0) {       /* For each character */
8694                     if (!ckstrcmp(bp[1],s,len2,inpcas[cmdlvl]) &&
8695                         (occur == 0 || occur == ++xx)) {
8696                         if (len3) {
8697                             ckstrncpy(p,bp[2],FNVALL);
8698                             p += len3;
8699                         }
8700                         s += len2;      /* and skip past it. */
8701                     } else {            /* No, */
8702                         *p++ = *s++;    /* just copy this character */
8703                     }
8704                 }
8705                 *p = NUL;
8706                 while ((*p++ = *s++));
8707                 if (occur < 0) {        /* cheap... */
8708                     occur = xx + occur + 1;
8709                     xx = 0;
8710                     p = fnval;
8711                     j = j2;
8712                     if (occur > 0)
8713                       goto ragain;
8714                 }
8715             }
8716         }
8717         p = fnval;
8718         goto fnend;
8719
8720       case FN_CHR:                      /* \fcharacter(arg1) */
8721         val1 = *(bp[0]) ? evalx(bp[0]) : "";
8722         if (chknum(val1)) {             /* Must be numeric */
8723             i = atoi(val1);
8724             if (i >= 0 && i < 256) {    /* Must be an 8-bit value */
8725                 p = fnval;
8726                 *p++ = (char) i;
8727                 *p = NUL;
8728                 p = fnval;
8729             } else {
8730                 failed = 1;
8731                 if (fndiags)
8732                   ckmakmsg(fnval,FNVALL,
8733                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
8734             }
8735         } else {
8736             failed = 1;
8737             evalerr(fn);
8738         }
8739         goto fnend;
8740
8741       case FN_COD:                      /* \fcode(char) */
8742         if ((int)strlen(bp[0]) > 0) {
8743             p = fnval;
8744             i = *bp[0];
8745             sprintf(p,"%d",(i & 0xff)); /* SAFE */
8746         } else p = "0";                 /* Can't happen */
8747         goto fnend;
8748
8749       case FN_LEN:                      /* \flength(arg1) */
8750         if (argn > 0) {
8751             p = fnval;
8752             sprintf(p,"%d",(int)strlen(bp[0])); /* SAFE */
8753         } else p = "0";
8754         goto fnend;
8755
8756       case FN_LOW:                      /* \flower(arg1) */
8757         s = bp[0] ? bp[0] : "";
8758         p = fnval;
8759         while (*s) {
8760             if (isupper(*s))
8761               *p = (char) tolower(*s);
8762             else
8763               *p = *s;
8764             p++; s++;
8765         }
8766         *p = NUL;
8767         p = fnval;
8768         goto fnend;
8769
8770       case FN_MAX:                      /* \fmax(arg1,arg2) */
8771       case FN_MIN:                      /* \fmin(arg1,arg2) */
8772       case FN_MOD:                      /* \fmod(arg1,arg2) */
8773         val1 = *(bp[0]) ? evalx(bp[0]) : "";
8774 #ifdef COMMENT
8775         /* No longer necessary because evalx() no longer overwrites its */
8776         /* result every time it's called (2000/09/23). */
8777         free(bp[0]);
8778         bp[0] = NULL;
8779 #endif /* COMMENT */
8780         if (argn > 1) {
8781 #ifdef COMMENT
8782             /* Ditto... */
8783             bp[0] = malloc((int)strlen(val1)+1);
8784             if (bp[0])
8785               strcpy(bp[0],val1);       /* safe */
8786             val1 = bp[0];
8787 #endif /* COMMENT */
8788             val2 = *(bp[1]) ? evalx(bp[1]) : "";
8789             if (chknum(val1) && chknum(val2)) {
8790                 i = atoi(val1);
8791                 j = atoi(val2);
8792                 switch (y) {
8793                   case FN_MAX:
8794                     if (j < i) j = i;
8795                     break;
8796                   case FN_MIN:
8797                     if (j > i) j = i;
8798                     break;
8799                   case FN_MOD:
8800                     if (j == 0) {
8801                         failed = 1;
8802                         if (fndiags)
8803                           ckmakmsg(fnval,FNVALL,
8804                                    "<ERROR:DIVIDE_BY_ZERO:\\f",fn,"()>",NULL);
8805                         else
8806                           fnval[0] = NUL;
8807                         goto fnend;
8808                     } else
8809                       j = i % j;
8810                 }
8811                 p = fnval;
8812                 sprintf(p,"%d",j);      /* SAFE */
8813             } else {
8814                 failed = 1;
8815                 evalerr(fn);
8816             }
8817         } else p = val1;
8818         goto fnend;
8819     } /* Break up big switch... */
8820
8821     switch (y) {
8822       case FN_SUB:                      /* \fsubstr(arg1,arg2,arg3) */
8823       case FN_RIG:                      /* \fright(arg1,arg2) */
8824       case FN_LEF:                      /* \fleft(arg1,arg2) */
8825         if (argn < 1)
8826           goto fnend;
8827         val1 = "";
8828         if (argn > 1)
8829           if (*(bp[1]))
8830             val1 =  evalx(bp[1]);
8831 #ifdef COMMENT
8832         if (bp[1]) free(bp[1]);         /* Have to copy this */
8833         bp[1] = malloc((int)strlen(val1)+1);
8834         if (!bp[1]) {
8835             failed = 1;
8836             if (fndiags) {
8837                 p = fnval;
8838                 ckmakmsg(fnval,FNVALL,
8839                          "<ERROR:MALLOC_FAILURE:\\f",fn,"()>",NULL);
8840             }
8841             goto fnend;
8842         }
8843         strcpy(bp[1],val1);             /* safe */
8844         val1 = bp[1];
8845 #endif /* COMMENT */
8846         val2 = "";
8847         if (argn > 2)
8848           if (*(bp[2]))
8849             val2 = evalx(bp[2]);
8850         if (
8851             ((argn > 1) && (int)strlen(val1) && !rdigits(val1)) ||
8852             ((cx == FN_SUB) &&
8853               ((argn > 2) && (int)strlen(val2) && !rdigits(val2)))
8854             ) {
8855             failed = 1;
8856             evalerr(fn);
8857         } else {
8858             int lx;
8859             p = fnval;                  /* pointer to result */
8860             lx = strlen(bp[0]);         /* length of arg1 */
8861             if (cx == FN_SUB) {         /* substring */
8862                 k = (argn > 2) ? atoi(val2) : MAXARGLEN; /* length */
8863                 j = (argn > 1) ? atoi(val1) : 1; /* start pos for substr */
8864             } else if (cx == FN_LEF) {  /* left */
8865                 k = (argn > 1) ? atoi(val1) : lx;
8866                 j = 1;
8867             } else {                             /* right */
8868                 k = (argn > 1) ? atoi(val1) : lx; /* length */
8869                 j = lx - k + 1;                  /* start pos for right */
8870                 if (j < 1) j = 1;
8871             }
8872             if (k > 0 && j <= lx) {              /* if start pos in range */
8873                 s = bp[0]+j-1;                   /* point to source string */
8874                 for (i = 0; (i < k) && (*p++ = *s++); i++) ;  /* copy */
8875             }
8876             *p = NUL;                   /* terminate the result */
8877             p = fnval;                  /* and point to it. */
8878         }
8879         goto fnend;
8880
8881       case FN_UPP:                      /* \fupper(arg1) */
8882         s = bp[0] ? bp[0] : "";
8883         p = fnval;
8884         while (*s) {
8885             if (islower(*s))
8886               *p = (char) toupper(*s);
8887             else
8888               *p = *s;
8889             p++; s++;
8890         }
8891         *p = NUL;
8892         p = fnval;
8893         goto fnend;
8894
8895       case FN_REP:                      /* \frepeat(text,number) */
8896         if (argn < 1)
8897           goto fnend;
8898         val1 = "1";
8899         if (argn > 1)
8900           if (*(bp[1]))
8901             val1 = evalx(bp[1]);
8902         if (chknum(val1)) {             /* Repeat count */
8903             n = atoi(val1);
8904             debug(F111,"SUNDAY frepeat n",val1,n);
8905             if (n > 0) {                /* Make n copies */
8906                 p = fnval;
8907                 *p = '\0';
8908                 k = (int)strlen(bp[0]); /* Make sure string has some length */
8909                 debug(F111,"SUNDAY frepeat k","",k);
8910                 debug(F111,"SUNDAY frepeat FNVALL","",FNVALL);
8911                 if (k * n >= FNVALL) {  /* But not too much... */
8912                     failed = 1;
8913                     if (fndiags)
8914                       ckmakmsg(fnval,FNVALL,
8915                                "<ERROR:RESULT_TOO_LONG:\\f",fn,"()>",NULL);
8916                     else
8917                       fnval[0] = NUL;
8918                     p = fnval;
8919                     goto fnend;
8920                 }
8921                 if (k > 0) {            /* If there is something to copy */
8922                     for (i = 0; i < n; i++) { /* Copy loop */
8923                         s = bp[0];
8924                         for (j = 0; j < k; j++) {
8925                             if ((p - fnval) >= FNVALL)
8926                               break;    /* shouldn't happen... */
8927                             else
8928                               *p++ = *s++;
8929                         }
8930                     }
8931                     *p = NUL;
8932                 }
8933             }
8934         } else {
8935             failed = 1;
8936             evalerr(fn);
8937         }
8938         p = fnval;
8939         goto fnend;
8940
8941 #ifndef NOFRILLS
8942       case FN_REV:                      /* \freverse() */
8943         if (argn < 1)
8944           goto fnend;
8945         yystring(bp[0],&p);
8946         goto fnend;
8947 #endif /* NOFRILLS */
8948
8949       case FN_RPA:                      /* \frpad() and \flpad() */
8950       case FN_LPA:
8951         if (argn < 1)
8952           goto fnend;
8953         val1 = "";
8954         if (argn > 1)
8955           if (*(bp[1]))
8956             val1 = evalx(bp[1]);
8957         if (argn == 1) {                /* If a number wasn't given */
8958             p = fnval;                  /* just return the original string */
8959             ckstrncpy(p,bp[0],FNVALL);
8960         } else if (argn > 1 &&  !*val1) {
8961             failed = 1;
8962             evalerr(fn);
8963         } else /* if (chknum(val1)) */ { /* Repeat count */
8964             char pc;
8965             n = atoi(val1);
8966             if (n >= 0) {
8967                 p = fnval;
8968                 k = (int)strlen(bp[0]); /* Length of string to be padded */
8969                 if (k >= n) {           /* It's already long enough */
8970                     ckstrncpy(p,bp[0],FNVALL);
8971                 } else {
8972                     if (n + k <= FNVALL) {
8973                         pc = (char) ((argn < 3) ? SP : *bp[2]);
8974                         if (!pc) pc = SP;
8975                         if (cx == FN_RPA) { /* RPAD */
8976                             strncpy(p,bp[0],k); /* (leave it like this) */
8977                             p[k] = NUL;
8978                             p += k;
8979                             for (i = k; i < n; i++)
8980                               *p++ = pc;
8981                         } else {        /* LPAD */
8982                             n -= k;
8983                             for (i = 0; i < n; i++)
8984                               *p++ = pc;
8985                             strncpy(p,bp[0],k); /* (leave it like this) */
8986                             p[k] = NUL;
8987                             p += k;
8988                         }
8989                     }
8990                     *p = NUL;
8991                 }
8992             }
8993         }
8994         p = fnval;
8995         goto fnend;
8996
8997 #ifdef ZFCDAT
8998       case FN_FD:                       /* \fdate(filename) */
8999         p = fnval;
9000         s = zfcdat(bp[0]);
9001         if (!s) s = "";
9002         if (!*s) {
9003             failed = 1;
9004             if (fndiags)
9005               ckmakmsg(fnval,FNVALL,"<ERROR:FILE_NOT_FOUND:\\f",fn,"()>",NULL);
9006             goto fnend;
9007         }
9008         ckstrncpy(fnval,s,FNVALL);
9009 #endif /* ZFCDAT */
9010         goto fnend;
9011
9012     } /* Break up big switch... */
9013
9014     switch (y) {
9015       case FN_FS: {                     /* \fsize(filename) */
9016           CK_OFF_T z;
9017           p = fnval;
9018           z = zchki(bp[0]);
9019           if (z < (CK_OFF_T)0) {
9020               failed = 1;
9021               if (fndiags) {
9022                   if (z == (CK_OFF_T)-1)
9023                     ckmakmsg(fnval,FNVALL,
9024                              "<ERROR:FILE_NOT_FOUND:\\f",fn,"()>",NULL);
9025                   else if (z == (CK_OFF_T)-2)
9026                     ckmakmsg(fnval,FNVALL,
9027                              "<ERROR:FILE_NOT_READABLE:\\f",fn,"()>",NULL);
9028                   else if (z == (CK_OFF_T)-3)
9029                     ckmakmsg(fnval,FNVALL,
9030                              "<ERROR:FILE_NOT_ACCESSIBLE:\\f",fn,"()>",NULL);
9031                   else
9032                     ckmakmsg(fnval,FNVALL,
9033                              "<ERROR:FILE_ERROR:\\f",fn,"()>",NULL);
9034               }
9035               goto fnend;
9036           }
9037           ckstrncpy(fnval,ckfstoa(z),FNVALL);
9038           goto fnend;
9039       }
9040       case FN_VER:                      /* \fverify() */
9041         p = "-1";
9042         if (argn == 1)                  /* No second arg */
9043           goto fnend;
9044         else if (!bp[1])                /* Or second arg null */
9045           goto fnend;
9046         else if (!*(bp[1]))             /* or empty. */
9047           goto fnend;
9048         p = "0";
9049         if (argn > 1) {                 /* Only works if we have 2 or 3 args */
9050             int start;
9051             char *s2, ch1, ch2;
9052             start = 0;
9053             if (argn > 2) {             /* Starting position specified */
9054                 val1 = *(bp[2]) ? evalx(bp[2]) : "0";
9055                 if (chknum(val1)) {
9056                     start = atoi(val1) /* - 1 */;
9057                     if (start < 0) start = 0;
9058                     if (start > (int)strlen(bp[1]))
9059                       goto verfin;
9060                 } else {
9061                     failed = 1;
9062                     evalerr(fn);
9063                     goto fnend;
9064                 }
9065             }
9066             i = start;
9067             p = "0";
9068             for (s = bp[1] + start; *s; s++,i++) {
9069                 ch1 = *s;
9070                 if (!inpcas[cmdlvl]) if (islower(ch1)) ch1 = toupper(ch1);
9071                 j = 0;
9072                 for (s2 = bp[0]; *s2; s2++) {
9073                     ch2 = *s2;
9074                     if (!inpcas[cmdlvl]) if (islower(ch2)) ch2 = toupper(ch2);
9075                     if (ch1 == ch2) {
9076                         j = 1;
9077                         break;
9078                     }
9079                 }
9080                 if (j == 0) {
9081                     sprintf(fnval,"%d",i+1); /* SAFE */
9082                     p = fnval;
9083                     break;
9084                 }
9085             }
9086         }
9087       verfin:
9088         goto fnend;
9089
9090       case FN_IPA:                      /* Find and return IP address */
9091         if (argn > 0) {                 /* in argument string. */
9092             int start = 0;
9093             if (argn > 1) {             /* Starting position specified */
9094                 if (chknum(bp[1])) {
9095                     start = atoi(bp[1]) - 1;
9096                     if (start < 0) start = 0;
9097                 } else {
9098                     failed = 1;
9099                     if (fndiags)
9100                       ckmakmsg(fnval,FNVALL,
9101                                "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9102                     goto fnend;
9103                 }
9104             }
9105             p = getip(bp[0]+start);
9106         } else p = "";
9107         goto fnend;
9108
9109 #ifdef OS2
9110       case FN_CRY:
9111         p = "";
9112         if (argn > 0) {
9113             p = fnval;
9114             ckstrncpy(p,bp[0],FNVALL);
9115             ck_encrypt(p);
9116         }
9117         goto fnend;
9118
9119       case FN_OOX:
9120         p = "";
9121         if (argn > 0)
9122           p = (char *) ck_oox(bp[0], (argn > 1) ? bp[1] : "");
9123         goto fnend;
9124 #endif /* OS2 */
9125
9126       case FN_HEX:                      /* \fhexify(arg1) */
9127         if (argn < 1)
9128           goto fnend;
9129         if ((int)strlen(bp[0]) < (FNVALL / 2)) {
9130             s = bp[0];
9131             p = fnval;
9132             while (*s) {
9133                 x = (*s >> 4) & 0x0f;
9134                 *p++ = hexdigits[x];
9135                 x = *s++ & 0x0f;
9136                 *p++ = hexdigits[x];
9137             }
9138             *p = NUL;
9139             p = fnval;
9140         }
9141         goto fnend;
9142
9143       case FN_UNTAB:                    /* \funtab(arg1) */
9144         if (argn < 1)
9145           goto fnend;
9146         if ((int)strlen(bp[0]) < (FNVALL * 2)) {
9147             s = bp[0];
9148             p = fnval;
9149             if (untabify(bp[0],p,FNVALL) < 0) {
9150                 failed = 1;
9151                 if (fndiags)
9152                   ckmakmsg(fnval,FNVALL,
9153                            "<ERROR:OVERFLOW:\\f",fn,"()>",NULL);
9154             }
9155             goto fnend;
9156         }
9157
9158       case FN_UNH: {                    /* \funhex(arg1) */
9159           int c[2], i;
9160           if (argn < 1)
9161             goto fnend;
9162           if ((int)strlen(bp[0]) < (FNVALL * 2)) {
9163               s = bp[0];
9164               p = fnval;
9165               while (*s) {
9166                   for (i = 0; i < 2; i++) {
9167                       c[i] = *s++;
9168                       if (!c[i]) { p = ""; goto unhexfin; }
9169                       if (islower(c[i])) c[i] = toupper(c[i]);
9170                       if (c[i] >= '0' && c[i] <= '9') {
9171                           c[i] -= 0x30;
9172                       } else if (c[i] >= 'A' && c[i] <= 'F') {
9173                           c[i] -= 0x37;
9174                       } else {
9175                           failed = 1;
9176                           if (fndiags)
9177                             ckmakmsg(fnval,
9178                                      FNVALL,
9179                                      "<ERROR:ARG_OUT_OF_RANGE:\\f",
9180                                      fn,
9181                                      "()>",
9182                                      NULL
9183                                      );
9184                           goto fnend;
9185                       }
9186                   }
9187                   *p++ = ((c[0] << 4) & 0xf0) | (c[1] & 0x0f);
9188               }
9189               *p = NUL;
9190               p = fnval;
9191           }
9192         unhexfin:
9193           goto fnend;
9194       }
9195
9196       case FN_BRK: {                    /* \fbreak() */
9197           char * c;                     /* Characters to break on */
9198           char c2, s2;
9199           int start = 0;
9200           int done = 0;
9201           if (argn < 1)
9202             goto fnend;
9203           if (argn > 2) {
9204               s = bp[2] ? bp[2] : "0";
9205               if (chknum(s)) {
9206                   start = atoi(s);
9207                   if (start < 0) start = 0;
9208                   if (start > (int)strlen(bp[0]))
9209                     goto brkfin;
9210               } else {
9211                   failed = 1;
9212                   if (fndiags)
9213                     ckmakmsg(fnval,FNVALL,
9214                              "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9215                   goto fnend;
9216               }
9217           }
9218           s = bp[0] + start;            /* Source pointer */
9219
9220           while (*s && !done) {
9221               s2 = *s;
9222               if (!inpcas[cmdlvl] && islower(s2)) s2 = toupper(s2);
9223               c = bp[1] ? bp[1] : "";   /* Character to break on */
9224               while (*c) {
9225                   c2 = *c;
9226                   if (!inpcas[cmdlvl] && islower(c2)) c2 = toupper(c2);
9227                   if (c2 == s2) {
9228                       done = 1;
9229                       break;
9230                   }
9231                   c++;
9232               }
9233               if (done) break;
9234               *p++ = *s++;
9235           }
9236           *p = NUL;                     /* terminate the result */
9237           p = fnval;                    /* and point to it. */
9238         brkfin:
9239           goto fnend;
9240       }
9241
9242       case FN_SPN: {                    /* \fspan() */
9243           char *q;
9244           char c1, c2;
9245           int start = 0;
9246           if (argn < 1)
9247             goto fnend;
9248           if (argn > 2) {               /* Starting position */
9249               s = bp[2] ? bp[2] : "0";
9250               if (chknum(s)) {
9251                   start = atoi(s);
9252                   if (start < 0) start = 0;
9253               } else {
9254                   failed = 1;
9255                   if (fndiags)
9256                     ckmakmsg(fnval,FNVALL,
9257                              "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9258                   goto fnend;
9259               }
9260           }
9261           s = bp[0] + start;            /* Source pointer */
9262           if (argn > 1 &&
9263               (int)strlen(bp[1]) > 0 &&
9264               start <= (int)strlen(bp[0])) {
9265               while (*s) {              /* Loop thru source string */
9266                   q = bp[1];            /* Span string */
9267                   c1 = *s;
9268                   if (!inpcas[cmdlvl])
9269                     if (islower(c1)) c1 = toupper(c1);
9270                   x = 0;
9271                   while ((c2 = *q++)) {
9272                       if (!inpcas[cmdlvl])
9273                         if (islower(c2)) c2 = toupper(c2);
9274                       if (c1 == c2) { x = 1; break; }
9275                   }
9276                   if (!x) break;
9277                   *p++ = *s++;
9278               }
9279               *p = NUL;                 /* Terminate and return the result */
9280               p = fnval;
9281           }
9282           goto fnend;
9283       }
9284     } /* Break up big switch... */
9285
9286     switch (y) {
9287       case FN_TRM:                      /* \ftrim(s1[,s2]) */
9288       case FN_LTR:                      /* \fltrim(s1[,s2]) */
9289         if (argn < 1)
9290           goto fnend;
9291         if ((len1 = (int)strlen(bp[0])) > 0) {
9292             if (len1 > FNVALL)
9293               len1 = FNVALL;
9294             s = " \t\r\n";
9295             if (argn > 1)               /* Trim list given */
9296               s = bp[1];
9297             len2 = (int)strlen(s);
9298             if (len2 < 1) {             /* or not... */
9299                 s = " \t\r\n";          /* Default is to trim whitespace */
9300                 len2 = 2;
9301             }
9302             if (cx == FN_TRM) {         /* Trim from right */
9303                 char * q, p2, q2;
9304                 ckstrncpy(fnval,bp[0],FNVALL); /* Copy string to output */
9305                 p = fnval + len1 - 1;   /* Point to last character */
9306
9307                 while (p >= (char *)fnval) { /* Go backwards */
9308                     q = s;              /* Point to trim list */
9309                     p2 = *p;
9310                     if (!inpcas[cmdlvl])
9311                       if (islower(p2)) p2 = toupper(p2);
9312                     while (*q) {        /* Is this char in trim list? */
9313                         q2 = *q;
9314                         if (!inpcas[cmdlvl])
9315                           if (islower(q2)) q2 = toupper(q2);
9316                         if (p2 == q2) { /* Yes, null it out */
9317                             *p = NUL;
9318                             break;
9319                         }
9320                         q++;
9321                     }
9322                     if (!*q)            /* Trim list exhausted */
9323                       break;            /* So we're done. */
9324                     p--;                /* Else keep trimming */
9325                 }
9326             } else {                    /* Trim from left */
9327                 char * q, p2, q2;
9328                 p = bp[0];              /* Source */
9329                 while (*p) {
9330                     p2 = *p;
9331                     if (!inpcas[cmdlvl])
9332                       if (islower(p2)) p2 = toupper(p2);
9333                     q = s;
9334                     while (*q) {        /* Is this char in trim list? */
9335                         q2 = *q;
9336                         if (!inpcas[cmdlvl])
9337                           if (islower(q2)) q2 = toupper(q2);
9338                         if (p2 == q2) { /* Yes, point past it */
9339                             p++;        /* and try next source character */
9340                             break;
9341                         }
9342                         q++;            /* No, try next trim character */
9343                     }
9344                     if (!*q)            /* Trim list exhausted */
9345                       break;            /* So we're done. */
9346                 }
9347                 ckstrncpy(fnval,p,FNVALL);
9348             }
9349             p = fnval;
9350         } else p = "";
9351         goto fnend;
9352
9353       case FN_CAP:                      /* \fcapitalize(arg1) */
9354         if (argn < 1)
9355           goto fnend;
9356         s = bp[0];
9357         p = fnval;
9358         x = 0;
9359         while ((c = *s++)) {
9360             if (isalpha(c)) {
9361                 if (x == 0) {
9362                     x = 1;
9363                     if (islower(c))
9364                       c = toupper(c);
9365                 } else if (isupper(c))
9366                   c = tolower(c);
9367             }
9368             *p++ = c;
9369         }
9370         *p = NUL;
9371         p = fnval;
9372         goto fnend;
9373
9374 #ifdef COMMENT
9375       case FN_TOD:                      /* Time of day to secs since midnite */
9376         sprintf(fnval,"%ld",tod2sec(bp[0])); /* SAFE */
9377         goto fnend;
9378 #endif /* COMMENT */
9379
9380       case FN_FFN:                      /* Full pathname of file */
9381         zfnqfp(bp[0],FNVALL,p);
9382         if (!p) p = "";
9383         goto fnend;
9384
9385       case FN_CHK: {                    /* \fchecksum() */
9386           long chk = 0;
9387           p = (argn > 0) ? bp[0] : "";
9388           while (*p) chk += *p++;
9389           sprintf(fnval,"%lu",chk);     /* SAFE */
9390           p = fnval;
9391           goto fnend;
9392       }
9393
9394 #ifndef NOXFER
9395       case FN_CRC:                      /* \fcrc16() */
9396         if (argn > 0)
9397           sprintf(fnval,"%u",           /* SAFE */
9398                   chk3((CHAR *)bp[0],(int)strlen(bp[0])));
9399         else
9400           p = "0";
9401         goto fnend;
9402 #endif /* NOXFER */
9403
9404       case FN_BSN:                      /* \fbasename() */
9405         zstrip(bp[0],&p);
9406         goto fnend;
9407
9408 #ifndef NOLOCAL
9409 #ifdef OS2
9410       case FN_SCRN_CX:                  /* \fscrncurx() */
9411         p = fnval;
9412         sprintf(p,"%d",(int)VscrnGetCurPos(VTERM)->x); /* SAFE */
9413         goto fnend;
9414
9415       case FN_SCRN_CY:                  /* \fscrncury() */
9416         p = fnval;
9417         sprintf(p,"%d",(int)VscrnGetCurPos(VTERM)->y); /* SAFE */
9418         goto fnend;
9419
9420       case FN_SCRN_STR: {               /* \fscrnstr() */
9421           videoline * line = NULL;
9422           viocell * cells = NULL;
9423           int row = 0, col = 0, len = 0;
9424           /* NOTE: On Unicode systems, the screen contents are stored in */
9425           /* in Unicode.  Therefore, we should really be performing a    */
9426           /* conversion to the local character set.                      */
9427
9428           /* 6/18/2000 - added the translation to lcs */
9429
9430           if (bp[0] == NULL || bp[0][0] == '\0') {
9431               row = 0;
9432           } else {
9433               if (chknum(bp[0])) {
9434                   row = atoi(bp[0]);
9435                   if (row < 0)
9436                     row = 0;
9437               } else {
9438                   failed = 1;
9439                   if (fndiags)
9440                     ckmakmsg(fnval,FNVALL,
9441                              "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9442                   goto fnend;
9443               }
9444           }
9445           line = VscrnGetLineFromTop( VTERM, (USHORT) row );
9446           if (line != NULL) {
9447               if (bp[1] == NULL || bp[1][0] == '\0')
9448                 col = 0;
9449               else {
9450                   if (chknum(bp[0])) {
9451                       col = atoi(bp[1]);
9452                       if (col < 0 || col >= line->width)
9453                         col = 0;
9454                   } else {
9455                       failed = 1;
9456                       if (fndiags)
9457                         ckmakmsg(fnval,FNVALL,
9458                                  "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9459                       goto fnend;
9460                   }
9461               }
9462               if (bp[2] == NULL || bp[2][0] == '\0') {
9463                   len = line->width - (col+1);
9464               } else {
9465                   if (!chknum(bp[2])) {
9466                       failed = 1;
9467                       if (fndiags)
9468                         ckmakmsg(fnval,FNVALL,
9469                                  "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9470                       goto fnend;
9471                   }
9472                   len = atoi(bp[2]);
9473                   if (len < 0 || len > line->width)
9474                     len = line->width;
9475               }
9476               cells = line->cells;
9477               for (i = 0; i < len; i++) {
9478                   int pos = i + col;
9479                   if (pos < line->width) {
9480                       if (isunicode())
9481                         fnval[i] = (CHAR) utolxlat(cells[pos].c);
9482                       else
9483                         fnval[i] = (CHAR) (cells[pos].c & 0xFF);
9484                       if (fnval[i] == 0)
9485                         fnval[i] = SP;
9486                   } else
9487                     fnval[i] = SP;
9488               }
9489               fnval[i] = '\0';
9490           } else {
9491               fnval[0] = '\0';
9492           }
9493           p = fnval;
9494           goto fnend;
9495       }
9496 #endif /* OS2 */
9497 #endif /* NOLOCAL */
9498
9499 #ifndef NOPUSH
9500       case FN_RAW:                      /* \frawcommand() */
9501       case FN_CMD: {                    /* \fcommand() */
9502           int x, c, n = FNVALL;
9503           x = 0;                        /* Completion flag */
9504 /*
9505   ZIFILE can be safely used because we can't possibly be transferring a file
9506   while executing this function.
9507 */
9508           if (!nopush && zxcmd(ZIFILE,bp[0]) > 0) { /* Open the command */
9509               while (n-- > -1) {        /* Read from it */
9510                   if ((c = zminchar()) < 0) {
9511                       x = 1;             /* EOF - set completion flag */
9512                       if (cx == FN_CMD) { /* If not "rawcommand" */
9513                           p--;           /* remove trailing newlines */
9514                           while (*p == CR || *p == LF)
9515                             p--;
9516                           p++;
9517                       }
9518                       *p = NUL;         /* Terminate the string */
9519                       break;
9520                   } else                /* Command still running */
9521                     *p++ = c;           /* Copy the bytes */
9522               }
9523               zclose(ZIFILE);           /* Close the command */
9524           }
9525           /* Return null string if command's output was too long. */
9526           p = fnval;
9527           if (!x) {
9528               failed = 1;
9529               if (fndiags)
9530                 ckmakmsg(fnval,FNVALL,
9531                          "<ERROR:RESULT_TOO_LONG:\\f",fn,"()>",NULL);
9532           }
9533           goto fnend;
9534       }
9535 #endif /* NOPUSH */
9536     } /* Break up big switch... */
9537
9538     switch (y) {
9539       case FN_STX:                      /* \fstripx(string,c) */
9540         if (!(s = bp[0]))               /* Make sure there is a string */
9541           goto fnend;
9542         c = '.';                        /* Character to strip from */
9543         if (argn > 1) if (*bp[1]) c = *bp[1];
9544         n = ckstrncpy(fnval,bp[0],FNVALL);
9545         while (--n >= 0) {
9546             if (fnval[n] == c) {
9547                 fnval[n] = NUL;
9548                 break;
9549             }
9550         }
9551         p = fnval;
9552         goto fnend;
9553
9554       case FN_STL:                      /* \flop(string,c) */
9555       case FN_LOPX: {                   /* \flopx(string,c) */
9556           int n = 1;
9557           if (!(s = bp[0]))             /* Make sure there is a string */
9558             goto fnend;
9559           c = '.';                      /* Character to strip to */
9560           if (argn > 1) if (*bp[1]) c = *bp[1];
9561           if (argn > 2) if (*bp[2]) {
9562 #ifndef NOFLOAT
9563               n = 0;
9564               if (isfloat(bp[2],0)) {
9565                   n = (int)floatval;
9566                   if (n < 0) n = 0;
9567               } else
9568 #endif  /* NOFLOAT */
9569                 n = atoi(bp[2]);
9570           }
9571           x = 0;
9572           if (cx == FN_LOPX) {          /* Lopx (from right) */
9573               if (n == 0)
9574                 goto fnend;
9575               s += strlen(s) - 1;       /* We already know it's > 0 */
9576               while (s-- >= bp[0]) {
9577                   if (*s == c) {
9578                       n--;
9579                       if (n == 0) {
9580                           s++;
9581                           x = 1;
9582                           break;
9583                       }
9584                   }
9585               }
9586               if (!x) s = "";
9587           } else {                      /* Lop (from left) */
9588               if (n == 0) {
9589                   p = bp[0];
9590                   goto fnend;
9591               }
9592               while (*s++) {
9593                   if (*(s-1) == c) {
9594                       if (--n == 0) {
9595                           x = 1;
9596                           break;
9597                       }
9598                   }
9599               }
9600               if (!x) s = bp[0];
9601           }
9602           ckstrncpy(fnval,s,FNVALL);
9603           p = fnval;
9604           goto fnend;
9605       }
9606       case FN_STN:                      /* \fstripn(string,n) */
9607         if (argn < 1)                   /* Remove n chars from right */
9608           goto fnend;
9609         val1 = "0";
9610         if (argn > 1)
9611           if (*(bp[1]))
9612             val1 = evalx(bp[1]);
9613         if (!chknum(val1)) {
9614             failed = 1;
9615             evalerr(fn);
9616             goto fnend;
9617         }
9618         n = atoi(val1);
9619         if (n < 0) n = 0;
9620         k = (int)strlen(s = bp[0]) - n;
9621         if (k < 0) k = 0;
9622         p = fnval;
9623         while (k-- > 0)
9624           *p++ = *s++;
9625         *p = NUL;
9626         p = fnval;
9627         goto fnend;
9628
9629       case FN_STB: {                    /* \fstripb(string,c) */
9630           char c2 = NUL;
9631           int i, k = 0;
9632           char * gr_opn = "\"{'([<";    /* Group open brackets */
9633           char * gr_cls = "\"}')]>";    /* Group close brackets */
9634
9635           p = fnval;
9636           *p = NUL;
9637           if (!(s = bp[0]))             /* Make sure there is a string */
9638             goto fnend;
9639           if ((x = strlen(s)) < 1)
9640             goto fnend;
9641           c = NUL;                      /* Brace/bracket kind */
9642           if (argn > 1) {
9643               if (*bp[1]) {
9644                   if (chknum(bp[1])) {
9645                       k = atoi(bp[1]);
9646                       if (k < 0) k = 63;
9647                       for (i = 0; i < 6; i++) {
9648                           if (k & (1<<i)) {
9649                               if (s[0] == gr_opn[i] && s[x-1] == gr_cls[i]) {
9650                                   ckstrncpy(fnval,s+1,FNVALL);
9651                                   fnval[x-2] = NUL;
9652                                   goto fnend;
9653                               }
9654                           }
9655                       }
9656                       ckstrncpy(fnval,s,FNVALL); /* No match */
9657                       goto fnend;
9658                   }
9659               }
9660           }
9661           c = !bp[1] ? 0 : *bp[1];
9662           if (!c) c = s[0];
9663           if (argn > 2) if (*bp[2]) c2 = *bp[2];
9664           if (*s == c) {
9665               if (!c2) {
9666                   switch (c) {
9667                     case '(': c2 = ')'; break;
9668                     case '[': c2 = ']'; break;
9669                     case '{': c2 = '}'; break;
9670                     case '<': c2 = '>'; break;
9671                     case '"': c2 = '"'; break;
9672                     case 39:  c2 = 39;  break;
9673                     case 96:  c2 = 39;  break;
9674                     default:
9675                       if (argn == 2) {
9676                           c2 = c;
9677                       } else {
9678                           strncpy(fnval,s,x); /* Leave it like this */
9679                           fnval[x] = NUL;
9680                           goto fnend;
9681                       }
9682                   }
9683               }
9684               if (s[x-1] == c2) {
9685                   strncpy(fnval,s+1,x-2); /* Leave it like this */
9686                   fnval[x-2] = NUL;
9687                   goto fnend;
9688               }
9689           }
9690           strncpy(fnval,s,x);
9691           fnval[x] = NUL;
9692           goto fnend;
9693       }
9694
9695       case FN_2HEX:                     /* Number to hex */
9696       case FN_2OCT:                     /* Number to octal */
9697         val1 = evalx(bp[0]);
9698         if (!*val1) {
9699             failed = 1;
9700             evalerr(fn);
9701             goto fnend;
9702         }
9703         sprintf(fnval, cx == FN_2HEX ? "%lx" : "%lo", atol(val1)); /* SAFE */
9704         if (cx == FN_2HEX && (int)(strlen(fnval)&1))
9705           sprintf(fnval,"0%lx",atol(val1)); /* SAFE */
9706         p = fnval;
9707         goto fnend;
9708
9709       case FN_DNAM: {                   /* Directory part of file name */
9710           char *s;
9711           zfnqfp(bp[0],FNVALL,p);       /* Get full name */
9712           if (!isdir(p)) {              /* Is it already a directory? */
9713               zstrip(p,&s);             /* No get basename */
9714               if (*s) {
9715                   x = ckindex(s,p,0,0,0); /* Pos of latter in former */
9716                   if (x > 0) p[x-1] = NUL;
9717               }
9718           }
9719           if (!p) p = "";
9720           goto fnend;
9721       }
9722
9723 #ifndef NORANDOM
9724       case FN_RAND:                     /* Random number */
9725 #ifdef CK_SSL
9726         if (RAND_bytes((unsigned char *)&k,sizeof(k)) < 0)
9727 #endif /* CK_SSL */
9728           k = rand();
9729         x = 0;
9730         if (argn > 0) {
9731             if (!chknum(bp[0])) {
9732                 failed = 1;
9733                 if (fndiags)
9734                   ckmakmsg(fnval,FNVALL,
9735                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9736                 goto fnend;
9737             }
9738             x = atoi(bp[0]);
9739         }
9740 #ifdef COMMENT
9741         sprintf(fnval,"%d", (x > 0 && k > 0) || (x < 0 && k < 0) ? k % x : 
9742                 (x == 0 ? 0 : (0 - (k % (-x)))));
9743 #else
9744         debug(F111,"rand",ckitoa(x),k);
9745 #ifdef SUNOS4
9746 /* This is really strange but on SunOS, if we are requesting random numbers */
9747 /* between 0 and 4 or less, they always come out in sequence: 0 1 2 3 0 1 2 */
9748 /* Shifting the result of rand() in this case gives a more random result.   */
9749         if (x < 5)
9750           k = k >> 5;
9751 #endif /* SUNOS4 */
9752         if ((x > 0 && k > 0) || (x < 0 && k < 0))
9753           x = k % x;
9754         else if (x == 0)
9755           x = 0;
9756         else
9757           x = 0 - (k % (-x));
9758         debug(F101,"rand x","",x);
9759         sprintf(fnval,"%d", x);         /* SAFE */
9760 #endif /* COMMENT */
9761         p = fnval;
9762         goto fnend;
9763 #endif /* NORANDOM */
9764     } /* Break up big switch... */
9765
9766     switch (y) {
9767       case FN_SPLIT:                    /* \fsplit(s1,a,s2,s3,mask) */
9768       case FN_WORD: {                   /* \fword(s1,n,s2,s3,mask) */
9769           int wordnum = 0;
9770           int splitting = 0;
9771           int x;
9772           int array = 0;
9773           int grouping = 0;
9774           int nocollapse = 0;
9775           char * sep = "";
9776           char * notsep = "";
9777           char * bp0 = NULL;
9778           char * bp1 = NULL;
9779           char   abuf[16];
9780           struct stringarray * q = NULL;
9781
9782           splitting = (cx == FN_SPLIT); /* Our job */
9783           debug(F101,"FN_SPLIT splitting","",splitting);
9784
9785           fnval[0] = splitting ? '0' : NUL; /* Initial return value */
9786           fnval[1] = NUL;
9787           p = fnval;
9788           bp0 = bp[0];                  /* Source string */
9789           if (!bp0) bp0 = "";
9790           debug(F111,"fsplit bp[0]",bp0,argn);
9791           if (argn < 1 || !*bp0)        /* If none, return default value */
9792             goto fnend;
9793
9794           bp1 = bp[1];                  /* Function-dependent arg */
9795           if (!bp1) bp1 = "";           /* (array or number) */
9796           debug(F110,"fsplit bp[1]",bp1,0);
9797           if (bp[5]) {
9798               if (!chknum(bp[5])) {
9799                   failed = 1;
9800                   ckmakmsg(fnval,FNVALL,
9801                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9802                   goto fnend;
9803               }
9804               x = atoi(bp[5]);
9805               nocollapse = x;
9806           }
9807           if (!splitting) {             /* \fword(): n = desired word number */
9808               val1 = "1";               /* Default is first word */
9809               if (argn > 1)             /* Word number supplied */
9810                 if (*bp1)
9811                   val1 = evalx(bp1);
9812               if (!chknum(val1)) {
9813                   failed = 1;
9814                   ckmakmsg(fnval,FNVALL,
9815                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9816                   goto fnend;
9817               }
9818               n = atoi(val1);
9819           } else if (argn > 1 && *bp1) { /* \fsplit(): n = word count */
9820               ckstrncpy(abuf,bp1,16);   /* Get array reference */
9821               debug(F110,"fsplit abuf 1",abuf,0);
9822               failed = 1;               /* Assume it's bad */
9823               if (fndiags)              /* Default is this error message */
9824                 ckmakmsg(fnval,FNVALL,
9825                          "<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
9826               if (abuf[0] != '&')       /* "Address" of array */
9827                 goto fnend;             /* It's bad */
9828               if (abuf[2]) {            /* Check for brackets */
9829                   if (abuf[2] != '[' || abuf[3] != ']') {
9830                       goto fnend;       /* Bad */
9831                   }
9832               }
9833               debug(F110,"fsplit abuf 2",abuf,0);
9834               if (abuf[1] > 64 && abuf[1] < 91) /* Convert upper to lower */
9835                 abuf[1] += 32;
9836               if (abuf[1] < 97 || abuf[1] > 122) { /* Check for a-z */
9837                   goto fnend;
9838               }
9839               debug(F110,"fsplit abuf 3",abuf,0);
9840               array = 1;
9841               fnval[0] = NUL;           /* No error, erase message */
9842               failed = 0;               /* Unset failure flag */
9843               n = 0;                    /* Initialize word counter */
9844           }
9845           if (argn > 2)                 /* Have break set? */
9846             sep = bp[2];
9847           debug(F111,"fsplit sep",sep,argn);
9848           if (argn > 3)                 /* Have include set? */
9849             notsep = bp[3];
9850           debug(F111,"fsplit notsep",notsep,argn);
9851           if (argn > 4) {               /* Have grouping set? */
9852               char * bp4 = bp[4];
9853               debug(F111,"fsplit bp4",bp4,argn);
9854               if (!bp4) bp4 = "0";
9855               if (!*bp4) bp4 = "0";
9856               if (chknum(bp4)) {
9857                   grouping = atoi(bp4);
9858                   if (grouping == -1)
9859                     grouping = 127;
9860               } else {
9861                   failed = 1;
9862                   if (fndiags)
9863                     ckmakmsg(fnval,FNVALL,
9864                              "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9865                   goto fnend;
9866               }
9867           }
9868           /* Args parsed, now do the work */
9869
9870           debug(F111,"fsplit bp0",bp0,n);
9871           q = cksplit(splitting,n,bp0,sep,notsep,grouping,0,nocollapse);
9872
9873           wordnum = q ? q->a_size : -1; /* Check result */
9874           if (wordnum < 0) {
9875               failed = 1;               /* Failure */
9876               if (fndiags)
9877                 ckmakmsg(fnval,FNVALL,
9878                          (wordnum == -1) ?
9879                          "<ERROR:MALLOC_FAILURE:\\f" :
9880                          "<ERROR:TOO_MANY_WORDS:\\f",
9881                          fn,
9882                          "()>",
9883                          NULL
9884                          );
9885               goto fnend;
9886           }
9887           if (splitting) {              /* \fsplit() result */
9888               ckstrncpy(fnval,ckitoa(wordnum),FNVALL);
9889               if (array) {              /* Array was not declared. */
9890                   int i;
9891                   if ((x = dclarray(abuf[1],wordnum)) < 0) { /* Declare it. */
9892                       failed = 1;
9893                       if (fndiags)
9894                         ckmakmsg(fnval,FNVALL,
9895                                  "<ERROR:MALLOC_FAILURE:\\f",fn,"()>",NULL);
9896                       goto fnend;
9897                   }
9898                   for (i = 1; i <= wordnum; i++) { /* Copy results */
9899                       makestr(&(a_ptr[x][i]),q->a_head[i]);
9900                   }
9901                   a_ptr[x][0] = NULL;   /* Array is 1-based */
9902                   makestr(&(a_ptr[x][0]),fnval); /* Element = size */
9903               }
9904           } else {                      /* \fword() result */
9905               char * s;
9906               s = q->a_head[1];
9907               if (!s) s = "";
9908               ckstrncpy(fnval,s,FNVALL);
9909           }
9910           goto fnend;                   /* Done */
9911       }
9912
9913     } /* Break up big switch... */
9914
9915     switch (y) {
9916
9917 #ifdef CK_KERBEROS
9918       case FN_KRB_TK:                   /* Kerberos tickets */
9919       case FN_KRB_NX:                   /* Kerberos next ticket */
9920       case FN_KRB_IV:                   /* Kerberos ticket is valid */
9921       case FN_KRB_FG:                   /* Kerberos Ticket flags */
9922       case FN_KRB_TT: {                 /* Kerberos ticket time */
9923           int kv = 0;                   /* Kerberos version */
9924           int n = 0;
9925           char * s = NULL;
9926           if (rdigits(bp[0])) {
9927               kv = atoi(bp[0]);
9928           } else {
9929               failed = 1;
9930               if (fndiags)
9931                 ckmakmsg(fnval,FNVALL,
9932                          "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
9933               goto fnend;
9934           }
9935           if (kv != 4 && kv != 5) {
9936               failed = 1;
9937               if (fndiags)
9938                 ckmakmsg(fnval,FNVALL,
9939                          "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
9940               goto fnend;
9941           }
9942           if ((cx == FN_KRB_IV || cx == FN_KRB_TT || cx == FN_KRB_FG) &&
9943                argn < 2) {
9944               failed = 1;
9945               if (fndiags)
9946                 ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
9947               goto fnend;
9948           }
9949           switch (y) {
9950             case FN_KRB_TK:             /* Number of Kerberos tickets */
9951 #ifdef CK_AUTHENTICATION
9952               switch (kv) {
9953                 case 4:
9954                   n = ck_krb4_get_tkts();
9955                   sprintf(fnval, "%d", (n >= 0) ? n : 0); /* SAFE */
9956                   goto fnend;
9957                 case 5: {
9958                     extern char * krb5_d_cc;
9959                     n = ck_krb5_get_tkts(krb5_d_cc);
9960                     sprintf(fnval, "%d", (n >= 0) ? n : 0); /* SAFE */
9961                     goto fnend;
9962                 }
9963               }
9964 #else
9965               sprintf(fnval,"%d",0);    /* SAFE */
9966 #endif /* CK_AUTHENTICATION */
9967               goto fnend;
9968
9969             case FN_KRB_NX:             /* Kerberos next ticket */
9970 #ifdef CK_AUTHENTICATION
9971               switch (kv) {
9972                 case 4:
9973                   s = ck_krb4_get_next_tkt();
9974                   ckstrncpy(fnval, s ? s : "",FNVALL);
9975                   goto fnend;
9976                 case 5:
9977                   s = ck_krb5_get_next_tkt();
9978                   ckstrncpy(fnval, s ? s : "",FNVALL);
9979                   goto fnend;
9980               }
9981 #else
9982               sprintf(fnval,"k%d next-ticket-string",kv); /* SAFE */
9983 #endif /* CK_AUTHENTICATION */
9984               goto fnend;
9985
9986             case FN_KRB_IV:             /* Kerberos ticket is valid */
9987 #ifdef CK_AUTHENTICATION
9988               /* Return 1 if valid, 0 if not */
9989               switch (kv) {
9990                 case 4:
9991                   n = ck_krb4_tkt_isvalid(bp[1]);
9992                   sprintf(fnval, "%d", n > 0 ? 1 : 0); /* SAVE */
9993                   goto fnend;
9994                 case 5: {
9995                     extern char * krb5_d_cc;
9996                     n = ck_krb5_tkt_isvalid(krb5_d_cc,bp[1]);
9997                     sprintf(fnval,"%d", n > 0 ? 1 : 0); /* SAFE */
9998                     goto fnend;
9999                 }
10000               }
10001 #else
10002               sprintf(fnval,"%d",0);    /* SAFE */
10003 #endif /* CK_AUTHENTICATION */
10004               goto fnend;
10005
10006             case FN_KRB_TT:             /* Kerberos ticket time */
10007 #ifdef CK_AUTHENTICATION
10008               switch (kv) {
10009                 case 4:
10010                   n = ck_krb4_tkt_time(bp[1]);
10011                   sprintf(fnval,"%d", n >= 0 ? n : 0); /* SAFE */
10012                   goto fnend;
10013                 case 5: {
10014                     extern char * krb5_d_cc;
10015                     n = ck_krb5_tkt_time(krb5_d_cc,bp[1]);
10016                     sprintf(fnval,"%d", n >= 0 ? n : 0); /* SAFE */
10017                     goto fnend;
10018                 }
10019               }
10020 #else
10021               ckstrncpy(fnval,"600",FNVALL); /* Some time */
10022 #endif /* CK_AUTHENTICATION */
10023               goto fnend;
10024
10025             case FN_KRB_FG:             /* Kerberos ticket flags */
10026 #ifdef CK_AUTHENTICATION
10027               switch (kv) {
10028                 case 4:
10029                   fnval[0] = '\0';
10030                   goto fnend;
10031                 case 5: {
10032                     extern char * krb5_d_cc;
10033                     ckstrncpy(fnval,ck_krb5_tkt_flags(krb5_d_cc,bp[1]),FNVALL);
10034                     goto fnend;
10035                 }
10036               }
10037 #else
10038               fnval[0] = '\0';
10039 #endif /* CK_AUTHENTICATION */
10040               goto fnend;
10041           }
10042           p = fnval;
10043           goto fnend;
10044       }
10045 #endif /* CK_KERBEROS */
10046
10047 #ifdef FN_ERRMSG
10048       case FN_ERRMSG:
10049         if (rdigits(bp[0])) {
10050             k = atoi(bp[0]);
10051         } else {
10052             failed = 1;
10053             if (fndiags)
10054              ckmakmsg(fnval,FNVALL,"<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10055             goto fnend;
10056         }
10057 #ifdef VMS
10058         ckstrncpy(fnval,ckvmserrstr(k),FNVALL);
10059 #else
10060         x = errno;
10061         errno = k;
10062         ckstrncpy(fnval,ck_errstr(),FNVALL);
10063         errno = x;
10064 #endif /* VMS */
10065         p = fnval;
10066         goto fnend;
10067 #endif /* FN_ERRMSG */
10068
10069       case FN_DIM: {
10070           int max;
10071           char abuf[16], *s;
10072           fnval[0] = NUL;               /* Initial return value */
10073           ckstrncpy(abuf,bp[0],16);     /* Get array reference */
10074           s = abuf;
10075           if (*s == CMDQ) s++;
10076           failed = 1;                   /* Assume it's bad */
10077           p = fnval;                    /* Point to result */
10078           if (fndiags)                  /* Default is this error message */
10079             ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
10080           if (s[0] != '&') {            /* "Address" of array */
10081               goto fnend;
10082           }
10083           if (s[2]) {
10084               if (s[2] != '[' || s[3] != ']') {
10085                   goto fnend;
10086               }
10087           }
10088           if (s[1] >= 64 && s[1] < 91)  /* Convert upper to lower */
10089             s[1] += 32;
10090           if (s[1] < 95 || s[1] > 122) { /* Check for a-z */
10091               goto fnend;                /* Bad */
10092           }
10093           if ((max = chkarray(s[1],1)) < 1) /* (second arg was 1) */
10094             max = 0;
10095           failed = 0;                   /* Unset failure flag */
10096           sprintf(fnval,"%d",max);      /* SAFE */
10097           goto fnend;
10098       }
10099
10100     } /* Break up big switch... */
10101
10102     switch (y) {
10103       case FN_JDATE:
10104         if (argn < 1)                   /* Check number of args */
10105           p = ckdate();                 /* None, get today's date-time */
10106         else                            /* Some */
10107           p = bp[0];                    /* Use first */
10108         p = ckcvtdate(p,0);             /* Convert to standard form */
10109         ckstrncpy(fnval,zjdate(p),FNVALL); /* Convert to Julian */
10110         p = fnval;                      /* Point to result */
10111         failed = 0;
10112         if (*p == '-') {
10113             failed = 1;
10114             if (fndiags)                /* Default is this error message */
10115               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_DATE:\\f",fn,"()>",NULL);
10116         }
10117         goto fnend;
10118
10119       case FN_DATEJ:
10120         ckstrncpy(fnval,jzdate(bp[0]),FNVALL); /* Convert to yyyy<dayofyear> */
10121         p = fnval;                      /* Point to result */
10122         failed = 0;
10123         if (*p == '-') {
10124             failed = 1;
10125             if (fndiags)                /* Default is this error message */
10126               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_DATE:\\f",fn,"()>",NULL);
10127         }
10128         goto fnend;
10129
10130       case FN_DTIM:                     /* \fcvtdate() */
10131       case FN_TIME:                     /* Free-format time to hh:mm:ss */
10132       case FN_NTIM:                     /* Time to sec since midnight */
10133         s = (argn > 0) ? bp[0] : "";
10134         if (!s) s = "";
10135         if (!*s)
10136           p = ckdate();                 /* None, get today's date */
10137         else                            /* Some */
10138           p = bp[0];                    /* Use first */
10139         {
10140             char * s;
10141             s = p;
10142             while (*s) {
10143                 if (*s < 32) {
10144                     *s = NUL;
10145                     break;
10146                 }
10147                 s++;
10148             }
10149             /* do { if (*s < '!') *s = NUL; break; } while (*s++); */
10150         }
10151         p = ckcvtdate(p,2);             /* Convert to standard form */
10152         if (*p == '<') {
10153             failed = 1;
10154             if (fndiags)                /* Default is this error message */
10155               ckmakmsg(fnval,FNVALL,
10156                        "<ERROR:ARG_BAD_DATE_OR_TIME:\\f",fn,"()>",NULL);
10157             p = fnval;
10158             goto fnend;
10159         }
10160         if (argn > 1) {                 /* Format code */
10161             s = bp[1];
10162             if (!s) s = "";
10163             if (!*s) s = "0";
10164             if (!chknum(s)) {
10165                 failed = 1;
10166                 if (fndiags)
10167                   ckmakmsg(fnval,FNVALL,
10168                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10169                 p = fnval;
10170                 goto fnend;
10171             }
10172             x = atoi(s);
10173             /* if (x) */ p = shuffledate(p,x);
10174         }
10175         if (cx == FN_TIME) {
10176             p += 9;
10177         } else if (cx == FN_NTIM) {
10178             long sec = 0L;
10179             p[11] = NUL;
10180             p[14] = NUL;
10181             sec = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15);
10182             sprintf(fnval,"%ld",sec);   /* SAFE */
10183             p = fnval;
10184         }
10185         goto fnend;
10186
10187       case FN_MJD:                      /* Modified Julian Date */
10188         if (argn < 1)                   /* Check number of args */
10189           p = zzndate();                /* None, get today's date-time */
10190         else                            /* Some */
10191           p = bp[0];                    /* Use first */
10192         p = ckcvtdate(p,0);             /* Convert to standard form */
10193         if (*p == '-') {
10194             failed = 1;
10195             if (fndiags)                /* Default is this error message */
10196               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_DATE:\\f",fn,"()>",NULL);
10197             goto fnend;
10198         }
10199         /* Convert to modified Julian date */
10200         sprintf(fnval,"%ld",mjd(p));    /* SAFE */
10201         p = fnval;                      /* Point to result */
10202         goto fnend;
10203
10204       case FN_MJD2: {
10205           long k = 0L;
10206           int n = 0;
10207           p = evalx(bp[0]);
10208           if (*p == '-') {
10209               p++;
10210               n = 1;
10211           }
10212           if (!rdigits(p)) {
10213               failed = 1;
10214               evalerr(fn);
10215               p = fnval;
10216               goto fnend;
10217           } else {
10218               k = atol(p);
10219               if (n) k = -k;
10220           }
10221           ckstrncpy(fnval,mjd2date(k),FNVALL); /* Convert to Date */
10222           p = fnval;                    /* Point to result */
10223           failed = 0;
10224           goto fnend;
10225       }
10226
10227 #ifndef NODIAL
10228       case FN_PNCVT: {                  /* Convert phone number */
10229           extern char * pncvt();
10230           failed = 0;
10231           p = pncvt(bp[0]);
10232           if (!p) p = "";
10233           if (!*p) {
10234             failed = 1;
10235             if (fndiags)                /* Default is this error message */
10236               ckmakmsg(fnval,FNVALL,
10237                        "<ERROR:ARG_BAD_PHONENUM:\\f",fn,"()>",NULL);
10238         }
10239         goto fnend;
10240       }
10241 #endif /* NODIAL */
10242
10243       case FN_DAY:
10244       case FN_NDAY:
10245         if (argn < 1)                   /* Check number of args */
10246           p = zzndate();                /* None, get today's date-time */
10247         else                            /* Some */
10248           p = bp[0];                    /* Use first */
10249         p = ckcvtdate(p,0);             /* Convert to standard form */
10250         if (*p == '-') {
10251             failed = 1;
10252             if (fndiags)                /* Default is this error message */
10253               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_DATE:\\f",fn,"()>",NULL);
10254             goto fnend;
10255         }
10256         failed = 0;
10257         z = mjd(p);                     /* Convert to modified Julian date */
10258         z = z % 7L;
10259         if (z < 0) {
10260             z = 0 - z;
10261             k = 6 - ((int)z + 3) % 7;
10262         } else {
10263             k = ((int)z + 3) % 7;       /* Day of week */
10264         }
10265         p = fnval;                      /* Point to result */
10266         if (cx == FN_NDAY)
10267           sprintf(fnval,"%d",k);        /* SAFE */
10268         else
10269           ckstrncpy(fnval,wkdays[k],FNVALL);
10270         goto fnend;
10271
10272       case FN_N2TIM: {                  /* Sec since midnight to hh:mm:ss */
10273           long k = 0L;
10274           int n = 0, hh, mm, ss;
10275           char * s = bp[0];
10276           if (argn < 1)                 /* If no arg substitute 0 */
10277             s = "0";
10278           p = evalx(s);                 /* Evaluate expression silently */
10279           if (*p == '-') {              /* Check result for minus sign */
10280               p++;
10281               n = 1;
10282           }
10283           if (!rdigits(p)) { /* Check for numeric */
10284               failed = 1;
10285               ckmakmsg(fnval,FNVALL,
10286                        "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10287               p = fnval;
10288               goto fnend;
10289           } else {
10290               k = atol(p);
10291               if (n) k = -k;
10292           }
10293           if (k < 0) {                  /* Check for negative */
10294               failed = 1;
10295               if (fndiags)
10296                 ckmakmsg(fnval,FNVALL,
10297                          "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
10298               p = fnval;
10299               goto fnend;
10300           }
10301           hh = k / 3600L;               /* Have positive number */
10302           mm = (k % 3600L) / 60L;       /* break it down... */
10303           ss = ((k % 3600L) % 60L);
10304
10305           sprintf(fnval,"%02d:%02d:%02d",hh,mm,ss); /* SAFE */
10306           p = fnval;
10307           failed = 0;
10308           goto fnend;
10309       }
10310
10311       case FN_PERM: {                   /* File permissions */
10312           p = fnval;
10313           z = zchki(bp[0]);
10314           if (z < 0) {
10315               failed = 1;
10316               if (fndiags) {
10317                   if (z == -1)
10318                     ckmakmsg(fnval,FNVALL,
10319                              "<ERROR:FILE_NOT_FOUND:\\f",fn,"()>",NULL);
10320                   else if (z == -2)
10321                     ckmakmsg(fnval,FNVALL,
10322                              "<ERROR:FILE_NOT_READABLE:\\f",fn,"()>",NULL);
10323                   else if (z == -3)
10324                     ckmakmsg(fnval,FNVALL,
10325                              "<ERROR:FILE_NOT_ACCESSIBLE:\\f",fn,"()>",NULL);
10326                   else
10327                     ckmakmsg(fnval,FNVALL,
10328                              "<ERROR:FILE_ERROR:\\f",fn,"()>",NULL);
10329               }
10330               goto fnend;
10331           }
10332 #ifdef CK_PERMS
10333           ckstrncpy(fnval,ziperm(bp[0]),FNVALL);
10334 #else
10335           ckstrncpy(fnval,"(unknown)",FNVALL);
10336 #endif /* CK_PERMS */
10337           goto fnend;
10338       }
10339       case FN_TLOOK:                    /* tablelook() */
10340       case FN_ALOOK: {                  /* arraylook() */
10341           int i, x, hi, lo, max, cmdlen;
10342           char abuf[16], *s, *pat;
10343           char kwbuf[256];
10344           char delim = ':';
10345           failed = 1;                   /* Assume failure */
10346           ckstrncpy(fnval,"-1",FNVALL);
10347           pat = bp[0];                  /* Point to search pattern */
10348           if (!pat) pat = "";           /* Watch out for NULL pointer */
10349           cmdlen = strlen(pat);         /* Get pattern length */
10350           if (argn < 2 /* || cmdlen < 1 */ ) { /* Need two args */
10351               if (fndiags)
10352                 ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
10353               goto fnend;
10354           }
10355           ckstrncpy(abuf,bp[1],16);     /* Get array reference */
10356           if (argn > 2)
10357             delim = *(bp[2]);
10358           s = abuf;
10359           if ((x = arraybounds(s,&lo,&hi)) < 0) { /* Get index and bounds */
10360               if (fndiags)
10361                ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
10362               goto fnend;
10363           }
10364           p = fnval;                    /* Point to result */
10365           max = a_dim[x];               /* Size of array */
10366           if (lo < 0) lo = 0;           /* Use given range if any */
10367           if (lo > max) lo = max;
10368           if (hi < 0) hi = max;
10369           if (hi > max) hi = max;
10370           failed = 0;                   /* Unset failure flag */
10371           if (max < 1)
10372             goto fnend;
10373           kwbuf[255] = NUL;
10374           for (i = lo; i <= hi; i++) {
10375               if (!a_ptr[x][i])
10376                 continue;
10377               if (cx == FN_ALOOK) {
10378                   if (ckmatch(pat,a_ptr[x][i],inpcas[cmdlvl],1+4)) {
10379                       sprintf(fnval,"%d",i); /* SAFE */
10380                       goto fnend;
10381                   }
10382               } else if (cx == FN_TLOOK) {
10383                   char * aa;
10384                   int j = 0, v = 0, len;
10385                   if (i == hi)
10386                     break;
10387                   aa = a_ptr[x][i];     /* Point to this array element */
10388                   if (!aa) aa = "";
10389                   while (j < 254 && *aa) { /* Isolate keyword */
10390                       if (*aa == delim)
10391                         break;
10392                       kwbuf[j++] = *aa++;
10393                   }
10394                   kwbuf[j] = NUL;
10395                   len = j;
10396                   v = 0;
10397                   if ((len == cmdlen && !ckstrcmp(kwbuf,pat,len,0)) ||
10398                       ((v = !ckstrcmp(kwbuf,pat,cmdlen,0)) &&
10399                        ckstrcmp(a_ptr[x][i+1],pat,cmdlen,0))) {
10400                       sprintf(fnval,"%d",i); /* SAFE */
10401                       goto fnend;
10402                   }
10403                   if (v) {              /* Ambiguous */
10404                       ckstrncpy(fnval,"-2",FNVALL);
10405                       goto fnend;
10406                   }
10407               }
10408           }
10409           if (cx == FN_TLOOK) {         /* tablelook() last element */
10410               ckstrncpy(fnval,"-1",FNVALL);
10411               if (!ckstrcmp(a_ptr[x][hi],pat,cmdlen,0))
10412                 sprintf(fnval,"%d",hi); /* SAFE */
10413           }
10414           goto fnend;
10415       }
10416       case FN_TOB64:                    /* Base-64 conversion */
10417       case FN_FMB64:
10418         p = fnval;
10419         *p = NUL;
10420         if (argn < 1)
10421           goto fnend;
10422         if (cx == FN_TOB64) {
10423             x = b8tob64(bp[0],-1,fnval,FNVALL);
10424         } else {
10425             x = strlen(bp[0]);
10426             if (x % 4) {                /* length must be multiple of 4 */
10427                 failed = 1;
10428                 ckmakmsg(fnval,FNVALL,
10429                          "<ERROR:ARG_INCOMPLETE:\\f",fn,"()>",NULL);
10430                 goto fnend;
10431             }
10432             b64tob8(NULL,0,NULL,0);     /* Reset */
10433             x = b64tob8(bp[0],-1,fnval,FNVALL);
10434             b64tob8(NULL,0,NULL,0);     /* Reset again */
10435         }
10436         if (x < 0) {
10437             failed = 1;
10438             if (fndiags) {
10439                 char * m = "INTERNAL_ERROR";
10440                 switch (x) {
10441                   case -1: m = "ARG_TOO_LONG"; break;
10442                   case -2: m = "ARG_OUT_OF_RANGE"; break;
10443                 }
10444                 if (ckmakmsg(fnval,FNVALL,"<ERROR:",m,"\\f",fn) > 0)
10445                   ckstrncat(fnval,"()>",FNVALL);
10446             }
10447         }
10448         goto fnend;
10449
10450       case FN_ABS: {
10451           char * s;
10452           s = bp[0];
10453           if (*s == '-' || *s == '+')
10454             s++;
10455           if (!rdigits(s)) {
10456               if (fndiags)
10457                 ckmakmsg(fnval,FNVALL,
10458                          "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10459               goto fnend;
10460           }
10461           ckstrncpy(fnval,s,FNVALL);
10462           goto fnend;
10463       }
10464
10465       case FN_AADUMP: {
10466           char abuf[16], *s = NULL, **ap = NULL, **vp = NULL;
10467           char pattern[VNAML];
10468           int slen, i, j, k, first = -1;
10469           extern int xdelmac();
10470           p = fnval;
10471           if (argn < 2) {
10472               if (fndiags)
10473                 ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG2:\\f",fn,"()>",NULL);
10474               goto fnend;
10475           }
10476           debug(F101,"aaconvert argn","",argn);
10477           s = bp[0];
10478           slen = strlen(s);
10479
10480           /* Count elements so we can create the array */
10481
10482           ckmakmsg(pattern,VNAML,s,"<*>",NULL,NULL);
10483           for (k = 0, i = 0; i < nmac; i++) {
10484               if (ckmatch(pattern,mactab[i].kwd,0,1)) {
10485                   if (first < 0)        /* Remember location of first match */
10486                     first = i;
10487                   k++;
10488               }
10489           }
10490           debug(F101,"aaconvert matches","",k);
10491           debug(F101,"aaconvert first","",first);
10492           fnval[0] = NUL;               /* Initial return value */
10493           ckstrncpy(abuf,bp[1],16);     /* Get array reference */
10494           s = abuf;
10495           if (*s == CMDQ) s++;
10496           p = fnval;                    /* Point to result */
10497           if (fndiags)                  /* Default is this error message */
10498             ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
10499           if (s[0] != '&')              /* Address of array */
10500             goto fnend;
10501           if (s[2])
10502             if (s[2] != '[' || s[3] != ']')
10503               goto fnend;
10504           if (s[1] >= 64 && s[1] < 91)  /* Convert upper to lower */
10505             s[1] += 32;
10506           if ((x = dclarray(s[1],k)) < 0) /* Declare array to size */
10507             goto fnend;
10508           ap = a_ptr[x];                /* Point to array we just declared */
10509           /* debug(F111,"aaconvert array 1",abuf,ap); */
10510           abuf[0] = NUL;
10511           if (argn > 2) {
10512               ckstrncpy(abuf,bp[2],16); /* Get value array reference */
10513               s = abuf;
10514               if (*s == CMDQ) s++;
10515               if (s[0] != '&')          /* Address of array */
10516                 goto fnend;
10517               if (s[2])
10518                 if (s[2] != '[' || s[3] != ']')
10519                   goto fnend;
10520               if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */
10521                 s[1] += 32;
10522               if ((x = dclarray(s[1],k)) < 0)
10523                 goto fnend;
10524               vp = a_ptr[x];            /* Point to array we just declared */
10525           }
10526           /* debug(F111,"aaconvert array 2",abuf,vp); */
10527           makestr(&ap[0],ckitoa(k));
10528           if (vp) makestr(&vp[0],ckitoa(k));
10529           if (fndiags)
10530            ckmakmsg(fnval,FNVALL,"<ERROR:ASSOCIATIVE_ARRAY:\\f",fn,"()>",NULL);
10531
10532           /* Copy macro index & value to the arrays and then remove the */
10533           /* macro, so the 'first' pointer keeps indicating the next one. */
10534           /* We could combine the initial counting loop with this one but */
10535           /* then it would be harder to create the array and anyway this */
10536           /* function is plenty fast as it is. */
10537
10538           for (i = 1; i <= k; ) {
10539               if (!ckmatch(pattern,mactab[first].kwd,0,1)) {
10540                   debug(F111,"aaconvert oddball",mactab[first].kwd,first);
10541                   first++;
10542                   continue;
10543               }
10544               ckstrncpy(tmpbuf,mactab[first].kwd,TMPBUFSIZ); /* Macro name */
10545               s = tmpbuf;                       /* Make writeable copy */
10546               s += slen;                        /* Isolate "index" */
10547               j = strlen(s) - 1;
10548               if (*s != '<' || *(s+j) != '>') { /* Check syntax */
10549                   /* This shouldn't happen */
10550                   debug(F111,"aaconvert ERROR",mactab[first].kwd,first);
10551                   goto fnend;
10552               }
10553               *(s+j) = NUL;             /* Remove final '>' */
10554               debug(F111,"aaconvert",s+1,i);
10555               makestr(&(ap[i]),s+1);    /* Set first array to index */
10556               if (vp)
10557                 makestr(&(vp[i]),mactab[first].mval); /* 2nd to value */
10558               if (xdelmac(first) < 0)
10559                 goto fnend;
10560               i++;
10561           }
10562           sprintf(fnval,"%d",k);        /* SAFE */
10563           p = fnval;                    /* Return size of array */
10564           debug(F110,"aaconvert return",p,0);
10565           failed = 0;                   /* Unset failure flag */
10566           goto fnend;
10567       }
10568
10569     } /* End of switch() */
10570
10571 #ifdef FNFLOAT
10572 /*
10573   Floating-point functions.  To be included only if FNFLOAT is defined, which
10574   should happen only if CKFLOAT is also defined, and if the math library is
10575   linked in.  Even then, we might have float-vs-double confusion as well as
10576   confusion about what the final "%f" format effector is supposed to reference
10577   (32 bits, 64 bits, etc).  Expect trouble if CKFLOAT does not match the data
10578   type of math library functions or args.
10579 */
10580     if (cx == FN_FPABS ||               /* Floating-point functions */
10581         cx == FN_FPADD ||
10582         cx == FN_FPDIV ||
10583         cx == FN_FPEXP ||
10584         cx == FN_FPLOG ||
10585         cx == FN_FPLN  ||
10586         cx == FN_FPMOD ||
10587         cx == FN_FPMAX ||
10588         cx == FN_FPMIN ||
10589         cx == FN_FPMUL ||
10590         cx == FN_FPPOW ||
10591         cx == FN_FPSQR ||
10592         cx == FN_FPINT ||
10593         cx == FN_FPSUB ||
10594         cx == FN_FPROU ||
10595         cx == FN_FPSIN ||
10596         cx == FN_FPCOS ||
10597         cx == FN_FPTAN) {
10598         CKFLOAT farg[2], fpresult = 0.0;
10599         char fpbuf[64], * bp0;
10600         double dummy;
10601         /* int sign = 0; */
10602         int i, j, places = 0;
10603         int argcount = 1;
10604
10605         failed = 1;
10606         p = fnval;
10607         bp0 = bp[0];
10608         if (!bp0)
10609           bp0 = "0";
10610         else if (!*bp0)
10611           bp0 = "0";
10612         if (!isfloat(bp0,0)) {
10613             k = mxlook(mactab,bp0,nmac);
10614             bp0 = (k > -1) ? mactab[k].mval : NULL;
10615             if (bp0) {
10616                 if (!isfloat(bp0,0)) {
10617                     if (fndiags)
10618                       ckmakmsg(fnval,FNVALL,
10619                                "<ERROR:ARG_NOT_FLOAT:\\f",fn,"()>",NULL);
10620                     goto fnend;
10621                 }
10622             }
10623         }
10624         if (cx == FN_FPINT) {           /* Float to int */
10625             failed = 0;
10626             ckstrncpy(fnval,bp0,FNVALL);
10627             for (i = 0; fnval[i]; i++) {
10628                 if (fnval[i] == '.') {
10629                     fnval[i] = NUL;
10630                     break;
10631                 }
10632             }
10633             goto fnend;
10634         }
10635         switch (y) {                    /* These need 2 args */
10636           case FN_FPADD:
10637           case FN_FPDIV:
10638           case FN_FPMOD:
10639           case FN_FPMAX:
10640           case FN_FPMIN:
10641           case FN_FPMUL:
10642           case FN_FPPOW:
10643           case FN_FPSUB:
10644             argcount = 2;
10645         }
10646         /* Missing arguments are supplied as 0.0 */
10647
10648         debug(F111,fn,"argcount",argcount);
10649         for (i = 0; i < argcount; i++) { /* Get floating-point args */
10650 #ifdef DEBUG
10651             if (deblog) {
10652                 ckmakmsg(fpbuf,
10653                          64,
10654                          "bp[",
10655                          ckitoa(i),
10656                          bp[i] ? bp[i] : "(null)",
10657                          "]"
10658                          );
10659                 debug(F100,fpbuf,"",0);
10660             }
10661 #endif /* DEBUG */
10662             if (!bp[i]) {
10663                 farg[i] = 0.0;
10664             } else if (!*(bp[i])) {
10665                 farg[i] = 0.0;
10666             } else if (!isfloat(bp[i],0)) {
10667                 char * tmp;
10668                 k = mxlook(mactab,bp[i],nmac);
10669                 tmp = (k > -1) ? mactab[k].mval : NULL;
10670                 if (tmp) {
10671                     if (!isfloat(tmp,0)) {
10672                         if (fndiags)
10673                           ckmakmsg(fnval,FNVALL,
10674                                    "<ERROR:ARG_NOT_FLOAT:\\f",fn,"()>",NULL);
10675                         goto fnend;
10676                     }
10677                 }
10678             }
10679             farg[i] = floatval;
10680
10681 #ifdef DEBUG
10682             if (deblog) {
10683                 sprintf(fpbuf,"farg[%d]=%f",i,farg[i]); /* SAFE */
10684                 debug(F100,fpbuf,"",0);
10685             }
10686 #endif /* DEBUG */
10687         }
10688         if (bp[argcount]) {             /* Get decimal places */
10689             char * s;
10690             s = bp[argcount];
10691             if (!s) s = "";
10692             if (!*s) s = "0";
10693             s = evalx(s);
10694             if (!s) s = "";
10695             if (!*s) {
10696                 evalerr(fn);
10697                 goto fnend;
10698             }
10699             places = atoi(s);
10700         }
10701         errno = 0;
10702         failed = 0;
10703         switch (y) {                    /* Now do the requested function */
10704           case FN_FPABS:                /* Floating-point absolute value */
10705 #ifndef COMMENT
10706             fpresult = fabs(farg[0]);
10707 #else
10708             if (farg[0] < 0.0)
10709               fpresult = 0.0 - farg[0];
10710 #endif /* COMMENT */
10711             break;
10712           case FN_FPADD:                /* FP add */
10713             fpresult = farg[0] + farg[1];
10714             break;
10715           case FN_FPDIV:                /* FP divide */
10716           case FN_FPMOD:                /* FP modulus */
10717             if (!farg[1]) {
10718                 failed = 1;
10719                 if (fndiags)
10720                   ckmakmsg(fnval,FNVALL,
10721                            "<ERROR:DIVIDE_BY_ZERO:\\f",fn,"()>",NULL);
10722             } else
10723               fpresult = (cx == FN_FPDIV) ?
10724                 (farg[0] / farg[1]) :
10725                   fmod(farg[0],farg[1]);
10726             break;
10727           case FN_FPEXP:                /* FP e to the x */
10728             fpresult = (CKFLOAT) exp(farg[0]);
10729             break;
10730           case FN_FPLOG:                /* FP base-10 logarithm */
10731           case FN_FPLN:                 /* FP natural logarithm */
10732             if (farg[0] < 0.0) {
10733                 failed = 1;
10734                 if (fndiags)
10735                   ckmakmsg(fnval,FNVALL,
10736                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
10737             } else
10738               fpresult = (cx == FN_FPLOG) ? log10(farg[0]) : log(farg[0]);
10739             break;
10740           case FN_FPMUL:                /* FP multiply */
10741             fpresult = farg[0] * farg[1];
10742             break;
10743           case FN_FPPOW:                /* FP raise to a power */
10744             fpresult = modf(farg[1],&dummy);
10745             if ((!farg[0] && farg[1] <= 0.0) ||
10746                 (farg[0] < 0.0 && fpresult)) {
10747                 failed = 1;
10748                 if (fndiags)
10749                   ckmakmsg(fnval,FNVALL,
10750                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
10751             } else
10752               fpresult = pow(farg[0],farg[1]);
10753             break;
10754           case FN_FPSQR:                /* FP square root */
10755             if (farg[0] < 0.0) {
10756                 failed = 1;
10757                 if (fndiags)
10758                   ckmakmsg(fnval,FNVALL,
10759                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
10760             } else
10761               fpresult = sqrt(farg[0]);
10762             break;
10763           case FN_FPSUB:                /* FP subtract */
10764             fpresult = farg[0] - farg[1];
10765             break;
10766           case FN_FPROU:                /* FP round */
10767             fpresult = farg[0];
10768             break;
10769           case FN_FPSIN:                /* FP sine */
10770             fpresult = (CKFLOAT) sin(farg[0]);
10771             break;
10772           case FN_FPCOS:                /* FP cosine */
10773             fpresult = (CKFLOAT) cos(farg[0]);
10774             break;
10775           case FN_FPTAN:                /* FP tangent */
10776             fpresult = (CKFLOAT) tan(farg[0]);
10777             break;
10778           case FN_FPMAX:
10779             fpresult = (farg[0] > farg[1]) ? farg[0] : farg[1];
10780             break;
10781           case FN_FPMIN:
10782             fpresult = (farg[0] < farg[1]) ? farg[0] : farg[1];
10783             break;
10784         }
10785
10786         /* Get here with fpresult = function result */
10787
10788         if (errno) {                    /* If range or domain error */
10789             failed = 1;
10790             if (fndiags)
10791               ckmakmsg(fnval,FNVALL,
10792                        "<ERROR:FLOATING-POINT-OP:\\f",fn,"()>",NULL);
10793         }
10794         if (failed)                     /* and/or any other kind of error, */
10795           goto fnend;                   /* fail. */
10796 #ifndef COMMENT
10797         /* Call routine containing code that was formerly inline */
10798         ckstrncpy(fnval,fpformat(fpresult,places,cx == FN_FPROU),FNVALL);
10799 #else
10800         {
10801             char fbuf[16];              /* For creating printf format */
10802             if (!fp_rounding &&         /* If printf doesn't round, */
10803                 (places > 0 ||          /* round result to decimal places. */
10804                  (places == 0 && cx == FN_FPROU)))
10805               fpresult += (0.5 / pow(10.0,(CKFLOAT)places));
10806             if (places > 0) {                   /* If places specified */
10807                 /* use specified places to write given number of digits */
10808                 sprintf(fbuf,"%%0.%df",places); /* SAFE */
10809                 sprintf(fnval,fbuf,fpresult);   /* SAFE */
10810             } else {                            /* Otherwise... */
10811 #ifdef COMMENT
10812 /*
10813   Here we want to print exactly fp_digits significant digits, no matter which
10814   side of the decimal point they are on.  That is, we want want the default
10815   format to show the maximum number of non-garbage digits, AND we want the last
10816   such digit to be rounded.  Of course there is no way to do that, since the
10817   digit after the last non-garbage digit is, well, garbage.  So the following
10818   clever ruse does no good.
10819 */
10820                 int sign = 0, m = 0;
10821                 sprintf(fnval,"%f",fpresult);
10822                 if (fnval[0] == '-') sign = 1;
10823                 for (i = sign; i < FNVALL; i++) {
10824                     if (isdigit(fnval[i]))
10825                       m++;
10826                     else
10827                       break;
10828                 }
10829                 if (m > 1) {
10830                     int d = fp_digits - m;
10831                     if (d < 1) d = 1;
10832                     sprintf(fbuf,"%%%d.%df",fp_digits+sign+1,d);
10833                 } else {
10834                     sprintf(fbuf,"%%0.%df",fp_digits);
10835                 }
10836                 sprintf(fnval,fbuf,fpresult);
10837 #else
10838                 /* Go for max precision */
10839                 sprintf(fbuf,"%%0.%df",fp_digits); /* SAFE */
10840                 sprintf(fnval,fbuf,fpresult); /* SAFE */
10841
10842 #endif /* COMMENT */
10843             }
10844             if (fnval[0] == '-') sign = 1;
10845         }
10846         debug(F111,"fpresult 1",fnval,errno); /* Check for over/underflow */
10847         for (i = sign; fnval[i]; i++) { /* Give requested decimal places */
10848             if (fnval[i] == '.')        /* First find the decimal point */
10849               break;
10850             else if (i > fp_digits + sign - 1) /* replacing garbage */
10851               fnval[i] = '0';           /* digits with 0... */
10852         }
10853         if (fnval[i] == '.') {          /* Have decimal point */
10854             int gotend = 0;
10855             /* d < 0 so truncate fraction */
10856             if (places < 0 || (places == 0 && cx == FN_FPROU)) {
10857                 fnval[i] = NUL;
10858             } else if (places > 0) {    /* d > 0 so this many decimal places */
10859                 i++;                           /* First digit after decimal */
10860                 for (j = 0; j < places; j++) { /* Truncate after d decimal */
10861                     if (!fnval[j+i])           /* places or extend to d  */
10862                       gotend = 1;              /* decimal places. */
10863                     if (gotend || j+i+sign > fp_digits)
10864                       fnval[j+i] = '0';
10865                 }
10866                 fnval[j+i] = NUL;
10867             } else {                    /* d == 0 so Do The Right Thing */
10868                 for (j = (int)strlen(fnval) - 1; j > i+1; j--) {
10869                     if ((j - sign) > fp_digits)
10870                       fnval[j] = '0';
10871                     if (fnval[j] == '0')
10872                       fnval[j] = NUL;   /* Strip useless trailing 0's. */
10873                     else
10874                       break;
10875                 }
10876             }
10877         }
10878 #endif /* COMMENT */
10879         debug(F111,"fpresult 2",fnval,errno);
10880         goto fnend;
10881
10882     }
10883 #endif /* FNFLOAT */
10884
10885 #ifdef CKCHANNELIO
10886     if (cx == FN_FSTAT  ||              /* File functions */
10887         cx == FN_FPOS   ||
10888         cx == FN_FEOF   ||
10889         cx == FN_FGCHAR ||
10890         cx == FN_FGLINE ||
10891         cx == FN_FGBLK  ||
10892         cx == FN_FPCHAR ||
10893         cx == FN_FPLINE ||
10894         cx == FN_FPBLK  ||
10895         cx == FN_NLINE  ||
10896         cx == FN_FERMSG ||
10897         cx == FN_FILNO) {
10898         int x = 0, t = 0, channel;
10899         long z;
10900         extern int z_maxchan;
10901
10902         failed = 1;                     /* Assume failure */
10903         p = fnval;                      /* until we validate args */
10904         if (cx == FN_FERMSG) {
10905             extern int z_error;
10906             if (argn < 1) {
10907                 x = z_error;
10908             } else if (chknum(bp[0])) {
10909                 x = atoi(bp[0]);
10910             } else if (fndiags)
10911               ckmakmsg(fnval,FNVALL,
10912                        "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10913             failed = 0;
10914             ckstrncpy(fnval,ckferror(x),FNVALL);
10915             goto fnend;
10916         }
10917         if (argn < 1) {                 /* All file functions need channel */
10918             if (cx == FN_FSTAT) {       /* Except f_status(), e.g. when */
10919                 fnval[0] = '0';         /* called with a variable that */
10920                 fnval[1] = NUL;         /* hasn't been defined yet. */
10921                 failed = 0;
10922             } else {
10923                 if (fndiags)
10924                  ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
10925             }
10926             goto fnend;
10927         }
10928         if (rdigits(bp[0])) {           /* Channel must be numeric */
10929             channel = atoi(bp[0]);
10930         } else {                        /* Fail if it isn't */
10931             if (fndiags)
10932               ckmakmsg(fnval,FNVALL,
10933                        "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10934             goto fnend;
10935         }
10936         if (channel < 0 || channel > z_maxchan) { /* Check channel range */
10937             if (fndiags)
10938               ckmakmsg(fnval,FNVALL,
10939                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
10940             goto fnend;
10941         }
10942         x = z_getmode(channel);         /* Find out about the channel */
10943
10944         failed = 0;                     /* Assume success from here down */
10945         if (cx == FN_FSTAT) {           /* Status / modes of channel */
10946             if (x > -1)
10947               x &= FM_RWB;              /* Mask out irrelevant bits */
10948             else                        /* In this case not open is OK */
10949               x = 0;                    /* 0 if not open, 1-7 if open */
10950             sprintf(fnval,"%d",x);      /* SAFE */
10951             goto fnend;
10952         } else if (x < 1) {             /* Not \f_status() so must be open */
10953             failed = 1;
10954             if (fndiags)
10955               ckmakmsg(fnval,FNVALL,"<ERROR:FILE_NOT_OPEN:\\f",fn,"()>",NULL);
10956             goto fnend;
10957         }
10958         switch (y) {                    /* Do the requested function */
10959           case FN_FPOS:                 /* Get position */
10960             z = z_getpos(channel);      /* FIX THIS */
10961             sprintf(fnval,"%ld",z);     /* SAFE */
10962             goto fnend;
10963
10964           case FN_NLINE:                /* Get line number */
10965             z = z_getline(channel);     /* FIX THIS */
10966             sprintf(fnval,"%ld",z);     /* SAFE */
10967             goto fnend;
10968
10969           case FN_FEOF:                 /* Check EOF */
10970             t = 0;
10971             if (x & FM_EOF) t = 1;
10972             sprintf(fnval,"%d",t);      /* SAFE */
10973             goto fnend;
10974
10975           case FN_FILNO:                /* Get file handle */
10976             x = z_getfnum(channel);
10977             sprintf(fnval,"%d",x);      /* SAFE */
10978             goto fnend;
10979
10980           case FN_FPBLK:                /* Read or write block */
10981           case FN_FGBLK:
10982             if (argn < 2) {
10983                 if (fndiags)
10984                   ckmakmsg(fnval,FNVALL,
10985                            "<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
10986                 goto fnend;
10987             }
10988             if (rdigits(bp[1])) {
10989                 t = atoi(bp[1]);
10990             } else {
10991                 if (fndiags)
10992                   ckmakmsg(fnval,FNVALL,
10993                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
10994                 goto fnend;
10995             }
10996           case FN_FGCHAR:               /* Read or write character or line */
10997           case FN_FPCHAR:
10998           case FN_FGLINE:
10999           case FN_FPLINE:
11000             fnval[0] = NUL;
11001             switch (y) {
11002               case FN_FGCHAR: t = z_in(channel,fnval,FNVALL,1,1); break;
11003               case FN_FGLINE: t = z_in(channel,fnval,FNVALL,FNVALL-1,0); break;
11004               case FN_FGBLK:
11005                 if (t >= FNVALL) t = FNVALL - 1;
11006                 t = z_in(channel,fnval,FNVALL,t,1);
11007                 break;
11008               case FN_FPCHAR: t = z_out(channel,bp[1],1,1);  break;
11009               case FN_FPLINE: t = z_out(channel,bp[1],-1,0); break;
11010               case FN_FPBLK:  t = z_out(channel,bp[1],-1,1); break;
11011             }
11012             if (t < 0) {                /* Handle read/write error */
11013                 failed = 1;
11014                 if (fndiags && t != FX_EOF)
11015                   ckmakmsg(fnval,FNVALL,
11016                            "<ERROR:FILE_ERROR_%d:\\f",fn,"()>",NULL);
11017                 goto fnend;
11018             }
11019             if (cx == FN_FGCHAR)        /* Null terminate char */
11020               fnval[1] = NUL;
11021             /* Write (put) functions return numeric status code */
11022             if (cx == FN_FPCHAR || cx == FN_FPLINE || cx == FN_FPBLK)
11023               sprintf(fnval,"%d",t);    /* SAFE */
11024             goto fnend;
11025         }
11026     }
11027 #endif /* CKCHANNELIO */
11028
11029     if (cx == FN_SQUEEZE) {             /* String function \fsqueeze() */
11030         /* Squeeze out whitespace */
11031         /* Add options later for whether to trim leading and trailing blanks */
11032         /* and what to do about control characters, 8-bit whitespace, etc */
11033         int started = 0;                /* Flag for first non-whitespace */
11034         int n = 0;                      /* Blank/Tab counter */
11035         s = bp[0] ? bp[0] : "";
11036         p = fnval;                      /* Result buffer */
11037         while (*s) {                    /* While there is input */
11038             if (!started && (*s == ' ' || *s == '\011')) {
11039                 s++;                    /* Skip past leading whitespace */
11040                 continue;
11041             }
11042             started++;                  /* Leading whitespace was skipped */
11043             if (*s != ' ' && *s != '\011') { /* Have a nonspace char */
11044                 n = 0;                  /* reset space counter */
11045                 *p++ = *s++;            /* copy char to destination */
11046                 continue;
11047             }               
11048             if (n++ > 0) {              /* Have blank or tab */
11049                 s++;                    /* don't copy more than one */
11050                 continue;
11051             }
11052             *p++ = ' ';                 /* Deposit one space */
11053             s++;                        /* and go to next source char */
11054         }
11055         if (*(p-1) == ' ') p--;         /* Remove trailing space */
11056         *p = NUL;                       /* Terminate string */
11057         p = fnval;                      /* point to beginning */
11058         goto fnend;                     /* Done. */
11059     }
11060     if (cx == FN_PATTERN) {             /* \fpattern() for INPUT */
11061         itsapattern = 1;
11062         if (argn > 0) {
11063             p = fnval;
11064             ckstrncpy(fnval,bp[0],FNVALL);
11065         } else p = "";
11066         goto fnend;
11067     }
11068     if (cx == FN_HEX2N || cx == FN_OCT2N) { /* \fhex2n(), \foct2n() */
11069         p = "0";
11070         if (argn < 1)
11071           goto fnend;
11072         p = ckradix(bp[0], ((cx == FN_HEX2N) ? 16 : 8), 10);
11073         if (!p) {
11074             if (fndiags)
11075               ckmakmsg(fnval,FNVALL,
11076                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11077             goto fnend;
11078         }
11079         failed = 0;
11080         ckstrncpy(fnval,p,FNVALL);
11081         p = fnval;
11082         goto fnend;
11083     }
11084
11085     if (cx == FN_HEX2IP) {
11086         int c[2], ip[4], i, k;
11087         p = "0";
11088         if (argn < 1)
11089           goto fnend;
11090         s = bp[0];
11091         if ((int)strlen(s) != 8) {
11092             failed = 1;
11093             if (fndiags)
11094               ckmakmsg(fnval,FNVALL,
11095                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11096             goto fnend;
11097         }
11098         p = fnval;
11099         for (k = 0; k < 8; k += 2) {
11100             for (i = 0; i < 2; i++) {
11101                 c[i] = *s++;
11102                 if (islower(c[i])) c[i] = toupper(c[i]);
11103                 if (c[i] >= '0' && c[i] <= '9') {
11104                     c[i] -= 0x30;
11105                 } else if (c[i] >= 'A' && c[i] <= 'F') {
11106                     c[i] -= 0x37;
11107                 } else {
11108                     failed = 1;
11109                     if (fndiags)
11110                       ckmakmsg(fnval,FNVALL,
11111                                "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11112                     goto fnend;
11113                 }
11114                 ip[k/2] = c[0] << 4 | c[1];
11115             }
11116             sprintf(p,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]); /* SAFE */
11117         }
11118         goto fnend;
11119     }
11120     if (cx == FN_IP2HEX) {
11121         int ip[4], i;
11122         char * q;
11123         p = "00000000";
11124         if (argn < 1)
11125           goto fnend;
11126         s = bp[0];
11127         p = fnval;
11128         for (i = 0; i < 3; i++) {
11129             q = ckstrchr(s,'.');
11130             if (q) {
11131                 *q++ = NUL;
11132                 ip[i] = atoi(s);
11133                 s = q;
11134             } else {
11135                 failed = 1;
11136                 if (fndiags)
11137                   ckmakmsg(fnval,FNVALL,
11138                            "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11139                 goto fnend;
11140             }
11141         }
11142         ip[3] = atoi(s);
11143         sprintf(p,"%02x%02x%02x%02x",ip[0],ip[1],ip[2],ip[3]); /* SAFE */
11144         goto fnend;
11145     }
11146     if (cx == FN_RADIX) {
11147         failed = 1;
11148         p = fnval;
11149         if (argn < 3) {
11150             if (fndiags)
11151               ckmakmsg(fnval,FNVALL,"<ERROR:MISSING_ARG:\\f",fn,"()>",NULL);
11152             goto fnend;
11153         }
11154         if (!rdigits(bp[1]) || !rdigits(bp[2])) {
11155             if (fndiags)
11156               ckmakmsg(fnval,FNVALL,
11157                        "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11158             goto fnend;
11159         }
11160         p = ckradix(bp[0],atoi(bp[1]),atoi(bp[2]));
11161         if (!p) {
11162             if (fndiags)
11163               ckmakmsg(fnval,FNVALL,
11164                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11165             goto fnend;
11166         }
11167         failed = 0;
11168         ckstrncpy(fnval,p,FNVALL);
11169         p = fnval;
11170         goto fnend;
11171     }
11172     if (cx == FN_JOIN) {
11173         int i, x, y, z, flag, flag2, hi, lo, max, seplen, grouping = 0;
11174         char abuf[16], c, *s, *q, *sep = NULL;
11175         char * gr_opn = "\"{'([<";      /* Group open brackets */
11176         char * gr_cls = "\"}')]>";      /* Group close brackets */
11177         char lb[2], rb[2];              /* Selected left and right brackets */
11178         int csv = 0, tsv = 0;           /* Function flags */
11179         char specialchar = 0;           /* Field char that triggers grouping */
11180         char *s2 = NULL;                /* Address of malloc'd storage */
11181
11182         failed = 1;                     /* Assume failure */
11183         fnval[0] = NUL;
11184         debug(F101,"FNJOIN ARGN","",argn);
11185
11186         ckstrncpy(abuf,bp[0],16);       /* Get array reference */
11187         s = abuf;
11188         if ((x = arraybounds(s,&lo,&hi)) < 0) {  /* Get index and bounds */
11189             if (fndiags)
11190               ckmakmsg(fnval,FNVALL,"<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
11191             goto fnend;
11192         }
11193         p = fnval;                      /* Point to result */
11194         max = a_dim[x];                 /* Size of array */
11195         if (lo < 0) lo = 1;             /* Use given range if any */
11196         if (lo > max) lo = max;
11197 #ifdef COMMENT
11198         hi = max;
11199 #else
11200 /*
11201   This is a workaround for the problem in which the dimension of the \&_[]
11202   array (but not its contents) grows upon entry to a SWITCH block.  But this
11203   code prevents the dimension from growing.  Go figure.
11204 */
11205         if (hi < 0) {                   /* Bounds not given */
11206             if (x)                      /* Regular array */
11207               hi = max;
11208             else                        /* Argument vector array */
11209               for (hi = max; hi >= lo; hi--) { /* ignore any trailing */
11210                   if (!a_ptr[x][hi]) continue; /* empty elements */
11211                   if (!*(a_ptr[x][hi])) continue;
11212                   break;
11213               }
11214         }
11215 #endif /* COMMENT */
11216         if (hi > max) hi = max;
11217         failed = 0;                     /* Unset failure flag */
11218         if (max < 1)
11219           goto fnend;
11220         sep = " ";                      /* Separator */
11221         lb[0] = NUL;                    /* Group start char (as string) */
11222         rb[0] = NUL;
11223         lb[1] = NUL;                    /* Group end char as string */
11224         rb[1] = NUL;
11225
11226         if (argn > 1) {
11227             if (bp[1]) if (*bp[1]) {    /* If arg1 given and not empty */
11228                 if (!strcmp(bp[1],"CSV")) { /* Special "CSV" symbolic arg */
11229                     csv++;              /* Make a comma separated list */
11230                     sep = ",";          /* Comma */
11231                     specialchar = *sep; /* Separator is special character */
11232                     grouping = 1;       /* Group with doublequotes */
11233                     lb[0] = '"';        /* and here */
11234                     rb[0] = '"';        /* they are */
11235                 } else if (!strcmp(bp[1],"TSV")) { /* "TSV" symbolic arg */
11236                     tsv++;              /* Make a Tab separated list */
11237                     sep = "\011";       /* Tab */
11238                     specialchar = *sep;
11239                     grouping = 0;       /* No grouping */
11240                 } else                  /* Normal case */
11241                   sep = bp[1];          /* use the separator char specified */
11242             }
11243         }
11244         if (argn > 2 && !csv && !tsv) { /* Grouping? */
11245             char * bp2 = bp[2];
11246             if (!bp2) bp2 = "0";
11247             if (!*bp2) bp2 = "0";
11248             if (chknum(bp2)) {
11249                 grouping = atoi(bp2);
11250                 if (grouping < 0 || grouping > 63)
11251                   grouping = 1;
11252             } else {
11253                 failed = 1;
11254                 if (fndiags)
11255                   ckmakmsg(fnval,FNVALL,
11256                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11257                 goto fnend;
11258             }
11259             if (grouping) {             /* Take lowest-order one */
11260                 int j, k;               /* and set the others to 0 */
11261                 for (k = 0; k < 6; k++) {
11262                     j = 1 << k;
11263                     if (grouping & j) {
11264                         lb[0] = gr_opn[k];
11265                         rb[0] = gr_cls[k];
11266                         break;
11267                     }
11268                 }
11269             }
11270         }
11271         if (!csv && !tsv) {             /* Normal case, not CSV or TSV */
11272             specialchar = SP;           /* Special character is space */
11273             if (argn > 3)               /* Nonzero 4th arg for no separator */
11274               if (chknum(bp[3]))
11275                 if (atoi(bp[3]) > 0)
11276                   sep = NULL;
11277             if (!sep) {
11278                 sep = "";
11279                 seplen = 0;
11280             } else
11281               seplen = strlen(sep);
11282         }
11283         for (i = lo; i <= hi; i++) {    /* Loop thru selected array elements */
11284             s = a_ptr[x][i];            /* Get next element */
11285             if (!s)
11286               s = "";
11287             flag = 0;                   /* Flag to indicate grouping needed */
11288             flag2 = 0;                  /* Flag for internal doublequotes */
11289             if (grouping) {             /* Does this element need quoting? */
11290                 q = s;                  /* Look for special character */
11291                 while ((c = *q++)) {    /* If found */
11292                     if (c == specialchar) /* grouping is required */
11293                       flag++;
11294                     if (csv && (c == '"')) /* Character that needs doubling */
11295                       flag2++;             /* in comma-separated list */
11296                     if (flag && !csv)   /* Exit early if no more to do */
11297                       break;
11298                 }
11299             }
11300             y = strlen(s);              /* Get length of this element */
11301             if ((y > 0) && csv && !flag) { /* CSV item needs grouping */
11302                 if (s[0] == SP || s[y-1] == SP || /* if it has leading */
11303                     s[0] == HT || s[y-1] == HT) /* or trailing whitespace */
11304                   flag++;               /* then it needs grouping */
11305             }
11306             if (flag || flag2) {        /* String needs grouping or quoting */
11307                 char *ss = s;
11308                 q = (char *)malloc(y + flag2 + 3); /* Make new buffer */
11309                 if (q) {
11310                     s2 = q;             /* and this is what to free */
11311                     if (flag)           /* If grouping */
11312                       *q++ = lb[0];     /* put opening group quote */
11313                     while (*ss) {       /* Loop through string */
11314                         if (flag2 && (*ss == '"')) /* If CSV and this a '"' */
11315                           *q++ = *ss;              /* double it. */
11316                         *q++ = *ss++;   /* Copy the character */
11317                     }
11318                     if (flag)           /* If grouping */
11319                       *q++ = rb[0];     /* add closing group quote */
11320                     *q = NUL;           /* terminate the result. */
11321                     s = s2;
11322                     y = strlen(s);
11323                 }
11324             }
11325             z = 0;                      /* Number of chars copied */
11326             flag = 0;                   /* flag is now buffer-overrun flag */
11327             if (y > 0)                  /* If this string is not empty */
11328               z = ckstrncat(fnval,s,FNVALL); /* copy it. */
11329             if (s2) free(s2);           /* Free temp storage */
11330             if (z < y)                  /* Now check for buffer overrun. */
11331               flag++;
11332             if (!flag && *sep && i < hi) { /* If buffer still has room */
11333                 z = ckstrncat(fnval,sep,FNVALL); /* copy delimiter */
11334                 if (z < seplen)
11335                   flag++;
11336             }
11337             if (flag) {
11338                 failed = 1;
11339                 if (fndiags)
11340                   ckmakmsg(fnval,FNVALL,
11341                            "<ERROR:RESULT_TOO_LONG:\\f",fn,"()>",NULL);
11342                 goto fnend;
11343             }
11344         }
11345         isjoin = 1;
11346         goto fnend;
11347     }
11348     if (cx == FN_SUBST) {               /* \fsubstitute() */
11349         CHAR c, * s, * r, * tp[2], buf1[256], buf2[256], buf3[256];
11350         int len, i, j, state = 0, lo = 0, hi = 0;
11351
11352         failed = 0;
11353         p = fnval;                      /* Result pointer */
11354         *p = NUL;
11355         if (!bp[0])                     /* No target, no result*/
11356           goto fnend;
11357
11358         len = strlen(bp[0]);            /* Length of source */
11359         if (len == 0)
11360           goto fnend;
11361         if (len > FNVALL) {
11362             failed = 1;
11363             if (fndiags)
11364               ckmakmsg(fnval,FNVALL,
11365                        "<ERROR:RESULT_TOO_LONG:\\f",fn,"()>",NULL);
11366             goto fnend;
11367         }
11368         if (!bp[1]) {
11369             ckstrncpy(bp[0],fnval,FNVALL);
11370             goto fnend;
11371         }
11372         tp[0] = buf1;                   /* For s2-s3 interpretation loop */
11373         tp[1] = buf2;
11374
11375         for (i = 0; i < 256; i++) {     /* Initialize working buffers */
11376             buf1[i] = 0;                /* s2 expansion buffer */
11377             buf2[i] = 0;                /* s3 expansion buffer */
11378             buf3[i] = i;                /* Translation table */
11379         }
11380         for (i = 0; i < 2; i++) {       /* Interpret s2 and s3 */
11381             s = (CHAR *)bp[i+1];        /* Arg pointer */
11382             if (!s) s = (CHAR *)"";
11383             r = tp[i];                  /* To construct interpreted arg */
11384             j = 0;                      /* Output buf pointer */
11385             state = 0;                  /* Initial state */
11386             while (c = *s++) {          /* Loop thru arg chars */
11387                 if (j > 255)            /* Output buf full */
11388                   break;
11389                 switch (state) {
11390                   case 0:               /* Normal state */
11391                     switch (c) {
11392                       case '\\':        /* Have quote */
11393                         state = 1;
11394                         break;
11395                       case '[':         /* Have range starter */
11396                         state = 2;
11397                         break;
11398                       default:          /* Anything else */
11399                         r[j++] = c;
11400                         break;
11401                     }
11402                     continue;
11403                   case 1:               /* Quoted char */
11404                     r[j++] = c;
11405                     state = 0;
11406                     continue;
11407                   case 2:               /* Range bottom */
11408                     lo = c;
11409                     state++;
11410                     continue;
11411                   case 3:               /* Range separater */
11412                     if (c != '-') {
11413                         failed = 1;
11414                         if (fndiags)
11415                           ckmakmsg(fnval,FNVALL,
11416                                    "<ERROR:BAD_RANGE:\\f",fn,"()>",NULL);
11417                         goto fnend;
11418                     }
11419                     state++;
11420                     continue;
11421                   case 4:               /* Range top */
11422                     hi = c;
11423                     state++;
11424                     continue;
11425                   case 5:               /* Range end */
11426                     if (c != ']') {
11427                         failed = 1;
11428                         if (fndiags)
11429                           ckmakmsg(fnval,FNVALL,
11430                                    "<ERROR:BAD_RANGE:\\f",fn,"()>",NULL);
11431                         goto fnend;
11432                     }
11433                     for (k = lo; k <= hi && j < 255; k++) /* Fill in */
11434                       r[j++] = k;
11435                     lo = 0; hi = 0;     /* Reset */
11436                     state = 0;
11437                     continue;
11438                 }
11439             }
11440         }
11441         for (i = 0; i < 256 && buf1[i]; i++) {  /* Create translation table */
11442             k = (unsigned)buf1[i];
11443             buf3[k] = buf2[i];
11444         }
11445         s = (CHAR *)bp[0];              /* Point to source string */
11446         for (i = 0; i < len; i++) {     /* Translation loop */
11447             k = (unsigned)s[i];         /* Get next char */
11448             if (!buf3[k])               /* Remove this char */
11449               continue;
11450             *p++ = buf3[k];             /* Substitute this char */
11451         }
11452         *p = NUL;
11453         p = fnval;
11454         goto fnend;
11455     }
11456
11457 #ifndef NOSEXP
11458     if (cx == FN_SEXP) {                /* \fsexpression(arg1) */
11459         char * p2;
11460         fsexpflag++;
11461         p = (argn > 0) ? dosexp(bp[0]) : "";
11462         fsexpflag--;
11463         p2 = fnval;
11464         while ((*p2++ = *p++)) ;
11465         p = fnval;
11466         goto fnend;
11467     }
11468 #endif /* NOSEXP */
11469
11470     if (cx == FN_CMDSTK) {              /* \fcmdstack(n1,n2) */
11471         int i, j, k;
11472         char * s;
11473
11474         if (bp[0])
11475           val1 = *(bp[0]) ? evalx(bp[0]) : ckitoa(cmdlvl);
11476         else
11477           val1 = ckitoa(cmdlvl);
11478 #ifdef COMMENT
11479         free(bp[0]);                    /* (evalx() always uses same buffer) */
11480         bp[0] = NULL;                   /* (not any more!) */
11481 #endif /* COMMENT */
11482         failed = 1;
11483         if (argn > 1) {
11484 #ifdef COMMENT
11485             makestr(&(bp[0]),val1);
11486             val1 = bp[0];
11487 #endif /* COMMENT */
11488             val2 = *(bp[1]) ? evalx(bp[1]) : "0";
11489             if (!(chknum(val1) && chknum(val2))) {
11490                 if (fndiags)
11491                   ckmakmsg(fnval,FNVALL,
11492                            "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11493                 goto fnend;
11494             }
11495         } else {
11496             val1 = ckitoa(cmdlvl);
11497             val2 = "0";
11498         }
11499         i = atoi(val1);                 /* Level */
11500         j = atoi(val2);                 /* Flags */
11501         if (i < 0 || i > cmdlvl) {
11502             if (fndiags)
11503               ckmakmsg(fnval,FNVALL,
11504                        "<ERROR:ARG_OUT_OF_RANGE:\\f",fn,"()>",NULL);
11505             goto fnend;
11506         }
11507         failed = 0;
11508         p = fnval;
11509         k = cmdstk[i].src;              /* What (prompt, file, macro) */
11510         if (j) {
11511             ckstrncpy(fnval,ckitoa(k),FNVALL);
11512             goto fnend;
11513         }
11514         switch (k) {
11515           case CMD_KB:
11516             ckstrncpy(fnval,"(prompt)",FNVALL);
11517             break;
11518           case CMD_TF:
11519             s = tfnam[cmdstk[i].lvl];
11520             if (!zfnqfp(s,FNVALL,fnval))
11521               ckstrncpy(fnval,s,FNVALL);
11522             break;
11523           case CMD_MD:
11524             ckstrncpy(fnval,m_arg[cmdstk[i].lvl][0],FNVALL);
11525             break;
11526         }
11527         goto fnend;
11528     }
11529 #ifdef CKFLOAT
11530     if (cx == FN_DIFDATE) {             /* \fdiffdates(d1,d2) */
11531         char * d1, * d2;
11532         d1 = bp[0] ? bp[0] : ckdate();
11533         d2 = bp[1] ? bp[1] : ckdate();
11534         p = (char *)cmdiffdate(d1,d2);
11535         if (!p) {
11536             failed = 1;
11537             if (fndiags) {
11538                 ckmakmsg(fnval,FNVALL,"<ERROR:BAD_DATE:\\f",fn,"()>",NULL);
11539                 p = fnval;
11540             }
11541         }
11542         goto fnend;
11543     }
11544 #endif /* CKFLOAT */
11545     if (cx == FN_CMPDATE) {             /* \fcmddates(d1,d2) */
11546         int x = 0;
11547         char d1[18], d2[18], * dp;
11548         failed = 0;
11549         d1[0] = NUL;
11550         d2[0] = NUL;
11551         p = fnval;
11552         dp = cmcvtdate(bp[0],1);
11553         if (dp) {
11554             ckstrncpy(d1,dp,18);
11555             if ((dp = cmcvtdate(bp[1],1))) {
11556                 ckstrncpy(d2,dp,18);
11557                 x = 1;
11558             }
11559         }
11560         if (x == 0) {
11561             failed = 1;
11562             if (fndiags)
11563               ckmakmsg(fnval,FNVALL,"<ERROR:BAD_DATE:\\f",fn,"()>",NULL);
11564         } else {
11565             x = strcmp(d1,d2);
11566             if (x > 0)
11567               x = 1;
11568             else if (x < 0)
11569               x = -1;
11570             sprintf(fnval,"%d",x);
11571         }
11572         goto fnend;
11573     }
11574     if (cx == FN_TOGMT) {               /* \futcdate(d1) */
11575         char * d1, * dp;
11576         char datebuf[32];
11577         char d2[32];
11578         p = fnval;
11579         failed = 1;
11580         if ((dp = cmcvtdate(bp[0],1))) { /* The given date */
11581             ckstrncpy(datebuf,dp,18);
11582             ckstrncpy(d2,dp,18);        /* local time */
11583             ckstrncat(datebuf,"Z",19);  /* Same time GMT */
11584             if ((dp = cmcvtdate(datebuf,1))) /* converted to local time */
11585               ckstrncpy(datebuf,dp,18);
11586             if ((p = (char *)cmdiffdate(d2,datebuf))) { /* Get offset */
11587                 ckstrncat(d2,p,32);     /* Append offset to local time */
11588                 if ((dp = cmcvtdate(d2,1))) {
11589                     failed = 0;
11590                     ckstrncpy(fnval,dp,FNVALL);
11591                     p = fnval;
11592                 }
11593             }
11594         }
11595         if (failed && fndiags)
11596           ckmakmsg(fnval,FNVALL,"<ERROR:BAD_DATE:\\f",fn,"()>",NULL);
11597         goto fnend;
11598     }
11599     if (cx == FN_DELSEC) {              /* \fdelta2secs(delta-time) */
11600         long secs;
11601         p = fnval;
11602         if ((x = delta2sec(bp[0],&secs)) < 0) {
11603             failed = 1;
11604             if (fndiags)
11605               ckmakmsg(fnval,FNVALL,
11606                        (x == -1) ?
11607                          "<ERROR:BAD_DELTA_TIME:\\f" :
11608                          "<ERROR:OVERFLOW:\\f",
11609                        fn,
11610                        "()>",
11611                        NULL
11612                        );
11613             goto fnend;
11614         }
11615         sprintf(p,"%ld",secs);
11616         goto fnend;
11617     }
11618     if (cx == FN_PC_DU) {
11619         char c, * s = bp[0];
11620         if (!s) s = "";
11621         p = fnval;
11622         while ((c = *s++)) {
11623             if (c == ':') {
11624                 if (*s != '\\')
11625                   *p++ = '/';
11626             } else if (c == '\\') {
11627                 *p++ = '/';
11628             } else {
11629                 *p++ = c;
11630             }
11631         }
11632         *p = NUL;
11633         p = fnval;
11634         goto fnend;
11635     }
11636     if (cx == FN_PC_UD) {               /* Unix to DOS path */
11637         char c, * s = bp[0];
11638         if (!s) s = "";
11639         if (*s == '~') {                /* Skip leading tilde */
11640             s++;
11641             if (*s == '/')
11642               s++;
11643         }
11644         p = fnval;
11645         while ((c = *s++))
11646           *p ++ = (c == '/') ? '\\' : c;
11647         *p = NUL;
11648         p = fnval;
11649         goto fnend;
11650     }
11651     if (cx == FN_KWVAL) {               /* Keyword=Value */
11652         p = dokwval(bp[0],bp[1]?bp[1]:"=");
11653         goto fnend;
11654     }
11655 #ifdef COMMENT
11656 /* Cute idea but doesn't work */
11657     if (cx == FN_SLEEP || cx == FN_MSLEEP) {
11658         p = "";
11659         if (chknum(bp[0])) {
11660             x = atoi(bp[0]);
11661         } else {
11662             failed = 1;
11663             if (fndiags) {
11664                 ckmakmsg(fnval,FNVALL,
11665                          "<ERROR:ARG_NOT_NUMERIC:\\f",fn,"()>",NULL);
11666                 p = fnval;
11667             }
11668             goto fnend;
11669         }
11670         if (cx == FN_SLEEP)
11671           x *= 1000;
11672         msleep(x);
11673         goto fnend;
11674     }
11675 #endif /* COMMENT */
11676
11677 #ifdef NT
11678     if (cx == FN_SNAME) {
11679         GetShortPathName(bp[0],fnval,FNVALL);
11680         goto fnend;
11681     }
11682     if (cx == FN_LNAME) {
11683         ckGetLongPathName(bp[0],fnval,FNVALL);
11684         goto fnend;
11685     }
11686 #endif /* NT */
11687
11688 /*
11689   \femailaddress():
11690   Picks the email address out of an RFC 2822 From: or Sender: header.
11691   Added 26 Nov 2005.  Handles all common, and some uncommon, cases but
11692   doesn't totally bother about nested comments.  Needed this for fetching
11693   email from a POP server and then constructing the BSD "From " line.
11694   Works with or without the "From: " or "Sender: " tag.
11695 */
11696     if (cx == FN_EMAIL) {
11697         char c, * s = bp[0], * s2, * s3, * ap = "";
11698         int k, state = 0, quote = 0, infield = 0;
11699         int pc = 0;                     /* For nested comments */
11700         if (!s) s = "";
11701         if (!*s) goto xemail;
11702
11703         if (ckindex("From: ",s,0,0,0) == 1) s += 5;
11704         if (ckindex("Sender: ",s,0,0,0) == 1) s += 7;
11705
11706         k = strlen(s);                  /* Strip junk from end */
11707         if (k < 1) goto xemail;
11708         k--;
11709         while (k >= 0 && s[k] == CR || s[k] == LF)
11710           s[k--] = NUL;
11711         while (k >= 0 && s[k] == SP || s[k] == HT)
11712           s[k--] = NUL;
11713         if (k == 0)
11714           goto xemail;
11715
11716 #ifndef COMMENT                      /* Simple method if not 100% foolproof */
11717         k = 0;
11718         for (s2 = s; *s2; s2++) {       /* Find at-sign */
11719             if (*s2 == '@') {
11720                 k++;                    /* If more than one use rightmost */
11721                 s3 = s2;
11722             }
11723         }
11724         if (k < 1)                      /* No at-sign */
11725           goto xemail;
11726
11727         for (ap = s3-1; ap >= s; ap--) { /* Back up to beginning of address */
11728             if (isspace(*ap) || *ap == '<') {
11729                 ap++;
11730                 break;
11731             }
11732             if (ap == s)
11733               break;
11734         }
11735         for (s2 = s3+1; *s2; s2++) {    /* Find end of address */
11736             if (isspace(*s2) || *s2 == '>')
11737               break;
11738         }
11739         *s2-- = NUL;
11740         if (*ap == '[' && *s2 == ']') { /* Handle [blah@blah.blah] */
11741             ap++;
11742             *s2 = NUL;
11743         }
11744         if (!ckstrcmp(ap,"mailto:",7,0)) /* Handle mailto: URLs */
11745           ap += 7;
11746
11747 #else  /* Too complicated and error-prone */
11748
11749         k = 0;
11750         for (s2 = s; *s2; s2++) {       /* Strip leading whitespace */
11751             if (*s2 == SP || *s2 == HT) {
11752                 k = 1;
11753                 break;
11754             }
11755         }
11756         if (!k) {                       /* Simple address */
11757             ap = s;
11758             goto xemail;
11759         }
11760         do {                            /* Not simple, have to extract it */
11761             if (quote) {
11762                 quote = 0;
11763                 continue;
11764             } else if (*s == '\\') {
11765                 quote = 1;
11766                 continue;
11767             }
11768             switch (state) {
11769               case 0:
11770                 if (!infield && *s == '"') { /* Quoted string */
11771                     infield = 1;
11772                     c = '"';
11773                     state = 1;
11774                 } else if (!infield && *s == '(') { /* Comment in parens */
11775                     pc++;
11776                     infield = 1;
11777                     c = ')';
11778                     if (*ap) *s = NUL;
11779                     state = 1;
11780                 } else if (!infield && *s == '<') { /* Address */
11781                     infield = 1;
11782                     c = '>';
11783                     ap = s+1;
11784                     state = 2;
11785                 } else if (infield && (*s == SP || *s == HT)) {
11786                     infield = 0;
11787                 } else {                /* One or more bare words */
11788                     infield = 1;        /* Could be an address */
11789                     if (!*ap) ap = s;   /* Could be comments */
11790                 }
11791                 continue;
11792               case 1:                   /* In Quoted string or Comment */
11793                 if (infield && *s == c) { /* Look for end */
11794                     infield = 0;
11795                     *s++ = NUL;
11796                     while (*s == SP || *s == HT) s++;
11797                     if (!*ap)
11798                       ap = s;
11799                     state = 0;
11800                 }
11801                 continue;
11802               case 2:                   /* In address */
11803                 if (infield && *s == c) { /* Looking for end */
11804                     infield = 0;
11805                     *s = NUL;
11806                     break;
11807                 }
11808             }
11809         } while (*s++);
11810
11811       xemail:
11812         if (*ap) {
11813             while (*ap == SP || *ap == HT) ap++;
11814         }
11815         k = strlen(ap) - 1;
11816         while (k >= 0 && (ap[k] == SP || ap[k] == HT))
11817           ap[k--] = NUL;
11818         if (*ap) {
11819             failed = 0;
11820             if (*ap == '<') {
11821                 k = strlen(ap);
11822                 if (*(ap+k-1) == '>') {
11823                     ap[k-1] = NUL;
11824                     ap++;
11825                 }
11826             }
11827         } else
11828           failed = 1;
11829         /* Here we might also want check against "*@*.*" */
11830 #endif  /* COMMENt */
11831       xemail:
11832         ckstrncpy(fnval,ap,FNVALL);
11833         goto fnend;
11834     }
11835
11836 /*
11837    \fpicture():   Get dimensions of GIF or JPG image.
11838    fdc June 2006
11839 */
11840     if (cx == FN_PICTURE) {
11841         FILE *fp = NULL;
11842         int c, x, w = 0, h = 0, eof = 0;
11843         unsigned int i, j, k;
11844         unsigned char buf[1024];
11845         char abuf[16], * p, * s;
11846         char ** ap = NULL;
11847
11848         p = fnval;                      /* Point to result */
11849         failed = 1;                     /* Assume failure */
11850         if (argn > 1) {
11851             int xi;
11852             ckstrncpy(abuf,bp[1],16);   /* Get array reference */
11853             s = abuf;
11854             if (*s == CMDQ) s++;
11855             if (fndiags)                /* Default is this error message */
11856               ckmakmsg(fnval,FNVALL,
11857                        "<ERROR:ARG_BAD_ARRAY:\\f",fn,"()>",NULL);
11858             if (s[0] != '&')            /* "Address" of array */
11859               goto fnend;
11860             if (s[2])
11861               if (s[2] != '[' || s[3] != ']')
11862                 goto fnend;
11863             if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */
11864               s[1] += 32;
11865             if ((xi = dclarray(s[1],2)) < 0) /* Two elements */
11866               goto fnend;
11867             ap = a_ptr[xi];             /* Point to array we just declared */
11868         }
11869         s = bp[0];                      /* Filename */
11870         failed = 0;                     /* From here on we don't fail */
11871         p[0] = '0';                     /* Default return value */
11872         p[1] = NUL;
11873         if (!ckmatch("*.{jpg,jpeg,gif}$",s,0,1+4)) /* Appropriate name? */
11874           goto fnend;                   /* No, fail */
11875         fp = fopen(s, "r");             /* Open it */
11876         if (fp == NULL) {               /* Can't, fail */
11877             p[0] = '-';
11878             p[1] = '1';
11879             p[2] = NUL;                 /* Return -1 */
11880             goto fnend;
11881         }
11882         k = strlen(s);
11883         if (!ckstrcmp(&s[k-4],".gif",4,0)) { /* GIF file */
11884             if (fread(buf,1,10,fp) != 10) {
11885                 fclose(fp);
11886                 goto fnend;
11887             }
11888             /* Check signature */
11889             if (ckstrcmp((char *)buf,"GIF87a",6,0) &&
11890                 ckstrcmp((char *)buf,"GIF89a",6,0)) {
11891                 fclose(fp);
11892                 goto fnend;
11893             }
11894             w = buf[6] + 256 * buf[7];
11895             h = buf[8] + 256 * buf[9];
11896             goto picend;
11897         } else if (!ckstrcmp(&s[k-4],".jpg",4,0) || /* JPEG file */
11898                    !ckstrcmp(&s[k-5],".jpeg",5,0)) {
11899             if (fread(buf,1,2,fp) != 2) {
11900                 fclose(fp);
11901                 goto fnend;
11902             }
11903             if (buf[0] != 0xff || buf[1] != 0xd8) { /* Check signature */
11904                 fclose(fp);
11905                 goto fnend;
11906             }
11907             eof = 0;
11908             while (!eof) {              /* Loop for each marker */
11909                 while (!eof) {          /* Find next marker */
11910                     c = getc(fp);
11911                     if (c == (unsigned int)EOF) {
11912                         eof++;
11913                         break;
11914                     }
11915                     if (c == 0xff) {
11916                         buf[0] = c;
11917                         c = getc(fp);
11918                         if (c == (unsigned int)EOF) {
11919                             eof++;
11920                             break;
11921                         }
11922                         buf[1] = c;
11923                         if (c == 0xd9)
11924                           eof++;
11925                         if (c >= 0xc0 && c <= 0xfe)
11926                           break;
11927                     }
11928                 }
11929                 if (eof) break;
11930                 x = buf[1];
11931                 if (x == 0xc0 || x == 0xc1 || x == 0xc2 || x == 0xc3 ||
11932                     x == 0xc9 || x == 0xca || x == 0xcb) {
11933                     if (fread(buf,1,7,fp) != 7) {
11934                         fclose(fp);
11935                         goto fnend;
11936                     }
11937                     h = buf[3] * 256 + buf[4];
11938                     w = buf[5] * 256 + buf[6];
11939                     goto picend;
11940                 } else {                /* Not a desired field */
11941                     if (feof(fp)) {
11942                         eof++;
11943                         break;
11944                     }
11945                     if (fread(buf,1,2,fp) != 2) { /* Length of this field */
11946                         fclose(fp);
11947                         goto fnend;
11948                     }
11949                     j = 256 * buf[0] + buf[1] - 2; /* Skip next field */
11950                     if (CKFSEEK(fp,(CK_OFF_T)j,SEEK_CUR) != 0) {
11951                         fclose(fp);
11952                         goto fnend;
11953                     }
11954                 }
11955             } 
11956         }
11957       picend:
11958         fclose(fp);
11959         if (ap) {
11960             makestr(&(ap[0]),"2");
11961             makestr(&(ap[1]),ckitoa(w));
11962             makestr(&(ap[2]),ckitoa(h));
11963         }
11964         if (w > 0 && h > 0) {
11965             if (w > h) p[0] = '1';
11966             if (h >= w) p[0] = '2';
11967         }
11968         goto fnend;
11969     }
11970     if (cx == FN_PID) {
11971         int x = -1;
11972         if (chknum(bp[0])) {            /* Need numeric argument */
11973             int pid;
11974             pid = atoi(bp[0]);          /* Convert to int */
11975 #ifdef UNIX
11976             if (kill(pid,0) < 0) {      /* Test it */
11977                 if (errno ==
11978 #ifdef ESRCH
11979                     ESRCH               /* No such process */
11980 #else
11981                     3
11982 #endif  /* ESRCH */
11983                     )
11984                   x = 0;
11985             } else                      /* Process exists */
11986               x = 1;
11987 #endif  /* UNIX */
11988         }
11989         sprintf(fnval,"%d",x);          /* SAFE */
11990         goto fnend;
11991     }
11992
11993     if (cx == FN_FUNC) {
11994         char * s = bp[0];
11995         p = "0";
11996         debug(F111,"ffunc",s,argn);
11997         if (argn > 0) {
11998             int x, y;
11999             for (p = s; *p; p++) {      /* Chop off trailing parens if any */
12000                 if (*p == '(') {
12001                     *p = NUL;
12002                     break;
12003                 }
12004             }
12005             /* Chop off leading "\\f" or "\f" or "f" */
12006             p = s;
12007             if (*p == CMDQ)             /* Allow for \\f... */
12008               p++;
12009             if (*p == CMDQ && (*(p+1) == 'f' || *(p+1) == 'F')) { /* or \f */
12010                 p += 2;
12011             } else if (*p == 'f' || *p == 'F') { /* or just f */
12012                 p++;
12013             }
12014             y = lookup(fnctab,p,nfuncs,&x); /* Look up the result */
12015             debug(F111,"ffunc",p,y);
12016             p = (y > -1) ? "1" : "0";
12017         }
12018         goto fnend;
12019     }
12020     if (cx == FN_RECURSE) {
12021         int t, n;
12022         char * s;
12023         fnval[0] = NUL;                 /* Default result is empty string */
12024         s = bp[0];                      /* Check for null argument */
12025         if (!s) s = "";                 /* or empty argument */
12026         if (!*s) goto fnend;            /* in which case return empty string */
12027         n = FNVALL;                     /* Not empty, max size for result */
12028         s = fnval;                      /* Location of result */
12029         {
12030             /* Force VARIABLE-EVALUATION SIMPLE RECURSIVE */
12031             /* NOTE: This is vulnerable to SIGINT and whatnot... */
12032             int tmp = vareval;          /* Save VARIABLE-EVALUATION setting */
12033             vareval = 1;                /* Force it to RECURSIVE */
12034             zzstring(bp[0],&s,&n);      /* Expand arg into result space */ 
12035             vareval = tmp;              /* Restore VARIABLE-EVALUATION */
12036         }
12037         goto fnend;
12038     }
12039
12040     if (cx == FN_XLATE) {               /* f_cvtcset() */
12041 #ifdef NOFRILLS
12042         ckstrncpy(fnval,bp[0],FNVALL);  
12043 #else
12044 #ifndef NOUNICODE
12045         _PROTOTYP( char * cvtstring, (char *, int, int) );
12046         char * string, * cset1, * cset2;
12047         int id1, id2;
12048 #endif  /* NOUNICODE */
12049         fnval[0] = NUL; 
12050 #ifdef NOUNICODE
12051         ckstrncpy(fnval,bp[0],FNVALL);  
12052 #else
12053         string = bp[0] ? bp[0] : "";    /* String to convert */
12054         if (!*string) goto fnend;       /* It's empty */
12055
12056         cset1 = bp[1] ? bp[1] : "ascii"; /* Current charset of string */
12057         cset2 = bp[2] ? bp[2] : "ascii"; /* Charset to convert to */
12058
12059         id1 = lookup(fcstab,cset1,nfilc,NULL); /* Lookup 'from' set */
12060         if (id1 < 0) {
12061             failed = 1;
12062             ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN_CHARSET:\\f",fn,"()>",NULL);
12063             goto fnend;
12064         }
12065         id2 = lookup(fcstab,cset2,nfilc,NULL); /* Lookup 'to' set */
12066         if (id2 < 0) {
12067             failed = 1;
12068             ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN_CHARSET:\\f",fn,"()>",NULL);
12069             goto fnend;
12070         }
12071         string = cvtstring(string,id1,id2);
12072         ckstrncpy(fnval,string,FNVALL);
12073 #endif  /* NOUNICODE */
12074 #endif  /* NOFRILLS */
12075         goto fnend;
12076     }
12077
12078 /* Decode strings containing hex escapes */
12079
12080     if (cx == FN_UNPCT) {               /* \fdecodehex() */
12081         char *s1, *s2;
12082         char *prefix;                   /* Can be 1 or 2 chars */
12083         char buf[3];
12084         int n = 0, k;
12085
12086         p = fnval;
12087         *p = NUL;
12088         if (argn < 1) goto fnend;       /* Empty string */
12089
12090         s1 = bp[0] ? bp[0] : "";        /* Original string */
12091         prefix = bp[1] ? bp[1] : "%%";  /* Hex byte prefix */
12092         n = (int)strlen(prefix);        /* Length of prefix */
12093         if (n < 1 || n > 2) {           /* must be 1 or 2 */
12094             ckmakmsg(fnval,FNVALL,
12095                        "<ERROR:INVALID_HEX_PREFIX:\\f",fn,"()>",NULL);
12096             goto xunpct;
12097         }
12098         while (*s1) {
12099             if (!ckstrcmp(s1,prefix,n,0)) { /* Case-independent */
12100                 if (!*(s1+n)) {
12101                     ckmakmsg(fnval,FNVALL,
12102                              "<ERROR:INCOMPLETE_SEQUENCE:\\f",fn,"()>",NULL);
12103                     goto xunpct;
12104                 }
12105                 buf[0] = *(s1+n);       /* First hex character */
12106                 buf[1] = *(s1+n+1);     /* Second hex character */
12107                 buf[2] = NUL;
12108                 if ((k = ckhexbytetoint((char *)buf)) > -1) {
12109                     *p++ = (char) k;    /* Deposit decoded result */
12110                     s1 += 2+n;          /* and advance the source pointer */
12111                 } else {                /* Fail on conversion error */
12112                     ckmakmsg(fnval,FNVALL,
12113                              "<ERROR:NON_HEX_CHARS:\\f",fn,"()>",NULL);
12114                     goto xunpct;
12115                 }
12116             } else {                    /* Not a hex escape sequence */
12117                 *p++ = *s1++;           /* Just copy the character */
12118             }
12119         }
12120         *p = NUL;                       /* Terminate the result string */
12121         failed = 0;                     /* Say we didn't fail */
12122         p = fnval;                      /* Set up result pointer */
12123         goto fnend;                     /* and finish */
12124
12125       xunpct:                           /* Error exit */
12126         p = fnval;
12127         failed = 1;
12128         goto fnend;
12129     }
12130
12131 /* Check a string for encoding family */
12132
12133     if (cx == FN_STRINGT) {             /* \fstringtype() */
12134         p = "UNK";
12135         switch (scanstring(bp[0])) {
12136           case FT_7BIT: p = "7BIT"; break;
12137           case FT_8BIT: p = "8BIT"; break;
12138           case FT_UTF8: p = "UTF8"; break;
12139           case FT_UCS2: p = "UCS2"; break;
12140           case FT_TEXT: p = "TEXT"; break;
12141           case FT_BIN:  p = "BINARY"; break;
12142         }
12143         ckstrncpy(fnval,p,FNVALL);
12144         p = fnval;
12145         goto fnend;
12146     }
12147
12148 /* String compare s1, s2, [ case ], [ start ] , [ len ] */
12149
12150     if (cx == FN_STRCMP) {
12151         int docase = 0;                 /* Case matters or not */
12152         int start = 0;                  /* Start of substring */
12153         int len = -1;                   /* Length of substring to compare */
12154         int x; char * s1, * s2;         /* workers */
12155
12156         p = "0";                        /* Return value */
12157         if (argn == 0) {                /* Two null strings are equal */
12158             ckstrncpy(fnval,p,FNVALL);
12159             p = fnval;
12160             goto fnend;
12161         }
12162         if (argn == 1) {                /* Non-null string > null string */
12163             p = "1";
12164             ckstrncpy(fnval,p,FNVALL);
12165             p = fnval;
12166             goto fnend;
12167         }
12168         if (argn > 2) {
12169             s = *(bp[2]) ? evalx(bp[2]) : "0"; /* 0 = caseless */
12170             if (chknum(s)) docase = atoi(s);
12171             if (argn > 3) {
12172                 s = *(bp[3]) ? evalx(bp[3]) : "1"; /* start is 1-based */
12173                 if (chknum(s)) start = atoi(s);
12174                 if (argn > 4) {
12175                     s = *(bp[4]) ? evalx(bp[4]) : "-1"; /* -1 = whole thing */
12176                     if (chknum(s)) len = atoi(s);
12177                 }               
12178             }        
12179         }
12180         if (start > 0) start--;         /* start is 0-based internally */
12181         s1 = bp[0];                     /* Get length of first arg */
12182         x = (int)strlen(s1);
12183         if (x > start)                  /* Point to start position of s1 */
12184           s1 += start;
12185         else
12186           s1 = "";
12187         s2 = bp[1];                     /* Get length of second arg */
12188         x = (int)strlen(s2);
12189         if (x > start)                  /* Point to start position of s2 */
12190           s2 += start;
12191         else
12192           s2 = "";
12193         x = ckstrcmp(s,s2,len,docase);
12194         p = ckitoa(x);
12195         ckstrncpy(fnval,p,FNVALL);
12196         p = fnval;
12197         goto fnend;
12198     }
12199
12200 /* Note: when adding new functions remember to update dohfunc in ckuus2.c. */
12201
12202     failed = 1;
12203     if (fndiags)
12204       ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN_FUNCTION:\\f",fn,"()>",NULL);
12205
12206   fnend:
12207     /* Free temporary storage for aguments */
12208     for (k = 0; k < argn; k++) if (bp[k]) free(bp[k]);
12209     fndepth--;
12210     if (failed) {                       /* Handle failure */
12211         debug(F111,"fnend",fnval,errno);
12212         if (!p) p = "";
12213         if (p[0]) {
12214             /* In case this wasn't caught above... */
12215             k = strlen(p);
12216             if (p[0] != '<' && p[k-1] != '>') {
12217                 ckmakmsg(fnval,FNVALL,"<ERROR:BAD_ARG:\\f",fn,"()>",NULL);
12218                 p = fnval;
12219             }
12220         } else {
12221             ckmakmsg(fnval,FNVALL,"<ERROR:UNKNOWN:\\f",fn,"()>",NULL);
12222             p = fnval;
12223         }
12224         if (fnerror)                    /* SET FUNCTION ERROR ON */
12225           fnsuccess = 0;                /* Make command fail (see ckuus5.c) */
12226         debug(F111,"fneval failed",p,fnsuccess);
12227         if (fndiags)                    /* SET FUNCTION DIAGNOSTICS ON */
12228           printf("?%s\n",p);            /* Print error message now. */
12229         else
12230           return("");                   /* Return nothing. */
12231     }
12232     return(p);
12233 }
12234 #endif /* NOSPL */
12235
12236 static char ckpidbuf[32] = "????";
12237
12238 #ifdef VMS
12239 _PROTOTYP(long zgpid,(void));
12240 #endif /* VMS */
12241
12242 char *
12243 ckgetpid() {                            /* Return pid as string */
12244 #ifdef CK_PID
12245 #ifdef OS2
12246 #define getpid _getpid
12247     unsigned long zz;
12248 #else
12249     long zz;
12250 #endif /* OS2 */
12251 #ifdef VMS
12252     zz = zgpid();
12253 #else
12254     zz = getpid();
12255 #endif /* VMS */
12256     sprintf(ckpidbuf,"%ld",zz);         /* SAFE */
12257 #endif /* CK_PID */
12258     return((char *)ckpidbuf);
12259 }
12260
12261 #ifndef NOSPL
12262 #define EMBUFLEN 128                    /* Error message buffer length */
12263
12264 static char embuf[EMBUFLEN+1];
12265
12266 char *                                  /* Evaluate builtin variable */
12267 nvlook(s) char *s; {
12268     int x, y, cx;
12269     long z;
12270     char *p;
12271 #ifndef NODIAL
12272     MDMINF * m;
12273 #endif /* NODIAL */
12274 #ifndef NOKVERBS                        /* Keyboard macro material */
12275     extern int keymac, keymacx;
12276 #endif /* NOKVERBS */
12277 #ifdef CK_LOGIN
12278     extern int isguest;
12279 #endif /* CK_LOGIN */
12280     if (!s) s = "";
12281     x = strlen(s);
12282     if (fndiags) {                      /* FUNCTION DIAGNOSTIC ON */
12283         if (x + 32 < EMBUFLEN)
12284           sprintf(embuf,"<ERROR:NO_SUCH_VARIABLE:\\v(%s)>",s); /* SAFE */
12285         else
12286           sprintf(embuf,"<ERROR:NO_SUCH_VARIABLE>"); /* SAFE */
12287     } else                              /* FUNCTION DIAGNOSTIC OFF */
12288       embuf[0] = NUL;
12289     x = VVBUFL;
12290     p = vvbuf;
12291     if (zzstring(s,&p,&x) < 0) {        /* e.g. for \v(\%a) */
12292         y = -1;
12293     } else {
12294         s = vvbuf;
12295         y = lookup(vartab,s,nvars,&x);
12296     }
12297     cx = y;                             /* y is too generic */
12298 #ifndef NODIAL
12299     m = (mdmtyp > 0) ? modemp[mdmtyp] : NULL; /* For \v(m_xxx) variables */
12300 #endif /* NODIAL */
12301
12302     debug(F101,"nvlook y","",y);
12303
12304     switch (y) {
12305       case VN_ARGC:                     /* ARGC */
12306         sprintf(vvbuf,"%d",maclvl < 0 ? topargc : macargc[maclvl]); /* SAFE */
12307         return(vvbuf);
12308
12309       case VN_ARGS:                     /* ARGS */
12310         sprintf(vvbuf,"%d",xargs);      /* SAFE */
12311         return(vvbuf);
12312
12313       case VN_COUN:                     /* COUNT */
12314         sprintf(vvbuf,"%d",count[cmdlvl]); /* SAFE */
12315         return(vvbuf);
12316
12317       case VN_DATE:                     /* DATE */
12318         ztime(&p);                      /* Get "asctime" string */
12319         if (p == NULL || *p == NUL) return(NULL);
12320         vvbuf[0] = p[8];                /* dd */
12321         vvbuf[1] = p[9];
12322         vvbuf[2] = SP;
12323         vvbuf[3] = p[4];                /* mmm */
12324         vvbuf[4] = p[5];
12325         vvbuf[5] = p[6];
12326         vvbuf[6] = SP;
12327         for (x = 20; x < 24; x++)       /* yyyy */
12328           vvbuf[x - 13] = p[x];
12329         vvbuf[11] = NUL;
12330         return(vvbuf);
12331
12332       case VN_NDAT:                     /* Numeric date */
12333         ckstrncpy(vvbuf,zzndate(),VVBUFL);
12334         return(vvbuf);
12335
12336       case VN_DIRE:                     /* DIRECTORY */
12337         s = zgtdir();                   /* Get current directory */
12338         if (!s)
12339 #ifdef UNIXOROSK
12340           s = "./";
12341 #else
12342 #ifdef VMS
12343           s = "[]";
12344 #else
12345           s = "";
12346 #endif /* VMS */
12347 #endif /* UNIXOROSK */
12348         ckstrncpy(vvbuf,s,VVBUFL);
12349         s = vvbuf;
12350 #ifdef UNIXOROSK
12351         x = strlen(s);
12352         if (x < VVBUFL - 1) {
12353             if (s[x-1] != '/') {
12354                 s[x] = '/';
12355                 s[x+1] = NUL;
12356             }
12357         }
12358 #endif /* UNIXOROSK */
12359         return(s);
12360
12361       case VN_FILE:                     /* filespec */
12362         return(fspec);
12363
12364       case VN_HOST:                     /* host name */
12365         if (*myhost) {                  /* If known */
12366             return(myhost);             /* return it. */
12367         } else {                        /* Otherwise */
12368             ckstrncpy(vvbuf,"unknown",VVBUFL); /* just say "unknown" */
12369             return(vvbuf);
12370         }
12371
12372       case VN_SYST:                     /* System type */
12373 #ifdef UNIX
12374         ckstrncpy(vvbuf,"UNIX",VVBUFL);
12375 #else
12376 #ifdef VMS
12377         ckstrncpy(vvbuf,"VMS",VVBUFL);
12378 #else
12379 #ifdef OSK
12380         ckstrncpy(vvbuf,"OS9/68K",VVBUFL);
12381 #else
12382 #ifdef AMIGA
12383         ckstrncpy(vvbuf,"Amiga",VVBUFL);
12384 #else
12385 #ifdef MAC
12386         ckstrncpy(vvbuf,"Macintosh",VVBUFL);
12387 #else
12388 #ifdef OS2
12389 #ifdef NT
12390         ckstrncpy(vvbuf,"WIN32",VVBUFL) ;
12391 #else /* NT */
12392         ckstrncpy(vvbuf,"OS/2",VVBUFL);
12393 #endif /* NT */
12394 #else
12395 #ifdef datageneral
12396         ckstrncpy(vvbuf,"AOS/VS",VVBUFL);
12397 #else
12398 #ifdef GEMDOS
12399         ckstrncpy(vvbuf,"Atari_ST",VVBUFL);
12400 #else
12401 #ifdef STRATUS
12402         ckstrncpy(vvbuf,"Stratus_VOS",VVBUFL);
12403 #else
12404         ckstrncpy(vvbuf,"unknown",VVBUFL);
12405 #endif /* STRATUS */
12406 #endif /* GEMDOS */
12407 #endif /* datageneral */
12408 #endif /* OS2 */
12409 #endif /* MAC */
12410 #endif /* AMIGA */
12411 #endif /* OSK */
12412 #endif /* VMS */
12413 #endif /* UNIX */
12414         return(vvbuf);
12415
12416       case VN_SYSV:                     /* System herald */
12417 #ifdef IKSD
12418 #ifdef CK_LOGIN
12419         if (inserver && isguest)
12420           return("");
12421 #endif /* CK_LOGIN */
12422 #endif /* IKSD */
12423         for (x = y = 0; x < VVBUFL; x++) {
12424             if (ckxsys[x] == SP && y == 0) continue;
12425             vvbuf[y++] = (char) ((ckxsys[x] == SP) ? '_' : ckxsys[x]);
12426         }
12427         vvbuf[y] = NUL;
12428         return(vvbuf);
12429     } /* Break up long switch statements... */
12430
12431     switch(y) {
12432       case VN_TIME:                     /* TIME. Assumes that ztime returns */
12433         ztime(&p);                      /* "Thu Feb  8 12:00:00 1990" */
12434         if (p == NULL || *p == NUL)     /* like asctime()! */
12435           return("");
12436         for (x = 11; x < 19; x++)       /* copy hh:mm:ss */
12437           vvbuf[x - 11] = p[x];         /* to vvbuf */
12438         vvbuf[8] = NUL;                 /* terminate */
12439         return(vvbuf);                  /* and return it */
12440
12441       case VN_NTIM:                     /* Numeric time */
12442         ztime(&p);                      /* "Thu Feb  8 12:00:00 1990" */
12443         if (p == NULL || *p == NUL)     /* like asctime()! */
12444           return(NULL);
12445         z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
12446         sprintf(vvbuf,"%ld",z);         /* SAFE */
12447         return(vvbuf);
12448
12449 #ifdef CK_TTYFD
12450       case VN_TTYF:                     /* TTY file descriptor */
12451         sprintf(vvbuf,"%d",             /* SAFE */
12452 #ifdef VMS
12453                 vmsttyfd()
12454 #else
12455                 ttyfd
12456 #endif /* VMS */
12457                 );
12458         return(vvbuf);
12459 #endif /* CK_TTYFD */
12460
12461       case VN_VERS:                     /* Numeric Kermit version number */
12462         sprintf(vvbuf,"%ld",vernum);    /* SAFE */
12463         return(vvbuf);
12464
12465       case VN_XVNUM:                    /* Product-specific version number */
12466         sprintf(vvbuf,"%ld",xvernum);   /* SAFE */
12467         return(vvbuf);
12468
12469       case VN_HOME:                     /* Home directory */
12470         return(homepath());
12471
12472       case VN_IBUF:                     /* INPUT buffer */
12473         return((char *)inpbuf);
12474
12475       case VN_ICHR:                     /* INPUT character */
12476         inchar[1] = NUL;
12477         return((char *)inchar);
12478
12479       case VN_ICNT:                     /* INPUT character count */
12480         sprintf(vvbuf,"%d",incount);    /* SAFE */
12481         return(vvbuf);
12482
12483       case VN_SPEE: {                   /* Transmission SPEED */
12484           long t;
12485           t = ttgspd();
12486           if (t < 0L)
12487             sprintf(vvbuf,"unknown");   /* SAFE */
12488           else
12489             sprintf(vvbuf,"%ld",t);     /* SAFE */
12490           return(vvbuf);
12491       }
12492
12493       case VN_SUCC:                     /* SUCCESS flag */
12494         /* Note inverted sense */
12495         sprintf(vvbuf,"%d",(success == 0) ? 1 : 0); /* SAFE */
12496         return(vvbuf);
12497
12498       case VN_LINE: {                   /* LINE */
12499 #ifdef DEBUG
12500           if (deblog) {
12501               debug(F111,"\\v(line) local",ttname,local);
12502               debug(F111,"\\v(line) inserver","",inserver);
12503 #ifdef TNCODE
12504               debug(F111,"\\v(line) tcp_incoming","",tcp_incoming);
12505 #endif /* TNCODE */
12506 #ifdef CK_TAPI
12507               debug(F111,"\\v(line) tttapi","",tttapi);
12508 #endif /* CK_TAPI */
12509           }
12510 #endif /* DEBUG */
12511
12512 #ifdef CK_TAPI
12513           if (tttapi) {                 /* If I have made a TAPI connection */
12514               int i;                    /* return the TAPI device name */
12515               for (i = 0; i < ntapiline; i++) {
12516                   if (!strcmp(ttname,tapilinetab[i].kwd)) {
12517                       p = _tapilinetab[i].kwd;
12518                       return(p);
12519                   }
12520               }
12521           }
12522 #endif /* CK_TAPI */
12523 #ifndef NOXFER
12524           if (inserver                  /* If I am a TCP server */
12525 #ifdef TNCODE
12526               || tcp_incoming
12527 #endif /* TNCODE */
12528               )
12529 #ifdef TCPSOCKET
12530             p = ckgetpeer();            /* return peer name */
12531           else
12532 #endif /* TCPSOCKET */
12533 #endif /* NOXFER */
12534           if (local)                    /* Otherwise if in local mode */
12535             p = (char *) ttname;        /* return SET LINE / SET HOST name */
12536           else                          /* Otherwise */
12537             p = "";                     /* return empty string */
12538           if (!p)                       /* In case ckgetpeer() returns */
12539             p = "";                     /* null pointer... */
12540           debug(F110,"\\v(line) p",p,0);
12541           if (!*p)
12542             p = (char *) ttname;
12543           return(p);
12544       }
12545       case VN_PROG:                     /* Program name */
12546         return("C-Kermit");
12547
12548     } /* Break up long switch statements... */
12549
12550     switch(y) {
12551       case VN_RET:                      /* Value of most recent RETURN */
12552         debug(F111,"\\v(return)",mrval[maclvl+1],maclvl+1);
12553         p = mrval[maclvl+1];
12554         if (p == NULL) p = "";
12555         return(p);
12556
12557       case VN_FFC:                      /* Size of most recent file */
12558         sprintf(vvbuf, "%s", ckfstoa(ffc)); /* SAFE */
12559         return(vvbuf);
12560
12561       case VN_TFC:                      /* Size of most recent file group */
12562         sprintf(vvbuf, "%s", ckfstoa(tfc)); /* SAFE */
12563         return(vvbuf);
12564
12565       case VN_CPU:                      /* CPU type */
12566 #ifdef IKSD
12567 #ifdef CK_LOGIN
12568         if (inserver && isguest)
12569           return("");
12570 #endif /* CK_LOGIN */
12571 #endif /* IKSD */
12572 #ifdef OS2
12573          {
12574             char * getcpu(void) ;
12575             return getcpu();
12576          }
12577 #else /* OS2 */
12578 #ifdef CKCPU
12579         return(CKCPU);                  /* Traditionally, compile-time value */
12580 #else
12581 #ifdef CK_UTSNAME
12582         {                               /* But if none, try runtime value */
12583             extern char unm_mch[];
12584             return((char *)unm_mch);
12585         }
12586 #else
12587         p = getenv("HOSTTYPE");         /* 20091116 */
12588         if (p) if (*p) return(p);
12589         return("unknown");
12590 #endif /* CK_UTSNAME */
12591 #endif /* CKCPU */
12592 #endif /* OS2 */
12593
12594       case VN_CMDL:                     /* Command level */
12595         sprintf(vvbuf, "%d", cmdlvl);   /* SAFE */
12596         return(vvbuf);
12597
12598       case VN_DAY:                      /* Day of week */
12599         ztime(&p);
12600         if (p != NULL && *p != NUL)     /* ztime() succeeded. */
12601           ckstrncpy(vvbuf,p,4);
12602         else
12603           vvbuf[0] = NUL;               /* ztime() failed. */
12604         return(vvbuf);                  /* Return what we got. */
12605
12606       case VN_NDAY: {                   /* Numeric day of week */
12607           long k;
12608           z = mjd(zzndate());           /* Get modified Julian date */
12609           k = (((int)(z % 7L)) + 3) % 7; /* Get day number */
12610           sprintf(vvbuf,"%ld",k);       /* SAFE */
12611           return(vvbuf);
12612       }
12613
12614       case VN_LCL:                      /* Local (vs remote) mode */
12615         ckstrncpy(vvbuf, local ? "1" : "0",VVBUFL);
12616         return(vvbuf);
12617
12618       case VN_CMDS:                     /* Command source */
12619         if (cmdstk[cmdlvl].src == CMD_KB)
12620           ckstrncpy(vvbuf,"prompt",VVBUFL);
12621         else if (cmdstk[cmdlvl].src == CMD_MD)
12622           ckstrncpy(vvbuf,"macro",VVBUFL);
12623         else if (cmdstk[cmdlvl].src == CMD_TF)
12624           ckstrncpy(vvbuf,"file",VVBUFL);
12625         else
12626           ckstrncpy(vvbuf,"unknown",VVBUFL);
12627         return(vvbuf);
12628
12629       case VN_CMDF:                     /* Current command file name */
12630 #ifdef COMMENT                          /* (see comments above) */
12631         if (tfnam[tlevel]) {            /* (near dblbs declaration) */
12632             dblbs(tfnam[tlevel],vvbuf,VVBUFL);
12633             return(vvbuf);
12634         } else return("");
12635 #else
12636         if (tlevel < 0)
12637           return("");
12638         else
12639           return(tfnam[tlevel] ? tfnam[tlevel] : "");
12640 #endif /* COMMENT */
12641
12642       case VN_MAC:                      /* Current macro name */
12643         return((maclvl > -1) ? m_arg[maclvl][0] : "");
12644
12645       case VN_EXIT:
12646         sprintf(vvbuf,"%d",xitsta);     /* SAFE */
12647         return(vvbuf);
12648
12649     } /* Break up long switch statements... */
12650
12651     switch(y) {
12652       case VN_PRTY: {                   /* Parity */
12653           char *ss;
12654           switch (parity) {
12655             case 0:   ss = "none";  break;
12656             case 'e': ss = "even";  break;
12657             case 'm': ss = "mark";  break;
12658             case 'o': ss = "odd";   break;
12659             case 's': ss = "space"; break;
12660             default:  ss = "unknown"; break;
12661           }
12662           ckstrncpy(vvbuf,ss,VVBUFL);
12663           return(vvbuf);
12664       }
12665
12666       case VN_DIAL:
12667         sprintf(vvbuf,"%d",             /* SAFE */
12668 #ifndef NODIAL
12669                 dialsta
12670 #else
12671                 -1
12672 #endif /* NODIAL */
12673                 );
12674         return(vvbuf);
12675
12676 #ifndef NODIAL
12677       case VN_DMSG:
12678 #ifdef BIGBUFOK
12679         ckstrncpy(vvbuf,dialmsg[dialsta],VVBUFL); /* Safe if src == NULL */
12680 #endif  /* BIGBUFOK */
12681         return((char *)vvbuf);
12682 #endif  /* NODIAL */
12683
12684 #ifdef OS2
12685       case VN_KEYB:
12686         ckstrncpy(vvbuf,conkbg(),VVBUFL);
12687         return(vvbuf);
12688       case VN_SELCT: {
12689 #ifndef NOLOCAL
12690           const char * selection = GetSelection();
12691           return( (char *) (selection ? selection : "" )) ;
12692 #else
12693           return("");
12694 #endif /* NOLOCAL */
12695       }
12696 #endif /* OS2 */
12697
12698 #ifndef NOXFER
12699       case VN_CPS:
12700         sprintf(vvbuf,"%ld",tfcps);     /* SAFE */
12701         return(vvbuf);
12702 #endif /* NOXFER */
12703
12704       case VN_MODE:                     /* File transfer mode */
12705         switch (binary) {
12706           case XYFT_T: ckstrncpy(vvbuf,"text",VVBUFL); break;
12707           case XYFT_B:
12708           case XYFT_U: ckstrncpy(vvbuf,"binary",VVBUFL); break;
12709           case XYFT_I: ckstrncpy(vvbuf,"image",VVBUFL); break;
12710           case XYFT_L: ckstrncpy(vvbuf,"labeled",VVBUFL); break;
12711           case XYFT_M: ckstrncpy(vvbuf,"macbinary",VVBUFL); break;
12712           default:     ckstrncpy(vvbuf,"unknown",VVBUFL);
12713         }
12714         return(vvbuf);
12715
12716 #ifdef CK_REXX
12717       case VN_REXX:
12718         return(rexxbuf);
12719 #endif /* CK_REXX */
12720
12721       case VN_NEWL:                     /* System newline char or sequence */
12722 #ifdef UNIX
12723         ckstrncpy(vvbuf,"\n",VVBUFL);
12724 #else
12725 #ifdef datageneral
12726         ckstrncpy(vvbuf,"\n",VVBUFL);
12727 #else
12728 #ifdef OSK
12729         ckstrncpy(vvbuf,"\15",VVBUFL);  /* Remember, these are octal... */
12730 #else
12731 #ifdef MAC
12732         ckstrncpy(vvbuf,"\15",VVBUFL);
12733 #else
12734 #ifdef OS2
12735         ckstrncpy(vvbuf,"\15\12",VVBUFL);
12736 #else
12737 #ifdef STRATUS
12738         ckstrncpy(vvbuf,"\n",VVBUFL);
12739 #else
12740 #ifdef VMS
12741         ckstrncpy(vvbuf,"\15\12",VVBUFL);
12742 #else
12743 #ifdef AMIGA
12744         ckstrncpy(vvbuf,"\n",VVBUFL);
12745 #else
12746 #ifdef GEMDOS
12747         ckstrncpy(vvbuf,"\n",VVBUFL);
12748 #else
12749         ckstrncpy(vvbuf,"\n",VVBUFL);
12750 #endif /* GEMDOS */
12751 #endif /* AMIGA */
12752 #endif /* VMS */
12753 #endif /* STRATUS */
12754 #endif /* OS2 */
12755 #endif /* MAC */
12756 #endif /* OSK */
12757 #endif /* datageneral */
12758 #endif /* UNIX */
12759         return(vvbuf);
12760
12761       case VN_ROWS:                     /* ROWS */
12762       case VN_COLS:                     /* COLS */
12763         ckstrncpy(vvbuf,(cx == VN_ROWS) ? "24" : "80",VVBUFL); /* Default */
12764 #ifdef CK_TTGWSIZ
12765 #ifdef OS2
12766         if (tt_cols[VTERM] < 0 || tt_rows[VTERM] < 0)
12767           ttgwsiz();
12768         sprintf(vvbuf,"%d",             /* SAFE */
12769                 (cx == VN_ROWS) ? tt_rows[VTERM] : tt_cols[VTERM]);
12770 #else /* OS2 */
12771         if (ttgwsiz() > 0)              /* Get window size */
12772           if (tt_cols > 0 && tt_rows > 0) /* sets tt_rows, tt_cols */
12773             sprintf(vvbuf,"%d",         /* SAFE */
12774                     (cx == VN_ROWS) ? tt_rows : tt_cols);
12775 #endif /* OS2 */
12776 #endif /* CK_TTGWSIZ */
12777         return(vvbuf);
12778
12779       case VN_TTYP:
12780 #ifdef NOTERM
12781         ckstrncpy(vvbuf,"unknown",VVBUFL);
12782 #else
12783 #ifdef OS2
12784         sprintf(vvbuf, "%s",            /* SAFE */
12785                 (tt_type >= 0 && tt_type <= max_tt) ?
12786                 tt_info[tt_type].x_name :
12787                 "unknown"
12788                 );
12789 #else
12790 #ifdef MAC
12791         ckstrncpy(vvbuf,"vt320",VVBUFL);
12792 #else
12793         p = getenv("TERM");
12794         ckstrncpy(vvbuf,p ? p : "unknown",VVBUFL+1);
12795 #endif /* MAC */
12796 #endif /* OS2 */
12797 #endif /* NOTERM */
12798         return(vvbuf);
12799
12800       case VN_MINP:                     /* MINPUT */
12801         sprintf(vvbuf, "%d", m_found);  /* SAFE */
12802         return(vvbuf);
12803     } /* Break up long switch statements... */
12804
12805     switch(y) {
12806       case VN_CONN:                     /* CONNECTION */
12807         if (!local) {
12808           ckstrncpy(vvbuf,"remote",VVBUFL);
12809         } else {
12810             if (!network)
12811               ckstrncpy(vvbuf,"serial",VVBUFL);
12812 #ifdef TCPSOCKET
12813             else if (nettype == NET_TCPB || nettype == NET_TCPA) {
12814                 if (ttnproto == NP_TELNET)
12815                   ckstrncpy(vvbuf,"tcp/ip_telnet",VVBUFL);
12816 #ifdef CK_SSL
12817                 else if (ttnproto == NP_SSL || ttnproto == NP_SSL_RAW)
12818                   ckstrncpy(vvbuf,"tcp/ip_ssl",VVBUFL);
12819                 else if (ttnproto == NP_TLS || ttnproto == NP_SSL_RAW)
12820                   ckstrncpy(vvbuf,"tcp/ip_tls",VVBUFL);
12821 #endif /* CK_SSL */
12822                 else
12823                   ckstrncpy(vvbuf,"tcp/ip",VVBUFL);
12824             }
12825 #endif /* TCPSOCKET */
12826 #ifdef SSHBUILTIN
12827             else if (nettype == NET_SSH)
12828                   ckstrncpy(vvbuf,"tcp/ip_ssh",VVBUFL);
12829 #endif /* SSHBUILTIN */
12830 #ifdef ANYX25
12831             else if (nettype == NET_SX25 ||
12832                      nettype == NET_VX25 ||
12833                      nettype == NET_IX25
12834                      )
12835               ckstrncpy(vvbuf,"x.25",VVBUFL);
12836 #endif /* ANYX25 */
12837 #ifdef DECNET
12838             else if (nettype == NET_DEC) {
12839                 if (ttnproto == NP_LAT)
12840                   ckstrncpy(vvbuf,"decnet_lat",VVBUFL);
12841                 else if ( ttnproto == NP_CTERM )
12842                   ckstrncpy(vvbuf,"decnet_cterm",VVBUFL);
12843                 else
12844                   ckstrncpy(vvbuf,"decnet",VVBUFL);
12845             }
12846 #endif /* DECNET */
12847 #ifdef SUPERLAT
12848             else if (nettype == NET_SLAT)
12849               ckstrncpy(vvbuf,"superlat",VVBUFL);
12850 #endif /* SUPERLAT */
12851 #ifdef NETFILE
12852             else if (nettype == NET_FILE)
12853               ckstrncpy(vvbuf,"local_file",VVBUFL);
12854 #endif /* NETFILE */
12855 #ifdef NETCMD
12856             else if (nettype == NET_CMD)
12857               ckstrncpy(vvbuf,"pipe",VVBUFL);
12858 #endif /* NETCMD */
12859 #ifdef NETPTY
12860             else if (nettype == NET_PTY)
12861               ckstrncpy(vvbuf,"pseudoterminal",VVBUFL);
12862 #endif /* NETPTY */
12863 #ifdef NETDLL
12864             else if (nettype == NET_DLL)
12865               ckstrncpy(vvbuf,"dynamic_link_library",VVBUFL);
12866 #endif /* NETDLL */
12867
12868 #ifdef NPIPE
12869             else if (nettype == NET_PIPE)
12870               ckstrncpy(vvbuf,"named_pipe",VVBUFL);
12871 #endif /* NPIPE */
12872 #ifdef CK_NETBIOS
12873             else if (nettype == NET_BIOS)
12874               ckstrncpy(vvbuf,"netbios",VVBUFL);
12875 #endif /* CK_NETBIOS */
12876             else
12877               ckstrncpy(vvbuf,"unknown",VVBUFL);
12878         }
12879         return(vvbuf);
12880
12881 #ifndef NOXFER
12882       case VN_SYSI:                     /* System ID, Kermit code */
12883         return((char *)cksysid);
12884 #endif /* NOXFER */
12885
12886 #ifdef OS2
12887       case VN_SPA: {
12888           unsigned long space = zdskspace(0);
12889           if (space > 0 && space < 1024)
12890             sprintf(vvbuf,"-1");
12891           else
12892             sprintf(vvbuf,"%lu",space); /* SAFE */
12893           return(vvbuf);
12894       }
12895 #endif /* OS2 */
12896
12897 #ifndef NOXFER
12898       case VN_QUE: {
12899           extern char querybuf[];
12900           return(querybuf);
12901       }
12902 #endif /* NOXFER */
12903
12904 #ifndef NOCSETS
12905       case VN_CSET:
12906 #ifdef OS2
12907         sprintf(vvbuf,"cp%d",os2getcp()); /* SAFE */
12908 #else
12909         ckstrncpy(vvbuf,fcsinfo[fcharset].keyword,VVBUFL+1);
12910 #endif /* OS2 */
12911         return(vvbuf);
12912 #endif /* NOCSETS */
12913
12914 #ifdef OS2
12915       case VN_EXEDIR:
12916         return(exedir);
12917       case VN_STAR:
12918         return(startupdir);
12919 #else
12920       case VN_EXEDIR:
12921         return(exedir ? exedir : "");
12922 #ifdef VMSORUNIX
12923       case VN_STAR:
12924         return(startupdir);
12925 #endif /* VMSORUNIX */
12926 #endif /* OS2 */
12927
12928       case VN_INI:
12929         return(inidir);
12930
12931       case VN_MDM:
12932         return(gmdmtyp());
12933
12934       case VN_EVAL:
12935         return(evalbuf);
12936
12937 #ifndef NODIAL
12938       case VN_D_CC:                     /* DIAL COUNTRY-CODE */
12939         return(diallcc ? diallcc : "");
12940
12941       case VN_D_AC:                     /* DIAL AREA-CODE */
12942         return(diallac ? diallac : "");
12943
12944       case VN_D_IP:                     /* DIAL INTERNATIONAL-PREFIX */
12945         return(dialixp ? dialixp : "");
12946
12947       case VN_D_LP:                     /* DIAL LD-PREFIX */
12948         return(dialldp ? dialldp : "");
12949
12950       case VN_D_LCP:                    /* DIAL LOCAL-PREFIX */
12951         return(diallcp ? diallcp : "");
12952
12953       case VN_D_PXX:                    /* DIAL PBX-EXCHANGE that matched */
12954         return(matchpxx ? matchpxx : "");
12955 #else
12956       case VN_D_CC:                     /* DIAL COUNTRY-CODE */
12957       case VN_D_AC:                     /* DIAL AREA-CODE */
12958       case VN_D_IP:                     /* DIAL INTERNATIONAL-PREFIX */
12959       case VN_D_LP:                     /* DIAL LD-PREFIX */
12960       case VN_D_LCP:                    /* DIAL LOCAL-PREFIX */
12961       case VN_D_PXX:                    /* DIAL PBX-EXCHANGE */
12962         return("");
12963 #endif /* NODIAL */
12964       case VN_UID:
12965 #ifdef UNIX
12966         {
12967 #ifdef IKSD
12968             if (inserver)
12969               return((char *)uidbuf);
12970             else
12971 #endif /* IKSD */
12972               if (uidbuf[0])
12973                 return((char *)uidbuf);
12974               else
12975                 return(whoami());
12976         }
12977 #else
12978         return((char *)uidbuf);
12979 #endif /* UNIX */
12980     } /* Break up long switch statements... */
12981
12982     switch(y) {
12983       case VN_PWD:
12984 #ifdef OS2
12985         if (activecmd == XXOUT || activecmd == XXLNOUT) {
12986             ckstrncpy(vvbuf,pwbuf,VVBUFL);
12987             ck_encrypt((char *)vvbuf);
12988             return((char *)vvbuf);
12989         } else
12990 #endif /* OS2 */
12991           return((char *)pwbuf);
12992
12993       case VN_PRM:
12994         return((char *)prmbuf);
12995
12996       case VN_PROTO:
12997 #ifdef NOXFER
12998         return("none");
12999 #else
13000 #ifdef CK_XYZ
13001         return(ptab[protocol].p_name);
13002 #else
13003         return("kermit");
13004 #endif /* CK_XYZ */
13005 #endif /* NOXFER */
13006
13007 #ifndef NOXFER
13008 #ifdef CK_TMPDIR
13009       case VN_DLDIR:
13010         return(dldir ? dldir : "");
13011 #endif /* CK_TMPDIR */
13012 #endif /* NOXFER */
13013
13014 #ifndef NODIAL
13015       case VN_M_INI:                    /* Modem init string */
13016         return(dialini ? dialini : (m ? m->wake_str : ""));
13017
13018       case VN_M_DCM:                    /* Modem dial command */
13019         return(dialcmd ? dialcmd : (m ? m->dial_str : ""));
13020
13021       case VN_M_DCO:                    /* Modem data compression on */
13022         return(dialdcon ? dialdcon : (m ? m->dc_on_str : ""));
13023
13024       case VN_M_DCX:                    /* Modem data compression off */
13025         return(dialdcoff ? dialdcoff : (m ? m->dc_off_str : ""));
13026
13027       case VN_M_ECO:                    /* Modem error correction on */
13028         return(dialecon ? dialecon : (m ? m->ec_on_str : ""));
13029
13030       case VN_M_ECX:                    /* Modem error correction off */
13031         return(dialecoff ? dialecoff : (m ? m->ec_off_str : ""));
13032
13033       case VN_M_AAO:                    /* Modem autoanswer on */
13034         return(dialaaon ? dialaaon : (m ? m->aa_on_str : ""));
13035
13036       case VN_M_AAX:                    /* Modem autoanswer off */
13037         return(dialaaoff ? dialaaoff : (m ? m->aa_off_str : ""));
13038
13039       case VN_M_HUP:                    /* Modem hangup command */
13040         return(dialhcmd ? dialhcmd : (m ? m->hup_str : ""));
13041
13042       case VN_M_HWF:                    /* Modem hardware flow command */
13043         return(dialhwfc ? dialhwfc : (m ? m->hwfc_str : ""));
13044
13045       case VN_M_SWF:                    /* Modem software flow command */
13046         return(dialswfc ? dialswfc : (m ? m->swfc_str : ""));
13047
13048       case VN_M_NFC:                    /* Modem no flow-control command */
13049         return(dialnofc ? dialnofc : (m ? m->nofc_str : ""));
13050
13051       case VN_M_PDM:                    /* Modem pulse dialing mode */
13052         return(dialpulse ? dialpulse : (m ? m->pulse : ""));
13053
13054       case VN_M_TDM:                    /* Modem tone dialing mode */
13055         return(dialtone ? dialtone : (m ? m->tone : ""));
13056
13057       case VN_M_NAM:                    /* Modem full name */
13058         return(dialname ? dialname : (m ? m->name : ""));
13059 #else
13060       case VN_M_INI:                    /* Modem init string */
13061       case VN_M_DCM:                    /* Modem dial command */
13062       case VN_M_DCO:                    /* Modem data compression on */
13063       case VN_M_DCX:                    /* Modem data compression off */
13064       case VN_M_ECO:                    /* Modem error correction on */
13065       case VN_M_ECX:                    /* Modem error correction off */
13066       case VN_M_AAO:                    /* Modem autoanswer on */
13067       case VN_M_AAX:                    /* Modem autoanswer off */
13068       case VN_M_HUP:                    /* Modem hangup command */
13069       case VN_M_HWF:                    /* Modem hardware flow command */
13070       case VN_M_SWF:                    /* Modem software flow command */
13071       case VN_M_NFC:                    /* Modem no flow-control command */
13072       case VN_M_PDM:                    /* Modem pulse dialing mode */
13073       case VN_M_TDM:                    /* Modem tone dialing mode */
13074       case VN_M_NAM:
13075         return("");
13076 #endif /* NODIAL */
13077
13078       case VN_ISTAT:                    /* INPUT status */
13079         sprintf(vvbuf, "%d", instatus); /* SAFE */
13080         return(vvbuf);
13081
13082       case VN_TEMP:                     /* Temporary directory */
13083         if (tempdir) {
13084             p = tempdir;
13085         } else {
13086 #ifdef OS2
13087 #ifdef NT
13088             p = getenv("K95TMP");
13089 #else
13090             p = getenv("K2TMP");
13091 #endif /* NT */
13092             if ( !p )
13093 #endif /* OS2 */
13094               p = getenv("CK_TMP");
13095             if (!p) p = getenv("TMPDIR");
13096             if (!p) p = getenv("TEMP");
13097             if (!p) p = getenv("TMP");
13098
13099 #ifdef OS2ORUNIX
13100             if (p) {
13101                 int len = strlen(p);
13102                 if (p[len-1] != '/'
13103 #ifdef OS2
13104                     && p[len-1] != '\\'
13105 #endif /* OS2 */
13106                      ) {
13107                     static char foo[CKMAXPATH];
13108                     ckstrncpy(foo,p,CKMAXPATH);
13109                     ckstrncat(foo,"/",CKMAXPATH);
13110                     p = foo;
13111                 }
13112             } else
13113 #else /* OS2ORUNIX */
13114             if (!p)
13115 #endif /* OS2ORUNIX */
13116 #ifdef UNIX                             /* Systems that have a standard */
13117               p = "/tmp/";              /* temporary directory... */
13118 #else
13119 #ifdef datageneral
13120               p = ":TMP:";
13121 #else
13122               p = "";
13123 #endif /* datageneral */
13124 #endif /* UNIX */
13125         }
13126         ckstrncpy(vvbuf,p,VVBUFL);
13127         p = vvbuf;
13128
13129 /* This needs generalizing for VOS, AOS/VS, etc... */
13130
13131         while (*p) {
13132 #ifdef OS2
13133             if (*p == '\\') *p = '/';
13134 #endif /* OS2 */
13135             p++;
13136         }
13137 #ifndef VMS
13138         if (p > vvbuf) {
13139             char c =                    /* Directory termination character */
13140 #ifdef MAC
13141               ':'
13142 #else
13143 #ifdef datageneral
13144               ':'
13145 #else
13146 #ifdef STRATUS
13147               '>'
13148 #else
13149               '/'
13150 #endif /* STRATUS */
13151 #endif /* datageneral */
13152 #endif /* MAC */
13153                 ;
13154
13155             if (*(p-1) != c) {
13156                 *p++ = c;
13157                 *p = NUL;
13158             }
13159         }
13160 #endif /* VMS */
13161         return(vvbuf);
13162     } /* Break up long switch statements... */
13163
13164     switch(y) {
13165       case VN_ERRNO:                    /* Error number */
13166 #ifdef VMS
13167         {
13168             extern int vms_lasterr;
13169             sprintf(vvbuf, "%d", vms_lasterr); /* SAFE */
13170         }
13171 #else
13172         sprintf(vvbuf, "%d", errno);    /* SAFE */
13173 #endif /* VMS */
13174         return(vvbuf);
13175
13176       case VN_ERSTR:                    /* Error string */
13177         ckstrncpy(vvbuf,ck_errstr(),VVBUFL);
13178         return(vvbuf);
13179
13180 #ifndef NOXFER
13181       case VN_RPSIZ:                    /* RECEIVE packet-length */
13182         sprintf(vvbuf,"%d",urpsiz);     /* SAFE */
13183         return(vvbuf);
13184
13185       case VN_WINDO:                    /* WINDOW slots */
13186         sprintf(vvbuf,"%d",wslotr);     /* SAFE */
13187         return(vvbuf);
13188 #endif /* NOXFER */
13189
13190       case VN_TFLN:                     /* TAKE-file line number */
13191         if (tlevel > -1) {
13192             sprintf(vvbuf, "%d", tfline[tlevel]); /* SAFE */
13193             return(vvbuf);
13194         } else
13195           return("0");
13196
13197       case VN_MDMSG:                    /* DIALRESULT */
13198 #ifndef NODIAL
13199         return((char *)modemmsg);
13200 #else
13201         return("");
13202 #endif /* NODIAL */
13203
13204       case VN_DNUM:                     /* DIALNUMBER */
13205 #ifndef NODIAL
13206         return(dialnum ? (char *) dialnum : "");
13207 #else
13208         return("");
13209 #endif /* NODIAL */
13210
13211       case VN_APC:
13212         sprintf(vvbuf, "%d",            /* SAFE */
13213 #ifdef CK_APC
13214                 apcactive
13215 #else
13216                 0
13217 #endif /* CK_APC */
13218                 );
13219         return((char *)vvbuf);
13220
13221 #ifdef OS2
13222 #ifndef NOKVERBS
13223       case VN_TRMK:
13224           sprintf(vvbuf, "%d", keymac); /* SAFE */
13225         return((char *)vvbuf);
13226 #endif /* NOKVERBS */
13227 #endif /* OS2 */
13228
13229       case VN_IPADDR:
13230 #ifdef TCPSOCKET
13231 #ifndef OSK
13232       /* This dumps core on OS-9 for some reason, but only if executed */
13233       /* before we have made a TCP connection.  This is obviously not */
13234       /* the ideal fix. */
13235         if (!myipaddr[0])
13236           getlocalipaddr();
13237 #endif /* OSK */
13238 #endif /* TCPSOCKET */
13239         ckstrncpy(vvbuf,
13240 #ifdef TCPSOCKET
13241                 (char *)myipaddr,
13242 #else
13243                 "",
13244 #endif /* TCPSOCKET */
13245                 VVBUFL);
13246         return((char *)vvbuf);
13247
13248 #ifndef NOXFER
13249       case VN_CRC16:                    /* CRC-16 of most recent transfer */
13250         sprintf(vvbuf,"%d",crc16);      /* SAFE */
13251         return(vvbuf);
13252 #endif /* NOXFER */
13253
13254 #ifdef CK_PID
13255       case VN_PID:
13256 #ifdef IKSD
13257 #ifdef CK_LOGIN
13258         if (inserver && isguest)
13259           return("");
13260 #endif /* CK_LOGIN */
13261 #endif /* IKSD */
13262         return(ckgetpid());
13263 #endif /* CK_PID */
13264
13265 #ifndef NOXFER
13266       case VN_FNAM: {                   /* \v(filename) */
13267           extern char filnam[], ofn1[], *sfspec, *rrfspec;
13268           char * tmp;
13269           switch (what) {               /* File transfer is in progress */
13270 #ifdef NEWFTP
13271             case (W_FTP|W_RECV):
13272             case (W_FTP|W_SEND):
13273               return((char *)filnam);
13274 #endif /* NEWFTP */
13275             case W_RECV:
13276             case W_REMO:
13277               return((char *)ofn1);
13278             default:                    /* Most recent file transferred */
13279               if (filnam[0]) {          /* (if any) */
13280                   return((char *)filnam);
13281               } else if (lastxfer & W_SEND && sfspec) {
13282                   if (fnspath == PATH_OFF)
13283                     zstrip(sfspec,&tmp);
13284                   else
13285                     tmp = sfspec;
13286                   return(tmp);
13287               } else if (lastxfer & W_RECV && rrfspec) {
13288                   if (fnrpath == PATH_OFF)
13289                     zstrip(rrfspec,&tmp);
13290                   else
13291                     tmp = rrfspec;
13292                   return(tmp);
13293               } else
13294                 return("");
13295           }
13296       }
13297       case VN_FNUM:                     /* \v(filenum) */
13298         sprintf(vvbuf,"%ld",filcnt);    /* SAFE */
13299         return((char *)vvbuf);
13300 #endif /* NOXFER */
13301
13302 #ifdef PEXITSTAT
13303       case VN_PEXIT: {
13304           extern int pexitstat;
13305           sprintf(vvbuf,"%d",pexitstat); /* SAFE */
13306           return((char *)vvbuf);
13307       }
13308 #endif /* PEXITSTAT */
13309
13310 #ifndef NOXFER
13311       case VN_P_8BIT:
13312         vvbuf[0] = parity ? ebq : NUL;
13313         vvbuf[1] = NUL;
13314         return((char *)vvbuf);
13315
13316       case VN_P_CTL: {
13317           extern CHAR myctlq;
13318           vvbuf[0] = myctlq;
13319           vvbuf[1] = NUL;
13320           return((char *)vvbuf);
13321       }
13322       case VN_P_RPT: {
13323           extern int rptena;
13324           vvbuf[0] = rptena ? rptq : NUL;
13325           vvbuf[1] = NUL;
13326           return((char *)vvbuf);
13327       }
13328 #endif /* NOXFER */
13329
13330 #ifdef OS2
13331       case VN_REGN:
13332         return(get_reg_name());
13333       case VN_REGO:
13334         return(get_reg_corp());
13335       case VN_REGS:
13336         return(get_reg_sn());
13337 #endif /* OS2 */
13338     } /* Break up long switch statements... */
13339
13340     switch(y) {
13341       case VN_XPROG:
13342 #ifdef OS2
13343 #ifdef NT
13344 #ifdef KUI
13345         return("K-95G");
13346 #else
13347         return("K-95");
13348 #endif /* KUI */
13349 #else
13350         return("K/2");
13351 #endif /* NT */
13352 #else
13353         return("C-Kermit");
13354 #endif /* OS2 */
13355
13356       case VN_EDITOR:
13357 #ifdef NOFRILLS
13358         return("");
13359 #else
13360 #ifdef NOPUSH
13361         return("");
13362 #else
13363         {
13364             extern char editor[];
13365             char *ss;
13366             if (!editor[0]) {
13367                 ss = getenv("EDITOR");
13368                 if (ss) {
13369                     ckstrncpy(editor,ss,CKMAXPATH);
13370                 }
13371             }
13372             debug(F110,"\\v(editor)",editor,0);
13373             return(editor[0] ? (char *)editor : "");
13374         }
13375 #endif /* NOPUSH */
13376 #endif /* NOFRILLS */
13377
13378       case VN_EDOPT:
13379 #ifdef NOFRILLS
13380         return("");
13381 #else
13382 #ifdef NOPUSH
13383         return("");
13384 #else
13385         {
13386             extern char editopts[];
13387             return(editopts[0] ? (char *)editopts : "");
13388         }
13389 #endif /* NOPUSH */
13390 #endif /* NOFRILLS */
13391
13392       case VN_EDFILE:
13393 #ifdef NOFRILLS
13394         return("");
13395 #else
13396 #ifdef NOPUSH
13397         return("");
13398 #else
13399         {
13400             extern char editfile[];
13401             return(editfile[0] ? (char *)editfile : "");
13402         }
13403 #endif /* NOPUSH */
13404 #endif /* NOFRILLS */
13405
13406 #ifdef BROWSER
13407       case VN_BROWSR: {
13408           extern char browser[];
13409           if (!browser[0]) {
13410               s = getenv("BROWSER");
13411               if (s) ckstrncpy(browser,s,CKMAXPATH);
13412           }
13413           return(browser[0] ? (char *)browser : "");
13414       }
13415       case VN_BROPT: {
13416           extern char browsopts[];
13417           return(browsopts[0] ? (char *)browsopts : "");
13418       }
13419       case VN_URL: {
13420           extern char browsurl[];
13421           return(browsurl[0] ? (char *)browsurl : "");
13422       }
13423 #endif /* BROWSER */
13424       case VN_HERALD:
13425         return((char *)versio);
13426
13427       case VN_TEST: {                   /* test */
13428           extern char * ck_s_test, * ck_s_tver;
13429           if (!ck_s_test) ck_s_test = "";
13430           if (!ck_s_tver) ck_s_tver = "";
13431           if (*ck_s_test) {
13432               ckstrncpy(vvbuf,ck_s_test,VVBUFL);
13433               if (*ck_s_tver) {
13434                   ckstrncat(vvbuf,".",VVBUFL);
13435                   ckstrncat(vvbuf,ck_s_tver,VVBUFL);
13436               }
13437           } else
13438             ckstrncpy(vvbuf,"0",VVBUFL);
13439           return((char *)vvbuf);
13440       }
13441
13442 #ifndef NOXFER
13443       case VN_XFSTAT:                   /* xferstatus */
13444         x = xferstat;                   /* Like success */
13445         if (x > -1) x = (x == 0) ? 1 : 0; /* External value is reversed */
13446         sprintf(vvbuf,"%d",x);          /* SAFE */
13447         return((char *)vvbuf);
13448
13449       case VN_XFMSG:                    /* xfermsg */
13450         return((char *)epktmsg);
13451
13452 #ifndef NOMSEND
13453       case VN_SNDL: {                   /* sendlist */
13454           extern int filesinlist;
13455           sprintf(vvbuf,"%d",filesinlist); /* SAFE */
13456           return((char *)vvbuf);
13457       }
13458 #endif /* NOMSEND */
13459 #endif /* NOXFER */
13460
13461 #ifdef CK_TRIGGER
13462       case VN_TRIG: {
13463           extern char * triggerval;
13464           return(triggerval ? triggerval : "");
13465       }
13466 #endif /* CK_TRIGGER */
13467 #ifdef OS2MOUSE
13468 #ifdef OS2
13469       case VN_MOU_X: {
13470           extern int MouseCurX;
13471           sprintf(vvbuf,"%d",MouseCurX); /* SAFE */
13472           return((char *)vvbuf);
13473       }
13474       case VN_MOU_Y: {
13475           extern int MouseCurY;
13476           sprintf(vvbuf,"%d",MouseCurY); /* SAFE */
13477           return((char *)vvbuf);
13478       }
13479 #endif /* OS2 */
13480 #endif /* OS2MOUSE */
13481       case VN_PRINT: {
13482           extern int printpipe;
13483           extern char * printername;
13484 #ifdef PRINTSWI
13485           extern int noprinter;
13486           if (noprinter) return("");
13487 #endif /* PRINTSWI */
13488           ckmakmsg(vvbuf,VVBUFL,
13489                    printpipe ? "|" : "",
13490                    printername ? printername :
13491 #ifdef OS2
13492                    "PRN",
13493 #else
13494                    "(default)",
13495 #endif /* OS2 */
13496                    NULL,
13497                    NULL
13498                    );
13499           return((char *)vvbuf);
13500       }
13501     } /* Break up long switch statements... */
13502
13503     switch(y) {
13504       case VN_ESC:                      /* Escape character */
13505         sprintf(vvbuf,"%d",escape);     /* SAFE */
13506         return((char *)vvbuf);
13507
13508       case VN_INTIME:
13509         sprintf(vvbuf,"%ld",inetime);   /* SAFE */
13510         return((char *)vvbuf);
13511
13512       case VN_INTMO:
13513         sprintf(vvbuf,"%d",inwait);     /* SAFE */
13514         return((char *)vvbuf);
13515
13516       case VN_SECURE:
13517         if (0
13518 #ifdef SSHBUILTIN
13519             || IS_SSH()
13520 #endif /* SSHBUILTIN */
13521 #ifdef CK_ENCRYPTION
13522             || ck_tn_encrypting() && ck_tn_decrypting()
13523 #endif /* CK_ENCRYPTION */
13524 #ifdef CK_SSL
13525             || tls_active_flag || ssl_active_flag
13526 #endif /* CK_SSL */
13527             )
13528           return("1");
13529         else
13530           return("0");
13531
13532       case VN_AUTHN:
13533 #ifdef CK_AUTHENTICATION
13534         {
13535             extern char szUserNameAuthenticated[];
13536             return((char *)szUserNameAuthenticated);
13537         }
13538 #else /* CK_AUTHENTICATION */
13539         return((char *)"");
13540 #endif /* CK_AUTHENTICATION */
13541
13542       case VN_AUTHS:
13543 #ifdef CK_AUTHENTICATION
13544         switch (ck_tn_auth_valid()) {
13545           case AUTH_UNKNOWN:
13546             return((char *)"unknown");
13547           case AUTH_OTHER:
13548             return((char *)"other");
13549           case AUTH_USER:
13550             return((char *)"user");
13551           case AUTH_VALID:
13552             return((char *)"valid");
13553           case AUTH_REJECT:
13554           default:
13555             return((char *)"rejected");
13556         }
13557 #else /* CK_AUTHENTICATION */
13558         return((char *)"rejected");
13559 #endif /* CK_AUTHENTICATION */
13560
13561       case VN_AUTHT:
13562 #ifdef CK_AUTHENTICATION
13563 #ifdef CK_SSL
13564         if ((ssl_active_flag || tls_active_flag) &&
13565             ck_tn_auth_valid() == AUTH_VALID &&
13566             (sstelnet ? (!TELOPT_U(TELOPT_AUTHENTICATION)) :
13567                         (!TELOPT_ME(TELOPT_AUTHENTICATION))) ||
13568              ck_tn_authenticated() == AUTHTYPE_NULL ||
13569              ck_tn_authenticated() == AUTHTYPE_AUTO)
13570           return("X_509_CERTIFICATE");
13571         else
13572 #endif /* CK_SSL */
13573           return(AUTHTYPE_NAME(ck_tn_authenticated()));
13574 #else /* CK_AUTHENTICATION */
13575         return((char *)"NULL");
13576 #endif /* CK_AUTHENTICATION */
13577
13578 #ifdef CK_KERBEROS
13579       case VN_K4PRN: {
13580           extern char * krb4_d_principal;
13581           if (krb4_d_principal)
13582             ckstrncpy(vvbuf,krb4_d_principal,VVBUFL+1);
13583           else
13584             *vvbuf = NUL;
13585           return((char *)vvbuf);
13586       }
13587       case VN_K5PRN: {
13588           extern char * krb5_d_principal;
13589           if (krb5_d_principal)
13590             ckstrncpy(vvbuf,krb5_d_principal,VVBUFL+1);
13591           else
13592             *vvbuf = NUL;
13593           return((char *)vvbuf);
13594       }
13595       case VN_K4RLM: {
13596           extern char * krb4_d_realm;
13597           if (krb4_d_realm) {
13598               ckstrncpy(vvbuf,krb4_d_realm,VVBUFL+1);
13599           } else {
13600               char * s = ck_krb4_getrealm();
13601               ckstrncpy(vvbuf,s ? s : "",VVBUFL+1);
13602           }
13603           return((char *)vvbuf);
13604       }
13605       case VN_K4SRV: {
13606           extern char * krb4_d_srv;
13607           if (krb4_d_srv)
13608             ckstrncpy(vvbuf,krb4_d_srv,VVBUFL+1);
13609           else
13610             ckstrncpy(vvbuf,"rcmd",VVBUFL);
13611           return((char *)vvbuf);
13612       }
13613       case VN_K5RLM: {
13614           extern char * krb5_d_realm;
13615           extern char * krb5_d_cc;
13616           if (krb5_d_realm) {
13617               ckstrncpy(vvbuf,krb5_d_realm,VVBUFL+1);
13618           } else {
13619               char * s = ck_krb5_getrealm(krb5_d_cc);
13620               ckstrncpy(vvbuf,s,VVBUFL+1);
13621           }
13622           return((char *)vvbuf);
13623       }
13624       case VN_K5CC: {
13625           extern char * krb5_d_cc;
13626           if (krb5_d_cc)
13627             ckstrncpy(vvbuf,krb5_d_cc,VVBUFL+1);
13628           else
13629             ckstrncpy(vvbuf,ck_krb5_get_cc_name(),VVBUFL+1);
13630           return((char *)vvbuf);
13631       }
13632       case VN_K5SRV: {
13633           extern char * krb5_d_srv;
13634           if (krb5_d_srv)
13635             ckstrncpy(vvbuf,krb5_d_srv,VVBUFL+1);
13636           else
13637             ckstrncpy(vvbuf,"host",VVBUFL);
13638           return((char *)vvbuf);
13639       }
13640       case VN_K4ENO: {
13641         extern int krb4_errno;
13642         sprintf(vvbuf,"%d",krb4_errno); /* SAFE */
13643         return((char *)vvbuf);
13644       }
13645       case VN_K5ENO: {
13646         extern int krb5_errno;
13647         sprintf(vvbuf,"%d",krb5_errno); /* SAFE */
13648         return((char *)vvbuf);
13649       }
13650       case VN_K4EMSG: {
13651         extern char * krb4_errmsg;
13652         ckstrncpy(vvbuf,krb4_errmsg?krb4_errmsg:"",VVBUFL+1);
13653         return((char *)vvbuf);
13654       }
13655       case VN_K5EMSG: {
13656         extern char * krb5_errmsg;
13657         ckstrncpy(vvbuf,krb5_errmsg,VVBUFL+1);
13658         return((char *)vvbuf);
13659       }
13660 #endif /* CK_KERBEROS */
13661 #ifdef CK_SSL
13662       case VN_X509_S:
13663         if (ssl_active_flag)
13664           ckstrncpy(vvbuf,ssl_get_subject_name(ssl_con),VVBUFL+1);
13665         else if (tls_active_flag)
13666           ckstrncpy(vvbuf,ssl_get_subject_name(tls_con),VVBUFL+1);
13667         else
13668           ckstrncpy(vvbuf,"",VVBUFL+1);
13669         return((char *)vvbuf);
13670       case VN_X509_I:
13671         if (ssl_active_flag)
13672           ckstrncpy(vvbuf,ssl_get_issuer_name(ssl_con),VVBUFL+1);
13673         else if (tls_active_flag)
13674           ckstrncpy(vvbuf,ssl_get_issuer_name(tls_con),VVBUFL+1);
13675         else
13676           ckstrncpy(vvbuf,"",VVBUFL+1);
13677         return((char *)vvbuf);
13678 #endif /* CK_SSL */
13679
13680       case VN_OSNAM:
13681 #ifdef IKSD
13682 #ifdef CK_LOGIN
13683         if (inserver && isguest)
13684           return("");
13685 #endif /* CK_LOGIN */
13686 #endif /* IKSD */
13687 #ifdef CK_UTSNAME
13688         {
13689             extern char unm_nam[];
13690             return((char *)unm_nam);
13691         }
13692 #else
13693         for (x = y = 0; x < VVBUFL; x++) {
13694             if (ckxsys[x] == SP && cx == 0) continue;
13695             vvbuf[y++] = (char) ((ckxsys[x] == SP) ? '_' : ckxsys[x]);
13696         }
13697         vvbuf[y] = NUL;
13698         return(vvbuf);
13699 #endif /* CK_UTSNAME */
13700
13701       case VN_OSVER: {
13702 #ifdef CK_UTSNAME
13703           extern char unm_ver[];
13704 #ifdef IKSD
13705 #ifdef CK_LOGIN
13706           if (inserver && isguest)
13707             return("");
13708 #endif /* CK_LOGIN */
13709 #endif /* IKSD */
13710           return((char *)unm_ver);
13711 #else
13712           return("");
13713 #endif /* CK_UTSNAME */
13714       }
13715
13716       case VN_OSREL: {
13717 #ifdef CK_UTSNAME
13718           extern char unm_rel[];
13719 #ifdef IKSD
13720 #ifdef CK_LOGIN
13721           if (inserver && isguest)
13722             return("");
13723 #endif /* CK_LOGIN */
13724 #endif /* IKSD */
13725           return((char *)unm_rel);
13726 #else
13727           return("");
13728 #endif /* CK_UTSNAME */
13729       }
13730     } /* Break up long switch statements... */
13731
13732     switch(y) {
13733       case VN_NAME: {
13734           extern char * myname;
13735           return(myname);
13736       }
13737
13738       case VN_MODL: {
13739 #ifdef CK_UTSNAME
13740           extern char unm_mod[], unm_mch[];
13741           int y = VVBUFL - 1;
13742           char * s = unm_mod;
13743 #endif /* CK_UTSNAME */
13744 #ifdef IKSD
13745 #ifdef CK_LOGIN
13746           if (inserver && isguest)
13747             return("");
13748 #endif /* CK_LOGIN */
13749 #endif /* IKSD */
13750
13751 #ifdef COMMENT                          /* was HPUX */
13752           if (!unm_mod[0] && !nopush)
13753             zzstring("\\fcommand(model)",&s,&y);
13754 /*
13755    Another possibility would be:
13756      "\\fcommand(ksh -c 'whence model 1>&- && model || uname -m')"
13757    But that would depend on having ksh.
13758 */
13759 #else
13760 #ifdef OSF32                            /* Digital UNIX 3.2 and higher... */
13761 /* Note: Ultrix has /etc/sizer, but it is not publicly executable. */
13762 /* sizer -c outputs 'cpu:<tab><tab>"DECxxxx"' */
13763           if (!unm_mod[0]) {
13764               char * p;
13765               int flag = 0;
13766               zzstring("\\fcommand(/usr/sbin/sizer -c)",&s,&y);
13767               debug(F110,"DU model",unm_mod,0);
13768               s = unm_mod;
13769               p = unm_mod;
13770               while (*p) {              /* Extract the part in quotes */
13771                   if (*p == '"') {
13772                       if (flag)
13773                         break;
13774                       flag = 1;
13775                       p++;
13776                       continue;
13777                   }
13778                   if (flag)
13779                     *s++ = *p;
13780                   p++;
13781               }
13782               *s = NUL;
13783           }
13784 #endif /* OSF32 */
13785 #endif /* COMMENT */
13786
13787 #ifdef CK_UTSNAME
13788           if (unm_mod[0])
13789             return((char *)unm_mod);
13790           else
13791             return((char *)unm_mch);
13792 #else
13793           return("");
13794 #endif /* CK_UTSNAME */
13795       }
13796
13797 #ifdef IBMX25
13798       /* X.25 variables (local and remote address) */
13799       case VN_X25LA:
13800         if (!local_nua[0] && !x25local_nua(local_nua))
13801           *vvbuf = NULL;
13802         else
13803           ckstrncpy(vvbuf,local_nua,VVBUFL+1);
13804         return((char *)vvbuf);
13805
13806       case VN_X25RA:
13807         if (!remote_nua[0])
13808           *vvbuf = NULL;
13809         else
13810           ckstrncpy(vvbuf,remote_nua,VVBUFL+1);
13811         return((char *)vvbuf);
13812 #endif /* IBMX25 */
13813
13814 #ifndef NODIAL
13815       case VN_PDSFX: {
13816           extern char pdsfx[];
13817           return((char *)pdsfx);
13818       }
13819       case VN_DTYPE: {
13820           extern int dialtype;
13821           sprintf(vvbuf,"%d",dialtype); /* SAFE */
13822           return((char *)vvbuf);
13823       }
13824 #endif /* NODIAL */
13825
13826 #ifdef UNIX
13827       case VN_LCKPID: {
13828           extern char lockpid[];
13829           return((char *)lockpid);
13830       }
13831 #endif /* UNIX */
13832
13833 #ifndef NOXFER
13834       case VN_BLK:
13835         sprintf(vvbuf,"%d",bctr);       /* SAFE */
13836         return((char *)vvbuf);
13837
13838       case VN_TFTIM:
13839         sprintf(vvbuf,                  /* SAFE */
13840 #ifdef GFTIMER
13841                 "%ld", (long)(fptsecs + 0.5)
13842 #else
13843                 "%d", tsecs
13844 #endif /* GFTIMER */
13845                 );
13846         return((char *)vvbuf);
13847 #endif /* NOXFER */
13848
13849       case VN_HWPAR:
13850       case VN_SERIAL: {
13851           int sb;
13852           char c, * ss;
13853           extern int stopbits;
13854           vvbuf[0] = NUL;
13855           if (hwparity && local && !network)
13856             ss = parnam((char)hwparity);
13857           else
13858             ss = parnam((char)parity);
13859           if (cx == VN_HWPAR) {
13860               ckstrncpy(vvbuf,ss,VVBUFL);
13861               return((char *)vvbuf);
13862           }
13863           c = ss[0];
13864           if (islower(c)) c = toupper(c);
13865           sb = stopbits;
13866           if (sb < 1)
13867             sb = (speed > 0 && speed <= 110L) ? 2 : 1;
13868           if (hwparity)
13869             sprintf(vvbuf," 8%c%d",c,sb); /* SAFE */
13870           else if (parity)
13871             sprintf(vvbuf," 7%c%d",c,sb); /* SAFE */
13872           else
13873             sprintf(vvbuf," 8N%d",sb);  /* SAFE */
13874           return((char *)vvbuf);
13875       }
13876
13877 #ifdef UNIX
13878       case VN_LCKDIR: {
13879 #ifndef NOUUCP
13880           extern char * uucplockdir;
13881           ckstrncpy(vvbuf,uucplockdir,VVBUFL);
13882           x = strlen(vvbuf);
13883           if (x > 0) {
13884               if (vvbuf[x-1] != '/') {
13885                   vvbuf[x] = '/';
13886                   vvbuf[x+1] = NUL;
13887               }
13888           }
13889 #else
13890           vvbuf[0] = NUL;
13891 #endif /* NOUUCP */
13892           return((char *)vvbuf);
13893       }
13894 #endif /* UNIX */
13895     } /* Break up long switch statements... */
13896
13897     switch(y) {
13898 #ifndef NODIAL
13899       case VN_DM_LP:
13900       case VN_DM_SP:
13901       case VN_DM_PD:
13902       case VN_DM_TD:
13903       case VN_DM_WA:
13904       case VN_DM_WD:
13905       case VN_DM_HF:
13906       case VN_DM_WB:
13907       case VN_DM_RC: {
13908           extern char * getdm();
13909           ckstrncpy(vvbuf,getdm(y),VVBUFL);
13910           return((char *)vvbuf);
13911       }
13912 #endif /* NODIAL */
13913
13914       case VN_TY_LN:
13915       case VN_TY_LC: {
13916           extern int typ_lines;
13917           sprintf(vvbuf,"%d",typ_lines); /* SAFE */
13918           return((char *)vvbuf);
13919       }
13920       case VN_TY_LM: {
13921           extern int typ_mtchs;
13922           sprintf(vvbuf,"%d",typ_mtchs); /* SAFE */
13923           return((char *)vvbuf);
13924       }
13925       case VN_MACLVL:
13926         sprintf(vvbuf,"%d",maclvl);     /* SAFE */
13927         return((char *)vvbuf);
13928     } /* Break up long switch statements... */
13929
13930     switch(y) {
13931 #ifndef NOLASTFILE
13932       case VN_LASTFIL: {
13933           extern char * lastfile;
13934           return(lastfile ? lastfile : "");
13935       }
13936 #endif  /* NOLASTFILE */
13937 #ifndef NOXFER
13938       case VN_XF_BC:
13939         sprintf(vvbuf,"%d",crunched);   /* SAFE */
13940         return((char *)vvbuf);
13941
13942       case VN_XF_TM:
13943         sprintf(vvbuf,"%d",timeouts);   /* SAFE */
13944         return((char *)vvbuf);
13945
13946       case VN_XF_RX:
13947         sprintf(vvbuf,"%d",retrans);    /* SAFE */
13948         return((char *)vvbuf);
13949 #endif /* NOXFER */
13950
13951       case VN_MS_CD:                    /* Modem signals */
13952       case VN_MS_CTS:
13953       case VN_MS_DSR:
13954       case VN_MS_DTR:
13955       case VN_MS_RI:
13956       case VN_MS_RTS: {
13957           int x, z = -1;
13958           x = ttgmdm();                 /* Try to get them */
13959           if (x > -1) {
13960               switch (y) {
13961                 case VN_MS_CD:  z = (x & BM_DCD) ? 1 : 0; break;
13962                 case VN_MS_DSR: z = (x & BM_DSR) ? 1 : 0; break;
13963                 case VN_MS_CTS: z = (x & BM_CTS) ? 1 : 0; break;
13964 #ifdef MAC
13965                 case VN_MS_DTR: z = (x & BM_DTR) ? 1 : 0; break;
13966 #else
13967 #ifndef STRATUS
13968                 case VN_MS_RI:  z = (x & BM_RNG) ? 1 : 0; break;
13969 #ifndef NT
13970                 case VN_MS_DTR: z = (x & BM_DTR) ? 1 : 0; break;
13971                 case VN_MS_RTS: z = (x & BM_RTS) ? 1 : 0; break;
13972 #endif /* NT */
13973 #endif /* STRATUS */
13974 #endif /* MAC */
13975               }
13976           }
13977           sprintf(vvbuf,"%d",z);        /* SAFE */
13978           return((char *)vvbuf);
13979       }
13980       case VN_MATCH:                    /* INPUT MATCH */
13981         return(inpmatch ? inpmatch : "");
13982
13983 #ifdef CKFLOAT
13984       case VN_ISCALE:                   /* INPUT SCALE-FACTOR */
13985         return(inpscale ? inpscale : "1.0");
13986 #endif  /* CKFLOAT */
13987
13988       case VN_SLMSG: {                  /* SET LINE / HOST message */
13989           extern char * slmsg;
13990           vvbuf[0] = NUL;
13991           if (slmsg)
13992             ckstrncpy(vvbuf,slmsg,VVBUFL);
13993          return(vvbuf);
13994       }
13995
13996       case VN_TXTDIR:                   /* TEXTDIR */
13997         return(k_info_dir ? k_info_dir : "");
13998
13999 #ifdef FNFLOAT
14000       case VN_MA_PI:
14001         return(math_pi);
14002
14003       case VN_MA_E:
14004         return(math_e);
14005
14006       case VN_MA_PR:
14007         sprintf(vvbuf,"%d",fp_digits);  /* SAFE */
14008         return(vvbuf);
14009 #endif /* FNFLOAT */
14010
14011       case VN_CMDBL:
14012         sprintf(vvbuf,"%d",CMDBL);      /* SAFE */
14013         return(vvbuf);
14014
14015 #ifdef CKCHANNELIO
14016       case VN_FERR: {
14017           extern int z_error;
14018           sprintf(vvbuf,"%d",z_error);  /* SAFE */
14019           return(vvbuf);
14020       }
14021       case VN_FMAX: {
14022           extern int z_maxchan;
14023           sprintf(vvbuf,"%d",z_maxchan); /* SAFE */
14024           return(vvbuf);
14025       }
14026       case VN_FCOU: {
14027           extern int z_filcount;
14028           sprintf(vvbuf,"%d",z_filcount); /* SAFE */
14029           return(vvbuf);
14030       }
14031 #endif /* CKCHANNELIO */
14032
14033 #ifndef NODIAL
14034       case VN_DRTR: {
14035           extern int dialcount;
14036           sprintf(vvbuf,"%d",dialcount); /* SAFE */
14037           return(vvbuf);
14038       }
14039 #endif /* NODIAL */
14040
14041 #ifndef NOLOGDIAL
14042 #ifndef NOLOCAL
14043       case VN_CXTIME:
14044         sprintf(vvbuf,"%ld",dologshow(0)); /* SAFE */
14045         return(vvbuf);
14046 #endif /* NOLOCAL */
14047 #endif /* NOLOGDIAL */
14048
14049       case VN_BYTE:
14050         sprintf(vvbuf,"%d",byteorder);  /* SAFE */
14051         return(vvbuf);
14052
14053       case VN_KBCHAR:
14054         vvbuf[0] = NUL;
14055         vvbuf[1] = NUL;
14056         if (kbchar > 0)
14057           vvbuf[0] = (kbchar & 0xff);
14058         return(vvbuf);
14059
14060       case VN_TTYNAM: {
14061 #ifdef HAVECTTNAM
14062           extern char cttnam[];
14063           return((char *)cttnam);
14064 #else
14065           return(CTTNAM);
14066 #endif /* HAVECTTNAM */
14067       }
14068
14069       case VN_PROMPT:
14070         return(cmgetp());
14071
14072       case VN_BUILD: {
14073           extern char * buildid;
14074           return(buildid);
14075       }
14076
14077 #ifndef NOSEXP
14078       case VN_SEXP: {
14079           extern char * lastsexp;
14080           return(lastsexp ? lastsexp : "");
14081       }
14082       case VN_VSEXP: {
14083           extern char * sexpval;
14084           return(sexpval ? sexpval : "");
14085       }
14086       case VN_LSEXP: {
14087           extern int sexpdep;
14088           ckstrncpy(vvbuf,ckitoa(sexpdep),VVBUFL);
14089           return(vvbuf);
14090       }
14091 #endif /* NOSEXP */
14092
14093 #ifdef GFTIMER
14094       case VN_FTIME: {
14095           CKFLOAT f;
14096           ztime(&p);
14097           if (p == NULL || *p == NUL)
14098             return(NULL);
14099           z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
14100           f = (CKFLOAT)z + ((CKFLOAT)ztusec) / 1000000.0;
14101           sprintf(vvbuf,"%f",f);        /* SAFE */
14102           return(vvbuf);
14103       }
14104 #endif /* GFTIMER */
14105
14106 #ifndef NOHTTP
14107       case VN_HTTP_C: {                 /* HTTP Code */
14108           extern int http_code;
14109           return(ckitoa(http_code));
14110       }
14111       case VN_HTTP_N:                   /* HTTP Connected */
14112         return( http_isconnected() ? "1" : "0");
14113       case VN_HTTP_H:                   /* HTTP Host */
14114         return( (char *)http_host() );
14115       case VN_HTTP_M: {                 /* HTTP Message */
14116           extern char http_reply_str[];
14117           return((char *)http_reply_str);
14118       }
14119       case VN_HTTP_S:                   /* HTTP Security */
14120         return((char *)http_security());
14121 #endif /* NOHTTP */
14122
14123 #ifdef NEWFTP
14124       case VN_FTP_B:
14125         return((char *)ftp_cpl_mode());
14126       case VN_FTP_D:
14127         return((char *)ftp_dpl_mode());
14128       case VN_FTP_Z:
14129         return((char *)ftp_authtype());
14130       case VN_FTP_C: {
14131           extern int ftpcode;
14132           return(ckitoa(ftpcode));
14133       }
14134       case VN_FTP_M: {
14135           extern char ftp_reply_str[];
14136           if (isdigit(ftp_reply_str[0]) &&
14137               isdigit(ftp_reply_str[1]) &&
14138               isdigit(ftp_reply_str[2]) &&
14139               ftp_reply_str[3] == ' ')
14140             return(&ftp_reply_str[4]);
14141           else
14142             return(ftp_reply_str);
14143       }
14144       case VN_FTP_S: {
14145           extern char ftp_srvtyp[];
14146           return((char *)ftp_srvtyp);
14147       }
14148       case VN_FTP_H: {
14149           extern char * ftp_host;
14150           return(ftp_host ? ftp_host : "");
14151       }
14152       case VN_FTP_X: {                  /* FTP Connected */
14153           return(ftpisconnected() ? "1" : "0");
14154       }
14155       case VN_FTP_L: {                  /* FTP Logged in */
14156           return(ftpisloggedin() ? "1" : "0");
14157       }
14158       case VN_FTP_G: {                  /* FTP GET-PUT-REMOTE */
14159           extern int ftpget;
14160           char * s = "";
14161           switch (ftpget) {
14162             case 0: s = "kermit"; break;
14163             case 1: s = "ftp"; break;
14164             case 2: s = "auto"; break;
14165           }
14166           return(s);
14167       }
14168 #endif /* NEWFTP */
14169
14170 #ifndef NOLOCAL
14171       case VN_CX_STA: {                 /* CONNECT status */
14172           extern int cx_status;
14173           return(ckitoa(cx_status));
14174       }
14175 #endif /* NOLOCAL */
14176       case VN_NOW:                      /* Timestamp */
14177         return(ckcvtdate(p,0));
14178
14179       case VN_HOUR:                     /* Hour of the day */
14180         ztime(&p);                      /* "Thu Feb  8 12:00:00 1990" */
14181         if (!p) p = "";
14182         if (!*p) return(p);
14183         vvbuf[0] = p[11];
14184         vvbuf[1] = p[12];
14185         vvbuf[2] = NUL;
14186         return(vvbuf);                  /* and return it */
14187
14188       case VN_BITS:                     /* Bits (16, 32, 64) */
14189         if (sizeof(long) > 4)
14190           return(ckitoa(8*sizeof(long)));
14191         else
14192           return(ckitoa(8*sizeof(int)));
14193
14194       case VN_LASTKWV:                  /* 212 */
14195         return(lastkwval ? lastkwval : "");
14196
14197       case VN_HOSTIP: {                 /* 212 */
14198 #ifdef TCPSOCKET
14199           extern char hostipaddr[];
14200           return((char *)hostipaddr);
14201 #else
14202           return("");
14203 #endif  /* TCPSOCKET */
14204       }
14205       case VN_INPMSG:
14206         switch (instatus) {
14207           case INP_OK:  return("SUCCESS");
14208           case INP_TO:  return("Timed out");
14209           case INP_UI:  return("Keyboard interrupt");
14210           case INP_IE:  return("Internal error");
14211           case INP_IO:  return("I/O error or connection lost");
14212           case INP_IKS: return("INPUT disabled");
14213           case INP_BF:  return("Buffer filled and /NOWRAP set");
14214           default:      return("Unknown");
14215         }
14216
14217       case VN_VAREVAL:                  /* 212 */
14218         return(vareval ? "recursive" : "simple");
14219
14220       case VN_LOG_CON:                  /* \v(...) for log files */
14221 #ifdef CKLOGDIAL
14222         return(diafil);
14223 #else 
14224         return("");
14225 #endif
14226       case VN_LOG_PKT:
14227 #ifndef NOXFER
14228         return(pktfil);
14229 #else
14230         return("");
14231 #endif
14232       case VN_LOG_SES:
14233 #ifndef NOLOCAL
14234         return(sesfil);
14235 #else
14236         return("");
14237 #endif
14238       case VN_LOG_TRA:
14239 #ifdef TLOG
14240         return(trafil);
14241 #else
14242         return("");
14243 #endif
14244       case VN_LOG_DEB:
14245 #ifdef DEBUG
14246         return(debfil);
14247 #else
14248         return("");
14249 #endif
14250       case VN_PREVCMD: {
14251           extern char * prevcmd;
14252           return(prevcmd ?  prevcmd : "");
14253       }
14254     }
14255
14256 #ifndef NODIAL
14257     switch (y) {                        /* Caller ID values */
14258       extern char
14259         * callid_date, * callid_time, * callid_name,
14260         * callid_nmbr, * callid_mesg;
14261
14262       case VN_CI_DA:
14263         return(callid_date ? callid_date : "");
14264
14265       case VN_CI_TI:
14266         return(callid_time ? callid_time : "");
14267
14268       case VN_CI_NA:
14269         return(callid_name ? callid_name : "");
14270
14271       case VN_CI_NU:
14272         return(callid_nmbr ? callid_nmbr : "");
14273
14274       case VN_CI_ME:
14275         return(callid_mesg ? callid_mesg : "");
14276
14277     } /* End of variable-name switches */
14278 #endif /* NODIAL */
14279
14280 #ifdef NT
14281     switch (y) {
14282       case VN_PERSONAL:
14283         p = (char *)GetPersonal();
14284         if (p) {
14285             GetShortPathName(p,vvbuf,VVBUFL);
14286             return(vvbuf);
14287         }
14288         return("");
14289       case VN_DESKTOP:
14290           p = (char *)GetDesktop();
14291           if (p) {
14292               GetShortPathName(p,vvbuf,VVBUFL);
14293               return(vvbuf);
14294           }
14295           return("");
14296       case VN_COMMON:
14297         p = (char *)GetAppData(1);
14298         if (p) {
14299             ckmakmsg(vvbuf,VVBUFL,p,"Kermit 95/",NULL,NULL);
14300             GetShortPathName(vvbuf,vvbuf,VVBUFL);
14301             return(vvbuf);
14302         }
14303         return("");
14304       case VN_APPDATA:
14305         p = (char *)GetAppData(0);
14306         if (p) {
14307             ckmakmsg(vvbuf,VVBUFL,p,"Kermit 95/",NULL,NULL);
14308             GetShortPathName(vvbuf,vvbuf,VVBUFL);
14309             return(vvbuf);
14310         }
14311         return("");
14312     }
14313 #endif /* NT */
14314
14315 #ifdef TN_COMPORT
14316     switch (y) {
14317       case VN_TNC_SIG: {
14318         p = (char *) tnc_get_signature();
14319         ckstrncpy(vvbuf,p ? p : "",VVBUFL);
14320         return(vvbuf);
14321       }
14322     }
14323 #endif /* TN_COMPORT */
14324
14325 #ifdef KUI
14326     switch (y) {
14327       case VN_GUI_RUN: {
14328           extern HWND getHwndKUI();
14329           if ( IsIconic(getHwndKUI()) )
14330             return("minimized");
14331           if ( IsZoomed(getHwndKUI()) )
14332             return("maximized");
14333           return("restored");
14334       }
14335       case VN_GUI_XP:
14336         sprintf(vvbuf,"%d",get_gui_window_pos_x());  /* SAFE */
14337         return(vvbuf);
14338       case VN_GUI_YP:
14339         sprintf(vvbuf,"%d",get_gui_window_pos_y());  /* SAFE */
14340         return(vvbuf);
14341       case VN_GUI_XR:
14342         sprintf(vvbuf,"%d",GetSystemMetrics(SM_CXSCREEN));  /* SAFE */
14343         return(vvbuf);
14344       case VN_GUI_YR:
14345         sprintf(vvbuf,"%d",GetSystemMetrics(SM_CYSCREEN));  /* SAFE */
14346         return(vvbuf);
14347       case VN_GUI_FNM:
14348           if ( ntermfont > 0 ) {
14349               int i;
14350               for (i = 0; i < ntermfont; i++) {
14351                   if (tt_font == term_font[i].kwval) {
14352                       ckstrncpy(vvbuf,term_font[i].kwd,VVBUFL);
14353                       return(vvbuf);
14354                   }
14355               }
14356           }
14357           return("(unknown)");
14358       case VN_GUI_FSZ:
14359           ckstrncpy(vvbuf,ckitoa(tt_font_size/2),VVBUFL);
14360           if ( tt_font_size % 2 )
14361               ckstrncat(vvbuf,".5",VVBUFL);
14362           return(vvbuf);
14363     }
14364 #endif /* KUI */
14365
14366     fnsuccess = 0;
14367     if (fnerror) {
14368         fnsuccess = 0;
14369     }
14370     if (fndiags) {
14371         if (!embuf[0])
14372           ckstrncpy(embuf,"<ERROR:NO_SUCH_VARIABLE>",EMBUFLEN);
14373         printf("?%s\n",embuf);
14374         return((char *)embuf);
14375     } else
14376       return("");
14377 }
14378 #endif /* NOSPL */
14379
14380
14381 /*
14382   X X S T R I N G  --  Expand variables and backslash codes.
14383
14384     int xxtstring(s,&s2,&n);
14385
14386   Expands \ escapes via recursive descent.
14387   Argument s is a pointer to string to expand (source).
14388   Argument s2 is the address of where to put result (destination).
14389   Argument n is the length of the destination string (to prevent overruns).
14390   Returns -1 on failure, 0 on success,
14391     with destination string null-terminated and s2 pointing to the
14392     terminating null, so that subsequent characters can be added.
14393     Failure reasons include destination buffer is filled up.
14394 */
14395
14396 #define XXDEPLIM 100                    /* Recursion depth limit */
14397 /*
14398   In Windows the stack is limited to 256K so big character arrays like
14399   vnambuf can't be on the stack in recursive functions like zzstring().
14400   But that's no reason use malloc() in Unix or VMS, which don't have
14401   this kind of restriction.
14402 */
14403 #ifdef DVNAMBUF                         /* Dynamic vnambuf[] */
14404 #undef DVNAMBUF                         /* Clean slate */
14405 #endif /* DVNAMBUF */
14406
14407 #ifndef NOSPL                           /* Only if SPL included */
14408 #ifdef OS2                              /* Only for K95 */
14409 #define DVNAMBUF
14410 #endif /* OS2 */
14411 #endif /* NOSPL */
14412
14413 int
14414 zzstring(s,s2,n) char *s; char **s2; int *n; {
14415     int x,                              /* Current character */
14416         y,                              /* Worker */
14417         pp,                             /* Paren level */
14418         kp,                             /* Brace level */
14419         argn,                           /* Function argument counter */
14420         n2,                             /* Local copy of n */
14421         d,                              /* Array dimension */
14422         vbi,                            /* Variable id (integer form) */
14423         argl,                           /* String argument length */
14424         nx,                             /* Save original length */
14425         quoting = 0;                    /* 299 */
14426
14427     char vb,                            /* Variable id (char form) */
14428         *vp,                            /* Pointer to variable definition */
14429         *new,                           /* Local pointer to target string */
14430 #ifdef COMMENT
14431         *old,                           /* Save original target pointer */
14432 #endif /* COMMENT */
14433         *p,                             /* Worker */
14434         *q,                             /* Worker */
14435         *s3;                            /* Worker */
14436     int  x3;                            /* Worker */
14437     char *r  = (char *)0;               /* For holding function args */
14438     char *r2 = (char *)0;
14439     char *r3p;
14440
14441 #ifndef NOSPL
14442 #ifdef DVNAMBUF
14443     char * vnambuf = NULL;              /* Buffer for variable/function name */
14444 #else /* DVNAMBUF */
14445     char vnambuf[VNAML];                /* Buffer for variable/function name */
14446 #endif /* DVNAMBUF */
14447     char *argp[FNARGS];                 /* Pointers to function args */
14448 #endif /* NOSPL */
14449
14450     static int depth = 0;               /* Call depth, avoid overflow */
14451
14452     n2 = *n;                            /* Make local copies of args */
14453     nx = n2;
14454
14455 #ifdef COMMENT
14456     /* This is always 32K in BIGBUFOK builds */
14457     if (depth == 0)
14458       debug(F101,"zzstring top-level n","",n2);
14459 #endif  /* COMMENT */
14460
14461     new = *s2;                          /* for one less level of indirection */
14462 #ifdef COMMENT
14463     old = new;
14464 #endif /* COMMENT */
14465
14466 #ifndef NOSPL
14467     itsapattern = 0;                    /* For \fpattern() */
14468     isjoin = 0;                         /* For \fjoin() */
14469 #endif /* NOSPL */
14470     depth++;                            /* Sink to a new depth */
14471     if (depth > XXDEPLIM) {             /* Too deep? */
14472         printf("?definition is circular or too deep\n");
14473         debug(F101,"zzstring fail","",depth);
14474         depth = 0;
14475         *new = NUL;
14476         return(-1);
14477     }
14478     if (!s || !new) {                   /* Watch out for null pointers */
14479         debug(F101,"zzstring fail 2","",depth);
14480         if (new)
14481           *new = NUL;
14482         depth = 0;
14483         return(-1);
14484     }
14485     s3 = s;
14486     argl = 0;
14487     while (*s3++) argl++;              /* Get length of source string */
14488     debug(F010,"zzstring entry",s,0);
14489     if (argl == 0) {                    /* Empty string */
14490         debug(F111,"zzstring empty arg",s,argl);
14491         depth = 0;
14492         *new = NUL;
14493         return(0);
14494     }
14495     if (argl < 0) {                     /* Watch out for garbage */
14496         debug(F101,"zzstring fail 3","",depth);
14497         *new = NUL;
14498         depth = 0;
14499         return(-1);
14500     }
14501 #ifdef DVNAMBUF
14502     debug(F100,"vnambuf malloc...","",0);
14503     vnambuf = malloc(VNAML);
14504     if (vnambuf == NULL) {
14505         printf("?Out of memory");
14506         return(-1);
14507     }
14508     debug(F100,"vnambuf malloc ok","",0);
14509 #endif /* DVNAMBUF */
14510
14511     while ((x = *s)) {                  /* Loop for all characters */
14512         if (x != CMDQ) {                /* Is it the command-quote char? */
14513             *new++ = *s++;              /* No, normal char, just copy */
14514             if (--n2 < 0) {             /* and count it, careful of overflow */
14515                 debug(F101,"zzstring overflow 1","",depth);
14516                 depth = 0;
14517 #ifdef DVNAMBUF
14518                 if (vnambuf) free(vnambuf);
14519 #endif /* DVNAMBUF */
14520                 return(-1);
14521             }
14522             continue;
14523         }
14524
14525 /* We have the command-quote character. */
14526
14527         x = *(s+1);                     /* Get the following character. */
14528         if (isupper(x)) x = tolower(x);
14529         switch (x) {                    /* Act according to variable type */
14530 #ifndef NOSPL
14531           case 0:                       /* It's a lone backslash */
14532             *new++ = *s++;
14533             if (--n2 < 0) {
14534                 debug(F101,"zzstring overflow 2","",0);
14535 #ifdef DVNAMBUF
14536                 if (vnambuf) free(vnambuf);
14537 #endif /* DVNAMBUF */
14538                 return(-1);
14539             }
14540             break;
14541           case '%':                     /* Variable */
14542             s += 2;                     /* Get the letter or digit */
14543             vb = *s++;                  /* and move source pointer past it */
14544             vp = NULL;                  /* Assume definition is empty */
14545             if (vb >= '0' && vb <= '9') { /* Digit for macro arg */
14546                 if (maclvl < 0)         /* Digit variables are global */
14547                   vp = g_var[vb];       /* if no macro is active */
14548                 else                    /* otherwise */
14549                   vp = m_arg[maclvl][vb - '0']; /* they're on the stack */
14550             } else if (vb == '*') {     /* Macro args string */
14551 #ifdef COMMENT
14552                 /* This doesn't take changes into account */
14553                 vp = (maclvl >= 0) ? m_line[maclvl] : topline;
14554                 if (!vp) vp = "";
14555 #else
14556                 char * ss = new;
14557                 if (zzstring("\\fjoin(&_[],,1)",&new,&n2) < 0) {
14558 #ifdef DVNAMBUF
14559                     if (vnambuf) free(vnambuf);
14560 #endif /* DVNAMBUF */
14561                     return(-1);
14562                 }
14563                 debug(F110,"zzstring \\%*",ss,0);
14564                 break;
14565 #endif /* COMMENT */
14566             } else {
14567                 if (isupper(vb)) vb += ('a'-'A');
14568                 vp = g_var[vb];         /* Letter for global variable */
14569             }
14570             if (!vp) vp = "";
14571 #ifdef COMMENT
14572             if (vp) {                   /* If definition not empty */
14573 #endif /* COMMENT */
14574                 if (vareval) {
14575                     debug(F010,"zzstring %n vp",vp,0);
14576                     /* call self to evaluate it */
14577                     if (zzstring(vp,&new,&n2) < 0) {
14578                         debug(F101,"zzstring fail 6","",depth);
14579 #ifdef DVNAMBUF
14580                         if (vnambuf) free(vnambuf);
14581 #endif /* DVNAMBUF */
14582                         return(-1);     /* Pass along failure */
14583                     }
14584                 } else {
14585                     while ((*new++ = *vp++)) /* copy it to output string. */
14586                       if (--n2 < 0) {
14587                           if (q) free(q);
14588                           debug(F101,"zzstring overflow 4.5","",depth);
14589 #ifdef DVNAMBUF
14590                           if (vnambuf) free(vnambuf);
14591 #endif /* DVNAMBUF */
14592                           return(-1);
14593                       }
14594                     new--;              /* Back up over terminating null */
14595                     n2++;               /* to allow for further deposits. */
14596                 }
14597 #ifdef COMMENT
14598             } else {
14599                 debug(F110,"zzstring %n vp","(NULL)",0);
14600                 n2 = nx;
14601                 new = old;
14602                 *new = NUL;
14603             }
14604 #endif /* COMMENT */
14605             break;
14606           case '&':                     /* An array reference */
14607             x = arraynam(s,&vbi,&d);    /* Get name and subscript */
14608             debug(F111,"zzstring arraynam",s,x);
14609             if (x < 0) {
14610                 debug(F101,"zzstring fail 7","",depth);
14611 #ifdef DVNAMBUF
14612                 if (vnambuf) free(vnambuf);
14613 #endif /* DVNAMBUF */
14614                 return(-1);
14615             }
14616             pp = 0;                     /* Bracket counter */
14617             while (*s) {                /* Advance source pointer... */
14618                 if (*s == '[') pp++;
14619                 if (*s == ']' && --pp == 0) break;
14620                 s++;
14621             }
14622             if (*s == ']') s++;         /* ...past the closing bracket. */
14623
14624             x = chkarray(vbi,d);        /* Array is declared? */
14625             debug(F101,"zzstring chkarray","",x);
14626             if (x > -1) {
14627 #ifdef COMMENT
14628                 char * s1 = NULL;
14629 #endif /* COMMENT */
14630                 vbi -= ARRAYBASE;       /* Convert name to index */
14631
14632                   if (a_dim[vbi] >= d) { /* If subscript in range */
14633                     char **ap;
14634                     ap = a_ptr[vbi];    /* get data pointer */
14635                     if (ap) {           /* and if there is one */
14636                         if (ap[d]) {    /* If definition not empty */
14637                             debug(F111,"zzstring ap[d]",ap[d],d);
14638                             if (vareval) {
14639                                 if (zzstring(ap[d],&new,&n2) < 0) {
14640                                     debug(F101,"zzstring fail 8","",depth);
14641 #ifdef DVNAMBUF
14642                                     if (vnambuf) free(vnambuf);
14643 #endif /* DVNAMBUF */
14644                                     return(-1); /* Pass along failure */
14645                                 }
14646                             } else {
14647                                 vp = ap[d];
14648                                 while ((*new++ = *vp++)) /* copy to result */
14649                                   if (--n2 < 0) {
14650                                       if (q) free(q);
14651                                       debug(F101,
14652                                             "zzstring overflow 8.5","",depth);
14653 #ifdef DVNAMBUF
14654                                       if (vnambuf) free(vnambuf);
14655 #endif /* DVNAMBUF */
14656                                       return(-1);
14657                                   }
14658                                 new--;  /* Back up over terminating null */
14659                                 n2++;   /* to allow for further deposits. */
14660                             }
14661                         }
14662
14663                     } else {
14664                         /* old = new; */
14665                         n2 = nx;
14666                     }
14667                 }
14668             }
14669             break;
14670
14671           case 'f':                     /* A builtin function */
14672             q = vnambuf;                /* Copy the name */
14673             y = 0;                      /* into a separate buffer */
14674             s += 2;                     /* point past 'F' */
14675             while (y++ < VNAML) {
14676                 if (*s == '(') { s++; break; } /* Look for open paren */
14677                 if ((*q = *s) == NUL) break;   /* or end of string */
14678                 s++; q++;
14679             }
14680             *q = NUL;                   /* Terminate function name */
14681             if (y >= VNAML) {           /* Handle pathological case */
14682                 while (*s && (*s != '(')) /* of very long string entered */
14683                   s++;                    /* as function name. */
14684                 if (*s == ')') s++;       /* Skip past it. */
14685             }
14686             r = r2 = malloc(argl+2);    /* And make a place to copy args */
14687             /* debug(F101,"zzstring r2","",r2); */
14688             if (!r2) {                  /* Watch out for malloc failure */
14689                 debug(F101,"zzstring fail 9","",depth);
14690                 *new = NUL;
14691                 depth = 0;
14692 #ifdef DVNAMBUF
14693                 if (vnambuf) free(vnambuf);
14694 #endif /* DVNAMBUF */
14695                 return(-1);
14696             }
14697             if (r3) free(r3); /* And another to copy literal arg string */
14698             r3 = malloc(argl+2);
14699             /* debug(F101,"zzstring r3","",r3); */
14700             if (!r3) {
14701                 debug(F101,"zzstring fail 10","",depth);
14702                 depth = 0;
14703                 *new = NUL;
14704                 if (r2) free(r2);
14705 #ifdef DVNAMBUF
14706                 if (vnambuf) free(vnambuf);
14707 #endif /* DVNAMBUF */
14708                 return(-1);
14709             } else
14710               r3p = r3;
14711             argn = 0;                   /* Argument counter */
14712             argp[argn++] = r;           /* Point to first argument */
14713             y = 0;                      /* Completion flag */
14714             pp = 1;                     /* Paren level (already have one). */
14715             kp = 0;
14716             while (1) {                 /* Copy each argument, char by char. */
14717                 *r3p++ = *s;            /* This is a literal copy for \flit */
14718                 if (!*s) break;
14719
14720                 if (*s == '{') {        /* Left brace */
14721                     kp++;
14722                 }
14723                 if (*s == '}') {        /* Right brace */
14724                     kp--;
14725                 }
14726                 if (*s == '(' && kp <= 0) { /* Open paren not in brace */
14727                     pp++;               /* Count it */
14728                 }
14729                 *r = *s;                /* Now copy resulting byte */
14730                 if (!*r)                /* If NUL, done. */
14731                   break;
14732                 if (*r == ')' && kp <= 0) { /* Closing paren, count it. */
14733                     if (--pp == 0) {    /* Final one? */
14734                         *r = NUL;       /* Make it a terminating null */
14735                         *(r3p - 1) = NUL;
14736                         s++;            /* Point past it in source string */
14737                         y = 1;          /* Flag we've got all the args */
14738                         break;          /* Done with while loop */
14739                     }
14740                 }
14741                 if (*r == ',' && kp <= 0) { /* Comma */
14742                     if (pp == 1) {          /* If not within ()'s, */
14743                         if (argn >= FNARGS) { /* Too many args */
14744                             s++; r++;   /* Keep collecting flit() string */
14745                             continue;
14746                         }
14747                         *r = NUL;           /* New arg, skip past comma */
14748                         argp[argn++] = r+1; /* In range, point to new arg */
14749                     }                   /* Otherwise just skip past  */
14750                 }
14751                 s++; r++;               /* Advance pointers */
14752             }
14753             if (!y)                     /* If we didn't find closing paren */
14754               argn = -1;
14755 #ifdef DEBUG
14756             if (deblog) {
14757                 char buf[24];
14758                 debug(F111,"zzstring function name",vnambuf,y);
14759                 debug(F010,"zzstring function r3",r3,0);
14760                 for (y = 0; y < argn; y++) {
14761                     sprintf(buf,"arg %2d ",y);
14762                     debug(F010,buf,argp[y],0);
14763                 }
14764             }
14765 #endif /* DEBUG */
14766             {
14767              /* In case the function name itself is constructed */
14768                 char buf[64]; char * p = buf; int n = 64; 
14769                 if (zzstring(vnambuf,&p,&n) > -1)
14770                   ckstrncpy(vnambuf,buf,64);
14771             }
14772             vp = fneval(vnambuf,argp,argn,r3); /* Evaluate the function. */
14773             if (vp) {                      /* If definition not empty */
14774                 while ((*new++ = *vp++)) { /* copy it to output string */
14775                     if (--n2 < 0) {        /* watch out for overflow */
14776                         debug(F101,"zzstring fail 12","",depth);
14777                         if (r2) { free(r2); r2 = NULL; }
14778                         if (r3) { free(r3); r3 = NULL; }
14779 #ifdef DVNAMBUF
14780                         if (vnambuf) free(vnambuf);
14781 #endif /* DVNAMBUF */
14782                         return(-1);
14783                     }
14784                 }
14785                 new--;                  /* Back up over terminating null */
14786                 n2++;                   /* to allow for further deposits. */
14787             }
14788             if (r2) { free(r2); r2 = NULL; }
14789             if (r3) { free(r3); r3 = NULL; }
14790             break;
14791           case 'q':                     /* 299 String to be take literally */
14792             quoting = 1;                /* 299 */
14793           case '$':                     /* An environment variable */
14794           case 'v':                     /* Or a named builtin variable. */
14795           case 'm':                     /* Or a macro /long variable */
14796           case 's':                     /* 196 Macro substring */
14797           case ':':                     /* 196 \-variable substring */
14798             pp = 0;
14799             p = s+2;                    /* $/V/M must be followed by (name) */
14800             if (*p != '(') {            /* as in \$(HOME) or \V(count) */
14801                 *new++ = *s++;          /* If not, just copy it */
14802                 if (--n2 < 0) {
14803                     debug(F101,"zzstring overflow 3","",depth);
14804 #ifdef DVNAMBUF
14805                     if (vnambuf) free(vnambuf);
14806 #endif /* DVNAMBUF */
14807                     return(-1);
14808                 }
14809                 break;
14810             }
14811             pp++;
14812             p++;                        /* Point to 1st char of name */
14813             q = vnambuf;                /* Copy the name */
14814             y = 0;                      /* into a separate buffer */
14815             debug(F110,">>>> \\q(ARG)",p,0);
14816             while (y++ < VNAML) {       /* Watch out for name too long */
14817                 if (*p == '(') {        /* Parens can be nested... */
14818                     if (*(p-1) != CMDQ) /* 299 */
14819                       pp++;
14820                 } else if (*p == ')') { /* Name properly terminated with ')' */
14821                     if (*(p-1) != CMDQ) /* 299 */
14822                       pp--;
14823                     if (pp == 0) {
14824                         p++;            /* Move source pointer past ')' */
14825                         break;
14826                     }
14827                 }
14828                 if ((*q = *p) == NUL)   /* String ends before ')' */
14829                   break;
14830                 p++; q++;               /* Advance pointers */
14831             }
14832             *q = NUL;                   /* Terminate the variable name */
14833             if (y >= VNAML) {           /* Handle pathological case */
14834                 while (*p && (*p != ')')) /* of very long string entered */
14835                   p++;                    /* as variable name. */
14836                 if (*p == ')') p++;       /* Skip ahead to the end of it. */
14837             }
14838             s = p;                      /* Adjust global source pointer */
14839             s3 = vnambuf;
14840             x3 = 0;
14841             while (*s3++) x3++;
14842             p = malloc(x3 + 1);         /* Make temporary space */
14843             if (p && !quoting) {        /* If we got the space */
14844                 vp = vnambuf;           /* Point to original */
14845                 strcpy(p,vp);           /* (safe) Make a copy of it */
14846                 y = VNAML;              /* Length of name buffer */
14847                 zzstring(p,&vp,&y);     /* Evaluate the copy */
14848                 free(p);                /* Free the temporary space */
14849                 p = NULL;
14850             }
14851             debug(F110,"zzstring vname",vnambuf,0);
14852             q = NULL;
14853             if (x == 'q') {             /* 299 Quoting this string */
14854                 vp = vnambuf;           /* 299 */
14855                 debug(F110,">>> VP",vp,0);
14856             } else if (x == '$') {      /* Look up its value */
14857                 vp = getenv(vnambuf);   /* This way for environment variable */
14858             } else if (x == 'm' || x == 's' || x == ':') { /* Macro / substr */
14859                 int k, x1 = -1, x2 = -1;
14860                 char c = NUL; 
14861                 k = strlen(vnambuf);
14862                 /* \s(name[n:m]) -- Compact substring notation */
14863                 if ((x == 's' || x == ':') && (k > 1)) { /* Substring wanted */
14864                     int bprc;
14865                     if (vnambuf[k-1] == ']') {
14866                         int i;
14867                         for (i = 0; i < k-1; i++) {
14868                             if (vnambuf[i] == '[') {
14869                                 bprc = boundspair(vnambuf,":_",&x1,&x2,&c);
14870                                 debug(F111,"zzstring boundspair",vnambuf,bprc);
14871                                 debug(F000,"zzstring boundspair c","",c);
14872                                 if (bprc > -1) {
14873                                     vnambuf[i] = NUL;
14874                                     if (x1 < 1)
14875                                       x1 = 1;
14876                                     x1--;       /* Adjust to 0-base */
14877                                 }
14878                                 break;
14879                             }
14880                         }
14881                     }
14882                 }
14883                 if (x == ':') {         /* Variable type (s or :) */
14884                     vp = vnambuf;
14885                 } else {
14886                     y = isaarray(vnambuf) ?
14887                         mxxlook(mactab,vnambuf,nmac) :
14888                         mxlook(mactab,vnambuf,nmac);
14889                     if (y > -1) {       /* Got definition */
14890                         vp = mactab[y].mval;
14891                     } else {
14892                         vp = NULL;
14893                     }
14894                 }
14895                 debug(F111,"zzstring vp",vp,(vp==NULL)?0:strlen(vp));
14896
14897                 if (vp) {
14898                     if ((x == 's' || x == ':') && (k > 1)) {
14899                         /* Compact substring notation */
14900                         if (x2 == 0) {  /* Length */
14901                             vp = NULL;
14902                         } else if (x1 > -1) { /* Start */
14903                             k = strlen(vp);
14904                             debug(F101,">>> k","",k);
14905                             /* If it's off the end, result is empty */
14906                             if (x1 > k) {
14907                                 vp = NULL;
14908                             } else if (k > 0) {
14909                                 /* Stay in bounds */
14910                                 if (c == '_' && x2 > k) /* startpos_endpos */
14911                                   x2 = k;
14912                                 if (c == ':' && x1 + x2 > k) /* start:length */
14913                                   x2 = -1;
14914                                 debug(F101,">>> x2","",x2);
14915                                 debug(F000,">>> c","",c);
14916                                 if ((q = malloc(k+1))) {
14917                                     strcpy(q,vp); /* safe */
14918                                     if (c == ':') { /* start:length */
14919                                         if ((x2 > -1) && ((x1 + x2) <= k)) {
14920                                             q[x1+x2] = NUL;
14921                                         }
14922                                         debug(F000,"XXX q",q,c);
14923                                     } else if (c == '_') { /* start_endpos */
14924                                         if (x1 >= x2) {
14925                                             q[x1 = 0] = NUL;
14926                                         } else if (x2 < k && x2 > -1) {
14927                                             q[x2] = NUL;
14928                                         }
14929                                         debug(F000,"XXX q",q,c);
14930                                     }
14931                                     vp = q+x1;
14932                                 } else vp = NULL;
14933                             } else vp = NULL;
14934                         }
14935
14936                         debug(F110,"XXX vnambuf",vnambuf,0);
14937                         debug(F000,"XXX c","",c);
14938                         debug(F101,"XXX x1","",x1);
14939                         debug(F101,"XXX x2","",x2);
14940                         debug(F110,"XXX result",vp,0);
14941 #ifdef DEBUG
14942                         if (deblog) {
14943                             if (!vp) {
14944                             } else {
14945                                 k = strlen(vp);
14946                             }
14947                         }
14948 #endif /* DEBUG */
14949                     }
14950                 }
14951             } else {                    /* or */
14952                 vp = nvlook(vnambuf);   /* this way for builtin variable */
14953             }
14954             if (vp) {                   /* If definition not empty */
14955                 while ((*new++ = *vp++)) /* copy it to output string. */
14956                   if (--n2 < 0) {
14957                       if (q) free(q);
14958                       debug(F101,"zzstring overflow 4","",depth);
14959 #ifdef DVNAMBUF
14960                       if (vnambuf) free(vnambuf);
14961 #endif /* DVNAMBUF */
14962                       return(-1);
14963                   }
14964                 new--;                  /* Back up over terminating null */
14965                 n2++;                   /* to allow for further deposits. */
14966             }
14967             if (q) {
14968                 free(q);
14969                 q = NULL;
14970             }
14971             break;
14972 #endif /* NOSPL */                      /* Handle \nnn even if NOSPL. */
14973
14974 #ifndef NOKVERBS
14975         case 'K':
14976         case 'k': {
14977             extern struct keytab kverbs[];
14978             extern int nkverbs;
14979 #define K_BUFLEN 30
14980             char kbuf[K_BUFLEN + 1];    /* Key verb name buffer */
14981             int x, y, z, brace = 0;
14982             s += 2;
14983 /*
14984   We assume that the verb name is {braced}, or it extends to the end of the
14985   string, s, or it ends with a space, control character, or backslash.
14986 */
14987             p = kbuf;                   /* Copy verb name into local buffer */
14988             x = 0;
14989             if (*s == '{')  {
14990                 s++;
14991                 brace++;
14992             }
14993             while ((x++ < K_BUFLEN) && (*s > SP) && (*s != CMDQ)) {
14994                 if (brace && *s == '}') {
14995                     s++;
14996                     break;
14997                 }
14998                 *p++ = *s++;
14999             }
15000             brace = 0;
15001             *p = NUL;                   /* Terminate. */
15002             p = kbuf;                   /* Point back to beginning */
15003             debug(F110,"zzstring kverb",p,0);
15004             y = xlookup(kverbs,p,nkverbs,&x); /* Look it up */
15005             debug(F101,"zzstring lookup",0,y);
15006             if (y > -1) {
15007                 dokverb(VCMD,y);
15008 #ifndef NOSPL
15009             } else {                    /* Is it a macro? */
15010                 y = mxlook(mactab,p,nmac);
15011                 if (y > -1) {
15012                     debug(F111,"zzstring mxlook",p,y);
15013                     if ((z = dodo(y,NULL,cmdstk[cmdlvl].ccflgs)) > 0) {
15014                         if (cmpush() > -1) {  /* Push command parser state */
15015                             extern int ifc;
15016                             int ifcsav = ifc; /* Push IF condition on stack */
15017                             y = parser(1);    /* New parser to execute macro */
15018                             cmpop();          /* Pop command parser */
15019                             ifc = ifcsav;     /* Restore IF condition */
15020                             if (y == 0) {     /* No errors, ignore actions */
15021                                 p = mrval[maclvl+1]; /* If OK set return val */
15022                                 if (p == NULL) p = "";
15023                             }
15024                         } else {                /* Can't push any more */
15025                             debug(F101,"zzstring pushed too deep","",depth);
15026                             printf(
15027                                "\n?Internal error: zzstring stack overflow\n"
15028                                    );
15029                             while (cmpop() > -1);
15030                             p = "";
15031                         }
15032                     }
15033                 }
15034 #endif /* NOSPL */
15035             }
15036             break;
15037         }
15038 #endif /* NOKVERBS */
15039
15040         default:                        /* Maybe it's a backslash code */
15041           y = xxesc(&s);                /* Go interpret it */
15042           if (y < 0) {                  /* Upon failure */
15043               *new++ = (char) x;        /* Just quote the next character */
15044               s += 2;                   /* Move past the pair */
15045               n2 -= 2;
15046               if (n2 < 0) {
15047                   debug(F101,"zzstring overflow 5","",depth);
15048 #ifdef DVNAMBUF
15049                   if (vnambuf) free(vnambuf);
15050 #endif /* DVNAMBUF */
15051                   return(-1);
15052               }
15053               continue;                 /* and go back for more */
15054           } else {
15055               *new++ = (char) y;        /* else deposit interpreted value */
15056               if (--n2 < 0) {
15057                   debug(F101,"zzstring overflow 6","",depth);
15058 #ifdef DVNAMBUF
15059                   if (vnambuf) free(vnambuf);
15060 #endif /* DVNAMBUF */
15061                   return(-1);
15062               }
15063           }
15064         }
15065     }
15066     *new = NUL;                         /* Terminate the new string */
15067     debug(F010,"zzstring while exit",*s2,0);
15068
15069     depth--;                            /* Adjust stack depth gauge */
15070     *s2 = new;                          /* Copy results back into */
15071     *n = n2;                            /* the argument addresses */
15072     debug(F101,"zzstring ok","",depth);
15073 #ifdef DVNAMBUF
15074     if (vnambuf) free(vnambuf);
15075 #endif /* DVNAMBUF */
15076     return(0);                          /* and return. */
15077 }
15078 #endif /* NOICP */