aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2021-09-17 11:36:53 +0100
committerRon Yorston <rmy@pobox.com>2021-09-17 11:38:52 +0100
commit46299d0c4f4c9a4bbad38bbbe26f196e1bccdc52 (patch)
tree6194cdabc0858ae3bed0276679b3b80a722f4150
parentcf91de46997888b7096352805c05401e7ebae01e (diff)
parent9fe1548bbfde548d54acaab113656a56ea0ccc72 (diff)
downloadbusybox-w32-46299d0c4f4c9a4bbad38bbbe26f196e1bccdc52.tar.gz
busybox-w32-46299d0c4f4c9a4bbad38bbbe26f196e1bccdc52.tar.bz2
busybox-w32-46299d0c4f4c9a4bbad38bbbe26f196e1bccdc52.zip
Merge branch 'busybox' into merge
Disable FEATURE_TIMEZONE for now.
-rw-r--r--configs/mingw32_defconfig1
-rw-r--r--configs/mingw64_defconfig1
-rw-r--r--coreutils/date.c7
-rw-r--r--coreutils/df.c26
-rw-r--r--coreutils/touch.c6
-rw-r--r--include/libbb.h2
-rw-r--r--libbb/Config.src11
-rw-r--r--libbb/time.c78
-rw-r--r--shell/ash.c58
-rw-r--r--testsuite/date/date-timezone32
-rw-r--r--util-linux/getopt.c64
11 files changed, 194 insertions, 92 deletions
diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig
index 765653a43..0882741d5 100644
--- a/configs/mingw32_defconfig
+++ b/configs/mingw32_defconfig
@@ -147,6 +147,7 @@ CONFIG_FEATURE_COPYBUF_KB=4
147# CONFIG_MONOTONIC_SYSCALL is not set 147# CONFIG_MONOTONIC_SYSCALL is not set
148# CONFIG_IOCTL_HEX2STR_ERROR is not set 148# CONFIG_IOCTL_HEX2STR_ERROR is not set
149# CONFIG_FEATURE_HWIB is not set 149# CONFIG_FEATURE_HWIB is not set
150# CONFIG_FEATURE_TIMEZONE is not set
150 151
151# 152#
152# Applets 153# Applets
diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig
index 656ebb101..358d19d4e 100644
--- a/configs/mingw64_defconfig
+++ b/configs/mingw64_defconfig
@@ -147,6 +147,7 @@ CONFIG_FEATURE_COPYBUF_KB=4
147# CONFIG_MONOTONIC_SYSCALL is not set 147# CONFIG_MONOTONIC_SYSCALL is not set
148# CONFIG_IOCTL_HEX2STR_ERROR is not set 148# CONFIG_IOCTL_HEX2STR_ERROR is not set
149# CONFIG_FEATURE_HWIB is not set 149# CONFIG_FEATURE_HWIB is not set
150# CONFIG_FEATURE_TIMEZONE is not set
150 151
151# 152#
152# Applets 153# Applets
diff --git a/coreutils/date.c b/coreutils/date.c
index d64ff94b9..4e62a6fb0 100644
--- a/coreutils/date.c
+++ b/coreutils/date.c
@@ -291,6 +291,7 @@ int date_main(int argc UNUSED_PARAM, char **argv)
291 291
292 /* If date string is given, update tm_time, and maybe set date */ 292 /* If date string is given, update tm_time, and maybe set date */
293 if (date_str != NULL) { 293 if (date_str != NULL) {
294 int check_dst = 1;
294 /* Zero out fields - take her back to midnight! */ 295 /* Zero out fields - take her back to midnight! */
295 tm_time.tm_sec = 0; 296 tm_time.tm_sec = 0;
296 tm_time.tm_min = 0; 297 tm_time.tm_min = 0;
@@ -301,12 +302,12 @@ int date_main(int argc UNUSED_PARAM, char **argv)
301 if (strptime(date_str, fmt_str2dt, &tm_time) == NULL) 302 if (strptime(date_str, fmt_str2dt, &tm_time) == NULL)
302 bb_error_msg_and_die(bb_msg_invalid_date, date_str); 303 bb_error_msg_and_die(bb_msg_invalid_date, date_str);
303 } else { 304 } else {
304 parse_datestr(date_str, &tm_time); 305 check_dst = parse_datestr(date_str, &tm_time);
305 } 306 }
306 307
307 /* Correct any day of week and day of year etc. fields */ 308 /* Correct any day of week and day of year etc. fields */
308 /* Be sure to recheck dst (but not if date is time_t format) */ 309 /* Be sure to recheck dst (but not if date is UTC) */
309 if (date_str[0] != '@') 310 if (check_dst)
310 tm_time.tm_isdst = -1; 311 tm_time.tm_isdst = -1;
311 ts.tv_sec = validate_tm_time(date_str, &tm_time); 312 ts.tv_sec = validate_tm_time(date_str, &tm_time);
312 ts.tv_nsec = 0; 313 ts.tv_nsec = 0;
diff --git a/coreutils/df.c b/coreutils/df.c
index e8d4bc8f2..9f8b3a71e 100644
--- a/coreutils/df.c
+++ b/coreutils/df.c
@@ -99,15 +99,16 @@ int df_main(int argc UNUSED_PARAM, char **argv)
99 struct mntent *mount_entry; 99 struct mntent *mount_entry;
100 struct statvfs s; 100 struct statvfs s;
101 enum { 101 enum {
102 OPT_KILO = (1 << 0), 102 OPT_KILO = (1 << 0),
103 OPT_POSIX = (1 << 1), 103 OPT_POSIX = (1 << 1),
104 OPT_FSTYPE = (1 << 2), 104 OPT_FSTYPE = (1 << 2),
105 OPT_t = (1 << 3), 105 OPT_t = (1 << 3),
106 OPT_ALL = (1 << 4) * ENABLE_FEATURE_DF_FANCY, 106 OPT_ALL = (1 << 4) * ENABLE_FEATURE_DF_FANCY,
107 OPT_INODE = (1 << 5) * ENABLE_FEATURE_DF_FANCY, 107 OPT_INODE = (1 << 5) * ENABLE_FEATURE_DF_FANCY,
108 OPT_BSIZE = (1 << 6) * ENABLE_FEATURE_DF_FANCY, 108 OPT_BSIZE = (1 << 6) * ENABLE_FEATURE_DF_FANCY,
109 OPT_HUMAN = (1 << (4 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE, 109 OPT_HUMAN = (1 << (4 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE,
110 OPT_MEGA = (1 << (5 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE, 110 OPT_HUMANDEC = (1 << (5 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE,
111 OPT_MEGA = (1 << (6 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE,
111 }; 112 };
112 const char *disp_units_hdr = NULL; 113 const char *disp_units_hdr = NULL;
113 char *chp, *opt_t; 114 char *chp, *opt_t;
@@ -124,7 +125,7 @@ int df_main(int argc UNUSED_PARAM, char **argv)
124 opt = getopt32(argv, "^" 125 opt = getopt32(argv, "^"
125 "kPTt:" 126 "kPTt:"
126 IF_FEATURE_DF_FANCY("aiB:") 127 IF_FEATURE_DF_FANCY("aiB:")
127 IF_FEATURE_HUMAN_READABLE("hm") 128 IF_FEATURE_HUMAN_READABLE("hHm")
128 "\0" 129 "\0"
129#if ENABLE_FEATURE_HUMAN_READABLE && ENABLE_FEATURE_DF_FANCY 130#if ENABLE_FEATURE_HUMAN_READABLE && ENABLE_FEATURE_DF_FANCY
130 "k-mB:m-Bk:B-km" 131 "k-mB:m-Bk:B-km"
@@ -151,8 +152,11 @@ int df_main(int argc UNUSED_PARAM, char **argv)
151 got_it: ; 152 got_it: ;
152 } 153 }
153 154
154 if (opt & OPT_HUMAN) { 155 if (opt & (OPT_HUMAN|OPT_HUMANDEC)) {
155 df_disp_hr = 0; 156 df_disp_hr = 0;
157//TODO: need to add support in make_human_readable_str() for "decimal human readable"
158 //if (opt & OPT_HUMANDEC)
159 // df_disp_hr--;
156 disp_units_hdr = " Size"; 160 disp_units_hdr = " Size";
157 } 161 }
158 if (opt & OPT_INODE) 162 if (opt & OPT_INODE)
diff --git a/coreutils/touch.c b/coreutils/touch.c
index 78100ba1d..7e13a27be 100644
--- a/coreutils/touch.c
+++ b/coreutils/touch.c
@@ -140,15 +140,17 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
140 if (opts & (OPT_d|OPT_t)) { 140 if (opts & (OPT_d|OPT_t)) {
141 struct tm tm_time; 141 struct tm tm_time;
142 time_t t; 142 time_t t;
143 int check_dst;
143 144
144 //memset(&tm_time, 0, sizeof(tm_time)); 145 //memset(&tm_time, 0, sizeof(tm_time));
145 /* Better than memset: makes "HH:MM" dates meaningful */ 146 /* Better than memset: makes "HH:MM" dates meaningful */
146 time(&t); 147 time(&t);
147 localtime_r(&t, &tm_time); 148 localtime_r(&t, &tm_time);
148 parse_datestr(date_str, &tm_time); 149 check_dst = parse_datestr(date_str, &tm_time);
149 150
150 /* Correct any day of week and day of year etc. fields */ 151 /* Correct any day of week and day of year etc. fields */
151 tm_time.tm_isdst = -1; /* Be sure to recheck dst */ 152 if (check_dst)
153 tm_time.tm_isdst = -1; /* recheck dst unless date is UTC */
152 t = validate_tm_time(date_str, &tm_time); 154 t = validate_tm_time(date_str, &tm_time);
153 155
154 timebuf[1].tv_sec = timebuf[0].tv_sec = t; 156 timebuf[1].tv_sec = timebuf[0].tv_sec = t;
diff --git a/include/libbb.h b/include/libbb.h
index 7a43967cb..cee37f9d8 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -730,7 +730,7 @@ struct BUG_too_small {
730}; 730};
731 731
732 732
733void parse_datestr(const char *date_str, struct tm *ptm) FAST_FUNC; 733int parse_datestr(const char *date_str, struct tm *ptm) FAST_FUNC;
734time_t validate_tm_time(const char *date_str, struct tm *ptm) FAST_FUNC; 734time_t validate_tm_time(const char *date_str, struct tm *ptm) FAST_FUNC;
735char *strftime_HHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC; 735char *strftime_HHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC;
736char *strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC; 736char *strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC;
diff --git a/libbb/Config.src b/libbb/Config.src
index f97de8ef7..58c5fad50 100644
--- a/libbb/Config.src
+++ b/libbb/Config.src
@@ -395,3 +395,14 @@ config FEATURE_HWIB
395 default y 395 default y
396 help 396 help
397 Support for printing infiniband addresses in network applets. 397 Support for printing infiniband addresses in network applets.
398
399config FEATURE_TIMEZONE
400 bool "Allow timezone in dates"
401 default y
402 depends on DESKTOP
403 help
404 Permit the use of timezones when parsing user-provided data
405 strings, e.g. '1996-04-09 12:45:00 -0500'.
406
407 This requires support for the '%z' extension to strptime() which
408 may not be available in all implementations.
diff --git a/libbb/time.c b/libbb/time.c
index cf5f2e5c8..41a69c754 100644
--- a/libbb/time.c
+++ b/libbb/time.c
@@ -8,16 +8,74 @@
8 */ 8 */
9#include "libbb.h" 9#include "libbb.h"
10 10
11void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) 11/* Returns 0 if the time structure contains an absolute UTC time which
12 * should not be subject to DST adjustment by the caller. */
13int FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
12{ 14{
13 char end = '\0'; 15 char end = '\0';
16#if ENABLE_DESKTOP
17/*
18 * strptime is BIG: ~1k in uclibc, ~10k in glibc
19 * We need it for 'month_name d HH:MM:SS YYYY', supported by GNU date,
20 * but if we've linked it we might as well use it for everything.
21 */
22 static const char fmt_str[] ALIGN1 =
23 "%R" "\0" /* HH:MM */
24 "%T" "\0" /* HH:MM:SS */
25 "%m.%d-%R" "\0" /* mm.dd-HH:MM */
26 "%m.%d-%T" "\0" /* mm.dd-HH:MM:SS */
27 "%Y.%m.%d-%R" "\0" /* yyyy.mm.dd-HH:MM */
28 "%Y.%m.%d-%T" "\0" /* yyyy.mm.dd-HH:MM:SS */
29 "%b %d %T %Y" "\0" /* month_name d HH:MM:SS YYYY */
30 "%Y-%m-%d %R" "\0" /* yyyy-mm-dd HH:MM */
31 "%Y-%m-%d %T" "\0" /* yyyy-mm-dd HH:MM:SS */
32#if ENABLE_FEATURE_TIMEZONE
33 "%Y-%m-%d %R %z" "\0" /* yyyy-mm-dd HH:MM TZ */
34 "%Y-%m-%d %T %z" "\0" /* yyyy-mm-dd HH:MM:SS TZ */
35#endif
36 "%Y-%m-%d %H" "\0" /* yyyy-mm-dd HH */
37 "%Y-%m-%d" "\0" /* yyyy-mm-dd */
38 /* extra NUL */;
39 struct tm save;
40 const char *fmt;
41 char *endp;
42
43 save = *ptm;
44 fmt = fmt_str;
45 while (*fmt) {
46 endp = strptime(date_str, fmt, ptm);
47 if (endp && *endp == '\0') {
48#if ENABLE_FEATURE_TIMEZONE
49 if (strchr(fmt, 'z')) {
50 time_t t;
51 struct tm *utm;
52
53 /* we have timezone offset: obtain Unix time_t */
54 ptm->tm_sec -= ptm->tm_gmtoff;
55 ptm->tm_isdst = 0;
56 t = timegm(ptm);
57 if (t == (time_t)-1)
58 break;
59 /* convert Unix time_t to struct tm in user's locale */
60 utm = localtime(&t);
61 if (!utm)
62 break;
63 *ptm = *utm;
64 return 0;
65 }
66#endif
67 return 1;
68 }
69 *ptm = save;
70 while (*++fmt)
71 continue;
72 ++fmt;
73 }
74#else
14 const char *last_colon = strrchr(date_str, ':'); 75 const char *last_colon = strrchr(date_str, ':');
15 76
16 if (last_colon != NULL) { 77 if (last_colon != NULL) {
17 /* Parse input and assign appropriately to ptm */ 78 /* Parse input and assign appropriately to ptm */
18#if ENABLE_DESKTOP
19 const char *endp;
20#endif
21 79
22 /* HH:MM */ 80 /* HH:MM */
23 if (sscanf(date_str, "%u:%u%c", 81 if (sscanf(date_str, "%u:%u%c",
@@ -50,14 +108,6 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
50 ptm->tm_year -= 1900; /* Adjust years */ 108 ptm->tm_year -= 1900; /* Adjust years */
51 ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */ 109 ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */
52 } else 110 } else
53#if ENABLE_DESKTOP /* strptime is BIG: ~1k in uclibc, ~10k in glibc */
54 /* month_name d HH:MM:SS YYYY. Supported by GNU date */
55 if ((endp = strptime(date_str, "%b %d %T %Y", ptm)) != NULL
56 && *endp == '\0'
57 ) {
58 return; /* don't fall through to end == ":" check */
59 } else
60#endif
61 { 111 {
62 bb_error_msg_and_die(bb_msg_invalid_date, date_str); 112 bb_error_msg_and_die(bb_msg_invalid_date, date_str);
63 } 113 }
@@ -89,6 +139,7 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
89 ptm->tm_year -= 1900; /* Adjust years */ 139 ptm->tm_year -= 1900; /* Adjust years */
90 ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */ 140 ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */
91 } else 141 } else
142#endif /* ENABLE_DESKTOP */
92 if (date_str[0] == '@') { 143 if (date_str[0] == '@') {
93 time_t t; 144 time_t t;
94 if (sizeof(t) <= sizeof(long)) 145 if (sizeof(t) <= sizeof(long))
@@ -99,7 +150,7 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
99 struct tm *lt = localtime(&t); 150 struct tm *lt = localtime(&t);
100 if (lt) { 151 if (lt) {
101 *ptm = *lt; 152 *ptm = *lt;
102 return; 153 return 0;
103 } 154 }
104 } 155 }
105 end = '1'; 156 end = '1';
@@ -216,6 +267,7 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
216 if (end != '\0') { 267 if (end != '\0') {
217 bb_error_msg_and_die(bb_msg_invalid_date, date_str); 268 bb_error_msg_and_die(bb_msg_invalid_date, date_str);
218 } 269 }
270 return 1;
219} 271}
220 272
221time_t FAST_FUNC validate_tm_time(const char *date_str, struct tm *ptm) 273time_t FAST_FUNC validate_tm_time(const char *date_str, struct tm *ptm)
diff --git a/shell/ash.c b/shell/ash.c
index 7544204d1..5adb95bc5 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -2355,6 +2355,7 @@ static const struct {
2355 { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset }, 2355 { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset },
2356#endif 2356#endif
2357 { VSTRFIXED|VTEXTFIXED , NULL /* inited to linenovar */, NULL }, 2357 { VSTRFIXED|VTEXTFIXED , NULL /* inited to linenovar */, NULL },
2358 { VSTRFIXED|VTEXTFIXED , NULL /* inited to funcnamevar */, NULL },
2358#if ENABLE_ASH_RANDOM_SUPPORT 2359#if ENABLE_ASH_RANDOM_SUPPORT
2359 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random }, 2360 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
2360#endif 2361#endif
@@ -2384,6 +2385,8 @@ struct globals_var {
2384 struct var varinit[ARRAY_SIZE(varinit_data)]; 2385 struct var varinit[ARRAY_SIZE(varinit_data)];
2385 int lineno; 2386 int lineno;
2386 char linenovar[sizeof("LINENO=") + sizeof(int)*3]; 2387 char linenovar[sizeof("LINENO=") + sizeof(int)*3];
2388 char funcnamevar[sizeof("FUNCNAME=") + 64];
2389 char *funcname;
2387 unsigned trap_depth; 2390 unsigned trap_depth;
2388 bool in_trap_ERR; /* ERR cannot recurse, no need to be a counter */ 2391 bool in_trap_ERR; /* ERR cannot recurse, no need to be a counter */
2389}; 2392};
@@ -2396,6 +2399,8 @@ extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var;
2396#define varinit (G_var.varinit ) 2399#define varinit (G_var.varinit )
2397#define lineno (G_var.lineno ) 2400#define lineno (G_var.lineno )
2398#define linenovar (G_var.linenovar ) 2401#define linenovar (G_var.linenovar )
2402#define funcnamevar (G_var.funcnamevar )
2403#define funcname (G_var.funcname )
2399#define trap_depth (G_var.trap_depth ) 2404#define trap_depth (G_var.trap_depth )
2400#define in_trap_ERR (G_var.in_trap_ERR ) 2405#define in_trap_ERR (G_var.in_trap_ERR )
2401#define vifs varinit[0] 2406#define vifs varinit[0]
@@ -2413,13 +2418,14 @@ extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var;
2413#endif 2418#endif
2414#define VAR_OFFSET2 (VAR_OFFSET1 + ENABLE_ASH_GETOPTS) 2419#define VAR_OFFSET2 (VAR_OFFSET1 + ENABLE_ASH_GETOPTS)
2415#define vlineno varinit[VAR_OFFSET2 + 5] 2420#define vlineno varinit[VAR_OFFSET2 + 5]
2421#define vfuncname varinit[VAR_OFFSET2 + 6]
2416#if ENABLE_ASH_RANDOM_SUPPORT 2422#if ENABLE_ASH_RANDOM_SUPPORT
2417# define vrandom varinit[VAR_OFFSET2 + 6] 2423# define vrandom varinit[VAR_OFFSET2 + 7]
2418#endif 2424#endif
2419#define VAR_OFFSET3 (VAR_OFFSET2 + ENABLE_ASH_RANDOM_SUPPORT) 2425#define VAR_OFFSET3 (VAR_OFFSET2 + ENABLE_ASH_RANDOM_SUPPORT)
2420#if BASH_EPOCH_VARS 2426#if BASH_EPOCH_VARS
2421# define vepochs varinit[VAR_OFFSET3 + 6] 2427# define vepochs varinit[VAR_OFFSET3 + 7]
2422# define vepochr varinit[VAR_OFFSET3 + 7] 2428# define vepochr varinit[VAR_OFFSET3 + 8]
2423#endif 2429#endif
2424#define INIT_G_var() do { \ 2430#define INIT_G_var() do { \
2425 unsigned i; \ 2431 unsigned i; \
@@ -2432,6 +2438,8 @@ extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var;
2432 } \ 2438 } \
2433 strcpy(linenovar, "LINENO="); \ 2439 strcpy(linenovar, "LINENO="); \
2434 vlineno.var_text = linenovar; \ 2440 vlineno.var_text = linenovar; \
2441 strcpy(funcnamevar, "FUNCNAME="); \
2442 vfuncname.var_text = funcnamevar; \
2435} while (0) 2443} while (0)
2436 2444
2437/* 2445/*
@@ -2571,6 +2579,9 @@ lookupvar(const char *name)
2571 if (!(v->flags & VUNSET)) { 2579 if (!(v->flags & VUNSET)) {
2572 if (v->var_text == linenovar) { 2580 if (v->var_text == linenovar) {
2573 fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno); 2581 fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno);
2582 } else
2583 if (v->var_text == funcnamevar) {
2584 safe_strncpy(funcnamevar+9, funcname ? funcname : "", sizeof(funcnamevar)-9);
2574 } 2585 }
2575 return var_end(v->var_text); 2586 return var_end(v->var_text);
2576 } 2587 }
@@ -6015,7 +6026,7 @@ stoppedjobs(void)
6015 int retval; 6026 int retval;
6016 6027
6017 retval = 0; 6028 retval = 0;
6018 if (job_warning) 6029 if (!iflag || job_warning)
6019 goto out; 6030 goto out;
6020 jp = curjob; 6031 jp = curjob;
6021 if (jp && jp->state == JOBSTOPPED) { 6032 if (jp && jp->state == JOBSTOPPED) {
@@ -10654,6 +10665,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
10654 int e; 10665 int e;
10655 int savelineno; 10666 int savelineno;
10656 int savefuncline; 10667 int savefuncline;
10668 char *savefuncname;
10657 char *savetrap = NULL; 10669 char *savetrap = NULL;
10658 10670
10659 if (!Eflag) { 10671 if (!Eflag) {
@@ -10663,6 +10675,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
10663 savelineno = lineno; 10675 savelineno = lineno;
10664 saveparam = shellparam; 10676 saveparam = shellparam;
10665 savefuncline = funcline; 10677 savefuncline = funcline;
10678 savefuncname = funcname;
10666 savehandler = exception_handler; 10679 savehandler = exception_handler;
10667 e = setjmp(jmploc.loc); 10680 e = setjmp(jmploc.loc);
10668 if (e) { 10681 if (e) {
@@ -10672,6 +10685,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
10672 exception_handler = &jmploc; 10685 exception_handler = &jmploc;
10673 shellparam.malloced = 0; 10686 shellparam.malloced = 0;
10674 func->count++; 10687 func->count++;
10688 funcname = func->n.ndefun.text;
10675 funcline = func->n.ndefun.linno; 10689 funcline = func->n.ndefun.linno;
10676 INT_ON; 10690 INT_ON;
10677 shellparam.nparam = argc - 1; 10691 shellparam.nparam = argc - 1;
@@ -10683,6 +10697,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
10683 evaltree(func->n.ndefun.body, flags & EV_TESTED); 10697 evaltree(func->n.ndefun.body, flags & EV_TESTED);
10684 funcdone: 10698 funcdone:
10685 INT_OFF; 10699 INT_OFF;
10700 funcname = savefuncname;
10686 if (savetrap) { 10701 if (savetrap) {
10687 if (!trap[NTRAP_ERR]) 10702 if (!trap[NTRAP_ERR])
10688 trap[NTRAP_ERR] = savetrap; 10703 trap[NTRAP_ERR] = savetrap;
@@ -11650,9 +11665,7 @@ preadfd(void)
11650 * Refill the input buffer and return the next input character: 11665 * Refill the input buffer and return the next input character:
11651 * 11666 *
11652 * 1) If a string was pushed back on the input, pop it; 11667 * 1) If a string was pushed back on the input, pop it;
11653 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM) 11668 * 2) If we are reading from a string we can't refill the buffer, return EOF.
11654 * or we are reading from a string so we can't refill the buffer,
11655 * return EOF.
11656 * 3) If there is more stuff in this buffer, use it else call read to fill it. 11669 * 3) If there is more stuff in this buffer, use it else call read to fill it.
11657 * 4) Process input up to the next newline, deleting nul characters. 11670 * 4) Process input up to the next newline, deleting nul characters.
11658 */ 11671 */
@@ -11669,21 +11682,9 @@ preadbuffer(void)
11669 popstring(); 11682 popstring();
11670 return __pgetc(); 11683 return __pgetc();
11671 } 11684 }
11672 /* on both branches above g_parsefile->left_in_line < 0.
11673 * "pgetc" needs refilling.
11674 */
11675 11685
11676 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read", 11686 if (g_parsefile->buf == NULL) {
11677 * pungetc() may increment it a few times.
11678 * Assuming it won't increment it to less than -90.
11679 */
11680 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
11681 pgetc_debug("preadbuffer PEOF1"); 11687 pgetc_debug("preadbuffer PEOF1");
11682 /* even in failure keep left_in_line and next_to_pgetc
11683 * in lock step, for correct multi-layer pungetc.
11684 * left_in_line was decremented before preadbuffer(),
11685 * must inc next_to_pgetc: */
11686 g_parsefile->next_to_pgetc++;
11687 return PEOF; 11688 return PEOF;
11688 } 11689 }
11689 11690
@@ -11693,10 +11694,8 @@ preadbuffer(void)
11693 again: 11694 again:
11694 more = preadfd(); 11695 more = preadfd();
11695 if (more <= 0) { 11696 if (more <= 0) {
11696 /* don't try reading again */ 11697 g_parsefile->left_in_buffer = g_parsefile->left_in_line = 0;
11697 g_parsefile->left_in_line = -99;
11698 pgetc_debug("preadbuffer PEOF2"); 11698 pgetc_debug("preadbuffer PEOF2");
11699 g_parsefile->next_to_pgetc++;
11700 return PEOF; 11699 return PEOF;
11701 } 11700 }
11702 } 11701 }
@@ -14376,12 +14375,13 @@ cmdloop(int top)
14376 if (!top || numeof >= 50) 14375 if (!top || numeof >= 50)
14377 break; 14376 break;
14378 if (!stoppedjobs()) { 14377 if (!stoppedjobs()) {
14378 if (!iflag)
14379 break;
14379 if (!Iflag) { 14380 if (!Iflag) {
14380 if (iflag) { 14381 newline_and_flush(stderr);
14381 newline_and_flush(stderr);
14382 }
14383 break; 14382 break;
14384 } 14383 }
14384 /* "set -o ignoreeof" active, do not exit command loop on ^D */
14385 out2str("\nUse \"exit\" to leave shell.\n"); 14385 out2str("\nUse \"exit\" to leave shell.\n");
14386 } 14386 }
14387 numeof++; 14387 numeof++;
@@ -14508,6 +14508,12 @@ exitcmd(int argc UNUSED_PARAM, char **argv)
14508 if (argv[1]) 14508 if (argv[1])
14509 savestatus = number(argv[1]); 14509 savestatus = number(argv[1]);
14510 14510
14511//TODO: this script
14512// trap 'echo trap:$FUNCNAME' EXIT
14513// f() { exit; }
14514// f
14515//prints "trap:f" in bash. We can call exitshell() here to achieve this.
14516//For now, keeping dash code:
14511 raise_exception(EXEXIT); 14517 raise_exception(EXEXIT);
14512 /* NOTREACHED */ 14518 /* NOTREACHED */
14513} 14519}
diff --git a/testsuite/date/date-timezone b/testsuite/date/date-timezone
new file mode 100644
index 000000000..8628aa1d7
--- /dev/null
+++ b/testsuite/date/date-timezone
@@ -0,0 +1,32 @@
1# FEATURE: CONFIG_FEATURE_TIMEZONE
2
3# 'Z' is UTC
4dt=$(TZ=UTC0 busybox date -d '1999-1-2 3:4:5Z')
5dt=$(echo "$dt" | cut -b1-19)
6test x"$dt" = x"Sat Jan 2 03:04:05"
7
8# '+0600' is six hours ahead of UTC
9dt=$(TZ=UTC0 busybox date -d '1999-1-2 3:4:5 +0600')
10dt=$(echo "$dt" | cut -b1-19)
11test x"$dt" = x"Fri Jan 1 21:04:05"
12
13# '-0600' is six hours behind UTC
14dt=$(TZ=UTC0 busybox date -d '1999-1-2 3:4:5 -0600')
15dt=$(echo "$dt" | cut -b1-19)
16test x"$dt" = x"Sat Jan 2 09:04:05"
17
18# before dst is switched on
19dt=$(TZ=GMT0BST,M3.5.0/1,M10.5.0/2 busybox date -d '2021-03-28 00:59:59 +0000')
20test x"$dt" = x"Sun Mar 28 00:59:59 GMT 2021"
21
22# after dst is switched on
23dt=$(TZ=GMT0BST,M3.5.0/1,M10.5.0/2 busybox date -d '2021-03-28 01:00:01 +0000')
24test x"$dt" = x"Sun Mar 28 02:00:01 BST 2021"
25
26# before dst is switched off
27dt=$(TZ=GMT0BST,M3.5.0/1,M10.5.0/2 busybox date -d '2021-10-31 00:00:01 +0000')
28test x"$dt" = x"Sun Oct 31 01:00:01 BST 2021"
29
30# after dst is switched off: back to 01:00:01 but with different TZ
31dt=$(TZ=GMT0BST,M3.5.0/1,M10.5.0/2 busybox date -d '2021-10-31 01:00:01 +0000')
32test x"$dt" = x"Sun Oct 31 01:00:01 GMT 2021"
diff --git a/util-linux/getopt.c b/util-linux/getopt.c
index 1fa402429..4148586d3 100644
--- a/util-linux/getopt.c
+++ b/util-linux/getopt.c
@@ -156,57 +156,43 @@ enum {
156static const char *normalize(const char *arg) 156static const char *normalize(const char *arg)
157{ 157{
158 char *bufptr; 158 char *bufptr;
159#if ENABLE_FEATURE_CLEAN_UP
160 static char *BUFFER = NULL;
161 free(BUFFER);
162#else
163 char *BUFFER; 159 char *BUFFER;
164#endif
165 160
166 if (!quote) { /* Just copy arg */ 161 if (!quote) { /* Just return arg */
167 BUFFER = xstrdup(arg); 162 return arg;
168 return BUFFER;
169 } 163 }
170 164
171 /* Each character in arg may take up to four characters in the result: 165 /* Each character in arg may take up to four characters in the result:
172 For a quote we need a closing quote, a backslash, a quote and an 166 For a quote we need a closing quote, a backslash, a quote and an
173 opening quote! We need also the global opening and closing quote, 167 opening quote! We need also the global opening and closing quote,
174 and one extra character for '\0'. */ 168 and one extra character for '\0'. */
175 BUFFER = xmalloc(strlen(arg)*4 + 3); 169 BUFFER = auto_string(xmalloc(strlen(arg)*4 + 3));
176 170
177 bufptr = BUFFER; 171 bufptr = BUFFER;
178 *bufptr ++= '\''; 172 *bufptr ++= '\'';
179 173
180 while (*arg) { 174 while (*arg) {
181 if (*arg == '\'') { 175 if (shell_TCSH && *arg == '\n') {
182 /* Quote: replace it with: '\'' */
183 *bufptr ++= '\'';
184 *bufptr ++= '\\';
185 *bufptr ++= '\'';
186 *bufptr ++= '\'';
187 } else if (shell_TCSH && *arg == '!') {
188 /* Exclamation mark: replace it with: \! */
189 *bufptr ++= '\'';
190 *bufptr ++= '\\';
191 *bufptr ++= '!';
192 *bufptr ++= '\'';
193 } else if (shell_TCSH && *arg == '\n') {
194 /* Newline: replace it with: \n */ 176 /* Newline: replace it with: \n */
195 *bufptr ++= '\\'; 177 *bufptr++ = '\\';
196 *bufptr ++= 'n'; 178 *bufptr++ = 'n';
197 } else if (shell_TCSH && isspace(*arg)) {
198 /* Non-newline whitespace: replace it with \<ws> */
199 *bufptr ++= '\'';
200 *bufptr ++= '\\';
201 *bufptr ++= *arg;
202 *bufptr ++= '\'';
203 } else 179 } else
180 if ((shell_TCSH && (*arg == '!' || isspace(*arg)))
181 || *arg == '\''
182 ) {
183 /* Quote exclamation marks, non-NL whitespace and quotes */
184 *bufptr++ = '\'';
185 *bufptr++ = '\\';
186 *bufptr++ = *arg;
187 *bufptr++ = '\'';
188 } else {
204 /* Just copy */ 189 /* Just copy */
205 *bufptr ++= *arg; 190 *bufptr ++= *arg;
191 }
206 arg++; 192 arg++;
207 } 193 }
208 *bufptr ++= '\''; 194 *bufptr++ = '\'';
209 *bufptr ++= '\0'; 195 *bufptr++ = '\0';
210 return BUFFER; 196 return BUFFER;
211} 197}
212 198
@@ -327,12 +313,18 @@ static struct option *add_long_options(struct option *long_options, char *option
327 313
328static void set_shell(const char *new_shell) 314static void set_shell(const char *new_shell)
329{ 315{
330 if (strcmp(new_shell, "bash") == 0 || strcmp(new_shell, "sh") == 0) 316 switch (index_in_strings("bash\0sh\0tcsh\0csh\0", new_shell)) {
331 return; 317 case 0:
332 if (strcmp(new_shell, "tcsh") == 0 || strcmp(new_shell, "csh") == 0) 318 case 1:
319 break;
320 case 2:
321 case 3:
333 option_mask32 |= SHELL_IS_TCSH; 322 option_mask32 |= SHELL_IS_TCSH;
334 else 323 break;
324 default:
335 bb_error_msg("unknown shell '%s', assuming bash", new_shell); 325 bb_error_msg("unknown shell '%s', assuming bash", new_shell);
326 break;
327 }
336} 328}
337 329
338 330