| #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; |
| } |
|