1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-13 03:32:24 +00:00

Fix handling of files without newlines in the head and tail builtins (#365)

The head and tail builtins don't correctly handle files that lack
newlines[*]:
    $ print -n foo > /tmp/bar
    $ /opt/ast/bin/head -1 /tmp/bar  # No output
    $ print -n 'foo\nbar' > /tmp/bar
    $ /opt/ast/bin/tail -1 /tmp/bar
    foo
    bar$
This commit backports the required changes from ksh93v- to handle files
without a newline in the head and tail builtins. (Also note that the
required fix to sfmove was already backported in commit 1bd06207.)

src/lib/libcmd/{head,tail}.c:
- Backport the relevant ksh93v- code for handling files
  without newlines.

src/cmd/ksh93/tests/builtins.sh:
- Add a few regression tests for using 'head -1' and 'tail -1' on a file
  missing and ending newline.

[*]: https://www.illumos.org/issues/4149
This commit is contained in:
Johnothan King 2021-12-07 22:56:52 -08:00 committed by Martijn Dekker
parent beccb93fd4
commit 2b8eaa6609
4 changed files with 52 additions and 12 deletions

3
NEWS
View file

@ -15,6 +15,9 @@ Any uppercase BUG_* names are modernish shell bug IDs.
return value that fits in a signed integer, typically a 32-bit value.
Note that $? is truncated to 8 bits when the current (sub)shell exits.
- The head and tail builtins now correctly handle files that do not have an
ending newline. (Note that the tail builtin is not compiled in by default.)
2021-12-05:
- Fixed an issue on illumos that caused some parameters in the getconf

View file

@ -1396,5 +1396,22 @@ got=$?; exp=1
got=$?; exp=2
(( got == exp )) || err_exit "cd -eP to empty string has wrong exit status (expected $exp, got $got)"
# ======
# The head and tail builtins should work on files without newlines
if builtin head 2> /dev/null; then
print -n nonewline > "$tmp/nonewline"
exp=nonewline
got=$(head -1 "$tmp/nonewline")
[[ $got == $exp ]] || err_exit "head builtin fails to correctly handle files without an ending newline" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
fi
if builtin tail 2> /dev/null; then
print -n 'newline\nnonewline' > "$tmp/nonewline"
exp=nonewline
got=$(tail -1 "$tmp/nonewline")
[[ $got == $exp ]] || err_exit "tail builtin fails to correctly handle files without an ending newline" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
fi
# ======
exit $((Errors<125?Errors:125))

View file

@ -1,7 +1,7 @@
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1992-2012 AT&T Intellectual Property *
* Copyright (c) 1992-2013 AT&T Intellectual Property *
* Copyright (c) 2020-2021 Contributors to ksh 93u+m *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
@ -15,8 +15,8 @@
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* Glenn Fowler <glenn.s.fowler@gmail.com> *
* David Korn <dgkorn@gmail.com> *
* *
***********************************************************************/
#pragma prototyped
@ -28,7 +28,7 @@
*/
static const char usage[] =
"[-n?\n@(#)$Id: head (AT&T Research) 2012-05-31 $\n]"
"[-n?\n@(#)$Id: head (AT&T Research) 2013-09-19 $\n]"
"[--catalog?" ERROR_CATALOG "]"
"[+NAME?head - output beginning portion of one or more files ]"
"[+DESCRIPTION?\bhead\b copies one or more input files to standard "
@ -81,6 +81,7 @@ b_head(int argc, register char** argv, Shbltin_t* context)
register off_t keep = 10;
register off_t skip = 0;
register int delim = '\n';
off_t moved;
int header = 1;
char* format = (char*)header_fmt+1;
@ -145,9 +146,16 @@ b_head(int argc, register char** argv, Shbltin_t* context)
sfprintf(sfstdout, format, cp);
format = (char*)header_fmt;
if (skip > 0)
sfmove(fp, NiL, skip, delim);
if (sfmove(fp, sfstdout, keep, delim) < 0 && !ERROR_PIPE(errno) && errno != EINTR)
{
if ((moved = sfmove(fp, NiL, skip, delim)) < 0 && !ERROR_PIPE(errno) && errno != EINTR)
error(ERROR_system(0), "%s: skip error", cp);
if (delim >= 0 && moved < skip)
goto next;
}
if ((moved = sfmove(fp, sfstdout, keep, delim)) < 0 && !ERROR_PIPE(errno) && errno != EINTR ||
delim >= 0 && moved < keep && sfmove(fp, sfstdout, SF_UNBOUND, -1) < 0 && !ERROR_PIPE(errno) && errno != EINTR)
error(ERROR_system(0), "%s: read error", cp);
next:
if (fp != sfstdin)
sfclose(fp);
} while (cp = *argv++);

View file

@ -1,7 +1,7 @@
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1992-2012 AT&T Intellectual Property *
* Copyright (c) 1992-2013 AT&T Intellectual Property *
* Copyright (c) 2020-2021 Contributors to ksh 93u+m *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
@ -15,8 +15,8 @@
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* Glenn Fowler <glenn.s.fowler@gmail.com> *
* David Korn <dgkorn@gmail.com> *
* *
***********************************************************************/
#pragma prototyped
@ -29,7 +29,7 @@
*/
static const char usage[] =
"+[-?\n@(#)$Id: tail (AT&T Research) 2012-10-10 $\n]"
"+[-?\n@(#)$Id: tail (AT&T Research) 2013-09-19 $\n]"
"[--catalog?" ERROR_CATALOG "]"
"[+NAME?tail - output trailing portion of one or more files ]"
"[+DESCRIPTION?\btail\b copies one or more input files to standard output "
@ -164,6 +164,7 @@ tailpos(register Sfio_t* fp, register Sfoff_t number, int delim)
register Sfoff_t last;
register char* s;
register char* t;
unsigned char incomplete;
struct stat st;
last = sfsize(fp);
@ -175,6 +176,7 @@ tailpos(register Sfio_t* fp, register Sfoff_t number, int delim)
return first;
return offset;
}
incomplete = 1;
for (;;)
{
if ((offset = last - SF_BUFSIZE) < first)
@ -184,6 +186,15 @@ tailpos(register Sfio_t* fp, register Sfoff_t number, int delim)
if (!(s = sfreserve(fp, n, SF_LOCKR)))
return -1;
t = s + n;
if (incomplete)
{
if (t > s && *(t - 1) != delim && number-- <= 0)
{
sfread(fp, s, 0);
return offset + (t - s);
}
incomplete = 0;
}
while (t > s)
if (*--t == delim && number-- <= 0)
{
@ -415,6 +426,7 @@ b_tail(int argc, char** argv, Shbltin_t* context)
char* t;
char* r;
char* file;
Sfoff_t moved;
Sfoff_t offset;
Sfoff_t number = DEFAULT;
unsigned long timeout = 0;
@ -765,8 +777,8 @@ b_tail(int argc, char** argv, Shbltin_t* context)
if (number < 0 || !number && (flags & POSITIVE))
{
sfset(ip, SF_SHARE, 1);
if (number < -1)
sfmove(ip, NiL, -number - 1, delim);
if (number < -1 && (moved = sfmove(ip, NiL, -(number + 1), delim)) >= 0 && delim >= 0 && moved < -(number + 1))
(void)sfgetr(ip, delim, SF_LASTR);
if (flags & REVERSE)
rev_line(ip, sfstdout, sfseek(ip, (Sfoff_t)0, SEEK_CUR));
else