Пример #1
0
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;
  
}
Пример #2
0
/*
 * 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);
}
Пример #3
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));
}
Пример #4
0
/*
 * 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));
}
Пример #5
0
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;
}
Пример #6
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));
}
Пример #7
0
/*
 * 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);
}
Пример #8
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)
Пример #9
0
/*
 * 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);
}
Пример #10
0
/*
 * 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;
}
Пример #11
0
Файл: exf.c Проект: fishman/nvi
/*
 * 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);
}
Пример #12
0
/*
 * 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));
}
Пример #13
0
/*
 * 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);
}
Пример #14
0
/*
 * 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);
}
Пример #15
0
Файл: main.c Проект: fishman/nvi
/*
 * 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);
	}
Пример #16
0
/*
 * 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);
}
Пример #17
0
Файл: exf.c Проект: fishman/nvi
/*
 * 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);
}
Пример #18
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;
}
Пример #19
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);
}
Пример #20
0
/*
 * 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);
}
Пример #21
0
/*
 * 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);
}