#include "ded.h" #include "char.h" #include "signal.h" #include "match.h" #include "jmp.h" #include "cursor.h" /* for set_c() */ /************************************************************************ * * * copyright Richard Bornat 1981 * * * ************************************************************************/ /* Interactive Compile facility added - PLS UMIST 1983 * Compile with flag "-DINTCOMP" to enable. * * Interactive Spell facility added - Compile with -DINTCOMP and -DINTSPELL * to enable. * Shell selection using $SHELL environment parameter added. * PLS 1985. */ #ifdef INTCOMP int cpipe[2] = {-1,-1}; # ifdef INTSPELL int spelling; # endif #endif extern char *fillin(); /************************************************************************ * * * this procedure allows the processing of command lines * * which may contain control characters, all kinds of other * * gibberish, but also may contain percent (%) characters. * * A percent is replaced by the current filename - and * * awfully inconsistently, '% means a percent character * * in the command string. I bow to popular pressure in this, * * although strictly '% should be a filename, % a percent char. * * * ************************************************************************/ do_unix(comm, shc) char *comm; char shc; /* shc == '-' means shell */ /* shc == '!' means unix cmd, '+' means w then unix cmd UMIST */ /* shc == '#' means w then unix cmd with pipe for errors UMIST */ /* shc == '@' means w then unix cmd with search for output lines */ { register int pid; int filepercent=false, changed=false; char combuf[512]; char *comlim = combuf+511; static char *shell; extern char *getenv(); #ifdef V7 /* run the user's favourite shell if possible - PLS */ if (shell == (char *)0) if ((shell = getenv("SHELL")) == (char *)0) shell = "/bin/sh"; #endif #ifdef INTSPELL if (shc == '@') { shc = '#'; spelling = true; } else spelling = false; #endif if (comm != 0) /* zero parameter means @ or - command */ { char c, *lp=combuf, *fp; comm = fillin(comm, (shc == '!') ? unixbuffer : plusbuffer, "no command"); /* copy command into buffer */ do { if ((c = *comm++) == '%') /* percent replaced by filename */ { changed = filepercent = true; fp = filename; while ((*lp++ = *fp++)) if (lp>comlim) goto disaster; /* sorry */ lp--; } else if (c == '\'' && *comm=='%' ) /* prime, percent */ { changed = true; *lp++ = *comm++; } else *lp++ = c; /* everything else */ } while (c != '\0' && lpcomlim) { disaster: fdiag("command too long"); } } if (check_replay("do you want to execute this command?")) /* Get ready to fork. First, do ttyreset in parent so * as not to mess up dlog file. * On return, use ttysetmode so as not to flush the input * buffer - ded will have read ahead * when reading from an old dlog file. */ { savescreen(); if (filepercent && tmp_changed && !query("file has changed - ok to execute???")) return; /* ok to run the command */ /* this next line is no longer a cheat - the expanded command * is redrawn properly (and before the fork) so that ded knows * about it. In this way, rowmap is updated and 'redraw' can * be called again to clear the line and print the compiler * error message for the UMIST interactive compile facility. */ if (changed) { diag_clear(); redraw(EDITROW, 2, combuf); blobit(); } #ifdef INTCOMP if (shc == '#') { close(cpipe[0]); /* close previous pipe (if any) */ pipe(cpipe); } else #endif ttyreset(); if ((pid = fork()) == 0) { /* I am now the child */ extern int f_tmp; #ifdef INTCOMP if (shc != '#') #endif printf("\n"); /* set default signal handling in child */ signal(SIGQUIT, SIG_DFL); signal(SIGINT, SIG_DFL); /* close open files */ close(dlogger); close(f_tmp); close(std_ter);/* mtf*/ if (comm) { tranline(combuf, combuf, false); /* allow control characters etc. */ #ifdef INTCOMP if (shc == '#') /* Redirect stdout & stderr of child into pipe */ { close(1); close(2); dup(cpipe[1]); dup(cpipe[1]); #if !vax close(0); /* can't have input */ #endif close(cpipe[0]); /* child does not read pipe */ } #endif #ifdef V7 execl(shell, "sh", "-ce", combuf, 0); #else execl("/bin/sh", "sh", "-c", combuf, 0); #endif } else if ( shc=='-' ) #ifdef V7 execl(shell, "sh", 0); #else execl("/bin/sh", "-", 0); #endif else editerror("garbage parameters to do_unix"); /* if you return from that, there is something wrong */ editerror("!?can't find or execute shell?!"); } else if(pid == -1) { /* it failed */ ttysetmode(); tdiag("!! try again !!"); } else { /* I am the parent */ #ifdef INTCOMP if (shc == '#') { close(cpipe[1]); showerror(0); leave_command(OK); } else #endif { #ifdef RUNSTACK pwait(pid); #else int status, w; while ((w=wait(&status)) != pid && w != -1) ; if (status & 0200) { printf("\nError - core dumped"); comm = ""; } #endif ttysetmode(); if (comm != 0 && NORMAL_INPUT) /* zero parameter means @ or - command */ { extern char qread(); complain(); printf("\n(ded here) - type any character to redisplay screen"); qread(); } setupscreen(); } } } } #ifdef RUNSTACK /* procedure that interprets error signals from activated process */ pwait(i) /* i - process no */ { register p; int s; if (i != 0) while ((p=wait(&s)) != i && p != -1) if(s&0200) runstack(s); } runstack(s) { char *flag; register pid, rpid; int status; flag = "-sx"; flag[2] = s&0377; if( (pid = fork()) == 0 ) { /* actually, stack does its own signals... */ signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_IGN); /* no more core dumps...*/ execl("/usr/bin/stack", "stack", flag, 0); exit(3); } while( (rpid = wait(&status)) != pid && rpid != -1 ); } #endif #ifdef INTCOMP /* Interactive compile facility PLS, UMIST 15/2/83 * Read and process (next) compiler error message from child process. * This routine reads one line from pipe, deduces the line and column * number, moves the cursor there and then prints the diagnostic part * of the message on the command line. * Format of full error line is: * :[][-][:] * If the line does not match this spec, if or are out * of range, or if is not that of the file being edited, * it is printed as a whole and no cursor movement occurs. * This function is called from do_unix() above, and also from * mainloop() on receipt of the READPIPE input character. */ showerror(flag) int flag; { char ebuff[ENOUGH]; char fbuff[ENOUGH]; register char *ep = ebuff; register char *fp = fbuff; int eline = 0, ecol = 0; #ifdef INTSPELL if (spelling) { showspell(flag); return; } #endif while (read(cpipe[0],ep,1) > 0 && *ep != '\n' && ep < &ebuff[ENOUGH]) ep++; *ep = 0; ep = ebuff; while ((*fp++ = *ep) && *ep != ':' && *ep != ',') ep++; *--fp = 0; if (*--fp == '"') *fp = 0; /* allow for quotes round the filename */ if (*(fp = fbuff) == '"') fp++; /* ugh! */ while (*ep && !digit(*ep)) ep++; while (digit(*ep)) eline = eline*10 + *ep++ - '0'; if (*ep == '-') { ep++; while (digit(*ep)) ecol = ecol*10 + *ep++ - '0'; } if (*ep == ':') ep++; while (*ep == ' ') ep++; /* ep now points at beginning of diagnostic */ redraw(EDITROW, 1, ""); eline--; ecol += (ecol==0) ? MARGIN : MARGIN-1; if (eline < 0 || eline > maxl || ecol < MARGIN || ecol > RIGHTCOL || !streq(fp, filename)) cdiag(ebuff); /* print whole line */ else { i_display(eline); set_c(eline-topl, ecol, &in_c); cdiag(ep); /* print compiler diagnostic */ } } #endif #ifdef INTSPELL /* Interactive spelling check facility PLS 1985 * Process (next) spelling mistake, reading from pipe as necessary. * This routine reads one line from pipe and searches for that line * in the file as a word. If found, the cursor is moved to the beginning * of the first instance of word in the file, and the word is also * displayed on the command line. * Successive invocations (with flag==READPIPE) cause each instance of * the last word read to be stepped through, until all instances in the * file have been shown. The next invocation will then read a new word * from the pipe, etc. * This function is called from do_unix() above (via showerror()) with * flag==0, and also from mainloop() on receipt of the READPIPE input * character (flag==READPIPE) or the sequence SEND READPIPE (flag==SEND). * For the flag values 0 or SEND, the next word from the pipe is read * immediately, without searching for any remaining instances of the * current word. * The user may correct or ignore the words indicated at any stage during * the processing, without affecting the operation of this routine. */ showspell(flag) int flag; { static char sbuff[ENOUGH]; static int spline, spcol; register char *sp; int i; /* * if invoked via a control char, set jump out position * in case of errors or interrupt during searches */ if (flag) { switch (i=setjmp(dc_env)) { case 0: break; case OK: case NOT_OK: return; default: editerror("showspell: val %d in longjmp to dc_env", i); } } redraw(EDITROW, 1, ""); if (flag != READPIPE || sbuff[0] == 0 || !findword(spline, spcol, sbuff)) { /* read new word from pipe */ sp = sbuff; while (read(cpipe[0],sp,1) > 0 && *sp != '\n' && sp < &sbuff[ENOUGH]) sp++; *sp = 0; if (sbuff[0] == 0) { cdiag("** last word **"); return; } spline = 0; spcol = MARGIN; if (!findword(spline, spcol, sbuff)) { cdiag("!! can't find %s !!", sbuff); return; } } /* display error */ spline = ms_line; spcol = ms_col; i_display(spline); set_c(spline-topl, spcol, &in_c); cdiag(sbuff); } /* * Find the first instance of 'word' between the position indicated * by 'line' and 'col' and the end of the file. * Returns true if found, else false. * * Works by searching for the word as a string, then rejecting targets * which do not meet the word boundary requirements. * This should really be done by adding word boundary metacharacters * to ded's regular expressions. */ findword(line, col, word) int line, col; char *word; { char linebuf[ENOUGH]; register char *wp, *lp; register int endcol; /* prevent interpretation of metacharacters */ wp = word; lp = linebuf; while (*lp = *wp++) if (*lp++ == '\'') *lp++ = '\''; build_re(linebuf); do { if ( find(FORWARD, line, col, line, maxl) ) { if (ms_line == line && ms_col <= col) /* found at or before current cursor position * => no more instances of this word */ return(false); line = ms_line; col = ms_col; getline(line, linebuf); } else /* can't find it at all -- an error if this is the first * search for a new word, otherwise it may be due to the * user having corrected mistakes as he goes along (ohmygod). */ return(false); } while (col > MARGIN && alpha(linebuf[col-1]) || (endcol = col + strlength(word)) <= RIGHTCOL && alpha(linebuf[endcol])); return(true); } #endif