mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +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:
parent
beccb93fd4
commit
2b8eaa6609
4 changed files with 52 additions and 12 deletions
3
NEWS
3
NEWS
|
@ -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.
|
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.
|
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:
|
2021-12-05:
|
||||||
|
|
||||||
- Fixed an issue on illumos that caused some parameters in the getconf
|
- Fixed an issue on illumos that caused some parameters in the getconf
|
||||||
|
|
|
@ -1396,5 +1396,22 @@ got=$?; exp=1
|
||||||
got=$?; exp=2
|
got=$?; exp=2
|
||||||
(( got == exp )) || err_exit "cd -eP to empty string has wrong exit status (expected $exp, got $got)"
|
(( 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))
|
exit $((Errors<125?Errors:125))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* *
|
* *
|
||||||
* This software is part of the ast package *
|
* 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 *
|
* Copyright (c) 2020-2021 Contributors to ksh 93u+m *
|
||||||
* and is licensed under the *
|
* and is licensed under the *
|
||||||
* Eclipse Public License, Version 1.0 *
|
* Eclipse Public License, Version 1.0 *
|
||||||
|
@ -15,8 +15,8 @@
|
||||||
* AT&T Research *
|
* AT&T Research *
|
||||||
* Florham Park NJ *
|
* Florham Park NJ *
|
||||||
* *
|
* *
|
||||||
* Glenn Fowler <gsf@research.att.com> *
|
* Glenn Fowler <glenn.s.fowler@gmail.com> *
|
||||||
* David Korn <dgk@research.att.com> *
|
* David Korn <dgkorn@gmail.com> *
|
||||||
* *
|
* *
|
||||||
***********************************************************************/
|
***********************************************************************/
|
||||||
#pragma prototyped
|
#pragma prototyped
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const char usage[] =
|
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 "]"
|
"[--catalog?" ERROR_CATALOG "]"
|
||||||
"[+NAME?head - output beginning portion of one or more files ]"
|
"[+NAME?head - output beginning portion of one or more files ]"
|
||||||
"[+DESCRIPTION?\bhead\b copies one or more input files to standard "
|
"[+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 keep = 10;
|
||||||
register off_t skip = 0;
|
register off_t skip = 0;
|
||||||
register int delim = '\n';
|
register int delim = '\n';
|
||||||
|
off_t moved;
|
||||||
int header = 1;
|
int header = 1;
|
||||||
char* format = (char*)header_fmt+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);
|
sfprintf(sfstdout, format, cp);
|
||||||
format = (char*)header_fmt;
|
format = (char*)header_fmt;
|
||||||
if (skip > 0)
|
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);
|
error(ERROR_system(0), "%s: read error", cp);
|
||||||
|
next:
|
||||||
if (fp != sfstdin)
|
if (fp != sfstdin)
|
||||||
sfclose(fp);
|
sfclose(fp);
|
||||||
} while (cp = *argv++);
|
} while (cp = *argv++);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* *
|
* *
|
||||||
* This software is part of the ast package *
|
* 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 *
|
* Copyright (c) 2020-2021 Contributors to ksh 93u+m *
|
||||||
* and is licensed under the *
|
* and is licensed under the *
|
||||||
* Eclipse Public License, Version 1.0 *
|
* Eclipse Public License, Version 1.0 *
|
||||||
|
@ -15,8 +15,8 @@
|
||||||
* AT&T Research *
|
* AT&T Research *
|
||||||
* Florham Park NJ *
|
* Florham Park NJ *
|
||||||
* *
|
* *
|
||||||
* Glenn Fowler <gsf@research.att.com> *
|
* Glenn Fowler <glenn.s.fowler@gmail.com> *
|
||||||
* David Korn <dgk@research.att.com> *
|
* David Korn <dgkorn@gmail.com> *
|
||||||
* *
|
* *
|
||||||
***********************************************************************/
|
***********************************************************************/
|
||||||
#pragma prototyped
|
#pragma prototyped
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const char usage[] =
|
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 "]"
|
"[--catalog?" ERROR_CATALOG "]"
|
||||||
"[+NAME?tail - output trailing portion of one or more files ]"
|
"[+NAME?tail - output trailing portion of one or more files ]"
|
||||||
"[+DESCRIPTION?\btail\b copies one or more input files to standard output "
|
"[+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 Sfoff_t last;
|
||||||
register char* s;
|
register char* s;
|
||||||
register char* t;
|
register char* t;
|
||||||
|
unsigned char incomplete;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
last = sfsize(fp);
|
last = sfsize(fp);
|
||||||
|
@ -175,6 +176,7 @@ tailpos(register Sfio_t* fp, register Sfoff_t number, int delim)
|
||||||
return first;
|
return first;
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
incomplete = 1;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
if ((offset = last - SF_BUFSIZE) < first)
|
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)))
|
if (!(s = sfreserve(fp, n, SF_LOCKR)))
|
||||||
return -1;
|
return -1;
|
||||||
t = s + n;
|
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)
|
while (t > s)
|
||||||
if (*--t == delim && number-- <= 0)
|
if (*--t == delim && number-- <= 0)
|
||||||
{
|
{
|
||||||
|
@ -415,6 +426,7 @@ b_tail(int argc, char** argv, Shbltin_t* context)
|
||||||
char* t;
|
char* t;
|
||||||
char* r;
|
char* r;
|
||||||
char* file;
|
char* file;
|
||||||
|
Sfoff_t moved;
|
||||||
Sfoff_t offset;
|
Sfoff_t offset;
|
||||||
Sfoff_t number = DEFAULT;
|
Sfoff_t number = DEFAULT;
|
||||||
unsigned long timeout = 0;
|
unsigned long timeout = 0;
|
||||||
|
@ -765,8 +777,8 @@ b_tail(int argc, char** argv, Shbltin_t* context)
|
||||||
if (number < 0 || !number && (flags & POSITIVE))
|
if (number < 0 || !number && (flags & POSITIVE))
|
||||||
{
|
{
|
||||||
sfset(ip, SF_SHARE, 1);
|
sfset(ip, SF_SHARE, 1);
|
||||||
if (number < -1)
|
if (number < -1 && (moved = sfmove(ip, NiL, -(number + 1), delim)) >= 0 && delim >= 0 && moved < -(number + 1))
|
||||||
sfmove(ip, NiL, -number - 1, delim);
|
(void)sfgetr(ip, delim, SF_LASTR);
|
||||||
if (flags & REVERSE)
|
if (flags & REVERSE)
|
||||||
rev_line(ip, sfstdout, sfseek(ip, (Sfoff_t)0, SEEK_CUR));
|
rev_line(ip, sfstdout, sfseek(ip, (Sfoff_t)0, SEEK_CUR));
|
||||||
else
|
else
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue