* Copyright (c) 1991, 1993, 1994
* 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
* 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
static char sccsid
[] = "@(#)util.c 8.50 (Berkeley) 4/17/94";
msgq(SCR
*sp
, enum msgtype mt
, const char *fmt
, ...)
msgq(sp
, mt
, fmt
, va_alist
)
* It's possible to enter msg when there's no screen to hold
* the message. If sp is NULL, ignore the special cases and
* just build the message, using __global_list.
if (!F_ISSET(sp
, S_EXSILENT
) &&
F_ISSET(sp
->gp
, G_STDIN_TTY
) && !O_ISSET(sp
, O_VERBOSE
)) {
if (!O_ISSET(sp
, O_VERBOSE
))
if (F_ISSET(sp
, S_EXSILENT
))
/* Length is the min length of the message or the buffer. */
len
= snprintf(msgbuf
, sizeof(msgbuf
),
"Error: %s, %d: %s%s%s.", sp
->if_name
, sp
->if_lno
,
fmt
== NULL
? "" : fmt
, fmt
== NULL
? "" : ": ",
nullsp1
: len
= snprintf(msgbuf
, sizeof(msgbuf
),
fmt
== NULL
? "" : fmt
, fmt
== NULL
? "" : ": ",
len
= sp
->if_name
== NULL
? 0 : snprintf(msgbuf
,
sizeof(msgbuf
), "%s, %d: ", sp
->if_name
, sp
->if_lno
);
nullsp2
: len
+= vsnprintf(msgbuf
+ len
, sizeof(msgbuf
) - len
, fmt
, ap
);
* If len >= the size, some characters were discarded.
if (len
>= sizeof(msgbuf
))
len
= sizeof(msgbuf
) - 1;
msg_app(__global_list
, sp
, mt
== M_ERR
? 1 : 0, msgbuf
, len
);
* Append a message into the queue. This can fail, but there's
* nothing we can do if it does.
msg_app(gp
, sp
, inv_video
, p
, len
)
static int reenter
; /* STATIC: Re-entrancy check. */
* It's possible to reenter msg when it allocates space.
* We're probably dead anyway, but no reason to drop core.
* Find an empty structure, or allocate a new one. Use the
* screen structure if it exists, otherwise the global one.
if ((mp
= sp
->msgq
.lh_first
) == NULL
) {
CALLOC(sp
, mp
, MSG
*, 1, sizeof(MSG
));
LIST_INSERT_HEAD(&sp
->msgq
, mp
, q
);
} else if ((mp
= gp
->msgq
.lh_first
) == NULL
) {
CALLOC(sp
, mp
, MSG
*, 1, sizeof(MSG
));
LIST_INSERT_HEAD(&gp
->msgq
, mp
, q
);
while (!F_ISSET(mp
, M_EMPTY
) && mp
->q
.le_next
!= NULL
)
if (!F_ISSET(mp
, M_EMPTY
)) {
CALLOC(sp
, nmp
, MSG
*, 1, sizeof(MSG
));
LIST_INSERT_AFTER(mp
, nmp
, q
);
/* Get enough memory for the message. */
store
: if (len
> mp
->blen
&& binc(sp
, &mp
->mbuf
, &mp
->blen
, len
))
memmove(mp
->mbuf
, p
, len
);
mp
->flags
= inv_video
? M_INV_VIDEO
: 0;
* Report on the lines that changed.
* Historic vi documentation (USD:15-8) claimed that "The editor will also
* always tell you when a change you make affects text which you cannot see."
* This isn't true -- edit a large file and do "100d|1". We don't implement
* this semantic as it would require that we track each line that changes
* during a command instead of just keeping count.
* Line counts weren't right in historic vi, either. For example, given the
* the command 2d}, from the 'b' would report that two lines were deleted,
static const char *const action
[] = {
"added", "changed", "copied", "deleted", "joined", "moved",
"put", "left shifted", "right shifted", "yanked", NULL
,
char *bp
, *p
, number
[40];
if (F_ISSET(sp
, S_EXSILENT
))
if ((rval
= O_VAL(sp
, O_REPORT
)) == 0)
GET_SPACE_RET(sp
, bp
, blen
, 512);
for (ap
= action
, cnt
= 0, first
= 1; *ap
!= NULL
; ++ap
, ++cnt
)
if (sp
->rptlines
[cnt
] != 0) {
total
+= sp
->rptlines
[cnt
];
len
= snprintf(number
, sizeof(number
),
"%s%lu line%s %s", first
? "" : "; ",
sp
->rptlines
[cnt
] > 1 ? "s" : "", *ap
);
* If nothing to report, return. Note that the number of lines
* must be > than the user's value, not >=. This is historic
* practice and means that users cannot report on single line
msgq(sp
, M_INFO
, "%s", bp
);
ex_printf(EXCOOKIE
, "%s\n", bp
);
FREE_SPACE(sp
, bp
, blen
);
/* Clear after each report. */
norpt
: memset(sp
->rptlines
, 0, sizeof(sp
->rptlines
));
* Increase the size of a buffer.
binc(sp
, argp
, bsizep
, min
)
SCR
*sp
; /* sp MAY BE NULL!!! */
/* If already larger than the minimum, just return. */
if (min
&& *bsizep
>= min
)
* If the initial pointer is null, use calloc (for non-ANSI
* C realloc implementations).
csize
= *bsizep
+ MAX(min
, 256);
MALLOC(sp
, bpp
, void *, csize
);
REALLOC(sp
, bpp
, void *, csize
);
* Theoretically, realloc is supposed to leave any already
* held memory alone if it can't get more. Don't trust it.
* Memory is guaranteed to be zero-filled, various parts of
memset((char *)bpp
+ *bsizep
, 0, csize
- *bsizep
);
* Set the column number of the first non-blank character
* including or after the starting column. On error, set
* the column to 0, it's safest.
nonblank(sp
, ep
, lno
, cnop
)
if ((p
= file_gline(sp
, ep
, lno
, &len
)) == NULL
) {
if (file_lline(sp
, ep
, &lno
))
if (len
== 0 || off
>= len
)
for (cnt
= off
, p
= &p
[off
],
len
-= off
; len
&& isblank(*p
); ++cnt
, ++p
, --len
);
*cnop
= len
? cnt
: cnt
- 1;
if ((p
= strrchr(path
, '/')) == NULL
)
* Set the window size, the row may be provided as an argument.
set_window_size(sp
, set_row
, ign_env
)
* Get the screen rows and columns. If the values are wrong, it's
* not a big deal -- as soon as the user sets them explicitly the
* environment will be set and the screen package will use the new
if (ioctl(STDERR_FILENO
, TIOCGWINSZ
, &win
) != -1) {
/* If TIOCGWINSZ failed, or had entries of 0, try termcap. */
if (row
== 0 || col
== 0) {
if (F_ISSET(&sp
->opts
[O_TERM
], OPT_SET
))
if (s
!= NULL
&& !term_tgetent(sp
, buf
, s
)) {
* If it's something completely unreasonable, stop now. The
* actual error is likely to be much less informative.
msgq(sp
, M_SYSERR
, "%lu rows is too large", (u_long
)row
);
msgq(sp
, M_ERR
, "%lu columns is too large.", (u_long
)col
);
/* If nothing else, well, it's probably a VT100. */
* POSIX 1003.2 requires the environment to override, however,
* if we're here because of a signal, we don't want to use the
if ((s
= getenv("LINES")) != NULL
)
row
= strtol(s
, NULL
, 10);
if ((s
= getenv("COLUMNS")) != NULL
)
col
= strtol(s
, NULL
, 10);
/* But, if we got an argument for the rows, use it. */
* Tell the options code that the screen size has changed.
* Since the user didn't do the set, clear the set bits.
user_set
= F_ISSET(&sp
->opts
[O_LINES
], OPT_SET
);
a
.len
= snprintf(buf
, sizeof(buf
), "lines=%u", row
);
F_CLR(&sp
->opts
[O_LINES
], OPT_SET
);
user_set
= F_ISSET(&sp
->opts
[O_COLUMNS
], OPT_SET
);
a
.len
= snprintf(buf
, sizeof(buf
), "columns=%u", col
);
F_CLR(&sp
->opts
[O_COLUMNS
], OPT_SET
);
* Set the alternate file name.
* Swap the alternate file name. It's a routine because I wanted some place
* to hang this comment. The alternate file name (normally referenced using
* the special character '#' during file expansion) is set by many
* operations. In the historic vi, the commands "ex", and "edit" obviously
* set the alternate file name because they switched the underlying file.
* Less obviously, the "read", "file", "write" and "wq" commands set it as
* well. In this implementation, some new commands have been added to the
* list. Where it gets interesting is that the alternate file name is set
* multiple times by some commands. If an edit attempt fails (for whatever
* reason, like the current file is modified but as yet unwritten), it is
* set to the file name that the user was unable to edit. If the edit
* succeeds, it is set to the last file name that was edited. Good fun.
* If the user edits a temporary file, there are time when there isn't an
* alternative file name. A name argument of NULL turns it off.
if (sp
->alt_name
!= NULL
)
FREE(sp
->alt_name
, strlen(sp
->alt_name
) + 1);
else if ((sp
->alt_name
= strdup(name
)) == NULL
)
msgq(sp
, M_SYSERR
, NULL
);
* Return the baud rate using the standard defines.
if (!F_ISSET(sp
->gp
, G_TERMIOS_SET
))
switch (v
= cfgetospeed(&sp
->gp
->original_termios
)) {
case B0
: /* Hangup -- ignore. */
* EXTA and EXTB aren't required by POSIX 1003.1, and
* are almost certainly the same as some of the above
* values, so they can't be part of the case statement.
msgq(sp
, M_ERR
, "Unknown terminal baud rate %u.\n", v
);
* Strdup for wide character strings with an associated length.
MALLOC(sp
, copy
, CHAR_T
*, len
);
memmove(copy
, str
, len
* sizeof(CHAR_T
));
* Functional version of putchar, for tputs.