mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-08-07 02:32:27 +02:00
add some missing files, update copyrights
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
/* kv - process a series of lines containing key-value pairs and assign them
|
||||
to an associative array. */
|
||||
|
||||
/*
|
||||
Copyright (C) 2023 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>
|
||||
|
||||
#include <sys/types.h>
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include "bashansi.h"
|
||||
#include "posixstat.h"
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "loadables.h"
|
||||
|
||||
#ifndef errno
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
#define KV_ARRAY_DEFAULT "KV"
|
||||
|
||||
/* Split LINE into a key and value, with the delimiter between the key and
|
||||
value being a member of DSTRING. A sequence of one or more delimiters
|
||||
separates the key and value. Assign to associative array KV as if
|
||||
KV[key]=value without expansion. This does not treat single or double
|
||||
quotes specially, nor does it remove whitespace at the beginning or end
|
||||
of LINE. */
|
||||
static int
|
||||
kvsplit (SHELL_VAR *v, char *line, char *dstring)
|
||||
{
|
||||
char *key, *value;
|
||||
size_t ind;
|
||||
|
||||
key = line;
|
||||
value = 0;
|
||||
|
||||
ind = (line && *line) ? strcspn (key, dstring) : 0;
|
||||
|
||||
/* blank line or line starting with delimiter */
|
||||
if (ind == 0 || *key == 0)
|
||||
return 0;
|
||||
|
||||
if (key[ind])
|
||||
{
|
||||
key[ind++] = '\0';
|
||||
value = key + ind;
|
||||
/* skip until non-delim; this allows things like key1 = value1 where delims = " =" */
|
||||
ind = strspn (value, dstring);
|
||||
value += ind;
|
||||
}
|
||||
else
|
||||
value = "";
|
||||
|
||||
return (bind_assoc_variable (v, name_cell (v), savestring (key), value, 0) != 0);
|
||||
}
|
||||
|
||||
int
|
||||
kvfile (SHELL_VAR *v, int fd, char *delims, char *rs)
|
||||
{
|
||||
ssize_t n;
|
||||
char *line;
|
||||
size_t llen;
|
||||
int unbuffered_read, nr;
|
||||
struct stat sb;
|
||||
|
||||
nr = 0;
|
||||
#ifndef __CYGWIN__
|
||||
/* We probably don't need to worry about setting this at all; we're not
|
||||
seeking back and forth yet. */
|
||||
if (*rs == '\n')
|
||||
unbuffered_read = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
|
||||
else
|
||||
unbuffered_read = (fstat (fd, &sb) != 0) || (S_ISREG (sb.st_mode) == 0);
|
||||
#else
|
||||
unbuffered_read = 1;
|
||||
#endif
|
||||
|
||||
line = 0;
|
||||
llen = 0;
|
||||
|
||||
zreset ();
|
||||
while ((n = zgetline (fd, &line, &llen, *rs, unbuffered_read)) != -1)
|
||||
{
|
||||
QUIT;
|
||||
if (line[n] == *rs)
|
||||
line[n] = '\0'; /* value doesn't include the record separator */
|
||||
nr += kvsplit (v, line, delims);
|
||||
free (line);
|
||||
line = 0;
|
||||
llen = 0;
|
||||
}
|
||||
|
||||
QUIT;
|
||||
return nr;
|
||||
}
|
||||
|
||||
int
|
||||
kv_builtin (WORD_LIST *list)
|
||||
{
|
||||
int opt, rval, free_delims;
|
||||
char *array_name, *delims, *rs;
|
||||
SHELL_VAR *v;
|
||||
|
||||
array_name = delims = rs = 0;
|
||||
rval = EXECUTION_SUCCESS;
|
||||
free_delims = 0;
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "A:s:d:")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'A':
|
||||
array_name = list_optarg;
|
||||
break;
|
||||
case 's':
|
||||
delims = list_optarg;
|
||||
break;
|
||||
case 'd':
|
||||
rs = list_optarg;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
if (list)
|
||||
{
|
||||
builtin_error ("too many arguments");
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
if (array_name == 0)
|
||||
array_name = KV_ARRAY_DEFAULT;
|
||||
|
||||
if (valid_identifier (array_name) == 0)
|
||||
{
|
||||
sh_invalidid (array_name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
if (delims == 0)
|
||||
{
|
||||
delims = getifs ();
|
||||
free_delims = 1;
|
||||
}
|
||||
if (rs == 0)
|
||||
rs = "\n";
|
||||
|
||||
v = find_or_make_array_variable (array_name, 3);
|
||||
if (v == 0 || readonly_p (v) || noassign_p (v))
|
||||
{
|
||||
if (v && readonly_p (v))
|
||||
err_readonly (array_name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else if (assoc_p (v) == 0)
|
||||
{
|
||||
builtin_error ("%s: not an associative array", array_name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
if (invisible_p (v))
|
||||
VUNSETATTR (v, att_invisible);
|
||||
assoc_flush (assoc_cell (v));
|
||||
|
||||
rval = kvfile (v, 0, delims, rs);
|
||||
|
||||
if (free_delims)
|
||||
free (delims); /* getifs returns allocated memory */
|
||||
return (rval > 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
/* Called when builtin is enabled and loaded from the shared object. If this
|
||||
function returns 0, the load fails. */
|
||||
int
|
||||
kv_builtin_load (char *name)
|
||||
{
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Called when builtin is disabled. */
|
||||
void
|
||||
kv_builtin_unload (char *name)
|
||||
{
|
||||
}
|
||||
|
||||
char *kv_doc[] = {
|
||||
"Read key-value pairs into an associative array.",
|
||||
"",
|
||||
"Read delimiter-terminated records composed of a single key-value pair",
|
||||
"from the standard input and add the key and corresponding value",
|
||||
"to the associative array ARRAYNAME. The key and value are separated",
|
||||
"by a sequence of one or more characters in SEPARATORS. Records are",
|
||||
"terminated by the first character of RS, similar to the read and",
|
||||
"mapfile builtins.",
|
||||
"",
|
||||
"If SEPARATORS is not supplied, $IFS is used to separate the keys",
|
||||
"and values. If RS is not supplied, newlines terminate records.",
|
||||
"If ARRAYNAME is not supplied, \"KV\" is the default array name.",
|
||||
"",
|
||||
"Returns success if at least one key-value pair is stored in ARRAYNAME.",
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
struct builtin kv_struct = {
|
||||
"kv", /* builtin name */
|
||||
kv_builtin, /* function implementing the builtin */
|
||||
BUILTIN_ENABLED, /* initial flags for builtin */
|
||||
kv_doc, /* array of long documentation strings. */
|
||||
"kv [-A ARRAYNAME] [-s SEPARATORS] [-d RS]", /* usage synopsis; becomes short_doc */
|
||||
0 /* reserved for internal use */
|
||||
};
|
||||
@@ -0,0 +1,241 @@
|
||||
/* strptime - take a date-time string and turn it into seconds since the epoch. */
|
||||
|
||||
/* See Makefile for compilation details. */
|
||||
|
||||
/*
|
||||
Copyright (C) 2023 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 "bashtypes.h"
|
||||
#include "posixtime.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "builtins.h"
|
||||
#include "shell.h"
|
||||
#include "bashgetopt.h"
|
||||
#include "common.h"
|
||||
|
||||
struct date_modifier
|
||||
{
|
||||
char *shorthand;
|
||||
int incr;
|
||||
};
|
||||
|
||||
static struct date_modifier date_time_modifiers[] =
|
||||
{
|
||||
{ "now", 0 },
|
||||
{ "today", 0 },
|
||||
{ "tomorrow", 24*60*60 },
|
||||
{ "yesterday", -24*60*60 },
|
||||
{ "day after tomorrow", 48*60*60 },
|
||||
{ "two days ago", -48*60*60 },
|
||||
{ "next week", 7*24*60*60 },
|
||||
{ "last week", -7*24*60*60 },
|
||||
{ "the day after tomorrow", 48*60*60 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static char * const date_time_formats[] =
|
||||
{
|
||||
"%a %b %d %T %Z %Y", /* Unix date */
|
||||
"%a %b %d %T %Y", /* Wkd Mon DD HH:MM:SS YYYY */
|
||||
"%FT%T%z", /* ISO8601 time YYYY-mm-ddTHH:MM:SSzone */
|
||||
"%FT%R%z", /* ISO8601 time YYYY-mm-ddTHH:MMzone */
|
||||
"%G-%m-%dT%T%z", /* ISO8601 time YYYY-mm-ddTHH:MM:SSzone */
|
||||
"%G-%m-%dT%R%z", /* ISO8601 time YYYY-mm-ddTHH:MMzone */
|
||||
"%G-%m-%d", /* ISO8601 time YYYY-mm-dd */
|
||||
/* Can't do 8601 time zone offset with colon or fractions of a second */
|
||||
"%a, %d %b %Y %T %Z", /* RFC822/RFC2822 time */
|
||||
"%a, %d %b %Y %T %z", /* RFC822/RFC2822 time */
|
||||
"%D %T", /* mm/dd/yy HH:MM:SS */
|
||||
"%D %R", /* mm/dd/yy HH:MM */
|
||||
"%D %r", /* mm/dd/yy HH:MM:SS a.m. */
|
||||
"%D %I:%M %p", /* mm/dd/yy HH:MM p.m. */
|
||||
"%m/%d/%Y %T", /* mm/dd/YYYY HH:MM:SS */
|
||||
"%m/%d/%Y %R", /* mm/dd/YYYY HH:MM */
|
||||
"%m/%d/%Y %r", /* mm/dd/YYYY HH:MM:SS a.m */
|
||||
"%m/%d/%Y %I:%M %p", /* mm/dd/YYYY HH:MM p.m. */
|
||||
"%m-%d-%Y %T", /* mm-dd-YYYY HH:MM:SS */
|
||||
"%m-%d-%Y %R", /* mm-dd-YYYY HH:MM */
|
||||
"%m-%d-%Y %r", /* mm-dd-YYYY HH:MM:SS a.m. */
|
||||
"%m-%d-%Y %I:%M %p", /* mm-dd-YYYY HH:MM p.m. */
|
||||
"%Y/%m/%d %T", /* YYYY/mm/dd HH:MM:SS */
|
||||
"%Y/%m/%d %R", /* YYYY/mm/dd HH:MM */
|
||||
"%Y/%m/%d %r", /* YYYY/mm/dd hh:MM:SS a.m. */
|
||||
"%F %T", /* YYYY-mm-dd HH:MM:SS */
|
||||
"%F %r", /* YYYY-mm-dd HH:MM:SS p.m. */
|
||||
"%F %R", /* YYYY-mm-dd HH:MM */
|
||||
"%F %I:%M %p", /* YYYY-mm-dd HH:MM a.m. */
|
||||
"%F", /* YYYY-mm-dd ISO8601 time */
|
||||
"%T", /* HH:MM:SS */
|
||||
"%H.%M.%S", /* HH.MM.SS */
|
||||
/* From coreutils-9.2 date */
|
||||
"%Y-%m-%dT%H:%M:%S%z", /* ISO8601 time */
|
||||
"%Y-%m-%dT%H%z", /* ISO8601 time */
|
||||
"%Y-%m-%dT%H:%M%z", /* ISO8601 time */
|
||||
/* RFC 3339 time */
|
||||
"%Y-%m-%d %H:%M:%S%z", /* RFC 3339 time */
|
||||
"%Y-%m-%dT%H:%M:%S%z", /* RFC 3339 time */
|
||||
/* more oddball formats */
|
||||
"%m.%d.%Y %T", /* mm.dd.YYYY HH:MM:SS */
|
||||
"%m.%d.%Y %R", /* mm.dd.YYYY HH:MM */
|
||||
"%m.%d.%Y %r", /* mm.dd.YYYY HH:MM:SS a.m. */
|
||||
"%m.%d.%Y %I:%M %p", /* mm.dd.YYYY HH:MM p.m. */
|
||||
"%m/%d/%Y", /* mm/dd/YYYY */
|
||||
"%d %B %Y %T", /* dd Month YYYY HH:MM:SS */
|
||||
"%d %B %Y %R", /* dd Month YYYY HH:MM */
|
||||
"%d %B %Y %r", /* dd Month YYYY HH:MM:SS a.m. */
|
||||
"%d %B %Y %I:%M %p", /* dd Month YYYY HH:MM p.m. */
|
||||
"%d %b %Y %T", /* dd Mon YYYY HH:MM:SS */
|
||||
"%d %b %Y %R", /* dd Mon YYYY HH:MM */
|
||||
"%d %b %Y %r", /* dd Mon YYYY HH:MM:SS a.m. */
|
||||
"%d %b %Y %I:%M %p", /* dd Mon YYYY HH:MM p.m. */
|
||||
"%b %d, %Y %T", /* Mon dd, YYYY HH:MM:SS */
|
||||
"%b %d, %Y %R", /* Mon dd, YYYY HH:MM */
|
||||
"%b %d, %Y %r", /* Mon dd, YYYY HH:MM:SS a.m. */
|
||||
"%b %d, %Y %I:%M %p", /* Mon dd, YYYY HH:MM p.m. */
|
||||
"%m-%b-%Y", /* dd-Mon-YYYY */
|
||||
"%m-%b-%Y %T", /* dd-Mon-YYYY HH:MM:SS */
|
||||
"%m-%b-%Y %R", /* dd-Mon-YYYY HH:MM */
|
||||
"%m-%b-%Y %r", /* dd-Mon-YYYY HH:MM:SS a.m. */
|
||||
"%m-%b-%Y %I:%M %p", /* dd-Mon-YYYY HH:MM p.m. */
|
||||
"%d/%b/%Y:%T %z", /* NCSA log format dd/Mon/YYYY:HH:MM:SS zone */
|
||||
"%d/%b/%Y:%T%z", /* NCSA log format dd/Mon/YYYY:HH:MM:SSzone */
|
||||
/* No delimiters */
|
||||
"%Y%m%d %T", /* YYYYMMDD HH:MM:SS */
|
||||
"%Y%m%d %R", /* YYYYMMDD HH:MM */
|
||||
"%Y%m%d %r", /* YYYYMMDD HH:MM:SS a.m. */
|
||||
"%Y%m%d %I:%M %p", /* YYYYMMDD HH:MM p.m. */
|
||||
"%Y%m%d %H:%M:%S%z", /* YYYYMMDD HH:MM:SSzone */
|
||||
"%Y%m%dT%H:%M:%S%z", /* YYYYMMDDTHH:MM:SSzone */
|
||||
"%Y%m%dT%T", /* YYYYMMDDTHH:MM:SS */
|
||||
"%Y%m%dT%R", /* YYYYMMDDTHH:MM */
|
||||
/* Non-US formats */
|
||||
"%d-%m-%Y", /* dd-mm-YYYY */
|
||||
"%d-%m-%Y %T", /* dd-mm-YYYY HH:MM:SS */
|
||||
"%d-%m-%Y %R", /* dd-mm-YYYY HH:MM */
|
||||
"%d-%m-%Y %r", /* dd-mm-YYYY HH:MM:SS a.m. */
|
||||
"%d-%m-%Y %I:%M %p", /* dd-mm-YYYY HH:MM p.m. */
|
||||
"%d/%m/%Y %T", /* dd/mm/YYYY HH:MM:SS */
|
||||
"%d/%m/%Y %R", /* dd/mm/YYYY HH:MM */
|
||||
"%d/%m/%Y %r", /* dd/mm/YYYY HH:MM:SS a.m. */
|
||||
"%d/%m/%Y %I:%M %p", /* dd/mm/YYYY HH:MM p.m. */
|
||||
"%Y-%d-%m %T", /* YYYY-dd-mm HH:MM:SS */
|
||||
"%Y-%d-%m %R", /* YYYY-dd-mm HH:MM */
|
||||
"%d-%m-%Y %T", /* dd-mm-YYYY HH:MM:SS */
|
||||
"%d-%m-%Y %R", /* dd-mm-YYYY HH:MM */
|
||||
"%d-%m-%Y %r", /* dd-mm-YYYY HH:MM:SS a.m. */
|
||||
"%d-%m-%Y %I:%M %p", /* dd-mm-YYYY HH:MM p.m. */
|
||||
"%d.%m.%Y %T", /* dd.mm.YYYY HH:MM:SS */
|
||||
"%d.%m.%Y %R", /* dd.mm.YYYY HH:MM */
|
||||
"%d.%m.%Y %r", /* dd.mm.YYYY HH:MM:SS a.m. */
|
||||
"%d.%m.%Y %I:%M %p", /* dd.mm.YYYY HH:MM p.m. */
|
||||
0
|
||||
};
|
||||
|
||||
static void
|
||||
inittime (time_t *clock, struct tm *timeptr)
|
||||
{
|
||||
timeptr = localtime (clock); /* for now */
|
||||
|
||||
/* but default to midnight */
|
||||
timeptr->tm_hour = timeptr->tm_min = timeptr->tm_sec = 0;
|
||||
/* and let the system figure out the right DST offset */
|
||||
timeptr->tm_isdst = -1;
|
||||
}
|
||||
|
||||
int
|
||||
strptime_builtin (WORD_LIST *list)
|
||||
{
|
||||
char *s;
|
||||
struct tm t, *tm;
|
||||
time_t now, secs;
|
||||
char *datestr;
|
||||
int i;
|
||||
|
||||
if (no_options (list)) /* for now */
|
||||
return (EX_USAGE);
|
||||
|
||||
list = loptend;
|
||||
if (list == 0)
|
||||
{
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
datestr = string_list (list);
|
||||
if (datestr == 0 || *datestr == 0)
|
||||
return (EXECUTION_SUCCESS);
|
||||
|
||||
now = getnow ();
|
||||
secs = -1;
|
||||
for (i = 0; date_time_modifiers[i].shorthand; i++)
|
||||
{
|
||||
if (STREQ (datestr, date_time_modifiers[i].shorthand))
|
||||
{
|
||||
secs = now + date_time_modifiers[i].incr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (secs == -1)
|
||||
{
|
||||
/* init struct tm */
|
||||
inittime (&now, tm);
|
||||
t = *tm;
|
||||
for (i = 0; date_time_formats[i]; i++)
|
||||
{
|
||||
s = strptime (datestr, date_time_formats[i], &t);
|
||||
if (s == 0)
|
||||
continue;
|
||||
/* skip extra characters at the end for now */
|
||||
secs = mktime (&t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf ("%ld\n", secs);
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
char *strptime_doc[] = {
|
||||
"Convert a date-time string to seconds since the epoch.",
|
||||
"",
|
||||
"Take DATE-TIME, a date-time string, parse it against a set of common",
|
||||
"date-time formats. If the string matches one of the formats, convert",
|
||||
"it into seconds since the epoch and display the result.",
|
||||
(char *)NULL
|
||||
};
|
||||
|
||||
/* The standard structure describing a builtin command. bash keeps an array
|
||||
of these structures. The flags must include BUILTIN_ENABLED so the
|
||||
builtin can be used. */
|
||||
struct builtin strptime_struct = {
|
||||
"strptime", /* builtin name */
|
||||
strptime_builtin, /* function implementing the builtin */
|
||||
BUILTIN_ENABLED, /* initial flags for builtin */
|
||||
strptime_doc, /* array of long documentation strings. */
|
||||
"strptime date-time", /* usage synopsis; becomes short_doc */
|
||||
0 /* reserved for internal use */
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
# if we are worried somehow about inheriting a function for unset or exec,
|
||||
# set posix mode, then unset it later
|
||||
POSIXLY_CORRECT=1
|
||||
|
||||
# make sure to run with bash -p to prevent inheriting functions. you can
|
||||
# do this (if the script does not need to run setuid) or use the
|
||||
# POSIXLY_CORRECT setting above (as long as you run set +o posix as done below)
|
||||
#case $SHELLOPTS in
|
||||
#*privileged*) ;;
|
||||
#*) \exec /bin/bash -p $0 "$@" ;;
|
||||
#esac
|
||||
|
||||
# unset is a special builtin and will be found before functions; quoting it
|
||||
# will prevent alias expansion
|
||||
# add any other shell builtins you're concerned about
|
||||
\unset -f command builtin unset shopt set unalias hash
|
||||
\unset -f read true exit echo printf
|
||||
|
||||
# remove all aliases and disable alias expansion
|
||||
\unalias -a
|
||||
\shopt -u expand_aliases
|
||||
|
||||
# and make sure we're no longer running in posix mode
|
||||
set +o posix
|
||||
|
||||
# get rid of any hashed commands
|
||||
hash -r
|
||||
|
||||
# if you're concerned about PATH spoofing, make sure to have a path that
|
||||
# will find the standard utilities
|
||||
#PATH=$(command getconf -p getconf PATH):$PATH
|
||||
Reference in New Issue
Block a user