ckucmd.c
上传用户:dufan58
上传日期:2007-01-05
资源大小:3407k
文件大小:151k
源码类别:

通讯/手机编程

开发平台:

Windows_Unix

  1. #include "ckcsym.h"
  2. #define DOCHKVAR
  3. char *cmdv = "Command package 7.0.121, 24 Dec 1999";
  4. /*  C K U C M D  --  Interactive command package for Unix  */
  5. /*
  6.   Author: Frank da Cruz (fdc@columbia.edu),
  7.   Columbia University Academic Information Systems, New York City.
  8.   Copyright (C) 1985, 2000,
  9.     Trustees of Columbia University in the City of New York.
  10.     All rights reserved.  See the C-Kermit COPYING.TXT file or the
  11.     copyright text in the ckcmai.c module for disclaimer and permissions.
  12. */
  13. #ifdef OS2                    /* Command-terminal-to-C-Kermit character mask */
  14. int cmdmsk = 255;
  15. #else
  16. int cmdmsk = 127;
  17. #endif /* OS2 */
  18. #ifdef BS_DIRSEP /* Directory separator is backslash */
  19. #undef BS_DIRSEP
  20. #endif /* BS_DIRSEP */
  21. #ifdef OS2
  22. #define BS_DIRSEP
  23. #endif /* BS_DIRSEP */
  24. #define CKUCMD_C
  25. #include "ckcdeb.h"                     /* Formats for debug(), etc. */
  26. #include "ckcker.h" /* Needed for BIGBUFOK definition */
  27. #include "ckcnet.h" /* Needed for server-side Telnet */
  28. #include "ckucmd.h" /* Needed for xx_strp prototype */
  29. #include "ckuusr.h"                     /* Needed for prompt length */
  30. #undef CKUCMD_C
  31. _PROTOTYP( int unhex, (char) );
  32. _PROTOTYP( static VOID cmdclrscn, (void) );
  33. struct keytab cmonths[] = {
  34.   { "april",     4, 0 },
  35.   { "august",    8, 0 },
  36.   { "december", 12, 0 },
  37.   { "february",  2, 0 },
  38.   { "january",   1, 0 },
  39.   { "july",      7, 0 },
  40.   { "june",      6, 0 },
  41.   { "march",     3, 0 },
  42.   { "may",       5, 0 },
  43.   { "november", 11, 0 },
  44.   { "october",  10, 0 },
  45.   { "september", 9, 0 }
  46. };
  47. #ifndef NOICP     /* The rest only if interactive command parsing selected */
  48. #ifndef NOSPL
  49. _PROTOTYP( int chkvar, (char *) );
  50. extern int askflag;
  51. #endif /* NOSPL */
  52. int cmfldflgs = 0; /* Flags for cmfld() */
  53. static int cmkwflgs = 0; /* Flags from last keyword parse */
  54. static int blocklvl = 0; /* Block nesting level */
  55. static int linebegin = 0; /* Flag for at start of a line */
  56. static int quoting = 1; /* Quoting is allowed */
  57. static int swarg = 0; /* Parsing a switch argument */
  58. static int xcmfdb = 0; /* Flag for parsing chained fdbs... */
  59. static int chsrc = 0; /* Source of character, 1 = tty */
  60. #ifdef BS_DIRSEP
  61. static int dirnamflg = 0;
  62. #endif /* BS_DIRSEP */
  63. /*
  64. Modeled after the DECSYSTEM-20 command parser (the COMND JSYS), RIP. Features:
  65. . parses and verifies keywords, filenames, text strings, numbers, other data
  66. . displays appropriate menu or help message when user types "?"
  67. . does keyword and filename completion when user types ESC or TAB
  68. . does partial filename completion
  69. . accepts any unique abbreviation for a keyword
  70. . allows keywords to have attributes, like "invisible" and "abbreviation"
  71. . can supply defaults for fields omitted by user
  72. . provides command retry and recall
  73. . provides command line editing (character, word, and line deletion)
  74. . accepts input from keyboard, command files, macros, or redirected stdin
  75. . allows for full or half duplex operation, character or line input
  76. . allows -escapes for hard-to-type characters
  77. . allows specification of a user exit to expand variables, etc.
  78. . settable prompt, protected from deletion, dynamically re-evaluated each time.
  79. . allows chained parse functions.
  80. Functions:
  81.  cmsetp - Set prompt (cmprom is prompt string)
  82.  cmsavp - Save current prompt
  83.  prompt - Issue prompt
  84.  cmini  - Clear the command buffer (before parsing a new command)
  85.  cmres  - Reset command buffer pointers (before reparsing)
  86.  cmkey  - Parse a keyword or token (also cmkey2)
  87.  cmswi  - Parse a switch
  88.  cmnum  - Parse a number
  89.  cmifi  - Parse an input file name
  90.  cmofi  - Parse an output file name (also cmifip, cmifi2, ...)
  91.  cmdir  - Parse a directory name (also cmdirp)
  92.  cmfld  - Parse an arbitrary field
  93.  cmtxt  - Parse a text string
  94.  cmdate - Parse a date-time string
  95.  cmcfm  - Parse command confirmation (end of line)
  96.  cmfdb  - Parse any of a list of the foregoing (chained parse functions)
  97. Return codes:
  98.  -9: like -2 except this module already printed the error message
  99.  -3: no input provided when required
  100.  -2: input was invalid (e.g. not a number when a number was required)
  101.  -1: reparse required (user deleted into a preceding field)
  102.   0 or greater: success
  103. See individual functions for greater detail.
  104. Before using these routines, the caller should #include ckucmd.h, and set the
  105. program's prompt by calling cmsetp().  If the file parsing functions cmifi,
  106. cmofi, or cmdir are to be used, this module must be linked with a ck?fio file
  107. system support module for the appropriate system, e.g. ckufio for Unix.  If
  108. the caller puts the terminal in character wakeup ("cbreak") mode with no echo,
  109. then these functions will provide line editing -- character, word, and line
  110. deletion, as well as keyword and filename completion upon ESC and help
  111. strings, keyword, or file menus upon '?'.  If the caller puts the terminal
  112. into character wakeup/noecho mode, care should be taken to restore it before
  113. exit from or interruption of the program.  If the character wakeup mode is not
  114. set, the system's own line editor may be used.
  115. NOTE: Contrary to expectations, many #ifdef's have been added to this module.
  116. Any operation requiring an #ifdef (like clear screen, get character from
  117. keyboard, erase character from screen, etc) should eventually be turned into a
  118. call to a function that is defined in ck?tio.c, but then all the ck?tio.c
  119. modules would have to be changed...
  120. */
  121. /* Includes */
  122. #include "ckcker.h"
  123. #include "ckcasc.h" /* ASCII character symbols */
  124. #include "ckucmd.h"                     /* Command parsing definitions */
  125. #ifdef OSF13
  126. #ifdef CK_ANSIC
  127. #ifdef _NO_PROTO
  128. #undef _NO_PROTO
  129. #endif /* _NO_PROTO */
  130. #endif /* CK_ANSIC */
  131. #endif /* OSF13 */
  132. #include <errno.h> /* Error number symbols */
  133. #ifdef OS2
  134. #ifndef NT
  135. #define INCL_NOPM
  136. #define INCL_VIO /* Needed for ckocon.h */
  137. #include <os2.h>
  138. #undef COMMENT
  139. #else
  140. #define APIRET ULONG
  141. #include <windows.h>
  142. #endif /* NT */
  143. #include "ckocon.h"
  144. #include <io.h>
  145. #endif /* OS2 */
  146. #ifdef NT
  147. #define stricmp _stricmp
  148. #endif /* NT */
  149. #ifdef OSK
  150. #define cc ccount /* OS-9/68K compiler bug */
  151. #endif /* OSK */
  152. #ifdef GEMDOS /* Atari ST */
  153. #ifdef putchar
  154. #undef putchar
  155. #endif /* putchar */
  156. #define putchar(x) conoc(x)
  157. #endif /* GEMDOS */
  158. #ifdef CK_AUTODL
  159. extern int cmdadl, justone;
  160. #endif /* CK_AUTODL */
  161. extern int timelimit, nzxopts;
  162. #ifdef CKSYSLOG
  163. #ifdef UNIX
  164. #ifdef CKXPRINTF /* Our printf macro conflicts with */
  165. #undef printf /* use of "printf" in syslog.h */
  166. #endif /* CKXPRINTF */
  167. #include <syslog.h>
  168. #ifdef CKXPRINTF
  169. #define printf ckxprintf
  170. #endif /* CKXPRINTF */
  171. #endif /* UNIX */
  172. #endif /* CKSYSLOG */
  173. /* Local variables */
  174. static
  175. int psetf = 0,                          /* Flag that prompt has been set */
  176.     cc = 0,                             /* Character count */
  177.     dpx = 0,                            /* Duplex (0 = full) */
  178.     inword = 0; /* In the middle of getting a word */
  179. #ifdef OLDHELP
  180. static
  181. int hw = HLPLW,                         /* Help line width */
  182.     hc = HLPCW,                         /* Help line column width */
  183.     hh,                                 /* Current help column number */
  184.     hx;                                 /* Current help line position */
  185. #endif /* OLDHELP */
  186. char *dfprom = "Command? ";             /* Default prompt */
  187. int cmflgs;                             /* Command flags */
  188. int cmfsav; /* A saved version of them */
  189. static char pushc = NUL;
  190. static char brkchar = NUL;
  191. #define CMDEFAULT 1023
  192. static char cmdefault[CMDEFAULT+1];
  193. #ifdef DCMDBUF
  194. char *cmdbuf = NULL; /* Command buffer */
  195. char *savbuf = NULL; /* Buffer to save copy of command */
  196. char *atmbuf = NULL; /* Atom buffer - for current field */
  197. char *atxbuf = NULL; /* For expanding the atom buffer */
  198. #ifdef OLDHELP
  199. static char *hlpbuf; /* Help string buffer */
  200. #endif /* OLDHELP */
  201. static char *atybuf = NULL; /* For copying atom buffer */
  202. static char *filbuf = NULL; /* File name buffer */
  203. static char *cmprom = NULL; /* Program's prompt */
  204. static char *cmprxx = NULL; /* Program's prompt, unevaluated */
  205. #ifdef CK_RECALL
  206. /*
  207.   Command recall is available only if we can make profligate use of malloc().
  208. */
  209. #define R_MAX 10 /* How many commands to save */
  210. int cm_recall = R_MAX; /* Size of command recall buffer */
  211. int on_recall = 1; /* Recall feature is ON */
  212. static int no_recall = 0; /* Recall OFF for this cmd only */
  213. static int force_add = 0; /* Force cmd into recall buffer */
  214. int in_recall = 0; /* Recall buffers are init'd */
  215. static int
  216.   current = -1, /* Pointer to current command */
  217.   rlast = -1; /* Index of last command in buffer */
  218. static char **recall = NULL; /* Array of recall buffer pointers */
  219. #endif /* CK_RECALL */
  220. #else
  221. char cmdbuf[CMDBL+4];                   /* Command buffer */
  222. char savbuf[CMDBL+4];                   /* Buffer to save copy of command */
  223. char atmbuf[ATMBL+4];                   /* Atom buffer */
  224. char atxbuf[CMDBL+4];                   /* For expanding the atom buffer */
  225. #ifdef OLDHELP
  226. static char hlpbuf[HLPBL+4]; /* Help string buffer */
  227. #endif /* OLDHELP */
  228. static char atybuf[ATMBL+4]; /* For copying atom buffer */
  229. static char filbuf[ATMBL+4]; /* File name buffer */
  230. static char cmprom[PROMPTL+1]; /* Program's prompt */
  231. static char cmprxx[PROMPTL+1]; /* Program's prompt, unevaluated */
  232. #endif /* DCMDBUF */
  233. /* Command buffer pointers */
  234. #define PPVLEN 24
  235. char ppvnambuf[PPVLEN+1] = { NUL, NUL };
  236. char * cmbptr = NULL; /* Current position (for export) */
  237. static char *bp,                        /* Current command buffer position */
  238.     *pp,                                /* Start of current field */
  239.     *np;                                /* Start of next field */
  240. static int ungw, /* For ungetting words */
  241.     atxn; /* Expansion buffer (atxbuf) length */
  242. #ifdef OS2
  243. extern int wideresult;
  244. #endif /* OS2 */
  245. extern int cmd_cols, cmd_rows, local, quiet;
  246. #ifdef TNCODE
  247. #ifdef IAC
  248. #undef IAC
  249. #endif /* IAC */
  250. #define IAC 255
  251. #endif /* TNCODE */
  252. #ifdef OLDHELP
  253. _PROTOTYP( static VOID addhlp, (char *) );
  254. _PROTOTYP( static VOID clrhlp, (void) );
  255. _PROTOTYP( static VOID dmphlp, (void) );
  256. #endif /* OLDHELP */
  257. _PROTOTYP( static int gtword, (int) );
  258. _PROTOTYP( static int addbuf, (char *) );
  259. _PROTOTYP( static int setatm, (char *, int) );
  260. _PROTOTYP( static VOID cmdnewl, (char) );
  261. _PROTOTYP( static VOID cmdchardel, (void) );
  262. _PROTOTYP( static VOID cmdecho, (char, int) );
  263. _PROTOTYP( static int test, (int, int) );
  264. #ifdef GEMDOS
  265. _PROTOTYP( extern char *strchr, (char *, int) );
  266. #endif /* GEMDOS */
  267. extern char * dftty;
  268. /* The following are for use with chained FDB's */
  269. static int crflag = 0; /* Carriage return was typed */
  270. static int qmflag = 0; /* Question mark was typed */
  271. static int esflag = 0; /* Escape was typed */
  272. /* Directory separator */
  273. #ifdef GEMDOS
  274. static char dirsep = '\';
  275. #else
  276. #ifdef datageneral
  277. static char dirsep = ':';
  278. #else
  279. #ifdef MAC
  280. static char dirsep = ':';
  281. #else
  282. #ifdef VMS
  283. static char dirsep = '.';
  284. #else
  285. #ifdef STRATUS
  286. static char dirsep = '>';
  287. #else
  288. static char dirsep = '/'; /* UNIX, OS/2, OS-9, Amiga, etc. */
  289. #endif /* STRATUS */
  290. #endif /* VMS */
  291. #endif /* MAC */
  292. #endif /* datageneral */
  293. #endif /* GEMDOS */
  294. /*  C K S P R E A D  --  Print string double-spaced  */
  295. static char * sprptr = NULL;
  296. static char *
  297. ckspread(s) char * s; {
  298.     int n = 0;
  299.     char * p;
  300.     n = strlen(s);
  301.     if (sprptr)
  302.       free(sprptr);
  303.     sprptr = malloc(n + n + 3);
  304.     if (sprptr) {
  305. p = sprptr;
  306. while (*s) {
  307.     *p++ = *s++;
  308.     *p++ = SP;
  309. }
  310. *p = NUL;
  311.     }
  312.     return(sprptr ? sprptr : "");
  313. }
  314. /*  T E S T  --  Bit test  */
  315. static int
  316. test(x,m) int x, m; { /*  Returns 1 if any bits from m are on in x, else 0  */
  317.     return((x & m) ? 1 : 0);
  318. }
  319. /*  K W D H E L P  --  Given a keyword table, print keywords in columns.  */
  320. /*
  321.   Call with:
  322.     s     - keyword table
  323.     n     - number of entries
  324.     pat   - pattern (left substring) that must match for each keyword
  325.     pre   - prefix to add to each keyword
  326.     post  - suffix to add to each keyword
  327.     off   - offset on first screenful, allowing room for introductory text
  328.     xhlp  - 1 to print any CM_INV keywords that are not also abbreviations.
  329.             2 to print CM_INV keywords if CM_HLP also set
  330.             4 if it's a switch table (to show ':' if CM_ARG)
  331.   Arranges keywords in columns with width based on longest keyword.
  332.   Does "more?" prompting at end of screen.
  333.   Uses global cmd_rows and cmd_cols for screen size.
  334. */
  335. VOID
  336. kwdhelp(s,n,pat,pre,post,off,xhlp)
  337.     struct keytab s[]; int n, off, xhlp; char *pat, *pre, *post;
  338. /* kwdhelp */ {
  339.     int width = 0;
  340.     int cc;
  341.     int cols, height, i, j, k, lc, n2 = 0;
  342.     char *b = NULL, *p, *q;
  343.     char *pa, *px;
  344.     char **s2 = NULL;
  345.     char *tmpbuf = NULL;
  346.     cc = strlen(pat);
  347.     if (!s) return; /* Nothing to do */
  348.     if (n < 1) return; /* Ditto */
  349.     if (off < 0) off = 0; /* Offset for first page */
  350.     if (!pre) pre = ""; /* Handle null string pointers */
  351.     if (!post) post = "";
  352.     lc = off; /* Screen-line counter */
  353.     if (xhlp & 4) /* For switches */
  354.       tmpbuf = (char *)malloc(TMPBUFSIZ+1);
  355.     if (s2 = (char **) malloc(n * sizeof(char *))) {
  356. for (i = 0; i < n; i++) { /* Find longest keyword */
  357.     s2[i] = NULL;
  358.     if (ckstrcmp(s[i].kwd,pat,cc,0))
  359.       continue;
  360.     if (s[i].flgs & CM_INV) {
  361. #ifdef COMMENT
  362. /* This code does not show invisible keywords at all except for "help ?" */
  363. /* and then only help topics (CM_HLP) in the top-level keyword list. */
  364. if ((xhlp & 2) == 0)
  365.   continue;
  366. else if ((s[i].flgs & CM_HLP) == 0)
  367.   continue;
  368. #else
  369. /* This code shows invisible keywords that are not also abbreviations when */
  370. /* ? was typed AFTER the beginning of the field so the user can find out */
  371. /* what they are and (for example) why completion doesn't work at this point */
  372. if (s[i].flgs & CM_ABR)
  373.   continue;
  374. else if ((xhlp & 3) == 0)
  375.   continue;
  376. else if ((xhlp & 2) && ((s[i].flgs & CM_HLP) == 0))
  377.   continue;
  378. #endif /* COMMENT */
  379.     }
  380.     j = strlen(s[i].kwd);
  381.     if (!(xhlp & 4) || !tmpbuf) { /* Regular keyword table */
  382. s2[n2++] = s[i].kwd; /* Copy pointers to visible ones */
  383.     } else { /* Switches */
  384. sprintf(tmpbuf, /* Make a copy that shows ":" if */
  385. "%s%s", /* the switch takes an argument. */
  386. s[i].kwd,
  387. (s[i].flgs & CM_ARG) ? ":" : ""
  388. );
  389. makestr(&(s2[n2]),tmpbuf);
  390. if (s[i].flgs & CM_ARG) j++;
  391. n2++;
  392.     }
  393.     if (j > width)
  394.       width = j;
  395. }
  396. /* Column width */
  397. n = n2;
  398.     }
  399.     if (s2 && (b = (char *) malloc(cmd_cols + 1))) { /* Make a line buffer   */
  400. char * bx;
  401. bx = b + cmd_cols;
  402. width += (int)strlen(pre) + (int)strlen(post) + 2;
  403. cols = cmd_cols / width; /* How many columns? */
  404. if (cols < 1) cols = 1;
  405. height = n / cols; /* How long is each column? */
  406. if (n % cols) height++; /* Add one for remainder, if any */
  407. for (i = 0; i < height; i++) {     /* Loop for each row */
  408.     for (j = 0; j < cmd_cols; j++)  /* First fill row with blanks */
  409.       b[j] = SP;
  410.     for (j = 0; j < cols; j++) {    /* Loop for each column in row */
  411. k = i + (j * height);       /* Index of next keyword */
  412. if (k < n) {     /* In range? */
  413.     pa = pre;
  414.     px = post;
  415.     p = s2[k];     /* Point to verb name */
  416.     q = b + (j * width) + 1; /* Where to copy it to */
  417.     while ((q < bx) && (*q++ = *pa++)) ; /* Copy prefix */
  418.     q--;                  /* Back up over NUL */
  419.     while ((q < bx) && (*q++ = *p++)) ;  /* Copy filename */
  420.     q--;                  /* Back up over NUL */
  421.     while ((q < bx) && (*q++ = *px++)) ; /* Copy suffix */
  422.     if (j < cols - 1) {
  423. q--;
  424. *q = SP; /* Replace the space */
  425.     }
  426. }
  427.     }
  428.     p = b + cmd_cols - 1; /* Last char in line */
  429.     while (*p-- == SP) ; /* Trim */
  430.     *(p+2) = NUL;
  431.     printf("%sn",b); /* Print the line */
  432.     if (++lc > (cmd_rows - 2)) { /* Screen full? */
  433. if (!askmore()) /* Do more-prompting... */
  434.   goto xkwdhelp;
  435. else
  436.   lc = 0;
  437.     }
  438. }
  439. /* printf("n"); */ /* Blank line at end of report */
  440.     } else { /* Malloc failure, no columns */
  441. for (i = 0; i < n; i++) {
  442.     if (s[i].flgs & CM_INV) /* Use original keyword table */
  443.       continue; /* skipping invisible entries */
  444.     printf("%s%s%sn",pre,s[i].kwd,post);
  445.     if (++lc > (cmd_rows - 2)) { /* Screen full? */
  446. if (!askmore()) /* Do more-prompting... */
  447.   goto xkwdhelp;
  448. else
  449.   lc = 0;
  450.     }
  451. }
  452.     }
  453.   xkwdhelp:
  454.     if (xhlp & 4) {
  455. if (tmpbuf) free(tmpbuf);
  456. for (i = 0; i < n; i++)
  457.   if (s2[i]) free(s2[i]);
  458.     }
  459.     if (s2) free(s2); /* Free array copy */
  460.     if (b) free(b); /* Free line buffer */
  461.     return;
  462. }
  463. /*  F I L H E L P  --  Given a file list, print names in columns.  */
  464. /*
  465.   Call with:
  466.     n     - number of entries
  467.     pre   - prefix to add to each filename
  468.     post  - suffix to add to each filename
  469.     off   - offset on first screenful, allowing room for introductory text
  470.     cmdirflg - 1 if only directory names should be listed, 0 to list all files
  471.   Arranges filenames in columns with width based on longest filename.
  472.   Does "more?" prompting at end of screen.
  473.   Uses global cmd_rows and cmd_cols for screen size.
  474. */
  475. int
  476. filhelp(n,pre,post,off,cmdirflg) int n, off; char *pre, *post; int cmdirflg; {
  477.     char filbuf[CKMAXPATH + 1]; /* Temp buffer for one filename */
  478.     int width = 0;
  479.     int cols, height, i, j, k, lc, n2 = 0, rc = 0, itsadir = 0;
  480.     char *b = NULL, *p, *q;
  481.     char *pa, *px;
  482.     char **s2 = NULL;
  483. #ifdef VMS
  484.     char * cdp = zgtdir();
  485. #endif /* VMS */
  486.     if (n < 1) return(0);
  487.     if (off < 0) off = 0; /* Offset for first page */
  488.     if (!pre) pre = ""; /* Handle null string pointers */
  489.     if (!post) post = "";
  490.     lc = off; /* Screen-line counter */
  491.     if (s2 = (char **) malloc(n * sizeof(char *))) {
  492. for (i = 0; i < n; i++) { /* Loop through filenames */
  493.     itsadir = 0;
  494.     s2[i] = NULL; /* Initialize each pointer to NULL */
  495.     znext(filbuf); /* Get next filename */
  496.     if (!filbuf[0]) /* Shouldn't happen */
  497.       break;
  498. #ifdef COMMENT
  499.     itsadir = isdir(filbuf); /* Is it a directory? */
  500.     if (cmdirflg && !itsadir) /* No, listing directories only? */
  501.       continue; /* So skip this one. */
  502. #endif /* COMMENT */
  503. #ifdef VMS
  504.     ckstrncpy(filbuf,zrelname(filbuf,cdp),CKMAXPATH);
  505. #endif /* VMS */
  506.     j = strlen(filbuf);
  507. #ifndef VMS
  508.     if (itsadir && j < CKMAXPATH - 1 && j > 0) {
  509. if (filbuf[j-1] != dirsep) {
  510.     filbuf[j++] = dirsep;
  511.     filbuf[j] = NUL;
  512. }
  513.     }
  514. #endif /* VMS */
  515.     if (!(s2[n2] = malloc(j+1))) {
  516. printf("?Memory allocation failuren");
  517. rc = -9;
  518. goto xfilhelp;
  519.     }
  520.     if (j <= CKMAXPATH) {
  521. strcpy(s2[n2],filbuf);
  522. n2++;
  523.     } else {
  524. printf("?Name too long - %sn", filbuf);
  525. rc = -9;
  526. goto xfilhelp;
  527.     }
  528.     if (j > width) /* Get width of widest one */
  529.       width = j;
  530. }
  531. n = n2; /* How many we actually got */
  532.     }
  533.     sh_sort(s2,NULL,n,0,0,filecase); /* Alphabetize the list */
  534.     rc = 1;
  535.     if (s2 && (b = (char *) malloc(cmd_cols + 1))) { /* Make a line buffer */
  536. char * bx;
  537. bx = b + cmd_cols;
  538. width += (int)strlen(pre) + (int)strlen(post) + 2;
  539. cols = cmd_cols / width; /* How many columns? */
  540. if (cols < 1) cols = 1;
  541. height = n / cols; /* How long is each column? */
  542. if (n % cols) height++; /* Add one for remainder, if any */
  543. for (i = 0; i < height; i++) {     /* Loop for each row */
  544.     for (j = 0; j < cmd_cols; j++)  /* First fill row with blanks */
  545.       b[j] = SP;
  546.     for (j = 0; j < cols; j++) {    /* Loop for each column in row */
  547. k = i + (j * height);       /* Index of next filename */
  548. if (k < n) {     /* In range? */
  549.     pa = pre;
  550.     px = post;
  551.     p = s2[k];                /* Point to filename */
  552.     q = b + (j * width) + 1;             /* and destination */
  553.     while ((q < bx) && (*q++ = *pa++)) ; /* Copy prefix */
  554.     q--;                  /* Back up over NUL */
  555.     while ((q < bx) && (*q++ = *p++)) ;  /* Copy filename */
  556.     q--;                  /* Back up over NUL */
  557.     while ((q < bx) && (*q++ = *px++)) ; /* Copy suffix */
  558.     if (j < cols - 1) {
  559. q--;
  560. *q = SP; /* Replace the space */
  561.     }
  562. }
  563.     }
  564.     p = b + cmd_cols - 1; /* Last char in line */
  565.     while (*p-- == SP) ; /* Trim */
  566.     *(p+2) = NUL;
  567.     printf("%sn",b); /* Print the line */
  568.     if (++lc > (cmd_rows - 2)) { /* Screen full? */
  569. if (!askmore()) { /* Do more-prompting... */
  570.     rc = 0;
  571.     goto xfilhelp;
  572. } else
  573.   lc = 0;
  574.     }
  575. }
  576. printf("n"); /* Blank line at end of report */
  577. goto xfilhelp;
  578.     } else { /* Malloc failure, no columns */
  579. for (i = 0; i < n; i++) {
  580.     znext(filbuf);
  581.     if (!filbuf[0]) break;
  582.     printf("%s%s%sn",pre,filbuf,post);
  583.     if (++lc > (cmd_rows - 2)) { /* Screen full? */
  584. if (!askmore()) {  /* Do more-prompting... */
  585.     rc = 0;
  586.     goto xfilhelp;
  587. } else lc = 0;
  588.     }
  589. }
  590. xfilhelp:
  591. if (b) free(b);
  592. for (i = 0; i < n2; i++)
  593.   if (s2[i]) free(s2[i]);
  594. if (s2) free(s2);
  595. return(rc);
  596.     }
  597. }
  598. /*  C M S E T U P  --  Set up command buffers  */
  599. #ifdef DCMDBUF
  600. int
  601. cmsetup() {
  602.     if (!(cmdbuf = malloc(CMDBL + 4))) return(-1);
  603.     if (!(savbuf = malloc(CMDBL + 4))) return(-1);
  604.     savbuf[0] = '';
  605. #ifdef OLDHELP
  606.     if (!(hlpbuf = malloc(HLPBL + 4))) return(-1);
  607. #endif /* OLDHELP */
  608.     if (!(atmbuf = malloc(ATMBL + 4))) return(-1);
  609.     if (!(atxbuf = malloc(CMDBL + 4))) return(-1);
  610.     if (!(atybuf = malloc(ATMBL + 4))) return(-1);
  611.     if (!(filbuf = malloc(ATMBL + 4))) return(-1);
  612.     if (!(cmprom = malloc(PROMPTL + 4))) return(-1);
  613.     if (!(cmprxx = malloc(PROMPTL + 4))) return(-1);
  614. #ifdef CK_RECALL
  615.     cmrini(cm_recall);
  616. #endif /* CK_RECALL */
  617.     return(0);
  618. }
  619. #endif /* DCMDBUF */
  620. /*  C M S E T P  --  Set the program prompt.  */
  621. VOID
  622. cmsetp(s) char *s; {
  623.     if (!s) s = "";
  624.     ckstrncpy(cmprxx,s,PROMPTL);
  625.     psetf = 1;                          /* Flag that prompt has been set. */
  626. }
  627. /*  C M S A V P  --  Save a copy of the current prompt.  */
  628. VOID
  629. #ifdef CK_ANSIC
  630. cmsavp(char s[], int n)
  631. #else
  632. cmsavp(s,n) char s[]; int n;
  633. #endif /* CK_ANSIC */
  634. /* cmsavp */ {
  635.     if (psetf) /* But not if no prompt is set. */
  636.       ckstrncpy(s,cmprxx,n);
  637. }
  638. int
  639. cmgbrk() {
  640.     return(brkchar);
  641. }
  642. int
  643. cmgkwflgs() {
  644.     return(cmkwflgs);
  645. }
  646. /*  P R O M P T  --  Issue the program prompt.  */
  647. VOID
  648. prompt(f) xx_strp f; {
  649.     char *sx, *sy; int n;
  650. #ifdef CK_SSL
  651.     extern int ssl_active_flag, tls_active_flag;
  652. #endif /* CK_SSL */
  653. #ifdef OS2
  654.     extern int display_demo;
  655.     /* If there is a demo screen to be displayed, display it */
  656.     if (display_demo && cmdsrc() == 0) {
  657.         demoscrn();
  658.         display_demo = 0;
  659.     }
  660. #endif /* OS2 */
  661.     if (psetf == 0) /* If no prompt set, set default. */
  662.       cmsetp(dfprom);
  663.     sx = cmprxx; /* Unevaluated copy */
  664.     if (f) { /* If conversion function given */
  665. sy = cmprom; /* Evaluate it */
  666. debug(F101,"prompt sx","",sx);
  667. debug(F101,"prompt sy","",sy);
  668. n = PROMPTL;
  669. if ((*f)(sx,&sy,&n) < 0) /* If evaluation failed */
  670.   sx = cmprxx; /* revert to unevaluated copy */
  671. else if (!*cmprom) /* ditto if it came up empty */
  672.   sx = cmprxx;
  673. else
  674.   sx = cmprom;
  675.     } else
  676.       ckstrncpy(cmprom,sx,PROMPTL);
  677.     cmprom[PROMPTL-1] = NUL;
  678.     if (!*sx) /* Don't print if empty */
  679.       return;
  680. #ifdef OSK
  681.     fputs(sx, stdout);
  682. #else
  683. #ifdef MAC
  684.     printf("%s", sx);
  685. #else
  686.     printf("r%s",sx); /* Print the prompt. */
  687. #ifdef CK_SSL
  688.     if (!(ssl_active_flag || tls_active_flag))
  689. #endif /* CK_SSL */
  690.       fflush(stdout); /* Now! */
  691. #endif /* MAC */
  692. #endif /* OSK */
  693. }
  694. #ifndef NOSPL
  695. VOID
  696. pushcmd(s) char * s; { /* For use with IF command. */
  697.     if (!s) s = np;
  698.     ckstrncpy(savbuf,s,CMDBL); /* Save the dependent clause,  */
  699.     cmres(); /* and clear the command buffer. */
  700.     debug(F110, "pushcmd savbuf", savbuf, 0);
  701. }
  702. VOID
  703. pushqcmd(s) char * s; { /* For use with ELSE command. */
  704.     char c, * p = savbuf; /* Dest */
  705.     if (!s) s = np; /* Source */
  706.     while (*s) { /* Get first nonwhitespace char */
  707. if (*s != SP)
  708.   break;
  709. else
  710.   s++;
  711.     }
  712.     if (*s != '{') { /* If it's not "{" */
  713. pushcmd(s); /* do regular pushcmd */
  714. return;
  715.     }
  716.     while (c = *s++) { /* Otherwise insert quotes */
  717. if (c == CMDQ)
  718.   *p++ = CMDQ;
  719. *p++ = c;
  720.     }
  721.     cmres(); /* and clear the command buffer. */
  722.     debug(F110, "pushqcmd savbuf", savbuf, 0);
  723. }
  724. #endif /* NOSPL */
  725. #ifdef COMMENT
  726. /* no longer used... */
  727. VOID
  728. popcmd() {
  729.     ckstrncpy(cmdbuf,savbuf,CMDBL); /* Put back the saved material */
  730.     *savbuf = ''; /* and clear the save buffer */
  731.     cmres();
  732. }
  733. #endif /* COMMENT */
  734. /*  C M R E S  --  Reset pointers to beginning of command buffer.  */
  735. VOID
  736. cmres() {
  737.     inword = 0; /* We're not in a word */
  738.     cc = 0; /* Character count is zero */
  739. /* Initialize pointers */
  740.     pp = cmdbuf; /* Beginning of current field */
  741.     bp = cmdbuf; /* Current position within buffer */
  742.     np = cmdbuf; /* Where to start next field */
  743.     cmfldflgs = 0;
  744.     cmflgs = -5;                        /* Parse not yet started. */
  745.     ungw = 0; /* Don't need to unget a word. */
  746. }
  747. /*  C M I N I  --  Clear the command and atom buffers, reset pointers.  */
  748. /*
  749. The argument specifies who is to echo the user's typein --
  750.   1 means the cmd package echoes
  751.   0 somebody else (system, front end, terminal) echoes
  752. */
  753. VOID
  754. cmini(d) int d; {
  755. #ifdef DCMDBUF
  756.     if (!atmbuf)
  757.       if (cmsetup()<0)
  758. fatal("fatal error: unable to allocate command buffers");
  759. #endif /* DCMDBUF */
  760. #ifdef USE_MEMCPY
  761.     memset(cmdbuf,0,CMDBL);
  762.     memset(atmbuf,0,ATMBL);
  763. #else
  764.     for (bp = cmdbuf; bp < cmdbuf+CMDBL; bp++) *bp = NUL;
  765.     for (bp = atmbuf; bp < atmbuf+ATMBL; bp++) *bp = NUL;
  766. #endif /* USE_MEMCPY */
  767.     *atmbuf = *savbuf = *atxbuf = *atybuf = *filbuf = NUL;
  768. #ifdef OLDHELP
  769.     *hlpbuf = NUL;
  770. #endif /* OLDHELP */
  771.     blocklvl = 0; /* Block level is 0 */
  772.     linebegin = 1; /* And we're at the beginning of a line */
  773.     dpx = d; /* Make a global copy of the echo flag */
  774.     debug(F101,"cmini dpx","",dpx);
  775.     crflag = 0;
  776.     qmflag = 0;
  777.     esflag = 0;
  778. #ifdef CK_RECALL
  779.     no_recall = 0;
  780. #endif /* CK_RECALL */
  781.     cmres();
  782. }
  783. #ifndef NOSPL
  784. /* The following bits are to allow the command package to call itself */
  785. /* in the middle of a parse.  To do this, begin by calling cmpush, and */
  786. /* end by calling cmpop. */
  787. #ifdef DCMDBUF
  788. struct cmp {
  789.     int i[5]; /* stack for integers */
  790.     char *c[3]; /* stack for pointers */
  791.     char *b[8]; /* stack for buffer contents */
  792. };
  793. struct cmp *cmp = 0;
  794. #else
  795. int cmp_i[CMDDEP+1][5]; /* Stack for integers */
  796. char *cmp_c[CMDDEP+1][5]; /* for misc pointers */
  797. char *cmp_b[CMDDEP+1][7]; /* for buffer contents pointers */
  798. #endif /* DCMDBUF */
  799. int cmddep = -1; /* Current stack depth */
  800. int
  801. cmpush() { /* Save the command environment */
  802.     char *cp; /* Character pointer */
  803.     if (cmddep >= CMDDEP) /* Enter a new command depth */
  804.       return(-1);
  805.     cmddep++;
  806.     debug(F101,"&cmpush","",cmddep);
  807. #ifdef DCMDBUF
  808.     /* allocate memory for cmp if not already done */
  809.     if (!cmp && !(cmp = (struct cmp *) malloc(sizeof(struct cmp)*(CMDDEP+1))))
  810.       fatal("cmpush: no memory for cmp");
  811.     cmp[cmddep].i[0] = cmflgs; /* First do the global ints */
  812.     cmp[cmddep].i[1] = cmfsav;
  813.     cmp[cmddep].i[2] = atxn;
  814.     cmp[cmddep].i[3] = ungw;
  815.     cmp[cmddep].c[0] = bp; /* Then the global pointers */
  816.     cmp[cmddep].c[1] = pp;
  817.     cmp[cmddep].c[2] = np;
  818. #else
  819.     cmp_i[cmddep][0] = cmflgs; /* First do the global ints */
  820.     cmp_i[cmddep][1] = cmfsav;
  821.     cmp_i[cmddep][2] = atxn;
  822.     cmp_i[cmddep][3] = ungw;
  823.     cmp_c[cmddep][0] = bp; /* Then the global pointers */
  824.     cmp_c[cmddep][1] = pp;
  825.     cmp_c[cmddep][2] = np;
  826. #endif /* DCMDBUF */
  827.     /* Now the buffers themselves.  A lot of repititious code... */
  828. #ifdef DCMDBUF
  829.     cp = malloc((int)strlen(cmdbuf)+1); /* 0: Command buffer */
  830.     if (cp) strcpy(cp,cmdbuf);
  831.     cmp[cmddep].b[0] = cp;
  832.     if (cp == NULL) return(-1);
  833.     cp = malloc((int)strlen(savbuf)+1); /* 1: Save buffer */
  834.     if (cp) strcpy(cp,savbuf);
  835.     cmp[cmddep].b[1] = cp;
  836.     if (cp == NULL) return(-1);
  837. #ifdef OLDHELP
  838.     cp = malloc((int)strlen(hlpbuf)+1); /* 2: Help string buffer */
  839.     if (cp) strcpy(cp,hlpbuf);
  840.     cmp[cmddep].b[2] = cp;
  841.     if (cp == NULL) return(-1);
  842. #else
  843.     cmp[cmddep].b[2] = NULL;
  844. #endif /* OLDHELP */
  845.     cp = malloc((int)strlen(atmbuf)+1); /* 3: Atom buffer */
  846.     if (cp) strcpy(cp,atmbuf);
  847.     cmp[cmddep].b[3] = cp;
  848.     if (cp == NULL) return(-1);
  849.     cp = malloc((int)strlen(atxbuf)+1); /* 4: Expansion buffer */
  850.     if (cp) strcpy(cp,atxbuf);
  851.     cmp[cmddep].b[4] = cp;
  852.     if (cp == NULL) return(-1);
  853.     cp = malloc((int)strlen(atybuf)+1); /* 5: Atom buffer copy */
  854.     if (cp) strcpy(cp,atybuf);
  855.     cmp[cmddep].b[5] = cp;
  856.     if (cp == NULL) return(-1);
  857.     cp = malloc((int)strlen(filbuf)+1); /* 6: File name buffer */
  858.     if (cp) strcpy(cp,filbuf);
  859.     cmp[cmddep].b[6] = cp;
  860.     if (cp == NULL) return(-1);
  861. #else
  862.     cp = malloc((int)strlen(cmdbuf)+1); /* 0: Command buffer */
  863.     if (cp) strcpy(cp,cmdbuf);
  864.     cmp_b[cmddep][0] = cp;
  865.     if (cp == NULL) return(-1);
  866.     cp = malloc((int)strlen(savbuf)+1); /* 1: Save buffer */
  867.     if (cp) strcpy(cp,savbuf);
  868.     cmp_b[cmddep][1] = cp;
  869.     if (cp == NULL) return(-1);
  870. #ifdef OLDHELP
  871.     cp = malloc((int)strlen(hlpbuf)+1); /* 2: Help string buffer */
  872.     if (cp) strcpy(cp,hlpbuf);
  873.     cmp_b[cmddep][2] = cp;
  874.     if (cp == NULL) return(-1);
  875. #else
  876.     cmp_b[cmddep][2] = NULL;
  877. #endif /* OLDHELP */
  878.     cp = malloc((int)strlen(atmbuf)+1); /* 3: Atom buffer */
  879.     if (cp) strcpy(cp,atmbuf);
  880.     cmp_b[cmddep][3] = cp;
  881.     if (cp == NULL) return(-1);
  882.     cp = malloc((int)strlen(atxbuf)+1); /* 4: Expansion buffer */
  883.     if (cp) strcpy(cp,atxbuf);
  884.     cmp_b[cmddep][4] = cp;
  885.     if (cp == NULL) return(-1);
  886.     cp = malloc((int)strlen(atybuf)+1); /* 5: Atom buffer copy */
  887.     if (cp) strcpy(cp,atybuf);
  888.     cmp_b[cmddep][5] = cp;
  889.     if (cp == NULL) return(-1);
  890.     cp = malloc((int)strlen(filbuf)+1); /* 6: File name buffer */
  891.     if (cp) strcpy(cp,filbuf);
  892.     cmp_b[cmddep][6] = cp;
  893.     if (cp == NULL) return(-1);
  894. #endif /* DCMDBUF */
  895.     cmini(dpx); /* Initize the command parser */
  896.     return(0);
  897. }
  898. int
  899. cmpop() { /* Restore the command environment */
  900.     debug(F101,"&cmpop","",cmddep);
  901.     if (cmddep < 0) return(-1); /* Don't pop too much! */
  902. #ifdef DCMDBUF
  903.     cmflgs = cmp[cmddep].i[0]; /* First do the global ints */
  904.     cmfsav = cmp[cmddep].i[1];
  905.     atxn = cmp[cmddep].i[2];
  906.     ungw = cmp[cmddep].i[3];
  907.     bp = cmp[cmddep].c[0]; /* Then the global pointers */
  908.     pp = cmp[cmddep].c[1];
  909.     np = cmp[cmddep].c[2];
  910. #else
  911.     cmflgs = cmp_i[cmddep][0]; /* First do the global ints */
  912.     cmfsav = cmp_i[cmddep][1];
  913.     atxn = cmp_i[cmddep][2];
  914.     ungw = cmp_i[cmddep][3];
  915.     bp = cmp_c[cmddep][0]; /* Then the global pointers */
  916.     pp = cmp_c[cmddep][1];
  917.     np = cmp_c[cmddep][2];
  918. #endif /* DCMDBUF */
  919.     /* Now the buffers themselves. */
  920.     /* Note: strncpy(), not ckstrncpy() -- Here we WANT the NUL padding... */
  921. #ifdef DCMDBUF
  922.     if (cmp[cmddep].b[0]) {
  923. strncpy(cmdbuf,cmp[cmddep].b[0],CMDBL); /* 0: Command buffer */
  924. free(cmp[cmddep].b[0]);
  925. cmp[cmddep].b[0] = NULL;
  926.     }
  927.     if (cmp[cmddep].b[1]) {
  928. strncpy(savbuf,cmp[cmddep].b[1],CMDBL); /* 1: Save buffer */
  929. free(cmp[cmddep].b[1]);
  930. cmp[cmddep].b[1] = NULL;
  931.     }
  932. #ifdef OLDHELP
  933.     if (cmp[cmddep].b[2]) {
  934. strncpy(hlpbuf,cmp[cmddep].b[2],HLPBL); /* 2: Help buffer */
  935. free(cmp[cmddep].b[2]);
  936. cmp[cmddep].b[2] = NULL;
  937.     }
  938. #endif /* OLDHELP */
  939.     if (cmp[cmddep].b[3]) {
  940. strncpy(atmbuf,cmp[cmddep].b[3],ATMBL); /* 3: Atomic buffer! */
  941. free(cmp[cmddep].b[3]);
  942. cmp[cmddep].b[3] = NULL;
  943.     }
  944.     if (cmp[cmddep].b[4]) {
  945. strncpy(atxbuf,cmp[cmddep].b[4],ATMBL); /* 4: eXpansion buffer */
  946. free(cmp[cmddep].b[4]);
  947. cmp[cmddep].b[4] = NULL;
  948.     }
  949.     if (cmp[cmddep].b[5]) {
  950. strncpy(atybuf,cmp[cmddep].b[5],ATMBL); /* 5: Atom buffer copY */
  951. free(cmp[cmddep].b[5]);
  952. cmp[cmddep].b[5] = NULL;
  953.     }
  954.     if (cmp[cmddep].b[6]) {
  955. strncpy(filbuf,cmp[cmddep].b[6],ATMBL); /* 6: Filename buffer */
  956. free(cmp[cmddep].b[6]);
  957. cmp[cmddep].b[6] = NULL;
  958.     }
  959. #else
  960.     if (cmp_b[cmddep][0]) {
  961. strncpy(cmdbuf,cmp_b[cmddep][0],CMDBL); /* 0: Command buffer */
  962. free(cmp_b[cmddep][0]);
  963. cmp_b[cmddep][0] = NULL;
  964.     }
  965.     if (cmp_b[cmddep][1]) {
  966. strncpy(savbuf,cmp_b[cmddep][1],CMDBL); /* 1: Save buffer */
  967. free(cmp_b[cmddep][1]);
  968. cmp_b[cmddep][1] = NULL;
  969.     }
  970. #ifdef OLDHELP
  971.     if (cmp_b[cmddep][2]) {
  972. strncpy(hlpbuf,cmp_b[cmddep][2],HLPBL); /* 2: Help buffer */
  973. free(cmp_b[cmddep][2]);
  974. cmp_b[cmddep][2] = NULL;
  975.     }
  976. #endif /* OLDHELP */
  977.     if (cmp_b[cmddep][3]) {
  978. strncpy(atmbuf,cmp_b[cmddep][3],ATMBL); /* 3: Atomic buffer! */
  979. free(cmp_b[cmddep][3]);
  980. cmp_b[cmddep][3] = NULL;
  981.     }
  982.     if (cmp_b[cmddep][4]) {
  983. strncpy(atxbuf,cmp_b[cmddep][4],ATMBL); /* 4: eXpansion buffer */
  984. free(cmp_b[cmddep][4]);
  985. cmp_b[cmddep][4] = NULL;
  986.     }
  987.     if (cmp_b[cmddep][5]) {
  988. strncpy(atybuf,cmp_b[cmddep][5],ATMBL); /* 5: Atom buffer copY */
  989. free(cmp_b[cmddep][5]);
  990. cmp_b[cmddep][5] = NULL;
  991.     }
  992.     if (cmp_b[cmddep][6]) {
  993. strncpy(filbuf,cmp_b[cmddep][6],ATMBL); /* 6: Filename buffer */
  994. free(cmp_b[cmddep][6]);
  995. cmp_b[cmddep][6] = NULL;
  996.     }
  997. #endif /* DCMDBUF */
  998.     cmddep--; /* Rise, rise */
  999.     debug(F101,"&cmpop","",cmddep);
  1000.     return(cmddep);
  1001. }
  1002. #endif /* NOSPL */
  1003. #ifdef COMMENT
  1004. VOID
  1005. stripq(s) char *s; {                    /* Function to strip '' quotes */
  1006.     char *t;
  1007.     while (*s) {
  1008.         if (*s == CMDQ) {
  1009.             for (t = s; *t != ''; t++) *t = *(t+1);
  1010.         }
  1011.         s++;
  1012.     }
  1013. }
  1014. #endif /* COMMENT */
  1015. /* Convert tabs to spaces, one for one */
  1016. VOID
  1017. untab(s) char *s; {
  1018.     while (*s) {
  1019. if (*s == HT) *s = SP;
  1020. s++;
  1021.     }
  1022. }
  1023. /*  C M N U M  --  Parse a number in the indicated radix  */
  1024. /*
  1025.  The only radix allowed in unquoted numbers is 10.
  1026.  Parses unquoted numeric strings in base 10.
  1027.  Parses backslash-quoted numbers in the radix indicated by the quote:
  1028.    nnn = dnnn = decimal, onnn = octal, xnn = Hexadecimal.
  1029.  If these fail, then if a preprocessing function is supplied, that is applied
  1030.  and then a second attempt is made to parse an unquoted decimal string.
  1031.  And if that fails, the preprocessed string is passed to an arithmetic
  1032.  expression evaluator.
  1033.  Returns:
  1034.    -3 if no input present when required,
  1035.    -2 if user typed an illegal number,
  1036.    -1 if reparse needed,
  1037.     0 otherwise, with argument n set to the number that was parsed
  1038. */
  1039. int
  1040. cmnum(xhlp,xdef,radix,n,f) char *xhlp, *xdef; int radix, *n; xx_strp f; {
  1041.     int x; char *s, *zp, *zq;
  1042.     char lbrace, rbrace;
  1043.     if (!xhlp) xhlp = "";
  1044.     if (!xdef) xdef = "";
  1045.     if (cmfldflgs & 1) {
  1046. lbrace = '(';
  1047. rbrace = ')';
  1048.     } else {
  1049. lbrace = '{';
  1050. rbrace = '}';
  1051.     }
  1052.     if (radix != 10) {                  /* Just do base 10 */
  1053.         printf("cmnum: illegal radix - %dn",radix);
  1054.         return(-1);
  1055.     } /* Easy to add others but there has never been a need for it. */
  1056.     x = cmfld(xhlp,xdef,&s,(xx_strp)0);
  1057.     debug(F101,"cmnum: cmfld","",x);
  1058.     if (x < 0) return(x); /* Parse a field */
  1059.     zp = atmbuf;
  1060. /*
  1061.   Edit 192 - Allow any number field to be braced.  This lets us include
  1062.   spaces in expressions, but perhaps more important lets us have user-defined
  1063.   functions in numeric fields.
  1064. */
  1065.     if (*zp == lbrace) { /* Braced field, strip braces */
  1066. x = (int) strlen(atmbuf);
  1067. if (x > 0) { /* The "if" is to shut up optimizers */
  1068.     if (*(atmbuf+x-1) == rbrace) {
  1069. *(atmbuf+x-1) = NUL; /* that complain about a possible */
  1070. zp++; /* reference to atbmbuf[-1] even */
  1071.     }
  1072. } /* though we know that x > 0. */
  1073.     }
  1074.     if (chknum(zp)) { /* Check for decimal number */
  1075.         *n = atoi(zp); /* Got one, we're done. */
  1076. debug(F101,"cmnum 1st chknum ok","",*n);
  1077.         return(0);
  1078.     } else if ((x = xxesc(&zp)) > -1) { /* Check for backslash escape */
  1079. #ifndef OS2
  1080. *n = x;
  1081. #else
  1082. *n = wideresult;
  1083. #endif /* OS2 */
  1084. debug(F101,"cmnum xxesc ok","",*n);
  1085. return(*zp ? -2 : 0);
  1086.     } else if (f) { /* If conversion function given */
  1087. zq = atxbuf; /* Try that */
  1088. atxn = CMDBL;
  1089. if ((*f)(zp,&zq,&atxn) < 0) /* Convert */
  1090.   return(-2);
  1091. zp = atxbuf;
  1092.     }
  1093.     debug(F110,"cmnum zp 1",zp,0);
  1094.     if (!*zp) zp = xdef; /* Result empty, substitute default */
  1095.     debug(F110,"cmnum zp 2",zp,0);
  1096.     if (chknum(zp)) { /* Check again for decimal number */
  1097.         *n = atoi(zp); /* Got one, we're done. */
  1098. debug(F101,"cmnum 2nd chknum ok","",*n);
  1099.         return(0);
  1100. #ifndef NOSPL
  1101.     }  else if ((x = xxesc(&zp)) > -1) { /* Check for backslash escape */
  1102. #ifndef OS2
  1103. *n = x;
  1104. #else
  1105. *n = wideresult;
  1106. #endif /* OS2 */
  1107. debug(F101,"cmnum xxesc 2 ok","",*n);
  1108. return(*zp ? -2 : 0);
  1109.     } else if (f) { /* Not numeric, maybe an expression */
  1110. char * p;
  1111. p = evala(zp);
  1112. if (chknum(p)) {
  1113.     *n = atoi(p);
  1114.     debug(F101,"cmnum exp eval ok","",*n);
  1115.     return(0);
  1116. } else return(-2);
  1117. #endif /* NOSPL */
  1118.     } else { /* Not numeric */
  1119. return(-2);
  1120.     }
  1121. }
  1122. #ifdef CKCHANNELIO
  1123. extern int z_error;
  1124. #endif /* CKCHANNELIO */
  1125. /*  C M O F I  --  Parse the name of an output file  */
  1126. /*
  1127.  Depends on the external function zchko(); if zchko() not available, use
  1128.  cmfld() to parse output file names.
  1129.  Returns:
  1130.    -9 like -2, except message already printed,
  1131.    -3 if no input present when required,
  1132.    -2 if permission would be denied to create the file,
  1133.    -1 if reparse needed,
  1134.     0 or 1 if file can be created, with xp pointing to name.
  1135.     2 if given the name of an existing directory.
  1136. */
  1137. int
  1138. cmofi(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; {
  1139.     int x; char *s, *zq;
  1140. #ifdef DOCHKVAR
  1141.     int tries;
  1142. #endif /* DOCHKVAR */
  1143. #ifdef DTILDE
  1144.     char *dirp;
  1145. #endif /* DTILDE */
  1146.     cmfldflgs = 0;
  1147.     if (!xhlp) xhlp = "";
  1148.     if (!xdef) xdef = "";
  1149.     if (*xhlp == NUL) xhlp = "Output file";
  1150.     *xp = "";
  1151.     x = cmfld(xhlp,xdef,&s,(xx_strp)0);
  1152.     debug(F111,"cmofi cmfld returns",s,x);
  1153.     if (x < 0)
  1154.       return(x);
  1155.     if (*s == '{') { /* Strip enclosing braces */
  1156. int n;
  1157. n = strlen(s);
  1158. if (s[n-1] == '}') {
  1159.     s[n-1] = NUL;
  1160.     s++;
  1161. }
  1162.     }
  1163.     debug(F110,"cmofi 1.5",s,0);
  1164. #ifdef DOCHKVAR
  1165.     tries = 0;
  1166.     {
  1167. char *p = s;
  1168.     /*
  1169.       This is really ugly.  If we skip conversion the first time through,
  1170.       then variable names like %a will be used as filenames (e.g. creating
  1171.       a file called %A in the root directory).  If we DON'T skip conversion
  1172.       the first time through, then single backslashes used as directory
  1173.       separators in filenames will be misinterpreted as variable lead-ins.
  1174.       So we prescan to see if it has any variable references.  But this
  1175.       module is not supposed to know anything about variables, functions,
  1176.       etc, so this code does not really belong here, but rather it should
  1177.       be at the same level as zzstring().
  1178.     */
  1179. /*
  1180.   Hmmm, this looks a lot like chkvar() except it that includes nnn number
  1181.   escapes.  But why?  This makes commands like "mkdir c:123" impossible.
  1182.   And in fact, "mkdir c:123" creates a directory called "c:{".  What's worse,
  1183.   rmdir(), which *does* call chkvar(), won't let us remove it.  So let's at
  1184.   least try making cmofi() symmetrical with cmifi()...
  1185. */
  1186. #ifdef COMMENT
  1187. char * q;
  1188. while ( (tries == 0) && (p = strchr(p,CMDQ)) ) {
  1189.     q = *(p+1); /* Char after backslash */
  1190.     if (!q) /* None, quit */
  1191.       break;
  1192.     if (isupper(q)) /* If letter, convert to lowercase */
  1193.       q = tolower(q);
  1194.     if (isdigit(q)) { /* If it's a digit, */
  1195. tries = 1; /* assume it's a backslash code  */
  1196. break;
  1197.     }
  1198.     switch (q) {
  1199.       case CMDQ: /* Double backslash */
  1200. tries = 1; /* so call the conversion function */
  1201. break;
  1202.       case '%': /* Variable or array reference */
  1203.       case '&': /* must be followed by letter */
  1204. if (isalpha(*(p+2)) || (*(p+2) >= '0' && *(p+2) <= '9'))
  1205.   tries = 1;
  1206. break;
  1207.       case 'm': case 'v': case '$': /* m(), v(), $() */
  1208. if (*(p+2) == '(')
  1209.   if (strchr(p+2,')'))
  1210.     tries = 1;
  1211. break;
  1212.       case 'f': /* Fname() */
  1213. if (strchr(p+2,'('))
  1214.   if (strchr(p+2,')'))
  1215.       tries = 1;
  1216. break;
  1217.       case '{': /* {...} */
  1218. if (strchr(p+2,'}'))
  1219.   tries = 1;
  1220. break;
  1221.       case 'd': case 'o': /* Decimal or Octal number */
  1222.         if (isdigit(*(p+2)))
  1223.   tries = 1;
  1224. break;
  1225.       case 'x': /* Hex number */
  1226. if (isdigit(*(p+2)) ||
  1227.     ((*(p+2) >= 'a' && *(p+2) <= 'f') ||
  1228.      ((*(p+2) >= 'A' && *(p+2) <= 'F'))))
  1229.   tries = 1;
  1230.       default:
  1231. break;
  1232.     }
  1233.     p++;
  1234. }
  1235. #else
  1236. #ifndef NOSPL
  1237. if (f) { /* If a conversion function is given */
  1238.     char *s = p; /* See if there are any variables in */
  1239.     while (*s) { /* the string and if so, expand them */
  1240. if (chkvar(s)) {
  1241.     tries = 1;
  1242.     break;
  1243. }
  1244. s++;
  1245.     }
  1246. }
  1247. #endif /* NOSPL */
  1248. #endif /* COMMENT */
  1249.     }
  1250. #ifdef OS2
  1251. o_again:
  1252. #endif /* OS2 */
  1253.     if (tries == 1)
  1254. #endif /* DOCHKVAR */
  1255.     if (f) { /* If a conversion function is given */
  1256. zq = atxbuf; /* do the conversion. */
  1257. atxn = CMDBL;
  1258. if ((x = (*f)(s,&zq,&atxn)) < 0)
  1259.   return(-2);
  1260. s = atxbuf;
  1261. if (!*s) /* Result empty, substitute default */
  1262.   s = xdef;
  1263.     }
  1264.     debug(F111,"cmofi 2",s,x);
  1265. #ifdef DTILDE
  1266.     dirp = tilde_expand(s); /* Expand tilde, if any, */
  1267.     if (*dirp != '') { /* right in the atom buffer. */
  1268. if (setatm(dirp,1) < 0) {
  1269.     printf("?Name too longn");
  1270.     return(-9);
  1271. }
  1272.     }
  1273.     s = atmbuf;
  1274.     debug(F110,"cmofi 3",s,0);
  1275. #endif /* DTILDE */
  1276.     if (iswild(s)) {
  1277.         printf("?Wildcards not allowed - %sn",s);
  1278.         return(-2);
  1279.     }
  1280.     debug(F110,"cmofi 4",s,0);
  1281. #ifdef CK_TMPDIR
  1282.     /* isdir() function required for this! */
  1283.     if (isdir(s)) {
  1284. debug(F110,"cmofi 5: is directory",s,0);
  1285.         *xp = s;
  1286. return(2);
  1287.     }
  1288. #endif /* CK_TMPDIR */
  1289.     if (strcmp(s,CTTNAM) && (zchko(s) < 0)) { /* OK to write to console */
  1290. #ifdef COMMENT
  1291. #ifdef OS2
  1292. /*
  1293.   We don't try again because we already prescanned the string to see if
  1294.   if it contained anything that could be used by zzstring().
  1295. */
  1296. if (tries++ < 1)
  1297.   goto o_again;
  1298. #endif /* OS2 */
  1299. #endif /* COMMENT */
  1300. /*
  1301.   Note: there are certain circumstances where zchko() can give a false
  1302.   positive, so don't rely on it to catch every conceivable situation in
  1303.   which the given output file can't be created.  In other words, we print
  1304.   a message and fail here if we KNOW the file can't be created.  If we
  1305.   succeed but the file can't be opened, the code that tries to open the file
  1306.   has to print a message.
  1307. */
  1308. debug(F110,"cmofi 6: failure",s,0);
  1309.         printf("?Write permission denied - %sn",s);
  1310. #ifdef CKCHANNELIO
  1311. z_error = FX_ACC;
  1312. #endif /* CKCHANNELIO */
  1313.         return(-9);
  1314.     } else {
  1315. debug(F110,"cmofi 7: ok",s,0);
  1316.         *xp = s;
  1317.         return(x);
  1318.     }
  1319. }
  1320. /*  C M I F I  --  Parse the name of an existing file  */
  1321. /*
  1322.  This function depends on the external functions:
  1323.    zchki()  - Check if input file exists and is readable.
  1324.    zxpand() - Expand a wild file specification into a list.
  1325.    znext()  - Return next file name from list.
  1326.  If these functions aren't available, then use cmfld() to parse filenames.
  1327. */
  1328. /*
  1329.  Returns
  1330.    -4 EOF
  1331.    -3 if no input present when required,
  1332.    -2 if file does not exist or is not readable,
  1333.    -1 if reparse needed,
  1334.     0 or 1 otherwise, with:
  1335.         xp pointing to name,
  1336.         wild = 1 if name contains '*' or '?', 0 otherwise.
  1337. */
  1338. /*
  1339.    C M I O F I  --  Parse an input file OR the name of a nonexistent file.
  1340.    Use this when an existing file is wanted (so we get help, completion, etc),
  1341.    but if a file of the given name does not exist, the name of a new file is
  1342.    accepted.  For example, with the EDIT command (edit an existing file, or
  1343.    create a new file).  Returns -9 if file does not exist.  It is up to the
  1344.    caller to check creatability.
  1345. */
  1346. static int nomsg = 0;
  1347. int
  1348. cmiofi(xhlp,xdef,xp,wild,f) char *xhlp, *xdef, **xp; int *wild; xx_strp f; {
  1349.     int msgsave, x;
  1350.     msgsave = nomsg;
  1351.     nomsg = 1;
  1352.     x = cmifi2(xhlp,xdef,xp,wild,0,NULL,f,0);
  1353.     nomsg = msgsave;
  1354.     return(x);
  1355. }
  1356. int
  1357. cmifi(xhlp,xdef,xp,wild,f) char *xhlp, *xdef, **xp; int *wild; xx_strp f; {
  1358.     return(cmifi2(xhlp,xdef,xp,wild,0,NULL,f,0));
  1359. }
  1360. /*
  1361.   cmifip() is called when we want to supply a path or path list to search
  1362.   in case the filename that the user gives is (a) not absolute, and (b) can't
  1363.   be found as given.  The path string can be the name of a single directory,
  1364.   or a list of directories separated by the PATHSEP character, defined in
  1365.   ckucmd.h.  Look in ckuusr.c and ckuus3.c for examples of usage.
  1366. */
  1367. int
  1368. cmifip(xhlp,xdef,xp,wild,d,path,f)
  1369.     char *xhlp,*xdef,**xp; int *wild, d; char * path; xx_strp f; {
  1370.     return(cmifi2(xhlp,xdef,xp,wild,0,path,f,0));
  1371. }
  1372. /*  C M D I R  --  Parse a directory name  */
  1373. /*
  1374.  This function depends on the external functions:
  1375.    isdir(s)  - Check if string s is the name of a directory
  1376.    zchki(s)  - Check if input file s exists and what type it is.
  1377.  If these functions aren't available, then use cmfld() to parse dir names.
  1378.  Returns
  1379.    -9 For all sorts of reasons, after printing appropriate error message.
  1380.    -4 EOF
  1381.    -3 if no input present when required,
  1382.    -2 if out of space or other internal error,
  1383.    -1 if reparse needed,
  1384.     0 or 1, with xp pointing to name, if directory specified,
  1385. */
  1386. int
  1387. cmdir(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; {
  1388.     int wild;
  1389.     return(cmifi2(xhlp,xdef,xp,&wild,0,NULL,f,1));
  1390. }
  1391. /* Like CMDIR but includes PATH search */
  1392. int
  1393. cmdirp(xhlp,xdef,xp,path,f) char *xhlp, *xdef, **xp; char * path; xx_strp f; {
  1394.     int wild;
  1395.     return(cmifi2(xhlp,xdef,xp,&wild,0,path,f,1));
  1396. }
  1397. /*
  1398.   cmifi2() is the base filename parser called by cmifi, cmifip, cmdir, etc.
  1399.   Use it directly when you also want to parse a directory or device
  1400.   name as an input file, as in the DIRECTORY command.  Call with:
  1401.     xhlp  -- help message on ?
  1402.     xdef  -- default response
  1403.     xp    -- pointer to result (in our space, must be copied from here)
  1404.     wild  -- flag set upon return to indicate if filespec was wild
  1405.     d     -- 0 to parse files, 1 to parse files or directories
  1406.     path  -- search path for files
  1407.     f     -- pointer to string processing function (e.g. to evaluate variables)
  1408.     dirflg -- 1 to parse *only* directories, 0 otherwise
  1409. */
  1410. int
  1411. cmifi2(xhlp,xdef,xp,wild,d,path,f,dirflg)
  1412.     char *xhlp,*xdef,**xp; int *wild, d; char * path; xx_strp f; int dirflg; {
  1413.     extern int recursive, diractive;
  1414.     int i, x, itsadir, xc, expanded = 0, nfiles = 0;
  1415.     long y;
  1416.     char *sp = NULL, *zq, *np = NULL;
  1417.     char *sv = NULL, *p = NULL;
  1418. #ifdef DTILDE
  1419.     char *dirp;
  1420. #endif /* DTILDE */
  1421. #ifndef NOPARTIAL
  1422. #ifndef OS2
  1423. #ifdef OSK
  1424.     /* This large array is dynamic for OS-9 -- should do for others too... */
  1425.     extern char **mtchs;
  1426. #else
  1427. #ifdef UNIX
  1428.     /* OK, for UNIX too */
  1429.     extern char **mtchs;
  1430. #else
  1431. #ifdef VMS
  1432.     extern char **mtchs;
  1433. #else
  1434.     extern char *mtchs[];
  1435. #endif /* VMS */
  1436. #endif /* UNIX */
  1437. #endif /* OSK */
  1438. #endif /* OS2 */
  1439. #endif /* NOPARTIAL */
  1440.     if (!xhlp) xhlp = "";
  1441.     if (!xdef) xdef = "";
  1442.     cmfldflgs = 0;
  1443.     if (path)
  1444.       if (!*path)
  1445. path = NULL;
  1446.     if (path) { /* Make a copy we can poke */
  1447. x = strlen(path);
  1448. np = (char *) malloc(x + 1);
  1449. if (np) {
  1450.     strcpy(np, path);
  1451.     path = sp = np;
  1452. }
  1453.     }
  1454.     debug(F110,"cmifi2 path",path,0);
  1455.     ckstrncpy(cmdefault,xdef,CMDEFAULT); /* Copy default */
  1456.     xdef = cmdefault;
  1457.     inword = 0; /* Initialize counts & pointers */
  1458.     cc = 0;
  1459.     xc = 0;
  1460.     *xp = ""; /* Pointer to result string */
  1461.     if ((x = cmflgs) != 1) {            /* Already confirmed? */
  1462. #ifdef BS_DIRSEP
  1463. dirnamflg = 1;
  1464.         x = gtword(0); /* No, get a word */
  1465. dirnamflg = 0;
  1466. #else
  1467.         x = gtword(0);                  /* No, get a word */
  1468. #endif /* BS_DIRSEP */
  1469.     } else { /* If so, use default, if any. */
  1470.         if (setatm(xdef,1) < 0) {
  1471.     printf("?Default name too longn");
  1472.     if (np) free(np);
  1473.     return(-9);
  1474. }
  1475.     }
  1476.   i_path:
  1477.     *xp = atmbuf;                       /* Point to result. */
  1478.     while (1) {
  1479.         xc += cc;                       /* Count this character. */
  1480.         debug(F111,"cmifi gtword",atmbuf,xc);
  1481. debug(F101,"cmifi switch x","",x);
  1482.         switch (x) { /* x = gtword() return code */
  1483.   case -10:
  1484.     if (gtimer() > timelimit) {
  1485. #ifdef IKSD
  1486.                 extern int inserver;
  1487.                 if (inserver) {
  1488.                     printf("rnIKSD IDLE TIMEOUT: %d secrn", timelimit);
  1489.                     doexit(GOOD_EXIT,0);
  1490.                 }
  1491. #endif /* IKSD */
  1492. if (!quiet) printf("?Timed outn");
  1493. return(-10);
  1494.     } else {
  1495. x = gtword(0);
  1496. continue;
  1497.     }
  1498.   case -9:
  1499.     printf("Command or field too longn");
  1500.   case -4: /* EOF */
  1501.   case -2: /* Out of space. */
  1502.   case -1: /* Reparse needed */
  1503.     if (np) free(np);
  1504.     return(x);
  1505.   case 0: /* SP or NL */
  1506.   case 1:
  1507.     if (xc == 0) /* If no input... */
  1508.       *xp = xdef; /* substitute the default */
  1509.     if (**xp == NUL) { /* If field still empty return -3. */
  1510. if (np) free(np);
  1511. return(-3);
  1512.     }
  1513.     *xp = brstrip(*xp); /* Strip braces */
  1514.     debug(F110,"cmifi brstrip",*xp,0);
  1515. #ifndef NOSPL
  1516.     if (f) { /* If a conversion function is given */
  1517. #ifdef DOCHKVAR
  1518. char *s = *xp; /* See if there are any variables in */
  1519. int x;
  1520. while (*s) { /* the string and if so, expand them */
  1521.     if (chkvar(s)) {
  1522. #endif /* DOCHKVAR */
  1523. zq = atxbuf;
  1524. atxn = CMDBL;
  1525. if ((*f)(*xp,&zq,&atxn) < 0) {
  1526.     if (np) free(np);
  1527.     return(-2);
  1528. }
  1529. *xp = atxbuf;
  1530. if (!atxbuf[0])
  1531.   *xp = xdef;
  1532. #ifdef DOCHKVAR
  1533. break;
  1534.     }
  1535.     s++;
  1536. }
  1537. #endif /* DOCHKVAR */
  1538.     }
  1539. #endif /* NOSPL */
  1540. #ifdef DTILDE
  1541.     dirp = tilde_expand(*xp); /* Expand tilde, if any, */
  1542.     if (*dirp != '') { /* in the atom buffer. */
  1543. if (setatm(dirp,1) < 0) {
  1544.     printf("Expanded name too longn");
  1545.     if (np) free(np);
  1546.     return(-9);
  1547. }
  1548.     }
  1549.     *xp = atmbuf;
  1550. #endif /* DTILDE */
  1551.     debug(F110,"cmifi tilde_expand",*xp,0);
  1552.     if (!sv) { /* Only do this once */
  1553. sv = malloc((int)strlen(*xp)+1); /* Make a safe copy */
  1554. if (!sv) {
  1555.     printf("?cmifi: malloc errorn");
  1556.     if (np) free(np);
  1557.     return(-9);
  1558. }
  1559. strcpy(sv,*xp);
  1560. debug(F110,"cmifi sv",sv,0);
  1561.     }
  1562. /* This is to get around "cd /" failing because "too many directories match" */
  1563.     expanded = 0; /* Didn't call zxpand */
  1564. #ifdef datageneral
  1565.     debug(F110,"cmifi isdir 1",*xp,0);
  1566.     {
  1567. int y; char *s;
  1568. s = *xp;
  1569. y = strlen(s);
  1570. if (y > 1 &&
  1571.     (s[y-1] == ':' ||
  1572.      s[y-1] == '^' ||
  1573.      s[y-1] == '=')
  1574.     )
  1575.   s[y-1] = NUL;
  1576.     }
  1577.     debug(F110,"cmifi isdir 2",*xp,0);
  1578. #endif /*  datageneral */
  1579. #ifdef VMS
  1580.     if (dirflg) {
  1581. if (!strcmp(*xp,"..")) { /* For UNIXers... */
  1582.     setatm("-",0);
  1583.     *xp = atmbuf;
  1584. } else if (!strcmp(*xp,".")) {
  1585.     setatm("[]",0);
  1586.     *xp = atmbuf;
  1587. }
  1588.     }
  1589. #endif /* VMS */
  1590.     itsadir = isdir(*xp); /* Is it a directory? */
  1591.     debug(F111,"cmifi itsadir",*xp,itsadir);
  1592. #ifdef VMS
  1593.     /* If they said "blah" where "blah.dir" is a directory... */
  1594.     /* change it to [.blah]. */
  1595.     if (!itsadir) {
  1596. char tmpbuf[600];
  1597. int flag = 0; char c, * p;
  1598. p = *xp;
  1599. while ((c = *p++) && !flag)
  1600.   if (ckstrchr(".[]:*?<>",c))
  1601.     flag = 1;
  1602. debug(F111,"cmifi VMS dirname flag",*xp,flag);
  1603. if (!flag) {
  1604.     sprintf(tmpbuf,"[.%s]",*xp);
  1605.     itsadir = isdir(tmpbuf);
  1606.     if (itsadir) {
  1607. setatm(tmpbuf,0);
  1608. *xp = atmbuf;
  1609.     }
  1610.     debug(F111,"cmifi VMS dirname flag itsadir",*xp,itsadir);
  1611. }
  1612.     } else if (itsadir == 1 && *(xp[0]) == '.' && *(xp[1])) {
  1613. char *p;
  1614. if (p = malloc(cc + 4)) {
  1615.     sprintf(p,"[%s]",*xp);
  1616.     setatm(p,0);
  1617.     *xp = atmbuf;
  1618.     debug(F110,"cmdir .foo",*xp,0);
  1619.     free(p);
  1620. }
  1621.     } else if (itsadir == 2 && !diractive) {
  1622. int x; /* [FOO]BAR.DIR instead of [FOO.BAR] */
  1623. char *p;
  1624. p = malloc(cc + 4);
  1625. if (p) {
  1626.     x = cvtdir(*xp,p); /* Convert to [FOO.BAR] */
  1627.     if (x > 0) {
  1628. setatm(p,0);
  1629. *xp = atmbuf;
  1630. debug(F110,"cmdir cvtdir",*xp,0);
  1631.     }
  1632.     free(p);
  1633. }
  1634.     }
  1635. #endif /* VMS */
  1636.     if (dirflg) { /* Parsing a directory name? */
  1637. /* Yes, does it contain wildcards? */
  1638. if (iswild(*xp) ||
  1639.     diractive && (!strcmp(*xp,".")  || !strcmp(*xp,".."))
  1640.     ) {
  1641.     nzxopts = ZX_DIRONLY; /* Match only directory names */
  1642.     if (matchdot)  nzxopts |= ZX_MATCHDOT;
  1643.     if (recursive) nzxopts |= ZX_RECURSE;
  1644.     y = nzxpand(*xp,nzxopts);
  1645.     nfiles = y;
  1646.     expanded = 1;
  1647. } else {
  1648. #ifdef VMS
  1649. /*
  1650.   This is to allow (e.g.) "cd foo", where FOO.DIR;1 is in the
  1651.   current directory.
  1652. */
  1653.     debug(F111,"cmdir itsadir",*xp,itsadir);
  1654.     if (!itsadir) {
  1655. char *s;
  1656. int n;
  1657. s = *xp;
  1658. n = strlen(s);
  1659. if (n > 0 &&
  1660. #ifdef COMMENT
  1661.     *s != '[' && s[n-1] != ']' &&
  1662.     *s != '<' && s[n-1] != '>' &&
  1663. #else
  1664.     ckindex("[",s,0,0,1) == 0 &&
  1665.     ckindex("<",s,0,0,1) == 0 &&
  1666. #endif /* COMMENT */
  1667.     s[n-1] != ':') {
  1668.     char * dirbuf = NULL;
  1669.     dirbuf = (char *)malloc(n+4);
  1670.     if (dirbuf) {
  1671. if (*s == '.')
  1672.   sprintf(dirbuf,"[%s]",s);
  1673. else
  1674.   sprintf(dirbuf,"[.%s]",s);
  1675. itsadir = isdir(dirbuf);
  1676. debug(F111,"cmdir dirbuf",dirbuf,itsadir);
  1677. if (itsadir) {
  1678.     setatm(dirbuf,0);
  1679.     *xp = atmbuf;
  1680.     debug(F110,"cmdir new *xp",*xp,0);
  1681. }
  1682. free(dirbuf);
  1683.     }
  1684. /* This is to allow CDPATH to work in VMS... */
  1685. } else if (n > 0) {
  1686.     char * p; int i, j, k, d;
  1687.     char rb[2] = "]";
  1688.     if (p = malloc(x + 8)) {
  1689. strcpy(p,*xp);
  1690. i = ckindex(".",p,-1,1,1);
  1691. d = ckindex(".dir",p,0,0,0);
  1692. j = ckindex("]",p,-1,1,1);
  1693. if (j == 0) {
  1694.     j = ckindex(">",p,-1,1,1);
  1695.     rb[0] = '>';
  1696. }
  1697. k = ckindex(":",p,-1,1,1);
  1698. if (i < j || i < k) i = 0;
  1699. if (d < j || d < k) d = 0;
  1700. /* Change [FOO]BAR or [FOO]BAR.DIR */
  1701. /* to [FOO.BAR] */
  1702. if (j > 0 && j < n) {
  1703.     p[j-1] = '.';
  1704.     if (d > 0) p[d-1] = NUL;
  1705.     strcat(p,rb);
  1706.     debug(F110,"cmdir xxx",p,0);
  1707. }
  1708. itsadir = isdir(p);
  1709. debug(F111,"cmdir p",p,itsadir);
  1710. if (itsadir) {
  1711.     setatm(p,0);
  1712.     *xp = atmbuf;
  1713.     debug(F110,"cmdir new *xp",*xp,0);
  1714. }
  1715. free(p);
  1716.     }
  1717. }
  1718.     }
  1719. #endif /* VMS */
  1720.     y = (!itsadir) ? 0 : 1;
  1721.     debug(F111,"cmifi y itsadir",*xp,y);
  1722. }
  1723.     } else { /* Parsing a filename. */
  1724. debug(F110,"cmifi *xp pre-zxpand",*xp,0);
  1725. #ifndef COMMENT
  1726. nzxopts = (d == 0) ? ZX_FILONLY : 0; /* So always expand. */
  1727. if (matchdot)  nzxopts |= ZX_MATCHDOT;
  1728. if (recursive) nzxopts |= ZX_RECURSE;
  1729. y = nzxpand(*xp,nzxopts);
  1730. #else
  1731. /* Here we're trying to fix a problem in which a directory name is accepted */
  1732. /* as a filename, but this breaks too many other things. */
  1733. nzxopts = 0;
  1734. if (!d) {
  1735.     if (itsadir & !iswild(*xp)) {
  1736. debug(F100,"cmifi dir when filonly","",0);
  1737. printf("?Not a regular file: "%s"n",*xp);
  1738. if (sv) free(sv);
  1739. if (np) free(np);
  1740. return(-9);
  1741.     } else {
  1742. nzxopts = ZX_FILONLY;
  1743. if (matchdot)  nzxopts |= ZX_MATCHDOT;
  1744. if (recursive) nzxopts |= ZX_RECURSE;
  1745. y = nzxpand(*xp,nzxopts);
  1746.     }
  1747. }
  1748. #endif /* COMMENT */
  1749. nfiles = y;
  1750. debug(F111,"cmifi y nzxpand",*xp,y);
  1751. debug(F111,"cmifi y atmbuf",atmbuf,itsadir);
  1752. expanded = 1;
  1753.     }
  1754.     /* domydir() calls zxrewind() so we MUST call nzxpand() here */
  1755.     if (!expanded && diractive) {
  1756. debug(F110,"cmifi diractive catch-all zxpand",*xp,0);
  1757. nzxopts = (d == 0) ? ZX_FILONLY : (dirflg ? ZX_DIRONLY : 0);
  1758. if (matchdot)  nzxopts |= ZX_MATCHDOT;
  1759. if (recursive) nzxopts |= ZX_RECURSE;
  1760. y = nzxpand(*xp,nzxopts);
  1761. nfiles = y;
  1762. expanded = 1;
  1763.     }
  1764.     *wild = (iswild(sv) || (y > 1)) && (itsadir == 0);
  1765. #ifdef RECURSIVE
  1766.     if (!*wild) *wild = recursive;
  1767. #endif /* RECURSIVE */
  1768.     debug(F111,"cmifi sv wild",sv,*wild);
  1769.     if (dirflg && *wild && !diractive) {
  1770. printf("?Wildcard matches more than one directoryn");
  1771. if (sv) free(sv);
  1772. if (np) free(np);
  1773. return(-9);
  1774.     }
  1775.     if (itsadir && d && !dirflg) { /* It's a directory and not wild */
  1776. if (sv) free(sv); /* and it's ok to parse directories */
  1777. if (np) free(np);
  1778. return(x);
  1779.     }
  1780.     if (y == 0) {
  1781. if (path && !isabsolute(sv)) {
  1782.     char * ptr = path;
  1783.     char c;
  1784.     while (1) {
  1785. c = *ptr;
  1786. if (c == PATHSEP || c == NUL) {
  1787.     if (!*path) {
  1788. path = NULL;
  1789. break;
  1790.     }
  1791.     *ptr = NUL;
  1792. #ifdef UNIX
  1793. /* By definition of CDPATH, an empty member denotes the current directory */
  1794.     if (!*path)
  1795.       strcpy(atmbuf,".");
  1796.     else
  1797. #endif /* UNIX */
  1798.       strncpy(atmbuf,path,ATMBL);
  1799. #ifdef VMS
  1800.     atmbuf[ATMBL] = NUL;
  1801. /* If we have a logical name, evaluate it recursively */
  1802.     if (*(ptr-1) == ':') { /* Logical name ends in : */
  1803. char *p; int n;
  1804. while (((n = strlen(atmbuf))  > 0) &&
  1805.        atmbuf[n-1] == ':') {
  1806.     atmbuf[n-1] = NUL;
  1807.     for (p = atmbuf; *p; p++)
  1808.       if (islower(*p)) *p = toupper(*p);
  1809.     debug(F111,"cmdir CDPATH LN 1",atmbuf,n);
  1810.     p = getenv(atmbuf);
  1811.     debug(F110,"cmdir CDPATH LN 2",p,0);
  1812.     if (!p)
  1813.       break;
  1814.     strncpy(atmbuf,p,ATMBL);
  1815.     atmbuf[ATMBL] = NUL;
  1816. }
  1817.     }
  1818. #else
  1819. #ifdef OS2
  1820.     if (*(ptr-1) != '\' && *(ptr-1) != '/')
  1821.       strcat(atmbuf,"\");
  1822. #else
  1823. #ifdef UNIX
  1824.     if (*(ptr-1) != '/')
  1825.       strcat(atmbuf,"/");
  1826. #else
  1827. #ifdef datageneral
  1828.     if (*(ptr-1) != ':')
  1829.       strcat(atmbuf,":");
  1830. #endif /* datageneral */
  1831. #endif /* UNIX */
  1832. #endif /* OS2 */
  1833. #endif /* VMS */
  1834.     strcat(atmbuf,sv);
  1835.     debug(F110,"cmifip add path",atmbuf,0);
  1836.     if (c == PATHSEP) ptr++;
  1837.     path = ptr;
  1838.     break;
  1839. }
  1840. ptr++;
  1841.     }
  1842.     x = 1;
  1843.     inword = 0;
  1844.     cc = 0;
  1845.     xc = (int) strlen(atmbuf);
  1846.     *xp = "";
  1847.     goto i_path;
  1848. }
  1849. if (d) {
  1850.     if (sv) free(sv);
  1851.     if (np) free(np);
  1852.     return(-2);
  1853. } else {
  1854.     if (!nomsg)
  1855.       printf("?No %s match - %sn",
  1856.      dirflg ? "directories" : "files", sv);
  1857.     if (sv) free(sv);
  1858.     if (np) free(np);
  1859.     return(-9);
  1860. }
  1861.     } else if (y < 0) {
  1862. printf("?Too many %s match - %sn",
  1863.        dirflg ? "directories" : "files", sv);
  1864. if (sv) free(sv);
  1865. if (np) free(np);
  1866. return(-9);
  1867.     } else if (*wild || y > 1) {
  1868. if (sv) free(sv);
  1869. if (np) free(np);
  1870. return(x);
  1871.     }
  1872.     /* If not wild, see if it exists and is readable. */
  1873.     debug(F111,"cmifi sv not wild",sv,*wild);
  1874.     if (expanded)
  1875.       znext(*xp); /* Get first (only?) matching file */
  1876.     if (dirflg) /* Maybe wild and expanded */
  1877.       itsadir = isdir(*xp); /* so do this again. */
  1878.     y = dirflg ? itsadir : zchki(*xp); /* Now check accessibility */
  1879.     if (expanded) {
  1880. #ifdef ZXREWIND
  1881. nfiles = zxrewind(); /* Rewind so next znext() gets 1st */
  1882. #else
  1883. nzxopts = dirflg ? ZX_DIRONLY : 0;
  1884. if (matchdot)  nzxopts |= ZX_MATCHDOT;
  1885. if (recursive) nzxopts |= ZX_RECURSE;
  1886. nfiles = nzxpand(*xp,nzxopts);
  1887. #endif /* ZXREWIND */
  1888.     }
  1889.     debug(F111,"cmifi nfiles",*xp,nfiles);
  1890.     free(sv); /* done with this */
  1891.     sv = NULL;
  1892.     if (dirflg && y == 0) {
  1893. printf("?Not a directory - %sn",*xp);
  1894. #ifdef CKCHANNELIO
  1895. z_error = FX_ACC;
  1896. #endif /* CKCHANNELIO */
  1897. return(-9);
  1898.     } else if (y == -3) {
  1899. if (!xcmfdb) {
  1900.     if (diractive)
  1901.       /* Don't show filename if we're not allowed to see it */
  1902.       printf("?Read permission deniedn");
  1903.     else
  1904.       printf("?Read permission denied - %sn",*xp);
  1905. }
  1906. if (np) free(np);
  1907. #ifdef CKCHANNELIO
  1908. z_error = FX_ACC;
  1909. #endif /* CKCHANNELIO */
  1910. return(xcmfdb ? -6 : -9);
  1911.     } else if (y == -2) {
  1912. if (!recursive) {
  1913.     if (np) free(np);
  1914.     if (d) return(0);
  1915.     if (!xcmfdb)
  1916.       printf("?File not readable - %sn",*xp);
  1917. #ifdef CKCHANNELIO
  1918.     z_error = FX_ACC;
  1919. #endif /* CKCHANNELIO */
  1920.     return(xcmfdb ? -6 : -9);
  1921. }
  1922.     } else if (y < 0) {
  1923. if (np) free(np);
  1924. if (!nomsg && !xcmfdb)
  1925.   printf("?File not found - %sn",*xp);
  1926. #ifdef CKCHANNELIO
  1927. z_error = FX_FNF;
  1928. #endif /* CKCHANNELIO */
  1929. return(xcmfdb ? -6 : -9);
  1930.     }
  1931.     if (np) free(np);
  1932.     return(x);
  1933. #ifndef MAC
  1934.   case 2: /* ESC */
  1935.     debug(F101,"cmifi esc, xc","",xc);
  1936.     if (xc == 0) {
  1937. if (*xdef) {
  1938.     printf("%s ",xdef); /* If at beginning of field */
  1939. #ifdef GEMDOS
  1940.     fflush(stdout);
  1941. #endif /* GEMDOS */
  1942.     inword = cmflgs = 0;
  1943.     addbuf(xdef); /* Supply default. */
  1944.     if (setatm(xdef,0) < 0) {
  1945. printf("Default name too longn");
  1946. if (np) free(np);
  1947. return(-9);
  1948.     }
  1949. } else { /* No default */
  1950.     bleep(BP_WARN);
  1951. }
  1952. break;
  1953.     }
  1954. #ifndef NOSPL
  1955.     if (f) { /* If a conversion function is given */
  1956. #ifdef DOCHKVAR
  1957. char *s = *xp; /* See if there are any variables in */
  1958. while (*s) { /* the string and if so, expand it.  */
  1959.     if (chkvar(s)) {
  1960. #endif /* DOCHKVAR */
  1961. zq = atxbuf;
  1962. atxn = CMDBL;
  1963. if ((x = (*f)(*xp,&zq,&atxn)) < 0) {
  1964.     if (np) free(np);
  1965.     return(-2);
  1966. }
  1967. #ifdef DOCHKVAR
  1968.     /* reduce cc by number of \ consumed by conversion */
  1969.     /* function (needed for OS/2, where  is path separator) */
  1970. cc -= (strlen(*xp) - strlen(atxbuf));
  1971. #endif /* DOCHKVAR */
  1972. *xp = atxbuf;
  1973. if (!atxbuf[0]) { /* Result empty, use default */
  1974.     *xp = xdef;
  1975.     cc = strlen(xdef);
  1976. }
  1977. #ifdef DOCHKVAR
  1978. break;
  1979.     }
  1980.     s++;
  1981. }
  1982. #endif /* DOCHKVAR */
  1983.     }
  1984. #endif /* NOSPL */
  1985. #ifdef DTILDE
  1986.     dirp = tilde_expand(*xp); /* Expand tilde, if any, */
  1987.     if (*dirp != '') { /* in the atom buffer. */
  1988. if (setatm(dirp,0) < 0) {
  1989.     printf("Expanded name too longn");
  1990.     if (np) free(np);
  1991.     return(-9);
  1992. }
  1993.     }
  1994.     *xp = atmbuf;
  1995. #endif /* DTILDE */
  1996.     sp = *xp + cc;
  1997. #ifdef UNIXOROSK
  1998.     if (!strcmp(atmbuf,"..")) {
  1999. printf(" ");
  2000. strcat(cmdbuf," ");
  2001. cc++;
  2002. bp++;
  2003. *wild = 0;
  2004. *xp = atmbuf;
  2005. break;
  2006.     } else if (!strcmp(atmbuf,".")) {
  2007. bleep(BP_WARN);
  2008. if (np) free(np);
  2009. return(-1);
  2010.     } else {
  2011. /* This patches a glitch when user types "./foo<ESC>" */
  2012. /* in which the next two chars are omitted from the */
  2013. /* expansion.  There should be a better fix, however, */
  2014. /* since there is no problem with "../foo<ESC>". */
  2015. char *p = *xp;
  2016. if (*p == '.' && *(p+1) == '/')
  2017.   cc -= 2;
  2018.     }
  2019. #endif /* UNIXOROSK */
  2020. #ifdef datageneral
  2021.     *sp++ = '+'; /* Data General AOS wildcard */
  2022. #else
  2023.     *sp++ = '*'; /* Others */
  2024. #endif /* datageneral */
  2025.     *sp-- = '';
  2026. #ifdef GEMDOS
  2027.     if (!strchr(*xp, '.')) /* abde.e -> abcde.e* */
  2028.       strcat(*xp, ".*"); /* abc -> abc*.* */
  2029. #endif /* GEMDOS */
  2030.     /* Add wildcard and expand list. */
  2031. #ifdef COMMENT
  2032.     /* This kills partial completion when ESC given in path segment */
  2033.     nzxopts = dirflg ? ZX_DIRONLY : (d ? 0 : ZX_FILONLY);
  2034. #else
  2035.     nzxopts = 0;
  2036. #endif /* COMMENT */
  2037.     if (matchdot)  nzxopts |= ZX_MATCHDOT;
  2038.     if (recursive) nzxopts |= ZX_RECURSE;
  2039.     y = nzxpand(*xp,nzxopts);
  2040.     nfiles = y;
  2041.     debug(F111,"cmifi nzxpand",*xp,y);
  2042.     if (y > 0) {
  2043. #ifdef OS2
  2044.                 znext(filbuf); /* Get first */
  2045. #ifdef ZXREWIND
  2046. zxrewind(); /* Must "rewind" */
  2047. #else
  2048. nzxpand(*xp,nxzopts);
  2049. #endif /* ZXREWIND */
  2050. #else  /* Not OS2 */
  2051.                 ckstrncpy(filbuf,mtchs[0],CKMAXPATH);
  2052. #endif /* OS2 */
  2053.     } else
  2054.       *filbuf = '';
  2055.     filbuf[CKMAXPATH] = NUL;
  2056.     debug(F111,"cmifi filbuf",filbuf,y);
  2057.     *sp = ''; /* Remove wildcard. */
  2058.     *wild = (y > 1);
  2059.     if (y == 0) {
  2060. if (!nomsg) {
  2061.     printf("?No %s match - %sn",
  2062.    dirflg ? "directories" : "files", atmbuf);
  2063.     if (np) free(np);
  2064.     return(-9);
  2065. } else {
  2066.     bleep(BP_WARN);
  2067.     if (np) free(np);
  2068.     return(-1);
  2069. }
  2070.     } else if (y < 0) {
  2071. printf("?Too many %s match - %sn",
  2072.        dirflg ? "directories" : "files", atmbuf);
  2073. if (np) free(np);
  2074. return(-9);
  2075.     } else if (y > 1) {     /* Not unique. */
  2076. #ifndef NOPARTIAL
  2077. /* Partial filename completion */
  2078. int j, k; char c;
  2079. k = 0;
  2080. debug(F111,"cmifi partial",filbuf,cc);
  2081. #ifdef OS2
  2082. {
  2083.     int cur = 0,
  2084.     len = 0,
  2085.     len2 = 0,
  2086.     min = strlen(filbuf);
  2087.     char localfn[CKMAXPATH+1];
  2088.     len = min;
  2089.     for (j = 1; j <= y; j++) {
  2090. znext(localfn);
  2091. if (dirflg && !isdir(localfn))
  2092.   continue;
  2093. len2 = strlen(localfn);
  2094. for (cur = cc;
  2095.      cur < len && cur < len2 && cur <= min;
  2096.      cur++
  2097.      ) {
  2098.                             /* OS/2 or Windows, case doesn't matter */
  2099.     if (tolower(filbuf[cur]) != tolower(localfn[cur]))
  2100.       break;
  2101. }
  2102. if (cur < min)
  2103.   min = cur;
  2104.     }
  2105.     filbuf[min] = NUL;
  2106.     if (min > cc)
  2107.       k++;
  2108. }
  2109. #else /* OS2 */
  2110. for (i = cc; (c = filbuf[i]); i++) {
  2111.     for (j = 1; j < y; j++)
  2112.       if (mtchs[j][i] != c) break;
  2113.     if (j == y) k++;
  2114.     else filbuf[i] = filbuf[i+1] = NUL;
  2115. }
  2116. #endif /* OS2 */
  2117. debug(F111,"cmifi partial k",filbuf,k);
  2118. if (k > 0) { /* Got more characters */
  2119.     sp = filbuf + cc; /* Point to new ones */
  2120. #ifdef VMS
  2121.     for (i = 0; i < cc; i++) {
  2122. cmdchardel(); /* Back up over old partial spec */
  2123. bp--;
  2124.     }
  2125.     sp = filbuf; /* Point to new word start */
  2126.     debug(F100,"cmifi vms erase ok","",0);
  2127. #endif /* VMS */
  2128.     cc = k; /* How many new ones we just got */
  2129.     printf("%s",sp); /* Print them */
  2130.     while (*bp++ = *sp++) ; /* Copy to command buffer */
  2131.     bp--;              /* Back up over NUL */
  2132.     debug(F110,"cmifi partial cmdbuf",cmdbuf,0);
  2133.     if (setatm(filbuf,0) < 0) {
  2134. printf("?Partial name too longn");
  2135. if (np) free(np);
  2136. return(-9);
  2137.     }
  2138.     debug(F111,"cmifi partial atmbuf",atmbuf,cc);
  2139.     *xp = atmbuf;
  2140. }
  2141. #endif /* NOPARTIAL */
  2142. bleep(BP_WARN);
  2143.     } else { /* Unique, complete it.  */
  2144. #ifndef VMS
  2145. #ifdef CK_TMPDIR
  2146. /* isdir() function required for this! */
  2147. debug(F111,"cmifi unique",filbuf,cc);
  2148. if (isdir(filbuf) && !dirflg) {
  2149.     int len;
  2150.     len = strlen(filbuf);
  2151.     if (len > 0 && len < ATMBL - 1) {
  2152. if (filbuf[len-1] != dirsep) {
  2153.     filbuf[len] = dirsep;
  2154.     filbuf[len+1] = NUL;
  2155. }
  2156.     }
  2157.     sp = filbuf + cc;
  2158.     bleep(BP_WARN);
  2159.     printf("%s",sp);
  2160.     cc++;
  2161.     while (*bp++ = *sp++) ;
  2162.     bp--;
  2163.     if (setatm(filbuf,0) < 0) {
  2164. printf("?Directory name too longn");
  2165. if (np) free(np);
  2166. return(-9);
  2167.     }
  2168.     debug(F111,"cmifi directory atmbuf",atmbuf,cc);
  2169.     *xp = atmbuf;
  2170. } else { /* Not a directory or dirflg */
  2171. #endif /* CK_TMPDIR */
  2172. #endif /* VMS */
  2173. #ifndef VMS /* VMS dir names are special */
  2174. #ifndef datageneral /* VS dirnames must not end in ":" */
  2175.     if (dirflg) {
  2176. int len;
  2177. len = strlen(filbuf);
  2178. if (len > 0 && len < ATMBL - 1) {
  2179.     if (filbuf[len-1] != dirsep) {
  2180. filbuf[len] = dirsep;
  2181. filbuf[len+1] = NUL;
  2182.     }
  2183. }
  2184.     }
  2185. #endif /* datageneral */
  2186. #endif /* VMS */
  2187.     sp = filbuf + cc; /* Point past what user typed. */
  2188. #ifdef VMS
  2189.     debug(F111,"cmifi VMS erasing",filbuf,cc);
  2190.     for (i = 0; i < cc; i++) {
  2191. cmdchardel(); /* Back up over old partial spec */
  2192. bp--;
  2193.     }
  2194.     sp = filbuf; /* Point to new word start */
  2195.     debug(F111,"cmifi after VMS erase sp=",sp,cc);
  2196. #endif /* VMS */
  2197.     /* Complete the name. */
  2198. #ifdef COMMENT
  2199.     printf("%s%s",dirflg ? "" : " ",sp);
  2200. #else
  2201.     printf("%s ",sp); /* Complete the name. */
  2202. #endif /* COMMENT */
  2203. #ifdef GEMDOS
  2204.     fflush(stdout);
  2205. #endif /* GEMDOS */
  2206.     addbuf(sp); /* Add the characters to cmdbuf. */
  2207.     if (setatm(filbuf,0) < 0) { /* And to atmbuf. */
  2208. printf("?Completed name too longn");
  2209. if (np) free(np);
  2210. return(-9);
  2211.     }
  2212.     if (dirflg && !isdir(filbuf)) {
  2213. printf("?Not a directory - %sn", filbuf);
  2214. if (np) free(np);
  2215. return(-9);
  2216.     }
  2217.     inword = cmflgs = 0;
  2218.     *xp = atmbuf; /* Return pointer to atmbuf. */
  2219.     if (np) free(np);
  2220.     return(0);
  2221. #ifndef VMS
  2222. #ifdef CK_TMPDIR
  2223. }
  2224. #endif /* CK_TMPDIR */
  2225. #endif /* VMS */
  2226.     }
  2227.     break;
  2228.   case 3: /* Question mark - file menu wanted */
  2229.     if (*xhlp == NUL)
  2230.       printf(dirflg ? " Directory name" : " Input file specification");
  2231.     else
  2232.       printf(" %s",xhlp);
  2233. #ifdef GEMDOS
  2234.     fflush(stdout);
  2235. #endif /* GEMDOS */
  2236. #ifdef OLDHELP
  2237.     if (xc > 0) {
  2238. #endif /* OLDHELP */
  2239. #ifndef NOSPL
  2240. if (f) { /* If a conversion function is given */
  2241. #ifdef DOCHKVAR
  2242.     char *s = *xp; /* See if there are any variables in */
  2243.     while (*s) { /* the string and if so, expand them */
  2244. if (chkvar(s)) {
  2245. #endif /* DOCHKVAR */
  2246.     zq = atxbuf;
  2247.     atxn = CMDBL;
  2248.     if ((x = (*f)(*xp,&zq,&atxn)) < 0) {
  2249. if (np) free(np);
  2250. return(-2);
  2251.     }
  2252. #ifdef DOCHKVAR
  2253.                     /* reduce cc by number of \ consumed by conversion */
  2254.                     /* function (needed for OS/2, where  is path separator) */
  2255.     cc -= (strlen(*xp) - strlen(atxbuf));
  2256. #endif /* DOCHKVAR */
  2257.     *xp = atxbuf;
  2258. #ifdef DOCHKVAR
  2259.     break;
  2260. }
  2261. s++;
  2262.     }
  2263. #endif /* DOCHKVAR */
  2264. }
  2265. #endif /* NOSPL */
  2266. #ifdef DTILDE
  2267. dirp = tilde_expand(*xp); /* Expand tilde, if any */
  2268. if (*dirp != '') {
  2269.     if (setatm(dirp,0) < 0) {
  2270. printf("?Expanded name too longn");
  2271. if (np) free(np);
  2272. return(-9);
  2273.     }
  2274. }
  2275. *xp = atmbuf;
  2276. #endif /* DTILDE */
  2277. debug(F111,"cmifi ? *xp, cc",*xp,cc);
  2278. sp = *xp + cc; /* Insert "*" at end */
  2279. #ifdef datageneral
  2280. *sp++ = '+'; /* Insert +, the DG wild card */
  2281. #else
  2282. *sp++ = '*';
  2283. #endif /* datageneral */
  2284. *sp-- = '';
  2285. #ifdef GEMDOS
  2286. if (! strchr(*xp, '.')) /* abde.e -> abcde.e* */
  2287.   strcat(*xp, ".*"); /* abc -> abc*.* */
  2288. #endif /* GEMDOS */
  2289. debug(F110,"cmifi ? wild",*xp,0);
  2290. #ifdef COMMENT
  2291. /* This kills file lists when we're still the path part */
  2292. nzxopts = dirflg ? ZX_DIRONLY : (d ? 0 : ZX_FILONLY);
  2293. #else
  2294. #ifdef COMMENT
  2295. /* But this makes "cd ?" list regular files */
  2296. nzxopts = 0;
  2297. #else
  2298. nzxopts = dirflg ? ZX_DIRONLY : 0;
  2299. #endif /* COMMENT */
  2300. #endif /* COMMENT */
  2301. debug(F101,"cmifi matchdot","",matchdot);
  2302. if (matchdot)  nzxopts |= ZX_MATCHDOT;
  2303. if (recursive) nzxopts |= ZX_RECURSE;
  2304. y = nzxpand(*xp,nzxopts);
  2305. nfiles = y;
  2306. *sp = '';
  2307. if (y == 0) {
  2308.     if (nomsg) {
  2309. printf(": %sn",atmbuf);
  2310. printf("%s%s",cmprom,cmdbuf);
  2311. fflush(stdout);
  2312. if (np) free(np);
  2313. return(-1);
  2314.     } else {
  2315. printf("?No %s match - %sn",
  2316.        dirflg ? "directories" : "files", atmbuf);
  2317. if (np) free(np);
  2318. return(-9);
  2319.     }
  2320. } else if (y < 0) {
  2321.     printf("?Too many %s match - %sn",
  2322.    dirflg ? "directories" : "files", atmbuf);
  2323.     if (np) free(np);
  2324.     return(-9);
  2325. } else {
  2326.     printf(", one of the following:n");
  2327. #ifdef OLDHELP
  2328.     clrhlp();
  2329.     for (i = 0; i < y; i++) {
  2330. znext(filbuf);
  2331. if (!dirflg || isdir(filbuf)) {
  2332. #ifdef VMS
  2333.     printf(" %sn",filbuf); /* VMS names can be long */
  2334. #else
  2335.     addhlp(filbuf);
  2336. #endif /* VMS */
  2337. }
  2338.     }
  2339.     dmphlp();
  2340. #else  /* New way... */
  2341.     if (filhelp(y,"","",1,dirflg) < 0) {
  2342. if (np) free(np);
  2343. return(-9);
  2344.     }
  2345. #endif /* OLDHELP */
  2346. }
  2347. #ifdef OLDHELP
  2348.     } else
  2349.       printf("n");
  2350. #endif /* OLDHELP */
  2351.     printf("%s%s",cmprom,cmdbuf);
  2352.     fflush(stdout);
  2353.     break;
  2354. #endif /* MAC */
  2355.         }
  2356. #ifdef BS_DIRSEP
  2357.         dirnamflg = 1;
  2358.         x = gtword(0);                  /* No, get a word */
  2359. dirnamflg = 0;
  2360. #else
  2361.         x = gtword(0);                  /* No, get a word */
  2362. #endif /* BS_DIRSEP */
  2363.     *xp = atmbuf;
  2364.     }
  2365. }
  2366. /*  C M F L D  --  Parse an arbitrary field  */
  2367. /*
  2368.  Returns
  2369.    -3 if no input present when required,
  2370.    -2 if field too big for buffer,
  2371.    -1 if reparse needed,
  2372.     0 otherwise, xp pointing to string result.
  2373. */
  2374. int
  2375. cmfld(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; {
  2376.     int x, xc;
  2377.     char *zq;
  2378.     inword = 0; /* Initialize counts & pointers */
  2379.     cc = 0;
  2380.     xc = 0;
  2381.     *xp = "";
  2382.     debug(F110,"cmfld xdef 1",xdef,0);
  2383.     if (!xhlp) xhlp = "";
  2384.     if (!xdef) xdef = "";
  2385.     ckstrncpy(cmdefault,xdef,CMDEFAULT); /* Copy default */
  2386.     xdef = cmdefault;
  2387.     debug(F111,"cmfld xdef 2",xdef,cmflgs);
  2388.     if ((x = cmflgs) != 1) {            /* Already confirmed? */
  2389.         x = gtword(0);                  /* No, get a word */
  2390.     } else {
  2391. if (setatm(xdef,0) < 0) { /* If so, use default, if any. */
  2392.     printf("?Default too longn");
  2393.     return(-9);
  2394. }
  2395.     }
  2396.     *xp = atmbuf;                       /* Point to result. */
  2397.     debug(F111,"cmfld atmbuf 1",atmbuf,cmflgs);
  2398.     while (1) {
  2399.         xc += cc;                       /* Count the characters. */
  2400.         debug(F111,"cmfld: gtword",atmbuf,xc);
  2401.         debug(F101,"cmfld x","",x);
  2402.         switch (x) {
  2403.   case -9:
  2404.     printf("Command or field too longn");
  2405.   case -4: /* EOF */
  2406.   case -3: /* Empty. */
  2407.   case -2: /* Out of space. */
  2408.   case -1: /* Reparse needed */
  2409.     return(x);
  2410.   case 0: /* SP or NL */
  2411.   case 1:
  2412.     debug(F111,"cmfld 1",atmbuf,xc);
  2413.     if (xc == 0) { /* If no input, return default. */
  2414. if (setatm(xdef,0) < 0) {
  2415.     printf("?Default too longn");
  2416.     return(-9);
  2417. }
  2418.     }
  2419.     *xp = atmbuf; /* Point to what we got. */
  2420.     debug(F111,"cmfld 2",atmbuf,(f) ? 1 : 0);
  2421.     if (f) { /* If a conversion function is given */
  2422. zq = atxbuf; /* employ it now. */
  2423. atxn = CMDBL;
  2424. debug(F111,"cmfld zzstring",atxbuf,x);
  2425. if ((*f)(*xp,&zq,&atxn) < 0)
  2426.   return(-2);
  2427. if (setatm(atxbuf,1) < 0) { /* Replace by new value */
  2428.     printf("Value too longn");
  2429.     return(-9);
  2430. }
  2431. *xp = atmbuf;
  2432.     }
  2433.     debug(F111,"cmfld 3",atmbuf,xc);
  2434.     if (**xp == NUL) { /* If variable evaluates to null */
  2435. if (setatm(xdef,0) < 0) {
  2436.     printf("?Default too longn");
  2437.     return(-9);
  2438. }
  2439. if (**xp == NUL) x = -3; /* If still empty, return -3. */
  2440.     }
  2441.     debug(F111,"cmfld returns",*xp,x);
  2442.     return(x);
  2443.   case 2: /* ESC */
  2444.     if (xc == 0 && *xdef) {
  2445. printf("%s ",xdef); /* If at beginning of field, */
  2446. #ifdef GEMDOS
  2447. fflush(stdout);
  2448. #endif /* GEMDOS */
  2449. addbuf(xdef); /* Supply default. */
  2450. inword = cmflgs = 0;
  2451. if (setatm(xdef,0) < 0) {
  2452.     printf("?Default too longn");
  2453.     return(-9);
  2454. } else /* Return as if whole field */
  2455.   return(0); /* typed, followed by space. */
  2456.     } else {
  2457. bleep(BP_WARN);
  2458.     }
  2459.     break;
  2460.   case 3: /* Question mark */
  2461.     if (*xhlp == NUL)
  2462.       printf(" Please complete this field");
  2463.     else
  2464.       printf(" %s",xhlp);
  2465.     printf("n%s%s",cmprom,cmdbuf);
  2466.     fflush(stdout);
  2467.     break;
  2468.         }
  2469. x = gtword(0);
  2470. /* *xp = atmbuf; */
  2471.     }
  2472. }
  2473. /*  C M T X T  --  Get a text string, including confirmation  */
  2474. /*
  2475.   Print help message 'xhlp' if ? typed, supply default 'xdef' if null
  2476.   string typed.  Returns:
  2477.    -1 if reparse needed or buffer overflows.
  2478.     1 otherwise.
  2479.   with cmflgs set to return code, and xp pointing to result string.
  2480. */
  2481. int
  2482. cmtxt(xhlp,xdef,xp,f) char *xhlp; char *xdef; char **xp; xx_strp f; {
  2483.     int x, i;
  2484.     char *xx, *zq;
  2485.     static int xc;
  2486.     if (!xhlp) xhlp = "";
  2487.     if (!xdef) xdef = "";
  2488.     cmfldflgs = 0;
  2489.     ckstrncpy(cmdefault,xdef,CMDEFAULT); /* Copy default */
  2490.     xdef = cmdefault;
  2491.     debug(F101,"cmtxt, cmflgs","",cmflgs);
  2492.     inword = 0; /* Start atmbuf counter off at 0 */
  2493.     cc = 0;
  2494.     if (cmflgs == -1) {                 /* If reparsing, */
  2495. *xp = pp;
  2496.         xc = (int)strlen(*xp); /* get back the total text length, */
  2497. bp = *xp; /* and back up the pointers. */
  2498. np = *xp;
  2499. pp = *xp;
  2500.     } else {                            /* otherwise, */
  2501. debug(F100,"cmtxt: fresh start","",0);
  2502.         *xp = "";                       /* start fresh. */
  2503.         xc = 0;
  2504.     }
  2505.     *atmbuf = NUL;                      /* And empty the atom buffer. */
  2506.     rtimer(); /* Reset timer */
  2507.     if ((x = cmflgs) != 1) {
  2508. int done = 0;
  2509. while (!done) {
  2510.     x = gtword(0); /* Get first word. */
  2511.     *xp = pp; /* Save pointer to it. */
  2512.     debug(F111,"cmtxt:",*xp,cc);
  2513.     if (x == -10) {
  2514. if (gtimer() > timelimit) {
  2515.     if (!quiet) printf("?Timed outn");
  2516.     return(x);
  2517. }
  2518.     } else
  2519.       done = 1;
  2520. }
  2521.     }
  2522.     while (1) { /* Loop for each word in text. */
  2523.         xc += cc;                       /* Char count for all words. */
  2524.         debug(F111,"cmtxt gtword",atmbuf,xc);
  2525.         debug(F101,"cmtxt x","",x);
  2526.         switch (x) {
  2527.   case -10:
  2528.     if (gtimer() > timelimit) {
  2529. #ifdef IKSD
  2530.                 extern int inserver;
  2531.                 if (inserver) {
  2532.                     printf("rnIKSD IDLE TIMEOUT: %d secrn", timelimit);
  2533.                     doexit(GOOD_EXIT,0);
  2534.                 }
  2535. #endif /* IKSD */
  2536. if (!quiet) printf("?Timed outn");
  2537. return(-10);
  2538.     } else {
  2539. x = gtword(0);
  2540. continue;
  2541.     }
  2542.   case -9: /* Buffer overflow */
  2543.     printf("Command or field too longn");
  2544.   case -4: /* EOF */
  2545. #ifdef MAC
  2546.   case -3: /* Quit/Timeout */
  2547. #endif /* MAC */
  2548.   case -2: /* Overflow */
  2549.   case -1: /* Deletion */
  2550.     return(x);
  2551.   case 0: /* Space */
  2552.     xc++; /* Just count it */
  2553.     break;
  2554.   case 1: /* CR or LF */
  2555.     if (xc == 0) *xp = xdef;
  2556.     if (f) { /* If a conversion function is given */
  2557. zq = atxbuf; /* Point to the expansion buffer */
  2558. atxn = CMDBL; /* specify its length */
  2559. debug(F110,"cmtxt calling (*f)",*xp,0);
  2560. if ((x = (*f)(*xp,&zq,&atxn)) < 0) return(-2);
  2561. cc = (int)strlen(atxbuf);
  2562. if (cc < 1) {
  2563.     *xp = xdef;
  2564.     cc = strlen(xdef);
  2565. } else {
  2566.     *xp = atxbuf; /* and return pointer to it. */
  2567. }
  2568. debug(F111,"cmtxt (*f) returns",*xp,cc);
  2569.     }
  2570.     xx = *xp;
  2571.     for (i = (int)strlen(xx) - 1; i > 0; i--)
  2572.       if (xx[i] != SP) /* Trim trailing blanks */
  2573. break;
  2574.       else
  2575. xx[i] = NUL;
  2576. #ifdef CK_RECALL
  2577.     addcmd(cmdbuf);
  2578. #endif /* CK_RECALL */
  2579.     return(x);
  2580.   case 2: /* ESC */
  2581.     if (xc == 0) { /* Nothing typed yet */
  2582. if (*xdef) { /* Have a default for this field? */
  2583.     printf("%s ",xdef); /* Yes, supply it */
  2584.     inword = cmflgs = 0;
  2585. #ifdef GEMDOS
  2586.     fflush(stdout);
  2587. #endif /* GEMDOS */
  2588.     cc = addbuf(xdef);
  2589. } else bleep(BP_WARN); /* No default */
  2590.     } else { /* Already in field */
  2591. int x; char *p;