mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-07-11 14:10:49 +02:00
bash-5.1 distribution sources and documentation
This commit is contained in:
@@ -29,7 +29,7 @@ _aindex=0
|
||||
#
|
||||
# Declare a function ($1) to be autoloaded from a file ($2) when it is first
|
||||
# called. This defines a `temporary' function that will `.' the file
|
||||
# containg the real function definition, then execute that new definition with
|
||||
# containing the real function definition, then execute that new definition with
|
||||
# the arguments given to this `fake' function. The autoload function defined
|
||||
# by the file and the file itself *must* be named identically.
|
||||
#
|
||||
|
||||
@@ -383,7 +383,7 @@ OPTIONS
|
||||
|
||||
'theCharCount=\$(wc -c \$theFuncFile)'
|
||||
|
||||
for each funcion and
|
||||
for each function and
|
||||
|
||||
if \$theCharCount < \$AUTOLOAD_SHIM_OVERHEAD
|
||||
|
||||
@@ -416,7 +416,7 @@ NOTES
|
||||
backticks or $(), or in a script that is not being sourced into the
|
||||
current environment. If you have not previously called the function
|
||||
in question at your command line or in a script that was sourced into
|
||||
the current envirnoment, then the various subshells are going to
|
||||
the current environment, then the various subshells are going to
|
||||
encounter the shim and replace with the real code before executing.
|
||||
|
||||
Remember, however, that environment modifications that occur in a
|
||||
@@ -490,9 +490,9 @@ NOTES
|
||||
matter how hard you try, your autoloaded functions will be
|
||||
unavailable to you, even if you run 'autoload -x -f'. The typical
|
||||
condition for this is starting up not a subshell, but a brand new
|
||||
DIFFERENT shell. And the typical example of this is git extentions.
|
||||
DIFFERENT shell. And the typical example of this is git extensions.
|
||||
|
||||
At the time of this writing, git extentions work by taking a command
|
||||
At the time of this writing, git extensions work by taking a command
|
||||
'git foo' and looking for a file 'git-foo' on the path. 'git' then
|
||||
executes 'git-foo' in a new shell - it executes your command in
|
||||
/bin/sh. That's not a subshell of your process. It will not get your
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# C-shell compatabilty package.
|
||||
# C-shell compatibilty package.
|
||||
# setenv VAR VALUE
|
||||
function setenv ()
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
isnum2()
|
||||
{
|
||||
case "$1" in
|
||||
'[-+]' | '') return 1;; # empty or bare `-' or `+'
|
||||
[-+] | '') return 1;; # empty or bare `-' or `+'
|
||||
[-+]*[!0-9]*) return 1;; # non-digit with leading sign
|
||||
[-+]*) return 0;; # OK
|
||||
*[!0-9]*) return 1;; # non-digit
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
shcat()
|
||||
{
|
||||
while read -r line
|
||||
while IFS= read -r line
|
||||
do
|
||||
echo "$line"
|
||||
done
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
shcat()
|
||||
{
|
||||
while read -r line
|
||||
while read -r
|
||||
do
|
||||
echo "$line"
|
||||
echo "$REPLY"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
# Sort the positional paramters.
|
||||
# Sort the positional parameters.
|
||||
# Make sure the positional parameters are passed as arguments to the function.
|
||||
# If -u is the first arg, remove duplicate array members.
|
||||
sort_posparams()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Simple makefile for the sample loadable builtins
|
||||
#
|
||||
# Copyright (C) 1996-2015 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1996-2019 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -76,7 +76,7 @@ INTL_BUILDDIR = ${LIBBUILD}/intl
|
||||
INTL_INC = @INTL_INC@
|
||||
LIBINTL_H = @LIBINTL_H@
|
||||
|
||||
CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(LOCAL_CFLAGS) $(CFLAGS)
|
||||
CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(LOCAL_CFLAGS) $(CPPFLAGS) $(CFLAGS)
|
||||
|
||||
#
|
||||
# These values are generated for configure by ${topdir}/support/shobj-conf.
|
||||
@@ -101,9 +101,10 @@ INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins -I${srcdir} \
|
||||
|
||||
|
||||
ALLPROG = print truefalse sleep finfo logname basename dirname fdflags \
|
||||
tty pathchk tee head mkdir rmdir printenv id whoami \
|
||||
uname sync push ln unlink realpath strftime mypid setpgid seq
|
||||
OTHERPROG = necho hello cat pushd stat rm
|
||||
tty pathchk tee head mkdir rmdir mkfifo mktemp printenv id whoami \
|
||||
uname sync push ln unlink realpath strftime mypid setpgid seq rm \
|
||||
accept csv cut
|
||||
OTHERPROG = necho hello cat pushd stat asort
|
||||
|
||||
all: $(SHOBJ_STATUS)
|
||||
|
||||
@@ -133,6 +134,9 @@ hello: hello.o
|
||||
truefalse: truefalse.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ truefalse.o $(SHOBJ_LIBS)
|
||||
|
||||
accept: accept.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ accept.o $(SHOBJ_LIBS)
|
||||
|
||||
sleep: sleep.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ sleep.o $(SHOBJ_LIBS)
|
||||
|
||||
@@ -175,6 +179,12 @@ mkdir: mkdir.o
|
||||
rmdir: rmdir.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ rmdir.o $(SHOBJ_LIBS)
|
||||
|
||||
mkfifo: mkfifo.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mkfifo.o $(SHOBJ_LIBS)
|
||||
|
||||
mktemp: mktemp.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mktemp.o $(SHOBJ_LIBS)
|
||||
|
||||
head: head.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ head.o $(SHOBJ_LIBS)
|
||||
|
||||
@@ -205,6 +215,12 @@ unlink: unlink.o
|
||||
realpath: realpath.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ realpath.o $(SHOBJ_LIBS)
|
||||
|
||||
csv: csv.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ csv.o $(SHOBJ_LIBS)
|
||||
|
||||
cut: cut.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cut.o $(SHOBJ_LIBS)
|
||||
|
||||
strftime: strftime.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ strftime.o $(SHOBJ_LIBS)
|
||||
|
||||
@@ -217,6 +233,9 @@ setpgid: setpgid.o
|
||||
stat: stat.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ stat.o $(SHOBJ_LIBS)
|
||||
|
||||
asort: asort.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ asort.o $(SHOBJ_LIBS)
|
||||
|
||||
# pushd is a special case. We use the same source that the builtin version
|
||||
# uses, with special compilation options.
|
||||
#
|
||||
@@ -272,6 +291,7 @@ uninstall: uninstall-$(SHOBJ_STATUS)
|
||||
|
||||
print.o: print.c
|
||||
truefalse.o: truefalse.c
|
||||
accept.o: accept.c
|
||||
sleep.o: sleep.c
|
||||
finfo.o: finfo.c
|
||||
logname.o: logname.c
|
||||
@@ -285,6 +305,8 @@ rmdir.o: rmdir.c
|
||||
necho.o: necho.c
|
||||
hello.o: hello.c
|
||||
cat.o: cat.c
|
||||
csv.o: csv.c
|
||||
cut.o: cut.c
|
||||
printenv.o: printenv.c
|
||||
id.o: id.c
|
||||
whoami.o: whoami.c
|
||||
@@ -292,9 +314,11 @@ uname.o: uname.c
|
||||
sync.o: sync.c
|
||||
push.o: push.c
|
||||
mkdir.o: mkdir.c
|
||||
mktemp.o: mktemp.c
|
||||
realpath.o: realpath.c
|
||||
strftime.o: strftime.c
|
||||
setpgid.o: setpgid.c
|
||||
stat.o: stat.c
|
||||
fdflags.o: fdflags.c
|
||||
seq.o: seq.c
|
||||
asort.o: asort.c
|
||||
|
||||
@@ -41,7 +41,7 @@ without having to search for the right CFLAGS and LDFLAGS.
|
||||
basename.c Return non-directory portion of pathname.
|
||||
cat.c cat(1) replacement with no options - the way cat was intended.
|
||||
dirname.c Return directory portion of pathname.
|
||||
fdflags.c Change the flag associated with one of bash's open file desriptors.
|
||||
fdflags.c Change the flag associated with one of bash's open file descriptors.
|
||||
finfo.c Print file info.
|
||||
head.c Copy first part of files.
|
||||
hello.c Obligatory "Hello World" / sample loadable.
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
/* accept - listen for and accept a remote network connection on a given port */
|
||||
|
||||
/*
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash.
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include "bashtypes.h"
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include "typemax.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "loadables.h"
|
||||
|
||||
static int accept_bind_variable (char *, int);
|
||||
|
||||
int
|
||||
accept_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
WORD_LIST *l;
|
||||
SHELL_VAR *v;
|
||||
intmax_t iport;
|
||||
int opt;
|
||||
char *tmoutarg, *fdvar, *rhostvar, *rhost;
|
||||
unsigned short uport;
|
||||
int servsock, clisock;
|
||||
struct sockaddr_in server, client;
|
||||
socklen_t clientlen;
|
||||
struct timeval timeval;
|
||||
struct linger linger = { 0, 0 };
|
||||
|
||||
rhostvar = tmoutarg = fdvar = rhost = (char *)NULL;
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "r:t:v:")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'r':
|
||||
rhostvar = list_optarg;
|
||||
break;
|
||||
case 't':
|
||||
tmoutarg = list_optarg;
|
||||
break;
|
||||
case 'v':
|
||||
fdvar = list_optarg;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
list = loptend;
|
||||
|
||||
/* Validate input and variables */
|
||||
if (tmoutarg)
|
||||
{
|
||||
long ival, uval;
|
||||
opt = uconvert (tmoutarg, &ival, &uval, (char **)0);
|
||||
if (opt == 0 || ival < 0 || uval < 0)
|
||||
{
|
||||
builtin_error ("%s: invalid timeout specification", tmoutarg);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
timeval.tv_sec = ival;
|
||||
timeval.tv_usec = uval;
|
||||
/* XXX - should we warn if ival == uval == 0 ? */
|
||||
}
|
||||
|
||||
if (list == 0)
|
||||
{
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
if (legal_number (list->word->word, &iport) == 0 || iport < 0 || iport > TYPE_MAXIMUM (unsigned short))
|
||||
{
|
||||
builtin_error ("%s: invalid port number", list->word->word);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
uport = (unsigned short)iport;
|
||||
|
||||
if (fdvar == 0)
|
||||
fdvar = "ACCEPT_FD";
|
||||
|
||||
unbind_variable (fdvar);
|
||||
if (rhostvar)
|
||||
unbind_variable (rhostvar);
|
||||
|
||||
if ((servsock = socket (AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
|
||||
{
|
||||
builtin_error ("cannot create socket: %s", strerror (errno));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
memset ((char *)&server, 0, sizeof (server));
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = htons(uport);
|
||||
server.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
if (bind (servsock, (struct sockaddr *)&server, sizeof (server)) < 0)
|
||||
{
|
||||
builtin_error ("socket bind failure: %s", strerror (errno));
|
||||
close (servsock);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
opt = 1;
|
||||
setsockopt (servsock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof (opt));
|
||||
setsockopt (servsock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof (linger));
|
||||
|
||||
if (listen (servsock, 1) < 0)
|
||||
{
|
||||
builtin_error ("listen failure: %s", strerror (errno));
|
||||
close (servsock);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
if (tmoutarg)
|
||||
{
|
||||
fd_set iofds;
|
||||
|
||||
FD_ZERO(&iofds);
|
||||
FD_SET(servsock, &iofds);
|
||||
|
||||
opt = select (servsock+1, &iofds, 0, 0, &timeval);
|
||||
if (opt < 0)
|
||||
builtin_error ("select failure: %s", strerror (errno));
|
||||
if (opt <= 0)
|
||||
{
|
||||
close (servsock);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
clientlen = sizeof (client);
|
||||
if ((clisock = accept (servsock, (struct sockaddr *)&client, &clientlen)) < 0)
|
||||
{
|
||||
builtin_error ("client accept failure: %s", strerror (errno));
|
||||
close (servsock);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
close (servsock);
|
||||
|
||||
accept_bind_variable (fdvar, clisock);
|
||||
if (rhostvar)
|
||||
{
|
||||
rhost = inet_ntoa (client.sin_addr);
|
||||
v = builtin_bind_variable (rhostvar, rhost, 0);
|
||||
if (v == 0 || readonly_p (v) || noassign_p (v))
|
||||
builtin_error ("%s: cannot set variable", rhostvar);
|
||||
}
|
||||
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
static int
|
||||
accept_bind_variable (varname, intval)
|
||||
char *varname;
|
||||
int intval;
|
||||
{
|
||||
SHELL_VAR *v;
|
||||
char ibuf[INT_STRLEN_BOUND (int) + 1], *p;
|
||||
|
||||
p = fmtulong (intval, 10, ibuf, sizeof (ibuf), 0);
|
||||
v = builtin_bind_variable (varname, p, 0);
|
||||
if (v == 0 || readonly_p (v) || noassign_p (v))
|
||||
builtin_error ("%s: cannot set variable", varname);
|
||||
return (v != 0);
|
||||
}
|
||||
|
||||
char *accept_doc[] = {
|
||||
"Accept a network connection on a specified port.",
|
||||
""
|
||||
"This builtin allows a bash script to act as a TCP/IP server.",
|
||||
"",
|
||||
"Options, if supplied, have the following meanings:",
|
||||
" -t timeout wait TIMEOUT seconds for a connection. TIMEOUT may",
|
||||
" be a decimal number including a fractional portion",
|
||||
" -v varname store the numeric file descriptor of the connected",
|
||||
" socket into VARNAME. The default VARNAME is ACCEPT_FD",
|
||||
" -r rhost store the IP address of the remote host into the shell",
|
||||
" variable RHOST, in dotted-decimal notation",
|
||||
"",
|
||||
"If successful, the shell variable ACCEPT_FD, or the variable named by the",
|
||||
"-v option, will be set to the fd of the connected socket, suitable for",
|
||||
"use as 'read -u$ACCEPT_FD'. RHOST, if supplied, will hold the IP address",
|
||||
"of the remote client. The return status is 0.",
|
||||
"",
|
||||
"On failure, the return status is 1 and ACCEPT_FD (or VARNAME) and RHOST,",
|
||||
"if supplied, will be unset.",
|
||||
"",
|
||||
"The server socket fd will be closed before accept returns.",
|
||||
(char *) NULL
|
||||
};
|
||||
|
||||
struct builtin accept_struct = {
|
||||
"accept", /* builtin name */
|
||||
accept_builtin, /* function implementing the builtin */
|
||||
BUILTIN_ENABLED, /* initial flags for builtin */
|
||||
accept_doc, /* array of long documentation strings. */
|
||||
"accept [-t timeout] [-v varname] [-r addrvar ] port", /* usage synopsis; becomes short_doc */
|
||||
0 /* reserved for internal use */
|
||||
};
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "bashtypes.h"
|
||||
#include "shell.h"
|
||||
#include "builtins.h"
|
||||
#include "common.h"
|
||||
#include "xmalloc.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
typedef struct sort_element {
|
||||
ARRAY_ELEMENT *v; // used when sorting array in-place
|
||||
char *key; // used when sorting assoc array
|
||||
char *value; // points to value of array element or assoc entry
|
||||
double num; // used for numeric sort
|
||||
} sort_element;
|
||||
|
||||
static int reverse_flag;
|
||||
static int numeric_flag;
|
||||
|
||||
static int
|
||||
compare(const void *p1, const void *p2) {
|
||||
const sort_element e1 = *(sort_element *) p1;
|
||||
const sort_element e2 = *(sort_element *) p2;
|
||||
|
||||
if (numeric_flag) {
|
||||
if (reverse_flag)
|
||||
return (e2.num > e1.num) ? 1 : (e2.num < e1.num) ? -1 : 0;
|
||||
else
|
||||
return (e1.num > e2.num) ? 1 : (e1.num < e2.num) ? -1 : 0;
|
||||
}
|
||||
else {
|
||||
if (reverse_flag)
|
||||
return strcoll(e2.value, e1.value);
|
||||
else
|
||||
return strcoll(e1.value, e2.value);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sort_index(SHELL_VAR *dest, SHELL_VAR *source) {
|
||||
HASH_TABLE *hash;
|
||||
BUCKET_CONTENTS *bucket;
|
||||
sort_element *sa;
|
||||
ARRAY *array, *dest_array;
|
||||
ARRAY_ELEMENT *ae;
|
||||
size_t i, j, n;
|
||||
char ibuf[INT_STRLEN_BOUND (intmax_t) + 1]; // used by fmtulong
|
||||
char *key;
|
||||
|
||||
dest_array = array_cell(dest);
|
||||
|
||||
if (assoc_p(source)) {
|
||||
hash = assoc_cell(source);
|
||||
n = hash->nentries;
|
||||
sa = xmalloc(n * sizeof(sort_element));
|
||||
i = 0;
|
||||
for ( j = 0; j < hash->nbuckets; ++j ) {
|
||||
bucket = hash->bucket_array[j];
|
||||
while ( bucket ) {
|
||||
sa[i].v = NULL;
|
||||
sa[i].key = bucket->key;
|
||||
if ( numeric_flag )
|
||||
sa[i].num = strtod(bucket->data, NULL);
|
||||
else
|
||||
sa[i].value = bucket->data;
|
||||
i++;
|
||||
bucket = bucket->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
array = array_cell(source);
|
||||
n = array_num_elements(array);
|
||||
sa = xmalloc(n * sizeof(sort_element));
|
||||
i = 0;
|
||||
|
||||
for (ae = element_forw(array->head); ae != array->head; ae = element_forw(ae)) {
|
||||
sa[i].v = ae;
|
||||
if (numeric_flag)
|
||||
sa[i].num = strtod(element_value(ae), NULL);
|
||||
else
|
||||
sa[i].value = element_value(ae);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check
|
||||
if ( i != n ) {
|
||||
builtin_error("%s: corrupt array", source->name);
|
||||
return EXECUTION_FAILURE;
|
||||
}
|
||||
|
||||
qsort(sa, n, sizeof(sort_element), compare);
|
||||
|
||||
array_flush(dest_array);
|
||||
|
||||
for ( i = 0; i < n; ++i ) {
|
||||
if ( assoc_p(source) )
|
||||
key = sa[i].key;
|
||||
else
|
||||
key = fmtulong((long unsigned)sa[i].v->ind, 10, ibuf, sizeof(ibuf), 0);
|
||||
|
||||
array_insert(dest_array, i, key);
|
||||
}
|
||||
|
||||
return EXECUTION_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
sort_inplace(SHELL_VAR *var) {
|
||||
size_t i, n;
|
||||
ARRAY *a;
|
||||
ARRAY_ELEMENT *ae;
|
||||
sort_element *sa = 0;
|
||||
|
||||
a = array_cell(var);
|
||||
n = array_num_elements(a);
|
||||
|
||||
if ( n == 0 )
|
||||
return EXECUTION_SUCCESS;
|
||||
|
||||
sa = xmalloc(n * sizeof(sort_element));
|
||||
|
||||
i = 0;
|
||||
for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
|
||||
sa[i].v = ae;
|
||||
if (numeric_flag)
|
||||
sa[i].num = strtod(element_value(ae), NULL);
|
||||
else
|
||||
sa[i].value = element_value(ae);
|
||||
i++;
|
||||
}
|
||||
|
||||
// sanity check
|
||||
if ( i != n ) {
|
||||
builtin_error("%s: corrupt array", var->name);
|
||||
return EXECUTION_FAILURE;
|
||||
}
|
||||
|
||||
qsort(sa, n, sizeof(sort_element), compare);
|
||||
|
||||
// for in-place sort, simply "rewire" the array elements
|
||||
sa[0].v->prev = sa[n-1].v->next = a->head;
|
||||
a->head->next = sa[0].v;
|
||||
a->head->prev = sa[n-1].v;
|
||||
a->max_index = n - 1;
|
||||
for (i = 0; i < n; i++) {
|
||||
sa[i].v->ind = i;
|
||||
if (i > 0)
|
||||
sa[i].v->prev = sa[i-1].v;
|
||||
if (i < n - 1)
|
||||
sa[i].v->next = sa[i+1].v;
|
||||
}
|
||||
xfree(sa);
|
||||
return EXECUTION_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
asort_builtin(WORD_LIST *list) {
|
||||
SHELL_VAR *var, *var2;
|
||||
char *word;
|
||||
int opt, ret;
|
||||
int index_flag = 0;
|
||||
|
||||
numeric_flag = 0;
|
||||
reverse_flag = 0;
|
||||
|
||||
reset_internal_getopt();
|
||||
while ((opt = internal_getopt(list, "inr")) != -1) {
|
||||
switch (opt) {
|
||||
case 'i': index_flag = 1; break;
|
||||
case 'n': numeric_flag = 1; break;
|
||||
case 'r': reverse_flag = 1; break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
if (list == 0) {
|
||||
builtin_usage();
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
if (legal_identifier (list->word->word) == 0) {
|
||||
sh_invalidid (list->word->word);
|
||||
return EXECUTION_FAILURE;
|
||||
}
|
||||
|
||||
if ( index_flag ) {
|
||||
if ( list->next == 0 || list->next->next ) {
|
||||
builtin_usage();
|
||||
return EX_USAGE;
|
||||
}
|
||||
if (legal_identifier (list->next->word->word) == 0) {
|
||||
sh_invalidid (list->next->word->word);
|
||||
return EXECUTION_FAILURE;
|
||||
}
|
||||
var = find_or_make_array_variable(list->word->word, 1);
|
||||
if (var == 0)
|
||||
return EXECUTION_FAILURE;
|
||||
var2 = find_variable(list->next->word->word);
|
||||
if ( !var2 || ( !array_p(var2) && !assoc_p(var2) ) ) {
|
||||
builtin_error("%s: Not an array", list->next->word->word);
|
||||
return EXECUTION_FAILURE;
|
||||
}
|
||||
return sort_index(var, var2);
|
||||
}
|
||||
|
||||
while (list) {
|
||||
word = list->word->word;
|
||||
var = find_variable(word);
|
||||
list = list->next;
|
||||
|
||||
if (var == 0 || array_p(var) == 0) {
|
||||
builtin_error("%s: Not an array", word);
|
||||
continue;
|
||||
}
|
||||
if (readonly_p(var) || noassign_p(var)) {
|
||||
if (readonly_p(var))
|
||||
err_readonly(word);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( (ret = sort_inplace(var)) != EXECUTION_SUCCESS )
|
||||
return ret;
|
||||
}
|
||||
return EXECUTION_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
char *asort_doc[] = {
|
||||
"Sort arrays in-place.",
|
||||
"",
|
||||
"Options:",
|
||||
" -n compare according to string numerical value",
|
||||
" -r reverse the result of comparisons",
|
||||
" -i sort using indices/keys",
|
||||
"",
|
||||
"If -i is supplied, SOURCE is not sorted in-place, but the indices (or keys",
|
||||
"if associative) of SOURCE, after sorting it by its values, are placed as",
|
||||
"values in the indexed array DEST",
|
||||
"",
|
||||
"Associative arrays may not be sorted in-place.",
|
||||
"",
|
||||
"Exit status:",
|
||||
"Return value is zero unless an error happened (like invalid variable name",
|
||||
"or readonly array).",
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
struct builtin asort_struct = {
|
||||
"asort",
|
||||
asort_builtin,
|
||||
BUILTIN_ENABLED,
|
||||
asort_doc,
|
||||
"asort [-nr] array ... or asort [-nr] -i dest source",
|
||||
0
|
||||
};
|
||||
@@ -3,7 +3,7 @@
|
||||
/* See Makefile for compilation details. */
|
||||
|
||||
/*
|
||||
Copyright (C) 1999-2009 Free Software Foundation, Inc.
|
||||
Copyright (C) 1999-2020 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash.
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
@@ -39,15 +39,14 @@ basename_builtin (list)
|
||||
int slen, sufflen, off;
|
||||
char *string, *suffix, *fn;
|
||||
|
||||
if (no_options (list))
|
||||
return (EX_USAGE);
|
||||
list = loptend;
|
||||
if (list == 0)
|
||||
{
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
if (no_options (list))
|
||||
return (EX_USAGE);
|
||||
list = loptend;
|
||||
}
|
||||
|
||||
string = list->word->word;
|
||||
suffix = (char *)NULL;
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
/* csv - process a line of csv data and populate an indexed array with the
|
||||
fields */
|
||||
|
||||
/*
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* See Makefile for compilation details. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include "bashansi.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include "loadables.h"
|
||||
|
||||
#define CSV_ARRAY_DEFAULT "CSV"
|
||||
|
||||
#define NQUOTE 0
|
||||
#define DQUOTE 1
|
||||
|
||||
/* Split LINE into comma-separated fields, storing each field into a separate
|
||||
element of array variable CSV, starting at index 0. The format of LINE is
|
||||
as described in RFC 4180. */
|
||||
static int
|
||||
csvsplit (csv, line)
|
||||
SHELL_VAR *csv;
|
||||
char *line;
|
||||
{
|
||||
arrayind_t ind;
|
||||
char *field, *prev, *buf, *xbuf;
|
||||
int delim, qstate;
|
||||
int b, rval;
|
||||
|
||||
xbuf = 0;
|
||||
ind = 0;
|
||||
field = prev = line;
|
||||
|
||||
do
|
||||
{
|
||||
if (*prev == '"')
|
||||
{
|
||||
if (xbuf == 0)
|
||||
xbuf = xmalloc (strlen (prev) + 1);
|
||||
buf = xbuf;
|
||||
b = 0;
|
||||
qstate = DQUOTE;
|
||||
for (field = ++prev; *field; field++)
|
||||
{
|
||||
if (qstate == DQUOTE && *field == '"' && field[1] == '"')
|
||||
buf[b++] = *field++; /* skip double quote */
|
||||
else if (qstate == DQUOTE && *field == '"')
|
||||
qstate = NQUOTE;
|
||||
else if (qstate == NQUOTE && *field == ',')
|
||||
break;
|
||||
else
|
||||
/* This copies any text between a closing double quote and the
|
||||
delimiter. If you want to change that, make sure to do the
|
||||
copy only if qstate == DQUOTE. */
|
||||
buf[b++] = *field;
|
||||
}
|
||||
buf[b] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
buf = prev;
|
||||
field = prev + strcspn (prev, ",");
|
||||
}
|
||||
|
||||
delim = *field;
|
||||
*field = '\0';
|
||||
|
||||
bind_array_element (csv, ind, buf, 0);
|
||||
ind++;
|
||||
|
||||
*field = delim;
|
||||
|
||||
if (delim == ',')
|
||||
prev = field + 1;
|
||||
}
|
||||
while (delim == ',');
|
||||
|
||||
if (xbuf)
|
||||
free (xbuf);
|
||||
|
||||
return (rval = ind); /* number of fields */
|
||||
}
|
||||
|
||||
int
|
||||
csv_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int opt, rval;
|
||||
char *array_name, *csvstring;
|
||||
SHELL_VAR *v;
|
||||
|
||||
array_name = 0;
|
||||
rval = EXECUTION_SUCCESS;
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "a:")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'a':
|
||||
array_name = list_optarg;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
if (array_name == 0)
|
||||
array_name = CSV_ARRAY_DEFAULT;
|
||||
|
||||
if (legal_identifier (array_name) == 0)
|
||||
{
|
||||
sh_invalidid (array_name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
if (list == 0)
|
||||
{
|
||||
builtin_error ("csv string argument required");
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
v = find_or_make_array_variable (array_name, 1);
|
||||
if (v == 0 || readonly_p (v) || noassign_p (v))
|
||||
{
|
||||
if (v && readonly_p (v))
|
||||
err_readonly (array_name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else if (array_p (v) == 0)
|
||||
{
|
||||
builtin_error ("%s: not an indexed array", array_name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
if (invisible_p (v))
|
||||
VUNSETATTR (v, att_invisible);
|
||||
array_flush (array_cell (v));
|
||||
|
||||
csvstring = list->word->word;
|
||||
|
||||
if (csvstring == 0 || *csvstring == 0)
|
||||
return (EXECUTION_SUCCESS);
|
||||
|
||||
opt = csvsplit (v, csvstring);
|
||||
/* Maybe do something with OPT here, it's the number of fields */
|
||||
|
||||
return (rval);
|
||||
}
|
||||
|
||||
/* Called when builtin is enabled and loaded from the shared object. If this
|
||||
function returns 0, the load fails. */
|
||||
int
|
||||
csv_builtin_load (name)
|
||||
char *name;
|
||||
{
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Called when builtin is disabled. */
|
||||
void
|
||||
csv_builtin_unload (name)
|
||||
char *name;
|
||||
{
|
||||
}
|
||||
|
||||
char *csv_doc[] = {
|
||||
"Read comma-separated fields from a string.",
|
||||
"",
|
||||
"Parse STRING, a line of comma-separated values, into individual fields,",
|
||||
"and store them into the indexed array ARRAYNAME starting at index 0.",
|
||||
"If ARRAYNAME is not supplied, \"CSV\" is the default array name.",
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
struct builtin csv_struct = {
|
||||
"csv", /* builtin name */
|
||||
csv_builtin, /* function implementing the builtin */
|
||||
BUILTIN_ENABLED, /* initial flags for builtin */
|
||||
csv_doc, /* array of long documentation strings. */
|
||||
"csv [-a ARRAY] string", /* usage synopsis; becomes short_doc */
|
||||
0 /* reserved for internal use */
|
||||
};
|
||||
@@ -0,0 +1,625 @@
|
||||
/* cut,lcut - extract specified fields from a line and assign them to an array
|
||||
or print them to the standard output */
|
||||
|
||||
/*
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* See Makefile for compilation details. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include "bashansi.h"
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "loadables.h"
|
||||
#include "shmbutil.h"
|
||||
|
||||
#define CUT_ARRAY_DEFAULT "CUTFIELDS"
|
||||
|
||||
#define NOPOS -2 /* sentinel for unset startpos/endpos */
|
||||
|
||||
#define BOL 0
|
||||
#define EOL INT_MAX
|
||||
#define NORANGE -1 /* just a position, no range */
|
||||
|
||||
#define BFLAG (1 << 0)
|
||||
#define CFLAG (1 << 1)
|
||||
#define DFLAG (1 << 2)
|
||||
#define FFLAG (1 << 3)
|
||||
#define SFLAG (1 << 4)
|
||||
|
||||
struct cutpos
|
||||
{
|
||||
int startpos, endpos; /* zero-based, correction done in getlist() */
|
||||
};
|
||||
|
||||
struct cutop
|
||||
{
|
||||
int flags;
|
||||
int delim;
|
||||
int npos;
|
||||
struct cutpos *poslist;
|
||||
};
|
||||
|
||||
static int
|
||||
poscmp (a, b)
|
||||
void *a, *b;
|
||||
{
|
||||
struct cutpos *p1, *p2;
|
||||
|
||||
p1 = (struct cutpos *)a;
|
||||
p2 = (struct cutpos *)b;
|
||||
return (p1->startpos - p2->startpos);
|
||||
}
|
||||
|
||||
static int
|
||||
getlist (arg, opp)
|
||||
char *arg;
|
||||
struct cutpos **opp;
|
||||
{
|
||||
char *ntok, *ltok, *larg;
|
||||
int s, e;
|
||||
intmax_t num;
|
||||
struct cutpos *poslist;
|
||||
int npos, nsize;
|
||||
|
||||
poslist = 0;
|
||||
nsize = npos = 0;
|
||||
s = e = 0;
|
||||
larg = arg;
|
||||
while (ltok = strsep (&larg, ","))
|
||||
{
|
||||
if (*ltok == 0)
|
||||
continue;
|
||||
|
||||
ntok = strsep (<ok, "-");
|
||||
if (*ntok == 0)
|
||||
s = BOL;
|
||||
else
|
||||
{
|
||||
if (legal_number (ntok, &num) == 0 || (int)num != num || num <= 0)
|
||||
{
|
||||
builtin_error ("%s: invalid list value", ntok);
|
||||
*opp = poslist;
|
||||
return -1;
|
||||
}
|
||||
s = num;
|
||||
s--; /* fields are 1-based */
|
||||
}
|
||||
if (ltok == 0)
|
||||
e = NORANGE;
|
||||
else if (*ltok == 0)
|
||||
e = EOL;
|
||||
else
|
||||
{
|
||||
if (legal_number (ltok, &num) == 0 || (int)num != num || num <= 0)
|
||||
{
|
||||
builtin_error ("%s: invalid list value", ltok);
|
||||
*opp = poslist;
|
||||
return -1;
|
||||
}
|
||||
e = num;
|
||||
e--;
|
||||
if (e == s)
|
||||
e = NORANGE;
|
||||
}
|
||||
|
||||
if (npos == nsize)
|
||||
{
|
||||
nsize += 4;
|
||||
poslist = (struct cutpos *)xrealloc (poslist, nsize * sizeof (struct cutpos));
|
||||
}
|
||||
poslist[npos].startpos = s;
|
||||
poslist[npos].endpos = e;
|
||||
npos++;
|
||||
}
|
||||
if (npos == 0)
|
||||
{
|
||||
builtin_error ("missing list of positions");
|
||||
*opp = poslist;
|
||||
return -1;
|
||||
}
|
||||
|
||||
qsort (poslist, npos, sizeof(poslist[0]), poscmp);
|
||||
*opp = poslist;
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
static int
|
||||
cutbytes (v, line, ops)
|
||||
SHELL_VAR *v;
|
||||
char *line;
|
||||
struct cutop *ops;
|
||||
{
|
||||
arrayind_t ind;
|
||||
char *buf, *bmap;
|
||||
size_t llen;
|
||||
int i, b, n, s, e;
|
||||
|
||||
llen = strlen (line);
|
||||
buf = xmalloc (llen + 1);
|
||||
bmap = xmalloc (llen + 1);
|
||||
memset (bmap, 0, llen);
|
||||
|
||||
for (n = 0; n < ops->npos; n++)
|
||||
{
|
||||
s = ops->poslist[n].startpos; /* no translation needed yet */
|
||||
e = ops->poslist[n].endpos;
|
||||
if (e == NORANGE)
|
||||
e = s;
|
||||
else if (e == EOL || e >= llen)
|
||||
e = llen - 1;
|
||||
/* even if a column is specified multiple times, it will only be printed
|
||||
once */
|
||||
for (i = s; i <= e; i++)
|
||||
bmap[i] = 1;
|
||||
}
|
||||
|
||||
b = 0;
|
||||
for (i = 0; i < llen; i++)
|
||||
if (bmap[i])
|
||||
buf[b++] = line[i];
|
||||
buf[b] = 0;
|
||||
|
||||
if (v)
|
||||
{
|
||||
ind = 0;
|
||||
bind_array_element (v, ind, buf, 0);
|
||||
ind++;
|
||||
}
|
||||
else
|
||||
printf ("%s\n", buf);
|
||||
|
||||
free (buf);
|
||||
free (bmap);
|
||||
|
||||
return ind;
|
||||
}
|
||||
|
||||
static int
|
||||
cutchars (v, line, ops)
|
||||
SHELL_VAR *v;
|
||||
char *line;
|
||||
struct cutop *ops;
|
||||
{
|
||||
arrayind_t ind;
|
||||
char *buf, *bmap;
|
||||
wchar_t *wbuf, *wb2;
|
||||
size_t llen, wlen;
|
||||
int i, b, n, s, e;
|
||||
|
||||
if (MB_CUR_MAX == 1)
|
||||
return (cutbytes (v, line, ops));
|
||||
if (locale_utf8locale && utf8_mbsmbchar (line) == 0)
|
||||
return (cutbytes (v, line, ops));
|
||||
|
||||
llen = strlen (line);
|
||||
wbuf = (wchar_t *)xmalloc ((llen + 1) * sizeof (wchar_t));
|
||||
|
||||
wlen = mbstowcs (wbuf, line, llen);
|
||||
if (MB_INVALIDCH (wlen))
|
||||
{
|
||||
free (wbuf);
|
||||
return (cutbytes (v, line, ops));
|
||||
}
|
||||
|
||||
bmap = xmalloc (llen + 1);
|
||||
memset (bmap, 0, llen);
|
||||
|
||||
for (n = 0; n < ops->npos; n++)
|
||||
{
|
||||
s = ops->poslist[n].startpos; /* no translation needed yet */
|
||||
e = ops->poslist[n].endpos;
|
||||
if (e == NORANGE)
|
||||
e = s;
|
||||
else if (e == EOL || e >= wlen)
|
||||
e = wlen - 1;
|
||||
/* even if a column is specified multiple times, it will only be printed
|
||||
once */
|
||||
for (i = s; i <= e; i++)
|
||||
bmap[i] = 1;
|
||||
}
|
||||
|
||||
wb2 = (wchar_t *)xmalloc ((wlen + 1) * sizeof (wchar_t));
|
||||
b = 0;
|
||||
for (i = 0; i < wlen; i++)
|
||||
if (bmap[i])
|
||||
wb2[b++] = wbuf[i];
|
||||
wb2[b] = 0;
|
||||
|
||||
free (wbuf);
|
||||
|
||||
buf = bmap;
|
||||
n = wcstombs (buf, wb2, llen);
|
||||
|
||||
if (v)
|
||||
{
|
||||
ind = 0;
|
||||
bind_array_element (v, ind, buf, 0);
|
||||
ind++;
|
||||
}
|
||||
else
|
||||
printf ("%s\n", buf);
|
||||
|
||||
free (buf);
|
||||
free (wb2);
|
||||
|
||||
return ind;
|
||||
}
|
||||
|
||||
/* The basic strategy is to cut the line into fields using strsep, populate
|
||||
an array of fields from 0..nf, then select those fields using the same
|
||||
bitmap approach as cut{bytes,chars} and assign them to the array variable
|
||||
V or print them on stdout. This function obeys SFLAG. */
|
||||
static int
|
||||
cutfields (v, line, ops)
|
||||
SHELL_VAR *v;
|
||||
char *line;
|
||||
struct cutop *ops;
|
||||
{
|
||||
arrayind_t ind;
|
||||
char *buf, *bmap, *field, **fields, delim[2];
|
||||
size_t llen, fsize;
|
||||
int i, b, n, s, e, nf;
|
||||
|
||||
ind = 0;
|
||||
|
||||
delim[0] = ops->delim;
|
||||
delim[1] = '\0';
|
||||
|
||||
fields = 0;
|
||||
nf = 0;
|
||||
fsize = 0;
|
||||
|
||||
field = buf = line;
|
||||
do
|
||||
{
|
||||
field = strsep (&buf, delim); /* destructive */
|
||||
if (nf == fsize)
|
||||
{
|
||||
fsize += 8;
|
||||
fields = xrealloc (fields, fsize * sizeof (char *));
|
||||
}
|
||||
fields[nf] = field;
|
||||
if (field)
|
||||
nf++;
|
||||
}
|
||||
while (field);
|
||||
|
||||
if (nf == 1)
|
||||
{
|
||||
free (fields);
|
||||
if (ops->flags & SFLAG)
|
||||
return ind;
|
||||
if (v)
|
||||
{
|
||||
bind_array_element (v, ind, line, 0);
|
||||
ind++;
|
||||
}
|
||||
else
|
||||
printf ("%s\n", line);
|
||||
return ind;
|
||||
}
|
||||
|
||||
bmap = xmalloc (nf + 1);
|
||||
memset (bmap, 0, nf);
|
||||
|
||||
for (n = 0; n < ops->npos; n++)
|
||||
{
|
||||
s = ops->poslist[n].startpos; /* no translation needed yet */
|
||||
e = ops->poslist[n].endpos;
|
||||
if (e == NORANGE)
|
||||
e = s;
|
||||
else if (e == EOL || e >= nf)
|
||||
e = nf - 1;
|
||||
/* even if a column is specified multiple times, it will only be printed
|
||||
once */
|
||||
for (i = s; i <= e; i++)
|
||||
bmap[i] = 1;
|
||||
}
|
||||
|
||||
for (i = 1, b = 0; b < nf; b++)
|
||||
{
|
||||
if (bmap[b] == 0)
|
||||
continue;
|
||||
if (v)
|
||||
{
|
||||
bind_array_element (v, ind, fields[b], 0);
|
||||
ind++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i == 0)
|
||||
putchar (ops->delim);
|
||||
printf ("%s", fields[b]);
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
if (v == 0)
|
||||
putchar ('\n');
|
||||
|
||||
return nf;
|
||||
}
|
||||
|
||||
static int
|
||||
cutline (v, line, ops)
|
||||
SHELL_VAR *v;
|
||||
char *line;
|
||||
struct cutop *ops;
|
||||
{
|
||||
int rval;
|
||||
|
||||
if (ops->flags & BFLAG)
|
||||
rval = cutbytes (v, line, ops);
|
||||
else if (ops->flags & CFLAG)
|
||||
rval = cutchars (v, line, ops);
|
||||
else
|
||||
rval = cutfields (v, line, ops);
|
||||
|
||||
return (rval >= 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
static int
|
||||
cutfile (v, list, ops)
|
||||
SHELL_VAR *v;
|
||||
WORD_LIST *list;
|
||||
struct cutop *ops;
|
||||
{
|
||||
int fd, unbuffered_read;
|
||||
char *line, *b;
|
||||
size_t llen;
|
||||
WORD_LIST *l;
|
||||
ssize_t n;
|
||||
|
||||
line = 0;
|
||||
llen = 0;
|
||||
|
||||
l = list;
|
||||
do
|
||||
{
|
||||
/* for each file */
|
||||
if (l == 0 || (l->word->word[0] == '-' && l->word->word[1] == '\0'))
|
||||
fd = 0;
|
||||
else
|
||||
fd = open (l->word->word, O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
file_error (l->word->word);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
#ifndef __CYGWIN__
|
||||
unbuffered_read = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
|
||||
#else
|
||||
unbuffered_read = 1;
|
||||
#endif
|
||||
|
||||
while ((n = zgetline (fd, &line, &llen, '\n', unbuffered_read)) != -1)
|
||||
cutline (v, line, ops); /* can modify line */
|
||||
if (fd > 0)
|
||||
close (fd);
|
||||
|
||||
if (l)
|
||||
l = l->next;
|
||||
}
|
||||
while (l);
|
||||
|
||||
free (line);
|
||||
return EXECUTION_SUCCESS;
|
||||
}
|
||||
|
||||
#define OPTSET(x) ((cutflags & (x)) ? 1 : 0)
|
||||
|
||||
static int
|
||||
cut_internal (which, list)
|
||||
int which; /* not used yet */
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int opt, rval, cutflags, delim, npos;
|
||||
char *array_name, *cutstring, *list_arg;
|
||||
SHELL_VAR *v;
|
||||
struct cutop op;
|
||||
struct cutpos *poslist;
|
||||
|
||||
v = 0;
|
||||
rval = EXECUTION_SUCCESS;
|
||||
|
||||
cutflags = 0;
|
||||
array_name = 0;
|
||||
list_arg = 0;
|
||||
delim = '\t';
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "a:b:c:d:f:sn")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'a':
|
||||
array_name = list_optarg;
|
||||
break;
|
||||
case 'b':
|
||||
cutflags |= BFLAG;
|
||||
list_arg = list_optarg;
|
||||
break;
|
||||
case 'c':
|
||||
cutflags |= CFLAG;
|
||||
list_arg = list_optarg;
|
||||
break;
|
||||
case 'd':
|
||||
cutflags |= DFLAG;
|
||||
delim = list_optarg[0];
|
||||
if (delim == 0 || list_optarg[1])
|
||||
{
|
||||
builtin_error ("delimiter must be a single non-null character");
|
||||
return (EX_USAGE);
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
cutflags |= FFLAG;
|
||||
list_arg = list_optarg;
|
||||
break;
|
||||
case 'n':
|
||||
break;
|
||||
case 's':
|
||||
cutflags |= SFLAG;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
if (array_name && (legal_identifier (array_name) == 0))
|
||||
{
|
||||
sh_invalidid (array_name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
if (list == 0 && which == 0)
|
||||
{
|
||||
builtin_error ("string argument required");
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
/* options are mutually exclusive and one is required */
|
||||
if ((OPTSET (BFLAG) + OPTSET (CFLAG) + OPTSET (FFLAG)) != 1)
|
||||
{
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
if ((npos = getlist (list_arg, &poslist)) < 0)
|
||||
{
|
||||
free (poslist);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
if (array_name)
|
||||
{
|
||||
v = find_or_make_array_variable (array_name, 1);
|
||||
if (v == 0 || readonly_p (v) || noassign_p (v))
|
||||
{
|
||||
if (v && readonly_p (v))
|
||||
err_readonly (array_name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else if (array_p (v) == 0)
|
||||
{
|
||||
builtin_error ("%s: not an indexed array", array_name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
if (invisible_p (v))
|
||||
VUNSETATTR (v, att_invisible);
|
||||
array_flush (array_cell (v));
|
||||
}
|
||||
|
||||
op.flags = cutflags;
|
||||
op.delim = delim;
|
||||
op.npos = npos;
|
||||
op.poslist = poslist;
|
||||
|
||||
/* we implement cut as a builtin with a cutfile() function that opens each
|
||||
filename in LIST as a filename (or `-' for stdin) and runs cutline on
|
||||
every line in the file. */
|
||||
if (which == 0)
|
||||
{
|
||||
cutstring = list->word->word;
|
||||
if (cutstring == 0 || *cutstring == 0)
|
||||
{
|
||||
free (poslist);
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
rval = cutline (v, cutstring, &op);
|
||||
}
|
||||
else
|
||||
rval = cutfile (v, list, &op);
|
||||
|
||||
return (rval);
|
||||
}
|
||||
|
||||
int
|
||||
lcut_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
return (cut_internal (0, list));
|
||||
}
|
||||
|
||||
int
|
||||
cut_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
return (cut_internal (1, list));
|
||||
}
|
||||
|
||||
char *lcut_doc[] = {
|
||||
"Extract selected fields from a string.",
|
||||
"",
|
||||
"Select portions of LINE (as specified by LIST) and assign them to",
|
||||
"elements of the indexed array ARRAY starting at index 0, or write",
|
||||
"them to the standard output if -a is not specified.",
|
||||
"",
|
||||
"Items specified by LIST are either column positions or fields delimited",
|
||||
"by a special character, and are described more completely in cut(1).",
|
||||
"",
|
||||
"Columns correspond to bytes (-b), characters (-c), or fields (-f). The",
|
||||
"field delimiter is specified by -d (default TAB). Column numbering",
|
||||
"starts at 1.",
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
struct builtin lcut_struct = {
|
||||
"lcut", /* builtin name */
|
||||
lcut_builtin, /* function implementing the builtin */
|
||||
BUILTIN_ENABLED, /* initial flags for builtin */
|
||||
lcut_doc, /* array of long documentation strings. */
|
||||
"lcut [-a ARRAY] [-b LIST] [-c LIST] [-f LIST] [-d CHAR] [-sn] line", /* usage synopsis; becomes short_doc */
|
||||
0 /* reserved for internal use */
|
||||
};
|
||||
|
||||
char *cut_doc[] = {
|
||||
"Extract selected fields from each line of a file.",
|
||||
"",
|
||||
"Select portions of each line (as specified by LIST) from each FILE",
|
||||
"and write them to the standard output. cut reads from the standard",
|
||||
"input if no FILE arguments are specified or if a FILE argument is a",
|
||||
"single hyphen.",
|
||||
"",
|
||||
"Items specified by LIST are either column positions or fields delimited",
|
||||
"by a special character, and are described more completely in cut(1).",
|
||||
"",
|
||||
"Columns correspond to bytes (-b), characters (-c), or fields (-f). The",
|
||||
"field delimiter is specified by -d (default TAB). Column numbering",
|
||||
"starts at 1.",
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
struct builtin cut_struct = {
|
||||
"cut", /* builtin name */
|
||||
cut_builtin, /* function implementing the builtin */
|
||||
BUILTIN_ENABLED, /* initial flags for builtin */
|
||||
cut_doc, /* array of long documentation strings. */
|
||||
"cut [-a ARRAY] [-b LIST] [-c LIST] [-f LIST] [-d CHAR] [-sn] [file ...]", /* usage synopsis; becomes short_doc */
|
||||
0 /* reserved for internal use */
|
||||
};
|
||||
@@ -3,7 +3,7 @@
|
||||
/* See Makefile for compilation details. */
|
||||
|
||||
/*
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
Copyright (C) 2017,2018,2019 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash.
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
@@ -32,6 +32,10 @@
|
||||
|
||||
#include "loadables.h"
|
||||
|
||||
#ifndef FD_CLOEXEC
|
||||
# define FD_CLOEXEC 1
|
||||
#endif
|
||||
|
||||
static const struct
|
||||
{
|
||||
const char *name;
|
||||
@@ -40,37 +44,69 @@ static const struct
|
||||
{
|
||||
#ifdef O_APPEND
|
||||
{ "append", O_APPEND },
|
||||
#else
|
||||
# define O_APPEND 0
|
||||
#endif
|
||||
#ifdef O_ASYNC
|
||||
{ "async", O_ASYNC },
|
||||
#else
|
||||
# define O_ASYNC 0
|
||||
#endif
|
||||
#ifdef O_SYNC
|
||||
{ "sync", O_SYNC },
|
||||
#else
|
||||
# define O_SYNC 0
|
||||
#endif
|
||||
#ifdef O_NONBLOCK
|
||||
{ "nonblock", O_NONBLOCK },
|
||||
#else
|
||||
# define O_NONBLOCK 0
|
||||
#endif
|
||||
#ifdef O_FSYNC
|
||||
{ "fsync", O_FSYNC },
|
||||
#else
|
||||
# define O_FSYNC 0
|
||||
#endif
|
||||
#ifdef O_DSYNC
|
||||
{ "dsync", O_DSYNC },
|
||||
#else
|
||||
# define O_DSYNC 0
|
||||
#endif
|
||||
#ifdef O_RSYNC
|
||||
{ "rsync", O_RSYNC },
|
||||
#else
|
||||
# define O_RSYNC 0
|
||||
#endif
|
||||
#ifdef O_ALT_IO
|
||||
{ "altio", O_ALT_IO },
|
||||
#else
|
||||
# define O_ALT_IO 0
|
||||
#endif
|
||||
#ifdef O_DIRECT
|
||||
{ "direct", O_DIRECT },
|
||||
#else
|
||||
# define O_DIRECT 0
|
||||
#endif
|
||||
#ifdef O_NOATIME
|
||||
{ "noatime", O_NOATIME },
|
||||
#else
|
||||
# define O_NOATIME 0
|
||||
#endif
|
||||
#ifdef O_NOSIGPIPE
|
||||
{ "nosigpipe", O_NOSIGPIPE },
|
||||
#else
|
||||
# define O_NOSIGPIPE 0
|
||||
#endif
|
||||
|
||||
#ifndef O_CLOEXEC
|
||||
# define ALLFLAGS (O_APPEND|O_ASYNC|O_SYNC|O_NONBLOCK|O_FSYNC|O_DSYNC|\
|
||||
O_RSYNC|O_ALT_IO|O_DIRECT|O_NOATIME|O_NOSIGPIPE)
|
||||
|
||||
/* An unsed bit in the file status flags word we can use to pass around the
|
||||
state of close-on-exec. */
|
||||
# define O_CLOEXEC ((~ALLFLAGS) ^ ((~ALLFLAGS) & ((~ALLFLAGS) - 1)))
|
||||
#endif
|
||||
|
||||
#ifdef O_CLOEXEC
|
||||
{ "cloexec", O_CLOEXEC },
|
||||
#endif
|
||||
@@ -199,10 +235,12 @@ setone(int fd, char *v, int verbose)
|
||||
parseflags(v, &pos, &neg);
|
||||
|
||||
cloexec = -1;
|
||||
|
||||
if ((pos & O_CLOEXEC) && (f & O_CLOEXEC) == 0)
|
||||
cloexec = FD_CLOEXEC;
|
||||
if ((neg & O_CLOEXEC) && (f & O_CLOEXEC))
|
||||
cloexec = 0;
|
||||
|
||||
if (cloexec != -1 && fcntl(fd, F_SETFD, cloexec) == -1)
|
||||
builtin_error("can't set status for fd %d: %s", fd, strerror(errno));
|
||||
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef MAJOR_IN_MKDEV
|
||||
# include <sys/mkdev.h>
|
||||
#endif
|
||||
#ifdef MAJOR_IN_SYSMACROS
|
||||
# include <sys/sysmacros.h>
|
||||
#endif
|
||||
#include "posixstat.h"
|
||||
#include <stdio.h>
|
||||
#include <pwd.h>
|
||||
@@ -334,13 +340,13 @@ int flags;
|
||||
else
|
||||
printf("%ld\n", st->st_ctime);
|
||||
} else if (flags & OPT_DEV)
|
||||
printf("%d\n", st->st_dev);
|
||||
printf("%lu\n", (unsigned long)st->st_dev);
|
||||
else if (flags & OPT_INO)
|
||||
printf("%lu\n", (unsigned long)st->st_ino);
|
||||
else if (flags & OPT_FID)
|
||||
printf("%d:%lu\n", st->st_dev, (unsigned long)st->st_ino);
|
||||
printf("%lu:%lu\n", (unsigned long)st->st_dev, (unsigned long)st->st_ino);
|
||||
else if (flags & OPT_NLINK)
|
||||
printf("%d\n", st->st_nlink);
|
||||
printf("%lu\n", (unsigned long)st->st_nlink);
|
||||
else if (flags & OPT_LNKNAM) {
|
||||
#ifdef S_ISLNK
|
||||
b = xmalloc(4096);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/* See Makefile for compilation details. */
|
||||
|
||||
/*
|
||||
Copyright (C) 1999-2009 Free Software Foundation, Inc.
|
||||
Copyright (C) 1999-2020 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash.
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
@@ -42,7 +42,7 @@
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
typedef int unix_link_syscall_t __P((const char *, const char *));
|
||||
typedef int unix_link_syscall_t PARAMS((const char *, const char *));
|
||||
|
||||
#define LN_SYMLINK 0x01
|
||||
#define LN_UNLINK 0x02
|
||||
|
||||
@@ -93,7 +93,7 @@ mkdir_builtin (list)
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
else if (mode)
|
||||
else /* symbolic mode */
|
||||
{
|
||||
/* initial bits are a=rwx; the mode argument modifies them */
|
||||
omode = parse_symbolic_mode (mode, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
/* mkfifo - make FIFOs */
|
||||
|
||||
/* See Makefile for compilation details. */
|
||||
|
||||
/*
|
||||
Copyright (C) 1999-2020 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash.
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "bashtypes.h"
|
||||
#include "posixstat.h"
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include "bashansi.h"
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "builtins.h"
|
||||
#include "shell.h"
|
||||
#include "bashgetopt.h"
|
||||
#include "common.h"
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
#define ISOCTAL(c) ((c) >= '0' && (c) <= '7')
|
||||
|
||||
extern int parse_symbolic_mode ();
|
||||
|
||||
static int original_umask;
|
||||
|
||||
int
|
||||
mkfifo_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int opt, mflag, omode, rval, nmode, basemode;
|
||||
char *mode;
|
||||
WORD_LIST *l;
|
||||
|
||||
mflag = 0;
|
||||
mode = (char *)NULL;
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt(list, "m:")) != -1)
|
||||
switch (opt)
|
||||
{
|
||||
case 'm':
|
||||
mflag = 1;
|
||||
mode = list_optarg;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
if (list == 0)
|
||||
{
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
basemode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
||||
if (mode == NULL)
|
||||
omode = basemode;
|
||||
else if (ISOCTAL (*mode)) /* octal number */
|
||||
{
|
||||
omode = read_octal (mode);
|
||||
if (omode < 0)
|
||||
{
|
||||
builtin_error ("invalid file mode: %s", mode);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
else /* symbolic mode */
|
||||
{
|
||||
/* initial bits are a=rwx; the mode argument modifies them */
|
||||
omode = parse_symbolic_mode (mode, basemode);
|
||||
if (omode < 0)
|
||||
{
|
||||
builtin_error ("invalid file mode: %s", mode);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Make the new mode */
|
||||
original_umask = umask (0);
|
||||
umask (original_umask);
|
||||
|
||||
nmode = basemode & ~original_umask;
|
||||
/* Adjust new mode based on mode argument */
|
||||
nmode &= omode;
|
||||
|
||||
for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next)
|
||||
{
|
||||
if (mkfifo (l->word->word, nmode) < 0)
|
||||
{
|
||||
builtin_error ("cannot create FIFO `%s': %s", l->word->word, strerror (errno));
|
||||
rval = EXECUTION_FAILURE;
|
||||
}
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
char *mkfifo_doc[] = {
|
||||
"Create FIFOs (named pipes).",
|
||||
"",
|
||||
"Make FIFOs. Create the FIFOs named as arguments, in",
|
||||
"the order specified, using mode a=rw as modified by the current",
|
||||
"umask (see `help umask'). The -m option causes the file permission",
|
||||
"bits of the final FIFO to be MODE. The MODE argument may be",
|
||||
"an octal number or a symbolic mode like that used by chmod(1). If",
|
||||
"a symbolic mode is used, the operations are interpreted relative to",
|
||||
"an initial mode of \"a=rw\". mkfifo returns 0 if the FIFOs are",
|
||||
"umask, plus write and search permissions for the owner. mkdir",
|
||||
"created successfully, and non-zero if an error occurs.",
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
struct builtin mkfifo_struct = {
|
||||
"mkfifo",
|
||||
mkfifo_builtin,
|
||||
BUILTIN_ENABLED,
|
||||
mkfifo_doc,
|
||||
"mkfifo [-m mode] fifo_name [fifo_name ...]",
|
||||
0
|
||||
};
|
||||
@@ -0,0 +1,212 @@
|
||||
/* mktemp - create temporary file or directory */
|
||||
|
||||
/*
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash.
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "bashansi.h"
|
||||
|
||||
#include "loadables.h"
|
||||
|
||||
#define DEFAULT_PREFIX "shtmp"
|
||||
|
||||
int
|
||||
mktemp_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
WORD_LIST *l;
|
||||
int rval, opt, fd, mflags, base_mflags;
|
||||
int dflag, qflag, tflag, uflag, onetime;
|
||||
char *prefix, *varname, *filename, *template;
|
||||
SHELL_VAR *v;
|
||||
|
||||
dflag = qflag = uflag = tflag = onetime = 0;
|
||||
prefix = varname = 0;
|
||||
rval = EXECUTION_SUCCESS;
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "dqut:v:")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'd':
|
||||
dflag = 1;
|
||||
break;
|
||||
case 'q':
|
||||
qflag = 1;
|
||||
break;
|
||||
case 't':
|
||||
tflag = 1;
|
||||
prefix = list_optarg;
|
||||
break;
|
||||
case 'u':
|
||||
uflag = 1;
|
||||
break;
|
||||
case 'v':
|
||||
varname = list_optarg;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
if (varname) /* check for validity, not readonly */
|
||||
{
|
||||
if (legal_identifier (varname) == 0)
|
||||
{
|
||||
if (qflag == 0)
|
||||
sh_invalidid (varname);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
v = find_variable (varname);
|
||||
if (v && readonly_p (v))
|
||||
{
|
||||
if (qflag == 0)
|
||||
sh_readonly (varname);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
onetime = (list == 0); /* once through the loop, $TMPDIR/prefix.XXXXXX */
|
||||
|
||||
if (prefix == 0)
|
||||
prefix = DEFAULT_PREFIX;
|
||||
base_mflags = MT_USETMPDIR|MT_USERANDOM; /* USERANDOM not strictly needed */
|
||||
|
||||
while (list || onetime)
|
||||
{
|
||||
mflags = base_mflags;
|
||||
onetime = 0;
|
||||
#if defined (USE_MKTEMP) && defined (USE_MKSTEMP)
|
||||
if (list)
|
||||
{
|
||||
template = list->word->word;
|
||||
mflags |= MT_TEMPLATE;
|
||||
}
|
||||
#else
|
||||
/* This is sub-optimal. */
|
||||
if (list)
|
||||
{
|
||||
/* Treat the basename as a prefix */
|
||||
template = strrchr (list->word->word, '/');
|
||||
if (template)
|
||||
template++;
|
||||
else
|
||||
template = list->word->word;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
template = prefix;
|
||||
|
||||
if (dflag)
|
||||
{
|
||||
filename = sh_mktmpdir (template, mflags);
|
||||
if (filename == 0)
|
||||
{
|
||||
if (qflag == 0)
|
||||
builtin_error ("%s: cannot create directory", template);
|
||||
rval = EXECUTION_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (uflag)
|
||||
rmdir (filename);
|
||||
printf ("%s\n", filename);
|
||||
}
|
||||
}
|
||||
else /* filename */
|
||||
{
|
||||
fd = sh_mktmpfd (template, mflags, &filename);
|
||||
if (fd < 0)
|
||||
{
|
||||
if (qflag == 0)
|
||||
builtin_error ("%s: cannot create file", template);
|
||||
rval = EXECUTION_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
close (fd);
|
||||
if (uflag)
|
||||
unlink (filename);
|
||||
printf ("%s\n", filename);
|
||||
}
|
||||
}
|
||||
|
||||
/* Assign variable if requested */
|
||||
if (filename && varname)
|
||||
{
|
||||
v = builtin_bind_variable (varname, filename, 0);
|
||||
if (v == 0 || readonly_p (v) || noassign_p (v))
|
||||
{
|
||||
builtin_error ("%s: cannot set variable", varname);
|
||||
rval = EXECUTION_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
FREE (filename);
|
||||
|
||||
if (list)
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return (rval);
|
||||
}
|
||||
|
||||
char *mktemp_doc[] = {
|
||||
"Make unique temporary file name",
|
||||
"",
|
||||
"Take each supplied filename template and overwrite a portion of it",
|
||||
"to create a filename, which is unique and may be used by the calling",
|
||||
"script. TEMPLATE is a string ending in some number of 'X's. If",
|
||||
"TEMPLATE is not supplied, shtmp.XXXXXX is used and $TMPDIR is used as",
|
||||
"the name of the containing directory. Files are created u+rw; directories",
|
||||
"are created u+rwx.",
|
||||
"",
|
||||
"Options, if supplied, have the following meanings:",
|
||||
"",
|
||||
" -d Create a directory instead of a file",
|
||||
" -q Do not print error messages about file creation failure",
|
||||
" -t PREFIX Use PREFIX as the directory in which to create files",
|
||||
" -u Do not create anything; simply print a name",
|
||||
" -v VAR Store the generated name into shell variable VAR",
|
||||
"",
|
||||
"Any PREFIX supplied with -t is ignored if TEMPLATE is supplied.",
|
||||
"",
|
||||
"The return status is true if the file or directory was created successfully;",
|
||||
"false if an error occurs or VAR is invalid or readonly.",
|
||||
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
struct builtin mktemp_struct = {
|
||||
"mktemp", /* builtin name */
|
||||
mktemp_builtin, /* function implementing the builtin */
|
||||
BUILTIN_ENABLED, /* initial flags for builtin */
|
||||
mktemp_doc, /* array of long documentation strings. */
|
||||
"mktemp [-d] [-q] [-t prefix] [-u] [-v varname] [template] ...",
|
||||
0 /* reserved for internal use */
|
||||
};
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright (C) 1999-2009 Free Software Foundation, Inc.
|
||||
Copyright (C) 1999-2020 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash.
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
@@ -35,7 +35,7 @@
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
extern int dollar_dollar_pid;
|
||||
extern pid_t dollar_dollar_pid;
|
||||
extern int last_command_exit_value;
|
||||
|
||||
int
|
||||
@@ -92,7 +92,7 @@ push_builtin (list)
|
||||
else
|
||||
{
|
||||
stop_pipeline (0, (COMMAND *)NULL);
|
||||
xstatus = wait_for (pid);
|
||||
xstatus = wait_for (pid, 0);
|
||||
return (xstatus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ char *realpath_doc[] = {
|
||||
"Display the canonicalized version of each PATHNAME argument, resolving",
|
||||
"symbolic links. The -c option checks whether or not each resolved name",
|
||||
"exists. The -s option produces no output; the exit status determines the",
|
||||
"valididty of each PATHNAME. The -v option produces verbose output. The",
|
||||
"validity of each PATHNAME. The -v option produces verbose output. The",
|
||||
"exit status is 0 if each PATHNAME was resolved; non-zero otherwise.",
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
@@ -145,8 +145,12 @@ rm_builtin (list)
|
||||
|
||||
if (list == 0)
|
||||
{
|
||||
builtin_usage ();
|
||||
return (EXECUTION_FAILURE);
|
||||
if (force == 0)
|
||||
{
|
||||
builtin_usage ();
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
for (l = list; l; l = l->next)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* seq - print sequence of numbers to standard output.
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
Copyright (C) 2018-2020 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -49,8 +49,8 @@ typedef double floatmax_t;
|
||||
# define FLOATMAX_FMT "%g"
|
||||
# define FLOATMAX_WFMT "%0.f"
|
||||
#endif
|
||||
static floatmax_t getfloatmax __P((const char *));
|
||||
static char *genformat __P((floatmax_t, floatmax_t, floatmax_t));
|
||||
static floatmax_t getfloatmax PARAMS((const char *));
|
||||
static char *genformat PARAMS((floatmax_t, floatmax_t, floatmax_t));
|
||||
|
||||
#define MAX(a, b) (((a) < (b))? (b) : (a))
|
||||
|
||||
@@ -238,7 +238,7 @@ print_fltseq (fmt, first, last, incr)
|
||||
floatmax_t next;
|
||||
const char *s;
|
||||
|
||||
n = 0; /* interation counter */
|
||||
n = 0; /* iteration counter */
|
||||
s = "";
|
||||
for (next = first; incr >= 0 ? (next <= last) : (next >= last); next = first + n * incr)
|
||||
{
|
||||
|
||||
@@ -105,7 +105,7 @@ const char *setpgid_doc[] = {
|
||||
"invoke the setpgid(2) system call",
|
||||
"",
|
||||
"Arguments:",
|
||||
" pid : numeric process identifer, >= 0",
|
||||
" pid : numeric process identifier, >= 0",
|
||||
" pgrpid: numeric process group identifier, >=0",
|
||||
"See the setpgid(2) manual page.",
|
||||
(const char *)NULL
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright (C) 1999-2009 Free Software Foundation, Inc.
|
||||
Copyright (C) 1999-2020 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash.
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
@@ -53,6 +53,9 @@ sleep_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
long sec, usec;
|
||||
char *ep;
|
||||
int r, mul;
|
||||
time_t t;
|
||||
|
||||
if (list == 0) {
|
||||
builtin_usage();
|
||||
@@ -68,8 +71,12 @@ WORD_LIST *list;
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
if (uconvert(list->word->word, &sec, &usec)) {
|
||||
r = uconvert(list->word->word, &sec, &usec, &ep);
|
||||
/* Maybe postprocess conversion failures here based on EP */
|
||||
|
||||
if (r) {
|
||||
fsleep(sec, usec);
|
||||
QUIT;
|
||||
return(EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "builtins.h"
|
||||
#include "shell.h"
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
int
|
||||
strftime_builtin (list)
|
||||
@@ -46,15 +47,16 @@ strftime_builtin (list)
|
||||
int n;
|
||||
intmax_t i;
|
||||
|
||||
if (no_options (list))
|
||||
return (EX_USAGE);
|
||||
list = loptend;
|
||||
|
||||
if (list == 0)
|
||||
{
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
if (no_options (list))
|
||||
return (EX_USAGE);
|
||||
|
||||
format = list->word->word;
|
||||
if (format == 0 || *format == 0)
|
||||
{
|
||||
@@ -86,7 +88,7 @@ strftime_builtin (list)
|
||||
/* Now try to figure out how big the buffer should really be. strftime(3)
|
||||
will return the number of bytes placed in the buffer unless it's greater
|
||||
than MAXSIZE, in which case it returns 0. */
|
||||
for (n = 1; n < 4; n++)
|
||||
for (n = 1; n <= 8; n++)
|
||||
{
|
||||
tbuf = xrealloc (tbuf, tbsize * n);
|
||||
tsize = strftime (tbuf, tbsize * n, format, t);
|
||||
@@ -94,7 +96,8 @@ strftime_builtin (list)
|
||||
break;
|
||||
}
|
||||
|
||||
printf ("%s\n", tbuf);
|
||||
if (tsize)
|
||||
printf ("%s\n", tbuf);
|
||||
free (tbuf);
|
||||
|
||||
return (EXECUTION_SUCCESS);
|
||||
|
||||
@@ -164,7 +164,7 @@ char *tee_doc[] = {
|
||||
"Duplicate standard output.",
|
||||
"",
|
||||
"Copy standard input to standard output, making a copy in each",
|
||||
"filename argument. If the `-a' option is gived, the specified",
|
||||
"filename argument. If the `-a' option is given, the specified",
|
||||
"files are appended to, otherwise they are overwritten. If the",
|
||||
"`-i' option is supplied, tee ignores interrupts.",
|
||||
(char *)NULL
|
||||
|
||||
@@ -17,7 +17,7 @@ alias pu="pushd"
|
||||
alias po="popd"
|
||||
|
||||
#
|
||||
# Csh compatability:
|
||||
# Csh compatibility:
|
||||
#
|
||||
alias unsetenv=unset
|
||||
function setenv () {
|
||||
|
||||
Reference in New Issue
Block a user