/* ** docmd2.c ** ** routines to actually execute commands ** ** This file is part of mse, under GPLv3. */ #include "config.h" #include #include #include #include #include #include "se.h" #include "changetty.h" #include "docmd1.h" #include "docmd2.h" #include "edit.h" #include "extern.h" #include "main.h" #include "misc.h" #include "pat.h" #include "scratch.h" #include "screen.h" #include "term.h" /* append --- append lines after "line" */ int append (int line, char str[]) { char lin[MAXLINE]; char term; int ret; int len, i, dpos, dotseen; Curln = line; if (str[0] == ':') /* text to be added is in the command line */ ret = inject (&str[1]); else { Cmdrow = Toprow + (Curln - Topln) + 1; /* 1 below Curln */ lin[0] = EOS; if (Indent > 0 || line <= 0) len = max (0, Indent - 1); else /* do auto indent */ { LINEDESC *k; k = se_gettxt (line); for (len = 0; Txt[len] == ' '; len++) ; } dpos = len; /* position for terminating '.' */ for (ret = NOSTATUS; ret == NOSTATUS; ) { if (! hwinsdel()) /* do it the old, slow way */ { if (Cmdrow > Botrow) { Cmdrow = Toprow + 1; cprow (Botrow, Toprow); adjust_window (Curln, Curln); if (First_affected > Topln) First_affected = Topln; } clrrow (Cmdrow); if (Cmdrow < Botrow) clrrow (Cmdrow + 1); } else /* try to be smart about it */ { if (Cmdrow > Botrow) { Cmdrow--; dellines (Toprow, 1); inslines (Cmdrow, 1); Topln++; } else { dellines (Botrow, 1); inslines (Cmdrow, 1); } } prompt ("apd>"); do getcmd (lin, Firstcol, &len, &term); while (term == CURSOR_UP || term == CURSOR_DOWN || term == CURSOR_SAME); dotseen = 0; if (lin[0] == '.' && lin[1] == '\n' && lin[2] == EOS) dotseen = 1; for (i = 0; i < dpos && lin[i] == ' '; i++) ; if (i == dpos && lin[dpos] == '.' && lin[dpos + 1] == '\n' && lin[dpos+2] == EOS) dotseen = 1; if (dotseen) { if (hwinsdel()) { dellines (Cmdrow, 1); inslines (Botrow, 1); } ret = OK; } else if (inject (lin) == ERR) ret = ERR; else /* inject occured */ prompt (""); /* erase prompt */ Cmdrow++; if (term != FUNNY) { if (Indent > 0) len = Indent - 1; else /* do auto indent */ for (len = 0; lin[len] == ' '; len++) ; dpos = len; lin[0] = EOS; } } Cmdrow = Botrow + 1; if (hwinsdel()) /* since we take control */ { /* of the screen, we're sure */ Sctop = Topln; /* it's still OK */ for (i = 0; i < Sclen; i++) Scline[i] = Sctop + i <= Lastln ? i : -1; } } if (Curln == 0 && Lastln > 0) /* for 0a or 1i followed by "." */ Curln = 1; if (First_affected > line) First_affected = line; tflush (); return (ret); } /* copy --- copy line1 through line2 after line3 */ int copy (int line3) { int i; int ret; LINEDESC *ptr3, *after3, *k; ret = ERR; if (Line1 <= 0) Errcode = EORANGE; else { ret = OK; Curln = line3; k = getind (Line1); for (i = Line1; i <= Line2; i++) { gtxt (k); if (inject (Txt) == ERR || intrpt ()) { ret = ERR; break; } if (Line1 < line3) k++; else k += 2; /* * inject calls blkmove, which will shift the * lines down one in the array, so we add two * instead of one to get to the next line. */ } First_affected = min (First_affected, line3 + 1); } return (ret); } /* delete --- delete lines from through to */ int se_delete (int from, int to, int *status) { LINEDESC *k1, *k2, *j1, *j2, *l1; if (from <= 0) /* can't delete line 0 */ { *status = ERR; Errcode = EORANGE; } else { if (First_affected > from) First_affected = from; blkmove (from, to, MAXBUF - 1); /* stick at end of buffer */ Lastln -= to - from + 1; /* adjust number of last line */ Curln = prevln (from); Lost_lines += Limcnt; Limcnt = to - from + 1; /* number of lines "deleted" */ /* point at first deleted */ Limbo = &Buf[MAXBUF - (to - from + 1)]; *status = OK; svdel (from, to - from + 1); Buffer_changed = YES; } return (*status); } /* join --- join a group of lines into a single line */ int join (char sub[]) { char new[MAXLINE]; int l, line, sublen; int ret; LINEDESC *k; ret = OK; if (Line1 <= 0) { Errcode = EORANGE; return (ERR); } sublen = strlen (sub) + 1; /* length of separator & EOS */ line = Line1; k = getind (line); gtxt (k); move_ (Txt, new, (int) k -> Lineleng); /* move in first chunk */ l = k -> Lineleng; for (line++; line <= Line2; line++) { if (intrpt ()) return (ERR); if (new[l - 2] == '\n') /* zap the NEWLINE */ l--; k = NEXTLINE(k); /* get the next line */ gtxt (k); if (l + sublen - 1 + k -> Lineleng - 1 > MAXLINE) /* won't fit */ { Errcode = E2LONG; return (ERR); } move_ (sub, &new[l - 1], sublen); /* insert separator string */ l += sublen - 1; move_ (Txt, &new[l - 1], (int) k -> Lineleng); /* move next line */ l += k -> Lineleng - 1; } Curln = Line2; /* all this will replace line1 through line2 */ ret = inject (new); /* inject the new line */ if (ret == OK) ret = se_delete (Line1, Line2, &ret); /* delete old lines */ Curln++; if (First_affected > Curln) First_affected = Curln; return (ret); } /* move --- move line1 through line2 after line3 */ int move (int line3) { LINEDESC *k0, *k1, *k2, *k3, *k4, *k5; if (Line1 <= 0) { Errcode = EORANGE; return (ERR); } if (Line1 <= line3 && line3 <= Line2) { Errcode = EINSIDEOUT; return (ERR); } blkmove (Line1, Line2, line3); if (line3 > Line1) { Curln = line3; } else Curln = line3 + (Line2 - Line1 + 1); Buffer_changed = YES; First_affected = min (First_affected, min (Line1, line3)); return (OK); } /* overlay --- let user edit lines directly */ void overlay (int *status) { char savtxt[MAXLINE], term, kname; static char empty[] = "\n"; int lng, vcol, lcurln, scurln; LINEDESC *indx; *status = OK; if (Line1 == 0) { Curln = 0; *status = inject (empty); if (*status == ERR) return; First_affected = 1; Line1 = 1; Line2++; } for (lcurln = Line1; lcurln <= Line2; lcurln++) { Curln = lcurln; vcol = Overlay_col - 1; do { adjust_window (Curln, Curln); updscreen (); Cmdrow = Curln - Topln + Toprow; indx = se_gettxt (Curln); lng = indx -> Lineleng; if (Txt[lng - 2] == '\n') /* clobber newline */ lng--; if (vcol < 0) vcol = lng - 1; while (lng - 1 < vcol) { Txt[lng - 1] = ' '; lng++; } Txt[lng - 1] = '\n'; Txt[lng] = EOS; move_ (Txt, savtxt, lng + 1); /* make a copy of the line */ getcmd (Txt, Firstcol, &vcol, &term); if (term == FUNNY) { if (First_affected > Curln) First_affected = Curln; Cmdrow = Botrow + 1; return; } if (strcmp (Txt, savtxt) != 0) /* was line changed? */ { kname = indx -> Markname; se_delete (Curln, Curln, status); scurln = Curln; if (*status == OK) *status = inject (Txt); if (*status == ERR) { Cmdrow = Botrow + 1; return; } indx = getind (nextln (scurln)); indx -> Markname = kname; } else { /* in case end-of-line is moved */ if (First_affected > Curln) First_affected = Curln; } switch (term) { case CURSOR_UP: if (Curln > 1) Curln--; else Curln = Lastln; break; case CURSOR_DOWN: if (Curln < Lastln) Curln++; else Curln = min (1, Lastln); break; case CURSOR_SAME: vcol = 0; break; } } while (term == CURSOR_UP || term == CURSOR_DOWN || term == CURSOR_SAME); } Cmdrow = Botrow + 1; return; } /* subst --- substitute "sub" for occurrences of pattern */ int subst (char sub[], int gflag, int glob) { char new[MAXLINE], kname; int line, m, k, lastm; int j, junk, status, subbed, ret; int tagbeg[10], tagend[10]; LINEDESC *inx; if (Globals && glob) ret = OK; else ret = ERR; if (Line1 <= 0) { Errcode = EORANGE; return (ERR); } /* the following code has been removed for your protection se_index() occasionally grabs newlines out of the character class counter in a pattern. for example [0-9] doesn't work due to this if (se_index (Pat, '\n') != -1) # never delete NEWLINE { Errcode = EBADPAT; return (ERR); } */ for (line = Line1; line <= Line2; line++) { if (intrpt ()) break; j = 0; subbed = NO; inx = se_gettxt (line); lastm = -1; for (k = 0; Txt[k] != EOS; ) { for (m = 1; m <= 9; m++) { tagbeg[m] = -1; tagend[m] = -1; } if (gflag == YES || subbed == NO) m = amatch (Txt, k, Pat, &tagbeg[1], &tagend[1]); else m = -1; if (m > -1 && lastm != m) /* replace matched text */ { subbed = YES; tagbeg[0] = k; tagend[0] = m; catsub (Txt, tagbeg, tagend, sub, new, &j, MAXLINE); lastm = m; } if (m == -1 || m == k) /* no match or null match */ { junk = addset (Txt[k], new, &j, MAXLINE); k++; } else k = m; /* skip matched text */ } if (subbed == YES) { if (addset (EOS, new, &j, MAXLINE) == NO) { ret = ERR; Errcode = E2LONG; break; } kname = inx -> Markname; se_delete (line, line, &status); /* remembers dot */ ret = inject (new); if (First_affected > Curln) First_affected = Curln; if (ret == ERR) break; inx = getind (Curln); inx -> Markname = kname; ret = OK; Buffer_changed = YES; } else /* subbed == NO */ Errcode = ENOMATCH; } return (ret); } /* uniquely_name --- mark-name line; make sure no other line has same name */ void uniquely_name (char kname, int *status) { int line; LINEDESC *k; defalt (Curln, Curln); if (Line1 <= 0) { *status = ERR; Errcode = EORANGE; return; } *status = OK; line = 0; k = Line0; do { line++; k = NEXTLINE(k); if (line == Line2) k -> Markname = kname; else if (k -> Markname == kname) k -> Markname = DEFAULTNAME; } while (line < Lastln); return; } /* draw_box --- draw or erase a box at coordinates in command line */ int draw_box (char lin[], int *i) { int left, right, col, len; int junk; LINEDESC *k; char text[MAXLINE]; char kname, ch; left = ctoi (lin, i); if (left <= 0 || left > MAXLINE) { Errcode = EBADCOL; return (ERR); } if (lin[*i] == ',') { (*i)++; SKIPBL (lin, *i); right = ctoi (lin, i); if (right <= 0 || right >= MAXLINE || left > right) { Errcode = EBADCOL; return (ERR); } } else right = left; SKIPBL (lin, *i); if (lin[*i] == '\n') ch = ' '; else ch = lin[(*i)++]; if (lin[*i] != '\n') { Errcode = EEGARB; return (ERR); } if (Line1 <= 0) { Errcode = EORANGE; return (ERR); } for (Curln = Line1; Curln <= Line2; Curln++) { k = se_gettxt (Curln); len = k -> Lineleng; move_ (Txt, text, len); if (text[len - 2] == '\n') col = len - 1; else col = len; while (col <= right) { text[col - 1] = ' '; col++; } text[col - 1] = '\n'; text[col] = EOS; if (Curln == Line1 || Curln == Line2) for (col = left; col <= right; col++) text[col - 1] = ch; else { text[left - 1] = ch; text[right - 1] = ch; } if (strcmp (text, Txt) != 0) { kname = k -> Markname; if (se_delete (Curln, Curln, &junk) == ERR || inject (text) == ERR) return (ERR); k = getind (Curln); k -> Markname = kname; Buffer_changed = YES; } } Curln = Line1; /* move to top of box */ if (First_affected > Curln) First_affected = Curln; adjust_window (Curln, Curln); updscreen (); return (OK); } /* dfltsopt --- set the 's' option to the extension on the file name */ void dfltsopt (char name[]) { int i; for (i = strlen (name) - 1; i >= 0; i--) if (name[i] == '.') { dosopt (&name[i + 1]); break; } if (i < 0) dosopt (""); } /* doshell --- escape to the Shell to run one or more Unix commands */ /* ** emulate vi: if running just a shell, redraw the screen as ** soon as the shell exits. if running a program, let the user ** redraw the screen when he/she is ready. ** ** also emulate USG Unix 5.0 ed: a ! as the first character is ** replaced by the previous shell command; an unescaped % is replaced ** by the saved file name. The expanded command is echoed. */ #define DEFAULT_PATH SHELL #define DEF_SHELL (rindex(DEFAULT_PATH, '/') ? rindex(DEFAULT_PATH, '/') + 1 : NULL) int doshell (char lin[], int *pi) { int rc; int forkstatus, childstatus; void (*save_quit)(int); void (*save_int)(int); int i, auto_redraw; char *path, *name, *p; char new_command[MAXLINE]; int j, k; static char sav_com[MAXLINE] = ""; int expanded = NO; if (Nlines == 0) /* use normal 'ed' behavior */ { tflush (); /* flush out the terminal output */ position_cursor (Nrows - 1, 0); /* bottom left corner */ if ((p = getenv ("SHELL")) == NULL || strcmp (p, DEFAULT_PATH) == 0) { path = DEFAULT_PATH; name = DEF_SHELL; /* default */ } /* on Berkeley systems, check the other shell */ else if (strcmp (p, "/bin/sh") == 0) { path = "/bin/sh"; name = "sh"; } else { if (p[0] == '/') /* full pathname there */ { /* work backwards to find just name */ path = p; i = strlen (p); while (p[i] != '/') i--; i++; /* skip '/' */ name = &p[i]; } else { char buf[MAXLINE]; memset(buf, '\0', MAXLINE); snprintf (buf, MAXLINE-1, "unknown shell, using %s", DEF_SHELL); remark (buf); path = DEFAULT_PATH; name = DEF_SHELL; } } auto_redraw = (lin[*pi] == '\n') ? YES : NO; /* build command, checking for leading !, and % anywhere */ if (lin[*pi] == '!') { if (sav_com[0] != EOS) { for (j = 0; sav_com[j] != EOS; j++) new_command[j] = sav_com[j]; if (new_command[j-1] == '\n') j--; (*pi)++; expanded = YES; } else { Errcode = ENOCMD; return (ERR); } } else j = 0; for (i = *pi; lin[i] != EOS; i++) { if (lin[i] == ESCAPE) { if (lin[i+1] != '%') { new_command[j++] = ESCAPE; new_command[j++] = lin[++i]; } else new_command[j++] = lin[++i]; } else if (lin[i] == '%') { for (k = 0; Savfil[k] != EOS; k++) new_command[j++] = Savfil[k]; expanded = YES; } else new_command[j++] = lin[i]; } if (new_command[j-1] == '\n') j--; new_command[j] = EOS; memset (sav_com, EOS, MAXLINE); strncpy (sav_com, new_command, MAXLINE-1); /* save it */ ttynormal (); t_exit (); rc = write (1, "\n\n", 2); /* clear out a line */ if (rc == -1) { Errcode = ESTUPID; return ERR; } forkstatus = fork(); if (forkstatus == -1) /* the fork failed */ { ttyedit (); t_init (); Errcode = ECANTFORK; return ERR; } if (forkstatus == 0) /* we're in the child process */ { signal (SIGINT, SIG_DFL); signal (SIGQUIT, SIG_DFL); if (strcmp (name, "sh") != 0) /* not /bin/sh */ { signal (SIGTSTP, SIG_DFL); } else { signal (SIGTSTP, SIG_IGN); } if (auto_redraw) /* no params; run a shell */ { execl (path, name, NULL); _exit (RETERR); /* exec failed, notify parent */ } else { if (expanded) /* echo it */ { printf ("%s\n", new_command); } execl (path, name, "-c", new_command, NULL); _exit (RETERR); } } /* we're in the parent process here */ save_int = signal (SIGINT, SIG_IGN); /* ignore interrupts */ save_quit = signal (SIGQUIT, SIG_IGN); while (wait (&childstatus) != forkstatus) ; save_int = signal (SIGINT, save_int); /* catch interupts */ save_quit = signal (SIGQUIT, save_quit); rc = write (1, "\n\n", 2); /* clear out some message space */ if (rc == -1) { Errcode = ESTUPID; return ERR; } Currow = Nrows - 1; Curcol = 0; if ((childstatus >> 8) != 0) { ttyedit (); t_init (); Errcode = ENOSHELL; return ERR; } /* a la vi: */ if (! auto_redraw) { int c; printf ("type return to continue: "); while ((c = getchar()) != '\n' && c != EOF) ; } ttyedit (); t_init (); restore_screen (); return OK; } else { remark ("Not implemented yet"); } return OK; }