/* vi:ts=4:sw=4 * * VIM - Vi IMproved * * Code Contributions By: Bram Moolenaar mool@oce.nl * Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ /* * cmdline.c: functions for reading in the command line and executing it */ #include "vim.h" #include "globals.h" #include "proto.h" #include "param.h" #include "cmdtab.h" #include "ops.h" /* included because we call functions in ops.c */ #include "fcntl.h" /* for chdir() */ #ifdef JP #include "jp.h" #endif #ifdef FEPCTRL #include "fepctrl.h" #endif #ifdef LATTICE # define mktemp(a) tmpnam(a) #endif /* * the history list of alternate files */ #define NUMALTFILES 20 static char *altfiles[NUMALTFILES]; /* alternate files */ static char *saltfiles[NUMALTFILES]; /* alternate files without path */ static linenr_t altlnum[NUMALTFILES]; /* line # in alternate file */ static linenr_t doecmdlnum = 0; /* line # in new file for doecmd() */ /* * variables shared between getcmdline() and redrawcmdline() */ static int cmdlen; /* number of chars on command line */ static int cmdpos; /* current cursor position */ static int cmdslen; /* lenght of command line on screen */ static int cmdspos; /* cursor position on screen */ static int cmdfirstc; /* ':', '/' or '?' */ static u_char *cmdbuff; /* pointer to command line buffer */ /* * The next two variables contain the bounds of any range given in a command. * They are set by docmdline(). */ static linenr_t line1, line2; static int forceit; static int regname; static int quitmore = 0; static int cmd_numfiles = -1; /* number of files found by filename completion */ static void putcmdline __ARGS((int, u_char *)); static void cmdchecklen __ARGS((void)); void cursorcmd __ARGS((void)); static int ccheck_abbr __ARGS((int)); static u_char *DoOneCmd __ARGS((u_char *)); static void dobang __ARGS((int, u_char *)); static int autowrite __ARGS((void)); static int dowrite __ARGS((u_char *, int)); static int doecmd __ARGS((char *, char *)); static void doshell __ARGS((char *)); static void dofilter __ARGS((u_char *, int, int)); static void domake __ARGS((char *)); static int doarglist __ARGS((char *)); static int check_readonly __ARGS((void)); static int check_changed __ARGS((int)); static int check_more __ARGS((int)); static void setaltfname __ARGS((char *, char *, linenr_t, int)); static void nextwild __ARGS((u_char *, int)); static void showmatches __ARGS((char *, int)); static char *addstar __ARGS((char *, int)); static linenr_t get_address __ARGS((u_char **)); static void do_align __ARGS((linenr_t, linenr_t, int, int)); extern char *mktemp __ARGS((char *)); extern int global_busy, global_wait; /* shared with csearch.c, message.c */ /* * getcmdline() - accept a command line starting with ':', '!', '/', or '?' * * For searches the optional matching '?' or '/' is removed. */ int getcmdline(firstc, buff) int firstc; /* either ':', '/', or '?' */ u_char *buff; /* buffer for command string */ { #ifdef JP u_char c; u_char k; #else register u_char c; #endif int nextc = 0; register int i; int retval; int hiscnt; /* current history line in use */ static char **history = NULL; /* history table */ static int hislen = 0; /* actual lengt of history table */ int newlen; /* new length of history table */ static int hisidx = -1; /* last entered entry */ char **temp; char *lookfor = NULL; /* string to match */ int j = -1; int gotesc = FALSE; /* TRUE when last char typed was */ #ifdef JPFEP int orgKanjiInput; #endif /* * set some variables for redrawcmd() */ cmdfirstc = firstc; cmdbuff = buff; cmdlen = cmdpos = 0; cmdslen = cmdspos = 1; State = CMDLINE; gotocmdline(TRUE, firstc); /* * if size of history table changed, reallocate it */ newlen = (int)p_hi; if (newlen != hislen) /* history length changed */ { if (newlen) temp = (char **)lalloc((u_long)(newlen * sizeof(char *)), TRUE); else temp = NULL; if (newlen == 0 || temp != NULL) { if (newlen > hislen) /* array becomes bigger */ { for (i = 0; i <= hisidx; ++i) temp[i] = history[i]; j = i; for ( ; i <= newlen - (hislen - hisidx); ++i) temp[i] = NULL; for ( ; j < hislen; ++i, ++j) temp[i] = history[j]; } else /* array becomes smaller */ { j = hisidx; for (i = newlen - 1; ; --i) { if (i >= 0) temp[i] = history[j]; /* copy newest entries */ else free(history[j]); /* remove older entries */ if (--j < 0) j = hislen - 1; if (j == hisidx) break; } hisidx = newlen - 1; } free(history); history = temp; hislen = newlen; } } hiscnt = hislen; /* set hiscnt to impossible history value */ #ifdef DIGRAPHS dodigraph(-1); /* init digraph typahead */ #endif #ifdef JPFEP orgKanjiInput = KanjiInput; fep_mode_switch(KanjiInput = FALSE); #endif /* collect the command string, handling '\b', @ and much more */ for (;;) { cursorcmd(); /* set the cursor on the right spot */ if (nextc) /* character remaining from CTRL-V */ { c = nextc; nextc = 0; } else { #ifdef JP jp_getchar(&c, &k, NULL); #else c = vgetc(); #endif if (c == Ctrl('C') && got_int) got_int = FALSE; } #ifdef DOSGEN /* may be used for UNIX */ if (c == Ctrl('Q') || c == Ctrl(']')) { #ifdef JP jp_getchar(&c, &k, NULL); #else c = vgetc(); #endif switch (c) { case 'h': case Ctrl('B'): c = K_LARROW; break; case 'j': case Ctrl('N'): c = K_DARROW; break; case 'k': case Ctrl('P'): c = K_UARROW; break; case 'l': case Ctrl('F'): c = K_RARROW; break; case 'H': case 'b': c = K_SLARROW; break; case 'J': c = K_SDARROW; break; case 'K': c = K_SUARROW; break; case 'L': case 'w': c = K_SRARROW; break; default: beep(); continue; } } #endif if (lookfor && c != K_SDARROW && c != K_SUARROW) { free(lookfor); lookfor = NULL; } if (cmd_numfiles > 0 && !(c == p_wc && KeyTyped) && c != Ctrl('N') && c != Ctrl('P') && c != Ctrl('A') && c != Ctrl('L')) (void)ExpandOne(NULL, FALSE, -2); /* may free expanded file names */ #ifdef DIGRAPHS c = dodigraph(c); #endif if (c == '\n' || c == '\r' || (c == ESC && !KeyTyped)) { if (ccheck_abbr(c + 0x100)) continue; outchar('\r'); flushbuf(); break; } /* hitting twice means: abandon command line */ /* wildcard expansion is only done when the key is really typed, not when it comes from a macro */ if (c == p_wc && !gotesc && KeyTyped) { if (cmd_numfiles > 0) /* typed p_wc twice */ nextwild(buff, 3); else /* typed p_wc first time */ nextwild(buff, 0); if (c == ESC) gotesc = TRUE; continue; } gotesc = FALSE; #ifndef JPFEP if (c == K_ZERO) /* NUL is stored as NL */ c = '\n'; #endif switch (c) { case BS: case DEL: case Ctrl('W'): /* * delete current character is the same as backspace on next * character, except at end of line */ if (c == DEL && cmdpos != cmdlen) { #ifdef JP if (IsKanji(buff[cmdpos])) cmdpos+=2; else #endif ++cmdpos; } if (cmdpos > 0) { j = cmdpos; if (c == Ctrl('W')) { while (cmdpos && isspace(buff[cmdpos - 1])) --cmdpos; i = isidchar(buff[cmdpos - 1]); while (cmdpos && !isspace(buff[cmdpos - 1]) && isidchar(buff[cmdpos - 1]) == i) #ifdef JP cmdpos -= IsKanji(buff[cmdpos - 1]) ? 2 : 1; #else cmdpos--; #endif } else #ifdef JP cmdpos -= IsKanji(buff[cmdpos - 1]) ? 2 : 1; #else cmdpos--; #endif cmdlen -= j - cmdpos; i = cmdpos; while (i < cmdlen) buff[i++] = buff[j++]; redrawcmd(); } else if (cmdlen == 0 && c != Ctrl('W')) { retval = FALSE; msg(""); screen_msg(FALSE, NULL); goto returncmd; /* back to cmd mode */ } continue; /* case '@': only in very old vi */ case Ctrl('U'): clearline: cmdpos = 0; cmdlen = 0; cmdslen = 1; cmdspos = 1; redrawcmd(); continue; case ESC: /* get here if p_wc != ESC or when ESC typed twice */ case Ctrl('C'): retval = FALSE; msg(""); goto returncmd; /* back to cmd mode */ case Ctrl('D'): { for (i = cmdpos; i > 0 && buff[i - 1] != ' '; --i) ; showmatches((char *)&buff[i], cmdpos - i); for (i = Rows_max - Rows; i; --i) outchar('\n'); redrawcmd(); continue; } case Ctrl('F'): case K_RARROW: case K_SRARROW: do { if (cmdpos >= cmdlen) break; #ifdef JP if (IsKanji(buff[cmdpos])) { cmdspos += 2; cmdpos ++; } else #endif cmdspos += charsize(buff[cmdpos]); ++cmdpos; } while (c == K_SRARROW && buff[cmdpos] != ' '); continue; case Ctrl('B'): case K_LARROW: case K_SLARROW: do { if (cmdpos <= 0) break; --cmdpos; #ifdef JP if (IsKanji(buff[cmdpos])) { cmdspos -= 2; cmdpos --; } else #endif cmdspos -= charsize(buff[cmdpos]); } while (c == K_SLARROW && buff[cmdpos - 1] != ' '); continue; case Ctrl('A'): /* all matches */ if (cmd_numfiles > 0) { nextwild(buff, 4); continue; } /* begin of command line */ cmdpos = 0; cmdspos = 1; continue; case Ctrl('E'): /* end of command line */ cmdpos = cmdlen; buff[cmdlen] = NUL; cmdspos = strsize((char *)buff) + 1; continue; case Ctrl('L'): /* longest common part */ nextwild(buff, 5); continue; case Ctrl('N'): /* next match */ case Ctrl('P'): /* previous match */ if (cmd_numfiles > 0) { nextwild(buff, (c == Ctrl('P')) ? 2 : 1); continue; } case K_UARROW: case K_DARROW: case K_SUARROW: case K_SDARROW: if (hislen == 0) /* no history */ continue; i = hiscnt; /* save current command string */ if (c == K_SUARROW || c == K_SDARROW) { buff[cmdpos] = NUL; if (lookfor == NULL && (lookfor = strsave((char *)buff)) == NULL) continue; j = strlen(lookfor); } for (;;) { /* one step backwards */ if (c == K_UARROW || c == K_SUARROW || c == Ctrl('P')) { if (hiscnt == hislen) /* first time */ hiscnt = hisidx; else if (hiscnt == 0 && hisidx != hislen - 1) hiscnt = hislen - 1; else if (hiscnt != hisidx + 1) --hiscnt; else /* at top of list */ break; } else /* one step forwards */ { if (hiscnt == hisidx) /* on last entry, clear the line */ { hiscnt = hislen; goto clearline; } if (hiscnt == hislen) /* not on a history line, nothing to do */ break; if (hiscnt == hislen - 1) /* wrap around */ hiscnt = 0; else ++hiscnt; } if (hiscnt < 0 || history[hiscnt] == NULL) { hiscnt = i; break; } if ((c != K_SUARROW && c != K_SDARROW) || hiscnt == i || strncmp(history[hiscnt], lookfor, (size_t)j) == 0) break; } if (hiscnt != i) /* jumped to other entry */ { strcpy((char *)buff, history[hiscnt]); cmdpos = cmdlen = strlen((char *)buff); redrawcmd(); } continue; #ifdef JPFEP case K_ZERO: case Ctrl('\\'): fep_mode_switch(KanjiInput = ! KanjiInput); cursorcmd(); continue; #endif #ifdef FEPCTRL case Ctrl('\\'): if (p_fc) { if (fep_get_mode()) fep_force_off(); else fep_force_on(); } continue; #endif case Ctrl('V'): putcmdline('^', buff); #ifdef JP get_literal(&nextc, &c, &k);/* get next (two) character(s) */ #ifdef ONEW if (p_oh) redrawcmd(); #endif #else c = get_literal(&nextc); /* get next (two) character(s) */ #endif break; #ifdef DIGRAPHS case Ctrl('K'): putcmdline('?', buff); c = vgetc(); putcmdline(c, buff); c = getdigraph(c, vgetc()); break; #endif /* DIGRAPHS */ } /* we come here if we have a normal character */ if (!isidchar(c) && ccheck_abbr(c)) continue; if (cmdlen < CMDBUFFSIZE - 2) { char str[3]; str[i = 0] = c; #ifdef JP if (IsKanji(c)) str[++ i] = k; #endif str[++ i] = NUL; cmd_insertstr(str); } cmdchecklen(); } retval = TRUE; /* when we get here we have a valid command line */ returncmd: #ifdef JPFEP fep_mode_switch(KanjiInput = orgKanjiInput); #endif buff[cmdlen] = NUL; if (hislen != 0 && cmdlen != 0) /* put line in history buffer */ { if (++hisidx == hislen) hisidx = 0; free(history[hisidx]); history[hisidx] = strsave((char *)buff); } /* * If the screen was shifted up, redraw the whole screen (later). * If the line is too long, clear it, so ruler and shown command do * not get printed in the middle of it. */ if (cmdoffset) must_redraw = CLEAR; else if (cmdslen >= sc_col) gotocmdline(TRUE, NUL); State = NORMAL; script_winsize_pp(); #ifdef FEPCTRL if (p_fc) fep_force_off(); #endif return retval; } /* * put a character on the command line. * Used for CTRL-V and CTRL-K */ static void putcmdline(c, buff) int c; u_char *buff; { int len; char buf[2]; buf[0] = c; buf[1] = 0; len = outtrans(buf, 1); outtrans((char *)(buff + cmdpos), cmdlen - cmdpos); cmdslen += len; cmdchecklen(); cmdslen -= len; cursorcmd(); } /* * Check if the command line spans more than one screen line. * The maximum number of lines is remembered. */ static void cmdchecklen() { if (cmdslen / (int)Columns > cmdoffset) cmdoffset = cmdslen / (int)Columns; } /* * this fuction is called when the screen size changes */ void redrawcmdline() { cmdoffset = 0; redrawcmd(); cursorcmd(); } /* * Redraw what is currently on the command line. */ void redrawcmd() { register int i; windgoto((int)Rows - 1 - cmdoffset, 0); outchar(cmdfirstc); cmdslen = 1; cmdspos = 1; outtrans((char *)cmdbuff, cmdlen); for (i = 0; i < cmdlen; ) { cmdslen += charsize(cmdbuff[i]); if (++i == cmdpos) cmdspos = cmdslen; } #ifdef UNIX outstr(T_EL); #else for (i = (cmdoffset + 1) * (int)Columns - cmdslen; --i > 0; ) outchar(' '); #endif cmdchecklen(); } void cursorcmd() { windgoto((int)Rows - 1 - cmdoffset + (cmdspos / (int)Columns), cmdspos % (int)Columns); } int cmd_insertstr(s) char *s; { int len, slen; u_char *cmdcurs; len = strlen(s); if (cmdlen + len < CMDBUFFSIZE - 1) { cursorcmd(); cmdcurs = cmdbuff + cmdpos; memmove(cmdcurs + len, cmdcurs, cmdlen - cmdpos + 2); memmove(cmdcurs, s, len); cmdlen += len; #ifdef ONEW if (Kconvlnum) { if (KconvAltStart == -1) { outstr(T_US); outtrans((char *)cmdbuff + cmdpos, len); outstr(T_UE); } else { outstr(T_US); outtrans((char *)cmdbuff + cmdpos, KconvAltStart); outstr(T_UE); outstr(T_TI); outtrans((char *)cmdbuff + cmdpos + KconvAltStart, KconvAltEnd - KconvAltStart); outstr(T_TP); outstr(T_US); outtrans((char *)cmdbuff + cmdpos + KconvAltEnd, len - KconvAltEnd); outstr(T_UE); } outtrans((char *)cmdbuff + cmdpos + len, cmdlen - cmdpos - len); } else #endif outtrans((char *)(cmdbuff + cmdpos), cmdlen - cmdpos); cmdpos += len; for (slen = 0; *s; s++) #ifdef JP if (IsKanji(*s)) { slen += 2; if (* ++s == NUL) /* adjust kanji error */ { *s = 'X'; *(s + 1) = NUL; } } else #endif slen += charsize(*s); cmdslen += slen; cmdspos += slen; outstr(T_EL); return len; } else return 0; } #ifdef ONEW void cmd_delchars(n) int n; { int i, slen; slen = 0; for(i = cmdpos - n ; i < cmdpos; i++) slen += IsKanji(cmdbuff[i]) ? 1 : charsize(cmdbuff[i]); for(i = cmdpos; i <= cmdlen; i++) cmdbuff[i - n] = cmdbuff[i]; cmdpos -= n; cmdlen -= n; cmdslen -= slen; cmdspos -= slen; cursorcmd(); } #define MINDIST (Columns / 4) void cmd_message(msg) char *msg; { int vslen, msglen; msglen = strlen(msg); vslen = p_fm ? cmdslen % (int)Columns + 2 : Columns - msglen - 1; if (vslen < MINDIST) vslen = MINDIST; windgoto((int)Rows - 1, cmdslen); outstr(T_EL); if (vslen + msglen <= Columns) { windgoto((int)Rows - 1, vslen); outtrans(msg, -1); cursorcmd(); } } #endif /* * Check the word in front of the cursor for an abbreviation. * Called when the non-id character "c" has been entered. * When an abbreviation is recognized it is removed from the text with * backspaces and the replacement string is inserted, followed by "c". */ static int ccheck_abbr(c) int c; { if (p_paste || no_abbr) /* no abbreviations or in paste mode */ return FALSE; return check_abbr(c, (char *)cmdbuff, cmdpos, 0); } /* * docmdline(): execute an Ex command line * * 1. If no line given, get one. * 2. Split up in parts separated with '|'. * * This function may be called recursively! */ void docmdline(cmdline) u_char *cmdline; { u_char buff[CMDBUFFSIZE]; /* command line */ u_char *nextcomm; /* * 1. If no line given: get one. */ if (cmdline == NULL) { if (!getcmdline(':', buff)) return; } else { if (strlen((char *)cmdline) > (size_t)(CMDBUFFSIZE - 2)) { emsg(e_toolong); return; } /* Make a copy of the command so we can mess with it. */ strcpy((char *)buff, (char *)cmdline); } /* * 2. Loop for each '|' separated command. * DoOneCmd will set nextcommand to NULL if there is no trailing '|'. */ for (;;) { nextcomm = DoOneCmd(buff); if (nextcomm == NULL) break; strcpy((char *)buff, (char *)nextcomm); } } /* * Execute one Ex command. * * 2. skip comment lines and leading space * 3. parse range * 4. parse command * 5. parse arguments * 6. switch on command name * * This function may be called recursively! */ static u_char * DoOneCmd(buff) u_char *buff; { u_char cmdbuf[CMDBUFFSIZE]; /* for '%' and '#' expansion */ u_char c; register u_char *p; char *q; u_char *cmd, *arg; int i; int cmdidx; int argt; register linenr_t lnum; long n; int addr_count; /* number of address specifications */ FPOS pos; int append = FALSE; /* write with append */ int usefilter = FALSE; /* filter instead of file name */ u_char *nextcomm; if (quitmore) --quitmore; /* when not editing the last file :q has to be typed twice */ /* * 2. skip comment lines and leading space, colons or bars */ for (cmd = buff; *cmd && strchr(" \t:|", *cmd) != NULL; cmd++) ; nextcomm = NULL; /* default: no next command */ if (strchr("#\"", *cmd) != NULL) /* ignore comment and empty lines */ goto doend; /* * 3. parse a range specifier of the form: addr [,addr] [;addr] .. * * where 'addr' is: * * % (entire file) * $ [+-NUM] * 'x [+-NUM] (where x denotes a currently defined mark) * . [+-NUM] * [+-NUM].. * NUM * * The cmd pointer is updated to point to the first character following the * range spec. If an initial address is found, but no second, the upper bound * is equal to the lower. */ addr_count = 0; --cmd; do { ++cmd; /* skip ',' or ';' */ line1 = line2; line2 = Curpos.lnum; /* default is current line number */ skipspace((char **)&cmd); lnum = get_address(&cmd); if (lnum == INVLNUM) { if (*cmd == '%') /* '%' - all lines */ { ++cmd; line1 = 1; line2 = line_count; ++addr_count; } } else line2 = lnum; addr_count++; if (*cmd == ';') { if (line2 == 0) Curpos.lnum = 1; else Curpos.lnum = line2; } } while (*cmd == ',' || *cmd == ';'); /* One address given: set start and end lines */ if (addr_count == 1) { line1 = line2; /* ... but only implicit: really no address given */ if (lnum == INVLNUM) addr_count = 0; } if (line1 > line2 || line2 > line_count) { emsg(e_invrange); goto doend; } /* * 4. parse command */ skipspace((char **)&cmd); /* * If we got a line, but no command, then go to the line. */ if (*cmd == NUL || *cmd == '"' || (*cmd == '|' && (nextcomm = cmd) != NULL)) { if (addr_count != 0) { if (line2 == 0) Curpos.lnum = 1; else Curpos.lnum = line2; Curpos.col = 0; cursupdate(); } goto doend; } /* * isolate the command and search for it in the command table */ p = cmd; if (*cmd != 'k') while (isalpha(*p)) ++p; if (p == cmd && strchr("@!=><&k", *p) != NULL) /* non-alpha or 'k' command */ ++p; i = (int)(p - cmd); for (cmdidx = 0; cmdidx < CMD_SIZE; ++cmdidx) if (strncmp(cmdnames[cmdidx].cmd_name, (char *)cmd, (size_t)i) == 0) break; if (i == 0 || cmdidx == CMD_SIZE) { emsg(e_invcmd); goto doend; } if (*p == '!') /* forced commands */ { ++p; forceit = TRUE; } else forceit = FALSE; /* * 5. parse arguments */ argt = cmdnames[cmdidx].cmd_argt; if (!(argt & RANGE) && addr_count) { emsg(e_norange); goto doend; } if (!(argt & ZEROR)) /* zero in range not allowed */ { if (line1 == 0) line1 = 1; if (line2 == 0) line2 = 1; } /* * for the :make command we insert the 'makeprg' option here, * so things like % get expanded */ if (cmdidx == CMD_make) { if (strlen(p_mp) + strlen((char *)p) + 2 >= (unsigned)CMDBUFFSIZE) { emsg(e_toolong); goto doend; } strcpy((char *)cmdbuf, p_mp); strcat((char *)cmdbuf, " "); strcat((char *)cmdbuf, (char *)p); strcpy((char *)buff, (char *)cmdbuf); p = buff; } arg = p; /* remember start of argument */ skipspace((char **)&arg); if ((argt & NEEDARG) && *arg == NUL) { emsg(e_argreq); goto doend; } /* * check for '|' to separate commands and '"' to start comments */ if (argt & TRLBAR) { while (*p) { if (*p == Ctrl('V')) { if (argt & USECTRLV) /* skip the CTRL-V and next char */ ++p; else /* remove CTRL-V and skip next char */ strcpy((char *)p, (char *)p + 1); } else if ((*p == '"' && !(argt & NOTRLCOM)) || *p == '|') { if (*(p - 1) == '\\') /* remove the backslash */ { strcpy((char *)p - 1, (char *)p); --p; } else { if (*p == '|') nextcomm = p + 1; *p = NUL; break; } } ++p; } if (!(argt & NOTRLCOM)) /* remove trailing spaces */ { q = (char *)arg + strlen((char *)arg); while (--q > (char *)arg && isspace(q[0]) && q[-1] != '\\' && q[-1] != Ctrl('V')) *q = NUL; } } if ((argt & DFLALL) && addr_count == 0) { line1 = 1; line2 = line_count; } regname = 0; /* accept numbered register only when no count allowed (:put) */ if ((argt & REGSTR) && (isalpha(*arg) || *arg == '.' || *arg == '"' || (!(argt & COUNT) && isdigit(*arg)))) { regname = *arg; ++arg; skipspace((char **)&arg); } if ((argt & COUNT) && isdigit(*arg)) { n = getdigits((char **)&arg); skipspace((char **)&arg); if (n <= 0) { emsg(e_zerocount); goto doend; } line1 = line2; line2 += n - 1; } if (!(argt & EXTRA) && strchr("|\"#", *arg) == NULL) /* no arguments allowed */ { emsg(e_trailing); goto doend; } if (cmdidx == CMD_write) { if (*arg == '>') /* append */ { if (*++arg != '>') /* typed wrong */ { emsg("Use w or w>>"); goto doend; } ++arg; skipspace((char **)&arg); append = TRUE; } else if (*arg == '!') /* :w !filter */ { ++arg; usefilter = TRUE; } } if (cmdidx == CMD_read) { usefilter = forceit; /* :r! filter if forceit */ if (*arg == '!') /* :r !filter */ { ++arg; usefilter = TRUE; } } /* * change '%' to Filename, '#' to altfile */ if (argt & XFILE) { for (p = arg; *p; ++p) { c = *p; if (c != '%' && c != '#') /* nothing to expand */ continue; if (*(p - 1) == '\\') /* remove escaped char */ { strcpy((char *)p - 1, (char *)p); --p; continue; } n = 1; /* length of what we expand */ if (c == '#' && *(p + 1) == '<') { /* "#<": current file name without extension */ n = 2; c = '<'; } if (c == '%' || c == '<') { if (check_fname()) goto doend; q = xFilename; } else { q = (char *)p + 1; i = (int)getdigits(&q); n = q - (char *)p; if (i >= NUMALTFILES || altfiles[i] == NULL) { emsg(e_noalt); goto doend; } doecmdlnum = altlnum[i]; if (did_cd) q = altfiles[i]; else q = saltfiles[i]; } i = strlen((char *)arg) + strlen(q) + 3; if (nextcomm) i += strlen((char *)nextcomm); if (i > CMDBUFFSIZE) { emsg(e_toolong); goto doend; } /* * we built the new argument in cmdbuf[], then copy it back to buff[] */ *p = NUL; /* truncate at the '#' or '%' */ strcpy((char *)cmdbuf, (char *)arg);/* copy up to there */ i = p - arg; /* remember the lenght */ strcat((char *)cmdbuf, q); /* append the file name */ if (c == '<' && (arg = (u_char *)strrchr(q, '.')) != NULL && arg >= (u_char *)gettail(q)) /* remove extension */ *((char *)cmdbuf + ((char *)arg - q) + i) = NUL; i = strlen((char *)cmdbuf); /* remember the end of the filename */ strcat((char *)cmdbuf, (char *)p+n);/* append what is after '#' or '%' */ p = buff + i - 1; /* remember where to continue */ if (nextcomm) /* append next command */ { i = strlen((char *)cmdbuf) + 1; strcpy((char *)cmdbuf + i, (char *)nextcomm); nextcomm = buff + i; } strcpy((char *)buff, (char *)cmdbuf);/* copy back to buff[] */ arg = buff; } /* * One file argument: expand wildcards. * Don't do this with ":r !command" or ":w !command". */ if (argt & NOSPC) { if (has_wildcard((char *)arg) && !usefilter) { if ((p = (u_char *)ExpandOne(arg, TRUE, -1)) == NULL) goto doend; if (strlen((char *)p) + arg - buff < CMDBUFFSIZE - 2) strcpy((char *)arg, (char *)p); else emsg(e_toolong); free(p); } } } /* * 6. switch on command name */ switch (cmdidx) { case CMD_quit: if (!check_more(FALSE)) /* if more files we won't exit */ exiting = TRUE; if (check_changed(FALSE) || check_more(TRUE)) { exiting = FALSE; settmode(1); break; } getout(0); case CMD_stop: case CMD_suspend: if (!forceit && Changed) autowrite(); gotocmdend(); flushbuf(); stoptermcap(); mch_suspend(); /* call machine specific function */ starttermcap(); must_redraw = CLEAR; break; case CMD_xit: case CMD_wq: if (!check_more(FALSE)) /* if more files we won't exit */ exiting = TRUE; if (((cmdidx == CMD_wq || Changed) && (check_readonly() || !dowrite(arg, FALSE))) || check_more(TRUE)) { exiting = FALSE; settmode(1); break; } getout(0); case CMD_args: if (numfiles == 0) /* no file name list */ { if (!check_fname()) /* check for no file name at all */ smsg("[%s]", Filename); break; } gotocmdline(TRUE, NUL); for (i = 0; i < numfiles; ++i) { if (i == curfile) outchar('['); outstrn(files[i]); if (i == curfile) outchar(']'); outchar(' '); } outchar('\n'); wait_return(TRUE); break; case CMD_wnext: n = line2; line1 = 1; line2 = line_count; dowrite(arg, FALSE); line2 = n; arg = (u_char *)""; /* no file list */ /*FALLTHROUGH*/ case CMD_next: if (check_changed(TRUE)) break; if (*arg != NUL) /* redefine file list */ { if (doarglist((char *)arg)) break; i = 0; } else { if (addr_count == 0) i = curfile + 1; else i = curfile + (int)line2; } donextfile: if (i < 0 || i >= numfiles) { emsg(e_nomore); break; } if (check_changed(TRUE)) break; curfile = i; doecmd(files[curfile], NULL); break; case CMD_previous: case CMD_Next: if (addr_count == 0) i = curfile - 1; else i = curfile - (int)line2; goto donextfile; case CMD_rewind: i = 0; goto donextfile; case CMD_write: if (usefilter) /* input lines to shell command */ dofilter(arg, TRUE, FALSE); else dowrite(arg, append); break; case CMD_edit: case CMD_ex: case CMD_visual: if (!forceit && Changed) /* ken */ autowrite(); doecmd((char *)arg, NULL); break; case CMD_file: if (*arg != NUL) { setfname((char *)arg, NULL); NotEdited = TRUE; maketitle(); } fileinfo(did_cd); /* print full filename if :cd used */ break; case CMD_files: #ifdef AMIGA settmode(0); /* set cooked mode, so output can be halted */ #endif for (i = 0; i < NUMALTFILES; ++i) { if (altfiles[i]) { sprintf(IObuff, "%2d \"%s\" line %ld\n", i, altfiles[i], (long)altlnum[i]); outstrn(IObuff); } flushbuf(); } #ifdef AMIGA settmode(1); #endif wait_return(TRUE); break; case CMD_read: if (usefilter) { dofilter(arg, FALSE, TRUE); /* :r!cmd */ break; } if (!u_save(line2, (linenr_t)(line2 + 1))) break; if (readfile((char *)arg, NULL, line2, FALSE)) { emsg(e_notopen); break; } updateScreen(NOT_VALID); break; case CMD_cd: case CMD_chdir: #ifdef UNIX /* * for UNIX ":cd" means: go to home directory */ if (*arg == NUL) /* use IObuff for home directory name */ { expand_env("$HOME", IObuff, IOSIZE); arg = (u_char *)IObuff; } #endif if (*arg != NUL) { if (!did_cd) { scriptfullpath(); xFilename = Filename; } did_cd = TRUE; if (chdir((char *)arg)) emsg(e_failed); break; } /*FALLTHROUGH*/ case CMD_pwd: if (dirname(IObuff, IOSIZE)) msg(IObuff); else emsg(e_unknown); break; case CMD_equal: smsg("line %ld", (long)line2); break; case CMD_list: i = p_list; p_list = 1; case CMD_number: case CMD_print: #ifdef AMIGA settmode(0); /* set cooked mode, so output can be halted */ #endif gotocmdline(TRUE, NUL); /* clear command line */ n = 0; for (;;) { if (p_nu || cmdidx == CMD_number) { sprintf(IObuff, "%7ld ", (long)line1); outstrn(IObuff); n += 8; } n += prt_line(nr2ptr(line1)); if (++line1 > line2) break; outchar('\n'); flushbuf(); n = Columns; /* call wait_return later */ } #ifdef AMIGA settmode(1); #endif if (cmdidx == CMD_list) p_list = i; /* * if we have one line that runs into the shown command, * or more than one line, call wait_return() */ if (n >= sc_col || global_busy) { outchar('\n'); wait_return(TRUE); } break; case CMD_shell: doshell(NULL); break; case CMD_tag: dotag((char *)arg, 0, addr_count ? (int)line2 : 1); break; case CMD_pop: dotag("", 1, addr_count ? (int)line2 : 1); break; case CMD_tags: dotags(); break; case CMD_marks: domarks(); break; case CMD_jumps: dojumps(); break; case CMD_digraphs: #ifdef DIGRAPHS if (*arg) putdigraph((char *)arg); else listdigraphs(); #else emsg("No digraphs in this version"); #endif /* DIGRAPHS */ break; case CMD_set: doset((char *)arg); break; case CMD_abbreviate: case CMD_cabbrev: case CMD_iabbrev: case CMD_cnoreabbrev: case CMD_inoreabbrev: case CMD_noreabbrev: case CMD_unabbreviate: case CMD_cunabbrev: case CMD_iunabbrev: i = ABBREV; goto doabbr; /* almost the same as mapping */ case CMD_cmap: case CMD_imap: case CMD_map: case CMD_cnoremap: case CMD_inoremap: case CMD_noremap: /* * If we are sourcing .exrc or .vimrc in current directory we * print the mappings for security reasons. */ if (secure) { secure = 2; outtrans((char *)cmd, -1); outchar('\n'); } case CMD_cunmap: case CMD_iunmap: case CMD_unmap: i = 0; doabbr: if (*cmd == 'c') /* cmap, cunmap, cnoremap, etc. */ { i += CMDLINE; ++cmd; } else if (*cmd == 'i') /* imap, iunmap, inoremap, etc. */ { i += INSERT; ++cmd; } else if (forceit || i) /* map!, unmap!, noremap!, abbrev */ i += INSERT + CMDLINE; else i += NORMAL; /* map, unmap, noremap */ switch (domap((*cmd == 'n') ? 2 : (*cmd == 'u'), (char *)arg, i)) { case 1: emsg(e_invarg); break; case 2: emsg(e_nomap); break; case 3: emsg(e_ambmap); break; } break; case CMD_display: outchar('\n'); dodis(); /* display buffer contents */ break; case CMD_help: help(); break; case CMD_version: #ifdef JP { static char longversion[MAX_COLUMNS + 1] = ""; #ifdef ONEW extern char *Onew_version(); #endif #ifdef DOSGEN extern char *longDosVersion(); #endif if (!longversion[0]) #ifdef ONEW sprintf(longversion, "%s [on %s] + %s", longJpVersion, longVersion, Onew_version()); #else # ifdef DOSGEN sprintf(longversion, "%s, %s [on %s]", longJpVersion, longDosVersion, longVersion); # else sprintf(longversion, "%s [on %s]", longJpVersion, longVersion); # endif #endif msg(longversion); } #else msg(longVersion); #endif break; case CMD_winsize: line1 = getdigits((char **)&arg); skipspace((char **)&arg); line2 = getdigits((char **)&arg); set_winsize((int)line1, (int)line2, TRUE); break; case CMD_delete: case CMD_yank: case CMD_rshift: case CMD_lshift: yankbuffer = regname; startop.lnum = line1; endop.lnum = line2; nlines = line2 - line1 + 1; mtype = MLINE; Curpos.lnum = line1; switch (cmdidx) { case CMD_delete: dodelete(); break; case CMD_yank: doyank(FALSE); break; case CMD_rshift: doshift(RSHIFT); break; case CMD_lshift: doshift(LSHIFT); break; } break; case CMD_put: yankbuffer = regname; Curpos.lnum = line2; doput(forceit ? BACKWARD : FORWARD, -1L); break; case CMD_t: case CMD_copy: case CMD_move: n = get_address(&arg); if (n == INVLNUM) { emsg(e_invaddr); break; } if (cmdidx == CMD_move) { if (n >= line1 && n < line2 && line2 > line1) { emsg("Move lines into themselves"); break; } if (n >= line1) { --n; Curpos.lnum = n - (line2 - line1) + 1; } else Curpos.lnum = n + 1; while (line1 <= line2) { /* this undo is not efficient, but it works */ u_save(line1 - 1, line1 + 1); q = delsline(line1, FALSE); u_save(n, n + 1); appendline(n, q); if (n < line1) { ++n; ++line1; } else --line2; } } else { /* * there are three situations: * 1. destination is above line1 * 2. destination is between line1 and line2 * 3. destination is below line2 * * n = destination (when starting) * Curpos.lnum = destination (while copying) * line1 = start of source (while copying) * line2 = end of source (while copying) */ u_save(n, n + 1); Curpos.lnum = n; lnum = line2 - line1 + 1; while (line1 <= line2) { appendline(Curpos.lnum, save_line(nr2ptr(line1))); /* situation 2: skip already copied lines */ if (line1 == n) line1 = Curpos.lnum; ++line1; if (Curpos.lnum < line1) ++line1; if (Curpos.lnum < line2) ++line2; ++Curpos.lnum; } msgmore((long)lnum); } u_clearline(); Curpos.col = 0; updateScreen(NOT_VALID); break; case CMD_and: case CMD_substitute: dosub(line1, line2, (char *)arg, &nextcomm); break; case CMD_join: Curpos.lnum = line1; if (line1 == line2) { if (line2 == line_count) { beep(); break; } ++line2; } dodojoin(line2 - line1 + 1, !forceit, TRUE); break; case CMD_global: if (forceit) *cmd = 'v'; case CMD_vglobal: doglob(*cmd, line1, line2, (char *)arg); break; case CMD_at: /* :[addr]@r */ Curpos.lnum = line2; if (!doexecbuf(*arg)) /* put the register in mapbuf */ beep(); else docmdline(NULL); /* execute from the mapbuf */ break; case CMD_bang: dobang(addr_count, arg); break; case CMD_undo: u_undo(1); break; case CMD_redo: u_redo(1); break; case CMD_source: if (forceit) /* :so! read vi commands */ openscript((char *)arg); else if (dosource((char *)arg)) /* :so read ex commands */ emsg(e_notopen); break; #ifdef JP case CMD_mkjvimrc: #ifdef JVIMRC_FILE if (*arg == NUL) arg = (u_char *)JVIMRC_FILE; /*FALLTHROUGH*/ #endif #endif case CMD_mkvimrc: if (*arg == NUL) arg = (u_char *)VIMRC_FILE; /*FALLTHROUGH*/ case CMD_mkexrc: { FILE *fd; if (*arg == NUL) arg = (u_char *)EXRC_FILE; #ifdef UNIX /* with Unix it is possible to open a directory */ if (isdir((char *)arg) > 0) { emsg2("\"%s\" is a directory", (char *)arg); break; } #endif if (!forceit && (fd = fopen((char *)arg, "r")) != NULL) { fclose(fd); emsg2("\"%s\" exists (use ! to override)", (char *)arg); break; } if ((fd = fopen((char *)arg, "w")) == NULL) { emsg2("Cannot open \"%s\" for writing", (char *)arg); break; } if (makemap(fd) || makeset(fd) || fclose(fd)) emsg(e_write); break; } case CMD_cc: qf_jump(atoi((char *)arg)); break; case CMD_cf: if (*arg != NUL) { /* * Great trick: Insert 'ef=' before arg. * Always ok, because "cf " must be there. */ arg -= 3; arg[0] = 'e'; arg[1] = 'f'; arg[2] = '='; doset((char *)arg); } qf_init(); break; case CMD_cl: qf_list(); break; case CMD_cn: qf_jump(-1); break; case CMD_cp: qf_jump(-2); break; case CMD_cq: getout(1); /* this does not always work. why? */ case CMD_mark: case CMD_k: pos = Curpos; /* save Curpos */ Curpos.lnum = line2; Curpos.col = 0; setmark(*arg); /* set mark */ Curpos = pos; /* restore Curpos */ break; #ifdef SETKEYMAP case CMD_setkeymap: set_keymap(arg); break; #endif case CMD_center: case CMD_right: case CMD_left: do_align(line1, line2, atoi((char *)arg), cmdidx == CMD_center ? 0 : cmdidx == CMD_right ? 1 : -1); break; case CMD_make: domake((char *)arg); break; #ifdef MDOMAIN case CMD_nget: do_nget((char *)arg); break; #endif default: emsg(e_invcmd); } doend: forceit = FALSE; /* reset now so it can be used in getfile() */ return nextcomm; } /* * handle the :! command. * We replace the extra bangs by the previously entered command and remember * the command. */ static void dobang(addr_count, arg) int addr_count; u_char *arg; { static char *prevcmd = NULL; /* the previous command */ char *t; char *trailarg; int len; /* * Disallow shell commands from .exrc and .vimrc in current directory for * security reasons. */ if (secure) { secure = 2; emsg(e_curdir); return; } len = strlen((char *)arg) + 1; if (Changed) autowrite(); /* * try to find an embedded bang, like in :! ! [args] * (:!! is indicated by the 'forceit' variable) */ trailarg = (char *)arg; skiptospace(&trailarg); skipspace(&trailarg); if (*trailarg == '!') *trailarg++ = NUL; else trailarg = NULL; if (forceit || trailarg != NULL) /* use the previous command */ { if (prevcmd == NULL) { emsg(e_noprev); return; } len += strlen(prevcmd) * (trailarg != NULL && forceit ? 2 : 1); } if (len > CMDBUFFSIZE) { emsg(e_toolong); return; } if ((t = alloc(len)) == NULL) return; *t = NUL; if (forceit) strcpy(t, prevcmd); strcat(t, (char *)arg); if (trailarg != NULL) { strcat(t, prevcmd); strcat(t, trailarg); } free(prevcmd); prevcmd = t; if (bangredo) /* put cmd in redo buffer for ! command */ { AppendToRedobuff(prevcmd); AppendToRedobuff("\n"); bangredo = FALSE; } /* echo the command */ gotocmdline(TRUE, ':'); if (addr_count) /* :range! */ { outnum((long)line1); outchar(','); outnum((long)line2); } outchar('!'); outtrans(prevcmd, -1); if (addr_count == 0) /* :! */ doshell(prevcmd); else /* :range! */ dofilter((u_char *)prevcmd, TRUE, TRUE); } static int autowrite() { if (!p_aw || check_readonly() || check_fname()) return FALSE; return (writeit(Filename, sFilename, (linenr_t)1, line_count, 0, 0, TRUE)); } static int dowrite(arg, append) u_char *arg; int append; { FILE *f; int other; /* * if we have a new file name put it in the list of alternate file names */ other = otherfile((char *)arg); if (*arg != NUL && other) setaltfname(strsave((char *)arg), strsave((char *)arg), (linenr_t)1, TRUE); /* * writing to the current file is not allowed in readonly mode */ if ((*arg == NUL || !other) && check_readonly()) return FALSE; /* * write to current file */ if (*arg == NUL || !other) { if (check_fname()) return FALSE; return (writeit(Filename, sFilename, line1, line2, append, forceit, TRUE)); } /* * write to other file; overwriting only allowed with '!' */ if (!forceit && !append && !p_wa && (f = fopen((char *)arg, "r")) != NULL) { /* don't overwrite existing file */ fclose(f); #ifdef UNIX /* with UNIX it is possible to open a directory */ if (isdir((char *)arg) > 0) emsg2("\"%s\" is a directory", (char *)arg); else #endif emsg(e_exists); return 0; } return (writeit((char *)arg, NULL, line1, line2, append, forceit, TRUE)); } static int doecmd(arg, sarg) char *arg; char *sarg; { int setalt; char *command = NULL; int redraw_save; linenr_t newlnum; newlnum = doecmdlnum; doecmdlnum = 0; /* reset it for next time */ if (*arg == '+') /* :e +[command] file */ { ++arg; if (isspace(*arg)) command = "$"; else { command = arg; while (*arg && !isspace(*arg)) ++arg; } if (*arg) *arg++ = NUL; skipspace(&arg); } if (sarg == NULL) sarg = arg; #ifdef AMIGA fname_case(arg); /* set correct case for filename */ #endif setalt = (*arg != NUL && otherfile(arg)); if (check_changed(FALSE)) { if (setalt) setaltfname(strsave(arg), strsave(sarg), (linenr_t)1, TRUE); return FALSE; } if (setalt) { setaltfname(Filename, sFilename, Curpos.lnum, FALSE); Filename = NULL; sFilename = NULL; setfname(arg, sarg); } else if (newlnum == 0) newlnum = Curpos.lnum; maketitle(); if (check_fname()) return FALSE; /* clear mem and read file */ freeall(); filealloc(); startop.lnum = 0; /* clear '[ and '] marks */ endop.lnum = 0; redraw_save = RedrawingDisabled; RedrawingDisabled = TRUE; /* don't redraw until the cursor is in * the right line */ startscript(); /* re-start auto script file */ readfile(Filename, sFilename, (linenr_t)0, setalt ? TRUE : FALSE); /* ken */ if (newlnum && command == NULL) { if (newlnum != INVLNUM) Curpos.lnum = newlnum; else Curpos.lnum = line_count; Curpos.col = 0; } UNCHANGED; if (command) docmdline((u_char *)command); RedrawingDisabled = redraw_save; /* cursupdate() will redraw the screen later */ if (p_im) stuffReadbuff("i"); /* start editing in insert mode */ return TRUE; } static void doshell(cmd) char *cmd; { /* * Disallow shell commands from .exrc and .vimrc in current directory for * security reasons. */ if (secure) { secure = 2; emsg(e_curdir); return; } stoptermcap(); outchar('\n'); /* shift screen one line up */ /* warning message before calling the shell */ if (p_warn && Changed) { gotocmdline(TRUE, NUL); outstr("[No write since last change]\n"); } call_shell(cmd, 0, TRUE); #ifdef AMIGA wait_return(!term_console); /* see below */ #else wait_return(TRUE); /* includes starttermcap() */ #endif /* * In an Amiga window redrawing is caused by asking the window size. * If we got an interrupt this will not work. The chance that the window * size is wrong is very small, but we need to redraw the screen. */ #ifdef AMIGA if (term_console) { outstr("\033[0 q"); /* get window size */ if (got_int) must_redraw = CLEAR; /* if got_int is TRUE we have to redraw */ else must_redraw = FALSE; /* no extra redraw needed */ } #endif /* AMIGA */ } /* * dofilter: filter lines through a command given by the user * * We use temp files and the call_shell() routine here. This would normally * be done using pipes on a UNIX machine, but this is more portable to * the machines we usually run on. The call_shell() routine needs to be able * to deal with redirection somehow, and should handle things like looking * at the PATH env. variable, and adding reasonable extensions to the * command name given by the user. All reasonable versions of call_shell() * do this. * We use input redirection if do_in is TRUE. * We use output redirection if do_out is TRUE. */ static void dofilter(buff, do_in, do_out) u_char *buff; int do_in, do_out; { #ifdef LATTICE char itmp[L_tmpnam]; /* use tmpnam() */ char otmp[L_tmpnam]; #else char itmp[TMPNAMELEN]; char otmp[TMPNAMELEN]; #endif linenr_t linecount; /* * Disallow shell commands from .exrc and .vimrc in current directory for * security reasons. */ if (secure) { secure = 2; emsg(e_curdir); return; } if (*buff == NUL) /* no filter command */ return; linecount = line2 - line1 + 1; Curpos.lnum = line1; Curpos.col = 0; /* cursupdate(); */ /* * 1. Form temp file names * 2. Write the lines to a temp file * 3. Run the filter command on the temp file * 4. Read the output of the command into the buffer * 5. Delete the original lines to be filtered * 6. Remove the temp files */ #ifndef LATTICE /* for lattice we use tmpnam(), which will make its own name */ strcpy(itmp, TMPNAME1); strcpy(otmp, TMPNAME2); #endif if ((do_in && *mktemp(itmp) == NUL) || (do_out && *mktemp(otmp) == NUL)) { emsg(e_notmp); return; } /* * ! command will be overwritten by next mesages * This is a trade off between showing the command and not scrolling the * text one line up (problem on slow terminals). */ must_redraw = CLEAR; /* screen has been shifted up one line */ if (do_in && !writeit(itmp, NULL, line1, line2, FALSE, 0, FALSE)) { outchar('\n'); /* keep message from writeit() */ emsg(e_notcreate); return; } if (!do_out) outchar('\n'); #ifdef UNIX /* * put braces around the command (for concatenated commands) */ sprintf(IObuff, "(%s)", (char *)buff); if (do_in) { strcat(IObuff, " < "); strcat(IObuff, itmp); } if (do_out) { strcat(IObuff, " > "); strcat(IObuff, otmp); } #else /* * for shells that don't understand braces around commands, at least allow * the use of commands in a pipe. */ strcpy(IObuff, (char *)buff); if (do_in) { char *p; /* * If there is a pipe, we have to put the '<' in front of it */ p = strchr(IObuff, '|'); if (p) *p = NUL; strcat(IObuff, " < "); strcat(IObuff, itmp); p = strchr((char *)buff, '|'); if (p) strcat(IObuff, p); } if (do_out) { strcat(IObuff, " > "); strcat(IObuff, otmp); } #endif call_shell(IObuff, 1, FALSE); /* errors are ignored, so you can see the error messages from the command; use 'u' to fix the text */ if (do_out) { if (!u_save((linenr_t)(line2), (linenr_t)(line2 + 1))) { linecount = 0; goto error; } if (readfile(otmp, NULL, line2, FALSE)) { outchar ('\n'); emsg(e_notread); linecount = 0; goto error; } if (do_in) { Curpos.lnum = line1; dellines(linecount, TRUE, TRUE); } } else { error: wait_return(FALSE); } updateScreen(CLEAR); /* do this before messages below */ if (linecount > p_report) { if (!do_in && do_out) msgmore(linecount); else smsg("%ld lines filtered", (long)linecount); } remove(itmp); remove(otmp); return; } static void domake(arg) char *arg; { if (*p_ef == NUL) { emsg("errorfile option not set"); return; } if (Changed) autowrite(); remove(p_ef); outchar(':'); outstr(arg); /* show what we are doing */ #ifdef UNIX sprintf(IObuff, "%s |& tee %s", arg, p_ef); #else sprintf(IObuff, "%s > %s", arg, p_ef); #endif doshell(IObuff); #ifdef AMIGA flushbuf(); vpeekc(); /* read window status report and redraw before message */ #endif qf_init(); remove(p_ef); } /* * Redefine the argument list to 'str'. * Return TRUE for failure. */ static int doarglist(str) char *str; { int new_numfiles = 0; char **new_files = NULL; int exp_numfiles; char **exp_files; char **t; char *p; int inquote; int i; while (*str) { /* * create a new entry in new_files[] */ t = (char **)lalloc((u_long)(sizeof(char *) * (new_numfiles + 1)), TRUE); if (t != NULL) for (i = new_numfiles; --i >= 0; ) t[i] = new_files[i]; free(new_files); if (t == NULL) return TRUE; new_files = t; new_files[new_numfiles++] = str; /* * isolate one argument, taking quotes */ inquote = FALSE; for (p = str; *str; ++str) { if (*str == '\\' && *(str + 1) != NUL) *p++ = *++str; else { if (!inquote && isspace(*str)) break; if (*str == '"') inquote ^= TRUE; else *p++ = *str; } } skipspace(&str); *p = NUL; } if (ExpandWildCards(new_numfiles, new_files, &exp_numfiles, &exp_files, FALSE, TRUE) != 0) return TRUE; else if (exp_numfiles == 0) { emsg(e_nomatch); return TRUE; } if (files_exp) /* files[] has been allocated, free it */ FreeWild(numfiles, files); else files_exp = TRUE; files = exp_files; numfiles = exp_numfiles; return FALSE; } void gotocmdline(clr, firstc) int clr; int firstc; { int i; if (clr) /* clear the bottom line(s) */ { for (i = 0; i <= cmdoffset; ++i) { windgoto((int)Rows - i - 1, 0); clear_line(); } redraw_msg = TRUE; } windgoto((int)Rows - cmdoffset - 1, 0); if (firstc) outchar(firstc); } void gotocmdend() { windgoto((int)Rows - 1, 0); outchar('\n'); } static int check_readonly() { if (!forceit && p_ro) { emsg(e_readonly); return TRUE; } return FALSE; } static int check_changed(checkaw) int checkaw; { if (!forceit && Changed && (!checkaw || !autowrite())) { emsg(e_nowrtmsg); return TRUE; } return FALSE; } int check_fname() { if (Filename == NULL) { emsg(e_noname); return TRUE; } return FALSE; } static int check_more(message) int message; /* when FALSE check only, no messages */ { if (!forceit && curfile + 1 < numfiles && quitmore == 0) { if (message) { emsg(e_more); quitmore = 2; /* next try to quit is allowed */ } return TRUE; } return FALSE; } /* * try to abandon current file and edit "fname" * return 1 for "normal" error, 2 for "not written" error, 0 for success * -1 for succesfully opening another file */ int getfile(fname, sfname, setpm) char *fname; char *sfname; int setpm; { int other; other = otherfile(fname); if (other && !forceit && Changed && !autowrite()) { emsg(e_nowrtmsg); return 2; /* file has been changed */ } if (setpm) setpcmark(); if (!other) return 0; /* it's in the same file */ if (doecmd(fname, sfname)) return -1; /* opened another file */ return 1; /* error encountered */ } /* * return TRUE if alternate file n is the same as the current file */ int samealtfile(n) int n; { if (n < NUMALTFILES && altfiles[n] != NULL && Filename != NULL && fnamecmp(altfiles[n], Filename) == 0) return TRUE; return FALSE; } /* * get alternate file n * set linenr to lnum or altlnum if lnum == 0 * if (setpm) setpcmark * return 1 for failure, 0 for success */ int getaltfile(n, lnum, setpm) int n; linenr_t lnum; int setpm; { if (n < 0 || n >= NUMALTFILES || altfiles[n] == NULL) { emsg(e_noalt); return 1; } if (lnum == 0) lnum = altlnum[n]; /* altlnum may be changed by getfile() */ RedrawingDisabled = TRUE; if (getfile(altfiles[n], saltfiles[n], setpm) <= 0) { RedrawingDisabled = FALSE; if (lnum == 0 || lnum > line_count) /* check for valid lnum */ Curpos.lnum = 1; else Curpos.lnum = lnum; Curpos.col = 0; return 0; } RedrawingDisabled = FALSE; return 1; } /* * get name of "n"th alternate file */ char * getaltfname(n) int n; { if (n >= NUMALTFILES) return NULL; return altfiles[n]; } /* * put name "arg" in the list of alternate files. * "arg" must have been allocated * "lnum" is the default line number when jumping to the file * "newfile" must be TRUE when "arg" != current file */ static void setaltfname(arg, sarg, lnum, newfile) char *arg; char *sarg; linenr_t lnum; int newfile; { int i; free(altfiles[NUMALTFILES - 1]); free(saltfiles[NUMALTFILES - 1]); for (i = NUMALTFILES - 1; i > 0; --i) { altfiles[i] = altfiles[i - 1]; saltfiles[i] = saltfiles[i - 1]; altlnum[i] = altlnum[i - 1]; } incrmarks(); /* increment file number for all jumpmarks */ incrtags(); /* increment file number for all tags */ if (newfile) { decrmarks(); /* decrement file number for jumpmarks in current file */ decrtags(); /* decrement file number for tags in current file */ } altfiles[0] = arg; saltfiles[0] = sarg; altlnum[0] = lnum; } static void nextwild(buff, type) u_char *buff; int type; { int i; char *p1, *p2; int oldlen; int difflen; outstr("..."); /* show that we are busy */ flushbuf(); i = cmdslen; cmdslen = cmdpos + 4; cmdchecklen(); /* check if we caused a scrollup */ cmdslen = i; for (i = cmdpos; i > 0 && buff[i - 1] != ' '; --i) ; oldlen = cmdpos - i; /* add a "*" to the file name and expand it */ if ((p1 = addstar((char *)&buff[i], oldlen)) != NULL) { if ((p2 = ExpandOne((u_char *)p1, FALSE, type)) != NULL) { if (cmdlen + (difflen = strlen(p2) - oldlen) > CMDBUFFSIZE - 4) emsg(e_toolong); else { strncpy((char *)&buff[cmdpos + difflen], (char *)&buff[cmdpos], (size_t)(cmdlen - cmdpos)); strncpy((char *)&buff[i], p2, strlen(p2)); cmdlen += difflen; cmdpos += difflen; } free(p2); } free(p1); } redrawcmd(); } /* * Do wildcard expansion on the string 'str'. * Return a pointer to alloced memory containing the new string. * Return NULL for failure. * * mode = -2: only release file names * mode = -1: normal expansion, do not keep file names * mode = 0: normal expansion, keep file names * mode = 1: use next match in multiple match * mode = 2: use previous match in multiple match * mode = 3: use next match in multiple match and wrap to first * mode = 4: return all matches concatenated * mode = 5: return longest matched part */ char * ExpandOne(str, list_notfound, mode) u_char *str; int list_notfound; int mode; { char *ss = NULL; static char **cmd_files = NULL; /* list of input files */ static int findex; int i, found = 0; int multmatch = FALSE; u_long len; char *filesuf, *setsuf, *nextsetsuf; int filesuflen, setsuflen; /* * first handle the case of using an old match */ if (mode >= 1 && mode < 4) { if (cmd_numfiles > 0) { if (mode == 2) --findex; else /* mode == 1 || mode == 3 */ ++findex; if (findex < 0) findex = 0; if (findex > cmd_numfiles - 1) { if (mode == 3) findex = 0; else findex = cmd_numfiles - 1; } return strsave(cmd_files[findex]); } else return NULL; } /* free old names */ if (cmd_numfiles != -1 && mode < 4) { FreeWild(cmd_numfiles, cmd_files); cmd_numfiles = -1; } findex = 0; if (mode == -2) /* only release file name */ return NULL; if (cmd_numfiles == -1) { if (ExpandWildCards(1, (char **)&str, &cmd_numfiles, &cmd_files, FALSE, list_notfound) != 0) /* error: do nothing */; else if (cmd_numfiles == 0) emsg(e_nomatch); else if (mode < 4) { if (cmd_numfiles > 1) /* more than one match; check suffixes */ { found = -2; for (i = 0; i < cmd_numfiles; ++i) { if ((filesuf = strrchr(cmd_files[i], '.')) != NULL) { filesuflen = strlen(filesuf); for (setsuf = p_su; *setsuf; setsuf = nextsetsuf) { if ((nextsetsuf = strchr(setsuf + 1, '.')) == NULL) nextsetsuf = setsuf + strlen(setsuf); setsuflen = (int)(nextsetsuf - setsuf); if (filesuflen == setsuflen && strncmp(setsuf, filesuf, (size_t)setsuflen) == 0) break; } if (*setsuf) /* suffix matched: ignore file */ continue; } if (found >= 0) { multmatch = TRUE; break; } found = i; } } if (multmatch || found < 0) { emsg(e_toomany); found = 0; /* return first one */ multmatch = TRUE; /* for found < 0 */ } if (found >= 0 && !(multmatch && mode == -1)) ss = strsave(cmd_files[found]); } } if (mode == 5 && cmd_numfiles > 0) /* find longest common part */ { for (len = 0; cmd_files[0][len]; ++len) { for (i = 0; i < cmd_numfiles; ++i) { #ifdef AMIGA if (toupper(cmd_files[i][len]) != toupper(cmd_files[0][len])) #else if (cmd_files[i][len] != cmd_files[0][len]) #endif break; } if (i < cmd_numfiles) break; } ss = alloc((unsigned)len + 1); if (ss) { strncpy(ss, cmd_files[0], (size_t)len); ss[len] = NUL; } multmatch = TRUE; /* don't free the names */ findex = -1; /* next p_wc gets first one */ } if (mode == 4 && cmd_numfiles > 0) /* concatenate all file names */ { len = 0; for (i = 0; i < cmd_numfiles; ++i) len += strlen(cmd_files[i]) + 1; ss = lalloc(len, TRUE); if (ss) { *ss = NUL; for (i = 0; i < cmd_numfiles; ++i) { strcat(ss, cmd_files[i]); if (i != cmd_numfiles - 1) strcat(ss, " "); } } } if (!multmatch || mode == -1 || mode == 4) { FreeWild(cmd_numfiles, cmd_files); cmd_numfiles = -1; } return ss; } /* * show all filenames that match the string "file" with length "len" */ static void showmatches(file, len) char *file; int len; { char *file_str; int num_files; char **files_found; int i, j, k; int maxlen; int lines; int columns; file_str = addstar(file, len); /* add star to file name */ if (file_str != NULL) { outchar('\n'); flushbuf(); /* find all files that match the description */ ExpandWildCards(1, &file_str, &num_files, &files_found, FALSE, FALSE); /* find the maximum length of the file names */ maxlen = 0; for (i = 0; i < num_files; ++i) { j = strlen(files_found[i]); if (j > maxlen) maxlen = j; } /* compute the number of columns and lines for the listing */ maxlen += 2; /* two spaces between file names */ columns = ((int)Columns + 2) / maxlen; if (columns < 1) columns = 1; lines = (num_files + columns - 1) / columns; /* list the files line by line */ #ifdef AMIGA settmode(0); /* allow output to be halted */ #endif for (i = 0; i < lines; ++i) { for (k = i; k < num_files; k += lines) { if (k > i) for (j = maxlen - strlen(files_found[k - lines]); --j >= 0; ) outchar(' '); j = isdir(files_found[k]); /* highlight directories */ if (j > 0) { #ifdef AMIGA if (term_console) outstr("\033[33m"); /* use highlight color */ else #endif /* AMIGA */ outstr(T_TI); } outstrn(files_found[k]); if (j > 0) { #ifdef AMIGA if (term_console) outstr("\033[0m"); /* use normal color */ else #endif /* AMIGA */ outstr(T_TP); } } outchar('\n'); flushbuf(); } free(file_str); FreeWild(num_files, files_found); #ifdef AMIGA settmode(1); #endif for (i = cmdoffset; --i >= 0; ) /* make room for the command */ outchar('\n'); must_redraw = CLEAR; /* must redraw later */ } } /* * copy the file name into allocated memory and add a '*' at the end */ static char * addstar(fname, len) char *fname; int len; { char *retval; #ifdef MSDOS int i; #endif retval = alloc(len + 4); if (retval != NULL) { strncpy(retval, fname, (size_t)len); #ifdef MSDOS /* * if there is no dot in the file name, add "*.*" instead of "*". */ for (i = len - 1; i >= 0; --i) if (strchr(".\\/:", retval[i])) break; if (i < 0 || retval[i] != '.') { retval[len++] = '*'; retval[len++] = '.'; } #endif retval[len] = '*'; retval[len + 1] = 0; } return retval; } /* * dosource: read the file "fname" and execute its lines as EX commands * * This function may be called recursively! */ int dosource(fname) register char *fname; { register FILE *fp; register int len; #ifdef MSDOS int error = FALSE; #endif expand_env(fname, IObuff, IOSIZE); /* use IObuff for expanded name */ if ((fp = fopen(IObuff, READBIN)) == NULL) return 1; len = 0; while (fgets(IObuff + len, IOSIZE - len, fp) != NULL && !got_int) { len = strlen(IObuff) - 1; if (len >= 0 && IObuff[len] == '\n') /* remove trailing newline */ { /* escaped newline, read more */ if (len > 0 && len < IOSIZE && IObuff[len - 1] == Ctrl('V')) { IObuff[len - 1] = '\n'; /* remove CTRL-V */ continue; } #ifdef MSDOS if (len > 0 && IObuff[len - 1] == '\r') /* trailing CR-LF */ --len; else { if (!error) emsg("Warning: Wrong line separator, ^M may be missing"); error = TRUE; /* lines like ":map xx yy^M" will fail */ } #endif IObuff[len] = NUL; } breakcheck(); /* check for ^C here, so recursive :so will be broken */ #ifdef JP { char tmp[IOSIZE]; len = kanjiconvsfrom(IObuff, len, tmp, IOSIZE, NULL, JP_READ, NULL); tmp[len] = NUL; docmdline((u_char *)tmp); } #else docmdline((u_char *)IObuff); #endif len = 0; } fclose(fp); if (got_int) emsg(e_interr); return 0; } /* * get single EX address */ static linenr_t get_address(ptr) u_char **ptr; { linenr_t curpos_lnum = Curpos.lnum; int c; int i; long n; u_char *cmd; FPOS pos; FPOS *fp; linenr_t lnum; cmd = *ptr; skipspace((char **)&cmd); lnum = INVLNUM; do { switch (*cmd) { case '.': /* '.' - Cursor position */ ++cmd; lnum = curpos_lnum; break; case '$': /* '$' - last line */ ++cmd; lnum = line_count; break; case '\'': /* ''' - mark */ if (*++cmd == NUL || (fp = getmark(*cmd++, FALSE)) == NULL) { emsg(e_umark); goto error; } lnum = fp->lnum; break; case '/': case '?': /* '/' or '?' - search */ c = *cmd++; pos = Curpos; /* save Curpos */ Curpos.col = -1; /* searchit() will increment the col */ if (c == '/') { if (Curpos.lnum == line_count) /* :/pat on last line */ Curpos.lnum = 1; else ++Curpos.lnum; } searchcmdlen = 0; if (dosearch(c, (char *)cmd, FALSE, (long)1, FALSE)) lnum = Curpos.lnum; Curpos = pos; cmd += searchcmdlen; /* adjust command string pointer */ break; default: if (isdigit(*cmd)) /* absolute line number */ lnum = getdigits((char **)&cmd); } while (*cmd == '-' || *cmd == '+') { if (lnum == INVLNUM) lnum = curpos_lnum; i = *cmd++; if (!isdigit(*cmd)) /* '+' is '+1', but '+0' is not '+1' */ n = 1; else n = getdigits((char **)&cmd); if (i == '-') lnum -= n; else lnum += n; } curpos_lnum = lnum; } while (*cmd == '/' || *cmd == '?'); error: *ptr = cmd; return lnum; } /* * align text: * type = -1 left aligned * type = 0 centered * type = 1 right aligned */ static void do_align(start, end, width, type) linenr_t start; linenr_t end; int width; int type; { FPOS pos; int len; int indent = 0; pos = Curpos; if (type == -1) /* left align: width is used for new indent */ { if (width >= 0) indent = width; } else { if (width <= 0) width = p_tw; if (width == 0) width = 80; } if (!u_save((linenr_t)(line1 - 1), (linenr_t)(line2 + 1))) return; for (Curpos.lnum = start; Curpos.lnum <= end; ++Curpos.lnum) { set_indent(indent, TRUE); /* remove existing indent */ if (type == -1) /* left align */ continue; len = strsize(nr2ptr(Curpos.lnum)); /* get line lenght */ if (len < width) switch (type) { case 0: set_indent((width - len) / 2, FALSE); /* center */ break; case 1: set_indent(width - len, FALSE); /* right */ break; } } Curpos = pos; beginline(TRUE); updateScreen(NOT_VALID); }