/* * v_esearch -- <dialog box> * Search command from the screen. * * PUBLIC: int v_esearch __P((SCR *, VICMD *)); */ int v_esearch(SCR *sp, VICMD *vp) { int flags; LF_INIT(SEARCH_NOOPT); if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_EXT)) LF_SET(SEARCH_EXTEND); if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_IC)) LF_SET(SEARCH_IC); if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_ICL)) LF_SET(SEARCH_ICL); if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_INCR)) LF_SET(SEARCH_INCR); if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_LIT)) LF_SET(SEARCH_LITERAL); if (FL_ISSET(vp->ev.e_flags, VI_SEARCH_WR)) LF_SET(SEARCH_WRAP); return (v_search(sp, vp, vp->ev.e_csp, vp->ev.e_len, flags, FL_ISSET(vp->ev.e_flags, VI_SEARCH_REV) ? BACKWARD : FORWARD)); }
/* * editor -- * Main editor routine. * * PUBLIC: int editor(GS *, int, char *[]); */ int editor(GS *gp, int argc, char *argv[]) { extern int optind; extern char *optarg; const char *p; EVENT ev; FREF *frp; SCR *sp; size_t len; u_int flags; int ch, flagchk, lflag, secure, startup, readonly, rval, silent; char *tag_f, *wsizearg, path[256]; CHAR_T *w; size_t wlen; /* Initialize the busy routine, if not defined by the screen. */ if (gp->scr_busy == NULL) gp->scr_busy = vs_busy; /* Initialize the message routine, if not defined by the screen. */ if (gp->scr_msg == NULL) gp->scr_msg = vs_msg; gp->catd = (nl_catd)-1; /* Common global structure initialization. */ TAILQ_INIT(gp->dq); TAILQ_INIT(gp->hq); SLIST_INIT(gp->ecq); SLIST_INSERT_HEAD(gp->ecq, &gp->excmd, q); gp->noprint = DEFAULT_NOPRINT; /* Structures shared by screens so stored in the GS structure. */ TAILQ_INIT(gp->frefq); TAILQ_INIT(gp->dcb_store.textq); SLIST_INIT(gp->cutq); SLIST_INIT(gp->seqq); /* Set initial screen type and mode based on the program name. */ readonly = 0; if (!strcmp(getprogname(), "ex") || !strcmp(getprogname(), "nex")) LF_INIT(SC_EX); else { /* Nview, view are readonly. */ if (!strcmp(getprogname(), "nview") || !strcmp(getprogname(), "view")) readonly = 1; /* Vi is the default. */ LF_INIT(SC_VI); } /* Convert old-style arguments into new-style ones. */ if (v_obsolete(argv)) return (1); /* Parse the arguments. */ flagchk = '\0'; tag_f = wsizearg = NULL; lflag = secure = silent = 0; startup = 1; /* Set the file snapshot flag. */ F_SET(gp, G_SNAPSHOT); #ifdef DEBUG while ((ch = getopt(argc, argv, "c:D:eFlRrSsT:t:vw:")) != EOF) #else while ((ch = getopt(argc, argv, "c:eFlRrSst:vw:")) != EOF) #endif switch (ch) { case 'c': /* Run the command. */ /* * XXX * We should support multiple -c options. */ if (gp->c_option != NULL) { warnx("only one -c command may be specified."); return (1); } gp->c_option = optarg; break; #ifdef DEBUG case 'D': switch (optarg[0]) { case 's': startup = 0; break; case 'w': attach(gp); break; default: warnx("usage: -D requires s or w argument."); return (1); } break; #endif case 'e': /* Ex mode. */ LF_CLR(SC_VI); LF_SET(SC_EX); break; case 'F': /* No snapshot. */ F_CLR(gp, G_SNAPSHOT); break; case 'l': /* Set lisp, showmatch options. */ lflag = 1; break; case 'R': /* Readonly. */ readonly = 1; break; case 'r': /* Recover. */ if (flagchk == 't') { warnx("only one of -r and -t may be specified."); return (1); } flagchk = 'r'; break; case 'S': secure = 1; break; case 's': silent = 1; break; #ifdef DEBUG case 'T': /* Trace. */ if ((gp->tracefp = fopen(optarg, "w")) == NULL) { warn("%s", optarg); goto err; } (void)fprintf(gp->tracefp, "\n===\ntrace: open %s\n", optarg); break; #endif case 't': /* Tag. */ if (flagchk == 'r') { warnx("only one of -r and -t may be specified."); return (1); } if (flagchk == 't') { warnx("only one tag file may be specified."); return (1); } flagchk = 't'; tag_f = optarg; break; case 'v': /* Vi mode. */ LF_CLR(SC_EX); LF_SET(SC_VI); break; case 'w': wsizearg = optarg; break; case '?': default: (void)gp->scr_usage(); return (1); } argc -= optind; argv += optind; /* * -s option is only meaningful to ex. * * If not reading from a terminal, it's like -s was specified. */ if (silent && !LF_ISSET(SC_EX)) { warnx("-s option is only applicable to ex."); goto err; } if (LF_ISSET(SC_EX) && F_ISSET(gp, G_SCRIPTED)) silent = 1; /* * Build and initialize the first/current screen. This is a bit * tricky. If an error is returned, we may or may not have a * screen structure. If we have a screen structure, put it on a * display queue so that the error messages get displayed. * * !!! * Everything we do until we go interactive is done in ex mode. */ if (screen_init(gp, NULL, &sp)) { if (sp != NULL) TAILQ_INSERT_HEAD(gp->dq, sp, q); goto err; } F_SET(sp, SC_EX); TAILQ_INSERT_HEAD(gp->dq, sp, q); if (v_key_init(sp)) /* Special key initialization. */ goto err; { int oargs[5], *oargp = oargs; if (lflag) { /* Command-line options. */ *oargp++ = O_LISP; *oargp++ = O_SHOWMATCH; } if (readonly) *oargp++ = O_READONLY; if (secure) *oargp++ = O_SECURE; *oargp = -1; /* Options initialization. */ if (opts_init(sp, oargs)) goto err; } if (wsizearg != NULL) { ARGS *av[2], a, b; (void)snprintf(path, sizeof(path), "window=%s", wsizearg); a.bp = (CHAR_T *)path; a.len = strlen(path); b.bp = NULL; b.len = 0; av[0] = &a; av[1] = &b; (void)opts_set(sp, av, NULL); } if (silent) { /* Ex batch mode option values. */ O_CLR(sp, O_AUTOPRINT); O_CLR(sp, O_PROMPT); O_CLR(sp, O_VERBOSE); O_CLR(sp, O_WARN); F_SET(sp, SC_EX_SILENT); } sp->rows = O_VAL(sp, O_LINES); /* Make ex formatting work. */ sp->cols = O_VAL(sp, O_COLUMNS); if (!silent && startup) { /* Read EXINIT, exrc files. */ if (ex_exrc(sp)) goto err; if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) { if (screen_end(sp)) goto err; goto done; } } /* * List recovery files if -r specified without file arguments. * Note, options must be initialized and startup information * read before doing this. */ if (flagchk == 'r' && argv[0] == NULL) { if (rcv_list(sp)) goto err; if (screen_end(sp)) goto err; goto done; } /* * !!! * Initialize the default ^D, ^U scrolling value here, after the * user has had every opportunity to set the window option. * * It's historic practice that changing the value of the window * option did not alter the default scrolling value, only giving * a count to ^D/^U did that. */ sp->defscroll = (O_VAL(sp, O_WINDOW) + 1) / 2; /* * If we don't have a command-line option, switch into the right * editor now, so that we position default files correctly, and * so that any tags file file-already-locked messages are in the * vi screen, not the ex screen. * * XXX * If we have a command-line option, the error message can end * up in the wrong place, but I think that the combination is * unlikely. */ if (gp->c_option == NULL) { F_CLR(sp, SC_EX | SC_VI); F_SET(sp, LF_ISSET(SC_EX | SC_VI)); } /* Open a tag file if specified. */ if (tag_f != NULL) { CHAR2INT(sp, tag_f, strlen(tag_f) + 1, w, wlen); if (ex_tag_first(sp, w)) goto err; } /* * Append any remaining arguments as file names. Files are recovery * files if -r specified. If the tag option or ex startup commands * loaded a file, then any file arguments are going to come after it. */ if (*argv != NULL) { if (sp->frp != NULL) { /* Cheat -- we know we have an extra argv slot. */ *--argv = strdup(sp->frp->name); if (*argv == NULL) { warn(NULL); goto err; } } sp->argv = sp->cargv = argv; F_SET(sp, SC_ARGNOFREE); if (flagchk == 'r') F_SET(sp, SC_ARGRECOVER); } /* * If the ex startup commands and or/the tag option haven't already * created a file, create one. If no command-line files were given, * use a temporary file. */ if (sp->frp == NULL) { if (sp->argv == NULL) { if ((frp = file_add(sp, NULL)) == NULL) goto err; } else { if ((frp = file_add(sp, sp->argv[0])) == NULL) goto err; if (F_ISSET(sp, SC_ARGRECOVER)) F_SET(frp, FR_RECOVER); } if (file_init(sp, frp, NULL, 0)) goto err; if (EXCMD_RUNNING(gp)) { (void)ex_cmd(sp); if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) { if (screen_end(sp)) goto err; goto done; } } } /* * Check to see if we need to wait for ex. If SC_SCR_EX is set, ex * was forced to initialize the screen during startup. We'd like to * wait for a single character from the user, but we can't because * we're not in raw mode. We can't switch to raw mode because the * vi initialization will switch to xterm's alternate screen, causing * us to lose the messages we're pausing to make sure the user read. * So, wait for a complete line. */ if (F_ISSET(sp, SC_SCR_EX)) { p = msg_cmsg(sp, CMSG_CONT_R, &len); (void)write(STDOUT_FILENO, p, len); for (;;) { if (v_event_get(sp, &ev, 0, 0)) goto err; if (ev.e_event == E_INTERRUPT || (ev.e_event == E_CHARACTER && (ev.e_value == K_CR || ev.e_value == K_NL))) break; (void)gp->scr_bell(sp); } } /* Switch into the right editor, regardless. */ F_CLR(sp, SC_EX | SC_VI); F_SET(sp, LF_ISSET(SC_EX | SC_VI) | SC_STATUS_CNT); /* * Main edit loop. Vi handles split screens itself, we only return * here when switching editor modes or restarting the screen. */ while (sp != NULL) if (F_ISSET(sp, SC_EX) ? ex(&sp) : vi(&sp)) goto err; done: rval = 0; if (0) err: rval = 1; /* Clean out the global structure. */ v_end(gp); return (rval); }
/* * editor -- * Main editor routine. * * PUBLIC: int editor __P((WIN *, int, char *[])); */ int editor(WIN *wp, int argc, char **argv) { extern int optind; extern char *optarg; const char *p; EVENT ev; FREF *frp; SCR *sp; GS *gp; size_t len; u_int flags; int ch, flagchk, lflag, secure, startup, readonly, rval, silent; char *tag_f, *wsizearg, path[256]; CHAR_T *w; size_t wlen; gp = wp->gp; /* Initialize the busy routine, if not defined by the screen. */ if (gp->scr_busy == NULL) gp->scr_busy = vs_busy; /* Initialize the message routine, if not defined by the screen. */ if (wp->scr_msg == NULL) wp->scr_msg = vs_msg; /* Set initial screen type and mode based on the program name. */ readonly = 0; if (!strcmp(gp->progname, "ex") || !strcmp(gp->progname, "nex")) LF_INIT(SC_EX); else { /* Nview, view are readonly. */ if (!strcmp(gp->progname, "nview") || !strcmp(gp->progname, "view")) readonly = 1; /* Vi is the default. */ LF_INIT(SC_VI); } /* Convert old-style arguments into new-style ones. */ if (v_obsolete(gp->progname, argv)) return (1); /* Parse the arguments. */ flagchk = '\0'; tag_f = wsizearg = NULL; lflag = secure = silent = 0; startup = 1; /* Set the file snapshot flag. */ F_SET(gp, G_SNAPSHOT); #ifdef DEBUG while ((ch = getopt(argc, argv, "c:D:eFlRrSsT:t:vw:")) != EOF) #else while ((ch = getopt(argc, argv, "c:eFlRrSst:vw:")) != EOF) #endif switch (ch) { case 'c': /* Run the command. */ /* * XXX * We should support multiple -c options. */ if (gp->c_option != NULL) { v_estr(gp->progname, 0, "only one -c command may be specified."); return (1); } gp->c_option = optarg; break; #ifdef DEBUG case 'D': switch (optarg[0]) { case 's': startup = 0; break; case 'w': attach(gp); break; default: v_estr(gp->progname, 0, "usage: -D requires s or w argument."); return (1); } break; #endif case 'e': /* Ex mode. */ LF_CLR(SC_VI); LF_SET(SC_EX); break; case 'F': /* No snapshot. */ v_estr(gp->progname, 0, "-F option no longer supported."); break; case 'l': /* Set lisp, showmatch options. */ lflag = 1; break; case 'R': /* Readonly. */ readonly = 1; break; case 'r': /* Recover. */ if (flagchk == 't') { v_estr(gp->progname, 0, "only one of -r and -t may be specified."); return (1); } flagchk = 'r'; break; case 'S': secure = 1; break; case 's': silent = 1; break; #ifdef TRACE case 'T': /* Trace. */ (void)vtrace_init(optarg); break; #endif case 't': /* Tag. */ if (flagchk == 'r') { v_estr(gp->progname, 0, "only one of -r and -t may be specified."); return (1); } if (flagchk == 't') { v_estr(gp->progname, 0, "only one tag file may be specified."); return (1); } flagchk = 't'; tag_f = optarg; break; case 'v': /* Vi mode. */ LF_CLR(SC_EX); LF_SET(SC_VI); break; case 'w': wsizearg = optarg; break; case '?': default: (void)gp->scr_usage(); return (1); } argc -= optind; argv += optind; /* * -s option is only meaningful to ex. * * If not reading from a terminal, it's like -s was specified. */ if (silent && !LF_ISSET(SC_EX)) { v_estr(gp->progname, 0, "-s option is only applicable to ex."); goto err; } if (LF_ISSET(SC_EX) && F_ISSET(gp, G_SCRIPTED)) silent = 1; /* * Build and initialize the first/current screen. This is a bit * tricky. If an error is returned, we may or may not have a * screen structure. If we have a screen structure, put it on a * display queue so that the error messages get displayed. * * !!! * Everything we do until we go interactive is done in ex mode. */ if (screen_init(gp, NULL, &sp)) { if (sp != NULL) { CIRCLEQ_INSERT_HEAD(&wp->scrq, sp, q); sp->wp = wp; } goto err; } F_SET(sp, SC_EX); CIRCLEQ_INSERT_HEAD(&wp->scrq, sp, q); sp->wp = wp; if (v_key_init(sp)) /* Special key initialization. */ goto err; { int oargs[5], *oargp = oargs; if (lflag) { /* Command-line options. */ *oargp++ = O_LISP; *oargp++ = O_SHOWMATCH; } if (readonly) *oargp++ = O_READONLY; if (secure) *oargp++ = O_SECURE; *oargp = -1; /* Options initialization. */ if (opts_init(sp, oargs)) goto err; } if (wsizearg != NULL) { ARGS *av[2], a, b; (void)snprintf(path, sizeof(path), "window=%s", wsizearg); a.bp = (CHAR_T *)path; a.len = strlen(path); b.bp = NULL; b.len = 0; av[0] = &a; av[1] = &b; (void)opts_set(sp, av, NULL); } if (silent) { /* Ex batch mode option values. */ O_CLR(sp, O_AUTOPRINT); O_CLR(sp, O_PROMPT); O_CLR(sp, O_VERBOSE); O_CLR(sp, O_WARN); F_SET(sp, SC_EX_SILENT); } sp->rows = O_VAL(sp, O_LINES); /* Make ex formatting work. */ sp->cols = O_VAL(sp, O_COLUMNS); if (!silent && startup) { /* Read EXINIT, exrc files. */ if (ex_exrc(sp)) goto err; if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) { if (screen_end(sp)) goto err; goto done; } } /* * List recovery files if -r specified without file arguments. * Note, options must be initialized and startup information * read before doing this. */ if (flagchk == 'r' && argv[0] == NULL) { if (rcv_list(sp)) goto err; if (screen_end(sp)) goto err; goto done; } /* * !!! * Initialize the default ^D, ^U scrolling value here, after the * user has had every opportunity to set the window option. * * It's historic practice that changing the value of the window * option did not alter the default scrolling value, only giving * a count to ^D/^U did that. */ sp->defscroll = (O_VAL(sp, O_WINDOW) + 1) / 2; /* * If we don't have a command-line option, switch into the right * editor now, so that we position default files correctly, and * so that any tags file file-already-locked messages are in the * vi screen, not the ex screen. * * XXX * If we have a command-line option, the error message can end * up in the wrong place, but I think that the combination is * unlikely. */ if (gp->c_option == NULL) { F_CLR(sp, SC_EX | SC_VI); F_SET(sp, LF_ISSET(SC_EX | SC_VI)); } /* Open a tag file if specified. */ if (tag_f != NULL) { CHAR2INT(sp, tag_f, strlen(tag_f) + 1, w, wlen); if (ex_tag_first(sp, w)) goto err; } /* * Append any remaining arguments as file names. Files are recovery * files if -r specified. If the tag option or ex startup commands * loaded a file, then any file arguments are going to come after it. */ if (*argv != NULL) { if (sp->frp != NULL) { /* Cheat -- we know we have an extra argv slot. */ MALLOC_NOMSG(sp, *--argv, char *, strlen(sp->frp->name) + 1); if (*argv == NULL) { v_estr(gp->progname, errno, NULL); goto err; } (void)strcpy(*argv, sp->frp->name); } sp->argv = sp->cargv = argv; F_SET(sp, SC_ARGNOFREE); if (flagchk == 'r') F_SET(sp, SC_ARGRECOVER); }
/* * ex_aci -- * Append, change, insert in ex. */ static int ex_aci(SCR *sp, EXCMD *cmdp, enum which cmd) { CHAR_T *p, *t; GS *gp; TEXT *tp; TEXTH tiq; db_recno_t cnt, lno; size_t len; u_int32_t flags; int need_newline; gp = sp->gp; NEEDFILE(sp, cmdp); /* * If doing a change, replace lines for as long as possible. Then, * append more lines or delete remaining lines. Changes to an empty * file are appends, inserts are the same as appends to the previous * line. * * !!! * Set the address to which we'll append. We set sp->lno to this * address as well so that autoindent works correctly when get text * from the user. */ lno = cmdp->addr1.lno; sp->lno = lno; if ((cmd == CHANGE || cmd == INSERT) && lno != 0) --lno; /* * !!! * If the file isn't empty, cut changes into the unnamed buffer. */ if (cmd == CHANGE && cmdp->addr1.lno != 0 && (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE) || del(sp, &cmdp->addr1, &cmdp->addr2, 1))) return (1); /* * !!! * Anything that was left after the command separator becomes part * of the inserted text. Apparently, it was common usage to enter: * * :g/pattern/append|stuff1 * * and append the line of text "stuff1" to the lines containing the * pattern. It was also historically legal to enter: * * :append|stuff1 * stuff2 * . * * and the text on the ex command line would be appended as well as * the text inserted after it. There was an historic bug however, * that the user had to enter *two* terminating lines (the '.' lines) * to terminate text input mode, in this case. This whole thing * could be taken too far, however. Entering: * * :append|stuff1\ * stuff2 * stuff3 * . * * i.e. mixing and matching the forms confused the historic vi, and, * not only did it take two terminating lines to terminate text input * mode, but the trailing backslashes were retained on the input. We * match historic practice except that we discard the backslashes. * * Input lines specified on the ex command line lines are separated by * <newline>s. If there is a trailing delimiter an empty line was * inserted. There may also be a leading delimiter, which is ignored * unless it's also a trailing delimiter. It is possible to encounter * a termination line, i.e. a single '.', in a global command, but not * necessary if the text insert command was the last of the global * commands. */ if (cmdp->save_cmdlen != 0) { for (p = cmdp->save_cmd, len = cmdp->save_cmdlen; len > 0; p = t) { for (t = p; len > 0 && t[0] != '\n'; ++t, --len); if (t != p || len == 0) { if (F_ISSET(sp, SC_EX_GLOBAL) && t - p == 1 && p[0] == '.') { ++t; if (len > 0) --len; break; } if (db_append(sp, 1, lno++, p, t - p)) return (1); } if (len != 0) { ++t; if (--len == 0 && db_append(sp, 1, lno++, NULL, 0)) return (1); } } /* * If there's any remaining text, we're in a global, and * there's more command to parse. * * !!! * We depend on the fact that non-global commands will eat the * rest of the command line as text input, and before getting * any text input from the user. Otherwise, we'd have to save * off the command text before or during the call to the text * input function below. */ if (len != 0) cmdp->save_cmd = t; cmdp->save_cmdlen = len; } if (F_ISSET(sp, SC_EX_GLOBAL)) { if ((sp->lno = lno) == 0 && db_exist(sp, 1)) sp->lno = 1; return (0); } /* * If not in a global command, read from the terminal. * * If this code is called by vi, we want to reset the terminal and use * ex's line get routine. It actually works fine if we use vi's get * routine, but it doesn't look as nice. Maybe if we had a separate * window or something, but getting a line at a time looks awkward. * However, depending on the screen that we're using, that may not * be possible. */ if (F_ISSET(sp, SC_VI)) { if (gp->scr_screen(sp, SC_EX)) { ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON); return (1); } /* If we're still in the vi screen, move out explicitly. */ need_newline = !F_ISSET(sp, SC_SCR_EXWROTE); F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE); if (need_newline) (void)ex_puts(sp, "\n"); /* * !!! * Users of historical versions of vi sometimes get confused * when they enter append mode, and can't seem to get out of * it. Give them an informational message. */ (void)ex_puts(sp, msg_cat(sp, "273|Entering ex input mode.", NULL)); (void)ex_puts(sp, "\n"); (void)ex_fflush(sp); } /* * Set input flags; the ! flag turns off autoindent for append, * change and insert. */ LF_INIT(TXT_DOTTERM | TXT_NUMBER); if (!FL_ISSET(cmdp->iflags, E_C_FORCE) && O_ISSET(sp, O_AUTOINDENT)) LF_SET(TXT_AUTOINDENT); if (O_ISSET(sp, O_BEAUTIFY)) LF_SET(TXT_BEAUTIFY); /* * This code can't use the common screen TEXTH structure (sp->tiq), * as it may already be in use, e.g. ":append|s/abc/ABC/" would fail * as we are only halfway through the text when the append code fires. * Use a local structure instead. (The ex code would have to use a * local structure except that we're guaranteed to finish remaining * characters in the common TEXTH structure when they were inserted * into the file, above.) */ memset(&tiq, 0, sizeof(TEXTH)); TAILQ_INIT(&tiq); if (ex_txt(sp, &tiq, 0, flags)) return (1); for (cnt = 0, tp = TAILQ_FIRST(&tiq); tp != NULL; ++cnt, tp = TAILQ_NEXT(tp, q)) if (db_append(sp, 1, lno++, tp->lb, tp->len)) return (1); /* * Set sp->lno to the final line number value (correcting for a * possible 0 value) as that's historically correct for the final * line value, whether or not the user entered any text. */ if ((sp->lno = lno) == 0 && db_exist(sp, 1)) sp->lno = 1; return (0); }
/* * exwr -- * The guts of the ex write commands. */ static int exwr(SCR *sp, EXCMD *cmdp, enum which cmd) { MARK rm; int flags; char *name; CHAR_T *p = NULL; size_t nlen; char *n; int rc; EX_PRIVATE *exp; NEEDFILE(sp, cmdp); /* All write commands can have an associated '!'. */ LF_INIT(FS_POSSIBLE); if (FL_ISSET(cmdp->iflags, E_C_FORCE)) LF_SET(FS_FORCE); /* Skip any leading whitespace. */ if (cmdp->argc != 0) for (p = cmdp->argv[0]->bp; *p != '\0' && cmdskip(*p); ++p); /* If "write !" it's a pipe to a utility. */ if (cmdp->argc != 0 && cmd == WRITE && *p == '!') { /* Secure means no shell access. */ if (O_ISSET(sp, O_SECURE)) { ex_wemsg(sp, cmdp->cmd->name, EXM_SECURE_F); return (1); } /* Expand the argument. */ for (++p; *p && cmdskip(*p); ++p); if (*p == '\0') { ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); return (1); } if (argv_exp1(sp, cmdp, p, STRLEN(p), 1)) return (1); /* Set the last bang command */ exp = EXP(sp); free(exp->lastbcomm); exp->lastbcomm = v_wstrdup(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len); /* * Historically, vi waited after a write filter even if there * wasn't any output from the command. People complained when * nvi waited only if there was output, wanting the visual cue * that the program hadn't written anything. */ F_SET(sp, SC_EX_WAIT_YES); /* * !!! * Ignore the return cursor position, the cursor doesn't * move. */ if (ex_filter(sp, cmdp, &cmdp->addr1, &cmdp->addr2, &rm, cmdp->argv[1]->bp, FILTER_WRITE)) return (1); /* Ex terminates with a bang, even if the command fails. */ if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT)) (void)ex_puts(sp, "!\n"); return (0); } /* Set the FS_ALL flag if we're writing the entire file. */ if (cmdp->addr1.lno <= 1 && !db_exist(sp, cmdp->addr2.lno + 1)) LF_SET(FS_ALL); /* If "write >>" it's an append to a file. */ if (cmdp->argc != 0 && cmd != XIT && p[0] == '>' && p[1] == '>') { LF_SET(FS_APPEND); /* Skip ">>" and whitespace. */ for (p += 2; *p && cmdskip(*p); ++p); } /* If no other arguments, just write the file back. */ if (cmdp->argc == 0 || *p == '\0') return (file_write(sp, &cmdp->addr1, &cmdp->addr2, NULL, flags)); /* Build an argv so we get an argument count and file expansion. */ if (argv_exp2(sp, cmdp, p, STRLEN(p))) return (1); /* * 0 args: impossible. * 1 args: impossible (I hope). * 2 args: read it. * >2 args: object, too many args. * * The 1 args case depends on the argv_sexp() function refusing * to return success without at least one non-blank character. */ switch (cmdp->argc) { case 0: case 1: abort(); /* NOTREACHED */ case 2: INT2CHAR(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len+1, n, nlen); name = v_strdup(sp, n, nlen - 1); /* * !!! * Historically, the read and write commands renamed * "unnamed" files, or, if the file had a name, set * the alternate file name. */ if (F_ISSET(sp->frp, FR_TMPFILE) && !F_ISSET(sp->frp, FR_EXNAMED)) { if ((n = v_strdup(sp, name, nlen - 1)) != NULL) { free(sp->frp->name); sp->frp->name = n; } /* * The file has a real name, it's no longer a * temporary, clear the temporary file flags. * * !!! * If we're writing the whole file, FR_NAMECHANGE * will be cleared by the write routine -- this is * historic practice. */ F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE); F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED); /* Notify the screen. */ (void)sp->gp->scr_rename(sp, sp->frp->name, 1); } else set_alt_name(sp, name); break; default: INT2CHAR(sp, p, STRLEN(p) + 1, n, nlen); ex_emsg(sp, n, EXM_FILECOUNT); return (1); } rc = file_write(sp, &cmdp->addr1, &cmdp->addr2, name, flags); free(name); return rc; }