void transPrint(char *p, int j){ int i = j; if (*p != '\0' && *p > '0' && *p <= '9'){ char c = *p; fakePrint(CHAR2CHAR(c), i++); transPrint(p+1, i); } else if (*p == '\0'){ fakePrint('\0', i); printf("%s\n", gPrintBuffer); return; } else { return; } i = j; if (*(p+1) != '\0' && *(p+1) >= '0' && *(p+1) <= '9'){ char c = *p; char cn = *(p+1); int doubleC = CHAR2INT(c) * 10 + CHAR2INT(cn); if (doubleC <= 26){ fakePrint(INT2CHAR(doubleC), i++); transPrint(p+2, i); } } return; }
/* * start_cscopes -- * Initialize the cscope package. */ static int start_cscopes(SCR *sp, EXCMD *cmdp) { size_t blen, len; char *bp, *cscopes, *p, *t; const CHAR_T *wp; size_t wlen; /* * EXTENSION #1: * * If the CSCOPE_DIRS environment variable is set, we treat it as a * list of cscope directories that we're using, similar to the tags * edit option. * * XXX * This should probably be an edit option, although that implies that * we start/stop cscope processes periodically, instead of once when * the editor starts. */ if ((cscopes = getenv("CSCOPE_DIRS")) == NULL) return (0); len = strlen(cscopes); GET_SPACE_RETC(sp, bp, blen, len); memcpy(bp, cscopes, len + 1); for (cscopes = t = bp; (p = strsep(&t, "\t :")) != NULL;) if (*p != '\0') { CHAR2INT(sp, p, strlen(p) + 1, wp, wlen); (void)cscope_add(sp, cmdp, wp); } FREE_SPACE(sp, bp, blen); return (0); }
/* * ex_source -- :source file * Execute ex commands from a file. * * PUBLIC: int ex_source __P((SCR *, EXCMD *)); */ int ex_source(SCR *sp, EXCMD *cmdp) { struct stat sb; int fd, len; char *bp; const char *name; size_t nlen; const CHAR_T *wp; CHAR_T *dp; size_t wlen; INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len + 1, name, nlen); if ((fd = open(name, O_RDONLY, 0)) < 0 || fstat(fd, &sb)) goto err; /* * XXX * I'd like to test to see if the file is too large to malloc. Since * we don't know what size or type off_t's or size_t's are, what the * largest unsigned integral type is, or what random insanity the local * C compiler will perpetrate, doing the comparison in a portable way * is flatly impossible. So, put an fairly unreasonable limit on it, * I don't want to be dropping core here. */ #define MEGABYTE 1048576 if (sb.st_size > MEGABYTE) { errno = ENOMEM; goto err; } MALLOC(sp, bp, char *, (size_t)sb.st_size + 1); if (bp == NULL) { (void)close(fd); return (1); } bp[sb.st_size] = '\0'; /* Read the file into memory. */ len = read(fd, bp, (int)sb.st_size); (void)close(fd); if (len == -1 || len != sb.st_size) { if (len != sb.st_size) errno = EIO; free(bp); err: msgq_str(sp, M_SYSERR, name, "%s"); return (1); } if (CHAR2INT(sp, bp, (size_t)sb.st_size + 1, wp, wlen)) msgq(sp, M_ERR, "323|Invalid input. Truncated."); dp = v_wstrdup(sp, wp, wlen - 1); free(bp); /* Put it on the ex queue. */ INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len + 1, name, nlen); return (ex_run_str(sp, name, dp, wlen - 1, 1, 1)); }
/* * api_aline -- * Append a line. * * PUBLIC: int api_aline __P((SCR *, db_recno_t, char *, size_t)); */ int api_aline(SCR *sp, db_recno_t lno, char *line, size_t len) { size_t wblen; const CHAR_T *wbp; CHAR2INT(sp, line, len, wbp, wblen); return (db_append(sp, 1, lno, wbp, wblen)); }
int main(void) { int linhas[LEN_TAB], colunas[LEN_TAB]; int n, i, j, k, v, tmp; char c; scanf ("%d%*[\r\n]", &n); while (n--) { memset(linhas, 0, sizeof(linhas)); memset(colunas, 0, sizeof(colunas)); for (i = 0; i < LEN_TAB; i++) { for (j = 0; j < LEN_TAB; j++) { scanf ("%c", &c); if (c != '0') { v = 1 << (CHAR2INT(c) - 1); linhas[i] |= v; colunas[j] |= v; } } scanf ("%*[\r\n]"); } for (i = 0; i < LEN_TAB; i++) { for (j = 0; j < LEN_TAB; j++) { tmp = 0; for (k = 1; k <= 9; k++) { v = (1 << (k - 1)); if (((linhas[i] & v) > 0) && ((colunas[j] & v) > 0)) { printf ("%d ", k); tmp = 1; break; } } if (tmp == 0) { printf ("0 "); } } printf ("\n"); } } return 0; }
/* * ex_run_file -- * Set up a file of ex commands to run. */ static int ex_run_file(SCR *sp, char *name) { EXCMD cmd; CHAR_T *wp; size_t wlen; ex_cinit(sp, &cmd, C_SOURCE, 0, OOBLNO, OOBLNO, 0); CHAR2INT(sp, name, strlen(name)+1, wp, wlen); argv_exp0(sp, &cmd, wp, wlen - 1); return (ex_source(sp, &cmd)); }
/* * api_edit * Create a new screen and return its id * or edit a new file in the current screen. * * PUBLIC: int api_edit __P((SCR *, char *, SCR **, int)); */ int api_edit(SCR *sp, char *file, SCR **spp, int newscreen) { EXCMD cmd; size_t wlen; const CHAR_T *wp; if (file) { ex_cinit(sp, &cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0); CHAR2INT(sp, file, strlen(file) + 1, wp, wlen); argv_exp0(sp, &cmd, wp, wlen - 1 /* terminating 0 */); } else ex_cinit(sp, &cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0); if (newscreen) cmd.flags |= E_NEWSCREEN; /* XXX */ if (cmd.cmd->fn(sp, &cmd)) return (1); *spp = sp->nextdisp; return (0); }
/* * ex_next -- :next [+cmd] [files] * Edit the next file, optionally setting the list of files. * * !!! * The :next command behaved differently from the :rewind command in * historic vi. See nvi/docs/autowrite for details, but the basic * idea was that it ignored the force flag if the autowrite flag was * set. This implementation handles them all identically. * * PUBLIC: int ex_next __P((SCR *, EXCMD *)); */ int ex_next(SCR *sp, EXCMD *cmdp) { ARGS **argv; FREF *frp; int noargs; char **ap; const CHAR_T *wp; size_t wlen; const char *np; size_t nlen; /* Check for file to move to. */ if (cmdp->argc == 0 && (sp->cargv == NULL || sp->cargv[1] == NULL)) { msgq(sp, M_ERR, "111|No more files to edit"); return (1); } if (F_ISSET(cmdp, E_NEWSCREEN)) { /* By default, edit the next file in the old argument list. */ if (cmdp->argc == 0) { CHAR2INT(sp, sp->cargv[1], strlen(sp->cargv[1]) + 1, wp, wlen); if (argv_exp0(sp, cmdp, wp, wlen - 1)) return (1); return (ex_edit(sp, cmdp)); } return (ex_N_next(sp, cmdp)); } /* Check modification. */ if (file_m1(sp, FL_ISSET(cmdp->iflags, E_C_FORCE), FS_ALL | FS_POSSIBLE)) return (1); /* Any arguments are a replacement file list. */ if (cmdp->argc) { /* Free the current list. */ if (!F_ISSET(sp, SC_ARGNOFREE) && sp->argv != NULL) { for (ap = sp->argv; *ap != NULL; ++ap) free(*ap); free(sp->argv); } F_CLR(sp, SC_ARGNOFREE | SC_ARGRECOVER); sp->cargv = NULL; /* Create a new list. */ CALLOC_RET(sp, sp->argv, char **, cmdp->argc + 1, sizeof(char *)); for (ap = sp->argv, argv = cmdp->argv; argv[0]->len != 0; ++ap, ++argv) { INT2CHAR(sp, argv[0]->bp, argv[0]->len, np, nlen); if ((*ap = v_strdup(sp, np, nlen)) == NULL) return (1); } *ap = NULL; /* Switch to the first file. */ sp->cargv = sp->argv; if ((frp = file_add(sp, *sp->cargv)) == NULL) return (1); noargs = 0; /* Display a file count with the welcome message. */ F_SET(sp, SC_STATUS_CNT); } else { if ((frp = file_add(sp, sp->cargv[1])) == NULL)
/* * ip_trans -- * Translate messages into events. */ static int ip_trans(SCR *sp, IP_PRIVATE *ipp, EVENT *evp) { u_int32_t skip, val; const char *fmt; const CHAR_T *wp; size_t wlen; if (ipp->ibuf[0] == CODE_OOB || ipp->ibuf[0] >= VI_EVENT_SUP) { /* * XXX: Protocol is out of sync? */ abort(); } fmt = vipfuns[ipp->ibuf[0]-1].format; evp->e_event = vipfuns[ipp->ibuf[0]-1].e_event; evp->e_ipcom = ipp->ibuf[0]; for (skip = IPO_CODE_LEN; *fmt != '\0'; ++fmt) switch (*fmt) { case '1': case '2': if (ipp->iblen < skip + IPO_INT_LEN) return (0); memcpy(&val, ipp->ibuf + skip, IPO_INT_LEN); val = ntohl(val); if (*fmt == '1') evp->e_val1 = val; else evp->e_val2 = val; skip += IPO_INT_LEN; break; case 'a': case 'b': if (ipp->iblen < skip + IPO_INT_LEN) return (0); memcpy(&val, ipp->ibuf + skip, IPO_INT_LEN); val = ntohl(val); skip += IPO_INT_LEN; if (ipp->iblen < skip + val) return (0); if (*fmt == 'a') { CHAR2INT(sp, ipp->ibuf + skip, val, wp, wlen); MEMCPYW(ipp->tbuf, wp, wlen); evp->e_str1 = ipp->tbuf; evp->e_len1 = wlen; } else { CHAR2INT(sp, ipp->ibuf + skip, val, wp, wlen); MEMCPYW(ipp->tbuf, wp, wlen); evp->e_str2 = ipp->tbuf; evp->e_len2 = wlen; } skip += val; break; } ipp->iskip = skip; if (evp->e_event == E_WRESIZE) (void)ip_resize(sp, evp->e_val1, evp->e_val2); return (1); }
/* * msgq_status -- * Report on the file's status. * * PUBLIC: void msgq_status(SCR *, recno_t, u_int); */ void msgq_status( SCR *sp, recno_t lno, u_int flags) { recno_t last; size_t blen, len; int cnt, needsep; const char *t; char **ap, *bp, *np, *p, *s, *ep; CHAR_T *wp; size_t wlen; /* Get sufficient memory. */ len = strlen(sp->frp->name); GET_SPACE_GOTOC(sp, bp, blen, len * MAX_CHARACTER_COLUMNS + 128); p = bp; ep = bp + blen; /* Convert the filename. */ CHAR2INT(sp, sp->frp->name, len + 1, wp, wlen); /* Copy in the filename. */ for (; *wp != '\0'; ++wp) { len = KEY_LEN(sp, *wp); memcpy(p, KEY_NAME(sp, *wp), len); p += len; } np = p; *p++ = ':'; *p++ = ' '; /* Copy in the argument count. */ if (F_ISSET(sp, SC_STATUS_CNT) && sp->argv != NULL) { for (cnt = 0, ap = sp->argv; *ap != NULL; ++ap, ++cnt); if (cnt > 1) { (void)snprintf(p, ep - p, msg_cat(sp, "317|%d files to edit", NULL), cnt); p += strlen(p); *p++ = ':'; *p++ = ' '; } F_CLR(sp, SC_STATUS_CNT); } /* * See nvi/exf.c:file_init() for a description of how and when the * read-only bit is set. * * !!! * The historic display for "name changed" was "[Not edited]". */ needsep = 0; if (F_ISSET(sp->frp, FR_NEWFILE)) { F_CLR(sp->frp, FR_NEWFILE); t = msg_cat(sp, "021|new file", &len); memcpy(p, t, len); p += len; needsep = 1; } else { if (F_ISSET(sp->frp, FR_NAMECHANGE)) { t = msg_cat(sp, "022|name changed", &len); memcpy(p, t, len); p += len; needsep = 1; } if (needsep) { *p++ = ','; *p++ = ' '; } if (F_ISSET(sp->ep, F_MODIFIED)) t = msg_cat(sp, "023|modified", &len); else t = msg_cat(sp, "024|unmodified", &len); memcpy(p, t, len); p += len; needsep = 1; } if (F_ISSET(sp->frp, FR_UNLOCKED)) { if (needsep) { *p++ = ','; *p++ = ' '; } t = msg_cat(sp, "025|UNLOCKED", &len); memcpy(p, t, len); p += len; needsep = 1; } if (O_ISSET(sp, O_READONLY)) { if (needsep) { *p++ = ','; *p++ = ' '; } t = msg_cat(sp, "026|readonly", &len); memcpy(p, t, len); p += len; needsep = 1; } if (needsep) { *p++ = ':'; *p++ = ' '; } if (LF_ISSET(MSTAT_SHOWLAST)) { if (db_last(sp, &last)) return; if (last == 0) { t = msg_cat(sp, "028|empty file", &len); memcpy(p, t, len); p += len; } else { t = msg_cat(sp, "027|line %lu of %lu [%ld%%]", &len); (void)snprintf(p, ep - p, t, (u_long)lno, (u_long)last, ((u_long)lno * 100) / last); p += strlen(p); } } else { t = msg_cat(sp, "029|line %lu", &len); (void)snprintf(p, ep - p, t, (u_long)lno); p += strlen(p); } #ifdef DEBUG (void)snprintf(p, ep - p, " (pid %lu)", (u_long)getpid()); p += strlen(p); #endif *p++ = '\n'; len = p - bp; /* * There's a nasty problem with long path names. Cscope and tags files * can result in long paths and vi will request a continuation key from * the user as soon as it starts the screen. Unfortunately, the user * has already typed ahead, and chaos results. If we assume that the * characters in the filenames and informational messages only take a * single screen column each, we can trim the filename. * * XXX * Status lines get put up at fairly awkward times. For example, when * you do a filter read (e.g., :read ! echo foo) in the top screen of a * split screen, we have to repaint the status lines for all the screens * below the top screen. We don't want users having to enter continue * characters for those screens. Make it really hard to screw this up. */ s = bp; if (LF_ISSET(MSTAT_TRUNCATE) && len > sp->cols) { for (; s < np && (*s != '/' || (p - s) > sp->cols - 3); ++s); if (s == np) { s = p - (sp->cols - 5); *--s = ' '; } *--s = '.'; *--s = '.'; *--s = '.'; len = p - s; } /* Flush any waiting ex messages. */ (void)ex_fflush(sp); sp->gp->scr_msg(sp, M_INFO, s, len); FREE_SPACE(sp, bp, blen); alloc_err: return; }
/* * file_backup -- * Backup the about-to-be-written file. * * XXX * We do the backup by copying the entire file. It would be nice to do * a rename instead, but: (1) both files may not fit and we want to fail * before doing the rename; (2) the backup file may not be on the same * disk partition as the file being written; (3) there may be optional * file information (MACs, DACs, whatever) that we won't get right if we * recreate the file. So, let's not risk it. */ static int file_backup(SCR *sp, char *name, char *bname) { struct dirent *dp; struct stat sb; DIR *dirp; EXCMD cmd; off_t off; size_t blen; int flags, maxnum, nr, num, nw, rfd, wfd, version; char *bp, *estr, *p, *pct, *slash, *t, *wfname, buf[8192]; CHAR_T *wp; size_t wlen; size_t nlen; char *d = NULL; rfd = wfd = -1; bp = estr = wfname = NULL; /* * Open the current file for reading. Do this first, so that * we don't exec a shell before the most likely failure point. * If it doesn't exist, it's okay, there's just nothing to back * up. */ errno = 0; if ((rfd = open(name, O_RDONLY, 0)) < 0) { if (errno == ENOENT) return (0); estr = name; goto err; } /* * If the name starts with an 'N' character, add a version number * to the name. Strip the leading N from the string passed to the * expansion routines, for no particular reason. It would be nice * to permit users to put the version number anywhere in the backup * name, but there isn't a special character that we can use in the * name, and giving a new character a special meaning leads to ugly * hacks both here and in the supporting ex routines. * * Shell and file name expand the option's value. */ ex_cinit(sp, &cmd, 0, 0, 0, 0, 0); if (bname[0] == 'N') { version = 1; ++bname; } else version = 0; CHAR2INT(sp, bname, strlen(bname) + 1, wp, wlen); if (argv_exp2(sp, &cmd, wp, wlen - 1)) return (1); /* * 0 args: impossible. * 1 args: use it. * >1 args: object, too many args. */ if (cmd.argc != 1) { msgq_str(sp, M_ERR, bname, "258|%s expanded into too many file names"); (void)close(rfd); return (1); } /* * If appending a version number, read through the directory, looking * for file names that match the name followed by a number. Make all * of the other % characters in name literal, so the user doesn't get * surprised and sscanf doesn't drop core indirecting through pointers * that don't exist. If any such files are found, increment its number * by one. */ if (version) { GET_SPACE_GOTOC(sp, bp, blen, cmd.argv[0]->len * 2 + 50); INT2SYS(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, p, nlen); d = strdup(p); p = d; for (t = bp, slash = NULL; p[0] != '\0'; *t++ = *p++) if (p[0] == '%') { if (p[1] != '%') *t++ = '%'; } else if (p[0] == '/') slash = t; pct = t; *t++ = '%'; *t++ = 'd'; *t = '\0'; if (slash == NULL) { dirp = opendir("."); p = bp; } else { *slash = '\0'; dirp = opendir(bp); *slash = '/'; p = slash + 1; } if (dirp == NULL) { INT2SYS(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, estr, nlen); goto err; } for (maxnum = 0; (dp = readdir(dirp)) != NULL;) if (sscanf(dp->d_name, p, &num) == 1 && num > maxnum) maxnum = num; (void)closedir(dirp); /* Format the backup file name. */ (void)snprintf(pct, blen - (pct - bp), "%d", maxnum + 1); wfname = bp; } else { bp = NULL; INT2SYS(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, wfname, nlen); } /* Open the backup file, avoiding lurkers. */ if (stat(wfname, &sb) == 0) { if (!S_ISREG(sb.st_mode)) { msgq_str(sp, M_ERR, bname, "259|%s: not a regular file"); goto err; } if (sb.st_uid != getuid()) { msgq_str(sp, M_ERR, bname, "260|%s: not owned by you"); goto err; } if (sb.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) { msgq_str(sp, M_ERR, bname, "261|%s: accessible by a user other than the owner"); goto err; } flags = O_TRUNC; } else flags = O_CREAT | O_EXCL; if ((wfd = open(wfname, flags | O_WRONLY, S_IRUSR | S_IWUSR)) < 0) { estr = bname; goto err; } /* Copy the file's current contents to its backup value. */ while ((nr = read(rfd, buf, sizeof(buf))) > 0) for (off = 0; nr != 0; nr -= nw, off += nw) if ((nw = write(wfd, buf + off, nr)) < 0) { estr = wfname; goto err; } if (nr < 0) { estr = name; goto err; } if (close(rfd)) { estr = name; goto err; } if (close(wfd)) { estr = wfname; goto err; } if (bp != NULL) FREE_SPACE(sp, bp, blen); return (0); alloc_err: err: if (rfd != -1) (void)close(rfd); if (wfd != -1) { (void)unlink(wfname); (void)close(wfd); } if (estr) msgq_str(sp, M_SYSERR, estr, "%s"); if (d != NULL) free(d); if (bp != NULL) FREE_SPACE(sp, bp, blen); return (1); }
/* * sscr_insert -- * Take a line from the shell and insert it into the file. */ static int sscr_insert(SCR *sp) { struct timeval tv; char *endp, *p, *t; SCRIPT *sc; fd_set rdfd; db_recno_t lno; size_t len; ssize_t nr; char bp[1024]; const CHAR_T *ip; size_t ilen = 0; /* Find out where the end of the file is. */ if (db_last(sp, &lno)) return (1); endp = bp; /* Read the characters. */ sc = sp->script; more: switch (nr = read(sc->sh_master, endp, bp + sizeof(bp) - endp)) { case 0: /* EOF; shell just exited. */ sscr_end(sp); return (0); case -1: /* Error or interrupt. */ msgq(sp, M_SYSERR, "shell"); return (1); default: endp += nr; break; } /* Append the lines into the file. */ for (p = t = bp; p < endp; ++p) { if (*p == '\r' || *p == '\n') { len = p - t; if (CHAR2INT(sp, t, len, ip, ilen) || db_append(sp, 1, lno++, ip, ilen)) return (1); t = p + 1; } } /* * If the last thing from the shell isn't another prompt, wait up to * 1/10 of a second for more stuff to show up, so that we don't break * the output into two separate lines. Don't want to hang indefinitely * because some program is hanging, confused the shell, or whatever. * Note that sc->sh_prompt can be NULL here. */ len = p - t; if (sc->sh_prompt == NULL || len != sc->sh_prompt_len || memcmp(t, sc->sh_prompt, len) != 0) { tv.tv_sec = 0; tv.tv_usec = 100000; FD_ZERO(&rdfd); FD_SET(sc->sh_master, &rdfd); if (select(sc->sh_master + 1, &rdfd, NULL, NULL, &tv) == 1) { if (len == sizeof(bp)) { if (CHAR2INT(sp, t, len, ip, ilen) || db_append(sp, 1, lno++, ip, ilen)) return (1); endp = bp; } else { memmove(bp, t, len); endp = bp + len; } goto more; } if (sscr_setprompt(sp, t, len)) return (1); } /* Append the remains into the file, and the cursor moves to EOF. */ if (len > 0) { if (CHAR2INT(sp, t, len, ip, ilen) || db_append(sp, 1, lno++, ip, ilen)) return (1); sp->cno = ilen - 1; } else sp->cno = 0; sp->lno = lno; return (vs_refresh(sp, 1)); }
/* * sscr_exec -- * Take a line and hand it off to the shell. * * PUBLIC: int sscr_exec __P((SCR *, db_recno_t)); */ int sscr_exec(SCR *sp, db_recno_t lno) { SCRIPT *sc; db_recno_t last_lno; size_t blen, len, last_len; int isempty, matchprompt, rval; ssize_t nw; char *bp = NULL; const char *p; const CHAR_T *ip; size_t ilen; sc = sp->script; /* If there's a prompt on the last line, append the command. */ if (db_last(sp, &last_lno)) return (1); if (db_get(sp, last_lno, DBG_FATAL, __UNCONST(&ip), &ilen)) return (1); INT2CHAR(sp, ip, ilen, p, last_len); if (last_len == sc->sh_prompt_len && memcmp(p, sc->sh_prompt, last_len) == 0) { matchprompt = 1; GET_SPACE_RETC(sp, bp, blen, last_len + 128); memmove(bp, p, last_len); } else matchprompt = 0; /* Get something to execute. */ if (db_eget(sp, lno, __UNCONST(&ip), &ilen, &isempty)) { if (isempty) goto empty; goto err1; } /* Empty lines aren't interesting. */ if (ilen == 0) goto empty; INT2CHAR(sp, ip, ilen, p, len); /* Delete any prompt. */ if (len >= sc->sh_prompt_len && memcmp(p, sc->sh_prompt, sc->sh_prompt_len) == 0) { len -= sc->sh_prompt_len; if (len == 0) { empty: msgq(sp, M_BERR, "151|No command to execute"); goto err1; } p += sc->sh_prompt_len; } /* Push the line to the shell. */ if ((size_t)(nw = write(sc->sh_master, p, len)) != len) goto err2; rval = 0; if (write(sc->sh_master, "\n", 1) != 1) { err2: if (nw == 0) errno = EIO; msgq(sp, M_SYSERR, "shell"); goto err1; } if (matchprompt) { ADD_SPACE_GOTO(sp, char, bp, blen, last_len + len); memmove(bp + last_len, p, len); CHAR2INT(sp, bp, last_len + len, ip, ilen); if (db_set(sp, last_lno, ip, ilen)) err1: rval = 1; } if (matchprompt) alloc_err: FREE_SPACE(sp, bp, blen); return (rval); }
/* * v_exaddr -- * Do a vi search (which is really an ex address). */ static int v_exaddr(SCR *sp, VICMD *vp, dir_t dir) { static EXCMDLIST fake = { L("search") }; EXCMD *cmdp; GS *gp; TEXT *tp; recno_t s_lno; size_t len, s_cno, tlen; int err, nb, type; char buf[20]; CHAR_T *cmd, *t; CHAR_T *w; size_t wlen; /* * !!! * If using the search command as a motion, any addressing components * are lost, i.e. y/ptrn/+2, when repeated, is the same as y/ptrn/. */ if (F_ISSET(vp, VC_ISDOT)) return (v_search(sp, vp, NULL, 0, SEARCH_PARSE | SEARCH_MSG | SEARCH_SET, dir)); /* Get the search pattern. */ if (v_tcmd(sp, vp, dir == BACKWARD ? CH_BSEARCH : CH_FSEARCH, TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT | (O_ISSET(sp, O_SEARCHINCR) ? TXT_SEARCHINCR : 0))) return (1); tp = TAILQ_FIRST(sp->tiq); /* If the user backspaced over the prompt, do nothing. */ if (tp->term == TERM_BS) return (1); /* * If the user was doing an incremental search, then we've already * updated the cursor and moved to the right location. Return the * correct values, we're done. */ if (tp->term == TERM_SEARCH) { vp->m_stop.lno = sp->lno; vp->m_stop.cno = sp->cno; if (ISMOTION(vp)) return (v_correct(sp, vp, 0)); vp->m_final = vp->m_stop; return (0); } /* * If the user entered <escape> or <carriage-return>, the length is * 1 and the right thing will happen, i.e. the prompt will be used * as a command character. * * Build a fake ex command structure. */ gp = sp->gp; gp->excmd.cp = tp->lb; gp->excmd.clen = tp->len; F_INIT(&gp->excmd, E_VISEARCH); /* * XXX * Warn if the search wraps. This is a pretty special case, but it's * nice feature that wasn't in the original implementations of ex/vi. * (It was added at some point to System V's version.) This message * is only displayed if there are no keys in the queue. The problem is * the command is going to succeed, and the message is informational, * not an error. If a macro displays it repeatedly, e.g., the pattern * only occurs once in the file and wrapscan is set, you lose big. For * example, if the macro does something like: * * :map K /pattern/^MjK * * Each search will display the message, but the following "/pattern/" * will immediately overwrite it, with strange results. The System V * vi displays the "wrapped" message multiple times, but because it's * overwritten each time, it's not as noticeable. As we don't discard * messages, it's a real problem for us. */ if (!KEYS_WAITING(sp)) F_SET(&gp->excmd, E_SEARCH_WMSG); /* Save the current line/column. */ s_lno = sp->lno; s_cno = sp->cno; /* * !!! * Historically, vi / and ? commands were full-blown ex addresses, * including ';' delimiters, trailing <blank>'s, multiple search * strings (separated by semi-colons) and, finally, full-blown z * commands after the / and ? search strings. (If the search was * being used as a motion, the trailing z command was ignored. * Also, we do some argument checking on the z command, to be sure * that it's not some other random command.) For multiple search * strings, leading <blank>'s at the second and subsequent strings * were eaten as well. This has some (unintended?) side-effects: * the command /ptrn/;3 is legal and results in moving to line 3. * I suppose you could use it to optionally move to line 3... * * !!! * Historically, if any part of the search command failed, the cursor * remained unmodified (even if ; was used). We have to play games * because the underlying ex parser thinks we're modifying the cursor * as we go, but I think we're compatible with historic practice. * * !!! * Historically, the command "/STRING/; " failed, apparently it * confused the parser. We're not that compatible. */ cmdp = &gp->excmd; if (ex_range(sp, cmdp, &err)) return (1); /* * Remember where any remaining command information is, and clean * up the fake ex command. */ cmd = cmdp->cp; len = cmdp->clen; gp->excmd.clen = 0; if (err) goto err2; /* Copy out the new cursor position and make sure it's okay. */ switch (cmdp->addrcnt) { case 1: vp->m_stop = cmdp->addr1; break; case 2: vp->m_stop = cmdp->addr2; break; } if (!db_exist(sp, vp->m_stop.lno)) { ex_badaddr(sp, &fake, vp->m_stop.lno == 0 ? A_ZERO : A_EOF, NUM_OK); goto err2; } /* * !!! * Historic practice is that a trailing 'z' was ignored if it was a * motion command. Should probably be an error, but not worth the * effort. */ if (ISMOTION(vp)) return (v_correct(sp, vp, F_ISSET(cmdp, E_DELTA))); /* * !!! * Historically, if it wasn't a motion command, a delta in the search * pattern turns it into a first nonblank movement. */ nb = F_ISSET(cmdp, E_DELTA); /* Check for the 'z' command. */ if (len != 0) { if (*cmd != 'z') goto err1; /* No blanks, just like the z command. */ for (t = cmd + 1, tlen = len - 1; tlen > 0; ++t, --tlen) if (!isdigit(*t)) break; if (tlen && (*t == '-' || *t == '.' || *t == '+' || *t == '^')) { ++t; --tlen; type = 1; } else type = 0; if (tlen) goto err1; /* The z command will do the nonblank for us. */ nb = 0; /* Default to z+. */ if (!type && v_event_push(sp, NULL, L("+"), 1, CH_NOMAP | CH_QUOTED)) return (1); /* Push the user's command. */ if (v_event_push(sp, NULL, cmd, len, CH_NOMAP | CH_QUOTED)) return (1); /* Push line number so get correct z display. */ tlen = snprintf(buf, sizeof(buf), "%lu", (u_long)vp->m_stop.lno); CHAR2INT(sp, buf, tlen, w, wlen); if (v_event_push(sp, NULL, w, wlen, CH_NOMAP | CH_QUOTED)) return (1); /* Don't refresh until after 'z' happens. */ F_SET(VIP(sp), VIP_S_REFRESH); } /* Non-motion commands move to the end of the range. */ vp->m_final = vp->m_stop; if (nb) { F_CLR(vp, VM_RCM_MASK); F_SET(vp, VM_RCM_SETFNB); } return (0); err1: msgq(sp, M_ERR, "188|Characters after search string, line offset and/or z command"); err2: vp->m_final.lno = s_lno; vp->m_final.cno = s_cno; return (1); }
/* * 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_exrc -- * Read the EXINIT environment variable and the startup exrc files, * and execute their commands. * * PUBLIC: int ex_exrc(SCR *); */ int ex_exrc(SCR *sp) { struct stat hsb, lsb; char *p, *path; CHAR_T *wp; size_t wlen; /* * Source the system, environment, $HOME and local .exrc values. * Vi historically didn't check $HOME/.exrc if the environment * variable EXINIT was set. This is all done before the file is * read in, because things in the .exrc information can set, for * example, the recovery directory. * * !!! * While nvi can handle any of the options settings of historic vi, * the converse is not true. Since users are going to have to have * files and environmental variables that work with both, we use nvi * versions of both the $HOME and local startup files if they exist, * otherwise the historic ones. * * !!! * For a discussion of permissions and when what .exrc files are * read, see the comment above the exrc_isok() function below. * * !!! * If the user started the historic of vi in $HOME, vi read the user's * .exrc file twice, as $HOME/.exrc and as ./.exrc. We avoid this, as * it's going to make some commands behave oddly, and I can't imagine * anyone depending on it. */ switch (exrc_isok(sp, &hsb, _PATH_SYSEXRC, 1, 0)) { case NOEXIST: case NOPERM: break; case RCOK: if (ex_run_file(sp, _PATH_SYSEXRC)) return (1); break; } /* Run the commands. */ if (EXCMD_RUNNING(sp->gp)) (void)ex_cmd(sp); if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) return (0); if ((p = getenv("NEXINIT")) != NULL) { CHAR2INT(sp, p, strlen(p) + 1, wp, wlen); if (ex_run_str(sp, "NEXINIT", wp, wlen - 1, 1, 0)) return (1); } else if ((p = getenv("EXINIT")) != NULL) { CHAR2INT(sp, p, strlen(p) + 1, wp, wlen); if (ex_run_str(sp, "EXINIT", wp, wlen - 1, 1, 0)) return (1); } else if ((p = getenv("HOME")) != NULL && *p) { int st = 0; if ((path = join(p, _PATH_NEXRC)) == NULL) { msgq(sp, M_SYSERR, NULL); return (1); } switch (exrc_isok(sp, &hsb, path, 0, 1)) { case NOEXIST: free(path); if ((path = join(p, _PATH_EXRC)) == NULL) { msgq(sp, M_SYSERR, NULL); return (1); } if (exrc_isok(sp, &hsb, path, 0, 1) == RCOK && ex_run_file(sp, path)) st = 1; break; case NOPERM: break; case RCOK: if (ex_run_file(sp, path)) st = 1; break; } free(path); if (st) return st; } /* Run the commands. */ if (EXCMD_RUNNING(sp->gp)) (void)ex_cmd(sp); if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) return (0); /* Previous commands may have set the exrc option. */ if (O_ISSET(sp, O_EXRC)) { switch (exrc_isok(sp, &lsb, _PATH_NEXRC, 0, 0)) { case NOEXIST: if (exrc_isok(sp, &lsb, _PATH_EXRC, 0, 0) == RCOK && (lsb.st_dev != hsb.st_dev || lsb.st_ino != hsb.st_ino) && ex_run_file(sp, _PATH_EXRC)) return (1); break; case NOPERM: break; case RCOK: if ((lsb.st_dev != hsb.st_dev || lsb.st_ino != hsb.st_ino) && ex_run_file(sp, _PATH_NEXRC)) return (1); break; } /* Run the commands. */ if (EXCMD_RUNNING(sp->gp)) (void)ex_cmd(sp); if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) return (0); } return (0); }
/* * file_cinit -- * Set up the initial cursor position. */ static void file_cinit(SCR *sp) { GS *gp; MARK m; size_t len; int nb; CHAR_T *wp; size_t wlen; /* Set some basic defaults. */ sp->lno = 1; sp->cno = 0; /* * Historically, initial commands (the -c option) weren't executed * until a file was loaded, e.g. "vi +10 nofile", followed by an * :edit or :tag command, would execute the +10 on the file loaded * by the subsequent command, (assuming that it existed). This * applied as well to files loaded using the tag commands, and we * follow that historic practice. Also, all initial commands were * ex commands and were always executed on the last line of the file. * * Otherwise, if no initial command for this file: * If in ex mode, move to the last line, first nonblank character. * If the file has previously been edited, move to the last known * position, and check it for validity. * Otherwise, move to the first line, first nonblank. * * This gets called by the file init code, because we may be in a * file of ex commands and we want to execute them from the right * location in the file. */ nb = 0; gp = sp->gp; if (gp->c_option != NULL && !F_ISSET(sp->frp, FR_NEWFILE)) { if (db_last(sp, &sp->lno)) return; if (sp->lno == 0) { sp->lno = 1; sp->cno = 0; } CHAR2INT(sp, gp->c_option, strlen(gp->c_option) + 1, wp, wlen); if (ex_run_str(sp, "-c option", wp, wlen - 1, 1, 1)) return; gp->c_option = NULL; } else if (F_ISSET(sp, SC_EX)) { if (db_last(sp, &sp->lno)) return; if (sp->lno == 0) { sp->lno = 1; sp->cno = 0; return; } nb = 1; } else { if (F_ISSET(sp->frp, FR_CURSORSET)) { sp->lno = sp->frp->lno; sp->cno = sp->frp->cno; /* If returning to a file in vi, center the line. */ F_SET(sp, SC_SCR_CENTER); } else { if (O_ISSET(sp, O_COMMENT)) file_comment(sp); else sp->lno = 1; nb = 1; } if (db_get(sp, sp->lno, 0, NULL, &len)) { sp->lno = 1; sp->cno = 0; return; } if (!nb && sp->cno > len) nb = 1; } if (nb) { sp->cno = 0; (void)nonblank(sp, sp->lno, &sp->cno); } /* * !!! * The initial column is also the most attractive column. */ sp->rcm = sp->cno; /* * !!! * Historically, vi initialized the absolute mark, but ex did not. * Which meant, that if the first command in ex mode was "visual", * or if an ex command was executed first (e.g. vi +10 file) vi was * entered without the mark being initialized. For consistency, if * the file isn't empty, we initialize it for everyone, believing * that it can't hurt, and is generally useful. Not initializing it * if the file is empty is historic practice, although it has always * been possible to set (and use) marks in empty vi files. */ m.lno = sp->lno; m.cno = sp->cno; (void)mark_set(sp, ABSMARK1, &m, 0); }
/* * msgq -- * Display a message. * * PUBLIC: void msgq(SCR *, mtype_t, const char *, ...); */ void msgq( SCR *sp, mtype_t mt, const char *fmt, ...) { #ifndef NL_ARGMAX #define __NL_ARGMAX 20 /* Set to 9 by System V. */ struct { const char *str; /* String pointer. */ size_t arg; /* Argument number. */ size_t prefix; /* Prefix string length. */ size_t skip; /* Skipped string length. */ size_t suffix; /* Suffix string length. */ } str[__NL_ARGMAX]; #endif static int reenter; /* STATIC: Re-entrancy check. */ GS *gp; size_t blen, len, mlen, nlen; const char *p; char *bp, *mp; va_list ap; #ifndef NL_ARGMAX int ch; char *rbp, *s_rbp; const char *t, *u; size_t cnt1, cnt2, soff; #endif /* * !!! * It's possible to enter msg when there's no screen to hold the * message. If sp is NULL, ignore the special cases and put the * message out to stderr. */ if (sp == NULL) { gp = NULL; if (mt == M_BERR) mt = M_ERR; else if (mt == M_VINFO) mt = M_INFO; } else { gp = sp->gp; switch (mt) { case M_BERR: if (F_ISSET(sp, SC_VI) && !O_ISSET(sp, O_VERBOSE)) { F_SET(gp, G_BELLSCHED); return; } mt = M_ERR; break; case M_VINFO: if (!O_ISSET(sp, O_VERBOSE)) return; mt = M_INFO; /* FALLTHROUGH */ case M_INFO: if (F_ISSET(sp, SC_EX_SILENT)) return; break; case M_ERR: case M_SYSERR: break; default: abort(); } } /* * It's possible to reenter msg when it allocates space. We're * probably dead anyway, but there's no reason to drop core. * * XXX * Yes, there's a race, but it should only be two instructions. */ if (reenter++) return; /* Get space for the message. */ nlen = 1024; if (0) { retry: FREE_SPACE(sp, bp, blen); nlen *= 2; } bp = NULL; blen = 0; GET_SPACE_GOTOC(sp, bp, blen, nlen); /* * Error prefix. * * mp: pointer to the current next character to be written * mlen: length of the already written characters * blen: total length of the buffer */ #define REM (blen - mlen) mp = bp; mlen = 0; if (mt == M_SYSERR) { p = msg_cat(sp, "020|Error: ", &len); if (REM < len) goto retry; memcpy(mp, p, len); mp += len; mlen += len; } /* * If we're running an ex command that the user didn't enter, display * the file name and line number prefix. */ if ((mt == M_ERR || mt == M_SYSERR) && sp != NULL && gp != NULL && gp->if_name != NULL) { CHAR_T *wp; size_t wlen; CHAR2INT(sp, gp->if_name, strlen(gp->if_name) + 1, wp, wlen); for (; *wp != '\0'; ++wp) { len = snprintf(mp, REM, "%s", KEY_NAME(sp, *wp)); mp += len; if ((mlen += len) > blen) goto retry; } len = snprintf(mp, REM, ", %d: ", gp->if_lno); mp += len; if ((mlen += len) > blen) goto retry; } /* If nothing to format, we're done. */ if (fmt == NULL) goto nofmt; fmt = msg_cat(sp, fmt, NULL); #ifndef NL_ARGMAX /* * Nvi should run on machines that don't support the numbered argument * specifications (%[digit]*$). We do this by reformatting the string * so that we can hand it to vsprintf(3) and it will use the arguments * in the right order. When vsprintf returns, we put the string back * into the right order. It's undefined, according to SVID III, to mix * numbered argument specifications with the standard style arguments, * so this should be safe. * * In addition, we also need a character that is known to not occur in * any vi message, for separating the parts of the string. As callers * of msgq are responsible for making sure that all the non-printable * characters are formatted for printing before calling msgq, we use a * random non-printable character selected at terminal initialization * time. This code isn't fast by any means, but as messages should be * relatively short and normally have only a few arguments, it won't be * too bad. Regardless, nobody has come up with any other solution. * * The result of this loop is an array of pointers into the message * string, with associated lengths and argument numbers. The array * is in the "correct" order, and the arg field contains the argument * order. */ for (p = fmt, soff = 0; soff < __NL_ARGMAX;) { for (t = p; *p != '\0' && *p != '%'; ++p); if (*p == '\0') break; ++p; if (!isdigit(*p)) { if (*p == '%') ++p; continue; } for (u = p; *++p != '\0' && isdigit(*p);); if (*p != '$') continue; /* Up to, and including the % character. */ str[soff].str = t; str[soff].prefix = u - t; /* Up to, and including the $ character. */ str[soff].arg = atoi(u); str[soff].skip = (p - u) + 1; if (str[soff].arg >= __NL_ARGMAX) goto ret; /* Up to, and including the conversion character. */ for (u = p; (ch = *++p) != '\0';) if (isalpha(ch) && strchr("diouxXfeEgGcspn", ch) != NULL) break; str[soff].suffix = p - u; if (ch != '\0') ++p; ++soff; } /* If no magic strings, we're done. */ if (soff == 0) goto format; /* Get space for the reordered strings. */ if ((rbp = malloc(nlen)) == NULL) goto ret; s_rbp = rbp; /* * Reorder the strings into the message string based on argument * order. * * !!! * We ignore arguments that are out of order, i.e. if we don't find * an argument, we continue. Assume (almost certainly incorrectly) * that whoever created the string knew what they were doing. * * !!! * Brute force "sort", but since we don't expect more than one or two * arguments in a string, the setup cost of a fast sort will be more * expensive than the loop. */ for (cnt1 = 1; cnt1 <= soff; ++cnt1) for (cnt2 = 0; cnt2 < soff; ++cnt2) if (cnt1 == str[cnt2].arg) { memmove(s_rbp, str[cnt2].str, str[cnt2].prefix); memmove(s_rbp + str[cnt2].prefix, str[cnt2].str + str[cnt2].prefix + str[cnt2].skip, str[cnt2].suffix); s_rbp += str[cnt2].prefix + str[cnt2].suffix; *s_rbp++ = gp == NULL ? DEFAULT_NOPRINT : gp->noprint; break; } *s_rbp = '\0'; fmt = rbp; #endif #ifndef NL_ARGMAX format: /* Format the arguments into the string. */ #endif va_start(ap, fmt); len = vsnprintf(mp, REM, fmt, ap); va_end(ap); if (len >= nlen) goto retry; #ifndef NL_ARGMAX if (soff == 0) goto nofmt; /* * Go through the resulting string, and, for each separator character * separated string, enter its new starting position and length in the * array. */ for (p = t = mp, cnt1 = 1, ch = gp == NULL ? DEFAULT_NOPRINT : gp->noprint; *p != '\0'; ++p) if (*p == ch) { for (cnt2 = 0; cnt2 < soff; ++cnt2) if (str[cnt2].arg == cnt1) break; str[cnt2].str = t; str[cnt2].prefix = p - t; t = p + 1; ++cnt1; } /* * Reorder the strings once again, putting them back into the * message buffer. * * !!! * Note, the length of the message gets decremented once for * each substring, when we discard the separator character. */ for (s_rbp = rbp, cnt1 = 0; cnt1 < soff; ++cnt1) { memmove(rbp, str[cnt1].str, str[cnt1].prefix); rbp += str[cnt1].prefix; --len; } memmove(mp, s_rbp, rbp - s_rbp); /* Free the reordered string memory. */ free(s_rbp); #endif nofmt: mp += len; if ((mlen += len) > blen) goto retry; if (mt == M_SYSERR) { len = snprintf(mp, REM, ": %s", strerror(errno)); mp += len; if ((mlen += len) > blen) goto retry; mt = M_ERR; } /* Add trailing newline. */ if ((mlen += 1) > blen) goto retry; *mp = '\n'; if (sp != NULL) (void)ex_fflush(sp); if (gp != NULL) gp->scr_msg(sp, mt, bp, mlen); else (void)fprintf(stderr, "%.*s", (int)mlen, bp); /* Cleanup. */ #ifndef NL_ARGMAX ret: #endif FREE_SPACE(sp, bp, blen); alloc_err: reenter = 0; }
/* * ip_read -- * Read characters from the input. */ static input_t ip_read(SCR *sp, IP_PRIVATE *ipp, struct timeval *tp, int termread, int *nr) { GS *gp; fd_set rdfd; input_t rval; size_t blen; int maxfd; char *bp; int fd; const CHAR_T *wp; size_t wlen; gp = sp == NULL ? __global_list : sp->gp; bp = ipp->ibuf + ipp->iblen; blen = sizeof(ipp->ibuf) - ipp->iblen; fd = termread ? ipp->t_fd : ipp->i_fd; /* * 1: A read with an associated timeout, e.g., trying to complete * a map sequence. If input exists, we fall into #2. */ FD_ZERO(&rdfd); if (tp != NULL) { FD_SET(fd, &rdfd); switch (select(fd + 1, &rdfd, NULL, NULL, tp)) { case 0: return (INP_TIMEOUT); case -1: goto err; default: break; } } /* * 2: Wait for input. * * Select on the command input and scripting window file descriptors. * It's ugly that we wait on scripting file descriptors here, but it's * the only way to keep from locking out scripting windows. */ if (sp != NULL && F_ISSET(gp, G_SCRWIN)) { FD_ZERO(&rdfd); FD_SET(fd, &rdfd); maxfd = fd; if (sscr_check_input(sp, &rdfd, maxfd)) goto err; } /* * 3: Read the input. */ switch (*nr = read(fd, termread ? (char *)ipp->tbuf : bp, termread ? sizeof(ipp->tbuf)/sizeof(CHAR_T) : blen)) { case 0: /* EOF. */ rval = INP_EOF; break; case -1: /* Error or interrupt. */ err: rval = INP_ERR; msgq(sp, M_SYSERR, "input"); break; default: /* Input characters. */ if (sp == NULL) { rval = INP_ERR; msgq(sp, M_SYSERR, "Can't convert input with NULL screen"); break; } if (!termread) ipp->iblen += *nr; else { CHAR2INT(sp, (char *)ipp->tbuf, *nr, wp, wlen); MEMMOVEW(ipp->tbuf, wp, wlen); } rval = INP_OK; break; } return (rval); }
/* * 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); }
/* * ex_visual -- :[line] vi[sual] [^-.+] [window_size] [flags] * Switch to visual mode. * * PUBLIC: int ex_visual __P((SCR *, EXCMD *)); */ int ex_visual(SCR *sp, EXCMD *cmdp) { SCR *tsp; size_t len; int pos; char buf[256]; size_t wlen; CHAR_T *wp; /* If open option off, disallow visual command. */ if (!O_ISSET(sp, O_OPEN)) { msgq(sp, M_ERR, "175|The visual command requires that the open option be set"); return (1); } /* Move to the address. */ sp->lno = cmdp->addr1.lno == 0 ? 1 : cmdp->addr1.lno; /* * Push a command based on the line position flags. If no * flag specified, the line goes at the top of the screen. */ switch (FL_ISSET(cmdp->iflags, E_C_CARAT | E_C_DASH | E_C_DOT | E_C_PLUS)) { case E_C_CARAT: pos = '^'; break; case E_C_DASH: pos = '-'; break; case E_C_DOT: pos = '.'; break; case E_C_PLUS: pos = '+'; break; default: sp->frp->lno = sp->lno; sp->frp->cno = 0; (void)nonblank(sp, sp->lno, &sp->cno); F_SET(sp->frp, FR_CURSORSET); goto nopush; } if (FL_ISSET(cmdp->iflags, E_C_COUNT)) len = snprintf(buf, sizeof(buf), "%luz%c%lu", (u_long)sp->lno, pos, cmdp->count); else len = snprintf(buf, sizeof(buf), "%luz%c", (u_long)sp->lno, pos); CHAR2INT(sp, buf, len, wp, wlen); (void)v_event_push(sp, NULL, wp, wlen, CH_NOMAP | CH_QUOTED); /* * !!! * Historically, if no line address was specified, the [p#l] flags * caused the cursor to be moved to the last line of the file, which * was then positioned as described above. This seems useless, so * I haven't implemented it. */ switch (FL_ISSET(cmdp->iflags, E_C_HASH | E_C_LIST | E_C_PRINT)) { case E_C_HASH: O_SET(sp, O_NUMBER); break; case E_C_LIST: O_SET(sp, O_LIST); break; case E_C_PRINT: break; } nopush: /* * !!! * You can call the visual part of the editor from within an ex * global command. * * XXX * Historically, undoing a visual session was a single undo command, * i.e. you could undo all of the changes you made in visual mode. * We don't get this right; I'm waiting for the new logging code to * be available. * * It's explicit, don't have to wait for the user, unless there's * already a reason to wait. */ if (!F_ISSET(sp, SC_SCR_EXWROTE)) F_SET(sp, SC_EX_WAIT_NO); if (F_ISSET(sp, SC_EX_GLOBAL)) { /* * When the vi screen(s) exit, we don't want to lose our hold * on this screen or this file, otherwise we're going to fail * fairly spectacularly. */ ++sp->refcnt; ++sp->ep->refcnt; /* XXXX where is this decremented ? */ /* * Fake up a screen pointer -- vi doesn't get to change our * underlying file, regardless. */ tsp = sp; if (vi(&tsp)) return (1); /* * !!! * Historically, if the user exited the vi screen(s) using an * ex quit command (e.g. :wq, :q) ex/vi exited, it was only if * they exited vi using the Q command that ex continued. Some * early versions of nvi continued in ex regardless, but users * didn't like the semantic. * * Reset the screen. */ if (ex_init(sp)) return (1); /* Move out of the vi screen. */ (void)ex_puts(sp, "\n"); } else { F_CLR(sp, SC_EX | SC_SCR_EX); F_SET(sp, SC_VI); } return (0); }