aboutsummaryrefslogtreecommitdiffstats
path: root/usr.bin/vi/nex/ex.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/vi/nex/ex.c')
-rw-r--r--usr.bin/vi/nex/ex.c1523
1 files changed, 0 insertions, 1523 deletions
diff --git a/usr.bin/vi/nex/ex.c b/usr.bin/vi/nex/ex.c
deleted file mode 100644
index 3f5beac4a734..000000000000
--- a/usr.bin/vi/nex/ex.c
+++ /dev/null
@@ -1,1523 +0,0 @@
-/*-
- * Copyright (c) 1992, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef lint
-static char sccsid[] = "@(#)ex.c 8.90 (Berkeley) 1/23/94";
-#endif /* not lint */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "vi.h"
-#include "excmd.h"
-
-static inline EXCMDLIST const *
- ex_comm_search __P((char *, size_t));
-static int ep_line __P((SCR *, EXF *, MARK *, char **, size_t *, int *));
-static int ep_range __P((SCR *, EXF *, EXCMDARG *, char **, size_t *));
-
-#define DEFCOM ".+1"
-
-/*
- * ex --
- * Read an ex command and execute it.
- */
-int
-ex(sp, ep)
- SCR *sp;
- EXF *ep;
-{
- TEXT *tp;
- u_int saved_mode;
- int eval;
- char defcom[sizeof(DEFCOM)];
-
- if (ex_init(sp, ep))
- return (1);
-
- if (sp->s_refresh(sp, ep))
- return (ex_end(sp));
-
- /* If reading from a file, messages should have line info. */
- if (!F_ISSET(sp->gp, G_ISFROMTTY)) {
- sp->if_lno = 1;
- sp->if_name = strdup("input");
- }
- for (eval = 0;; ++sp->if_lno) {
- /* Get the next command. */
- switch (sp->s_get(sp, ep, &sp->tiq, ':', TXT_CR | TXT_PROMPT)) {
- case INP_OK:
- break;
- case INP_EOF:
- F_SET(sp, S_EXIT_FORCE);
- goto ret;
- case INP_ERR:
- continue;
- }
-
- saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
- tp = sp->tiq.cqh_first;
- if (tp->len == 0) {
- if (F_ISSET(sp->gp, G_ISFROMTTY)) {
- (void)fputc('\r', stdout);
- (void)fflush(stdout);
- }
- memmove(defcom, DEFCOM, sizeof(DEFCOM));
- (void)ex_icmd(sp, ep, defcom, sizeof(DEFCOM) - 1);
- } else {
- if (F_ISSET(sp->gp, G_ISFROMTTY))
- (void)fputc('\n', stdout);
- (void)ex_icmd(sp, ep, tp->lb, tp->len);
- }
- (void)msg_rpt(sp, 0);
-
- if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE))
- break;
-
- if (sp->s_refresh(sp, ep)) {
- eval = 1;
- break;
- }
- }
-ret: if (sp->if_name != NULL) {
- FREE(sp->if_name, strlen(sp->if_name) + 1);
- sp->if_name = NULL;
- }
- return (ex_end(sp) || eval);
-}
-
-/*
- * ex_cfile --
- * Execute ex commands from a file.
- */
-int
-ex_cfile(sp, ep, filename)
- SCR *sp;
- EXF *ep;
- char *filename;
-{
- struct stat sb;
- int fd, len, rval;
- char *bp;
-
- bp = NULL;
- if ((fd = open(filename, O_RDONLY, 0)) < 0 || fstat(fd, &sb))
- goto err;
-
- /*
- * XXX
- * We'd like to test if the file is too big 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. Hope that malloc fails if the file is too
- * large.
- */
- MALLOC(sp, bp, char *, (size_t)sb.st_size + 1);
- if (bp == NULL)
- goto err;
-
- len = read(fd, bp, (int)sb.st_size);
- if (len == -1 || len != sb.st_size) {
- if (len != sb.st_size)
- errno = EIO;
-err: rval = 1;
- msgq(sp, M_SYSERR, filename);
- } else {
- bp[sb.st_size] = '\0'; /* XXX */
-
- /* Run the command. Messages include file/line information. */
- sp->if_lno = 1;
- sp->if_name = strdup(filename);
- rval = ex_icmd(sp, ep, bp, len);
- FREE(sp->if_name, strlen(sp->if_name) + 1);
- sp->if_name = NULL;
- }
-
- /*
- * !!!
- * THE UNDERLYING EXF MAY HAVE CHANGED.
- */
- if (bp != NULL)
- FREE(bp, sb.st_size);
- if (fd >= 0)
- (void)close(fd);
- return (rval);
-}
-
-/*
- * ex_icmd --
- * Call ex_cmd() after turning off interruptible bits.
- */
-int
-ex_icmd(sp, ep, cmd, len)
- SCR *sp;
- EXF *ep;
- char *cmd;
- size_t len;
-{
- /*
- * Ex goes through here for each vi :colon command and for each ex
- * command, however, globally executed commands don't go through
- * here, instead, they call ex_cmd directly. So, reset all of the
- * interruptible flags now.
- */
- F_CLR(sp, S_INTERRUPTED | S_INTERRUPTIBLE);
-
- return (ex_cmd(sp, ep, cmd, len));
-}
-
-/* Special command structure for :s as a repeat substitution command. */
-static EXCMDLIST const cmd_subagain =
- {"s", ex_subagain, E_ADDR2|E_NORC,
- "s",
- "[line [,line]] s [cgr] [count] [#lp]",
- "repeat the last subsitution"};
-
-/*
- * ex_cmd --
- * Parse and execute a string containing ex commands.
- */
-int
-ex_cmd(sp, ep, cmd, cmdlen)
- SCR *sp;
- EXF *ep;
- char *cmd;
- size_t cmdlen;
-{
- CHAR_T vlit;
- EX_PRIVATE *exp;
- EXCMDARG exc;
- EXCMDLIST const *cp;
- MARK cur;
- recno_t lno, num;
- size_t arg1_len, len, save_cmdlen;
- long flagoff;
- u_int saved_mode;
- int ch, cnt, delim, flags, namelen, nl, uselastcmd, tmp;
- char *arg1, *save_cmd, *p, *t;
-
- /* Init. */
- nl = 0;
-loop: if (nl) {
- nl = 0;
- ++sp->if_lno;
- }
- arg1 = NULL;
- save_cmdlen = 0;
-
- /*
- * It's possible that we've been interrupted during a
- * command.
- */
- if (F_ISSET(sp, S_INTERRUPTED))
- return (0);
-
- /* Skip whitespace, separators, newlines. */
- for (; cmdlen > 0; ++cmd, --cmdlen)
- if ((ch = *cmd) == '\n')
- ++sp->if_lno;
- else if (!isblank(ch))
- break;
- if (cmdlen == 0)
- return (0);
-
- /* Command lines that start with a double-quote are comments. */
- if (ch == '"') {
- while (--cmdlen > 0 && *++cmd != '\n');
- if (*cmd == '\n') {
- ++cmd;
- --cmdlen;
- ++sp->if_lno;
- }
- goto loop;
- }
-
- /*
- * !!!
- * Permit extra colons at the start of the line. Historically,
- * ex/vi allowed a single extra one. It's simpler not to count.
- * The stripping is done here because, historically, any command
- * could have preceding colons, e.g. ":g/pattern/:p" worked.
- */
- if (ch == ':')
- while (--cmdlen > 0 && *++cmd == ':');
-
- /* Skip whitespace. */
- for (; cmdlen > 0; ++cmd, --cmdlen) {
- ch = *cmd;
- if (!isblank(ch))
- break;
- }
-
- /* The last point at which an empty line means do nothing. */
- if (cmdlen == 0)
- return (0);
-
- /* Initialize the structure passed to underlying functions. */
- memset(&exc, 0, sizeof(EXCMDARG));
- exp = EXP(sp);
- if (argv_init(sp, ep, &exc))
- goto err;
-
- /* Parse command addresses. */
- if (ep_range(sp, ep, &exc, &cmd, &cmdlen))
- goto err;
-
- /* Skip whitespace. */
- for (; cmdlen > 0; ++cmd, --cmdlen) {
- ch = *cmd;
- if (!isblank(ch))
- break;
- }
-
- /*
- * If no command, ex does the last specified of p, l, or #, and vi
- * moves to the line. Otherwise, determine the length of the command
- * name by looking for the first non-alphabetic character. (There
- * are a few non-alphabetic characters in command names, but they're
- * all single character commands.) This isn't a great test, because
- * it means that, for the command ":e +cut.c file", we'll report that
- * the command "cut" wasn't known. However, it makes ":e+35 file" work
- * correctly.
- */
-#define SINGLE_CHAR_COMMANDS "!#&<=>@~"
- if (cmdlen != 0 && cmd[0] != '|' && cmd[0] != '\n') {
- if (strchr(SINGLE_CHAR_COMMANDS, *cmd)) {
- p = cmd;
- ++cmd;
- --cmdlen;
- namelen = 1;
- } else {
- for (p = cmd; cmdlen > 0; --cmdlen, ++cmd)
- if (!isalpha(*cmd))
- break;
- if ((namelen = cmd - p) == 0) {
- msgq(sp, M_ERR, "Unknown command name.");
- goto err;
- }
- }
-
- /*
- * Search the table for the command.
- *
- * !!!
- * Historic vi permitted the mark to immediately follow the
- * 'k' in the 'k' command. Make it work.
- *
- * !!!
- * Historic vi permitted pretty much anything to follow the
- * substitute command, e.g. "s/e/E/|s|sgc3p" was fine. Make
- * it work.
- *
- * Use of msgq below is safe, command names are all alphabetics.
- */
- if ((cp = ex_comm_search(p, namelen)) == NULL)
- if (p[0] == 'k' && p[1] && !p[2]) {
- cmd -= namelen - 1;
- cmdlen += namelen - 1;
- cp = &cmds[C_K];
- } else if (p[0] == 's') {
- cmd -= namelen - 1;
- cmdlen += namelen - 1;
- cp = &cmd_subagain;
- } else {
- msgq(sp, M_ERR,
- "The %.*s command is unknown.", namelen, p);
- goto err;
- }
-
- /* Some commands are either not implemented or turned off. */
- if (F_ISSET(cp, E_NOPERM)) {
- msgq(sp, M_ERR,
- "The %s command is not currently supported.",
- cp->name);
- goto err;
- }
-
- /* Some commands aren't okay in globals. */
- if (F_ISSET(sp, S_GLOBAL) && F_ISSET(cp, E_NOGLOBAL)) {
- msgq(sp, M_ERR,
- "The %s command can't be used as part of a global command.",
- cp->name);
- goto err;
- }
-
- /*
- * Multiple < and > characters; another "feature". Note,
- * The string passed to the underlying function may not be
- * nul terminated in this case.
- */
- if ((cp == &cmds[C_SHIFTL] && *p == '<') ||
- (cp == &cmds[C_SHIFTR] && *p == '>')) {
- for (ch = *p; cmdlen > 0; --cmdlen, ++cmd)
- if (*cmd != ch)
- break;
- if (argv_exp0(sp, ep, &exc, p, cmd - p))
- goto err;
- }
-
- /*
- * The visual command has a different syntax when called
- * from ex than when called from a vi colon command. FMH.
- */
- if (cp == &cmds[C_VISUAL_EX] && IN_VI_MODE(sp))
- cp = &cmds[C_VISUAL_VI];
-
- uselastcmd = 0;
- } else {
- cp = exp->lastcmd;
- uselastcmd = 1;
- }
-
- /* Initialize local flags to the command flags. */
- LF_INIT(cp->flags);
-
- /*
- * File state must be checked throughout this code, because it is
- * called when reading the .exrc file and similar things. There's
- * this little chicken and egg problem -- if we read the file first,
- * we won't know how to display it. If we read/set the exrc stuff
- * first, we can't allow any command that requires file state.
- * Historic vi generally took the easy way out and dropped core.
- */
- if (LF_ISSET(E_NORC) && ep == NULL) {
- msgq(sp, M_ERR,
- "The %s command requires that a file already have been read in.",
- cp->name);
- goto err;
- }
-
- /*
- * There are three normal termination cases for an ex command. They
- * are the end of the string (cmdlen), or unescaped (by literal next
- * characters) newline or '|' characters. As we're past any addresses,
- * we can now determine how long the command is, so we don't have to
- * look for all the possible terminations. There are three exciting
- * special cases:
- *
- * 1: The bang, global, vglobal and the filter versions of the read and
- * write commands are delimited by newlines (they can contain shell
- * pipes).
- * 2: The ex, edit and visual in vi mode commands take ex commands as
- * their first arguments.
- * 3: The substitute command takes an RE as its first argument, and
- * wants it to be specially delimited.
- *
- * Historically, '|' characters in the first argument of the ex, edit,
- * and substitute commands did not delimit the command. And, in the
- * filter cases for read and write, and the bang, global and vglobal
- * commands, they did not delimit the command at all.
- *
- * For example, the following commands were legal:
- *
- * :edit +25|s/abc/ABC/ file.c
- * :substitute s/|/PIPE/
- * :read !spell % | columnate
- * :global/pattern/p|l
- *
- * It's not quite as simple as it sounds, however. The command:
- *
- * :substitute s/a/b/|s/c/d|set
- *
- * was also legal, i.e. the historic ex parser (using the word loosely,
- * since "parser" implies some regularity) delimited the RE's based on
- * its delimiter and not anything so irretrievably vulgar as a command
- * syntax.
- *
- * One thing that makes this easier is that we can ignore most of the
- * command termination conditions for the commands that want to take
- * the command up to the next newline. None of them are legal in .exrc
- * files, so if we're here, we only dealing with a single line, and we
- * can just eat it.
- *
- * Anyhow, the following code makes this all work. First, for the
- * special cases we move past their special argument. Then, we do
- * normal command processing on whatever is left. Barf-O-Rama.
- */
- arg1_len = 0;
- save_cmd = cmd;
- (void)term_key_ch(sp, K_VLNEXT, &vlit);
- if (cp == &cmds[C_EDIT] ||
- cp == &cmds[C_EX] || cp == &cmds[C_VISUAL_VI]) {
- /*
- * Move to the next non-whitespace character. As '+' must
- * be the character after the command name, if there isn't
- * one, we're done.
- */
- for (; cmdlen > 0; --cmdlen, ++cmd) {
- ch = *cmd;
- if (!isblank(ch))
- break;
- }
- /*
- * QUOTING NOTE:
- *
- * The historic implementation ignored all escape characters
- * so there was no way to put a space or newline into the +cmd
- * field. We do a simplistic job of fixing it by moving to the
- * first whitespace character that isn't escaped by a literal
- * next character. The literal next characters are stripped
- * as they're no longer useful.
- */
- if (cmdlen > 0 && ch == '+') {
- ++cmd;
- --cmdlen;
- for (arg1 = p = cmd; cmdlen > 0; --cmdlen, ++cmd) {
- if ((ch = *cmd) == vlit && cmdlen > 1) {
- --cmdlen;
- ch = *++cmd;
- } else if (isblank(ch))
- break;
- *p++ = ch;
- }
- arg1_len = cmd - arg1;
-
- /* Reset, so the first argument isn't reparsed. */
- save_cmd = cmd;
- }
- } else if (cp == &cmds[C_BANG] ||
- cp == &cmds[C_GLOBAL] || cp == &cmds[C_VGLOBAL]) {
- cmd += cmdlen;
- cmdlen = 0;
- } else if (cp == &cmds[C_READ] || cp == &cmds[C_WRITE]) {
- /*
- * Move to the next character. If it's a '!', it's a filter
- * command and we want to eat it all, otherwise, we're done.
- */
- for (; cmdlen > 0; --cmdlen, ++cmd) {
- ch = *cmd;
- if (!isblank(ch))
- break;
- }
- if (cmdlen > 0 && ch == '!') {
- cmd += cmdlen;
- cmdlen = 0;
- }
- } else if (cp == &cmds[C_SUBSTITUTE]) {
- /*
- * Move to the next non-whitespace character, we'll use it as
- * the delimiter. If the character isn't an alphanumeric or
- * a '|', it's the delimiter, so parse it. Otherwise, we're
- * into something like ":s g", so use the special substitute
- * command.
- */
- for (; cmdlen > 0; --cmdlen, ++cmd)
- if (!isblank(cmd[0]))
- break;
-
- if (isalnum(cmd[0]) || cmd[0] == '|')
- cp = &cmd_subagain;
- else if (cmdlen > 0) {
- /*
- * QUOTING NOTE:
- *
- * Backslashes quote delimiter characters for RE's.
- * The backslashes are NOT removed since they'll be
- * used by the RE code. Move to the third delimiter
- * that's not escaped (or the end of the command).
- */
- delim = *cmd;
- ++cmd;
- --cmdlen;
- for (cnt = 2; cmdlen > 0 && cnt; --cmdlen, ++cmd)
- if (cmd[0] == '\\' && cmdlen > 1) {
- ++cmd;
- --cmdlen;
- } else if (cmd[0] == delim)
- --cnt;
- }
- }
- /*
- * Use normal quoting and termination rules to find the end
- * of this command.
- *
- * QUOTING NOTE:
- *
- * Historically, vi permitted ^V's to escape <newline>'s in the .exrc
- * file. It was almost certainly a bug, but that's what bug-for-bug
- * compatibility means, Grasshopper. Also, ^V's escape the command
- * delimiters. Literal next quote characters in front of the newlines,
- * '|' characters or literal next characters are stripped as as they're
- * no longer useful.
- */
- for (p = cmd, cnt = 0; cmdlen > 0; --cmdlen, ++cmd) {
- if ((ch = cmd[0]) == vlit && cmdlen > 1) {
- ch = cmd[1];
- if (ch == '\n' || ch == '|') {
- if (ch == '\n')
- ++sp->if_lno;
- --cmdlen;
- ++cmd;
- ++cnt;
- } else
- ch = vlit;
- } else if (ch == '\n' || ch == '|') {
- if (ch == '\n')
- nl = 1;
- --cmdlen;
- break;
- }
- *p++ = ch;
- }
-
- /*
- * Save off the next command information, go back to the
- * original start of the command.
- */
- p = cmd + 1;
- cmd = save_cmd;
- save_cmd = p;
- save_cmdlen = cmdlen;
- cmdlen = ((save_cmd - cmd) - 1) - cnt;
-
- /*
- * !!!
- * The "set tags" command historically used a backslash, not the
- * user's literal next character, to escape whitespace. Handle
- * it here instead of complicating the argv_exp3() code. Note,
- * this isn't a particularly complex trap, and if backslashes were
- * legal in set commands, this would have to be much more complicated.
- */
- if (cp == &cmds[C_SET])
- for (p = cmd, len = cmdlen; len > 0; --len, ++p)
- if (*p == '\\')
- *p = vlit;
-
- /*
- * Set the default addresses. It's an error to specify an address for
- * a command that doesn't take them. If two addresses are specified
- * for a command that only takes one, lose the first one. Two special
- * cases here, some commands take 0 or 2 addresses. For most of them
- * (the E_ADDR2_ALL flag), 0 defaults to the entire file. For one
- * (the `!' command, the E_ADDR2_NONE flag), 0 defaults to no lines.
- *
- * Also, if the file is empty, some commands want to use an address of
- * 0, i.e. the entire file is 0 to 0, and the default first address is
- * 0. Otherwise, an entire file is 1 to N and the default line is 1.
- * Note, we also add the E_ZERO flag to the command flags, for the case
- * where the 0 address is only valid if it's a default address.
- *
- * Also, set a flag if we set the default addresses. Some commands
- * (ex: z) care if the user specified an address of if we just used
- * the current cursor.
- */
- switch (LF_ISSET(E_ADDR1|E_ADDR2|E_ADDR2_ALL|E_ADDR2_NONE)) {
- case E_ADDR1: /* One address: */
- switch (exc.addrcnt) {
- case 0: /* Default cursor/empty file. */
- exc.addrcnt = 1;
- F_SET(&exc, E_ADDRDEF);
- if (LF_ISSET(E_ZERODEF)) {
- if (file_lline(sp, ep, &lno))
- goto err;
- if (lno == 0) {
- exc.addr1.lno = 0;
- LF_SET(E_ZERO);
- } else
- exc.addr1.lno = sp->lno;
- } else
- exc.addr1.lno = sp->lno;
- exc.addr1.cno = sp->cno;
- break;
- case 1:
- break;
- case 2: /* Lose the first address. */
- exc.addrcnt = 1;
- exc.addr1 = exc.addr2;
- }
- break;
- case E_ADDR2_NONE: /* Zero/two addresses: */
- if (exc.addrcnt == 0) /* Default to nothing. */
- break;
- goto two;
- case E_ADDR2_ALL: /* Zero/two addresses: */
- if (exc.addrcnt == 0) { /* Default entire/empty file. */
- exc.addrcnt = 2;
- F_SET(&exc, E_ADDRDEF);
- if (file_lline(sp, ep, &exc.addr2.lno))
- goto err;
- if (LF_ISSET(E_ZERODEF) && exc.addr2.lno == 0) {
- exc.addr1.lno = 0;
- LF_SET(E_ZERO);
- } else
- exc.addr1.lno = 1;
- exc.addr1.cno = exc.addr2.cno = 0;
- F_SET(&exc, E_ADDR2_ALL);
- break;
- }
- /* FALLTHROUGH */
- case E_ADDR2: /* Two addresses: */
-two: switch (exc.addrcnt) {
- case 0: /* Default cursor/empty file. */
- exc.addrcnt = 2;
- F_SET(&exc, E_ADDRDEF);
- if (LF_ISSET(E_ZERODEF) && sp->lno == 1) {
- if (file_lline(sp, ep, &lno))
- goto err;
- if (lno == 0) {
- exc.addr1.lno = exc.addr2.lno = 0;
- LF_SET(E_ZERO);
- } else
- exc.addr1.lno = exc.addr2.lno = sp->lno;
- } else
- exc.addr1.lno = exc.addr2.lno = sp->lno;
- exc.addr1.cno = exc.addr2.cno = sp->cno;
- break;
- case 1: /* Default to first address. */
- exc.addrcnt = 2;
- exc.addr2 = exc.addr1;
- break;
- case 2:
- break;
- }
- break;
- default:
- if (exc.addrcnt) /* Error. */
- goto usage;
- }
-
- flagoff = 0;
- for (p = cp->syntax; *p != '\0'; ++p) {
- /*
- * The write command is sensitive to leading whitespace, e.g.
- * "write !" is different from "write!". If not the write
- * command, skip leading whitespace.
- */
- if (cp != &cmds[C_WRITE])
- for (; cmdlen > 0; --cmdlen, ++cmd) {
- ch = *cmd;
- if (!isblank(ch))
- break;
- }
-
- /*
- * Quit when reach the end of the command, unless it's a
- * command that does its own parsing, in which case we want
- * to build a reasonable argv for it. This code guarantees
- * that there will be an argv when the function gets called,
- * so the correct test is for a length of 0, not for the
- * argc > 0.
- */
- if (cmdlen == 0 && *p != '!' && *p != 'S' && *p != 's')
- break;
-
- switch (*p) {
- case '!': /* ! */
- if (*cmd == '!') {
- ++cmd;
- --cmdlen;
- F_SET(&exc, E_FORCE);
- }
- break;
- case '1': /* +, -, #, l, p */
- /*
- * !!!
- * Historically, some flags were ignored depending
- * on where they occurred in the command line. For
- * example, in the command, ":3+++p--#", historic vi
- * acted on the '#' flag, but ignored the '-' flags.
- * It's unambiguous what the flags mean, so we just
- * handle them regardless of the stupidity of their
- * location.
- */
- for (; cmdlen; --cmdlen, ++cmd)
- switch (*cmd) {
- case '+':
- ++flagoff;
- break;
- case '-':
- --flagoff;
- break;
- case '#':
- F_SET(&exc, E_F_HASH);
- break;
- case 'l':
- F_SET(&exc, E_F_LIST);
- break;
- case 'p':
- F_SET(&exc, E_F_PRINT);
- break;
- default:
- goto end1;
- }
-end1: break;
- case '2': /* -, ., +, ^ */
- case '3': /* -, ., +, ^, = */
- for (; cmdlen; --cmdlen, ++cmd)
- switch (*cmd) {
- case '-':
- F_SET(&exc, E_F_DASH);
- break;
- case '.':
- F_SET(&exc, E_F_DOT);
- break;
- case '+':
- F_SET(&exc, E_F_PLUS);
- break;
- case '^':
- F_SET(&exc, E_F_CARAT);
- break;
- case '=':
- if (*p == '3') {
- F_SET(&exc, E_F_EQUAL);
- break;
- }
- /* FALLTHROUGH */
- default:
- goto end2;
- }
-end2: break;
- case 'b': /* buffer */
- /*
- * Digits can't be buffer names in ex commands, or the
- * command "d2" would be a delete into buffer '2', and
- * not a two-line deletion.
- */
- if (!isdigit(cmd[0])) {
- exc.buffer = *cmd;
- ++cmd;
- --cmdlen;
- F_SET(&exc, E_BUFFER);
- }
- break;
- case 'c': /* count [01+a] */
- ++p;
- if (!isdigit(*cmd) &&
- (*p != '+' || (*cmd != '+' && *cmd != '-')))
- break;
-/* 8-bit XXX */ if ((lno = strtol(cmd, &t, 10)) == 0 && *p != '0') {
- msgq(sp, M_ERR, "Count may not be zero.");
- goto err;
- }
- cmdlen -= (t - cmd);
- cmd = t;
- /*
- * Count as address offsets occur in commands taking
- * two addresses. Historic vi practice was to use
- * the count as an offset from the *second* address.
- *
- * Set a count flag; some underlying commands (see
- * join) do different things with counts than with
- * line addresses.
- */
- if (*p == 'a') {
- exc.addr1 = exc.addr2;
- exc.addr2.lno = exc.addr1.lno + lno - 1;
- } else
- exc.count = lno;
- F_SET(&exc, E_COUNT);
- break;
- case 'f': /* file */
- if (argv_exp2(sp, ep,
- &exc, cmd, cmdlen, cp == &cmds[C_BANG]))
- goto err;
- goto countchk;
- case 'l': /* line */
- if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp))
- goto err;
- /* Line specifications are always required. */
- if (!tmp) {
- msgq(sp, M_ERR,
- "%s: bad line specification", cmd);
- goto err;
- }
- exc.lineno = cur.lno;
- break;
- case 'S': /* string, file exp. */
- if (argv_exp1(sp, ep,
- &exc, cmd, cmdlen, cp == &cmds[C_BANG]))
- goto err;
- goto addr2;
- case 's': /* string */
- if (argv_exp0(sp, ep, &exc, cmd, cmdlen))
- goto err;
- goto addr2;
- case 'W': /* word string */
- /*
- * QUOTING NOTE:
- *
- * Literal next characters escape the following
- * character. Quoting characters are stripped
- * here since they are no longer useful.
- *
- * First there was the word.
- */
- for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd) {
- if ((ch = *cmd) == vlit && cmdlen > 1) {
- --cmdlen;
- *p++ = *++cmd;
- } else if (isblank(ch)) {
- ++cmd;
- --cmdlen;
- break;
- } else
- *p++ = ch;
- }
- if (argv_exp0(sp, ep, &exc, t, p - t))
- goto err;
-
- /* Delete intervening whitespace. */
- for (; cmdlen > 0; --cmdlen, ++cmd) {
- ch = *cmd;
- if (!isblank(ch))
- break;
- }
- if (cmdlen == 0)
- goto usage;
-
- /* Followed by the string. */
- for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd, ++p)
- if ((ch = *cmd) == vlit && cmdlen > 1) {
- --cmdlen;
- *p = *++cmd;
- } else
- *p = ch;
- if (argv_exp0(sp, ep, &exc, t, p - t))
- goto err;
- goto addr2;
- case 'w': /* word */
- if (argv_exp3(sp, ep, &exc, cmd, cmdlen))
- goto err;
-countchk: if (*++p != 'N') { /* N */
- /*
- * If a number is specified, must either be
- * 0 or that number, if optional, and that
- * number, if required.
- */
- num = *p - '0';
- if ((*++p != 'o' || exp->argsoff != 0) &&
- exp->argsoff != num)
- goto usage;
- }
- goto addr2;
- default:
- msgq(sp, M_ERR,
- "Internal syntax table error (%s: %c).",
- cp->name, *p);
- }
- }
-
- /* Skip trailing whitespace. */
- for (; cmdlen; --cmdlen) {
- ch = *cmd++;
- if (!isblank(ch))
- break;
- }
-
- /*
- * There shouldn't be anything left, and no more required
- * fields, i.e neither 'l' or 'r' in the syntax string.
- */
- if (cmdlen || strpbrk(p, "lr")) {
-usage: msgq(sp, M_ERR, "Usage: %s.", cp->usage);
- goto err;
- }
-
- /* Verify that the addresses are legal. */
-addr2: switch (exc.addrcnt) {
- case 2:
- if (file_lline(sp, ep, &lno))
- goto err;
- /*
- * Historic ex/vi permitted commands with counts to go past
- * EOF. So, for example, if the file only had 5 lines, the
- * ex command "1,6>" would fail, but the command ">300"
- * would succeed. Since we don't want to have to make all
- * of the underlying commands handle random line numbers,
- * fix it here.
- */
- if (exc.addr2.lno > lno)
- if (F_ISSET(&exc, E_COUNT))
- exc.addr2.lno = lno;
- else {
- if (lno == 0)
- msgq(sp, M_ERR, "The file is empty.");
- else
- msgq(sp, M_ERR,
- "Only %lu line%s in the file",
- lno, lno > 1 ? "s" : "");
- goto err;
- }
- /* FALLTHROUGH */
- case 1:
- num = exc.addr1.lno;
- /*
- * If it's a "default vi command", zero is okay. Historic
- * vi allowed this, note, it's also the hack that allows
- * "vi + nonexistent_file" to work.
- */
- if (num == 0 && (!IN_VI_MODE(sp) || uselastcmd != 1) &&
- !LF_ISSET(E_ZERO)) {
- msgq(sp, M_ERR,
- "The %s command doesn't permit an address of 0.",
- cp->name);
- goto err;
- }
- if (file_lline(sp, ep, &lno))
- goto err;
- if (num > lno) {
- if (lno == 0)
- msgq(sp, M_ERR, "The file is empty.");
- else
- msgq(sp, M_ERR, "Only %lu line%s in the file",
- lno, lno > 1 ? "s" : "");
- goto err;
- }
- break;
- }
-
- /* If doing a default command, vi just moves to the line. */
- if (IN_VI_MODE(sp) && uselastcmd) {
- switch (exc.addrcnt) {
- case 2:
- sp->lno = exc.addr2.lno ? exc.addr2.lno : 1;
- sp->cno = exc.addr2.cno;
- break;
- case 1:
- sp->lno = exc.addr1.lno ? exc.addr1.lno : 1;
- sp->cno = exc.addr1.cno;
- break;
- }
- cmd = save_cmd;
- cmdlen = save_cmdlen;
- goto loop;
- }
-
- /* Reset "last" command. */
- if (LF_ISSET(E_SETLAST))
- exp->lastcmd = cp;
-
- /* Final setup for the command. */
- exc.cmd = cp;
-
-#if defined(DEBUG) && 0
- TRACE(sp, "ex_cmd: %s", exc.cmd->name);
- if (exc.addrcnt > 0) {
- TRACE(sp, "\taddr1 %d", exc.addr1.lno);
- if (exc.addrcnt > 1)
- TRACE(sp, " addr2: %d", exc.addr2.lno);
- TRACE(sp, "\n");
- }
- if (exc.lineno)
- TRACE(sp, "\tlineno %d", exc.lineno);
- if (exc.flags)
- TRACE(sp, "\tflags %0x", exc.flags);
- if (F_ISSET(&exc, E_BUFFER))
- TRACE(sp, "\tbuffer %c", exc.buffer);
- TRACE(sp, "\n");
- if (exc.argc) {
- for (cnt = 0; cnt < exc.argc; ++cnt)
- TRACE(sp, "\targ %d: {%s}", cnt, exc.argv[cnt]);
- TRACE(sp, "\n");
- }
-#endif
- /* Clear autoprint flag. */
- F_CLR(exp, EX_AUTOPRINT);
-
- /* Increment the command count if not called from vi. */
- if (!IN_VI_MODE(sp))
- ++sp->ccnt;
-
- /*
- * If file state and not doing a global command, log the start of
- * an action.
- */
- if (ep != NULL && !F_ISSET(sp, S_GLOBAL))
- (void)log_cursor(sp, ep);
-
- /* Save the current mode. */
- saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
-
- /* Do the command. */
- if ((cp->fn)(sp, ep, &exc))
- goto err;
-
-#ifdef DEBUG
- /* Make sure no function left the temporary space locked. */
- if (F_ISSET(sp->gp, G_TMP_INUSE)) {
- F_CLR(sp->gp, G_TMP_INUSE);
- msgq(sp, M_ERR, "Error: ex: temporary buffer not released.");
- goto err;
- }
-#endif
- if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE)) {
- /*
- * Only here if the mode of the underlying file changed, e.g.
- * the user switched files or is exiting. There are two things
- * that we might have to save. First, any "+cmd" field set up
- * for an ex/edit command will have to be saved for later, also,
- * any not yet executed part of the current ex command.
- *
- * :edit +25 file.c|s/abc/ABC/|1
- *
- * for example.
- *
- * The historic vi just hung, of course; we handle by
- * pushing the keys onto the tty queue. If we're
- * switching modes to vi, since the commands are intended
- * as ex commands, add the extra characters to make it
- * work.
- *
- * For the fun of it, if you want to see if a vi clone got
- * the ex argument parsing right, try:
- *
- * echo 'foo|bar' > file1; echo 'foo/bar' > file2;
- * vi
- * :edit +1|s/|/PIPE/|w file1| e file2|1 | s/\//SLASH/|wq
- */
- if (arg1_len == NULL && save_cmdlen == 0)
- return (0);
- if (IN_VI_MODE(sp) && term_push(sp, "\n", 1, 0, 0))
- goto err;
- if (save_cmdlen != 0)
- if (term_push(sp, save_cmd, save_cmdlen, 0, 0))
- goto err;
- if (arg1 != NULL) {
- if (IN_VI_MODE(sp) && save_cmdlen != 0 &&
- term_push(sp, "|", 1, 0, 0))
- goto err;
- if (term_push(sp, arg1, arg1_len, 0, 0))
- goto err;
- }
- if (IN_VI_MODE(sp) && term_push(sp, ":", 1, 0, 0))
- goto err;
- return (0);
- }
-
- if (IN_EX_MODE(sp) && ep != NULL) {
- /*
- * The print commands have already handled the `print' flags.
- * If so, clear them. Don't return, autoprint may still have
- * stuff to print out.
- */
- if (LF_ISSET(E_F_PRCLEAR))
- F_CLR(&exc, E_F_HASH | E_F_LIST | E_F_PRINT);
-
- /*
- * If the command was successful, and there was an explicit
- * flag to display the new cursor line, or we're in ex mode,
- * autoprint is set, and a change was made, display the line.
- */
- if (flagoff) {
- if (flagoff < 0) {
- if (sp->lno < -flagoff) {
- msgq(sp, M_ERR,
- "Flag offset before line 1.");
- goto err;
- }
- } else {
- if (file_lline(sp, ep, &lno))
- goto err;
- if (sp->lno + flagoff > lno) {
- msgq(sp, M_ERR,
- "Flag offset past end-of-file.");
- goto err;
- }
- }
- sp->lno += flagoff;
- }
-
- if (O_ISSET(sp, O_AUTOPRINT) &&
- (F_ISSET(exp, EX_AUTOPRINT) || F_ISSET(cp, E_AUTOPRINT)))
- LF_INIT(E_F_PRINT);
- else
- LF_INIT(F_ISSET(&exc, E_F_HASH | E_F_LIST | E_F_PRINT));
-
- memset(&exc, 0, sizeof(EXCMDARG));
- exc.addrcnt = 2;
- exc.addr1.lno = exc.addr2.lno = sp->lno;
- exc.addr1.cno = exc.addr2.cno = sp->cno;
- switch (LF_ISSET(E_F_HASH | E_F_LIST | E_F_PRINT)) {
- case E_F_HASH:
- exc.cmd = &cmds[C_HASH];
- ex_number(sp, ep, &exc);
- break;
- case E_F_LIST:
- exc.cmd = &cmds[C_LIST];
- ex_list(sp, ep, &exc);
- break;
- case E_F_PRINT:
- exc.cmd = &cmds[C_PRINT];
- ex_pr(sp, ep, &exc);
- break;
- }
- }
-
- cmd = save_cmd;
- cmdlen = save_cmdlen;
- goto loop;
- /* NOTREACHED */
-
- /*
- * On error, we discard any keys we have left, as well as any keys
- * that were mapped. The test of save_cmdlen isn't necessarily
- * correct. If we fail early enough we don't know if the entire
- * string was a single command or not. Try and guess, it's useful
- * to know if part of the command was discarded.
- */
-err: if (save_cmdlen == 0)
- for (; cmdlen; --cmdlen)
- if ((ch = *cmd++) == vlit && cmdlen > 1) {
- --cmdlen;
- ++cmd;
- } else if (ch == '\n' || ch == '|') {
- if (cmdlen > 1)
- save_cmdlen = 1;
- break;
- }
- if (save_cmdlen != 0)
- msgq(sp, M_ERR,
- "Ex command failed: remaining command input discarded.");
- term_map_flush(sp, "Ex command failed");
- return (1);
-}
-
-/*
- * ep_range --
- * Get a line range for ex commands.
- */
-static int
-ep_range(sp, ep, excp, cmdp, cmdlenp)
- SCR *sp;
- EXF *ep;
- EXCMDARG *excp;
- char **cmdp;
- size_t *cmdlenp;
-{
- MARK cur, savecursor;
- size_t cmdlen;
- int savecursor_set, tmp;
- char *cmd;
-
- /* Percent character is all lines in the file. */
- cmd = *cmdp;
- cmdlen = *cmdlenp;
- if (*cmd == '%') {
- excp->addr1.lno = 1;
- if (file_lline(sp, ep, &excp->addr2.lno))
- return (1);
-
- /* If an empty file, then the first line is 0, not 1. */
- if (excp->addr2.lno == 0)
- excp->addr1.lno = 0;
- excp->addr1.cno = excp->addr2.cno = 0;
- excp->addrcnt = 2;
-
- ++*cmdp;
- --*cmdlenp;
- return (0);
- }
-
- /* Parse comma or semi-colon delimited line specs. */
- for (savecursor_set = 0, excp->addrcnt = 0; cmdlen > 0;)
- switch (*cmd) {
- case ';': /* Semi-colon delimiter. */
- /*
- * Comma delimiters delimit; semi-colon delimiters
- * change the current address for the 2nd address
- * to be the first address. Trailing or multiple
- * delimiters are discarded.
- */
- if (excp->addrcnt == 0)
- goto done;
- if (!savecursor_set) {
- savecursor.lno = sp->lno;
- savecursor.cno = sp->cno;
- sp->lno = excp->addr1.lno;
- sp->cno = excp->addr1.cno;
- savecursor_set = 1;
- }
- ++cmd;
- --cmdlen;
- break;
- case ',': /* Comma delimiter. */
- /* If no addresses yet, defaults to ".". */
- if (excp->addrcnt == 0) {
- excp->addr1.lno = sp->lno;
- excp->addr1.cno = sp->cno;
- excp->addrcnt = 1;
- }
- /* FALLTHROUGH */
- case ' ': /* Whitespace. */
- case '\t': /* Whitespace. */
- ++cmd;
- --cmdlen;
- break;
- default:
- if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp))
- return (1);
- if (!tmp)
- goto done;
-
- /*
- * Extra addresses are discarded, starting with
- * the first.
- */
- switch (excp->addrcnt) {
- case 0:
- excp->addr1 = cur;
- excp->addrcnt = 1;
- break;
- case 1:
- excp->addr2 = cur;
- excp->addrcnt = 2;
- break;
- case 2:
- excp->addr1 = excp->addr2;
- excp->addr2 = cur;
- break;
- }
- break;
- }
-
- /*
- * XXX
- * This is probably not right behavior for savecursor -- need
- * to figure out what the historical ex did for ";,;,;5p" or
- * similar stupidity.
- */
-done: if (savecursor_set) {
- sp->lno = savecursor.lno;
- sp->cno = savecursor.cno;
- }
- if (excp->addrcnt == 2 &&
- (excp->addr2.lno < excp->addr1.lno ||
- excp->addr2.lno == excp->addr1.lno &&
- excp->addr2.cno < excp->addr1.cno)) {
- msgq(sp, M_ERR,
- "The second address is smaller than the first.");
- return (1);
- }
- *cmdp = cmd;
- *cmdlenp = cmdlen;
- return (0);
-}
-
-/*
- * Get a single line address specifier.
- */
-static int
-ep_line(sp, ep, cur, cmdp, cmdlenp, addr_found)
- SCR *sp;
- EXF *ep;
- MARK *cur;
- char **cmdp;
- size_t *cmdlenp;
- int *addr_found;
-{
- MARK m, *mp;
- long total;
- u_int flags;
- size_t cmdlen;
- char *cmd, *endp;
-
- *addr_found = 0;
-
- cmd = *cmdp;
- cmdlen = *cmdlenp;
- switch (*cmd) {
- case '$': /* Last line in the file. */
- *addr_found = 1;
- cur->cno = 0;
- if (file_lline(sp, ep, &cur->lno))
- return (1);
- ++cmd;
- --cmdlen;
- break; /* Absolute line number. */
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- *addr_found = 1;
- /*
- * The way the vi "previous context" mark worked was that
- * "non-relative" motions set it. While vi wasn't totally
- * consistent about this, ANY numeric address was considered
- * non-relative, and set the value. Which is why we're
- * hacking marks down here.
- */
- if (IN_VI_MODE(sp)) {
- m.lno = sp->lno;
- m.cno = sp->cno;
- if (mark_set(sp, ep, ABSMARK1, &m, 1))
- return (1);
- }
- cur->cno = 0;
-/* 8-bit XXX */ cur->lno = strtol(cmd, &endp, 10);
- cmdlen -= (endp - cmd);
- cmd = endp;
- break;
- case '\'': /* Use a mark. */
- *addr_found = 1;
- if (cmdlen == 1) {
- msgq(sp, M_ERR, "No mark name supplied.");
- return (1);
- }
- if ((mp = mark_get(sp, ep, cmd[1])) == NULL)
- return (1);
- *cur = *mp;
- cmd += 2;
- cmdlen -= 2;
- break;
- case '\\': /* Search: forward/backward. */
- /*
- * !!!
- * I can't find any difference between // and \/ or
- * between ?? and \?. Mark Horton doesn't remember
- * there being any difference. C'est la vie.
- */
- if (cmdlen < 2 || cmd[1] != '/' && cmd[1] != '?') {
- msgq(sp, M_ERR, "\\ not followed by / or ?.");
- return (1);
- }
- ++cmd;
- --cmdlen;
- if (cmd[0] == '/')
- goto forward;
- if (cmd[0] == '?')
- goto backward;
- /* NOTREACHED */
- case '/': /* Search forward. */
-forward: *addr_found = 1;
- m.lno = sp->lno;
- m.cno = sp->cno;
- flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET;
- if (f_search(sp, ep, &m, &m, cmd, &endp, &flags))
- return (1);
- cur->lno = m.lno;
- cur->cno = m.cno;
- cmdlen -= (endp - cmd);
- cmd = endp;
- break;
- case '?': /* Search backward. */
-backward: *addr_found = 1;
- m.lno = sp->lno;
- m.cno = sp->cno;
- flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET;
- if (b_search(sp, ep, &m, &m, cmd, &endp, &flags))
- return (1);
- cur->lno = m.lno;
- cur->cno = m.cno;
- cmdlen -= (endp - cmd);
- cmd = endp;
- break;
- case '.': /* Current position. */
- *addr_found = 1;
- cur->cno = sp->cno;
-
- /* If an empty file, then '.' is 0, not 1. */
- if (sp->lno == 1) {
- if (file_lline(sp, ep, &cur->lno))
- return (1);
- if (cur->lno != 0)
- cur->lno = 1;
- } else
- cur->lno = sp->lno;
- ++cmd;
- --cmdlen;
- break;
- }
-
- /*
- * Evaluate any offset. Offsets are +/- any number, or any number
- * of +/- signs, or any combination thereof. If no address found
- * yet, offset is relative to ".".
- */
- for (total = 0; cmdlen > 0 && (cmd[0] == '-' || cmd[0] == '+');) {
- if (!*addr_found) {
- cur->lno = sp->lno;
- cur->cno = sp->cno;
- *addr_found = 1;
- }
-
- if (cmdlen > 1 && isdigit(cmd[1])) {
-/* 8-bit XXX */ total += strtol(cmd, &endp, 10);
- cmdlen -= (endp - cmd);
- cmd = endp;
- } else {
- total += cmd[0] == '-' ? -1 : 1;
- --cmdlen;
- ++cmd;
- }
- }
- if (total < 0 && -total > cur->lno) {
- msgq(sp, M_ERR, "Reference to a line number less than 0.");
- return (1);
- }
- cur->lno += total;
-
- *cmdp = cmd;
- *cmdlenp = cmdlen;
- return (0);
-}
-
-/*
- * ex_is_abbrev -
- * The vi text input routine needs to know if ex thinks this is
- * an [un]abbreviate command, so it can turn off abbreviations.
- * Usual ranting in the vi/v_ntext:txt_abbrev() routine.
- */
-int
-ex_is_abbrev(name, len)
- char *name;
- size_t len;
-{
- EXCMDLIST const *cp;
-
- return ((cp = ex_comm_search(name, len)) != NULL &&
- (cp == &cmds[C_ABBR] || cp == &cmds[C_UNABBREVIATE]));
-}
-
-static inline EXCMDLIST const *
-ex_comm_search(name, len)
- char *name;
- size_t len;
-{
- EXCMDLIST const *cp;
-
- for (cp = cmds; cp->name != NULL; ++cp) {
- if (cp->name[0] > name[0])
- return (NULL);
- if (cp->name[0] != name[0])
- continue;
- if (!memcmp(name, cp->name, len))
- return (cp);
- }
- return (NULL);
-}