blob: e1bca349ab9fe2fefff3adb42560af00a140ec66 [file] [log] [blame]
#include "ckcsym.h"
char *cmdv = "Command package 8.0.157, 11 May 2003";
/* C K U C M D -- Interactive command package for Unix */
/*
Author: Frank da Cruz (fdc@columbia.edu),
Columbia University Academic Information Systems, New York City.
Copyright (C) 1985, 2004,
Trustees of Columbia University in the City of New York.
All rights reserved. See the C-Kermit COPYING.TXT file or the
copyright text in the ckcmai.c module for disclaimer and permissions.
*/
#define TOKPRECHECK
#define DOCHKVAR
/* Command-terminal-to-C-Kermit character mask */
#ifdef OS2 /* K95 */
int cmdmsk = 255; /* (always was 255) */
#else /* All others... */
int cmdmsk = 255; /* 31 Dec 2000 (was 127) */
#endif /* OS2 */
#ifdef BS_DIRSEP /* Directory separator is backslash */
#undef BS_DIRSEP
#endif /* BS_DIRSEP */
#ifdef OS2
#define BS_DIRSEP
#endif /* BS_DIRSEP */
#define CKUCMD_C
#include "ckcdeb.h" /* Formats for debug(), etc. */
#include "ckcker.h" /* Needed for BIGBUFOK definition */
#include "ckcnet.h" /* Needed for server-side Telnet */
#include "ckucmd.h" /* Needed for everything */
#include "ckuusr.h" /* Needed for prompt length */
#ifndef NOARROWKEYS
#ifndef NOESCSEQ
#ifdef VMSORUNIX
#define USE_ARROWKEYS /* Use arrow keys for command recall */
#endif /* VMSORUNIX */
#endif /* NOESCSEQ */
#endif /* NOARROWKEYS */
#undef CKUCMD_C
_PROTOTYP( int unhex, (char) );
_PROTOTYP( static VOID cmdclrscn, (void) );
#ifdef CKLEARN
_PROTOTYP( VOID learncmd, (char *) );
#endif /* CKLEARN */
static char *moname[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
struct keytab cmonths[] = {
{ "april", 4, 0 },
{ "august", 8, 0 },
{ "december", 12, 0 },
{ "february", 2, 0 },
{ "january", 1, 0 },
{ "july", 7, 0 },
{ "june", 6, 0 },
{ "march", 3, 0 },
{ "may", 5, 0 },
{ "november", 11, 0 },
{ "october", 10, 0 },
{ "september", 9, 0 }
};
#ifndef NOICP /* The rest only if interactive command parsing selected */
#ifndef NOSPL
_PROTOTYP( int chkvar, (char *) );
extern int askflag;
#endif /* NOSPL */
#ifdef CKROOT
extern int ckrooterr;
#endif /* CKROOT */
#ifdef IKSD
extern int inserver;
#endif /* IKSD */
int cmfldflgs = 0; /* Flags for cmfld() */
int cmkwflgs = 0; /* Flags from last keyword parse */
static int blocklvl = 0; /* Block nesting level */
static int linebegin = 0; /* Flag for at start of a line */
static int quoting = 1; /* Quoting is allowed */
static int swarg = 0; /* Parsing a switch argument */
static int xcmfdb = 0; /* Flag for parsing chained fdbs... */
static int chsrc = 0; /* Source of character, 1 = tty */
static int newcmd = 0; /* See addcmd() */
#ifdef BS_DIRSEP
static int dirnamflg = 0;
#endif /* BS_DIRSEP */
/*
Modeled after the DECSYSTEM-20 command parser (the COMND JSYS), RIP. Features:
. parses and verifies keywords, filenames, text strings, numbers, other data
. displays appropriate menu or help message when user types "?"
. does keyword and filename completion when user types ESC or TAB
. does partial keyword and filename completion
. accepts any unique abbreviation for a keyword
. allows keywords to have attributes, like "invisible" and "abbreviation"
. can supply defaults for fields omitted by user
. provides command retry and recall
. provides character, word, and line deletion (but only from the end)
. accepts input from keyboard, command files, macros, or redirected stdin
. allows for full or half duplex operation, character or line input
. allows \-escapes for special characters
. allows specification of a user exit to expand variables, etc.
. settable prompt, protected from deletion, dynamically re-evaluated each time
. allows chained parse functions.
Functions:
cmsetp - Set prompt (cmprom is prompt string)
cmsavp - Save current prompt
cmgetp = Get current prompt
prompt - Issue prompt
cmini - Clear the command buffer (before parsing a new command)
cmres - Reset command buffer pointers (before reparsing)
cmkey - Parse a keyword or token (also cmkey2)
cmswi - Parse a switch
cmnum - Parse a number
cmifi - Parse an input file name
cmofi - Parse an output file name (also cmifip, cmifi2, ...)
cmdir - Parse a directory name (also cmdirp)
cmfld - Parse an arbitrary field
cmtxt - Parse a text string
cmdate - Parse a date-time string
cmcfm - Parse command confirmation (end of line)
cmfdb - Parse any of a list of the foregoing (chained parse functions)
Return codes:
-9: like -2 except this module already printed the error message
-3: no input provided when required
-2: input was invalid (e.g. not a number when a number was required)
-1: reparse required (user deleted into a preceding field)
0 or greater: success
See individual functions for greater detail.
Before using these routines, the caller should #include "ckucmd.h" and set the
program's prompt by calling cmsetp(). If the file parsing functions cmifi,
cmofi, or cmdir are to be used, this module must be linked with a ck?fio file
system support module for the appropriate system, e.g. ckufio for Unix. If
the caller puts the terminal in character wakeup ("cbreak") mode with no echo,
then these functions will provide line editing -- character, word, and line
deletion, as well as keyword and filename completion upon ESC and help
strings, keyword, or file menus upon '?'. If the caller puts the terminal
into character wakeup/noecho mode, care should be taken to restore it before
exit from or interruption of the program. If the character wakeup mode is not
set, the system's own line editor may be used.
NOTE: Contrary to expectations, many #ifdef's have been added to this module.
Any operation requiring an #ifdef (like clear screen, get character from
keyboard, erase character from screen, etc) should eventually be turned into a
call to a function that is defined in ck?tio.c, but then all the ck?tio.c
modules would have to be changed...
*/
/* Includes */
#include "ckcker.h"
#include "ckcasc.h" /* ASCII character symbols */
#include "ckucmd.h" /* Command parsing definitions */
#ifdef OSF13
#ifdef CK_ANSIC
#ifdef _NO_PROTO
#undef _NO_PROTO
#endif /* _NO_PROTO */
#endif /* CK_ANSIC */
#endif /* OSF13 */
#include <errno.h> /* Error number symbols */
#ifdef OS2
#ifndef NT
#define INCL_NOPM
#define INCL_VIO /* Needed for ckocon.h */
#include <os2.h>
#undef COMMENT
#else
#define APIRET ULONG
#include <windows.h>
#endif /* NT */
#include "ckocon.h"
#include <io.h>
#endif /* OS2 */
#ifdef NT
#define stricmp _stricmp
#endif /* NT */
#ifdef OSK
#define cc ccount /* OS-9/68K compiler bug */
#endif /* OSK */
#ifdef GEMDOS /* Atari ST */
#ifdef putchar
#undef putchar
#endif /* putchar */
#define putchar(x) conoc(x)
#endif /* GEMDOS */
#ifdef CK_AUTODL
extern int cmdadl, justone;
#endif /* CK_AUTODL */
extern int timelimit, nzxopts, nopush, nolocal, xcmdsrc, keepallchars;
#ifdef CKSYSLOG
#ifdef UNIX
#ifdef CKXPRINTF /* Our printf macro conflicts with */
#undef printf /* use of "printf" in syslog.h */
#endif /* CKXPRINTF */
#ifdef RTAIX
#include <sys/syslog.h>
#else /* RTAIX */
#include <syslog.h>
#endif /* RTAIX */
#ifdef CKXPRINTF
#define printf ckxprintf
#endif /* CKXPRINTF */
#endif /* UNIX */
#endif /* CKSYSLOG */
/* Local variables */
static
int psetf = 0, /* Flag that prompt has been set */
cc = 0, /* Character count */
dpx = 0, /* Duplex (0 = full) */
inword = 0; /* In the middle of getting a word */
char *dfprom = "Command? "; /* Default prompt */
int cmflgs; /* Command flags */
int cmfsav; /* A saved version of them */
static char pushc = NUL;
static char brkchar = NUL;
#define CMDEFAULT 1023
static char cmdefault[CMDEFAULT+1];
#ifdef DCMDBUF
char *cmdbuf = NULL; /* Command buffer */
char *savbuf = NULL; /* Buffer to save copy of command */
char *atmbuf = NULL; /* Atom buffer - for current field */
char *atxbuf = NULL; /* For expanding the atom buffer */
static char *atybuf = NULL; /* For copying atom buffer */
static char *filbuf = NULL; /* File name buffer */
static char *cmprom = NULL; /* Program's prompt */
static char *cmprxx = NULL; /* Program's prompt, unevaluated */
#ifdef CK_RECALL
/*
Command recall is available only if we can make profligate use of malloc().
*/
#define R_MAX 10 /* How many commands to save */
int cm_recall = R_MAX; /* Size of command recall buffer */
int on_recall = 1; /* Recall feature is ON */
static int no_recall = 0; /* Recall OFF for this cmd only */
static int force_add = 0; /* Force cmd into recall buffer */
static int last_recall = -1; /* Last recall-related action */
/*
-1 = none
0 = CR (a command was entered)
1 = Up
2 = Down
*/
int in_recall = 0; /* Recall buffers are init'd */
static int
current = -1, /* Pointer to current command */
rlast = -1; /* Index of last command in buffer */
static char **recall = NULL; /* Array of recall buffer pointers */
#endif /* CK_RECALL */
#else /* !DCMDBUF */
char cmdbuf[CMDBL+4]; /* Command buffer */
char savbuf[CMDBL+4]; /* Buffer to save copy of command */
char atmbuf[ATMBL+4]; /* Atom buffer */
char atxbuf[CMDBL+4]; /* For expanding the atom buffer */
static char atybuf[ATMBL+4]; /* For copying atom buffer */
static char filbuf[ATMBL+4]; /* File name buffer */
static char cmprom[PROMPTL+1]; /* Program's prompt */
static char cmprxx[PROMPTL+1]; /* Program's prompt, unevaluated */
#endif /* DCMDBUF */
/* Command buffer pointers */
#define PPVLEN 24
char ppvnambuf[PPVLEN+1] = { NUL, NUL };
char * cmbptr = NULL; /* Current position (for export) */
static char *bp, /* Current command buffer position */
*pp, /* Start of current field */
*np; /* Start of next field */
static int ungw, /* For ungetting words */
atxn; /* Expansion buffer (atxbuf) length */
#ifdef OS2
extern int wideresult;
#endif /* OS2 */
extern int cmd_cols, cmd_rows, local, quiet;
#ifdef TNCODE
#ifdef IAC
#undef IAC
#endif /* IAC */
#define IAC 255
#endif /* TNCODE */
_PROTOTYP( static int gtword, (int) );
_PROTOTYP( static int addbuf, (char *) );
_PROTOTYP( static int setatm, (char *, int) );
_PROTOTYP( static VOID cmdnewl, (char) );
_PROTOTYP( static VOID cmdchardel, (void) );
_PROTOTYP( static VOID cmdecho, (char, int) );
_PROTOTYP( static int test, (int, int) );
#ifdef GEMDOS
_PROTOTYP( extern char *strchr, (char *, int) );
#endif /* GEMDOS */
extern char * dftty;
/* The following are for use with chained FDB's */
static int crflag = 0; /* Carriage return was typed */
static int qmflag = 0; /* Question mark was typed */
static int esflag = 0; /* Escape was typed */
/* Directory separator */
#ifdef GEMDOS
static char dirsep = '\\';
#else
#ifdef datageneral
static char dirsep = ':';
#else
#ifdef MAC
static char dirsep = ':';
#else
#ifdef VMS
static char dirsep = '.';
#else
#ifdef STRATUS
static char dirsep = '>';
#else
static char dirsep = '/'; /* UNIX, OS/2, OS-9, Amiga, etc. */
#endif /* STRATUS */
#endif /* VMS */
#endif /* MAC */
#endif /* datageneral */
#endif /* GEMDOS */
/* H A S N O P A T H */
/* Returns 0 if filespec s includes any path segments; 1 if it doesn't. */
int
hasnopath(s) char * s; {
char * p = NULL;
if (!s) return(0);
if (!*s) return(0);
zstrip(s,&p);
return(ckstrcmp(s,p,CKMAXPATH,filecase) == 0 ? 1 : 0);
}
/* C K S P R E A D -- Print string double-spaced */
static char * sprptr = NULL;
static char *
ckspread(s) char * s; {
int n = 0;
char * p;
n = strlen(s);
if (sprptr)
free(sprptr);
sprptr = malloc(n + n + 3);
if (sprptr) {
p = sprptr;
while (*s) {
*p++ = *s++;
*p++ = SP;
}
*p = NUL;
}
return(sprptr ? sprptr : "");
}
/* T E S T -- Bit test */
static int
test(x,m) int x, m; { /* Returns 1 if any bits from m are on in x, else 0 */
return((x & m) ? 1 : 0);
}
/* K W D H E L P -- Given a keyword table, print keywords in columns. */
/*
Call with:
s - keyword table
n - number of entries
pat - pattern (left substring) that must match for each keyword
pre - prefix to add to each keyword
post - suffix to add to each keyword
off - offset on first screenful, allowing room for introductory text
xhlp - 1 to print any CM_INV keywords that are not also abbreviations.
2 to print CM_INV keywords if CM_HLP also set
4 if it's a switch table (to show ':' if CM_ARG)
Arranges keywords in columns with width based on longest keyword.
Does "more?" prompting at end of screen.
Uses global cmd_rows and cmd_cols for screen size.
*/
VOID
kwdhelp(s,n,pat,pre,post,off,xhlp)
struct keytab s[]; int n, off, xhlp; char *pat, *pre, *post;
/* kwdhelp */ {
int width = 0;
int cc;
int cols, height, i, j, k, lc, n2 = 0;
char *b = NULL, *p, *q;
char *pa, *px;
char **s2 = NULL;
char *tmpbuf = NULL;
cc = strlen(pat);
if (!s) return; /* Nothing to do */
if (n < 1) return; /* Ditto */
if (off < 0) off = 0; /* Offset for first page */
if (!pre) pre = ""; /* Handle null string pointers */
if (!post) post = "";
lc = off; /* Screen-line counter */
if (xhlp & 4) /* For switches */
tmpbuf = (char *)malloc(TMPBUFSIZ+1);
if ((s2 = (char **) malloc(n * sizeof(char *)))) {
for (i = 0; i < n; i++) { /* Find longest keyword */
s2[i] = NULL;
if (ckstrcmp(s[i].kwd,pat,cc,0))
continue;
if (s[i].flgs & CM_PSH /* NOPUSH or nopush screening */
#ifndef NOPUSH
&& nopush
#endif /* NOPUSH */
)
continue;
if (s[i].flgs & CM_LOC /* NOLOCAL or nolocal screening */
#ifndef NOLOCAL
&& nolocal
#endif /* NOLOCAL */
)
continue;
if (s[i].flgs & CM_INV) {
#ifdef COMMENT
/* This code does not show invisible keywords at all except for "help ?" */
/* and then only help topics (CM_HLP) in the top-level keyword list. */
if ((xhlp & 2) == 0)
continue;
else if ((s[i].flgs & CM_HLP) == 0)
continue;
#else
/* This code shows invisible keywords that are not also abbreviations when */
/* ? was typed AFTER the beginning of the field so the user can find out */
/* what they are and (for example) why completion doesn't work at this point */
if (s[i].flgs & CM_ABR)
continue;
else if ((xhlp & 3) == 0)
continue;
else if ((xhlp & 2) && ((s[i].flgs & CM_HLP) == 0))
continue;
#endif /* COMMENT */
}
j = strlen(s[i].kwd);
if (!(xhlp & 4) || !tmpbuf) { /* Regular keyword table */
s2[n2++] = s[i].kwd; /* Copy pointers to visible ones */
} else { /* Switches */
ckmakmsg(tmpbuf, /* Make a copy that shows ":" if */
TMPBUFSIZ, /* the switch takes an argument. */
s[i].kwd,
(s[i].flgs & CM_ARG) ? ":" : "",
NULL,
NULL
);
makestr(&(s2[n2]),tmpbuf);
if (s[i].flgs & CM_ARG) j++;
n2++;
}
if (j > width)
width = j;
}
/* Column width */
n = n2;
}
if (s2 && (b = (char *) malloc(cmd_cols + 1))) { /* Make a line buffer */
char * bx;
bx = b + cmd_cols;
width += (int)strlen(pre) + (int)strlen(post) + 2;
cols = cmd_cols / width; /* How many columns? */
if (cols < 1) cols = 1;
height = n / cols; /* How long is each column? */
if (n % cols) height++; /* Add one for remainder, if any */
for (i = 0; i < height; i++) { /* Loop for each row */
for (j = 0; j < cmd_cols; j++) /* First fill row with blanks */
b[j] = SP;
for (j = 0; j < cols; j++) { /* Loop for each column in row */
k = i + (j * height); /* Index of next keyword */
if (k < n) { /* In range? */
pa = pre;
px = post;
p = s2[k]; /* Point to verb name */
q = b + (j * width) + 1; /* Where to copy it to */
while ((q < bx) && (*q++ = *pa++)) ; /* Copy prefix */
q--; /* Back up over NUL */
while ((q < bx) && (*q++ = *p++)) ; /* Copy filename */
q--; /* Back up over NUL */
while ((q < bx) && (*q++ = *px++)) ; /* Copy suffix */
if (j < cols - 1) {
q--;
*q = SP; /* Replace the space */
}
}
}
p = b + cmd_cols - 1; /* Last char in line */
while (*p-- == SP) ; /* Trim */
*(p+2) = NUL;
printf("%s\n",b); /* Print the line */
if (++lc > (cmd_rows - 2)) { /* Screen full? */
if (!askmore()) /* Do more-prompting... */
goto xkwdhelp;
else
lc = 0;
}
}
/* printf("\n"); */ /* Blank line at end of report */
} else { /* Malloc failure, no columns */
for (i = 0; i < n; i++) {
if (s[i].flgs & CM_INV) /* Use original keyword table */
continue; /* skipping invisible entries */
printf("%s%s%s\n",pre,s[i].kwd,post);
if (++lc > (cmd_rows - 2)) { /* Screen full? */
if (!askmore()) /* Do more-prompting... */
goto xkwdhelp;
else
lc = 0;
}
}
}
xkwdhelp:
if (xhlp & 4) {
if (tmpbuf) free((char *)tmpbuf);
for (i = 0; i < n; i++)
if (s2[i]) free(s2[i]);
}
if (s2) free(s2); /* Free array copy */
if (b) free(b); /* Free line buffer */
return;
}
/* F I L H E L P -- Given a file list, print names in columns. */
/*
Call with:
n - number of entries
pre - prefix to add to each filename
post - suffix to add to each filename
off - offset on first screenful, allowing room for introductory text
cmdirflg - 1 if only directory names should be listed, 0 to list all files
Arranges filenames in columns with width based on longest filename.
Does "more?" prompting at end of screen.
Uses global cmd_rows and cmd_cols for screen size.
*/
int
filhelp(n,pre,post,off,cmdirflg) int n, off; char *pre, *post; int cmdirflg; {
char filbuf[CKMAXPATH + 1]; /* Temp buffer for one filename */
int width = 0;
int cols, height, i, j, k, lc, n2 = 0, rc = 0, itsadir = 0;
char *b = NULL, *p, *q;
char *pa, *px;
char **s2 = NULL;
#ifdef VMS
char * cdp = zgtdir();
#endif /* VMS */
if (n < 1) return(0);
if (off < 0) off = 0; /* Offset for first page */
if (!pre) pre = ""; /* Handle null string pointers */
if (!post) post = "";
lc = off; /* Screen-line counter */
if ((s2 = (char **) malloc(n * sizeof(char *)))) {
for (i = 0; i < n; i++) { /* Loop through filenames */
itsadir = 0;
s2[i] = NULL; /* Initialize each pointer to NULL */
znext(filbuf); /* Get next filename */
if (!filbuf[0]) /* Shouldn't happen */
break;
#ifdef COMMENT
itsadir = isdir(filbuf); /* Is it a directory? */
if (cmdirflg && !itsadir) /* No, listing directories only? */
continue; /* So skip this one. */
#endif /* COMMENT */
#ifdef VMS
ckstrncpy(filbuf,zrelname(filbuf,cdp),CKMAXPATH);
#endif /* VMS */
j = strlen(filbuf);
#ifndef VMS
if (itsadir && j < CKMAXPATH - 1 && j > 0) {
if (filbuf[j-1] != dirsep) {
filbuf[j++] = dirsep;
filbuf[j] = NUL;
}
}
#endif /* VMS */
if (!(s2[n2] = malloc(j+1))) {
printf("?Memory allocation failure\n");
rc = -9;
goto xfilhelp;
}
if (j <= CKMAXPATH) {
strcpy(s2[n2],filbuf);
n2++;
} else {
printf("?Name too long - %s\n", filbuf);
rc = -9;
goto xfilhelp;
}
if (j > width) /* Get width of widest one */
width = j;
}
n = n2; /* How many we actually got */
}
sh_sort(s2,NULL,n,0,0,filecase); /* Alphabetize the list */
rc = 1;
if (s2 && (b = (char *) malloc(cmd_cols + 1))) { /* Make a line buffer */
char * bx;
bx = b + cmd_cols;
width += (int)strlen(pre) + (int)strlen(post) + 2;
cols = cmd_cols / width; /* How many columns? */
if (cols < 1) cols = 1;
height = n / cols; /* How long is each column? */
if (n % cols) height++; /* Add one for remainder, if any */
for (i = 0; i < height; i++) { /* Loop for each row */
for (j = 0; j < cmd_cols; j++) /* First fill row with blanks */
b[j] = SP;
for (j = 0; j < cols; j++) { /* Loop for each column in row */
k = i + (j * height); /* Index of next filename */
if (k < n) { /* In range? */
pa = pre;
px = post;
p = s2[k]; /* Point to filename */
q = b + (j * width) + 1; /* and destination */
while ((q < bx) && (*q++ = *pa++)) ; /* Copy prefix */
q--; /* Back up over NUL */
while ((q < bx) && (*q++ = *p++)) ; /* Copy filename */
q--; /* Back up over NUL */
while ((q < bx) && (*q++ = *px++)) ; /* Copy suffix */
if (j < cols - 1) {
q--;
*q = SP; /* Replace the space */
}
}
}
p = b + cmd_cols - 1; /* Last char in line */
while (*p-- == SP) ; /* Trim */
*(p+2) = NUL;
printf("%s\n",b); /* Print the line */
if (++lc > (cmd_rows - 2)) { /* Screen full? */
if (!askmore()) { /* Do more-prompting... */
rc = 0;
goto xfilhelp;
} else
lc = 0;
}
}
printf("\n"); /* Blank line at end of report */
goto xfilhelp;
} else { /* Malloc failure, no columns */
for (i = 0; i < n; i++) {
znext(filbuf);
if (!filbuf[0]) break;
printf("%s%s%s\n",pre,filbuf,post);
if (++lc > (cmd_rows - 2)) { /* Screen full? */
if (!askmore()) { /* Do more-prompting... */
rc = 0;
goto xfilhelp;
} else lc = 0;
}
}
xfilhelp:
if (b) free(b);
for (i = 0; i < n2; i++)
if (s2[i]) free(s2[i]);
if (s2) free((char *)s2);
return(rc);
}
}
/* C M S E T U P -- Set up command buffers */
#ifdef DCMDBUF
int
cmsetup() {
if (!(cmdbuf = malloc(CMDBL + 4))) return(-1);
if (!(savbuf = malloc(CMDBL + 4))) return(-1);
savbuf[0] = '\0';
if (!(atmbuf = malloc(ATMBL + 4))) return(-1);
if (!(atxbuf = malloc(CMDBL + 4))) return(-1);
if (!(atybuf = malloc(ATMBL + 4))) return(-1);
if (!(filbuf = malloc(ATMBL + 4))) return(-1);
if (!(cmprom = malloc(PROMPTL + 4))) return(-1);
if (!(cmprxx = malloc(PROMPTL + 4))) return(-1);
#ifdef CK_RECALL
cmrini(cm_recall);
#endif /* CK_RECALL */
return(0);
}
#endif /* DCMDBUF */
/* C M S E T P -- Set the program prompt. */
VOID
cmsetp(s) char *s; {
if (!s) s = "";
ckstrncpy(cmprxx,s,PROMPTL);
psetf = 1; /* Flag that prompt has been set. */
}
/* C M S A V P -- Save a copy of the current prompt. */
VOID
#ifdef CK_ANSIC
cmsavp(char s[], int n)
#else
cmsavp(s,n) char s[]; int n;
#endif /* CK_ANSIC */
/* cmsavp */ {
if (psetf) /* But not if no prompt is set. */
ckstrncpy(s,cmprxx,n);
}
char *
cmgetp() {
return(cmprxx);
}
int
cmgbrk() {
return(brkchar);
}
int
cmgkwflgs() {
return(cmkwflgs);
}
/* P R O M P T -- Issue the program prompt. */
VOID
prompt(f) xx_strp f; {
char *sx, *sy; int n;
#ifdef CK_SSL
extern int ssl_active_flag, tls_active_flag;
#endif /* CK_SSL */
#ifdef OS2
extern int display_demo;
/* If there is a demo screen to be displayed, display it */
if (display_demo && xcmdsrc == 0) {
demoscrn(VCMD);
display_demo = 0;
}
#endif /* OS2 */
if (psetf == 0) /* If no prompt set, set default. */
cmsetp(dfprom);
sx = cmprxx; /* Unevaluated copy */
if (f) { /* If conversion function given */
sy = cmprom; /* Evaluate it */
debug(F101,"prompt sx","",sx);
debug(F101,"prompt sy","",sy);
n = PROMPTL;
if ((*f)(sx,&sy,&n) < 0) /* If evaluation failed */
sx = cmprxx; /* revert to unevaluated copy */
else if (!*cmprom) /* ditto if it came up empty */
sx = cmprxx;
else
sx = cmprom;
} else
ckstrncpy(cmprom,sx,PROMPTL);
cmprom[PROMPTL-1] = NUL;
if (!*sx) /* Don't print if empty */
return;
#ifdef OSK
fputs(sx, stdout);
#else
#ifdef MAC
printf("%s", sx);
#else
#ifdef IKSD
if (inserver) { /* Print the prompt. */
ttoc(CR); /* If TELNET Server */
ttoc(NUL); /* must folloW CR by NUL */
printf("%s",sx);
} else
#endif /* IKSD */
printf("\r%s",sx);
#ifdef CK_SSL
if (!(ssl_active_flag || tls_active_flag))
#endif /* CK_SSL */
fflush(stdout); /* Now! */
#endif /* MAC */
#endif /* OSK */
}
#ifndef NOSPL
VOID
pushcmd(s) char * s; { /* For use with IF command. */
if (!s) s = np;
ckstrncpy(savbuf,s,CMDBL); /* Save the dependent clause, */
cmres(); /* and clear the command buffer. */
debug(F110, "pushcmd savbuf", savbuf, 0);
}
VOID
pushqcmd(s) char * s; { /* For use with ELSE command. */
char c, * p = savbuf; /* Dest */
if (!s) s = np; /* Source */
while (*s) { /* Get first nonwhitespace char */
if (*s != SP)
break;
else
s++;
}
if (*s != '{') { /* If it's not "{" */
pushcmd(s); /* do regular pushcmd */
return;
}
while ((c = *s++)) { /* Otherwise insert quotes */
if (c == CMDQ)
*p++ = CMDQ;
*p++ = c;
}
cmres(); /* and clear the command buffer. */
debug(F110, "pushqcmd savbuf", savbuf, 0);
}
#endif /* NOSPL */
#ifdef COMMENT
/* no longer used... */
VOID
popcmd() {
ckstrncpy(cmdbuf,savbuf,CMDBL); /* Put back the saved material */
*savbuf = '\0'; /* and clear the save buffer */
cmres();
}
#endif /* COMMENT */
/* C M R E S -- Reset pointers to beginning of command buffer. */
VOID
cmres() {
inword = 0; /* We're not in a word */
cc = 0; /* Character count is zero */
/* Initialize pointers */
pp = cmdbuf; /* Beginning of current field */
bp = cmdbuf; /* Current position within buffer */
np = cmdbuf; /* Where to start next field */
cmfldflgs = 0;
cmflgs = -5; /* Parse not yet started. */
ungw = 0; /* Don't need to unget a word. */
}
/* C M I N I -- Clear the command and atom buffers, reset pointers. */
/*
The argument specifies who is to echo the user's typein --
1 means the cmd package echoes
0 somebody else (system, front end, terminal) echoes
*/
VOID
cmini(d) int d; {
#ifdef DCMDBUF
if (!atmbuf)
if (cmsetup()<0)
fatal("fatal error: unable to allocate command buffers");
#endif /* DCMDBUF */
#ifdef USE_MEMCPY
memset(cmdbuf,0,CMDBL);
memset(atmbuf,0,ATMBL);
#else
for (bp = cmdbuf; bp < cmdbuf+CMDBL; bp++) *bp = NUL;
for (bp = atmbuf; bp < atmbuf+ATMBL; bp++) *bp = NUL;
#endif /* USE_MEMCPY */
*atmbuf = *savbuf = *atxbuf = *atybuf = *filbuf = NUL;
blocklvl = 0; /* Block level is 0 */
linebegin = 1; /* At the beginning of a line */
dpx = d; /* Global copy of the echo flag */
debug(F101,"cmini dpx","",dpx);
crflag = 0; /* Reset flags */
qmflag = 0;
esflag = 0;
#ifdef CK_RECALL
no_recall = 0; /* Start out with recall enabled */
#endif /* CK_RECALL */
cmres(); /* Sets bp etc */
newcmd = 1; /* See addcmd() */
}
#ifndef NOSPL
/*
The following bits are to allow the command package to call itself
in the middle of a parse. To do this, begin by calling cmpush, and
end by calling cmpop. As you can see, this is rather expensive.
*/
#ifdef DCMDBUF
struct cmp {
int i[5]; /* stack for integers */
char *c[3]; /* stack for pointers */
char *b[8]; /* stack for buffer contents */
};
struct cmp *cmp = 0;
#else
int cmp_i[CMDDEP+1][5]; /* Stack for integers */
char *cmp_c[CMDDEP+1][5]; /* for misc pointers */
char *cmp_b[CMDDEP+1][7]; /* for buffer contents pointers */
#endif /* DCMDBUF */
int cmddep = -1; /* Current stack depth */
int
cmpush() { /* Save the command environment */
char *cp; /* Character pointer */
if (cmddep >= CMDDEP) /* Enter a new command depth */
return(-1);
cmddep++;
debug(F101,"&cmpush to depth","",cmddep);
#ifdef DCMDBUF
/* allocate memory for cmp if not already done */
if (!cmp && !(cmp = (struct cmp *) malloc(sizeof(struct cmp)*(CMDDEP+1))))
fatal("cmpush: no memory for cmp");
cmp[cmddep].i[0] = cmflgs; /* First do the global ints */
cmp[cmddep].i[1] = cmfsav;
cmp[cmddep].i[2] = atxn;
cmp[cmddep].i[3] = ungw;
cmp[cmddep].c[0] = bp; /* Then the global pointers */
cmp[cmddep].c[1] = pp;
cmp[cmddep].c[2] = np;
#else
cmp_i[cmddep][0] = cmflgs; /* First do the global ints */
cmp_i[cmddep][1] = cmfsav;
cmp_i[cmddep][2] = atxn;
cmp_i[cmddep][3] = ungw;
cmp_c[cmddep][0] = bp; /* Then the global pointers */
cmp_c[cmddep][1] = pp;
cmp_c[cmddep][2] = np;
#endif /* DCMDBUF */
/* Now the buffers themselves. A lot of repititious code... */
#ifdef DCMDBUF
cp = malloc((int)strlen(cmdbuf)+1); /* 0: Command buffer */
if (cp) strcpy(cp,cmdbuf);
cmp[cmddep].b[0] = cp;
if (cp == NULL) return(-1);
cp = malloc((int)strlen(savbuf)+1); /* 1: Save buffer */
if (cp) strcpy(cp,savbuf);
cmp[cmddep].b[1] = cp;
if (cp == NULL) return(-1);
cmp[cmddep].b[2] = NULL;
cp = malloc((int)strlen(atmbuf)+1); /* 3: Atom buffer */
if (cp) strcpy(cp,atmbuf);
cmp[cmddep].b[3] = cp;
if (cp == NULL) return(-1);
cp = malloc((int)strlen(atxbuf)+1); /* 4: Expansion buffer */
if (cp) strcpy(cp,atxbuf);
cmp[cmddep].b[4] = cp;
if (cp == NULL) return(-1);
cp = malloc((int)strlen(atybuf)+1); /* 5: Atom buffer copy */
if (cp) strcpy(cp,atybuf);
cmp[cmddep].b[5] = cp;
if (cp == NULL) return(-1);
cp = malloc((int)strlen(filbuf)+1); /* 6: File name buffer */
if (cp) strcpy(cp,filbuf);
cmp[cmddep].b[6] = cp;
if (cp == NULL) return(-1);
#else
cp = malloc((int)strlen(cmdbuf)+1); /* 0: Command buffer */
if (cp) strcpy(cp,cmdbuf);
cmp_b[cmddep][0] = cp;
if (cp == NULL) return(-1);
cp = malloc((int)strlen(savbuf)+1); /* 1: Save buffer */
if (cp) strcpy(cp,savbuf);
cmp_b[cmddep][1] = cp;
if (cp == NULL) return(-1);
cmp_b[cmddep][2] = NULL;
cp = malloc((int)strlen(atmbuf)+1); /* 3: Atom buffer */
if (cp) strcpy(cp,atmbuf);
cmp_b[cmddep][3] = cp;
if (cp == NULL) return(-1);
cp = malloc((int)strlen(atxbuf)+1); /* 4: Expansion buffer */
if (cp) strcpy(cp,atxbuf);
cmp_b[cmddep][4] = cp;
if (cp == NULL) return(-1);
cp = malloc((int)strlen(atybuf)+1); /* 5: Atom buffer copy */
if (cp) strcpy(cp,atybuf);
cmp_b[cmddep][5] = cp;
if (cp == NULL) return(-1);
cp = malloc((int)strlen(filbuf)+1); /* 6: File name buffer */
if (cp) strcpy(cp,filbuf);
cmp_b[cmddep][6] = cp;
if (cp == NULL) return(-1);
#endif /* DCMDBUF */
cmini(dpx); /* Initize the command parser */
return(0);
}
int
cmpop() { /* Restore the command environment */
if (cmddep < 0) {
debug(F100,"&cmpop called from top level","",0);
return(-1); /* Don't pop too much! */
}
#ifdef DCMDBUF
cmflgs = cmp[cmddep].i[0]; /* First do the global ints */
cmfsav = cmp[cmddep].i[1];
atxn = cmp[cmddep].i[2];
ungw = cmp[cmddep].i[3];
bp = cmp[cmddep].c[0]; /* Then the global pointers */
pp = cmp[cmddep].c[1];
np = cmp[cmddep].c[2];
#else
cmflgs = cmp_i[cmddep][0]; /* First do the global ints */
cmfsav = cmp_i[cmddep][1];
atxn = cmp_i[cmddep][2];
ungw = cmp_i[cmddep][3];
bp = cmp_c[cmddep][0]; /* Then the global pointers */
pp = cmp_c[cmddep][1];
np = cmp_c[cmddep][2];
#endif /* DCMDBUF */
/* Now the buffers themselves. */
/* Note: strncpy(), not ckstrncpy() -- Here we WANT the NUL padding... */
#ifdef DCMDBUF
if (cmp[cmddep].b[0]) {
strncpy(cmdbuf,cmp[cmddep].b[0],CMDBL); /* 0: Command buffer */
free(cmp[cmddep].b[0]);
cmp[cmddep].b[0] = NULL;
}
if (cmp[cmddep].b[1]) {
strncpy(savbuf,cmp[cmddep].b[1],CMDBL); /* 1: Save buffer */
free(cmp[cmddep].b[1]);
cmp[cmddep].b[1] = NULL;
}
if (cmp[cmddep].b[3]) {
strncpy(atmbuf,cmp[cmddep].b[3],ATMBL); /* 3: Atomic buffer! */
free(cmp[cmddep].b[3]);
cmp[cmddep].b[3] = NULL;
}
if (cmp[cmddep].b[4]) {
strncpy(atxbuf,cmp[cmddep].b[4],ATMBL); /* 4: eXpansion buffer */
free(cmp[cmddep].b[4]);
cmp[cmddep].b[4] = NULL;
}
if (cmp[cmddep].b[5]) {
strncpy(atybuf,cmp[cmddep].b[5],ATMBL); /* 5: Atom buffer copY */
free(cmp[cmddep].b[5]);
cmp[cmddep].b[5] = NULL;
}
if (cmp[cmddep].b[6]) {
strncpy(filbuf,cmp[cmddep].b[6],ATMBL); /* 6: Filename buffer */
free(cmp[cmddep].b[6]);
cmp[cmddep].b[6] = NULL;
}
#else
if (cmp_b[cmddep][0]) {
strncpy(cmdbuf,cmp_b[cmddep][0],CMDBL); /* 0: Command buffer */
free(cmp_b[cmddep][0]);
cmp_b[cmddep][0] = NULL;
}
if (cmp_b[cmddep][1]) {
strncpy(savbuf,cmp_b[cmddep][1],CMDBL); /* 1: Save buffer */
free(cmp_b[cmddep][1]);
cmp_b[cmddep][1] = NULL;
}
if (cmp_b[cmddep][3]) {
strncpy(atmbuf,cmp_b[cmddep][3],ATMBL); /* 3: Atomic buffer! */
free(cmp_b[cmddep][3]);
cmp_b[cmddep][3] = NULL;
}
if (cmp_b[cmddep][4]) {
strncpy(atxbuf,cmp_b[cmddep][4],ATMBL); /* 4: eXpansion buffer */
free(cmp_b[cmddep][4]);
cmp_b[cmddep][4] = NULL;
}
if (cmp_b[cmddep][5]) {
strncpy(atybuf,cmp_b[cmddep][5],ATMBL); /* 5: Atom buffer copY */
free(cmp_b[cmddep][5]);
cmp_b[cmddep][5] = NULL;
}
if (cmp_b[cmddep][6]) {
strncpy(filbuf,cmp_b[cmddep][6],ATMBL); /* 6: Filename buffer */
free(cmp_b[cmddep][6]);
cmp_b[cmddep][6] = NULL;
}
#endif /* DCMDBUF */
cmddep--; /* Rise, rise */
debug(F101,"&cmpop to depth","",cmddep);
return(cmddep);
}
#endif /* NOSPL */
#ifdef COMMENT
VOID /* Not used */
stripq(s) char *s; { /* Function to strip '\' quotes */
char *t;
while (*s) {
if (*s == CMDQ) {
for (t = s; *t != '\0'; t++) *t = *(t+1);
}
s++;
}
}
#endif /* COMMENT */
/* Convert tabs to spaces, one for one */
VOID
untab(s) char *s; {
while (*s) {
if (*s == HT) *s = SP;
s++;
}
}
/* C M N U M -- Parse a number in the indicated radix */
/*
The radix is specified in the arg list.
Parses unquoted numeric strings in the given radix.
Parses backslash-quoted numbers in the radix indicated by the quote:
\nnn = \dnnn = decimal, \onnn = octal, \xnn = Hexadecimal.
If these fail, then if a preprocessing function is supplied, that is applied
and then a second attempt is made to parse an unquoted decimal string.
And if that fails, the preprocessed string is passed to an arithmetic
expression evaluator.
Returns:
-3 if no input present when required,
-2 if user typed an illegal number,
-1 if reparse needed,
0 otherwise, with argument n set to the number that was parsed
*/
int
cmnum(xhlp,xdef,radix,n,f) char *xhlp, *xdef; int radix, *n; xx_strp f; {
int x; char *s, *zp, *zq;
#ifdef COMMENT
char lbrace, rbrace;
#endif /* COMMENT */
if (!xhlp) xhlp = "";
if (!xdef) xdef = "";
#ifdef COMMENT
if (cmfldflgs & 1) {
lbrace = '(';
rbrace = ')';
} else {
lbrace = '{';
rbrace = '}';
}
#endif /* COMMENT */
if (radix != 10 && radix != 8) { /* Just do bases 8 and 10 */
printf("cmnum: illegal radix - %d\n",radix);
return(-2);
} /* Easy to add others but there has never been a need for it. */
x = cmfld(xhlp,xdef,&s,(xx_strp)0);
debug(F101,"cmnum: cmfld","",x);
if (x < 0) return(x); /* Parse a field */
zp = atmbuf;
/*
Edit 192 - Allow any number field to be braced. This lets us include
spaces in expressions, but perhaps more important lets us have user-defined
functions in numeric fields.
*/
zp = brstrip(zp); /* Strip braces */
if (cmfldflgs & 1 && *zp == '(') { /* Parens too.. */
x = (int) strlen(atmbuf);
if (x > 0) {
if (*(atmbuf+x-1) == ')') {
*(atmbuf+x-1) = NUL;
zp++;
}
}
}
if (chknum(zp)) { /* Check for number */
if (radix == 8) { /* If it's supposed to be octal */
zp = ckradix(zp,8,10); /* convert to decimal */
if (!zp) return(-2);
if (!strcmp(zp,"-1")) return(-2);
}
errno = 0; /* Got one, we're done. */
*n = atoi(zp);
if (errno) {
perror(zp);
return(-9);
}
debug(F101,"cmnum 1st chknum ok","",*n);
return(0);
} else if ((x = xxesc(&zp)) > -1) { /* Check for backslash escape */
#ifndef OS2
*n = x;
#else
*n = wideresult;
#endif /* OS2 */
debug(F101,"cmnum xxesc ok","",*n);
return(*zp ? -2 : 0);
} else if (f) { /* If conversion function given */
zq = atxbuf; /* Try that */
atxn = CMDBL;
if ((*f)(zp,&zq,&atxn) < 0) /* Convert */
return(-2);
zp = atxbuf;
}
debug(F110,"cmnum zp 1",zp,0);
if (!*zp) zp = xdef; /* Result empty, substitute default */
debug(F110,"cmnum zp 2",zp,0);
if (chknum(zp)) { /* Check again for decimal number */
if (radix == 8) { /* If it's supposed to be octal */
zp = ckradix(zp,8,10); /* convert to decimal */
if (!zp) return(-2);
if (!strcmp(zp,"-1")) return(-2);
}
errno = 0;
*n = atoi(zp);
if (errno) {
perror(zp);
return(-9);
}
debug(F101,"cmnum 2nd chknum ok","",*n);
return(0);
#ifndef NOSPL
} else if ((x = xxesc(&zp)) > -1) { /* Check for backslash escape */
#ifndef OS2
*n = x;
#else
*n = wideresult;
#endif /* OS2 */
debug(F101,"cmnum xxesc 2 ok","",*n);
return(*zp ? -2 : 0);
} else if (f) { /* Not numeric, maybe an expression */
char * p;
p = evala(zp);
if (chknum(p)) {
if (radix == 8) { /* If it's supposed to be octal */
zp = ckradix(zp,8,10); /* convert to decimal */
if (!zp) return(-2);
if (!strcmp(zp,"-1")) return(-2);
}
errno = 0;
*n = atoi(p);
if (errno) {
perror(p);
return(-9);
}
debug(F101,"cmnum exp eval ok","",*n);
return(0);
} else return(-2);
#endif /* NOSPL */
} else { /* Not numeric */
return(-2);
}
}
#ifdef CKCHANNELIO
extern int z_error;
#endif /* CKCHANNELIO */
/* C M O F I -- Parse the name of an output file */
/*
Depends on the external function zchko(); if zchko() not available, use
cmfld() to parse output file names.
Returns:
-9 like -2, except message already printed,
-3 if no input present when required,
-2 if permission would be denied to create the file,
-1 if reparse needed,
0 or 1 if file can be created, with xp pointing to name.
2 if given the name of an existing directory.
*/
int
cmofi(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; {
int x; char *s, *zq;
#ifdef DOCHKVAR
int tries;
#endif /* DOCHKVAR */
#ifdef DTILDE
char *dirp;
#endif /* DTILDE */
cmfldflgs = 0;
if (!xhlp) xhlp = "";
if (!xdef) xdef = "";
if (*xhlp == NUL) xhlp = "Output file";
*xp = "";
debug(F110,"cmofi xdef",xdef,0);
x = cmfld(xhlp,xdef,&s,(xx_strp)0);
debug(F111,"cmofi cmfld returns",s,x);
if (x < 0)
return(x);
s = brstrip(s); /* Strip enclosing braces */
debug(F110,"cmofi 1.5",s,0);
#ifdef DOCHKVAR
tries = 0;
{
char *p = s;
/*
This is really ugly. If we skip conversion the first time through,
then variable names like \%a will be used as filenames (e.g. creating
a file called %A in the root directory). If we DON'T skip conversion
the first time through, then single backslashes used as directory
separators in filenames will be misinterpreted as variable lead-ins.
So we prescan to see if it has any variable references. But this
module is not supposed to know anything about variables, functions,
etc, so this code does not really belong here, but rather it should
be at the same level as zzstring().
*/
/*
Hmmm, this looks a lot like chkvar() except it that includes \nnn number
escapes. But why? This makes commands like "mkdir c:\123" impossible.
And in fact, "mkdir c:\123" creates a directory called "c:{". What's worse,
rmdir(), which *does* call chkvar(), won't let us remove it. So let's at
least try making cmofi() symmetrical with cmifi()...
*/
#ifdef COMMENT
char * q;
while ( (tries == 0) && (p = strchr(p,CMDQ)) ) {
q = *(p+1); /* Char after backslash */
if (!q) /* None, quit */
break;
if (isupper(q)) /* If letter, convert to lowercase */
q = tolower(q);
if (isdigit(q)) { /* If it's a digit, */
tries = 1; /* assume it's a backslash code */
break;
}
switch (q) {
case CMDQ: /* Double backslash */
tries = 1; /* so call the conversion function */
break;
case '%': /* Variable or array reference */
case '&': /* must be followed by letter */
if (isalpha(*(p+2)) || (*(p+2) >= '0' && *(p+2) <= '9'))
tries = 1;
break;
case 'm': case 'v': case '$': /* \m(), \v(), \$() */
if (*(p+2) == '(')
if (strchr(p+2,')'))
tries = 1;
break;
case 'f': /* \Fname() */
if (strchr(p+2,'('))
if (strchr(p+2,')'))
tries = 1;
break;
case '{': /* \{...} */
if (strchr(p+2,'}'))
tries = 1;
break;
case 'd': case 'o': /* Decimal or Octal number */
if (isdigit(*(p+2)))
tries = 1;
break;
case 'x': /* Hex number */
if (isdigit(*(p+2)) ||
((*(p+2) >= 'a' && *(p+2) <= 'f') ||
((*(p+2) >= 'A' && *(p+2) <= 'F'))))
tries = 1;
default:
break;
}
p++;
}
#else
#ifndef NOSPL
if (f) { /* If a conversion function is given */
char *s = p; /* See if there are any variables in */
while (*s) { /* the string and if so, expand them */
if (chkvar(s)) {
tries = 1;
break;
}
s++;
}
}
#endif /* NOSPL */
#endif /* COMMENT */
}
#ifdef OS2
o_again:
#endif /* OS2 */
if (tries == 1)
#endif /* DOCHKVAR */
if (f) { /* If a conversion function is given */
zq = atxbuf; /* do the conversion. */
atxn = CMDBL;
if ((x = (*f)(s,&zq,&atxn)) < 0)
return(-2);
s = atxbuf;
if (!*s) /* Result empty, substitute default */
s = xdef;
}
debug(F111,"cmofi 2",s,x);
#ifdef DTILDE
dirp = tilde_expand(s); /* Expand tilde, if any, */
if (*dirp != '\0') { /* right in the atom buffer. */
if (setatm(dirp,1) < 0) {
printf("?Name too long\n");
return(-9);
}
}
s = atmbuf;
debug(F110,"cmofi 3",s,0);
#endif /* DTILDE */
if (iswild(s)) {
printf("?Wildcards not allowed - %s\n",s);
return(-2);
}
debug(F110,"cmofi 4",s,0);
#ifdef CK_TMPDIR
/* isdir() function required for this! */
if (isdir(s)) {
debug(F110,"cmofi 5: is directory",s,0);
*xp = s;
return(2);
}
#endif /* CK_TMPDIR */
if (strcmp(s,CTTNAM) && (zchko(s) < 0)) { /* OK to write to console */
#ifdef COMMENT
#ifdef OS2
/*
We don't try again because we already prescanned the string to see if
if it contained anything that could be used by zzstring().
*/
if (tries++ < 1)
goto o_again;
#endif /* OS2 */
#endif /* COMMENT */
/*
Note: there are certain circumstances where zchko() can give a false
positive, so don't rely on it to catch every conceivable situation in
which the given output file can't be created. In other words, we print
a message and fail here if we KNOW the file can't be created. If we
succeed but the file can't be opened, the code that tries to open the file
has to print a message.
*/
debug(F110,"cmofi 6: failure",s,0);
#ifdef CKROOT
if (ckrooterr)
printf("?Off Limits: %s\n",s);
else
#endif /* CKROOT */
printf("?Write permission denied - %s\n",s);
#ifdef CKCHANNELIO
z_error = FX_ACC;
#endif /* CKCHANNELIO */
return(-9);
} else {
debug(F110,"cmofi 7: ok",s,0);
*xp = s;
return(x);
}
}
/* C M I F I -- Parse the name of an existing file */
/*
This function depends on the external functions:
zchki() - Check if input file exists and is readable.
zxpand() - Expand a wild file specification into a list.
znext() - Return next file name from list.
If these functions aren't available, then use cmfld() to parse filenames.
*/
/*
Returns
-4 EOF
-3 if no input present when required,
-2 if file does not exist or is not readable,
-1 if reparse needed,
0 or 1 otherwise, with:
xp pointing to name,
wild = 1 if name contains '*' or '?', 0 otherwise.
*/
/*
C M I O F I -- Parse an input file OR the name of a nonexistent file.
Use this when an existing file is wanted (so we get help, completion, etc),
but if a file of the given name does not exist, the name of a new file is
accepted. For example, with the EDIT command (edit an existing file, or
create a new file). Returns -9 if file does not exist. It is up to the
caller to check creatability.
*/
static int nomsg = 0;
int
cmiofi(xhlp,xdef,xp,wild,f) char *xhlp, *xdef, **xp; int *wild; xx_strp f; {
int msgsave, x;
msgsave = nomsg;
nomsg = 1;
x = cmifi2(xhlp,xdef,xp,wild,0,NULL,f,0);
nomsg = msgsave;
return(x);
}
int
cmifi(xhlp,xdef,xp,wild,f) char *xhlp, *xdef, **xp; int *wild; xx_strp f; {
return(cmifi2(xhlp,xdef,xp,wild,0,NULL,f,0));
}
/*
cmifip() is called when we want to supply a path or path list to search
in case the filename that the user gives is (a) not absolute, and (b) can't
be found as given. The path string can be the name of a single directory,
or a list of directories separated by the PATHSEP character, defined in
ckucmd.h. Look in ckuusr.c and ckuus3.c for examples of usage.
*/
int
cmifip(xhlp,xdef,xp,wild,d,path,f)
char *xhlp,*xdef,**xp; int *wild, d; char * path; xx_strp f; {
return(cmifi2(xhlp,xdef,xp,wild,0,path,f,0));
}
/* C M D I R -- Parse a directory name */
/*
This function depends on the external functions:
isdir(s) - Check if string s is the name of a directory
zchki(s) - Check if input file s exists and what type it is.
If these functions aren't available, then use cmfld() to parse dir names.
Returns
-9 For all sorts of reasons, after printing appropriate error message.
-4 EOF
-3 if no input present when required,
-2 if out of space or other internal error,
-1 if reparse needed,
0 or 1, with xp pointing to name, if directory specified,
*/
int
cmdir(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; {
int wild;
return(cmifi2(xhlp,xdef,xp,&wild,0,NULL,f,1));
}
/* Like CMDIR but includes PATH search */
int
cmdirp(xhlp,xdef,xp,path,f) char *xhlp, *xdef, **xp; char * path; xx_strp f; {
int wild;
return(cmifi2(xhlp,xdef,xp,&wild,0,path,f,1));
}
/*
cmifi2() is the base filename parser called by cmifi, cmifip, cmdir, etc.
Use it directly when you also want to parse a directory or device
name as an input file, as in the DIRECTORY command. Call with:
xhlp -- help message on ?
xdef -- default response
xp -- pointer to result (in our space, must be copied from here)
wild -- flag set upon return to indicate if filespec was wild
d -- 0 to parse files, 1 to parse files or directories
Add 2 to inhibit following of symlinks.
path -- search path for files
f -- pointer to string processing function (e.g. to evaluate variables)
dirflg -- 1 to parse *only* directories, 0 otherwise
*/
int
cmifi2(xhlp,xdef,xp,wild,d,path,f,dirflg)
char *xhlp,*xdef,**xp; int *wild, d; char * path; xx_strp f; int dirflg; {
extern int recursive, diractive, cdactive, dblquo;
int i, x, itsadir, xc, expanded = 0, nfiles = 0, children = -1;
int qflag = 0;
long y;
char *sp = NULL, *zq, *np = NULL;
char *sv = NULL, *p = NULL;
#ifdef DTILDE
char *dirp;
#endif /* DTILDE */
#ifndef NOPARTIAL
#ifndef OS2
#ifdef OSK
/* This large array is dynamic for OS-9 -- should do for others too... */
extern char **mtchs;
#else
#ifdef UNIX
/* OK, for UNIX too */
extern char **mtchs;
#else
#ifdef VMS
extern char **mtchs;
#else
extern char *mtchs[];
#endif /* VMS */
#endif /* UNIX */
#endif /* OSK */
#endif /* OS2 */
#endif /* NOPARTIAL */
if (!xhlp) xhlp = "";
if (!xdef) xdef = "";
nzxopts = 0; /* zxpand() options */
debug(F101,"cmifi d","",d);
if (d & 2) { /* d & 2 means don't follow symlinks */
d ^= 2;
nzxopts = ZX_NOLINKS;
}
debug(F101,"cmifi nzxopts","",nzxopts);
cmfldflgs = 0;
if (path)
if (!*path)
path = NULL;
if (path) { /* Make a copy we can poke */
x = strlen(path);
np = (char *) malloc(x + 1);
if (np) {
strcpy(np, path);
path = sp = np;
}
}
debug(F110,"cmifi2 path",path,0);
ckstrncpy(cmdefault,xdef,CMDEFAULT); /* Copy default */
xdef = cmdefault;
inword = 0; /* Initialize counts & pointers */
cc = 0;
xc = 0;
*xp = ""; /* Pointer to result string */
if ((x = cmflgs) != 1) { /* Already confirmed? */
#ifdef BS_DIRSEP
dirnamflg = 1;
x = gtword(0); /* No, get a word */
dirnamflg = 0;
#else
x = gtword(0); /* No, get a word */
#endif /* BS_DIRSEP */
} else { /* If so, use default, if any. */
if (setatm(xdef,1) < 0) {
printf("?Default name too long\n");
if (np) free(np);
return(-9);
}
}
i_path:
*xp = atmbuf; /* Point to result. */
while (1) {
xc += cc; /* Count this character. */
debug(F111,"cmifi gtword",atmbuf,xc);
debug(F101,"cmifi switch x","",x);
switch (x) { /* x = gtword() return code */
case -10:
if (gtimer() > timelimit) {
#ifdef IKSD
if (inserver) {
printf("\r\nIKSD IDLE TIMEOUT: %d sec\r\n", timelimit);
doexit(GOOD_EXIT,0);
}
#endif /* IKSD */
/* if (!quiet) printf("?Timed out\n"); */
return(-10);
} else {
x = gtword(0);
continue;
}
case -9:
printf("Command or field too long\n");
case -4: /* EOF */
case -2: /* Out of space. */
case -1: /* Reparse needed */
if (np) free(np);
return(x);
case 1: /* CR */
case 0: /* SP */
if (xc == 0) /* If no input... */
*xp = xdef; /* substitute the default */
*xp = brstrip(*xp); /* Strip braces */
if (**xp == NUL) { /* 12 mar 2001 */
if (np) free(np);
return(-3);
}
debug(F110,"cmifi brstrip",*xp,0);
#ifndef NOSPL
if (f) { /* If a conversion function is given */
#ifdef DOCHKVAR
char *s = *xp; /* See if there are any variables in */
int x;
while (*s) { /* the string and if so, expand them */
x = chkvar(s);
/* debug(F111,"cmifi chkvar",*xp,x); */
if (x) {
#endif /* DOCHKVAR */
zq = atxbuf;
atxn = CMDBL;
if ((*f)(*xp,&zq,&atxn) < 0) {
if (np) free(np);
return(-2);
}
*xp = atxbuf;
if (!atxbuf[0])
*xp = xdef;
#ifdef DOCHKVAR
break;
}
s++;
}
#endif /* DOCHKVAR */
}
#endif /* NOSPL */
if (**xp == NUL) { /* 12 mar 2001 */
if (np) free(np);
return(-3);
}
#ifdef DTILDE
if (dirflg) {
dirp = tilde_expand(*xp); /* Expand tilde, if any, */
if (*dirp != '\0') { /* in the atom buffer. */
if (setatm(dirp,1) < 0) {
printf("Expanded name too long\n");
if (np) free(np);
return(-9);
}
}
*xp = atmbuf;
debug(F110,"cmifi tilde_expand",*xp,0);
}
#endif /* DTILDE */
if (!sv) { /* Only do this once */
sv = malloc((int)strlen(*xp)+1); /* Make a safe copy */
if (!sv) {
printf("?cmifi: malloc error\n");
if (np) free(np);
return(-9);
}
strcpy(sv,*xp);
debug(F110,"cmifi sv",sv,0);
}
/* This is to get around "cd /" failing because "too many directories match" */
expanded = 0; /* Didn't call zxpand */
#ifdef datageneral
debug(F110,"cmifi isdir 1",*xp,0);
{
int y; char *s;
s = *xp;
y = strlen(s);
if (y > 1 &&
(s[y-1] == ':' ||
s[y-1] == '^' ||
s[y-1] == '=')
)
s[y-1] = NUL;
}
debug(F110,"cmifi isdir 2",*xp,0);
#endif /* datageneral */
#ifdef VMS
if (dirflg) {
if (!strcmp(*xp,"..")) { /* For UNIXers... */
setatm("-",0);
*xp = atmbuf;
} else if (!strcmp(*xp,".")) {
setatm("[]",0);
*xp = atmbuf;
}
}
#endif /* VMS */
itsadir = isdir(*xp); /* Is it a directory? */
debug(F111,"cmifi itsadir",*xp,itsadir);
#ifdef VMS
/* If they said "blah" where "blah.dir" is a directory... */
/* change it to [.blah]. */
if (!itsadir) {
char tmpbuf[600];
int flag = 0; char c, * p;
p = *xp;
while ((c = *p++) && !flag)
if (ckstrchr(".[]:*?<>",c))
flag = 1;
debug(F111,"cmifi VMS dirname flag",*xp,flag);
if (!flag) {
ckmakmsg(tmpbuf,TMPBUFSIZ,"[.",*xp,"]",NULL);
itsadir = isdir(tmpbuf);
if (itsadir) {
setatm(tmpbuf,0);
*xp = atmbuf;
}
debug(F111,"cmifi VMS dirname flag itsadir",*xp,itsadir);
}
} else if (itsadir == 1 && *(xp[0]) == '.' && *(xp[1])) {
char *p;
if (p = malloc(cc + 4)) {
ckmakmsg(p,cc+4,"[",*xp,"]",NULL);
setatm(p,0);
*xp = atmbuf;
debug(F110,"cmdir .foo",*xp,0);
free(p);
}
} else if (itsadir == 2 && !diractive) {
int x; /* [FOO]BAR.DIR instead of [FOO.BAR] */
char *p;
p = malloc(cc + 4);
if (p) {
x = cvtdir(*xp,p,ATMBL); /* Convert to [FOO.BAR] */
if (x > 0) {
setatm(p,0);
*xp = atmbuf;
debug(F110,"cmdir cvtdir",*xp,0);
}
free(p);
}
}
#endif /* VMS */
debug(F101,"cmifi dirflg","",dirflg);
if (dirflg) { /* Parsing a directory name? */
/* Yes, does it contain wildcards? */
if (iswild(*xp) ||
(diractive && (!strcmp(*xp,".") || !strcmp(*xp,"..")))
) {
nzxopts |= ZX_DIRONLY; /* Match only directory names */
if (matchdot) nzxopts |= ZX_MATCHDOT;
if (recursive) nzxopts |= ZX_RECURSE;
debug(F111,"cmifi nzxopts 2",*xp,nzxopts);
y = nzxpand(*xp,nzxopts);
debug(F111,"cmifi nzxpand 2",*xp,y);
nfiles = y;
expanded = 1;
} else {
#ifdef VMS
/*
This is to allow (e.g.) "cd foo", where FOO.DIR;1 is in the
current directory.
*/
debug(F111,"cmdir itsadir",*xp,itsadir);
if (!itsadir) {
char *s;
int n;
s = *xp;
n = strlen(s);
if (n > 0 &&
#ifdef COMMENT
*s != '[' && s[n-1] != ']' &&
*s != '<' && s[n-1] != '>' &&
#else
ckindex("[",s,0,0,1) == 0 &&
ckindex("<",s,0,0,1) == 0 &&
#endif /* COMMENT */
s[n-1] != ':') {
char * dirbuf = NULL;
dirbuf = (char *)malloc(n+4);
if (dirbuf) {
if (*s == '.')
ckmakmsg(dirbuf,n+4,"[",s,"]",NULL);
else
ckmakmsg(dirbuf,n+4,"[.",s,"]",NULL);
itsadir = isdir(dirbuf);
debug(F111,"cmdir dirbuf",dirbuf,itsadir);
if (itsadir) {
setatm(dirbuf,0);
*xp = atmbuf;
debug(F110,"cmdir new *xp",*xp,0);
}
free(dirbuf);
}
/* This is to allow CDPATH to work in VMS... */
} else if (n > 0) {
char * p; int i, j, k, d;
char rb[2] = "]";
if (p = malloc(x + 8)) {
ckstrncpy(p,*xp,x+8);
i = ckindex(".",p,-1,1,1);
d = ckindex(".dir",p,0,0,0);
j = ckindex("]",p,-1,1,1);
if (j == 0) {
j = ckindex(">",p,-1,1,1);
rb[0] = '>';
}
k = ckindex(":",p,-1,1,1);
if (i < j || i < k) i = 0;
if (d < j || d < k) d = 0;
/* Change [FOO]BAR or [FOO]BAR.DIR */
/* to [FOO.BAR] */
if (j > 0 && j < n) {
p[j-1] = '.';
if (d > 0) p[d-1] = NUL;
ckstrncat(p,rb,x+8);
debug(F110,"cmdir xxx",p,0);
}
itsadir = isdir(p);
debug(F111,"cmdir p",p,itsadir);
if (itsadir) {
setatm(p,0);
*xp = atmbuf;
debug(F110,"cmdir new *xp",*xp,0);
}
free(p);
}
}
}
#endif /* VMS */
y = (!itsadir) ? 0 : 1;
debug(F111,"cmifi y itsadir",*xp,y);
}
} else { /* Parsing a filename. */
debug(F110,"cmifi *xp pre-zxpand",*xp,0);
#ifndef COMMENT
nzxopts |= (d == 0) ? ZX_FILONLY : 0; /* So always expand. */
if (matchdot) nzxopts |= ZX_MATCHDOT;
if (recursive) nzxopts |= ZX_RECURSE;
y = nzxpand(*xp,nzxopts);
#else
/* Here we're trying to fix a problem in which a directory name is accepted */
/* as a filename, but this breaks too many other things. */
/* nzxopts = 0; */
if (!d) {
if (itsadir & !iswild(*xp)) {
debug(F100,"cmifi dir when filonly","",0);
printf("?Not a regular file: \"%s\"\n",*xp);
if (sv) free(sv);
if (np) free(np);
return(-9);
} else {
nzxopts |= ZX_FILONLY;
if (matchdot) nzxopts |= ZX_MATCHDOT;
if (recursive) nzxopts |= ZX_RECURSE;
y = nzxpand(*xp,nzxopts);
}
}
#endif /* COMMENT */
nfiles = y;
debug(F111,"cmifi y nzxpand",*xp,y);
debug(F111,"cmifi y atmbuf",atmbuf,itsadir);
expanded = 1;
}
/* domydir() calls zxrewind() so we MUST call nzxpand() here */
if (!expanded && diractive) {
debug(F110,"cmifi diractive catch-all zxpand",*xp,0);
nzxopts |= (d == 0) ? ZX_FILONLY : (dirflg ? ZX_DIRONLY : 0);
if (matchdot) nzxopts |= ZX_MATCHDOT;
if (recursive) nzxopts |= ZX_RECURSE;
y = nzxpand(*xp,nzxopts);
debug(F111,"cmifi diractive nzxpand",*xp,y);
nfiles = y;
expanded = 1;
}
*wild = (iswild(sv) || (y > 1)) && (itsadir == 0);
#ifdef RECURSIVE
if (!*wild) *wild = recursive;
#endif /* RECURSIVE */
debug(F111,"cmifi sv wild",sv,*wild);
debug(F101,"cmifi y","",y);
if (dirflg && *wild && cdactive) {
if (y > 1) {
printf("?Wildcard matches more than one directory\n");
if (sv) free(sv);
if (np) free(np);
return(-9);
} else {
znext(*xp);
}
}
if (itsadir && d && !dirflg) { /* It's a directory and not wild */
if (sv) free(sv); /* and it's ok to parse directories */
if (np) free(np);
return(x);
}
if (y == 0) { /* File was not found */
int dosearch = 0;
dosearch = (path != NULL); /* A search path was given */
if (dosearch) {
dosearch = hasnopath(sv); /* Filename includes no path */
debug(F111,"cmifip hasnopath",sv,dosearch);
}
if (dosearch) { /* Search the path... */
char * ptr = path;
char c;
while (1) {
c = *ptr;
if (c == PATHSEP || c == NUL) {
if (!*path) {
path = NULL;
break;
}
*ptr = NUL;
#ifdef UNIX
/* By definition of CDPATH, an empty member denotes the current directory */
if (!*path)
ckstrncpy(atmbuf,".",ATMBL);
else
#endif /* UNIX */
ckstrncpy(atmbuf,path,ATMBL);
#ifdef VMS
atmbuf[ATMBL] = NUL;
/* If we have a logical name, evaluate it recursively */
if (*(ptr-1) == ':') { /* Logical name ends in : */
char *p; int n;
while (((n = strlen(atmbuf)) > 0) &&
atmbuf[n-1] == ':') {
atmbuf[n-1] = NUL;
for (p = atmbuf; *p; p++)
if (islower(*p)) *p = toupper(*p);
debug(F111,"cmdir CDPATH LN 1",atmbuf,n);
p = getenv(atmbuf);
debug(F110,"cmdir CDPATH LN 2",p,0);
if (!p)
break;
strncpy(atmbuf,p,ATMBL);
atmbuf[ATMBL] = NUL;
}
}
#else
#ifdef OS2
if (*(ptr-1) != '\\' && *(ptr-1) != '/')
ckstrncat(atmbuf,"\\",ATMBL);
#else
#ifdef UNIX
if (*(ptr-1) != '/')
ckstrncat(atmbuf,"/",ATMBL);
#else
#ifdef datageneral
if (*(ptr-1) != ':')
ckstrncat(atmbuf,":",ATMBL);
#endif /* datageneral */
#endif /* UNIX */
#endif /* OS2 */
#endif /* VMS */
ckstrncat(atmbuf,sv,ATMBL);
debug(F110,"cmifip add path",atmbuf,0);
if (c == PATHSEP) ptr++;
path = ptr;
break;
}
ptr++;
}
x = 1;
inword = 0;
cc = 0;
xc = (int) strlen(atmbuf);
*xp = "";
goto i_path;
}
if (d) {
if (sv) free(sv);
if (np) free(np);
return(-2);
} else {
if (!nomsg) {
#ifdef CKROOT
if (ckrooterr)
printf("?Off Limits: %s\n",sv);
else
#endif /* CKROOT */
printf("?No %s match - %s\n",
dirflg ? "directories" : "files", sv);
}
if (sv) free(sv);
if (np) free(np);
return(-9);
}
} else if (y < 0) {
#ifdef CKROOT
if (ckrooterr)
printf("?Off Limits: %s\n",sv);
else
#endif /* CKROOT */
printf("?Too many %s match - %s\n",
dirflg ? "directories" : "files", sv);
if (sv) free(sv);
if (np) free(np);
return(-9);
} else if (*wild || y > 1) {
if (sv) free(sv);
if (np) free(np);
return(x);
}
/* If not wild, see if it exists and is readable. */
debug(F111,"cmifi sv not wild",sv,*wild);
if (expanded)
znext(*xp); /* Get first (only?) matching file */
if (dirflg) /* Maybe wild and expanded */
itsadir = isdir(*xp); /* so do this again. */
y = dirflg ? itsadir : zchki(*xp); /* Now check accessibility */
if (expanded) {
#ifdef ZXREWIND
nfiles = zxrewind(); /* Rewind so next znext() gets 1st */
#else
nzxopts |= dirflg ? ZX_DIRONLY : 0;
if (matchdot) nzxopts |= ZX_MATCHDOT;
if (recursive) nzxopts |= ZX_RECURSE;
nfiles = nzxpand(*xp,nzxopts);
#endif /* ZXREWIND */
}
debug(F111,"cmifi nfiles",*xp,nfiles);
free(sv); /* done with this */
sv = NULL;
if (dirflg && y == 0) {
printf("?Not a directory - %s\n",*xp);
#ifdef CKCHANNELIO
z_error = FX_ACC;
#endif /* CKCHANNELIO */
return(-9);
} else if (y == -3) {
if (!xcmfdb) {
if (diractive)
/* Don't show filename if we're not allowed to see it */
printf("?Read permission denied\n");
else
printf("?Read permission denied - %s\n",*xp);
}
if (np) free(np);
#ifdef CKCHANNELIO
z_error = FX_ACC;
#endif /* CKCHANNELIO */
return(xcmfdb ? -6 : -9);
} else if (y == -2) {
if (!recursive) {
if (np) free(np);
if (d) return(0);
if (!xcmfdb)
printf("?File not readable - %s\n",*xp);
#ifdef CKCHANNELIO
z_error = FX_ACC;
#endif /* CKCHANNELIO */
return(xcmfdb ? -6 : -9);
}
} else if (y < 0) {
if (np) free(np);
if (!nomsg && !xcmfdb)
printf("?File not found - %s\n",*xp);
#ifdef CKCHANNELIO
z_error = FX_FNF;
#endif /* CKCHANNELIO */
return(xcmfdb ? -6 : -9);
}
if (np) free(np);
return(x);
#ifndef MAC
case 2: /* ESC */
debug(F101,"cmifi esc, xc","",xc);
if (xc == 0) {
if (*xdef) {
printf("%s ",xdef); /* If at beginning of field */
#ifdef GEMDOS
fflush(stdout);
#endif /* GEMDOS */
inword = cmflgs = 0;
addbuf(xdef); /* Supply default. */
if (setatm(xdef,0) < 0) {
printf("Default name too long\n");
if (np) free(np);
return(-9);
}
} else { /* No default */
bleep(BP_WARN);
}
break;
}
if (**xp == '{') { /* Did user type opening brace... */
*xp = *xp + 1;
xc--;
cc--;
qflag = '}';
} else if (dblquo && **xp == '"') { /* or doublequote? */
*xp = *xp + 1; /* If so ignore it and space past it */
xc--;
cc--;
qflag = '"';
}
#ifndef NOSPL
if (f) { /* If a conversion function is given */
#ifdef DOCHKVAR
char *s = *xp; /* See if there are any variables in */
while (*s) { /* the string and if so, expand it. */
if (chkvar(s)) {
#endif /* DOCHKVAR */
zq = atxbuf;
atxn = CMDBL;
if ((x = (*f)(*xp,&zq,&atxn)) < 0) {
if (np) free(np);
return(-2);
}
#ifdef DOCHKVAR
/* reduce cc by number of \\ consumed by conversion */
/* function (needed for OS/2, where \ is path separator) */
cc -= (strlen(*xp) - strlen(atxbuf));
#endif /* DOCHKVAR */
*xp = atxbuf;
if (!atxbuf[0]) { /* Result empty, use default */
*xp = xdef;
cc = strlen(xdef);
}
#ifdef DOCHKVAR
break;
}
s++;
}
#endif /* DOCHKVAR */
}
#endif /* NOSPL */
#ifdef DTILDE
if (dirflg && *(*xp) == '~') {
debug(F111,"cmifi tilde_expand A",*xp,cc);
dirp = tilde_expand(*xp); /* Expand tilde, if any... */
if (!dirp) dirp = "";
if (*dirp) {
int i, xx;
char * sp;
xc = cc; /* Length of ~thing */
xx = setatm(dirp,0); /* Copy expansion to atom buffer */
debug(F111,"cmifi tilde_expand B",atmbuf,cc);
if (xx < 0) {
printf("Expanded name too long\n");
if (np) free(np);
return(-9);
}
debug(F111,"cmifi tilde_expand xc","",xc);
for (i = 0; i < xc; i++) {
cmdchardel(); /* Back up over ~thing */
bp--;
}
xc = cc; /* How many new ones we just got */
sp = atmbuf;
printf("%s",sp); /* Print them */
while ((*bp++ = *sp++)) ; /* Copy to command buffer */
bp--; /* Back up over NUL */
}
*xp = atmbuf;
}
#endif /* DTILDE */
sp = *xp + cc;
#ifdef UNIXOROSK
if (!strcmp(atmbuf,"..")) {
printf(" ");
ckstrncat(cmdbuf," ",CMDBL);
cc++;
bp++;
*wild = 0;
*xp = atmbuf;
break;
} else if (!strcmp(atmbuf,".")) {
bleep(BP_WARN);
if (np) free(np);
return(-1);
} else {
/* This patches a glitch when user types "./foo<ESC>" */
/* in which the next two chars are omitted from the */
/* expansion. There should be a better fix, however, */
/* since there is no problem with "../foo<ESC>". */
char *p = *xp;
if (*p == '.' && *(p+1) == '/')
cc -= 2;
}
#endif /* UNIXOROSK */
#ifdef datageneral
*sp++ = '+'; /* Data General AOS wildcard */
#else
*sp++ = '*'; /* Others */
#endif /* datageneral */
*sp-- = '\0';
#ifdef GEMDOS
if (!strchr(*xp, '.')) /* abde.e -> abcde.e* */
strcat(*xp, ".*"); /* abc -> abc*.* */
#endif /* GEMDOS */
/* Add wildcard and expand list. */
#ifdef COMMENT
/* This kills partial completion when ESC given in path segment */
nzxopts |= dirflg ? ZX_DIRONLY : (d ? 0 : ZX_FILONLY);
#else
/* nzxopts = 0; */
#endif /* COMMENT */
if (matchdot) nzxopts |= ZX_MATCHDOT;
if (recursive) nzxopts |= ZX_RECURSE;
y = nzxpand(*xp,nzxopts);
nfiles = y;
debug(F111,"cmifi nzxpand",*xp,y);
if (y > 0) {
#ifdef OS2
znext(filbuf); /* Get first */
#ifdef ZXREWIND
zxrewind(); /* Must "rewind" */
#else
nzxpand(*xp,nxzopts);
#endif /* ZXREWIND */
#else /* Not OS2 */
ckstrncpy(filbuf,mtchs[0],CKMAXPATH);
#endif /* OS2 */
} else
*filbuf = '\0';
filbuf[CKMAXPATH] = NUL;
*sp = '\0'; /* Remove wildcard. */
debug(F111,"cmifi filbuf",filbuf,y);
debug(F111,"cmifi *xp",*xp,cc);
*wild = (y > 1);
if (y == 0) {
if (!nomsg) {
#ifdef CKROOT
if (ckrooterr)
printf("?Off Limits: %s\n",atmbuf);
else
#endif /* CKROOT */
printf("?No %s match - %s\n",
dirflg ? "directories" : "files", atmbuf);
if (np) free(np);
return(-9);
} else {
bleep(BP_WARN);
if (np) free(np);
return(-1);
}
} else if (y < 0) {
#ifdef CKROOT
if (ckrooterr)
printf("?Off Limits: %s\n",atmbuf);
else
#endif /* CKROOT */
printf("?Too many %s match - %s\n",
dirflg ? "directories" : "files", atmbuf);
if (np) free(np);
return(-9);
} else if (y > 1 /* Not unique */
#ifndef VMS
|| (y == 1 && isdir(filbuf)) /* Unique directory */
#endif /* VMS */
) {
#ifndef NOPARTIAL
/* Partial filename completion */
int j, k; char c;
k = 0;
debug(F111,"cmifi partial",filbuf,cc);
#ifdef OS2
{
int cur = 0,
len = 0,
len2 = 0,
min = strlen(filbuf),
found = 0;
char localfn[CKMAXPATH+1];
len = min;
for (j = 1; j <= y; j++) {
znext(localfn);
if (dirflg && !isdir(localfn))
continue;
found = 1;
len2 = strlen(localfn);
for (cur = cc;
cur < len && cur < len2 && cur <= min;
cur++
) {
/* OS/2 or Windows, case doesn't matter */
if (tolower(filbuf[cur]) != tolower(localfn[cur]))
break;
}
if (cur < min)
min = cur;
}
if (!found)
min = cc;
filbuf[min] = NUL;
if (min > cc)
k++;
}
#else /* OS2 */
for (i = cc; (c = filbuf[i]); i++) {
for (j = 1; j < y; j++)
if (mtchs[j][i] != c) break;
if (j == y) k++;
else filbuf[i] = filbuf[i+1] = NUL;
}
#endif /* OS2 */
#ifndef VMS
/* isdir() function required for this! */
if (y == 1 && isdir(filbuf)) { /* Dont we already know this? */
int len;
len = strlen(filbuf);
if (len > 0 && len < ATMBL - 1) {
if (filbuf[len-1] != dirsep) {
filbuf[len] = dirsep;
filbuf[len+1] = NUL;
}
}
/*
At this point, before just doing partial completion, we should look first to
see if the given directory does indeed have any subdirectories (dirflg) or
files (!dirflg); if it doesn't we should do full completion. Otherwise, the
result looks funny to the user and "?" blows up the command for no good
reason.
*/
{
int flags = 0;
filbuf[len+1] = '*';
filbuf[len+2] = NUL;
if (dirflg) flags = ZX_DIRONLY;
children = nzxpand(filbuf,flags);
debug(F111,"cmifi children",filbuf,children);
filbuf[len+1] = NUL;
nzxpand(filbuf,flags); /* Restore previous list */
if (children == 0)
goto NOSUBDIRS;
}
if (len + 1 > cc)
k++;
}
/* Add doublequotes if there are spaces in the name */
{
int x;
if (qflag) {
x = (qflag == '}'); /* (or braces) */
} else {
x = !dblquo;
}
if (filbuf[0] != '"' && filbuf[0] != '{')
k = dquote(filbuf,ATMBL,x);
}
#endif /* VMS */
debug(F111,"cmifi REPAINT filbuf",filbuf,k);
if (k > 0) { /* Got more characters */
debug(F101,"cmifi REPAINT cc","",cc);
debug(F101,"cmifi REPAINT xc","",xc);
debug(F110,"cmifi REPAINT bp-cc",bp-cc,0);
debug(F110,"cmifi REPAINT bp-xc",bp-xc,0);
sp = filbuf + cc; /* Point to new ones */
if (qflag || strncmp(filbuf,bp-cc,cc)) { /* Repaint? */
int x;
x = cc;
if (qflag) x++;
for (i = 0; i < x; i++) {
cmdchardel(); /* Back up over old partial spec */
bp--;
}
sp = filbuf; /* Point to new word start */
debug(F110,"cmifi erase ok",sp,0);
}
cc = k; /* How many new ones we just got */
printf("%s",sp); /* Print them */
while ((*bp++ = *sp++)) ; /* Copy to command buffer */
bp--; /* Back up over NUL */
debug(F110,"cmifi partial cmdbuf",cmdbuf,0);
if (setatm(filbuf,0) < 0) {
printf("?Partial name too long\n");
if (np) free(np);
return(-9);
}
debug(F111,"cmifi partial atmbuf",atmbuf,cc);
*xp = atmbuf;
}
#endif /* NOPARTIAL */
bleep(BP_WARN);
} else { /* Unique, complete it. */
#ifndef VMS
#ifdef CK_TMPDIR
/* isdir() function required for this! */
NOSUBDIRS:
debug(F111,"cmifi unique",filbuf,children);
if (isdir(filbuf) && children > 0) {
int len;
len = strlen(filbuf);
if (len > 0 && len < ATMBL - 1) {
if (filbuf[len-1] != dirsep) {
filbuf[len] = dirsep;
filbuf[len+1] = NUL;
}
}
sp = filbuf + cc;
bleep(BP_WARN);
printf("%s",sp);
cc++;
while ((*bp++ = *sp++)) ;
bp--;
if (setatm(filbuf,0) < 0) {
printf("?Directory name too long\n");
if (np) free(np);
return(-9);
}
debug(F111,"cmifi directory atmbuf",atmbuf,cc);
*xp = atmbuf;
} else { /* Not a directory or dirflg */
#endif /* CK_TMPDIR */
#endif /* VMS */
#ifndef VMS /* VMS dir names are special */
#ifndef datageneral /* VS dirnames must not end in ":" */
if (dirflg) {
int len;
len = strlen(filbuf);
if (len > 0 && len < ATMBL - 1) {
if (filbuf[len-1] != dirsep) {
filbuf[len] = dirsep;
filbuf[len+1] = NUL;
}
}
}
#endif /* datageneral */
#endif /* VMS */
sp = filbuf + cc; /* Point past what user typed. */
{
int x;
if (qflag) {
x = (qflag == '}');
} else {
x = !dblquo;
}
if (filbuf[0] != '"' && filbuf[0] != '{')
dquote(filbuf,ATMBL,x);
}
if (qflag || strncmp(filbuf,bp-cc,cc)) { /* Repaint? */
int x;
x = cc;
if (qflag) x++;
for (i = 0; i < x; i++) {
cmdchardel(); /* Back up over old partial spec */
bp--;
}
sp = filbuf; /* Point to new word start */
debug(F111,"cmifi after erase sp=",sp,cc);
}
printf("%s ",sp); /* Print the completed name. */
#ifdef GEMDOS
fflush(stdout);
#endif /* GEMDOS */
addbuf(sp); /* Add the characters to cmdbuf. */
if (setatm(filbuf,0) < 0) { /* And to atmbuf. */
printf("?Completed name too long\n");
if (np) free(np);
return(-9);
}
inword = cmflgs = 0;
*xp = brstrip(atmbuf); /* Return pointer to atmbuf. */
if (dirflg && !isdir(*xp)) {
printf("?Not a directory - %s\n", filbuf);
if (np) free(np);
return(-9);
}
if (np) free(np);
return(0);
#ifndef VMS
#ifdef CK_TMPDIR
}
#endif /* CK_TMPDIR */
#endif /* VMS */
}
break;
case 3: /* Question mark - file menu wanted */
if (*xhlp == NUL)
printf(dirflg ? " Directory name" : " Input file specification");
else
printf(" %s",xhlp);
#ifdef GEMDOS
fflush(stdout);
#endif /* GEMDOS */
/* If user typed an opening quote or brace, just skip past it */
if (**xp == '"' || **xp == '{') {
*xp = *xp + 1;
xc--;
cc--;
}
#ifndef NOSPL
if (f) { /* If a conversion function is given */
#ifdef DOCHKVAR
char *s = *xp; /* See if there are any variables in */
while (*s) { /* the string and if so, expand them */
if (chkvar(s)) {
#endif /* DOCHKVAR */
zq = atxbuf;
atxn = CMDBL;
if ((x = (*f)(*xp,&zq,&atxn)) < 0) {
if (np) free(np);
return(-2);
}
#ifdef DOCHKVAR
/* reduce cc by number of \\ consumed by conversion */
/* function (needed for OS/2, where \ is path separator) */
cc -= (strlen(*xp) - strlen(atxbuf));
#endif /* DOCHKVAR */
*xp = atxbuf;
#ifdef DOCHKVAR
break;
}
s++;
}
#endif /* DOCHKVAR */
}
#endif /* NOSPL */
debug(F111,"cmifi ? *xp, cc",*xp,cc);
sp = *xp + cc; /* Insert "*" at end */
#ifdef datageneral
*sp++ = '+'; /* Insert +, the DG wild card */
#else
*sp++ = '*';
#endif /* datageneral */
*sp-- = '\0';
#ifdef GEMDOS
if (! strchr(*xp, '.')) /* abde.e -> abcde.e* */
strcat(*xp, ".*"); /* abc -> abc*.* */
#endif /* GEMDOS */
debug(F110,"cmifi ? wild",*xp,0);
nzxopts |= dirflg ? ZX_DIRONLY : (d ? 0 : ZX_FILONLY);
debug(F101,"cmifi matchdot","",matchdot);
if (matchdot) nzxopts |= ZX_MATCHDOT;
if (recursive) nzxopts |= ZX_RECURSE;
y = nzxpand(*xp,nzxopts);
nfiles = y;
*sp = '\0';
if (y == 0) {
if (nomsg) {
printf(": %s\n",atmbuf);
printf("%s%s",cmprom,cmdbuf);
fflush(stdout);
if (np) free(np);
return(-1);
} else {
#ifdef CKROOT
if (ckrooterr)
printf("?Off Limits: %s\n",atmbuf);
else
#endif /* CKROOT */
printf("?No %s match - %s\n",
dirflg ? "directories" : "files", atmbuf);
if (np) free(np);
return(-9);
}
} else if (y < 0) {
#ifdef CKROOT
if (ckrooterr)
printf("?Off Limits: %s\n",atmbuf);
else
#endif /* CKROOT */
printf("?Too many %s match - %s\n",
dirflg ? "directories" : "files", atmbuf);
if (np) free(np);
return(-9);
} else {
printf(", one of the following:\n");
if (filhelp((int)y,"","",1,dirflg) < 0) {
if (np) free(np);
return(-9);
}
}
printf("%s%s",cmprom,cmdbuf);
fflush(stdout);
break;
#endif /* MAC */
}
#ifdef BS_DIRSEP
dirnamflg = 1;
x = gtword(0); /* No, get a word */
dirnamflg = 0;
#else
x = gtword(0); /* No, get a word */
#endif /* BS_DIRSEP */
*xp = atmbuf;
}
}
/* C M F L D -- Parse an arbitrary field */
/*
Returns:
-3 if no input present when required,
-2 if field too big for buffer,
-1 if reparse needed,
0 otherwise, xp pointing to string result.
NOTE: Global flag keepallchars says whether this routine should break on CR
or LF: needed for MINPUT targets and DECLARE initializers, where we want to
keep control characters if the user specifies them (March 2003). It might
have been better to change the calling sequence but that was not practical.
*/
int
cmfld(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; {
int x, xc;
char *zq;
inword = 0; /* Initialize counts & pointers */
cc = 0;
xc = 0;
*xp = "";
debug(F110,"cmfld xdef 1",xdef,0);
if (!xhlp) xhlp = "";
if (!xdef) xdef = "";
ckstrncpy(cmdefault,xdef,CMDEFAULT); /* Copy default */
xdef = cmdefault;
debug(F111,"cmfld xdef 2",xdef,cmflgs);
debug(F111,"cmfld atmbuf 1",atmbuf,xc);
if ((x = cmflgs) != 1) { /* Already confirmed? */
x = gtword(0); /* No, get a word */
} else {
if (setatm(xdef,0) < 0) { /* If so, use default, if any. */
printf("?Default too long\n");
return(-9);
}
}
*xp = atmbuf; /* Point to result. */
debug(F111,"cmfld atmbuf 2",atmbuf,cmflgs);
while (1) {
xc += cc; /* Count the characters. */
debug(F111,"cmfld gtword",atmbuf,xc);
debug(F101,"cmfld x","",x);
switch (x) {
case -9:
printf("Command or field too long\n");
case -4: /* EOF */
case -3: /* Empty. */
case -2: /* Out of space. */
case -1: /* Reparse needed */
return(x);
case 1: /* CR */
case 0: /* SP */
debug(F111,"cmfld 1",atmbuf,xc);
if (xc == 0) { /* If no input, return default. */
if (setatm(xdef,0) < 0) {
printf("?Default too long\n");
return(-9);
}
}
*xp = atmbuf; /* Point to what we got. */
debug(F111,"cmfld 2",atmbuf,(f) ? 1 : 0);
if (f) { /* If a conversion function is given */
zq = atxbuf; /* employ it now. */
atxn = CMDBL;
if ((*f)(*xp,&zq,&atxn) < 0)
return(-2);
debug(F111,"cmfld 3",atxbuf,xc);
/* Replace by new value -- for MINPUT only keep all chars */
if (setatm(atxbuf,keepallchars ? 3:1) < 0) { /* 16 Mar 2003 */
printf("Value too long\n");
return(-9);
}
*xp = atmbuf;
}