davidsen@steinmetz.UUCP (William E. Davidsen Jr) (06/15/87)
: #!/bin/sh # shar+ created from directory /usr2/davidsen/emacs38i # 13:42 on Thu Jun 11, 1987 by davidsen echo 'x - search.c (text)' sed << 'E!O!F' 's/^X//' > search.c X/* X * The functions in this file implement commands that search in the forward X * and backward directions. There are no special characters in the search X * strings. Probably should have a regular expression search, or something X * like that. X * X * Aug. 1986 John M. Gamble: X * Made forward and reverse search use the same scan routine. X * X * Added a limited number of regular expressions - 'any', X * 'character class', 'closure', 'beginning of line', and X * 'end of line'. X * X * Replacement metacharacters will have to wait for a re-write of X * the replaces function, and a new variation of ldelete(). X * X * For those curious as to my references, i made use of X * Kernighan & Plauger's "Software Tools." X * I deliberately did not look at any published grep or editor X * source (aside from this one) for inspiration. I did make use of X * Allen Hollub's bitmap routines as published in Doctor Dobb's Journal, X * June, 1985 and modified them for the limited needs of character class X * matching. Any inefficiences, bugs, stupid coding examples, etc., X * are therefore my own responsibility. X * X * April 1987: John M. Gamble X * Deleted the "if (n == 0) n = 1;" statements in front of the X * search/hunt routines. Since we now use a do loop, these X * checks are unnecessary. Consolidated common code into the X * function delins(). Renamed global mclen matchlen, X * and added the globals matchline, matchoff, patmatch, and X * mlenold. X * This gave us the ability to unreplace regular expression searches, X * and to put the matched string into an evironment variable. X * SOON TO COME: Meta-replacement characters! X * X * 25-apr-87 DML X * - cleaned up an unneccessary if/else in forwsearch() and X * backsearch() X * - savematch() failed to malloc room for the terminating byte X * of the match string (stomp...stomp...). It does now. Also X * it now returns gracefully if malloc fails X */ X X#include <stdio.h> X#include "estruct.h" X#include "edef.h" X X X/* X * forwsearch -- Search forward. Get a search string from the user, and X * search for the string. If found, reset the "." to be just after X * the match string, and (perhaps) repaint the display. X */ X Xforwsearch(f, n) X Xint f, n; /* default flag / numeric argument */ X X{ X register int status = TRUE; X X /* If n is negative, search backwards. X * Otherwise proceed by asking for the search string. X */ X if (n < 0) X return(backsearch(f, -n)); X X /* Ask the user for the text of a pattern. If the X * response is TRUE (responses other than FALSE are X * possible), search for the pattern for as long as X * n is positive (n == 0 will go through once, which X * is just fine). X */ X if ((status = readpattern("Search", &pat[0], TRUE)) == TRUE) { X do { X#if MAGIC X if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0) X status = mcscanner(&mcpat[0], FORWARD, PTEND); X else X#endif X status = scanner(&pat[0], FORWARD, PTEND); X } while ((--n > 0) && status); X X /* Save away the match, or complain X * if not there. X */ X if (status == TRUE) X savematch(); X else X mlwrite("Not found"); X } X return(status); X} X X/* X * forwhunt -- Search forward for a previously acquired search string. X * If found, reset the "." to be just after the match string, X * and (perhaps) repaint the display. X */ X Xforwhunt(f, n) X Xint f, n; /* default flag / numeric argument */ X X{ X register int status = TRUE; X X if (n < 0) /* search backwards */ X return(backhunt(f, -n)); X X /* Make sure a pattern exists, or that we didn't switch X * into MAGIC mode until after we entered the pattern. X */ X if (pat[0] == '\0') X { X mlwrite("No pattern set"); X return FALSE; X } X#if MAGIC X if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 && X mcpat[0].mc_type == MCNIL) X { X if (!mcstr()) X return FALSE; X } X#endif X X /* Search for the pattern for as long as X * n is positive (n == 0 will go through once, which X * is just fine). X */ X do X { X#if MAGIC X if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0) X status = mcscanner(&mcpat[0], FORWARD, PTEND); X else X#endif X status = scanner(&pat[0], FORWARD, PTEND); X } while ((--n > 0) && status); X X /* Save away the match, or complain X * if not there. X */ X if (status == TRUE) X savematch(); X else X mlwrite("Not found"); X X return(status); X} X X/* X * backsearch -- Reverse search. Get a search string from the user, and X * search, starting at "." and proceeding toward the front of the buffer. X * If found "." is left pointing at the first character of the pattern X * (the last character that was matched). X */ Xbacksearch(f, n) X Xint f, n; /* default flag / numeric argument */ X X{ X register int status = TRUE; X X /* If n is negative, search forwards. X * Otherwise proceed by asking for the search string. X */ X if (n < 0) X return(forwsearch(f, -n)); X X /* Ask the user for the text of a pattern. If the X * response is TRUE (responses other than FALSE are X * possible), search for the pattern for as long as X * n is positive (n == 0 will go through once, which X * is just fine). X */ X if ((status = readpattern("Reverse search", &pat[0], TRUE)) == TRUE) { X do { X#if MAGIC X if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0) X status = mcscanner(&tapcm[0], REVERSE, PTBEG); X else X#endif X status = scanner(&tap[0], REVERSE, PTBEG); X } while ((--n > 0) && status); X X /* Save away the match, or complain X * if not there. X */ X if (status == TRUE) X savematch(); X else X mlwrite("Not found"); X } X return(status); X} X X/* X * backhunt -- Reverse search for a previously acquired search string, X * starting at "." and proceeding toward the front of the buffer. X * If found "." is left pointing at the first character of the pattern X * (the last character that was matched). X */ Xbackhunt(f, n) X Xint f, n; /* default flag / numeric argument */ X X{ X register int status = TRUE; X X if (n < 0) X return(forwhunt(f, -n)); X X /* Make sure a pattern exists, or that we didn't switch X * into MAGIC mode until after we entered the pattern. X */ X if (tap[0] == '\0') X { X mlwrite("No pattern set"); X return FALSE; X } X#if MAGIC X if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 && X tapcm[0].mc_type == MCNIL) X { X if (!mcstr()) X return FALSE; X } X#endif X X /* Go search for it for as long as X * n is positive (n == 0 will go through once, which X * is just fine). X */ X do X { X#if MAGIC X if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0) X status = mcscanner(&tapcm[0], REVERSE, PTBEG); X else X#endif X status = scanner(&tap[0], REVERSE, PTBEG); X } while ((--n > 0) && status); X X /* Save away the match, or complain X * if not there. X */ X if (status == TRUE) X savematch(); X else X mlwrite("Not found"); X X return(status); X} X X#if MAGIC X/* X * mcscanner -- Search for a meta-pattern in either direction. If found, X * reset the "." to be at the start or just after the match string, X * and (perhaps) repaint the display. X */ Xint mcscanner(mcpatrn, direct, beg_or_end) XMC *mcpatrn; /* pointer into pattern */ Xint direct; /* which way to go.*/ Xint beg_or_end; /* put point at beginning or end of pattern.*/ X{ X LINE *curline; /* current line during scan */ X int curoff; /* position within current line */ X int c; /* (dummy) char at current position */ X X /* If we are going in reverse, then the 'end' is actually X * the beginning of the pattern. Toggle it. X */ X beg_or_end ^= direct; X X /* X * Save the old matchlen length, in case it is X * horribly different (closure) from the old length. X * This is terribly important for query-replace undo X * command. X */ X mlenold = matchlen; X X /* Setup local scan pointers to global ".". X */ X curline = curwp->w_dotp; X curoff = curwp->w_doto; X X /* Scan each character until we hit the head link record. X */ X while (!boundry(curline, curoff, direct)) X { X /* Save the current position in case we need to X * restore it on a match, and initialize matchlen to X * zero in case we are doing a search for replacement. X */ X matchline = curline; X matchoff = curoff; X matchlen = 0; X X if (amatch(mcpatrn, direct, &curline, &curoff)) X { X /* A SUCCESSFULL MATCH!!! X * reset the global "." pointers. X */ X if (beg_or_end == PTEND) /* at end of string */ X { X curwp->w_dotp = curline; X curwp->w_doto = curoff; X } X else /* at beginning of string */ X { X curwp->w_dotp = matchline; X curwp->w_doto = matchoff; X } X X curwp->w_flag |= WFMOVE; /* flag that we have moved */ X return TRUE; X } X X /* Advance the cursor. X */ X c = nextch(&curline, &curoff, direct); X } X X return FALSE; /* We could not find a match.*/ X} X X/* X * amatch -- Search for a meta-pattern in either direction. Based on the X * recursive routine amatch() (for "anchored match") in X * Kernighan & Plauger's "Software Tools". X */ Xstatic int amatch(mcptr, direct, pcwline, pcwoff) Xregister MC *mcptr; /* string to scan for */ Xint direct; /* which way to go.*/ XLINE **pcwline; /* current line during scan */ Xint *pcwoff; /* position within current line */ X{ X register int c; /* character at current position */ X LINE *curline; /* current line during scan */ X int curoff; /* position within current line */ X int nchars; X X /* Set up local scan pointers to ".", and get X * the current character. Then loop around X * the pattern pointer until success or failure. X */ X curline = *pcwline; X curoff = *pcwoff; X X /* The beginning-of-line and end-of-line metacharacters X * do not compare against characters, they compare X * against positions. X * BOL is guaranteed to be at the start of the pattern X * for forward searches, and at the end of the pattern X * for reverse searches. The reverse is true for EOL. X * So, for a start, we check for them on entry. X */ X if (mcptr->mc_type == BOL) X { X if (curoff != 0) X return FALSE; X mcptr++; X } X X if (mcptr->mc_type == EOL) X { X if (curoff != llength(curline)) X return FALSE; X mcptr++; X } X X while (mcptr->mc_type != MCNIL) X { X c = nextch(&curline, &curoff, direct); X X if (mcptr->mc_type & CLOSURE) X { X /* Try to match as many characters as possible X * against the current meta-character. A X * newline never matches a closure. X */ X nchars = 0; X while (c != '\n' && mceq(c, mcptr)) X { X c = nextch(&curline, &curoff, direct); X nchars++; X } X X /* We are now at the character that made us X * fail. Try to match the rest of the pattern. X * Shrink the closure by one for each failure. X * Since closure matches *zero* or more occurences X * of a pattern, a match may start even if the X * previous loop matched no characters. X */ X mcptr++; X X for (;;) X { X c = nextch(&curline, &curoff, direct ^ REVERSE); X X if (amatch(mcptr, direct, &curline, &curoff)) X { X matchlen += nchars; X goto success; X } X X if (nchars-- == 0) X return FALSE; X } X } X else /* Not closure.*/ X { X /* The only way we'd get a BOL metacharacter X * at this point is at the end of the reversed pattern. X * The only way we'd get an EOL metacharacter X * here is at the end of a regular pattern. X * So if we match one or the other, and are at X * the appropriate position, we are guaranteed success X * (since the next pattern character has to be MCNIL). X * Before we report success, however, we back up by X * one character, so as to leave the cursor in the X * correct position. For example, a search for ")$" X * will leave the cursor at the end of the line, while X * a search for ")<NL>" will leave the cursor at the X * beginning of the next line. This follows the X * notion that the meta-character '$' (and likewise X * '^') match positions, not characters. X */ X if (mcptr->mc_type == BOL) X if (curoff == llength(curline)) X { X c = nextch(&curline, &curoff, X direct ^ REVERSE); X goto success; X } X else X return FALSE; X X if (mcptr->mc_type == EOL) X if (curoff == 0) X { X c = nextch(&curline, &curoff, X direct ^ REVERSE); X goto success; X } X else X return FALSE; X X /* Neither BOL nor EOL, so go through X * the meta-character equal function. X */ X if (!mceq(c, mcptr)) X return FALSE; X } X X /* Increment the length counter and X * advance the pattern pointer. X */ X matchlen++; X mcptr++; X } /* End of mcptr loop.*/ X X /* A SUCCESSFULL MATCH!!! X * Reset the "." pointers. X */ Xsuccess: X *pcwline = curline; X *pcwoff = curoff; X X return TRUE; X} X#endif X X/* X * scanner -- Search for a pattern in either direction. If found, X * reset the "." to be at the start or just after the match string, X * and (perhaps) repaint the display. X */ Xint scanner(patrn, direct, beg_or_end) Xchar *patrn; /* string to scan for */ Xint direct; /* which way to go.*/ Xint beg_or_end; /* put point at beginning or end of pattern.*/ X{ X register int c; /* character at current position */ X register char *patptr; /* pointer into pattern */ X LINE *curline; /* current line during scan */ X int curoff; /* position within current line */ X LINE *scanline; /* current line during scanning */ X int scanoff; /* position in scanned line */ X X /* If we are going in reverse, then the 'end' is actually X * the beginning of the pattern. Toggle it. X */ X beg_or_end ^= direct; X X /* Set up local pointers to global ".". X */ X curline = curwp->w_dotp; X curoff = curwp->w_doto; X X /* Scan each character until we hit the head link record. X */ X while (!boundry(curline, curoff, direct)) X { X /* Save the current position in case we match X * the search string at this point. X */ X matchline = curline; X matchoff = curoff; X X /* Get the character resolving newlines, and X * test it against first char in pattern. X */ X c = nextch(&curline, &curoff, direct); X X if (eq(c, patrn[0])) /* if we find it..*/ X { X /* Setup scanning pointers. X */ X scanline = curline; X scanoff = curoff; X patptr = &patrn[0]; X X /* Scan through the pattern for a match. X */ X while (*++patptr != '\0') X { X c = nextch(&scanline, &scanoff, direct); X X if (!eq(c, *patptr)) X goto fail; X } X X /* A SUCCESSFULL MATCH!!! X * reset the global "." pointers X */ X if (beg_or_end == PTEND) /* at end of string */ X { X curwp->w_dotp = scanline; X curwp->w_doto = scanoff; X } X else /* at beginning of string */ X { X curwp->w_dotp = matchline; X curwp->w_doto = matchoff; X } X X curwp->w_flag |= WFMOVE; /* Flag that we have moved.*/ X return TRUE; X X } Xfail:; /* continue to search */ X } X X return FALSE; /* We could not find a match */ X} X X/* X * eq -- Compare two characters. The "bc" comes from the buffer, "pc" X * from the pattern. If we are not in EXACT mode, fold out the case. X */ Xint eq(bc, pc) Xregister int bc; Xregister int pc; X{ X if ((curwp->w_bufp->b_mode & MDEXACT) == 0) X { X if (islower(bc)) X bc ^= DIFCASE; X X if (islower(pc)) X pc ^= DIFCASE; X } X X return (bc == pc); X} X X/* X * readpattern -- Read a pattern. Stash it in apat. If it is the X * search string, create the reverse pattern and the magic X * pattern, assuming we are in MAGIC mode (and defined that way). X * Apat is not updated if the user types in an empty line. If X * the user typed an empty line, and there is no old pattern, it is X * an error. Display the old pattern, in the style of Jeff Lomicka. X * There is some do-it-yourself control expansion. Change to using X * <META> to delimit the end-of-pattern to allow <NL>s in the search X * string. X */ Xstatic int readpattern(prompt, apat, srch) Xchar *prompt; Xchar apat[]; Xint srch; X{ X int status; X char tpat[NPAT+20]; X X strcpy(tpat, prompt); /* copy prompt to output string */ X strcat(tpat, " ["); /* build new prompt string */ X expandp(&apat[0], &tpat[strlen(tpat)], NPAT/2); /* add old pattern */ X strcat(tpat, "]<META>: "); X X /* Read a pattern. Either we get one, X * or we just get the META charater, and use the previous pattern. X * Then, if it's the search string, make a reversed pattern. X * *Then*, make the meta-pattern, if we are defined that way. X */ X if ((status = mlreplyt(tpat, tpat, NPAT, metac)) == TRUE) X { X strcpy(apat, tpat); X if (srch) /* If we are doing the search string.*/ X { X matchlen = strlen(apat); X /* Reverse string copy. X */ X rvstrcpy(tap, apat); X#if MAGIC X /* Only make the meta-pattern if in magic mode, X * since the pattern in question might have an X * invalid meta combination. X */ X if ((curwp->w_bufp->b_mode & MDMAGIC) == 0) X mcclear(); X else X status = mcstr(); X#endif X } X } X else if (status == FALSE && apat[0] != 0) /* Old one */ X status = TRUE; X X return(status); X} X X/* X * savematch -- We found the pattern? Let's save it away. X */ X Xsavematch() X X{ X register char *ptr; /* ptr into malloced last match string */ X register int j; /* index */ X LINE *curline; /* line of last match */ X int curoff; /* offset " " */ X X /* free any existing match string */ X if (patmatch != NULL) X free(patmatch); X X /* attempt to allocate a new one */ X ptr = patmatch = malloc(matchlen + 1); X if (ptr == NULL) X return; X X /* save the match! */ X curoff = matchoff; X curline = matchline; X X for (j = 0; j < matchlen; j++) X *ptr++ = nextch(&curline, &curoff, FORWARD); X X /* null terminate the match string */ X *ptr = '\0'; X} X X/* X * rvstrcpy -- Reverse string copy. X */ Xrvstrcpy(rvstr, str) Xregister char *rvstr, *str; X{ X register int i; X X str += (i = strlen(str)); X X while (i-- > 0) X *rvstr++ = *--str; X X *rvstr = '\0'; X} X X/* X * sreplace -- Search and replace. X */ Xsreplace(f, n) X Xint f; /* default flag */ Xint n; /* # of repetitions wanted */ X X{ X return(replaces(FALSE, f, n)); X} X X/* X * qreplace -- search and replace with query. X */ Xqreplace(f, n) Xint f; /* default flag */ Xint n; /* # of repetitions wanted */ X{ X return(replaces(TRUE, f, n)); X} X X/* X * replaces -- Search for a string and replace it with another X * string. Query might be enabled (according to kind). X */ Xstatic int replaces(kind, f, n) Xint kind; /* Query enabled flag */ Xint f; /* default flag */ Xint n; /* # of repetitions wanted */ X{ X register int status; /* success flag on pattern inputs */ X register int rlength; /* length of replacement string */ X register int numsub; /* number of substitutions */ X register int nummatch; /* number of found matches */ X int nlflag; /* last char of search string a <NL>? */ X int nlrepl; /* was a replace done on the last line? */ X char c; /* input char for query */ X char tpat[NPAT]; /* temporary to hold search pattern */ X LINE *origline; /* original "." position */ X int origoff; /* and offset (for . query option) */ X LINE *lastline; /* position of last replace and */ X int lastoff; /* offset (for 'u' query option) */ X X if (curbp->b_mode & MDVIEW) /* don't allow this command if */ X return(rdonly()); /* we are in read only mode */ X X /* Check for negative repetitions. X */ X if (f && n < 0) X return(FALSE); X X /* Ask the user for the text of a pattern. X */ X if ((status = readpattern( X (kind == FALSE ? "Replace" : "Query replace"), &pat[0], TRUE)) X != TRUE) X return(status); X X /* Ask for the replacement string. X */ X if ((status = readpattern("with", &rpat[0], FALSE)) == ABORT) X return(status); X X /* Find the length of the replacement string. X */ X rlength = strlen(&rpat[0]); X X /* Set up flags so we can make sure not to do a recursive X * replace on the last line. X */ X nlflag = (pat[matchlen - 1] == '\n'); X nlrepl = FALSE; X X if (kind) X { X /* Build query replace question string. X */ X strcpy(tpat, "Replace '"); X expandp(&pat[0], &tpat[strlen(tpat)], NPAT/3); X strcat(tpat, "' with '"); X expandp(&rpat[0], &tpat[strlen(tpat)], NPAT/3); X strcat(tpat, "'? "); X X /* Initialize last replaced pointers. X */ X lastline = NULL; X lastoff = 0; X } X X /* Save original . position, init the number of matches and X * substitutions, and scan through the file. X */ X origline = curwp->w_dotp; X origoff = curwp->w_doto; X numsub = 0; X nummatch = 0; X X while ( (f == FALSE || n > nummatch) && X (nlflag == FALSE || nlrepl == FALSE) ) X { X /* Search for the pattern. X * If we search with a regular expression, X * matchlen is reset to the true length of X * the matched string. X */ X#if MAGIC X if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0) X { X if (!mcscanner(&mcpat[0], FORWARD, PTBEG)) X break; X } X else X#endif X if (!scanner(&pat[0], FORWARD, PTBEG)) X break; /* all done */ X X ++nummatch; /* Increment # of matches */ X X /* Check if we are on the last line. X */ X nlrepl = (lforw(curwp->w_dotp) == curwp->w_bufp->b_linep); X X /* Check for query. X */ X if (kind) X { X /* Get the query. X */ Xpprompt: mlwrite(&tpat[0], &pat[0], &rpat[0]); Xqprompt: X update(TRUE); /* show the proposed place to change */ X c = tgetc(); /* and input */ X mlwrite(""); /* and clear it */ X X /* And respond appropriately. X */ X switch (c) X { X case 'y': /* yes, substitute */ X case ' ': X savematch(); X break; X X case 'n': /* no, onword */ X forwchar(FALSE, 1); X continue; X X case '!': /* yes/stop asking */ X kind = FALSE; X break; X X case 'u': /* undo last and re-prompt */ X X /* Restore old position. X */ X if (lastline == NULL) X { X /* There is nothing to undo. X */ X TTbeep(); X goto pprompt; X } X curwp->w_dotp = lastline; X curwp->w_doto = lastoff; X lastline = NULL; X lastoff = 0; X X /* Delete the new string. X */ X backchar(FALSE, rlength); X status = delins(rlength, patmatch); X if (status != TRUE) X return (status); X X /* Record one less substitution, X * backup, and reprompt. X */ X --numsub; X backchar(FALSE, mlenold); X goto pprompt; X X case '.': /* abort! and return */ X /* restore old position */ X curwp->w_dotp = origline; X curwp->w_doto = origoff; X curwp->w_flag |= WFMOVE; X X case BELL: /* abort! and stay */ X mlwrite("Aborted!"); X return(FALSE); X X default: /* bitch and beep */ X TTbeep(); X X case '?': /* help me */ X mlwrite( X"(Y)es, (N)o, (!)Do rest, (U)ndo last, (^G)Abort, (.)Abort back, (?)Help: "); X goto qprompt; X X } /* end of switch */ X } /* end of "if kind" */ X X /* X * Delete the sucker, and insert its X * replacement. X */ X status = delins(matchlen, &rpat[0]); X if (status != TRUE) X return (status); X X /* Save where we are if we might undo this.... X */ X if (kind) X { X lastline = curwp->w_dotp; X lastoff = curwp->w_doto; X } X X numsub++; /* increment # of substitutions */ X } X X /* And report the results. X */ X mlwrite("%d substitutions", numsub); X return(TRUE); X} X X/* X * delins -- Delete a specified length from the current X * point, then insert the string. X */ Xdelins(dlength, instr) Xint dlength; Xchar *instr; X{ X int status; X char tmpc; X X /* Zap what we gotta, X * and insert its replacement. X */ X if (!(status = ldelete((long) dlength, FALSE))) X { X mlwrite("%%ERROR while deleting"); X return(FALSE); X } X else X while (tmpc = *instr) X { X status = (tmpc == '\n'? lnewline(): linsert(1, tmpc)); X X /* Insertion error? X */ X if (!status) X { X mlwrite("%%Out of memory while inserting"); X break; X } X instr++; X } X return (status); X} X X/* X * expandp -- Expand control key sequences for output. X */ Xexpandp(srcstr, deststr, maxlength) Xchar *srcstr; /* string to expand */ Xchar *deststr; /* destination of expanded string */ Xint maxlength; /* maximum chars in destination */ X{ X char c; /* current char to translate */ X X /* Scan through the string. X */ X while ((c = *srcstr++) != 0) X { X if (c == '\n') /* it's a newline */ X { X *deststr++ = '<'; X *deststr++ = 'N'; X *deststr++ = 'L'; X *deststr++ = '>'; X maxlength -= 4; X } X else if (c < 0x20 || c == 0x7f) /* control character */ X { X *deststr++ = '^'; X *deststr++ = c ^ 0x40; X maxlength -= 2; X } X else if (c == '%') X { X *deststr++ = '%'; X *deststr++ = '%'; X maxlength -= 2; X } X else /* any other character */ X { X *deststr++ = c; X maxlength--; X } X X /* check for maxlength */ X if (maxlength < 4) X { X *deststr++ = '$'; X *deststr = '\0'; X return(FALSE); X } X } X *deststr = '\0'; X return(TRUE); X} X X/* X * boundry -- Return information depending on whether we may search no X * further. Beginning of file and end of file are the obvious X * cases, but we may want to add further optional boundry restrictions X * in future, a' la VMS EDT. At the moment, just return TRUE or X * FALSE depending on if a boundry is hit (ouch). X */ Xint boundry(curline, curoff, dir) XLINE *curline; Xint curoff, dir; X{ X register int border; X X if (dir == FORWARD) X { X border = (curoff == llength(curline)) && X (lforw(curline) == curbp->b_linep); X } X else X { X border = (curoff == 0) && X (lback(curline) == curbp->b_linep); X } X return (border); X} X X/* X * nextch -- retrieve the next/previous character in the buffer, X * and advance/retreat the point. X * The order in which this is done is significant, and depends X * upon the direction of the search. Forward searches look at X * the current character and move, reverse searches move and X * look at the character. X */ Xstatic int nextch(pcurline, pcuroff, dir) XLINE **pcurline; Xint *pcuroff; Xint dir; X{ X register LINE *curline; X register int curoff; X register int c; X X curline = *pcurline; X curoff = *pcuroff; X X if (dir == FORWARD) X { X if (curoff == llength(curline)) /* if at EOL */ X { X curline = lforw(curline); /* skip to next line */ X curoff = 0; X c = '\n'; /* and return a <NL> */ X } X else X c = lgetc(curline, curoff++); /* get the char */ X } X else /* Reverse.*/ X { X if (curoff == 0) X { X curline = lback(curline); X curoff = llength(curline); X c = '\n'; X } X else X c = lgetc(curline, --curoff); X X } X *pcurline = curline; X *pcuroff = curoff; X X return (c); X} X X#if MAGIC X/* X * mcstr -- Set up the 'magic' array. The closure symbol is taken as X * a literal character when (1) it is the first character in the X * pattern, and (2) when preceded by a symbol that does not allow X * closure, such as a newline, beginning of line symbol, or another X * closure symbol. X * X * Coding comment (jmg): yes, i know i have gotos that are, strictly X * speaking, unnecessary. But right now we are so cramped for X * code space that i will grab what i can in order to remain X * within the 64K limit. C compilers actually do very little X * in the way of optimizing - they expect you to do that. X */ Xint mcstr() X{ X MC *mcptr, *rtpcm; X char *patptr; X int mj; X int pchr; X int status = TRUE; X int does_closure = FALSE; X X /* If we had metacharacters in the MC array previously, X * free up any bitmaps that may have been allocated. X */ X if (magical) X mcclear(); X X magical = FALSE; X mj = 0; X mcptr = &mcpat[0]; X patptr = &pat[0]; X X while ((pchr = *patptr) && status) X { X switch (pchr) X { X case MC_CCL: X status = cclmake(&patptr, mcptr); X magical = TRUE; X does_closure = TRUE; X break; X case MC_BOL: X if (mj != 0) X goto litcase; X X mcptr->mc_type = BOL; X magical = TRUE; X does_closure = FALSE; X break; X case MC_EOL: X if (*(patptr + 1) != '\0') X goto litcase; X X mcptr->mc_type = EOL; X magical = TRUE; X does_closure = FALSE; X break; X case MC_ANY: X mcptr->mc_type = ANY; X magical = TRUE; X does_closure = TRUE; X break; X case MC_CLOSURE: X /* Does the closure symbol mean closure here? X * If so, back up to the previous element X * and indicate it is enclosed. X */ X if (!does_closure) X goto litcase; X mj--; X mcptr--; X mcptr->mc_type |= CLOSURE; X magical = TRUE; X does_closure = FALSE; X break; X X /* Note: no break between MC_ESC case and the default. X */ X case MC_ESC: X if (*(patptr + 1) != '\0') X { X pchr = *++patptr; X magical = TRUE; X } X default: Xlitcase: mcptr->mc_type = LITCHAR; X mcptr->u.lchar = pchr; X does_closure = (pchr != '\n'); X break; X } /* End of switch.*/ X mcptr++; X patptr++; X mj++; X } /* End of while.*/ X X /* Close off the meta-string. X */ X mcptr->mc_type = MCNIL; X X /* Set up the reverse array, if the status is good. Please note the X * structure assignment - your compiler may not like that. X * If the status is not good, nil out the meta-pattern. X * The only way the status would be bad is from the cclmake() X * routine, and the bitmap for that member is guarenteed to be X * freed. So we stomp a MCNIL value there, and call mcclear() X * to free any other bitmaps. X */ X if (status) X { X rtpcm = &tapcm[0]; X while (--mj >= 0) X { X#if LATTICE X movmem(--mcptr, rtpcm++, sizeof (MC)); X#endif X X#if MWC86 | AZTEC | MSC | VMS | USG | BSD | V7 X *rtpcm++ = *--mcptr; X#endif X } X rtpcm->mc_type = MCNIL; X } X else X { X (--mcptr)->mc_type = MCNIL; X mcclear(); X } X X return(status); X} X X/* X * mcclear -- Free up any CCL bitmaps, and MCNIL the MC arrays. X */ Xmcclear() X{ X register MC *mcptr; X X mcptr = &mcpat[0]; X X while (mcptr->mc_type != MCNIL) X { X if ((mcptr->mc_type & MASKCL) == CCL || X (mcptr->mc_type & MASKCL) == NCCL) X free(mcptr->u.cclmap); X mcptr++; X } X mcpat[0].mc_type = tapcm[0].mc_type = MCNIL; X} X X/* X * mceq -- meta-character equality with a character. In Kernighan & Plauger's X * Software Tools, this is the function omatch(), but i felt there X * were too many functions with the 'match' name already. X */ Xstatic int mceq(bc, mt) Xint bc; XMC *mt; X{ X register int result; X X switch (mt->mc_type & MASKCL) X { X case LITCHAR: X result = eq(bc, mt->u.lchar); X break; X X case ANY: X result = (bc != '\n'); X break; X X case CCL: X if (!(result = biteq(bc, mt->u.cclmap))) X { X if ((curwp->w_bufp->b_mode & MDEXACT) == 0 && X (isletter(bc))) X { X result = biteq(CHCASE(bc), mt->u.cclmap); X } X } X break; X X case NCCL: X result = !biteq(bc, mt->u.cclmap); X X if ((curwp->w_bufp->b_mode & MDEXACT) == 0 && X (isletter(bc))) X { X result &= !biteq(CHCASE(bc), mt->u.cclmap); X } X break; X X default: X mlwrite("mceq: what is %d?", mt->mc_type); X result = FALSE; X break; X X } /* End of switch.*/ X X return (result); X} X X/* X * cclmake -- create the bitmap for the character class. X * ppatptr is left pointing to the end-of-character-class character, X * so that a loop may automatically increment with safety. X */ Xstatic int cclmake(ppatptr, mcptr) Xchar **ppatptr; XMC *mcptr; X{ X BITMAP clearbits(); X BITMAP bmap; X register char *patptr; X register int pchr, ochr; X X if ((bmap = clearbits()) == NULL) X { X mlwrite("%%Out of memory"); X return FALSE; X } X X mcptr->u.cclmap = bmap; X patptr = *ppatptr; X X /* X * Test the initial character(s) in ccl for X * special cases - negate ccl, or an end ccl X * character as a first character. Anything X * else gets set in the bitmap. X */ X if (*++patptr == MC_NCCL) X { X patptr++; X mcptr->mc_type = NCCL; X } X else X mcptr->mc_type = CCL; X X if ((ochr = *patptr) == MC_ECCL) X { X mlwrite("%%No characters in character class"); X return (FALSE); X } X else X { X if (ochr == MC_ESC) X ochr = *++patptr; X X setbit(ochr, bmap); X patptr++; X } X X while (ochr != '\0' && (pchr = *patptr) != MC_ECCL) X { X switch (pchr) X { X /* Range character loses its meaning X * if it is the last character in X * the class. X */ X case MC_RCCL: X if (*(patptr + 1) == MC_ECCL) X setbit(pchr, bmap); X else X { X pchr = *++patptr; X while (++ochr <= pchr) X setbit(ochr, bmap); X } X break; X X /* Note: no break between case MC_ESC and the default. X */ X case MC_ESC: X pchr = *++patptr; X default: X setbit(pchr, bmap); X break; X } X patptr++; X ochr = pchr; X } X X *ppatptr = patptr; X X if (ochr == '\0') X { X mlwrite("%%Character class not ended"); X free(bmap); X return FALSE; X } X return TRUE; X} X X/* X * biteq -- is the character in the bitmap? X */ Xstatic int biteq(bc, cclmap) Xint bc; XBITMAP cclmap; X{ X if (bc >= HICHAR) X return FALSE; X X return( (*(cclmap + (bc >> 3)) & BIT(bc & 7))? TRUE: FALSE ); X} X X/* X * clearbits -- Allocate and zero out a CCL bitmap. X */ Xstatic BITMAP clearbits() X{ X char *malloc(); X X BITMAP cclstart, cclmap; X register int j; X X if ((cclmap = cclstart = (BITMAP) malloc(HIBYTE)) != NULL) X for (j = 0; j < HIBYTE; j++) X *cclmap++ = 0; X X return (cclstart); X} X X/* X * setbit -- Set a bit (ON only) in the bitmap. X */ Xstatic setbit(bc, cclmap) Xint bc; XBITMAP cclmap; X{ X if (bc < HICHAR) X *(cclmap + (bc >> 3)) |= BIT(bc & 7); X} X#endif E!O!F newsize=`wc -c < search.c` if [ $newsize -ne 32143 ] then echo "File search.c was $newsize bytes, 32143 expected" fi echo 'x - spawn.c (text)' sed << 'E!O!F' 's/^X//' > spawn.c X/* Spawn: various DOS access commands X for MicroEMACS X*/ X X#include <stdio.h> X#include "estruct.h" X#include "edef.h" X X#if AMIGA X#define NEW 1006 X#endif X X#if ST520 & MEGAMAX X#include <osbind.h> X#include <string.h> X#define LOAD_EXEC 0 /* load and execute the program */ Xchar *STcmd, /* the command filename & path */ X *STargs, /* command args (if any) */ X *STenv, /* environment */ X *STwork; /* work area */ X#endif X X#if VMS X#define EFN 0 /* Event flag. */ X X#include <ssdef.h> /* Random headers. */ X#include <stsdef.h> X#include <descrip.h> X#include <iodef.h> X Xextern int oldmode[3]; /* In "termio.c" */ Xextern int newmode[3]; /* In "termio.c" */ Xextern short iochan; /* In "termio.c" */ X#endif X X#if V7 | USG | BSD X#include <signal.h> Xextern int vttidy(); X#endif X X#if MSDOS & MSC X#include <process.h> X#define system(a) spawnlp(P_WAIT, a, NULL) X#endif X X/* X * Create a subjob with a copy of the command intrepreter in it. When the X * command interpreter exits, mark the screen as garbage so that you do a full X * repaint. Bound to "^X C". The message at the start in VMS puts out a newline. X * Under some (unknown) condition, you don't get one free when DCL starts up. X */ Xspawncli(f, n) X{ X#if AMIGA X long newcli; X X#endif X X#if V7 | USG | BSD X register char *cp; X char *getenv(); X#endif X X /* don't allow this command if restricted */ X if (restflag) X return(resterr()); X X#if AMIGA X newcli = Open("CON:0/0/639/199/MicroEmacs Subprocess", NEW); X mlwrite("[Starting new CLI]"); X sgarbf = TRUE; X Execute("", newcli, 0); X Close(newcli); X return(TRUE); X#endif X X#if VMS X movecursor(term.t_nrow, 0); /* In last line. */ X mlputs("[Starting DCL]\r\n"); X TTflush(); /* Ignore "ttcol". */ X sgarbf = TRUE; X return (sys(NULL)); /* NULL => DCL. */ X#endif X#if CPM X mlwrite("Not in CP/M-86"); X#endif X#if ST520 X mlwrite("Not in TOS"); X#endif X#if MSDOS & AZTEC X movecursor(term.t_nrow, 0); /* Seek to last line. */ X TTflush(); X TTkclose(); X system("command.com"); X TTkopen(); X sgarbf = TRUE; X return(TRUE); X#endif X#if MSDOS & LATTICE X movecursor(term.t_nrow, 0); /* Seek to last line. */ X TTflush(); X TTkclose(); X sys("\\command.com", ""); /* Run CLI. */ X TTkopen(); X sgarbf = TRUE; X return(TRUE); X#endif X#if V7 | USG | BSD X movecursor(term.t_nrow, 0); /* Seek to last line. */ X TTflush(); X TTclose(); /* stty to old settings */ X if ((cp = getenv("SHELL")) != NULL && *cp != '\0') X system(cp); X else X#if BSD X system("exec /bin/csh"); X#else X system("exec /bin/sh"); X#endif X sgarbf = TRUE; X sleep(2); X TTopen(); X return(TRUE); X#endif X} X X#if BSD X Xbktoshell() /* suspend MicroEMACS and wait to wake up */ X{ X int pid; X X vttidy(); X pid = getpid(); X kill(pid,SIGTSTP); X} X Xrtfrmshell() X{ X TTopen(); X curwp->w_flag = WFHARD; X sgarbf = TRUE; X} X#endif X X/* X * Run a one-liner in a subjob. When the command returns, wait for a single X * character to be typed, then mark the screen as garbage so a full repaint is X * done. Bound to "C-X !". X */ Xspawn(f, n) X{ X register int s; X char line[NLINE]; X X#if ST520 & MEGAMAX X int i,j,k; X char *sptr,*tptr; X#endif X X#if AMIGA X long newcli; X#endif X X /* don't allow this command if restricted */ X if (restflag) X return(resterr()); X X#if AMIGA X if ((s=mlreply("!", line, NLINE)) != TRUE) X return (s); X newcli = Open("CON:0/0/639/199/MicroEmacs Subprocess", NEW); X Execute(line,0,newcli); X Close(newcli); X tgetc(); /* Pause. */ X sgarbf = TRUE; X return(TRUE); X#endif X#if ST520 & MEGAMAX X if ((s=mlreply("!", line, NLINE)) != TRUE) X return(s); X movecursor(term.t_nrow - 1, 0); X TTclose(); X/* X * break the line into the command and its args X * be cute about it, if there is no '.' in the filename, try X * to find .prg, .tos or .ttp in that order X * in any case check to see that the file exists before we run X * amok X */ X STenv = NULL; X if((tptr = index(&line[0],' ')) == NULL) { /* no args */ X STcmd = (char *)malloc(strlen(line) + 1); X strcpy(STcmd,line); X STargs = NULL; X } X else { /* seperate out the args from the command */ X /* resist the temptation to do ptr arithmetic */ X STcmd = (char *)malloc(strlen(line) + 1); X for(i = 0,sptr = &line[0]; sptr != tptr; sptr++,i++) X STcmd[i] = *sptr; X STcmd[i] = '\0'; X for(; *tptr == ' ' || *tptr == '\t'; tptr++); X if(*tptr == '\0') X STargs = NULL; X else { X STargs = (char *)malloc(strlen(tptr) + 2); X/* first byte of STargs is the length of the string */ X STargs[0] = strlen(tptr); X STargs[1] = NULL; /* fake it for strcat */ X strcat(STargs,tptr); X } X } X/* X * before we issue the command look for the '.', if it's not there X * try adding .prg, .tos and .ttp to see if they exist, if not X * issue the command as is X */ X if((tptr = index(STcmd,'.')) == NULL) { X STwork = (char *)malloc(strlen(STcmd) + 4); X strcpy(STwork,STcmd); X strcat(STwork,".prg"); X tptr = index(STwork,'.'); X if(Fsfirst(1,STwork) != 0) { /* try .tos */ X strcpy(tptr,".tos"); X if(Fsfirst(1,STwork) != 0) { /* try .ttp */ X strcpy(tptr,".ttp"); X if(Fsfirst(1,STwork) != 0) /* never mind */ X *STwork = NULL; X } X } X } X if(*STwork != NULL) X Pexec(LOAD_EXEC,STwork,STargs,STenv); X else X Pexec(LOAD_EXEC,STcmd,STargs,STenv); X TTopen(); X mlputs("\r\n\n[End]"); /* Pause. */ X TTgetc(); /* Pause. */ X sgarbf = TRUE; X return (TRUE); X#endif X#if VMS X if ((s=mlreply("!", line, NLINE)) != TRUE) X return (s); X TTputc('\n'); /* Already have '\r' */ X TTflush(); X s = sys(line); /* Run the command. */ X mlputs("\r\n\n[End]"); /* Pause. */ X TTflush(); X tgetc(); X sgarbf = TRUE; X return (s); X#endif X#if CPM X mlwrite("Not in CP/M-86"); X return (FALSE); X#endif X#if MSDOS | (ST520 & LATTICE) X if ((s=mlreply("!", line, NLINE)) != TRUE) X return(s); X movecursor(term.t_nrow - 1, 0); X TTkclose(); X system(line); X TTkopen(); X /* if we are interactive, pause here */ X if (clexec == FALSE) { X mlputs("\r\n\n[End]"); X tgetc(); X } X sgarbf = TRUE; X return (TRUE); X#endif X#if V7 | USG | BSD X if ((s=mlreply("!", line, NLINE)) != TRUE) X return (s); X TTputc('\n'); /* Already have '\r' */ X TTflush(); X TTclose(); /* stty to old modes */ X system(line); X TTopen(); X mlputs("[End]"); /* Pause. */ X TTflush(); X while ((s = tgetc()) != '\r' && s != ' ') X ; X sgarbf = TRUE; X return (TRUE); X#endif X} X X/* X * Pipe a one line command into a window X * Bound to ^X @ X */ Xpipecmd(f, n) X{ X register int s; /* return status from CLI */ X register WINDOW *wp; /* pointer to new window */ X register BUFFER *bp; /* pointer to buffer to zot */ X char line[NLINE]; /* command line send to shell */ X static char bname[] = "command"; X X#if AMIGA X static char filnam[] = "ram:command"; X long newcli; X#else X static char filnam[] = "command"; X#endif X X#if MSDOS X char *tmp; X char *getenv(); X FILE *fp; X FILE *fopen(); X#endif X X /* don't allow this command if restricted */ X if (restflag) X return(resterr()); X X#if MSDOS X if ((tmp = getenv("TMP")) == NULL) X strcpy(filnam, "command"); X else X strcpy(filnam, tmp); X#endif X X#if VMS X mlwrite("Not availible under VMS"); X return(FALSE); X#endif X#if CPM X mlwrite("Not availible under CP/M-86"); X return(FALSE); X#endif X X /* get the command to pipe in */ X if ((s=mlreply("@", line, NLINE)) != TRUE) X return(s); X X /* get rid of the command output buffer if it exists */ X if ((bp=bfind(bname, FALSE, 0)) != FALSE) { X /* try to make sure we are off screen */ X wp = wheadp; X while (wp != NULL) { X if (wp->w_bufp == bp) { X onlywind(FALSE, 1); X break; X } X wp = wp->w_wndp; X } X if (zotbuf(bp) != TRUE) X X return(FALSE); X } X X#if AMIGA X newcli = Open("CON:0/0/639/199/MicroEmacs Subprocess", NEW); X strcat(line, " >"); X strcat(line, filnam); X Execute(line,0,newcli); X s = TRUE; X Close(newcli); X sgarbf = TRUE; X#endif X#if MSDOS X strcat(line," >>"); X strcat(line,filnam); X movecursor(term.t_nrow - 1, 0); X TTkclose(); X system(line); X TTkopen(); X sgarbf = TRUE; X if ((fp = fopen(filnam, "r")) == NULL) { X s = FALSE; X } else { X fclose(fp); X s = TRUE; X } X#endif X#if V7 | USG | BSD X TTputc('\n'); /* Already have '\r' */ X TTflush(); X TTclose(); /* stty to old modes */ X strcat(line,">"); X strcat(line,filnam); X system(line); X TTopen(); X TTflush(); X sgarbf = TRUE; X s = TRUE; X#endif X X if (s != TRUE) X return(s); X X /* split the current window to make room for the command output */ X if (splitwind(FALSE, 1) == FALSE) X return(FALSE); X X /* and read the stuff in */ X if (getfile(filnam, FALSE) == FALSE) X return(FALSE); X X /* make this window in VIEW mode, update all mode lines */ X curwp->w_bufp->b_mode |= MDVIEW; X wp = wheadp; X while (wp != NULL) { X wp->w_flag |= WFMODE; X wp = wp->w_wndp; X } X X /* and get rid of the temporary file */ X unlink(filnam); X return(TRUE); X} X X/* X * filter a buffer through an external DOS program X * Bound to ^X # X */ Xfilter(f, n) X X{ X register int s; /* return status from CLI */ X register BUFFER *bp; /* pointer to buffer to zot */ X char line[NLINE]; /* command line send to shell */ X char tmpnam[NFILEN]; /* place to store real file name */ X static char bname1[] = "fltinp"; X X#if AMIGA X static char filnam1[] = "ram:fltinp"; X static char filnam2[] = "ram:fltout"; X long newcli; X#else X static char filnam1[] = "fltinp"; X static char filnam2[] = "fltout"; X#endif X X /* don't allow this command if restricted */ X if (restflag) X return(resterr()); X X if (curbp->b_mode&MDVIEW) /* don't allow this command if */ X return(rdonly()); /* we are in read only mode */ X X#if VMS X mlwrite("Not availible under VMS"); X return(FALSE); X#endif X#if CPM X mlwrite("Not availible under CP/M-86"); X return(FALSE); X#endif X X /* get the filter name and its args */ X if ((s=mlreply("#", line, NLINE)) != TRUE) X return(s); X X /* setup the proper file names */ X bp = curbp; X strcpy(tmpnam, bp->b_fname); /* save the original name */ X strcpy(bp->b_fname, bname1); /* set it to our new one */ X X /* write it out, checking for errors */ X if (writeout(filnam1) != TRUE) { X mlwrite("[Cannot write filter file]"); X strcpy(bp->b_fname, tmpnam); X return(FALSE); X } X X#if AMIGA X newcli = Open("CON:0/0/639/199/MicroEmacs Subprocess", NEW); X strcat(line, " <ram:fltinp >ram:fltout"); X Execute(line,0,newcli); X s = TRUE; X Close(newcli); X sgarbf = TRUE; X#endif X#if MSDOS X strcat(line," <fltinp >fltout"); X movecursor(term.t_nrow - 1, 0); X TTkclose(); X system(line); X TTkopen(); X sgarbf = TRUE; X s = TRUE; X#endif X#if V7 | USG | BSD X TTputc('\n'); /* Already have '\r' */ X TTflush(); X TTclose(); /* stty to old modes */ X strcat(line," <fltinp >fltout"); X system(line); X TTopen(); X TTflush(); X sgarbf = TRUE; X s = TRUE; X#endif X X /* on failure, escape gracefully */ X if (s != TRUE || (readin(filnam2,FALSE) == FALSE)) { X mlwrite("[Execution failed]"); X strcpy(bp->b_fname, tmpnam); X unlink(filnam1); X unlink(filnam2); X return(s); X } X X /* reset file name */ X strcpy(bp->b_fname, tmpnam); /* restore name */ X bp->b_flag |= BFCHG; /* flag it as changed */ X X /* and get rid of the temporary file */ X unlink(filnam1); X unlink(filnam2); X return(TRUE); X} X X#if VMS X/* X * Run a command. The "cmd" is a pointer to a command string, or NULL if you X * want to run a copy of DCL in the subjob (this is how the standard routine X * LIB$SPAWN works. You have to do wierd stuff with the terminal on the way in X * and the way out, because DCL does not want the channel to be in raw mode. X */ Xsys(cmd) Xregister char *cmd; X{ X struct dsc$descriptor cdsc; X struct dsc$descriptor *cdscp; X long status; X long substatus; X long iosb[2]; X X status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0, X oldmode, sizeof(oldmode), 0, 0, 0, 0); X if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL) X return (FALSE); X cdscp = NULL; /* Assume DCL. */ X if (cmd != NULL) { /* Build descriptor. */ X cdsc.dsc$a_pointer = cmd; X cdsc.dsc$w_length = strlen(cmd); X cdsc.dsc$b_dtype = DSC$K_DTYPE_T; X cdsc.dsc$b_class = DSC$K_CLASS_S; X cdscp = &cdsc; X } X status = LIB$SPAWN(cdscp, 0, 0, 0, 0, 0, &substatus, 0, 0, 0); X if (status != SS$_NORMAL) X substatus = status; X status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0, X newmode, sizeof(newmode), 0, 0, 0, 0); X if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL) X return (FALSE); X if ((substatus&STS$M_SUCCESS) == 0) /* Command failed. */ X return (FALSE); X return (TRUE); X} X#endif X X#if ~AZTEC & MSDOS X X/* X * This routine, once again by Bob McNamara, is a C translation of the "system" X * routine in the MWC-86 run time library. It differs from the "system" routine X * in that it does not unconditionally append the string ".exe" to the end of X * the command name. We needed to do this because we want to be able to spawn X * off "command.com". We really do not understand what it does, but if you don't X * do it exactly "malloc" starts doing very very strange things. X */ Xsys(cmd, tail) Xchar *cmd; Xchar *tail; X{ X#if MWC_86 X register unsigned n; X extern char *__end; X X n = __end + 15; X n >>= 4; X n = ((n + dsreg() + 16) & 0xFFF0) + 16; X return(execall(cmd, tail, n)); X#endif X X#if LATTICE X return(forklp(cmd, tail, (char *)NULL)); X#endif X X#if MSC X return(spawnlp(P_WAIT, cmd, tail, NULL)); X#endif X} X#endif X X#if MSDOS & LATTICE X/* System: a modified version of lattice's system() function X that detects the proper switchar and uses it X written by Dana Hogget */ X Xsystem(cmd) X Xchar *cmd; /* Incoming command line to execute */ X X{ X char *getenv(); X static char *swchar = "/C"; /* Execution switch */ X union REGS inregs; /* parameters for dos call */ X union REGS outregs; /* Return results from dos call */ X char *shell; /* Name of system command processor */ X char *p; /* Temporary pointer */ X int ferr; /* Error condition if any */ X X /* get name of system shell */ X if ((shell = getenv("COMSPEC")) == NULL) { X return (-1); /* No shell located */ X } X X p = cmd; X while (isspace(*p)) { /* find out if null command */ X p++; X } X X /** If the command line is not empty, bring up the shell **/ X /** and execute the command. Otherwise, bring up the **/ X /** shell in interactive mode. **/ X X if (p && *p) { X /** detect current switch character and us it **/ X inregs.h.ah = 0x37; /* get setting data */ X inregs.h.al = 0x00; /* get switch character */ X intdos(&inregs, &outregs); X *swchar = outregs.h.dl; X ferr = forkl(shell, "command", swchar, cmd, (char *)NULL); X } else { X ferr = forkl(shell, "command", (char *)NULL); X } X X return (ferr ? ferr : wait()); X} X#endif E!O!F newsize=`wc -c < spawn.c` if [ $newsize -ne 16513 ] then echo "File spawn.c was $newsize bytes, 16513 expected" fi bill davidsen (wedu@ge-crd.arpa) {chinet | philabs | sesimo}!steinmetz!crdos1!davidsen "Stupidity, like virtue, is its own reward" ilre