aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2021-05-14 08:17:12 +0100
committerRon Yorston <rmy@pobox.com>2021-05-14 08:17:12 +0100
commita3f5a1b7f4275f713acf22f534f95c0da8392e53 (patch)
tree49b65422a3e9c33f508da9ccf3ae79d324bd9e96
parent375cda9a88024135d630ca8990d9aff4ea414e89 (diff)
parent7de0ab21d939a5a304157f75918d0318a95261a3 (diff)
downloadbusybox-w32-a3f5a1b7f4275f713acf22f534f95c0da8392e53.tar.gz
busybox-w32-a3f5a1b7f4275f713acf22f534f95c0da8392e53.tar.bz2
busybox-w32-a3f5a1b7f4275f713acf22f534f95c0da8392e53.zip
Merge branch 'busybox' into merge
-rw-r--r--Makefile.flags13
-rw-r--r--applets/applet_tables.c2
-rw-r--r--archival/libarchive/decompress_gunzip.c11
-rw-r--r--archival/unzip.c34
-rw-r--r--coreutils/chgrp.c2
-rw-r--r--coreutils/chown.c2
-rw-r--r--coreutils/cp.c2
-rw-r--r--coreutils/cut.c8
-rw-r--r--coreutils/dd.c50
-rw-r--r--coreutils/du.c2
-rw-r--r--coreutils/echo.c2
-rw-r--r--coreutils/fold.c2
-rw-r--r--coreutils/head.c2
-rw-r--r--coreutils/mv.c2
-rw-r--r--coreutils/printf.c1
-rw-r--r--coreutils/realpath.c2
-rw-r--r--coreutils/sleep.c4
-rw-r--r--coreutils/sort.c1
-rw-r--r--coreutils/tail.c2
-rw-r--r--coreutils/touch.c139
-rw-r--r--coreutils/truncate.c4
-rw-r--r--coreutils/wc.c2
-rw-r--r--editors/vi.c1503
-rw-r--r--include/libbb.h25
-rw-r--r--include/platform.h5
-rw-r--r--klibc-utils/resume.c9
-rw-r--r--libbb/appletlib.c30
-rw-r--r--libbb/duration.c14
-rw-r--r--libbb/lineedit.c4
-rw-r--r--libbb/xfuncs_printf.c2
-rw-r--r--loginutils/login.c16
-rw-r--r--miscutils/bc.c4
-rw-r--r--miscutils/dc.c1
-rw-r--r--miscutils/time.c7
-rw-r--r--miscutils/watchdog.c26
-rw-r--r--networking/brctl.c1
-rw-r--r--networking/httpd.c305
-rw-r--r--networking/inetd.c2
-rw-r--r--networking/ntpd.c109
-rw-r--r--networking/tls.c120
-rw-r--r--networking/tls.h13
-rw-r--r--networking/tls_fe.c89
-rw-r--r--networking/tls_fe.h7
-rw-r--r--networking/tls_pstm.c8
-rw-r--r--networking/tls_pstm_montgomery_reduce.c30
-rw-r--r--networking/tls_rsa.c4
-rw-r--r--networking/tls_sp_c32.c927
-rw-r--r--networking/udhcp/common.c26
-rw-r--r--networking/udhcp/d6_dhcpc.c122
-rw-r--r--networking/udhcp/dhcpc.c170
-rw-r--r--networking/udhcp/signalpipe.c2
-rw-r--r--networking/wget.c52
-rw-r--r--scripts/basic/docproc.c18
-rw-r--r--scripts/basic/fixdep.c12
-rw-r--r--scripts/basic/split-include.c4
-rwxr-xr-xscripts/gcc-version.sh4
-rw-r--r--scripts/kconfig/conf.c6
-rw-r--r--scripts/kconfig/confdata.c2
-rw-r--r--scripts/kconfig/mconf.c2
-rw-r--r--selinux/chcon.c2
-rw-r--r--shell/hush.c12
-rw-r--r--testsuite/dd/dd-count-bytes1
-rwxr-xr-xtestsuite/start-stop-daemon.tests5
63 files changed, 2730 insertions, 1260 deletions
diff --git a/Makefile.flags b/Makefile.flags
index dfdd957f3..0950ddb0d 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -153,18 +153,21 @@ LDLIBS += ws2_32 mingwex -l:libssp.a
153endif 153endif
154 154
155ifneq ($(CONFIG_PLATFORM_MINGW32),y) 155ifneq ($(CONFIG_PLATFORM_MINGW32),y)
156# libm may be needed for dc, awk, ntpd
157LDLIBS += m
156# Android has no separate crypt library 158# Android has no separate crypt library
157# gcc-4.2.1 fails if we try to feed C source on stdin: 159# gcc-4.2.1 fails if we try to feed C source on stdin:
158# echo 'int main(void){return 0;}' | $(CC) $(CFLAGS) -lcrypt -o /dev/null -xc - 160# echo 'int main(void){return 0;}' | $(CC) $(CFLAGS) -lcrypt -o /dev/null -xc -
159# fall back to using a temp file: 161# fall back to using a temp file:
160CRYPT_AVAILABLE := $(shell echo 'int main(void){return 0;}' >crypttest.c; $(CC) $(CFLAGS) -lcrypt -o /dev/null crypttest.c >/dev/null 2>&1 && echo "y"; rm crypttest.c) 162CRYPT_AVAILABLE := $(shell echo 'int main(void){return 0;}' >bb_libtest.c; $(CC) $(CFLAGS) -lcrypt -o /dev/null bb_libtest.c >/dev/null 2>&1 && echo "y"; rm bb_libtest.c)
163RT_AVAILABLE := $(shell echo 'int main(void){return 0;}' >bb_libtest.c; $(CC) $(CFLAGS) -lrt -o /dev/null bb_libtest.c >/dev/null 2>&1 && echo "y"; rm bb_libtest.c)
161ifeq ($(CRYPT_AVAILABLE),y) 164ifeq ($(CRYPT_AVAILABLE),y)
162LDLIBS += m rt crypt 165LDLIBS += crypt
163else
164LDLIBS += m rt
165endif 166endif
166# libm may be needed for dc, awk, ntpd
167# librt may be needed for clock_gettime() 167# librt may be needed for clock_gettime()
168ifeq ($(RT_AVAILABLE),y)
169LDLIBS += rt
170endif
168endif 171endif
169 172
170# libpam may use libpthread, libdl and/or libaudit. 173# libpam may use libpthread, libdl and/or libaudit.
diff --git a/applets/applet_tables.c b/applets/applet_tables.c
index b1dafccd2..fe26a5109 100644
--- a/applets/applet_tables.c
+++ b/applets/applet_tables.c
@@ -56,7 +56,7 @@ static int cmp_name(const void *a, const void *b)
56static int str_isalnum_(const char *s) 56static int str_isalnum_(const char *s)
57{ 57{
58 while (*s) { 58 while (*s) {
59 if (!isalnum(*s) && *s != '_') 59 if (!isalnum((unsigned char)*s) && *s != '_')
60 return 0; 60 return 0;
61 s++; 61 s++;
62 } 62 }
diff --git a/archival/libarchive/decompress_gunzip.c b/archival/libarchive/decompress_gunzip.c
index b2a3eb1c2..d2f7a9309 100644
--- a/archival/libarchive/decompress_gunzip.c
+++ b/archival/libarchive/decompress_gunzip.c
@@ -220,10 +220,19 @@ static const uint8_t border[] ALIGN1 = {
220 * each table. 220 * each table.
221 * t: table to free 221 * t: table to free
222 */ 222 */
223#define BAD_HUFT(p) ((uintptr_t)(p) & 1)
224#define ERR_RET ((huft_t*)(uintptr_t)1)
223static void huft_free(huft_t *p) 225static void huft_free(huft_t *p)
224{ 226{
225 huft_t *q; 227 huft_t *q;
226 228
229 /*
230 * If 'p' has the error bit set we have to clear it, otherwise we might run
231 * into a segmentation fault or an invalid pointer to free(p)
232 */
233 //if (BAD_HUFT(p)) // commented out, since bit clearing has effect only if condition is true
234 p = (huft_t*)((uintptr_t)p & ~(uintptr_t)ERR_RET);
235
227 /* Go through linked list, freeing from the malloced (t[-1]) address. */ 236 /* Go through linked list, freeing from the malloced (t[-1]) address. */
228 while (p) { 237 while (p) {
229 q = (--p)->v.t; 238 q = (--p)->v.t;
@@ -289,8 +298,6 @@ static unsigned fill_bitbuffer(STATE_PARAM unsigned bitbuffer, unsigned *current
289 * or a valid pointer to a Huffman table, ORed with 0x1 if incompete table 298 * or a valid pointer to a Huffman table, ORed with 0x1 if incompete table
290 * is given: "fixed inflate" decoder feeds us such data. 299 * is given: "fixed inflate" decoder feeds us such data.
291 */ 300 */
292#define BAD_HUFT(p) ((uintptr_t)(p) & 1)
293#define ERR_RET ((huft_t*)(uintptr_t)1)
294static huft_t* huft_build(const unsigned *b, const unsigned n, 301static huft_t* huft_build(const unsigned *b, const unsigned n,
295 const unsigned s, const struct cp_ext *cp_ext, 302 const unsigned s, const struct cp_ext *cp_ext,
296 unsigned *m) 303 unsigned *m)
diff --git a/archival/unzip.c b/archival/unzip.c
index 19353c708..e02cf33e3 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -64,6 +64,7 @@
64//usage: "\n -o Overwrite" 64//usage: "\n -o Overwrite"
65//usage: "\n -j Do not restore paths" 65//usage: "\n -j Do not restore paths"
66//usage: "\n -p Print to stdout" 66//usage: "\n -p Print to stdout"
67//usage: "\n -t Test"
67//usage: "\n -q Quiet" 68//usage: "\n -q Quiet"
68//usage: "\n -x FILE Exclude FILEs" 69//usage: "\n -x FILE Exclude FILEs"
69//usage: "\n -d DIR Extract into DIR" 70//usage: "\n -d DIR Extract into DIR"
@@ -85,11 +86,13 @@ enum {
85 ZIP_FILEHEADER_MAGIC = 0x504b0304, 86 ZIP_FILEHEADER_MAGIC = 0x504b0304,
86 ZIP_CDF_MAGIC = 0x504b0102, /* CDF item */ 87 ZIP_CDF_MAGIC = 0x504b0102, /* CDF item */
87 ZIP_CDE_MAGIC = 0x504b0506, /* End of CDF */ 88 ZIP_CDE_MAGIC = 0x504b0506, /* End of CDF */
89 ZIP64_CDE_MAGIC = 0x504b0606, /* End of Zip64 CDF */
88 ZIP_DD_MAGIC = 0x504b0708, 90 ZIP_DD_MAGIC = 0x504b0708,
89#else 91#else
90 ZIP_FILEHEADER_MAGIC = 0x04034b50, 92 ZIP_FILEHEADER_MAGIC = 0x04034b50,
91 ZIP_CDF_MAGIC = 0x02014b50, 93 ZIP_CDF_MAGIC = 0x02014b50,
92 ZIP_CDE_MAGIC = 0x06054b50, 94 ZIP_CDE_MAGIC = 0x06054b50,
95 ZIP64_CDE_MAGIC = 0x06064b50,
93 ZIP_DD_MAGIC = 0x08074b50, 96 ZIP_DD_MAGIC = 0x08074b50,
94#endif 97#endif
95}; 98};
@@ -263,6 +266,12 @@ static uint32_t find_cdf_offset(void)
263 continue; 266 continue;
264 /* we found CDE! */ 267 /* we found CDE! */
265 memcpy(cde.raw, p + 1, CDE_LEN); 268 memcpy(cde.raw, p + 1, CDE_LEN);
269 dbg("cde.this_disk_no:%d", cde.fmt.this_disk_no );
270 dbg("cde.disk_with_cdf_no:%d", cde.fmt.disk_with_cdf_no );
271 dbg("cde.cdf_entries_on_this_disk:%d", cde.fmt.cdf_entries_on_this_disk);
272 dbg("cde.cdf_entries_total:%d", cde.fmt.cdf_entries_total );
273 dbg("cde.cdf_size:%d", cde.fmt.cdf_size );
274 dbg("cde.cdf_offset:%x", cde.fmt.cdf_offset );
266 FIX_ENDIANNESS_CDE(cde); 275 FIX_ENDIANNESS_CDE(cde);
267 /* 276 /*
268 * I've seen .ZIP files with seemingly valid CDEs 277 * I've seen .ZIP files with seemingly valid CDEs
@@ -305,19 +314,27 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf)
305 dbg("got ZIP_CDE_MAGIC"); 314 dbg("got ZIP_CDE_MAGIC");
306 return 0; /* EOF */ 315 return 0; /* EOF */
307 } 316 }
317 if (magic == ZIP64_CDE_MAGIC) { /* seen in .zip with >4GB files */
318 dbg("got ZIP64_CDE_MAGIC");
319 return 0; /* EOF */
320 }
308 xread(zip_fd, cdf->raw, CDF_HEADER_LEN); 321 xread(zip_fd, cdf->raw, CDF_HEADER_LEN);
309 322
310 FIX_ENDIANNESS_CDF(*cdf); 323 FIX_ENDIANNESS_CDF(*cdf);
311 dbg(" filename_len:%u extra_len:%u file_comment_length:%u", 324 dbg(" magic:%08x filename_len:%u extra_len:%u file_comment_length:%u",
325 magic,
312 (unsigned)cdf->fmt.filename_len, 326 (unsigned)cdf->fmt.filename_len,
313 (unsigned)cdf->fmt.extra_len, 327 (unsigned)cdf->fmt.extra_len,
314 (unsigned)cdf->fmt.file_comment_length 328 (unsigned)cdf->fmt.file_comment_length
315 ); 329 );
330//TODO: require that magic == ZIP_CDF_MAGIC?
331
316 cdf_offset += 4 + CDF_HEADER_LEN 332 cdf_offset += 4 + CDF_HEADER_LEN
317 + cdf->fmt.filename_len 333 + cdf->fmt.filename_len
318 + cdf->fmt.extra_len 334 + cdf->fmt.extra_len
319 + cdf->fmt.file_comment_length; 335 + cdf->fmt.file_comment_length;
320 336
337 dbg("Next cdf_offset 0x%x", cdf_offset);
321 return cdf_offset; 338 return cdf_offset;
322}; 339};
323#endif 340#endif
@@ -439,7 +456,9 @@ static void unzip_extract(zip_header_t *zip, int dst_fd)
439 } 456 }
440 457
441 /* Validate decompression - size */ 458 /* Validate decompression - size */
442 if (zip->fmt.ucmpsize != xstate.bytes_out) { 459 if (zip->fmt.ucmpsize != 0xffffffff /* seen on files with >4GB uncompressed data */
460 && zip->fmt.ucmpsize != xstate.bytes_out
461 ) {
443 /* Don't die. Who knows, maybe len calculation 462 /* Don't die. Who knows, maybe len calculation
444 * was botched somewhere. After all, crc matched! */ 463 * was botched somewhere. After all, crc matched! */
445 bb_simple_error_msg("bad length"); 464 bb_simple_error_msg("bad length");
@@ -541,7 +560,7 @@ int unzip_main(int argc, char **argv)
541 560
542 opts = 0; 561 opts = 0;
543 /* '-' makes getopt return 1 for non-options */ 562 /* '-' makes getopt return 1 for non-options */
544 while ((i = getopt(argc, argv, "-d:lnopqxjv")) != -1) { 563 while ((i = getopt(argc, argv, "-d:lnotpqxjv")) != -1) {
545 switch (i) { 564 switch (i) {
546 case 'd': /* Extract to base directory */ 565 case 'd': /* Extract to base directory */
547 base_dir = optarg; 566 base_dir = optarg;
@@ -559,8 +578,13 @@ int unzip_main(int argc, char **argv)
559 overwrite = O_ALWAYS; 578 overwrite = O_ALWAYS;
560 break; 579 break;
561 580
562 case 'p': /* Extract files to stdout and fall through to set verbosity */ 581 case 't': /* Extract files to /dev/null */
582 xmove_fd(xopen("/dev/null", O_WRONLY), STDOUT_FILENO);
583 /*fallthrough*/
584
585 case 'p': /* Extract files to stdout */
563 dst_fd = STDOUT_FILENO; 586 dst_fd = STDOUT_FILENO;
587 /*fallthrough*/
564 588
565 case 'q': /* Be quiet */ 589 case 'q': /* Be quiet */
566 quiet++; 590 quiet++;
@@ -969,7 +993,6 @@ int unzip_main(int argc, char **argv)
969 /* O_NOFOLLOW defends against symlink attacks */ 993 /* O_NOFOLLOW defends against symlink attacks */
970 dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW); 994 dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW);
971#endif 995#endif
972 do_extract:
973 if (!quiet) { 996 if (!quiet) {
974 printf(/* zip.fmt.method == 0 997 printf(/* zip.fmt.method == 0
975 ? " extracting: %s\n" 998 ? " extracting: %s\n"
@@ -977,6 +1000,7 @@ int unzip_main(int argc, char **argv)
977 printable_string(dst_fn) 1000 printable_string(dst_fn)
978 ); 1001 );
979 } 1002 }
1003 do_extract:
980#if ENABLE_FEATURE_UNZIP_CDF 1004#if ENABLE_FEATURE_UNZIP_CDF
981 if (S_ISLNK(file_mode)) { 1005 if (S_ISLNK(file_mode)) {
982 if (dst_fd != STDOUT_FILENO) /* not -p? */ 1006 if (dst_fd != STDOUT_FILENO) /* not -p? */
diff --git a/coreutils/chgrp.c b/coreutils/chgrp.c
index 4da43c45e..0c2060981 100644
--- a/coreutils/chgrp.c
+++ b/coreutils/chgrp.c
@@ -23,7 +23,7 @@
23//usage:#define chgrp_trivial_usage 23//usage:#define chgrp_trivial_usage
24//usage: "[-Rh"IF_DESKTOP("LHPcvf")"]... GROUP FILE..." 24//usage: "[-Rh"IF_DESKTOP("LHPcvf")"]... GROUP FILE..."
25//usage:#define chgrp_full_usage "\n\n" 25//usage:#define chgrp_full_usage "\n\n"
26//usage: "Change the group membership of each FILE to GROUP\n" 26//usage: "Change the group membership of FILEs to GROUP\n"
27//usage: "\n -R Recurse" 27//usage: "\n -R Recurse"
28//usage: "\n -h Affect symlinks instead of symlink targets" 28//usage: "\n -h Affect symlinks instead of symlink targets"
29//usage: IF_DESKTOP( 29//usage: IF_DESKTOP(
diff --git a/coreutils/chown.c b/coreutils/chown.c
index ffccc6cce..170507147 100644
--- a/coreutils/chown.c
+++ b/coreutils/chown.c
@@ -28,7 +28,7 @@
28//usage:#define chown_trivial_usage 28//usage:#define chown_trivial_usage
29//usage: "[-Rh"IF_DESKTOP("LHPcvf")"]... USER[:[GRP]] FILE..." 29//usage: "[-Rh"IF_DESKTOP("LHPcvf")"]... USER[:[GRP]] FILE..."
30//usage:#define chown_full_usage "\n\n" 30//usage:#define chown_full_usage "\n\n"
31//usage: "Change the owner and/or group of each FILE to USER and/or GRP\n" 31//usage: "Change the owner and/or group of FILEs to USER and/or GRP\n"
32//usage: "\n -R Recurse" 32//usage: "\n -R Recurse"
33//usage: "\n -h Affect symlinks instead of symlink targets" 33//usage: "\n -h Affect symlinks instead of symlink targets"
34//usage: IF_DESKTOP( 34//usage: IF_DESKTOP(
diff --git a/coreutils/cp.c b/coreutils/cp.c
index 9b9b8f7bf..f92ba6886 100644
--- a/coreutils/cp.c
+++ b/coreutils/cp.c
@@ -40,7 +40,7 @@
40//usage:#define cp_trivial_usage 40//usage:#define cp_trivial_usage
41//usage: "[-arPLHpfilsTu] SOURCE... DEST" 41//usage: "[-arPLHpfilsTu] SOURCE... DEST"
42//usage:#define cp_full_usage "\n\n" 42//usage:#define cp_full_usage "\n\n"
43//usage: "Copy SOURCE(s) to DEST\n" 43//usage: "Copy SOURCEs to DEST\n"
44//usage: "\n -a Same as -dpR" 44//usage: "\n -a Same as -dpR"
45//usage: IF_SELINUX( 45//usage: IF_SELINUX(
46//usage: "\n -c Preserve security context" 46//usage: "\n -c Preserve security context"
diff --git a/coreutils/cut.c b/coreutils/cut.c
index 16418ff33..5897d82b6 100644
--- a/coreutils/cut.c
+++ b/coreutils/cut.c
@@ -22,12 +22,12 @@
22//usage:#define cut_trivial_usage 22//usage:#define cut_trivial_usage
23//usage: "[OPTIONS] [FILE]..." 23//usage: "[OPTIONS] [FILE]..."
24//usage:#define cut_full_usage "\n\n" 24//usage:#define cut_full_usage "\n\n"
25//usage: "Print selected fields from each input FILE to stdout\n" 25//usage: "Print selected fields from FILEs to stdout\n"
26//usage: "\n -b LIST Output only bytes from LIST" 26//usage: "\n -b LIST Output only bytes from LIST"
27//usage: "\n -c LIST Output only characters from LIST" 27//usage: "\n -c LIST Output only characters from LIST"
28//usage: "\n -d CHAR Use CHAR instead of tab as the field delimiter" 28//usage: "\n -d CHAR Use CHAR instead of tab as field delimiter"
29//usage: "\n -s Output only the lines containing delimiter" 29//usage: "\n -s Output only lines containing delimiter"
30//usage: "\n -f N Print only these fields" 30//usage: "\n -f LIST Print only these fields"
31//usage: "\n -n Ignored" 31//usage: "\n -n Ignored"
32//usage: 32//usage:
33//usage:#define cut_example_usage 33//usage:#define cut_example_usage
diff --git a/coreutils/dd.c b/coreutils/dd.c
index bd799aa2b..15938f689 100644
--- a/coreutils/dd.c
+++ b/coreutils/dd.c
@@ -59,7 +59,7 @@
59//usage: "[if=FILE] [of=FILE] [" IF_FEATURE_DD_IBS_OBS("ibs=N obs=N/") "bs=N] [count=N] [skip=N] [seek=N]\n" 59//usage: "[if=FILE] [of=FILE] [" IF_FEATURE_DD_IBS_OBS("ibs=N obs=N/") "bs=N] [count=N] [skip=N] [seek=N]\n"
60//usage: IF_FEATURE_DD_IBS_OBS( 60//usage: IF_FEATURE_DD_IBS_OBS(
61//usage: " [conv=notrunc|noerror|sync|fsync]\n" 61//usage: " [conv=notrunc|noerror|sync|fsync]\n"
62//usage: " [iflag=skip_bytes|fullblock|direct] [oflag=seek_bytes|append|direct]" 62//usage: " [iflag=skip_bytes|count_bytes|fullblock|direct] [oflag=seek_bytes|append|direct]"
63//usage: ) 63//usage: )
64//usage:#define dd_full_usage "\n\n" 64//usage:#define dd_full_usage "\n\n"
65//usage: "Copy a file with converting and formatting\n" 65//usage: "Copy a file with converting and formatting\n"
@@ -82,6 +82,7 @@
82//usage: "\n conv=fsync Physically write data out before finishing" 82//usage: "\n conv=fsync Physically write data out before finishing"
83//usage: "\n conv=swab Swap every pair of bytes" 83//usage: "\n conv=swab Swap every pair of bytes"
84//usage: "\n iflag=skip_bytes skip=N is in bytes" 84//usage: "\n iflag=skip_bytes skip=N is in bytes"
85//usage: "\n iflag=count_bytes count=N is in bytes"
85//usage: "\n oflag=seek_bytes seek=N is in bytes" 86//usage: "\n oflag=seek_bytes seek=N is in bytes"
86//usage: "\n iflag=direct O_DIRECT input" 87//usage: "\n iflag=direct O_DIRECT input"
87//usage: "\n oflag=direct O_DIRECT output" 88//usage: "\n oflag=direct O_DIRECT output"
@@ -139,21 +140,22 @@ enum {
139 FLAG_SWAB = (1 << 4) * ENABLE_FEATURE_DD_IBS_OBS, 140 FLAG_SWAB = (1 << 4) * ENABLE_FEATURE_DD_IBS_OBS,
140 /* end of conv flags */ 141 /* end of conv flags */
141 /* start of input flags */ 142 /* start of input flags */
142 FLAG_IFLAG_SHIFT = 5, 143 FLAG_IFLAG_SHIFT = 5,
143 FLAG_SKIP_BYTES = (1 << 5) * ENABLE_FEATURE_DD_IBS_OBS, 144 FLAG_SKIP_BYTES = (1 << 5) * ENABLE_FEATURE_DD_IBS_OBS,
144 FLAG_FULLBLOCK = (1 << 6) * ENABLE_FEATURE_DD_IBS_OBS, 145 FLAG_COUNT_BYTES = (1 << 6) * ENABLE_FEATURE_DD_IBS_OBS,
145 FLAG_IDIRECT = (1 << 7) * ENABLE_FEATURE_DD_IBS_OBS, 146 FLAG_FULLBLOCK = (1 << 7) * ENABLE_FEATURE_DD_IBS_OBS,
147 FLAG_IDIRECT = (1 << 8) * ENABLE_FEATURE_DD_IBS_OBS,
146 /* end of input flags */ 148 /* end of input flags */
147 /* start of output flags */ 149 /* start of output flags */
148 FLAG_OFLAG_SHIFT = 8, 150 FLAG_OFLAG_SHIFT = 9,
149 FLAG_SEEK_BYTES = (1 << 8) * ENABLE_FEATURE_DD_IBS_OBS, 151 FLAG_SEEK_BYTES = (1 << 9) * ENABLE_FEATURE_DD_IBS_OBS,
150 FLAG_APPEND = (1 << 9) * ENABLE_FEATURE_DD_IBS_OBS, 152 FLAG_APPEND = (1 << 10) * ENABLE_FEATURE_DD_IBS_OBS,
151 FLAG_ODIRECT = (1 << 10) * ENABLE_FEATURE_DD_IBS_OBS, 153 FLAG_ODIRECT = (1 << 11) * ENABLE_FEATURE_DD_IBS_OBS,
152 /* end of output flags */ 154 /* end of output flags */
153 FLAG_TWOBUFS = (1 << 11) * ENABLE_FEATURE_DD_IBS_OBS, 155 FLAG_TWOBUFS = (1 << 12) * ENABLE_FEATURE_DD_IBS_OBS,
154 FLAG_COUNT = 1 << 12, 156 FLAG_COUNT = 1 << 13,
155 FLAG_STATUS_NONE = 1 << 13, 157 FLAG_STATUS_NONE = 1 << 14,
156 FLAG_STATUS_NOXFER = 1 << 14, 158 FLAG_STATUS_NOXFER = 1 << 15,
157}; 159};
158 160
159static void dd_output_status(int UNUSED_PARAM cur_signal) 161static void dd_output_status(int UNUSED_PARAM cur_signal)
@@ -178,8 +180,9 @@ static void dd_output_status(int UNUSED_PARAM cur_signal)
178 //So far we react to it (we print the stats), 180 //So far we react to it (we print the stats),
179 //status=none only suppresses final, non-USR1 generated status message. 181 //status=none only suppresses final, non-USR1 generated status message.
180# endif 182# endif
181 fprintf(stderr, "%llu bytes (%sB) copied, ", 183 fprintf(stderr, /*G.total_bytes < 1024
182 G.total_bytes, 184 ? "%llu bytes copied, " : */ "%llu bytes (%sB) copied, "
185 , G.total_bytes,
183 /* show fractional digit, use suffixes */ 186 /* show fractional digit, use suffixes */
184 make_human_readable_str(G.total_bytes, 1, 0) 187 make_human_readable_str(G.total_bytes, 1, 0)
185 ); 188 );
@@ -322,7 +325,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
322 static const char conv_words[] ALIGN1 = 325 static const char conv_words[] ALIGN1 =
323 "notrunc\0""sync\0""noerror\0""fsync\0""swab\0"; 326 "notrunc\0""sync\0""noerror\0""fsync\0""swab\0";
324 static const char iflag_words[] ALIGN1 = 327 static const char iflag_words[] ALIGN1 =
325 "skip_bytes\0""fullblock\0""direct\0"; 328 "skip_bytes\0""count_bytes\0""fullblock\0""direct\0";
326 static const char oflag_words[] ALIGN1 = 329 static const char oflag_words[] ALIGN1 =
327 "seek_bytes\0append\0""direct\0"; 330 "seek_bytes\0append\0""direct\0";
328#endif 331#endif
@@ -364,6 +367,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
364 /* Partially implemented: */ 367 /* Partially implemented: */
365 //swab swap every pair of input bytes: will abort on non-even reads 368 //swab swap every pair of input bytes: will abort on non-even reads
366 OP_iflag_skip_bytes, 369 OP_iflag_skip_bytes,
370 OP_iflag_count_bytes,
367 OP_iflag_fullblock, 371 OP_iflag_fullblock,
368 OP_iflag_direct, 372 OP_iflag_direct,
369 OP_oflag_seek_bytes, 373 OP_oflag_seek_bytes,
@@ -581,8 +585,17 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
581 goto die_outfile; 585 goto die_outfile;
582 } 586 }
583 587
584 while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { 588 while (1) {
585 ssize_t n = dd_read(ibuf, ibs); 589 ssize_t n = ibs;
590
591 if (G.flags & FLAG_COUNT) {
592 if (count == 0)
593 break;
594 if ((G.flags & FLAG_COUNT_BYTES) && count < ibs)
595 n = count;
596 }
597
598 n = dd_read(ibuf, n);
586 if (n == 0) 599 if (n == 0)
587 break; 600 break;
588 if (n < 0) { 601 if (n < 0) {
@@ -617,6 +630,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
617 p16++; 630 p16++;
618 } 631 }
619 } 632 }
633 count -= (G.flags & FLAG_COUNT_BYTES) ? n : 1;
620 if ((size_t)n == ibs) 634 if ((size_t)n == ibs)
621 G.in_full++; 635 G.in_full++;
622 else { 636 else {
diff --git a/coreutils/du.c b/coreutils/du.c
index 4fd09a8ee..247a08c95 100644
--- a/coreutils/du.c
+++ b/coreutils/du.c
@@ -40,7 +40,7 @@
40//usage:#define du_trivial_usage 40//usage:#define du_trivial_usage
41//usage: "[-aHLdclsx" IF_FEATURE_HUMAN_READABLE("hm") "k] [FILE]..." 41//usage: "[-aHLdclsx" IF_FEATURE_HUMAN_READABLE("hm") "k] [FILE]..."
42//usage:#define du_full_usage "\n\n" 42//usage:#define du_full_usage "\n\n"
43//usage: "Summarize disk space used for each FILE and/or directory\n" 43//usage: "Summarize disk space used for FILEs (or directories)\n"
44//usage: "\n -a Show file sizes too" 44//usage: "\n -a Show file sizes too"
45//usage: "\n -L Follow all symlinks" 45//usage: "\n -L Follow all symlinks"
46//usage: "\n -H Follow symlinks on command line" 46//usage: "\n -H Follow symlinks on command line"
diff --git a/coreutils/echo.c b/coreutils/echo.c
index 61ba060ec..aab177cee 100644
--- a/coreutils/echo.c
+++ b/coreutils/echo.c
@@ -87,6 +87,7 @@ int echo_main(int argc UNUSED_PARAM, char **argv)
87 char *out; 87 char *out;
88 char *buffer; 88 char *buffer;
89 unsigned buflen; 89 unsigned buflen;
90 int err;
90#if !ENABLE_FEATURE_FANCY_ECHO 91#if !ENABLE_FEATURE_FANCY_ECHO
91 enum { 92 enum {
92 eflag = 0, /* 0 -- disable escape sequences */ 93 eflag = 0, /* 0 -- disable escape sequences */
@@ -97,7 +98,6 @@ int echo_main(int argc UNUSED_PARAM, char **argv)
97#else 98#else
98 char nflag = 1; 99 char nflag = 1;
99 char eflag = 0; 100 char eflag = 0;
100 int err;
101 101
102 while ((arg = *++argv) != NULL) { 102 while ((arg = *++argv) != NULL) {
103 char n, e; 103 char n, e;
diff --git a/coreutils/fold.c b/coreutils/fold.c
index 1e26dde0c..98c3b1491 100644
--- a/coreutils/fold.c
+++ b/coreutils/fold.c
@@ -23,7 +23,7 @@
23//usage:#define fold_trivial_usage 23//usage:#define fold_trivial_usage
24//usage: "[-bs] [-w WIDTH] [FILE]..." 24//usage: "[-bs] [-w WIDTH] [FILE]..."
25//usage:#define fold_full_usage "\n\n" 25//usage:#define fold_full_usage "\n\n"
26//usage: "Wrap input lines in each FILE (or stdin), writing to stdout\n" 26//usage: "Wrap input lines in FILEs (or stdin), writing to stdout\n"
27//usage: "\n -b Count bytes rather than columns" 27//usage: "\n -b Count bytes rather than columns"
28//usage: "\n -s Break at spaces" 28//usage: "\n -s Break at spaces"
29//usage: "\n -w Use WIDTH columns instead of 80" 29//usage: "\n -w Use WIDTH columns instead of 80"
diff --git a/coreutils/head.c b/coreutils/head.c
index b6efabbe0..efb023c6f 100644
--- a/coreutils/head.c
+++ b/coreutils/head.c
@@ -29,7 +29,7 @@
29//usage:#define head_trivial_usage 29//usage:#define head_trivial_usage
30//usage: "[OPTIONS] [FILE]..." 30//usage: "[OPTIONS] [FILE]..."
31//usage:#define head_full_usage "\n\n" 31//usage:#define head_full_usage "\n\n"
32//usage: "Print first 10 lines of each FILE (or stdin) to stdout.\n" 32//usage: "Print first 10 lines of FILEs (or stdin) to stdout.\n"
33//usage: "With more than one FILE, precede each with a filename header.\n" 33//usage: "With more than one FILE, precede each with a filename header.\n"
34//usage: "\n -n N[kbm] Print first N lines" 34//usage: "\n -n N[kbm] Print first N lines"
35//usage: IF_FEATURE_FANCY_HEAD( 35//usage: IF_FEATURE_FANCY_HEAD(
diff --git a/coreutils/mv.c b/coreutils/mv.c
index b9f8f6982..f5ed9fcfc 100644
--- a/coreutils/mv.c
+++ b/coreutils/mv.c
@@ -26,7 +26,7 @@
26//usage: "[-fin] SOURCE DEST\n" 26//usage: "[-fin] SOURCE DEST\n"
27//usage: "or: mv [-fin] SOURCE... DIRECTORY" 27//usage: "or: mv [-fin] SOURCE... DIRECTORY"
28//usage:#define mv_full_usage "\n\n" 28//usage:#define mv_full_usage "\n\n"
29//usage: "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY\n" 29//usage: "Rename SOURCE to DEST, or move SOURCEs to DIRECTORY\n"
30//usage: "\n -f Don't prompt before overwriting" 30//usage: "\n -f Don't prompt before overwriting"
31//usage: "\n -i Interactive, prompt before overwrite" 31//usage: "\n -i Interactive, prompt before overwrite"
32//usage: "\n -n Don't overwrite an existing file" 32//usage: "\n -n Don't overwrite an existing file"
diff --git a/coreutils/printf.c b/coreutils/printf.c
index 7d36fdd61..b5b1c6576 100644
--- a/coreutils/printf.c
+++ b/coreutils/printf.c
@@ -122,6 +122,7 @@ static void FAST_FUNC conv_strtod(const char *arg, void *result)
122 char *end; 122 char *end;
123 /* Well, this one allows leading whitespace... so what? */ 123 /* Well, this one allows leading whitespace... so what? */
124 /* What I like much less is that "-" accepted too! :( */ 124 /* What I like much less is that "-" accepted too! :( */
125//TODO: needs setlocale(LC_NUMERIC, "C")?
125 *(double*)result = strtod(arg, &end); 126 *(double*)result = strtod(arg, &end);
126 if (end[0]) { 127 if (end[0]) {
127 errno = ERANGE; 128 errno = ERANGE;
diff --git a/coreutils/realpath.c b/coreutils/realpath.c
index f5f868744..aeeef601c 100644
--- a/coreutils/realpath.c
+++ b/coreutils/realpath.c
@@ -23,7 +23,7 @@
23//usage:#define realpath_trivial_usage 23//usage:#define realpath_trivial_usage
24//usage: "FILE..." 24//usage: "FILE..."
25//usage:#define realpath_full_usage "\n\n" 25//usage:#define realpath_full_usage "\n\n"
26//usage: "Return the absolute pathnames of given FILE" 26//usage: "Print absolute pathnames of FILEs"
27 27
28#include "libbb.h" 28#include "libbb.h"
29 29
diff --git a/coreutils/sleep.c b/coreutils/sleep.c
index 7bfaab920..2658e84df 100644
--- a/coreutils/sleep.c
+++ b/coreutils/sleep.c
@@ -74,10 +74,6 @@ int sleep_main(int argc UNUSED_PARAM, char **argv)
74 sleep(INT_MAX); 74 sleep(INT_MAX);
75 75
76#if ENABLE_FEATURE_FANCY_SLEEP 76#if ENABLE_FEATURE_FANCY_SLEEP
77# if ENABLE_FLOAT_DURATION
78 /* undo busybox.c setlocale */
79 setlocale(LC_NUMERIC, "C");
80# endif
81 duration = 0; 77 duration = 0;
82 do { 78 do {
83 duration += parse_duration_str(*argv); 79 duration += parse_duration_str(*argv);
diff --git a/coreutils/sort.c b/coreutils/sort.c
index b194847d1..6c4e3038c 100644
--- a/coreutils/sort.c
+++ b/coreutils/sort.c
@@ -295,6 +295,7 @@ static int compare_keys(const void *xarg, const void *yarg)
295#if ENABLE_FEATURE_SORT_BIG 295#if ENABLE_FEATURE_SORT_BIG
296 case FLAG_g: { 296 case FLAG_g: {
297 char *xx, *yy; 297 char *xx, *yy;
298//TODO: needs setlocale(LC_NUMERIC, "C")?
298 double dx = strtod(x, &xx); 299 double dx = strtod(x, &xx);
299 double dy = strtod(y, &yy); 300 double dy = strtod(y, &yy);
300 /* not numbers < NaN < -infinity < numbers < +infinity) */ 301 /* not numbers < NaN < -infinity < numbers < +infinity) */
diff --git a/coreutils/tail.c b/coreutils/tail.c
index 1f458f9ed..08fde6cdd 100644
--- a/coreutils/tail.c
+++ b/coreutils/tail.c
@@ -48,7 +48,7 @@
48//usage:#define tail_trivial_usage 48//usage:#define tail_trivial_usage
49//usage: "[OPTIONS] [FILE]..." 49//usage: "[OPTIONS] [FILE]..."
50//usage:#define tail_full_usage "\n\n" 50//usage:#define tail_full_usage "\n\n"
51//usage: "Print last 10 lines of each FILE (or stdin) to stdout.\n" 51//usage: "Print last 10 lines of FILEs (or stdin) to stdout.\n"
52//usage: "With more than one FILE, precede each with a filename header.\n" 52//usage: "With more than one FILE, precede each with a filename header.\n"
53//usage: "\n -f Print data as file grows" 53//usage: "\n -f Print data as file grows"
54//usage: "\n -c [+]N[kbm] Print last N bytes" 54//usage: "\n -c [+]N[kbm] Print last N bytes"
diff --git a/coreutils/touch.c b/coreutils/touch.c
index b30811157..2b225dd16 100644
--- a/coreutils/touch.c
+++ b/coreutils/touch.c
@@ -19,16 +19,8 @@
19//config: touch is used to create or change the access and/or 19//config: touch is used to create or change the access and/or
20//config: modification timestamp of specified files. 20//config: modification timestamp of specified files.
21//config: 21//config:
22//config:config FEATURE_TOUCH_NODEREF
23//config: bool "Add support for -h"
24//config: default y
25//config: depends on TOUCH
26//config: help
27//config: Enable touch to have the -h option.
28//config: This requires libc support for lutimes() function.
29//config:
30//config:config FEATURE_TOUCH_SUSV3 22//config:config FEATURE_TOUCH_SUSV3
31//config: bool "Add support for SUSV3 features (-d -t -r)" 23//config: bool "Add support for SUSV3 features (-a -d -m -t -r)"
32//config: default y 24//config: default y
33//config: depends on TOUCH 25//config: depends on TOUCH
34//config: help 26//config: help
@@ -38,18 +30,17 @@
38 30
39//kbuild:lib-$(CONFIG_TOUCH) += touch.o 31//kbuild:lib-$(CONFIG_TOUCH) += touch.o
40 32
41/* BB_AUDIT SUSv3 _NOT_ compliant -- options -a, -m not supported. */
42/* http://www.opengroup.org/onlinepubs/007904975/utilities/touch.html */
43
44//usage:#define touch_trivial_usage 33//usage:#define touch_trivial_usage
45//usage: "[-c]" IF_FEATURE_TOUCH_SUSV3(" [-d DATE] [-t DATE] [-r FILE]") " FILE..." 34//usage: "[-c" IF_FEATURE_TOUCH_SUSV3("am") "]"
35//usage: IF_FEATURE_TOUCH_SUSV3(" [-d DATE] [-t DATE] [-r FILE]")
36//usage: " FILE..."
46//usage:#define touch_full_usage "\n\n" 37//usage:#define touch_full_usage "\n\n"
47//usage: "Update the last-modified date on the given FILE[s]\n" 38//usage: "Update mtime of FILEs\n"
48//usage: "\n -c Don't create files" 39//usage: "\n -c Don't create files"
49//usage: IF_FEATURE_TOUCH_NODEREF(
50//usage: "\n -h Don't follow links" 40//usage: "\n -h Don't follow links"
51//usage: )
52//usage: IF_FEATURE_TOUCH_SUSV3( 41//usage: IF_FEATURE_TOUCH_SUSV3(
42//usage: "\n -a Change only atime"
43//usage: "\n -m Change only mtime"
53//usage: "\n -d DT Date/time to use" 44//usage: "\n -d DT Date/time to use"
54//usage: "\n -t DT Date/time to use" 45//usage: "\n -t DT Date/time to use"
55//usage: "\n -r FILE Use FILE's date/time" 46//usage: "\n -r FILE Use FILE's date/time"
@@ -85,70 +76,67 @@ int touch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
85int touch_main(int argc UNUSED_PARAM, char **argv) 76int touch_main(int argc UNUSED_PARAM, char **argv)
86{ 77{
87 int fd; 78 int fd;
88 int status = EXIT_SUCCESS;
89 int opts; 79 int opts;
80 smalluint status = EXIT_SUCCESS;
81#if ENABLE_FEATURE_TOUCH_SUSV3
82 char *reference_file;
83 char *date_str;
84 /* timebuf[0] is atime, timebuf[1] is mtime */
85 struct timespec timebuf[2];
86#else
87# define reference_file NULL
88# define date_str NULL
89# define timebuf ((struct timespec*)NULL)
90#endif
91
90 enum { 92 enum {
91 OPT_c = (1 << 0), 93 OPT_c = (1 << 0),
92 OPT_r = (1 << 1) * ENABLE_FEATURE_TOUCH_SUSV3, 94 OPT_h = (1 << 1),
93 OPT_d = (1 << 2) * ENABLE_FEATURE_TOUCH_SUSV3, 95 OPT_r = (1 << 2) * ENABLE_FEATURE_TOUCH_SUSV3,
94 OPT_t = (1 << 3) * ENABLE_FEATURE_TOUCH_SUSV3, 96 OPT_d = (1 << 3) * ENABLE_FEATURE_TOUCH_SUSV3,
95 OPT_h = (1 << 4) * ENABLE_FEATURE_TOUCH_NODEREF, 97 OPT_t = (1 << 4) * ENABLE_FEATURE_TOUCH_SUSV3,
98 OPT_a = (1 << 5) * ENABLE_FEATURE_TOUCH_SUSV3,
99 OPT_m = (1 << 6) * ENABLE_FEATURE_TOUCH_SUSV3,
96 }; 100 };
97#if ENABLE_FEATURE_TOUCH_SUSV3 101#if ENABLE_LONG_OPTS
98# if ENABLE_LONG_OPTS
99 static const char touch_longopts[] ALIGN1 = 102 static const char touch_longopts[] ALIGN1 =
100 /* name, has_arg, val */ 103 /* name, has_arg, val */
101 "no-create\0" No_argument "c" 104 "no-create\0" No_argument "c"
102 "reference\0" Required_argument "r" 105 "no-dereference\0" No_argument "h"
103 "date\0" Required_argument "d" 106 IF_FEATURE_TOUCH_SUSV3("reference\0" Required_argument "r")
104 IF_FEATURE_TOUCH_NODEREF("no-dereference\0" No_argument "h") 107 IF_FEATURE_TOUCH_SUSV3("date\0" Required_argument "d")
105 ; 108 ;
106# define GETOPT32 getopt32long
107# define LONGOPTS ,touch_longopts
108# else
109# define GETOPT32 getopt32
110# define LONGOPTS
111# endif
112 char *reference_file = NULL;
113 char *date_str = NULL;
114 struct timeval timebuf[2];
115 timebuf[1].tv_usec = timebuf[0].tv_usec = 0;
116#else
117# define reference_file NULL
118# define date_str NULL
119# define timebuf ((struct timeval*)NULL)
120# define GETOPT32 getopt32
121# define LONGOPTS
122#endif 109#endif
123
124 /* -d and -t both set time. In coreutils, 110 /* -d and -t both set time. In coreutils,
125 * accepted data format differs a bit between -d and -t. 111 * accepted data format differs a bit between -d and -t.
126 * We accept the same formats for both */ 112 * We accept the same formats for both
127 opts = GETOPT32(argv, "c" IF_FEATURE_TOUCH_SUSV3("r:d:t:") 113 */
128 IF_FEATURE_TOUCH_NODEREF("h") 114 opts = getopt32long(argv, "^"
129 /*ignored:*/ "fma" 115 "ch"
130 LONGOPTS 116 IF_FEATURE_TOUCH_SUSV3("r:d:t:am")
131 IF_FEATURE_TOUCH_SUSV3(, &reference_file) 117 /*ignored:*/ "f" IF_NOT_FEATURE_TOUCH_SUSV3("am")
132 IF_FEATURE_TOUCH_SUSV3(, &date_str) 118 "\0" /* opt_complementary: */
133 IF_FEATURE_TOUCH_SUSV3(, &date_str) 119 /* at least one arg: */ "-1"
120 /* coreutils forbids -r and -t at once: */ IF_FEATURE_TOUCH_SUSV3(":r--t:t--r")
121 /* but allows these combinations: "r--d:d--r:t--d:d--t" */
122 , touch_longopts
123#if ENABLE_FEATURE_TOUCH_SUSV3
124 , &reference_file
125 , &date_str
126 , &date_str
127#endif
134 ); 128 );
135 129
136 argv += optind; 130 timebuf[0].tv_nsec = timebuf[1].tv_nsec = UTIME_NOW;
137 if (!*argv) { 131 if (opts & OPT_r) {
138 bb_show_usage();
139 }
140
141 if (reference_file) {
142 struct stat stbuf; 132 struct stat stbuf;
143 xstat(reference_file, &stbuf); 133 xstat(reference_file, &stbuf);
144 timebuf[1].tv_sec = timebuf[0].tv_sec = stbuf.st_mtime; 134 timebuf[0].tv_sec = stbuf.st_atime;
145 /* Can use .st_mtim.tv_nsec 135 timebuf[1].tv_sec = stbuf.st_mtime;
146 * (or is it .st_mtimensec?? see date.c) 136 timebuf[0].tv_nsec = stbuf.st_atim.tv_nsec;
147 * to set microseconds too. 137 timebuf[1].tv_nsec = stbuf.st_mtim.tv_nsec;
148 */
149 } 138 }
150 139 if (opts & (OPT_d|OPT_t)) {
151 if (date_str) {
152 struct tm tm_time; 140 struct tm tm_time;
153 time_t t; 141 time_t t;
154 142
@@ -163,15 +151,20 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
163 t = validate_tm_time(date_str, &tm_time); 151 t = validate_tm_time(date_str, &tm_time);
164 152
165 timebuf[1].tv_sec = timebuf[0].tv_sec = t; 153 timebuf[1].tv_sec = timebuf[0].tv_sec = t;
154 timebuf[1].tv_nsec = timebuf[0].tv_nsec = 0;
166 } 155 }
156 /* If both -a and -m specified, both times should be set.
157 * IOW: set OMIT only if one, not both, of them is given!
158 */
159 if ((opts & (OPT_a|OPT_m)) == OPT_a)
160 timebuf[1].tv_nsec = UTIME_OMIT;
161 if ((opts & (OPT_a|OPT_m)) == OPT_m)
162 timebuf[0].tv_nsec = UTIME_OMIT;
167 163
164 argv += optind;
168 do { 165 do {
169 int result; 166 int result = utimensat(AT_FDCWD, *argv, timebuf,
170 result = ( 167 (opts & OPT_h) ? AT_SYMLINK_NOFOLLOW : 0);
171#if ENABLE_FEATURE_TOUCH_NODEREF
172 (opts & OPT_h) ? lutimes :
173#endif
174 utimes)(*argv, (reference_file || date_str) ? timebuf : NULL);
175 if (result != 0) { 168 if (result != 0) {
176 if (errno == ENOENT) { /* no such file? */ 169 if (errno == ENOENT) { /* no such file? */
177 if (opts & OPT_c) { 170 if (opts & OPT_c) {
@@ -181,9 +174,9 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
181 /* Try to create the file */ 174 /* Try to create the file */
182 fd = open(*argv, O_RDWR | O_CREAT, 0666); 175 fd = open(*argv, O_RDWR | O_CREAT, 0666);
183 if (fd >= 0) { 176 if (fd >= 0) {
177 if (opts & (OPT_r|OPT_d|OPT_t))
178 futimens(fd, timebuf);
184 xclose(fd); 179 xclose(fd);
185 if (reference_file || date_str)
186 utimes(*argv, timebuf);
187 continue; 180 continue;
188 } 181 }
189 } 182 }
diff --git a/coreutils/truncate.c b/coreutils/truncate.c
index 233d0f2d1..e26c3e3e1 100644
--- a/coreutils/truncate.c
+++ b/coreutils/truncate.c
@@ -19,9 +19,9 @@
19//usage:#define truncate_trivial_usage 19//usage:#define truncate_trivial_usage
20//usage: "[-c] -s SIZE FILE..." 20//usage: "[-c] -s SIZE FILE..."
21//usage:#define truncate_full_usage "\n\n" 21//usage:#define truncate_full_usage "\n\n"
22//usage: "Truncate FILEs to the given size\n" 22//usage: "Truncate FILEs to SIZE\n"
23//usage: "\n -c Do not create files" 23//usage: "\n -c Do not create files"
24//usage: "\n -s SIZE Truncate to SIZE" 24//usage: "\n -s SIZE"
25//usage: 25//usage:
26//usage:#define truncate_example_usage 26//usage:#define truncate_example_usage
27//usage: "$ truncate -s 1G foo" 27//usage: "$ truncate -s 1G foo"
diff --git a/coreutils/wc.c b/coreutils/wc.c
index 99eb9dc87..d5238d3fc 100644
--- a/coreutils/wc.c
+++ b/coreutils/wc.c
@@ -80,7 +80,7 @@
80//usage: "[-c"IF_UNICODE_SUPPORT("m")"lwL] [FILE]..." 80//usage: "[-c"IF_UNICODE_SUPPORT("m")"lwL] [FILE]..."
81//usage: 81//usage:
82//usage:#define wc_full_usage "\n\n" 82//usage:#define wc_full_usage "\n\n"
83//usage: "Count lines, words, and bytes for each FILE (or stdin)\n" 83//usage: "Count lines, words, and bytes for FILEs (or stdin)\n"
84//usage: "\n -c Count bytes" 84//usage: "\n -c Count bytes"
85//usage: IF_UNICODE_SUPPORT( 85//usage: IF_UNICODE_SUPPORT(
86//usage: "\n -m Count characters" 86//usage: "\n -m Count characters"
diff --git a/editors/vi.c b/editors/vi.c
index 8181a5384..3dcde9b24 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -7,7 +7,6 @@
7 */ 7 */
8// 8//
9//Things To Do: 9//Things To Do:
10// EXINIT
11// $HOME/.exrc and ./.exrc 10// $HOME/.exrc and ./.exrc
12// add magic to search /foo.*bar 11// add magic to search /foo.*bar
13// add :help command 12// add :help command
@@ -54,6 +53,14 @@
54//config: Enable a limited set of colon commands. This does not 53//config: Enable a limited set of colon commands. This does not
55//config: provide an "ex" mode. 54//config: provide an "ex" mode.
56//config: 55//config:
56//config:config FEATURE_VI_COLON_EXPAND
57//config: bool "Expand \"%\" and \"#\" in colon commands"
58//config: default y
59//config: depends on FEATURE_VI_COLON
60//config: help
61//config: Expand the special characters \"%\" (current filename)
62//config: and \"#\" (alternate filename) in colon commands.
63//config:
57//config:config FEATURE_VI_YANKMARK 64//config:config FEATURE_VI_YANKMARK
58//config: bool "Enable yank/put commands and mark cmds" 65//config: bool "Enable yank/put commands and mark cmds"
59//config: default y 66//config: default y
@@ -160,6 +167,14 @@
160//config: and will generally malloc() larger objects and less frequently. 167//config: and will generally malloc() larger objects and less frequently.
161//config: Unless you want more (or less) frequent "undo points" while typing, 168//config: Unless you want more (or less) frequent "undo points" while typing,
162//config: you should probably leave this unchanged. 169//config: you should probably leave this unchanged.
170//config:
171//config:config FEATURE_VI_VERBOSE_STATUS
172//config: bool "Enable verbose status reporting"
173//config: default y
174//config: depends on VI
175//config: help
176//config: Enable more verbose reporting of the results of yank, change,
177//config: delete, undo and substitution commands.
163 178
164//applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP)) 179//applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP))
165 180
@@ -242,7 +257,7 @@ enum {
242//#define ESC_CURSOR_UP ESC"[A" 257//#define ESC_CURSOR_UP ESC"[A"
243//#define ESC_CURSOR_DOWN "\n" 258//#define ESC_CURSOR_DOWN "\n"
244 259
245#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK 260#if ENABLE_FEATURE_VI_DOT_CMD
246// cmds modifying text[] 261// cmds modifying text[]
247static const char modifying_cmds[] ALIGN1 = "aAcCdDiIJoOpPrRs""xX<>~"; 262static const char modifying_cmds[] ALIGN1 = "aAcCdDiIJoOpPrRs""xX<>~";
248#endif 263#endif
@@ -254,12 +269,17 @@ enum {
254 BACK = -1, // code depends on "-1" for array index 269 BACK = -1, // code depends on "-1" for array index
255 LIMITED = 0, // char_search() only current line 270 LIMITED = 0, // char_search() only current line
256 FULL = 1, // char_search() to the end/beginning of entire text 271 FULL = 1, // char_search() to the end/beginning of entire text
272 PARTIAL = 0, // buffer contains partial line
273 WHOLE = 1, // buffer contains whole lines
274 MULTI = 2, // buffer may include newlines
257 275
258 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot" 276 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
259 S_TO_WS = 2, // used in skip_thing() for moving "dot" 277 S_TO_WS = 2, // used in skip_thing() for moving "dot"
260 S_OVER_WS = 3, // used in skip_thing() for moving "dot" 278 S_OVER_WS = 3, // used in skip_thing() for moving "dot"
261 S_END_PUNCT = 4, // used in skip_thing() for moving "dot" 279 S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
262 S_END_ALNUM = 5, // used in skip_thing() for moving "dot" 280 S_END_ALNUM = 5, // used in skip_thing() for moving "dot"
281
282 C_END = -1, // cursor is at end of line due to '$' command
263}; 283};
264 284
265 285
@@ -274,16 +294,38 @@ struct globals {
274 int text_size; // size of the allocated buffer 294 int text_size; // size of the allocated buffer
275 295
276 // the rest 296 // the rest
277 smallint vi_setops; 297#if ENABLE_FEATURE_VI_SETOPTS
278#define VI_AUTOINDENT 1 298 smallint vi_setops; // set by setops()
279#define VI_SHOWMATCH 2 299#define VI_AUTOINDENT (1 << 0)
280#define VI_IGNORECASE 4 300#define VI_EXPANDTAB (1 << 1)
281#define VI_ERR_METHOD 8 301#define VI_ERR_METHOD (1 << 2)
302#define VI_IGNORECASE (1 << 3)
303#define VI_SHOWMATCH (1 << 4)
304#define VI_TABSTOP (1 << 5)
282#define autoindent (vi_setops & VI_AUTOINDENT) 305#define autoindent (vi_setops & VI_AUTOINDENT)
283#define showmatch (vi_setops & VI_SHOWMATCH ) 306#define expandtab (vi_setops & VI_EXPANDTAB )
307#define err_method (vi_setops & VI_ERR_METHOD) // indicate error with beep or flash
284#define ignorecase (vi_setops & VI_IGNORECASE) 308#define ignorecase (vi_setops & VI_IGNORECASE)
285// indicate error with beep or flash 309#define showmatch (vi_setops & VI_SHOWMATCH )
286#define err_method (vi_setops & VI_ERR_METHOD) 310#define openabove (vi_setops & VI_TABSTOP )
311// order of constants and strings must match
312#define OPTS_STR \
313 "ai\0""autoindent\0" \
314 "et\0""expandtab\0" \
315 "fl\0""flash\0" \
316 "ic\0""ignorecase\0" \
317 "sm\0""showmatch\0" \
318 "ts\0""tabstop\0"
319#define set_openabove() (vi_setops |= VI_TABSTOP)
320#define clear_openabove() (vi_setops &= ~VI_TABSTOP)
321#else
322#define autoindent (0)
323#define expandtab (0)
324#define err_method (0)
325#define openabove (0)
326#define set_openabove() ((void)0)
327#define clear_openabove() ((void)0)
328#endif
287 329
288#if ENABLE_FEATURE_VI_READONLY 330#if ENABLE_FEATURE_VI_READONLY
289 smallint readonly_mode; 331 smallint readonly_mode;
@@ -313,19 +355,27 @@ struct globals {
313 // [don't make smallint!] 355 // [don't make smallint!]
314 int last_status_cksum; // hash of current status line 356 int last_status_cksum; // hash of current status line
315 char *current_filename; 357 char *current_filename;
358#if ENABLE_FEATURE_VI_COLON_EXPAND
359 char *alt_filename;
360#endif
316 char *screenbegin; // index into text[], of top line on the screen 361 char *screenbegin; // index into text[], of top line on the screen
317 char *screen; // pointer to the virtual screen buffer 362 char *screen; // pointer to the virtual screen buffer
318 int screensize; // and its size 363 int screensize; // and its size
319 int tabstop; 364 int tabstop;
320 int last_forward_char; // last char searched for with 'f' (int because of Unicode) 365 int last_search_char; // last char searched for (int because of Unicode)
366 smallint last_search_cmd; // command used to invoke last char search
321#if ENABLE_FEATURE_VI_CRASHME 367#if ENABLE_FEATURE_VI_CRASHME
322 char last_input_char; // last char read from user 368 char last_input_char; // last char read from user
323#endif 369#endif
370#if ENABLE_FEATURE_VI_UNDO_QUEUE
371 char undo_queue_state; // One of UNDO_INS, UNDO_DEL, UNDO_EMPTY
372#endif
324 373
325#if ENABLE_FEATURE_VI_DOT_CMD 374#if ENABLE_FEATURE_VI_DOT_CMD
326 smallint adding2q; // are we currently adding user input to q 375 smallint adding2q; // are we currently adding user input to q
327 int lmc_len; // length of last_modifying_cmd 376 int lmc_len; // length of last_modifying_cmd
328 char *ioq, *ioq_start; // pointer to string for get_one_char to "read" 377 char *ioq, *ioq_start; // pointer to string for get_one_char to "read"
378 int dotcnt; // number of times to repeat '.' command
329#endif 379#endif
330#if ENABLE_FEATURE_VI_SEARCH 380#if ENABLE_FEATURE_VI_SEARCH
331 char *last_search_pattern; // last pattern from a '/' or '?' search 381 char *last_search_pattern; // last pattern from a '/' or '?' search
@@ -343,8 +393,8 @@ struct globals {
343 smalluint YDreg;//,Ureg;// default delete register and orig line for "U" 393 smalluint YDreg;//,Ureg;// default delete register and orig line for "U"
344#define Ureg 27 394#define Ureg 27
345 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27 395 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
396 char regtype[28]; // buffer type: WHOLE, MULTI or PARTIAL
346 char *mark[28]; // user marks points somewhere in text[]- a-z and previous context '' 397 char *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
347 char *context_start, *context_end;
348#endif 398#endif
349#if ENABLE_FEATURE_VI_USE_SIGNALS 399#if ENABLE_FEATURE_VI_USE_SIGNALS
350 sigjmp_buf restart; // int_handler() jumps to location remembered here 400 sigjmp_buf restart; // int_handler() jumps to location remembered here
@@ -352,6 +402,8 @@ struct globals {
352#if !ENABLE_PLATFORM_MINGW32 402#if !ENABLE_PLATFORM_MINGW32
353 struct termios term_orig; // remember what the cooked mode was 403 struct termios term_orig; // remember what the cooked mode was
354#endif 404#endif
405 int cindex; // saved character index for up/down motion
406 smallint keep_index; // retain saved character index
355#if ENABLE_FEATURE_VI_COLON 407#if ENABLE_FEATURE_VI_COLON
356 char *initial_cmds[3]; // currently 2 entries, NULL terminated 408 char *initial_cmds[3]; // currently 2 entries, NULL terminated
357#endif 409#endif
@@ -377,26 +429,22 @@ struct globals {
377#define UNDO_DEL 1 429#define UNDO_DEL 1
378#define UNDO_INS_CHAIN 2 430#define UNDO_INS_CHAIN 2
379#define UNDO_DEL_CHAIN 3 431#define UNDO_DEL_CHAIN 3
380// UNDO_*_QUEUED must be equal to UNDO_xxx ORed with UNDO_QUEUED_FLAG 432# if ENABLE_FEATURE_VI_UNDO_QUEUE
381#define UNDO_QUEUED_FLAG 4
382#define UNDO_INS_QUEUED 4 433#define UNDO_INS_QUEUED 4
383#define UNDO_DEL_QUEUED 5 434#define UNDO_DEL_QUEUED 5
384#define UNDO_USE_SPOS 32 435# endif
385#define UNDO_EMPTY 64 436
386// Pass-through flags for functions that can be undone 437// Pass-through flags for functions that can be undone
387#define NO_UNDO 0 438#define NO_UNDO 0
388#define ALLOW_UNDO 1 439#define ALLOW_UNDO 1
389#define ALLOW_UNDO_CHAIN 2 440#define ALLOW_UNDO_CHAIN 2
390# if ENABLE_FEATURE_VI_UNDO_QUEUE 441# if ENABLE_FEATURE_VI_UNDO_QUEUE
391#define ALLOW_UNDO_QUEUED 3 442#define ALLOW_UNDO_QUEUED 3
392 char undo_queue_state;
393 int undo_q;
394 char *undo_queue_spos; // Start position of queued operation
395 char undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX];
396# else 443# else
397// If undo queuing disabled, don't invoke the missing queue logic 444// If undo queuing disabled, don't invoke the missing queue logic
398#define ALLOW_UNDO_QUEUED 1 445#define ALLOW_UNDO_QUEUED ALLOW_UNDO
399# endif 446# endif
447
400 struct undo_object { 448 struct undo_object {
401 struct undo_object *prev; // Linking back avoids list traversal (LIFO) 449 struct undo_object *prev; // Linking back avoids list traversal (LIFO)
402 int start; // Offset where the data should be restored/deleted 450 int start; // Offset where the data should be restored/deleted
@@ -404,6 +452,13 @@ struct globals {
404 uint8_t u_type; // 0=deleted, 1=inserted, 2=swapped 452 uint8_t u_type; // 0=deleted, 1=inserted, 2=swapped
405 char undo_text[1]; // text that was deleted (if deletion) 453 char undo_text[1]; // text that was deleted (if deletion)
406 } *undo_stack_tail; 454 } *undo_stack_tail;
455# if ENABLE_FEATURE_VI_UNDO_QUEUE
456#define UNDO_USE_SPOS 32
457#define UNDO_EMPTY 64
458 char *undo_queue_spos; // Start position of queued operation
459 int undo_q;
460 char undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX];
461# endif
407#endif /* ENABLE_FEATURE_VI_UNDO */ 462#endif /* ENABLE_FEATURE_VI_UNDO */
408}; 463};
409#define G (*ptr_to_globals) 464#define G (*ptr_to_globals)
@@ -429,11 +484,13 @@ struct globals {
429#define have_status_msg (G.have_status_msg ) 484#define have_status_msg (G.have_status_msg )
430#define last_status_cksum (G.last_status_cksum ) 485#define last_status_cksum (G.last_status_cksum )
431#define current_filename (G.current_filename ) 486#define current_filename (G.current_filename )
487#define alt_filename (G.alt_filename )
432#define screen (G.screen ) 488#define screen (G.screen )
433#define screensize (G.screensize ) 489#define screensize (G.screensize )
434#define screenbegin (G.screenbegin ) 490#define screenbegin (G.screenbegin )
435#define tabstop (G.tabstop ) 491#define tabstop (G.tabstop )
436#define last_forward_char (G.last_forward_char ) 492#define last_search_char (G.last_search_char )
493#define last_search_cmd (G.last_search_cmd )
437#if ENABLE_FEATURE_VI_CRASHME 494#if ENABLE_FEATURE_VI_CRASHME
438#define last_input_char (G.last_input_char ) 495#define last_input_char (G.last_input_char )
439#endif 496#endif
@@ -446,6 +503,7 @@ struct globals {
446#define lmc_len (G.lmc_len ) 503#define lmc_len (G.lmc_len )
447#define ioq (G.ioq ) 504#define ioq (G.ioq )
448#define ioq_start (G.ioq_start ) 505#define ioq_start (G.ioq_start )
506#define dotcnt (G.dotcnt )
449#define last_search_pattern (G.last_search_pattern) 507#define last_search_pattern (G.last_search_pattern)
450 508
451#define edit_file__cur_line (G.edit_file__cur_line) 509#define edit_file__cur_line (G.edit_file__cur_line)
@@ -454,11 +512,12 @@ struct globals {
454 512
455#define YDreg (G.YDreg ) 513#define YDreg (G.YDreg )
456//#define Ureg (G.Ureg ) 514//#define Ureg (G.Ureg )
515#define regtype (G.regtype )
457#define mark (G.mark ) 516#define mark (G.mark )
458#define context_start (G.context_start )
459#define context_end (G.context_end )
460#define restart (G.restart ) 517#define restart (G.restart )
461#define term_orig (G.term_orig ) 518#define term_orig (G.term_orig )
519#define cindex (G.cindex )
520#define keep_index (G.keep_index )
462#define initial_cmds (G.initial_cmds ) 521#define initial_cmds (G.initial_cmds )
463#define readbuffer (G.readbuffer ) 522#define readbuffer (G.readbuffer )
464#define scr_out_buf (G.scr_out_buf ) 523#define scr_out_buf (G.scr_out_buf )
@@ -741,6 +800,30 @@ static int next_tabstop(int col)
741 return col + ((tabstop - 1) - (col % tabstop)); 800 return col + ((tabstop - 1) - (col % tabstop));
742} 801}
743 802
803static int prev_tabstop(int col)
804{
805 return col - ((col % tabstop) ?: tabstop);
806}
807
808static int next_column(char c, int co)
809{
810 if (c == '\t')
811 co = next_tabstop(co);
812 else if ((unsigned char)c < ' ' || c == 0x7f)
813 co++; // display as ^X, use 2 columns
814 return co + 1;
815}
816
817static int get_column(char *p)
818{
819 const char *r;
820 int co = 0;
821
822 for (r = begin_line(p); r < p; r++)
823 co = next_column(*r, co);
824 return co;
825}
826
744//----- Erase the Screen[] memory ------------------------------ 827//----- Erase the Screen[] memory ------------------------------
745static void screen_erase(void) 828static void screen_erase(void)
746{ 829{
@@ -769,7 +852,7 @@ static void new_screen(int ro, int co)
769} 852}
770 853
771//----- Synchronize the cursor to Dot -------------------------- 854//----- Synchronize the cursor to Dot --------------------------
772static NOINLINE void sync_cursor(char *d, int *row, int *col) 855static void sync_cursor(char *d, int *row, int *col)
773{ 856{
774 char *beg_cur; // begin and end of "d" line 857 char *beg_cur; // begin and end of "d" line
775 char *tp; 858 char *tp;
@@ -817,21 +900,16 @@ static NOINLINE void sync_cursor(char *d, int *row, int *col)
817 900
818 // find out what col "d" is on 901 // find out what col "d" is on
819 co = 0; 902 co = 0;
820 while (tp < d) { // drive "co" to correct column 903 do { // drive "co" to correct column
821 if (*tp == '\n') //vda || *tp == '\0') 904 if (*tp == '\n') //vda || *tp == '\0')
822 break; 905 break;
823 if (*tp == '\t') { 906 co = next_column(*tp, co) - 1;
824 // handle tabs like real vi 907 // inserting text before a tab, don't include its position
825 if (d == tp && cmd_mode) { 908 if (cmd_mode && tp == d - 1 && *d == '\t') {
826 break; 909 co++;
827 } 910 break;
828 co = next_tabstop(co);
829 } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) {
830 co++; // display as ^X, use 2 columns
831 } 911 }
832 co++; 912 } while (tp++ < d && ++co);
833 tp++;
834 }
835 913
836 // "co" is the column where "dot" is. 914 // "co" is the column where "dot" is.
837 // The screen has "columns" columns. 915 // The screen has "columns" columns.
@@ -1012,6 +1090,9 @@ static void refresh(int full_screen)
1012 1090
1013 place_cursor(crow, ccol); 1091 place_cursor(crow, ccol);
1014 1092
1093 if (!keep_index)
1094 cindex = ccol + offset;
1095
1015 old_offset = offset; 1096 old_offset = offset;
1016#undef old_offset 1097#undef old_offset
1017} 1098}
@@ -1097,8 +1178,8 @@ static int get_one_char(void)
1097 } 1178 }
1098 // we are adding STDIN chars to q. 1179 // we are adding STDIN chars to q.
1099 c = readit(); 1180 c = readit();
1100 if (lmc_len >= ARRAY_SIZE(last_modifying_cmd) - 1) { 1181 if (lmc_len >= ARRAY_SIZE(last_modifying_cmd) - 2) {
1101 // last_modifying_cmd[] is too small, can't remeber the cmd 1182 // last_modifying_cmd[] is too small, can't remember the cmd
1102 // - drop it 1183 // - drop it
1103 adding2q = 0; 1184 adding2q = 0;
1104 lmc_len = 0; 1185 lmc_len = 0;
@@ -1111,6 +1192,27 @@ static int get_one_char(void)
1111# define get_one_char() readit() 1192# define get_one_char() readit()
1112#endif 1193#endif
1113 1194
1195// Get type of thing to operate on and adjust count
1196static int get_motion_char(void)
1197{
1198 int c, cnt;
1199
1200 c = get_one_char();
1201 if (isdigit(c)) {
1202 if (c != '0') {
1203 // get any non-zero motion count
1204 for (cnt = 0; isdigit(c); c = get_one_char())
1205 cnt = cnt * 10 + (c - '0');
1206 cmdcnt = (cmdcnt ?: 1) * cnt;
1207 } else {
1208 // ensure standalone '0' works
1209 cmdcnt = 0;
1210 }
1211 }
1212
1213 return c;
1214}
1215
1114// Get input line (uses "status line" area) 1216// Get input line (uses "status line" area)
1115static char *get_input_line(const char *prompt) 1217static char *get_input_line(const char *prompt)
1116{ 1218{
@@ -1123,10 +1225,10 @@ static char *get_input_line(const char *prompt)
1123 strcpy(buf, prompt); 1225 strcpy(buf, prompt);
1124 last_status_cksum = 0; // force status update 1226 last_status_cksum = 0; // force status update
1125 go_bottom_and_clear_to_eol(); 1227 go_bottom_and_clear_to_eol();
1126 write1(prompt); // write out the :, /, or ? prompt 1228 write1(buf); // write out the :, /, or ? prompt
1127 1229
1128 i = strlen(buf); 1230 i = strlen(buf);
1129 while (i < MAX_INPUT_LEN) { 1231 while (i < MAX_INPUT_LEN - 1) {
1130 c = get_one_char(); 1232 c = get_one_char();
1131 if (c == '\n' || c == '\r' || c == 27) 1233 if (c == '\n' || c == '\r' || c == 27)
1132 break; // this is end of input 1234 break; // this is end of input
@@ -1136,8 +1238,8 @@ static char *get_input_line(const char *prompt)
1136 if (c == 8 || c == 127) { 1238 if (c == 8 || c == 127) {
1137#endif 1239#endif
1138 // user wants to erase prev char 1240 // user wants to erase prev char
1139 buf[--i] = '\0';
1140 write1("\b \b"); // erase char on screen 1241 write1("\b \b"); // erase char on screen
1242 buf[--i] = '\0';
1141 if (i <= 0) // user backs up before b-o-l, exit 1243 if (i <= 0) // user backs up before b-o-l, exit
1142 break; 1244 break;
1143 } else if (c > 0 && c < 256) { // exclude Unicode 1245 } else if (c > 0 && c < 256) { // exclude Unicode
@@ -1343,15 +1445,20 @@ static void not_implemented(const char *s)
1343 1445
1344//----- Block insert/delete, undo ops -------------------------- 1446//----- Block insert/delete, undo ops --------------------------
1345#if ENABLE_FEATURE_VI_YANKMARK 1447#if ENABLE_FEATURE_VI_YANKMARK
1346static char *text_yank(char *p, char *q, int dest) // copy text into a register 1448// copy text into a register
1449static char *text_yank(char *p, char *q, int dest, int buftype)
1347{ 1450{
1451 char *oldreg = reg[dest];
1348 int cnt = q - p; 1452 int cnt = q - p;
1349 if (cnt < 0) { // they are backwards- reverse them 1453 if (cnt < 0) { // they are backwards- reverse them
1350 p = q; 1454 p = q;
1351 cnt = -cnt; 1455 cnt = -cnt;
1352 } 1456 }
1353 free(reg[dest]); // if already a yank register, free it 1457 // Don't free register yet. This prevents the memory allocator
1458 // from reusing the free block so we can detect if it's changed.
1354 reg[dest] = xstrndup(p, cnt + 1); 1459 reg[dest] = xstrndup(p, cnt + 1);
1460 regtype[dest] = buftype;
1461 free(oldreg);
1355 return p; 1462 return p;
1356} 1463}
1357 1464
@@ -1371,18 +1478,10 @@ static char what_reg(void)
1371 1478
1372static void check_context(char cmd) 1479static void check_context(char cmd)
1373{ 1480{
1374 // A context is defined to be "modifying text" 1481 // Certain movement commands update the context.
1375 // Any modifying command establishes a new context. 1482 if (strchr(":%{}'GHLMz/?Nn", cmd) != NULL) {
1376 1483 mark[27] = mark[26]; // move cur to prev
1377 if (dot < context_start || dot > context_end) { 1484 mark[26] = dot; // move local to cur
1378 if (strchr(modifying_cmds, cmd) != NULL) {
1379 // we are trying to modify text[]- make this the current context
1380 mark[27] = mark[26]; // move cur to prev
1381 mark[26] = dot; // move local to cur
1382 context_start = prev_line(prev_line(dot));
1383 context_end = next_line(next_line(dot));
1384 //loiter= start_loiter= now;
1385 }
1386 } 1485 }
1387} 1486}
1388 1487
@@ -1397,15 +1496,29 @@ static char *swap_context(char *p) // goto new context for '' command make this
1397 tmp = mark[27]; 1496 tmp = mark[27];
1398 mark[27] = p; 1497 mark[27] = p;
1399 mark[26] = p = tmp; 1498 mark[26] = p = tmp;
1400 context_start = prev_line(prev_line(prev_line(p)));
1401 context_end = next_line(next_line(next_line(p)));
1402 } 1499 }
1403 return p; 1500 return p;
1404} 1501}
1502
1503# if ENABLE_FEATURE_VI_VERBOSE_STATUS
1504static void yank_status(const char *op, const char *p, int cnt)
1505{
1506 int lines, chars;
1507
1508 lines = chars = 0;
1509 while (*p) {
1510 ++chars;
1511 if (*p++ == '\n')
1512 ++lines;
1513 }
1514 status_line("%s %d lines (%d chars) from [%c]",
1515 op, lines * cnt, chars * cnt, what_reg());
1516}
1517# endif
1405#endif /* FEATURE_VI_YANKMARK */ 1518#endif /* FEATURE_VI_YANKMARK */
1406 1519
1407#if ENABLE_FEATURE_VI_UNDO 1520#if ENABLE_FEATURE_VI_UNDO
1408static void undo_push(char *, unsigned, unsigned char); 1521static void undo_push(char *, unsigned, int);
1409#endif 1522#endif
1410 1523
1411// open a hole in text[] 1524// open a hole in text[]
@@ -1532,9 +1645,12 @@ static void flush_undo_data(void)
1532 1645
1533// Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com) 1646// Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com)
1534// Add to the undo stack 1647// Add to the undo stack
1535static void undo_push(char *src, unsigned length, uint8_t u_type) 1648static void undo_push(char *src, unsigned length, int u_type)
1536{ 1649{
1537 struct undo_object *undo_entry; 1650 struct undo_object *undo_entry;
1651# if ENABLE_FEATURE_VI_UNDO_QUEUE
1652 int use_spos = u_type & UNDO_USE_SPOS;
1653# endif
1538 1654
1539 // "u_type" values 1655 // "u_type" values
1540 // UNDO_INS: insertion, undo will remove from buffer 1656 // UNDO_INS: insertion, undo will remove from buffer
@@ -1543,8 +1659,8 @@ static void undo_push(char *src, unsigned length, uint8_t u_type)
1543 // The CHAIN operations are for handling multiple operations that the user 1659 // The CHAIN operations are for handling multiple operations that the user
1544 // performs with a single action, i.e. REPLACE mode or find-and-replace commands 1660 // performs with a single action, i.e. REPLACE mode or find-and-replace commands
1545 // UNDO_{INS,DEL}_QUEUED: If queuing feature is enabled, allow use of the queue 1661 // UNDO_{INS,DEL}_QUEUED: If queuing feature is enabled, allow use of the queue
1546 // for the INS/DEL operation. The raw values should be equal to the values of 1662 // for the INS/DEL operation.
1547 // UNDO_{INS,DEL} ORed with UNDO_QUEUED_FLAG 1663 // UNDO_{INS,DEL} ORed with UNDO_USE_SPOS: commit the undo queue
1548 1664
1549# if ENABLE_FEATURE_VI_UNDO_QUEUE 1665# if ENABLE_FEATURE_VI_UNDO_QUEUE
1550 // This undo queuing functionality groups multiple character typing or backspaces 1666 // This undo queuing functionality groups multiple character typing or backspaces
@@ -1597,9 +1713,7 @@ static void undo_push(char *src, unsigned length, uint8_t u_type)
1597 } 1713 }
1598 break; 1714 break;
1599 } 1715 }
1600# else 1716 u_type &= ~UNDO_USE_SPOS;
1601 // If undo queuing is disabled, ignore the queuing flag entirely
1602 u_type = u_type & ~UNDO_QUEUED_FLAG;
1603# endif 1717# endif
1604 1718
1605 // Allocate a new undo object 1719 // Allocate a new undo object
@@ -1616,12 +1730,11 @@ static void undo_push(char *src, unsigned length, uint8_t u_type)
1616 } 1730 }
1617 undo_entry->length = length; 1731 undo_entry->length = length;
1618# if ENABLE_FEATURE_VI_UNDO_QUEUE 1732# if ENABLE_FEATURE_VI_UNDO_QUEUE
1619 if ((u_type & UNDO_USE_SPOS) != 0) { 1733 if (use_spos) {
1620 undo_entry->start = undo_queue_spos - text; // use start position from queue 1734 undo_entry->start = undo_queue_spos - text; // use start position from queue
1621 } else { 1735 } else {
1622 undo_entry->start = src - text; // use offset from start of text buffer 1736 undo_entry->start = src - text; // use offset from start of text buffer
1623 } 1737 }
1624 u_type = (u_type & ~UNDO_USE_SPOS);
1625# else 1738# else
1626 undo_entry->start = src - text; 1739 undo_entry->start = src - text;
1627# endif 1740# endif
@@ -1674,10 +1787,12 @@ static void undo_pop(void)
1674 u_start = text + undo_entry->start; 1787 u_start = text + undo_entry->start;
1675 text_hole_make(u_start, undo_entry->length); 1788 text_hole_make(u_start, undo_entry->length);
1676 memcpy(u_start, undo_entry->undo_text, undo_entry->length); 1789 memcpy(u_start, undo_entry->undo_text, undo_entry->length);
1790# if ENABLE_FEATURE_VI_VERBOSE_STATUS
1677 status_line("Undo [%d] %s %d chars at position %d", 1791 status_line("Undo [%d] %s %d chars at position %d",
1678 modified_count, "restored", 1792 modified_count, "restored",
1679 undo_entry->length, undo_entry->start 1793 undo_entry->length, undo_entry->start
1680 ); 1794 );
1795# endif
1681 break; 1796 break;
1682 case UNDO_INS: 1797 case UNDO_INS:
1683 case UNDO_INS_CHAIN: 1798 case UNDO_INS_CHAIN:
@@ -1685,10 +1800,12 @@ static void undo_pop(void)
1685 u_start = undo_entry->start + text; 1800 u_start = undo_entry->start + text;
1686 u_end = u_start - 1 + undo_entry->length; 1801 u_end = u_start - 1 + undo_entry->length;
1687 text_hole_delete(u_start, u_end, NO_UNDO); 1802 text_hole_delete(u_start, u_end, NO_UNDO);
1803# if ENABLE_FEATURE_VI_VERBOSE_STATUS
1688 status_line("Undo [%d] %s %d chars at position %d", 1804 status_line("Undo [%d] %s %d chars at position %d",
1689 modified_count, "deleted", 1805 modified_count, "deleted",
1690 undo_entry->length, undo_entry->start 1806 undo_entry->length, undo_entry->start
1691 ); 1807 );
1808# endif
1692 break; 1809 break;
1693 } 1810 }
1694 repeat = 0; 1811 repeat = 0;
@@ -1752,17 +1869,11 @@ static char *move_to_col(char *p, int l)
1752 1869
1753 p = begin_line(p); 1870 p = begin_line(p);
1754 co = 0; 1871 co = 0;
1755 while (co < l && p < end) { 1872 do {
1756 if (*p == '\n') //vda || *p == '\0') 1873 if (*p == '\n') //vda || *p == '\0')
1757 break; 1874 break;
1758 if (*p == '\t') { 1875 co = next_column(*p, co);
1759 co = next_tabstop(co); 1876 } while (co <= l && p++ < end);
1760 } else if (*p < ' ' || *p == 127) {
1761 co++; // display as ^X, use 2 columns
1762 }
1763 co++;
1764 p++;
1765 }
1766 return p; 1877 return p;
1767} 1878}
1768 1879
@@ -1785,6 +1896,33 @@ static void dot_skip_over_ws(void)
1785 dot++; 1896 dot++;
1786} 1897}
1787 1898
1899static void dot_to_char(int cmd)
1900{
1901 char *q = dot;
1902 int dir = islower(cmd) ? FORWARD : BACK;
1903
1904 if (last_search_char == 0)
1905 return;
1906
1907 do {
1908 do {
1909 q += dir;
1910 if ((dir == FORWARD ? q > end - 1 : q < text) || *q == '\n') {
1911 indicate_error();
1912 return;
1913 }
1914 } while (*q != last_search_char);
1915 } while (--cmdcnt > 0);
1916
1917 dot = q;
1918
1919 // place cursor before/after char as required
1920 if (cmd == 't')
1921 dot_left();
1922 else if (cmd == 'T')
1923 dot_right();
1924}
1925
1788static void dot_scroll(int cnt, int dir) 1926static void dot_scroll(int cnt, int dir)
1789{ 1927{
1790 char *q; 1928 char *q;
@@ -1827,13 +1965,9 @@ static char *bound_dot(char *p) // make sure text[0] <= P < "end"
1827static void start_new_cmd_q(char c) 1965static void start_new_cmd_q(char c)
1828{ 1966{
1829 // get buffer for new cmd 1967 // get buffer for new cmd
1830 // if there is a current cmd count put it in the buffer first 1968 dotcnt = cmdcnt ?: 1;
1831 if (cmdcnt > 0) { 1969 last_modifying_cmd[0] = c;
1832 lmc_len = sprintf(last_modifying_cmd, "%u%c", cmdcnt, c); 1970 lmc_len = 1;
1833 } else { // just save char c onto queue
1834 last_modifying_cmd[0] = c;
1835 lmc_len = 1;
1836 }
1837 adding2q = 1; 1971 adding2q = 1;
1838} 1972}
1839static void end_cmd_q(void) 1973static void end_cmd_q(void)
@@ -1848,12 +1982,11 @@ static void end_cmd_q(void)
1848#endif /* FEATURE_VI_DOT_CMD */ 1982#endif /* FEATURE_VI_DOT_CMD */
1849 1983
1850// copy text into register, then delete text. 1984// copy text into register, then delete text.
1851// if dist <= 0, do not include, or go past, a NewLine
1852// 1985//
1853#if !ENABLE_FEATURE_VI_UNDO 1986#if !ENABLE_FEATURE_VI_UNDO
1854#define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d) 1987#define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d)
1855#endif 1988#endif
1856static char *yank_delete(char *start, char *stop, int dist, int yf, int undo) 1989static char *yank_delete(char *start, char *stop, int buftype, int yf, int undo)
1857{ 1990{
1858 char *p; 1991 char *p;
1859 1992
@@ -1864,22 +1997,11 @@ static char *yank_delete(char *start, char *stop, int dist, int yf, int undo)
1864 start = stop; 1997 start = stop;
1865 stop = p; 1998 stop = p;
1866 } 1999 }
1867 if (dist <= 0) { 2000 if (buftype == PARTIAL && *start == '\n')
1868 // we cannot cross NL boundaries 2001 return start;
1869 p = start;
1870 if (*p == '\n')
1871 return p;
1872 // dont go past a NewLine
1873 for (; p + 1 <= stop; p++) {
1874 if (p[1] == '\n') {
1875 stop = p; // "stop" just before NewLine
1876 break;
1877 }
1878 }
1879 }
1880 p = start; 2002 p = start;
1881#if ENABLE_FEATURE_VI_YANKMARK 2003#if ENABLE_FEATURE_VI_YANKMARK
1882 text_yank(start, stop, YDreg); 2004 text_yank(start, stop, YDreg, buftype);
1883#endif 2005#endif
1884 if (yf == YANKDEL) { 2006 if (yf == YANKDEL) {
1885 p = text_hole_delete(start, stop, undo); 2007 p = text_hole_delete(start, stop, undo);
@@ -1949,6 +2071,11 @@ static int file_insert(const char *fn, char *p, int initial)
1949#endif 2071#endif
1950 status_line_bold("can't read '%s'", fn); 2072 status_line_bold("can't read '%s'", fn);
1951 } 2073 }
2074# if ENABLE_FEATURE_VI_UNDO
2075 else {
2076 undo_push_insert(p, size, ALLOW_UNDO);
2077 }
2078# endif
1952 fi: 2079 fi:
1953 close(fd); 2080 close(fd);
1954 2081
@@ -2033,6 +2160,11 @@ static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at
2033#endif 2160#endif
2034static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' 2161static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2035{ 2162{
2163#if ENABLE_FEATURE_VI_SETOPTS
2164 char *q;
2165 size_t len;
2166#endif
2167
2036 if (c == 22) { // Is this an ctrl-V? 2168 if (c == 22) { // Is this an ctrl-V?
2037 p += stupid_insert(p, '^'); // use ^ to indicate literal next 2169 p += stupid_insert(p, '^'); // use ^ to indicate literal next
2038 refresh(FALSE); // show the ^ 2170 refresh(FALSE); // show the ^
@@ -2053,6 +2185,35 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2053 if ((p[-1] != '\n') && (dot > text)) { 2185 if ((p[-1] != '\n') && (dot > text)) {
2054 p--; 2186 p--;
2055 } 2187 }
2188 } else if (c == 4) { // ctrl-D reduces indentation
2189 int prev;
2190 char *r, *bol;
2191 bol = begin_line(p);
2192 for (r = bol; r < end_line(p); ++r) {
2193 if (!isblank(*r))
2194 break;
2195 }
2196
2197 prev = prev_tabstop(get_column(r));
2198 while (r > bol && get_column(r) > prev) {
2199 if (p > bol)
2200 p--;
2201 r--;
2202 r = text_hole_delete(r, r, ALLOW_UNDO_QUEUED);
2203 }
2204#if ENABLE_FEATURE_VI_SETOPTS
2205 } else if (c == '\t' && expandtab) { // expand tab
2206 int col = get_column(p);
2207 col = next_tabstop(col) - col + 1;
2208 while (col--) {
2209# if ENABLE_FEATURE_VI_UNDO
2210 undo_push_insert(p, 1, undo);
2211# else
2212 modified_count++;
2213# endif
2214 p += 1 + stupid_insert(p, ' ');
2215 }
2216#endif
2056#if !ENABLE_PLATFORM_MINGW32 2217#if !ENABLE_PLATFORM_MINGW32
2057 } else if (c == term_orig.c_cc[VERASE] || c == 8 || c == 127) { // Is this a BS 2218 } else if (c == term_orig.c_cc[VERASE] || c == 8 || c == 127) { // Is this a BS
2058#else 2219#else
@@ -2081,18 +2242,21 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2081 showmatching(p - 1); 2242 showmatching(p - 1);
2082 } 2243 }
2083 if (autoindent && c == '\n') { // auto indent the new line 2244 if (autoindent && c == '\n') { // auto indent the new line
2084 char *q; 2245 // use current/previous line as template
2085 size_t len; 2246 q = openabove ? p : prev_line(p);
2086 q = prev_line(p); // use prev line as template
2087 len = strspn(q, " \t"); // space or tab 2247 len = strspn(q, " \t"); // space or tab
2248 if (openabove) {
2249 p--; // this replaces dot_prev() in do_cmd()
2250 q += len; // template will be shifted by text_hole_make()
2251 }
2088 if (len) { 2252 if (len) {
2089 uintptr_t bias; 2253 uintptr_t bias;
2090 bias = text_hole_make(p, len); 2254 bias = text_hole_make(p, len);
2091 p += bias; 2255 p += bias;
2092 q += bias; 2256 q += bias;
2093#if ENABLE_FEATURE_VI_UNDO 2257# if ENABLE_FEATURE_VI_UNDO
2094 undo_push_insert(p, len, undo); 2258 undo_push_insert(p, len, undo);
2095#endif 2259# endif
2096 memcpy(p, q, len); 2260 memcpy(p, q, len);
2097 p += len; 2261 p += len;
2098 } 2262 }
@@ -2102,6 +2266,41 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2102 return p; 2266 return p;
2103} 2267}
2104 2268
2269#if ENABLE_FEATURE_VI_COLON_EXPAND
2270static void init_filename(char *fn)
2271{
2272 char *copy = xstrdup(fn);
2273
2274 if (current_filename == NULL) {
2275 current_filename = copy;
2276 } else {
2277 free(alt_filename);
2278 alt_filename = copy;
2279 }
2280}
2281#else
2282# define init_filename(f) ((void)(0))
2283#endif
2284
2285static void update_filename(char *fn)
2286{
2287#if ENABLE_FEATURE_VI_COLON_EXPAND
2288 if (fn == NULL)
2289 return;
2290
2291 if (current_filename == NULL || strcmp(fn, current_filename) != 0) {
2292 free(alt_filename);
2293 alt_filename = current_filename;
2294 current_filename = xstrdup(fn);
2295 }
2296#else
2297 if (fn != current_filename) {
2298 free(current_filename);
2299 current_filename = xstrdup(fn);
2300 }
2301#endif
2302}
2303
2105// read text from file or create an empty buf 2304// read text from file or create an empty buf
2106// will also update current_filename 2305// will also update current_filename
2107static int init_text_buffer(char *fn) 2306static int init_text_buffer(char *fn)
@@ -2113,10 +2312,7 @@ static int init_text_buffer(char *fn)
2113 text_size = 10240; 2312 text_size = 10240;
2114 screenbegin = dot = end = text = xzalloc(text_size); 2313 screenbegin = dot = end = text = xzalloc(text_size);
2115 2314
2116 if (fn != current_filename) { 2315 update_filename(fn);
2117 free(current_filename);
2118 current_filename = xstrdup(fn);
2119 }
2120 rc = file_insert(fn, text, 1); 2316 rc = file_insert(fn, text, 1);
2121 if (rc < 0) { 2317 if (rc < 0) {
2122 // file doesnt exist. Start empty buf with dummy line 2318 // file doesnt exist. Start empty buf with dummy line
@@ -2153,16 +2349,6 @@ static uintptr_t string_insert(char *p, const char *s, int undo) // insert the s
2153 bias = text_hole_make(p, i); 2349 bias = text_hole_make(p, i);
2154 p += bias; 2350 p += bias;
2155 memcpy(p, s, i); 2351 memcpy(p, s, i);
2156#if ENABLE_FEATURE_VI_YANKMARK
2157 {
2158 int cnt;
2159 for (cnt = 0; *s != '\0'; s++) {
2160 if (*s == '\n')
2161 cnt++;
2162 }
2163 status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2164 }
2165#endif
2166 return bias; 2352 return bias;
2167} 2353}
2168#endif 2354#endif
@@ -2312,112 +2498,211 @@ static char *char_search(char *p, const char *pat, int dir_and_range)
2312 2498
2313//----- The Colon commands ------------------------------------- 2499//----- The Colon commands -------------------------------------
2314#if ENABLE_FEATURE_VI_COLON 2500#if ENABLE_FEATURE_VI_COLON
2315static char *get_one_address(char *p, int *addr) // get colon addr, if present 2501static char *get_one_address(char *p, int *result) // get colon addr, if present
2316{ 2502{
2317 int st; 2503 int st, num, sign, addr, new_addr;
2318 char *q; 2504# if ENABLE_FEATURE_VI_YANKMARK || ENABLE_FEATURE_VI_SEARCH
2319 IF_FEATURE_VI_YANKMARK(char c;) 2505 char *q, c;
2506# endif
2507 IF_FEATURE_VI_SEARCH(int dir;)
2320 2508
2321 *addr = -1; // assume no addr 2509 addr = -1; // assume no addr
2322 if (*p == '.') { // the current line 2510 sign = 0;
2323 p++; 2511 for (;;) {
2324 q = begin_line(dot); 2512 new_addr = -1;
2325 *addr = count_lines(text, q); 2513 if (isblank(*p)) {
2326 } 2514 p++;
2327#if ENABLE_FEATURE_VI_YANKMARK 2515 } else if (*p == '.') { // the current line
2328 else if (*p == '\'') { // is this a mark addr 2516 p++;
2329 p++; 2517 new_addr = count_lines(text, dot);
2330 c = tolower(*p); 2518 }
2331 p++; 2519# if ENABLE_FEATURE_VI_YANKMARK
2332 if (c >= 'a' && c <= 'z') { 2520 else if (*p == '\'') { // is this a mark addr
2333 // we have a mark 2521 p++;
2334 c = c - 'a'; 2522 c = tolower(*p);
2335 q = mark[(unsigned char) c]; 2523 p++;
2336 if (q != NULL) { // is mark valid 2524 q = NULL;
2337 *addr = count_lines(text, q); 2525 if (c >= 'a' && c <= 'z') {
2526 // we have a mark
2527 c = c - 'a';
2528 q = mark[(unsigned char) c];
2338 } 2529 }
2530 if (q == NULL) // is mark valid
2531 return NULL;
2532 new_addr = count_lines(text, q);
2339 } 2533 }
2340 } 2534# endif
2341#endif 2535# if ENABLE_FEATURE_VI_SEARCH
2342#if ENABLE_FEATURE_VI_SEARCH 2536 else if (*p == '/' || *p == '?') { // a search pattern
2343 else if (*p == '/') { // a search pattern 2537 c = *p;
2344 q = strchrnul(p + 1, '/'); 2538 q = strchrnul(p + 1, c);
2345 if (p + 1 != q) { 2539 if (p + 1 != q) {
2346 // save copy of new pattern 2540 // save copy of new pattern
2347 free(last_search_pattern); 2541 free(last_search_pattern);
2348 last_search_pattern = xstrndup(p, q - p); 2542 last_search_pattern = xstrndup(p, q - p);
2543 }
2544 p = q;
2545 if (*p == c)
2546 p++;
2547 if (c == '/') {
2548 q = next_line(dot);
2549 dir = (FORWARD << 1) | FULL;
2550 } else {
2551 q = begin_line(dot);
2552 dir = ((unsigned)BACK << 1) | FULL;
2553 }
2554 q = char_search(q, last_search_pattern + 1, dir);
2555 if (q == NULL)
2556 return NULL;
2557 new_addr = count_lines(text, q);
2349 } 2558 }
2350 p = q; 2559# endif
2351 if (*p == '/') 2560 else if (*p == '$') { // the last line in file
2352 p++; 2561 p++;
2353 q = char_search(next_line(dot), last_search_pattern + 1, 2562 new_addr = count_lines(text, end - 1);
2354 (FORWARD << 1) | FULL); 2563 } else if (isdigit(*p)) {
2355 if (q != NULL) { 2564 sscanf(p, "%d%n", &num, &st);
2356 *addr = count_lines(text, q); 2565 p += st;
2566 if (addr < 0) { // specific line number
2567 addr = num;
2568 } else { // offset from current addr
2569 addr += sign >= 0 ? num : -num;
2570 }
2571 sign = 0;
2572 } else if (*p == '-' || *p == '+') {
2573 sign = *p++ == '-' ? -1 : 1;
2574 if (addr < 0) { // default address is dot
2575 addr = count_lines(text, dot);
2576 }
2577 } else {
2578 addr += sign; // consume unused trailing sign
2579 break;
2580 }
2581 if (new_addr >= 0) {
2582 if (addr >= 0) // only one new address per expression
2583 return NULL;
2584 addr = new_addr;
2357 } 2585 }
2358 } 2586 }
2359#endif 2587 *result = addr;
2360 else if (*p == '$') { // the last line in file
2361 p++;
2362 q = begin_line(end - 1);
2363 *addr = count_lines(text, q);
2364 } else if (isdigit(*p)) { // specific line number
2365 sscanf(p, "%d%n", addr, &st);
2366 p += st;
2367 } else {
2368 // unrecognized address - assume -1
2369 *addr = -1;
2370 }
2371 return p; 2588 return p;
2372} 2589}
2373 2590
2374static char *get_address(char *p, int *b, int *e) // get two colon addrs, if present 2591# define GET_ADDRESS 0
2592# define GET_SEPARATOR 1
2593
2594// Read line addresses for a colon command. The user can enter as
2595// many as they like but only the last two will be used.
2596static char *get_address(char *p, int *b, int *e)
2375{ 2597{
2598 int state = GET_ADDRESS;
2599 char *save_dot = dot;
2600
2376 //----- get the address' i.e., 1,3 'a,'b ----- 2601 //----- get the address' i.e., 1,3 'a,'b -----
2377 // get FIRST addr, if present 2602 for (;;) {
2378 while (isblank(*p)) 2603 if (isblank(*p)) {
2379 p++; // skip over leading spaces 2604 p++;
2380 if (*p == '%') { // alias for 1,$ 2605 } else if (*p == '%' && state == GET_ADDRESS) { // alias for 1,$
2381 p++;
2382 *b = 1;
2383 *e = count_lines(text, end-1);
2384 goto ga0;
2385 }
2386 p = get_one_address(p, b);
2387 while (isblank(*p))
2388 p++;
2389 if (*p == ',') { // is there a address separator
2390 p++;
2391 while (isblank(*p))
2392 p++; 2606 p++;
2393 // get SECOND addr, if present 2607 *b = 1;
2394 p = get_one_address(p, e); 2608 *e = count_lines(text, end-1);
2609 state = GET_SEPARATOR;
2610 } else if (state == GET_SEPARATOR && (*p == ',' || *p == ';')) {
2611 if (*p == ';')
2612 dot = find_line(*e);
2613 p++;
2614 *b = *e;
2615 state = GET_ADDRESS;
2616 } else if (state == GET_ADDRESS) {
2617 p = get_one_address(p, e);
2618 if (p == NULL)
2619 break;
2620 state = GET_SEPARATOR;
2621 } else {
2622 if (state == GET_SEPARATOR && *b >= 0 && *e < 0)
2623 *e = count_lines(text, dot);
2624 break;
2625 }
2395 } 2626 }
2396 ga0: 2627 dot = save_dot;
2397 while (isblank(*p))
2398 p++; // skip over trailing spaces
2399 return p; 2628 return p;
2400} 2629}
2401 2630
2402#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS 2631# if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
2403static void setops(const char *args, const char *opname, int flg_no, 2632static void setops(char *args, int flg_no)
2404 const char *short_opname, int opt)
2405{ 2633{
2406 const char *a = args + flg_no; 2634 char *eq;
2407 int l = strlen(opname) - 1; // opname have + ' ' 2635 int index;
2636
2637 eq = strchr(args, '=');
2638 if (eq) *eq = '\0';
2639 index = index_in_strings(OPTS_STR, args + flg_no);
2640 if (eq) *eq = '=';
2641 if (index < 0) {
2642 bad:
2643 status_line_bold("bad option: %s", args);
2644 return;
2645 }
2408 2646
2409 // maybe strncmp? we had tons of erroneous strncasecmp's... 2647 index = 1 << (index >> 1); // convert to VI_bit
2410 if (strncasecmp(a, opname, l) == 0 2648
2411 || strncasecmp(a, short_opname, 2) == 0 2649 if (index & VI_TABSTOP) {
2412 ) { 2650 // don't set this bit in vi_setops, it's reused as 'openabove'
2413 if (flg_no) 2651 int t;
2414 vi_setops &= ~opt; 2652 if (!eq || flg_no) // no "=NNN" or it is "notabstop"?
2415 else 2653 goto bad;
2416 vi_setops |= opt; 2654 t = bb_strtou(eq + 1, NULL, 10);
2655 if (t <= 0 || t > MAX_TABSTOP)
2656 goto bad;
2657 tabstop = t;
2658 return;
2659 }
2660 if (eq) goto bad; // boolean option has "="?
2661 if (flg_no) {
2662 vi_setops &= ~index;
2663 } else {
2664 vi_setops |= index;
2417 } 2665 }
2418} 2666}
2419#endif 2667# endif
2420 2668
2669# if ENABLE_FEATURE_VI_COLON_EXPAND
2670static char *expand_args(char *args)
2671{
2672 char *s, *t;
2673 const char *replace;
2674
2675 args = xstrdup(args);
2676 for (s = args; *s; s++) {
2677 if (*s == '%') {
2678 replace = current_filename;
2679 } else if (*s == '#') {
2680 replace = alt_filename;
2681 } else {
2682 if (*s == '\\' && s[1] != '\0') {
2683 for (t = s++; *t; t++)
2684 *t = t[1];
2685 }
2686 continue;
2687 }
2688
2689 if (replace == NULL) {
2690 free(args);
2691 status_line_bold("No previous filename");
2692 return NULL;
2693 }
2694
2695 *s = '\0';
2696 t = xasprintf("%s%s%s", args, replace, s+1);
2697 s = t + (s - args) + strlen(replace);
2698 free(args);
2699 args = t;
2700 }
2701 return args;
2702}
2703# else
2704# define expand_args(a) (a)
2705# endif
2421#endif /* FEATURE_VI_COLON */ 2706#endif /* FEATURE_VI_COLON */
2422 2707
2423// buf must be no longer than MAX_INPUT_LEN! 2708// buf must be no longer than MAX_INPUT_LEN!
@@ -2434,7 +2719,7 @@ static void colon(char *buf)
2434 if (cnt == 0) 2719 if (cnt == 0)
2435 return; 2720 return;
2436 if (strncmp(p, "quit", cnt) == 0 2721 if (strncmp(p, "quit", cnt) == 0
2437 || strncmp(p, "q!", cnt) == 0 2722 || strcmp(p, "q!") == 0
2438 ) { 2723 ) {
2439 if (modified_count && p[1] != '!') { 2724 if (modified_count && p[1] != '!') {
2440 status_line_bold("No write since last change (:%s! overrides)", p); 2725 status_line_bold("No write since last change (:%s! overrides)", p);
@@ -2444,8 +2729,8 @@ static void colon(char *buf)
2444 return; 2729 return;
2445 } 2730 }
2446 if (strncmp(p, "write", cnt) == 0 2731 if (strncmp(p, "write", cnt) == 0
2447 || strncmp(p, "wq", cnt) == 0 2732 || strcmp(p, "wq") == 0
2448 || strncmp(p, "wn", cnt) == 0 2733 || strcmp(p, "wn") == 0
2449 || (p[0] == 'x' && !p[1]) 2734 || (p[0] == 'x' && !p[1])
2450 ) { 2735 ) {
2451 if (modified_count != 0 || p[0] != 'x') { 2736 if (modified_count != 0 || p[0] != 'x') {
@@ -2463,7 +2748,6 @@ static void colon(char *buf)
2463 ); 2748 );
2464 if (p[0] == 'x' 2749 if (p[0] == 'x'
2465 || p[1] == 'q' || p[1] == 'n' 2750 || p[1] == 'q' || p[1] == 'n'
2466 || p[1] == 'Q' || p[1] == 'N'
2467 ) { 2751 ) {
2468 editing = 0; 2752 editing = 0;
2469 } 2753 }
@@ -2483,12 +2767,9 @@ static void colon(char *buf)
2483#else 2767#else
2484 2768
2485 char c, *buf1, *q, *r; 2769 char c, *buf1, *q, *r;
2486 char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN]; 2770 char *fn, cmd[MAX_INPUT_LEN], *cmdend, *args, *exp = NULL;
2487 int i, l, li, b, e; 2771 int i, l, li, b, e;
2488 int useforce; 2772 int useforce;
2489# if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC
2490 char *orig_buf;
2491# endif
2492 2773
2493 // :3154 // if (-e line 3154) goto it else stay put 2774 // :3154 // if (-e line 3154) goto it else stay put
2494 // :4,33w! foo // write a portion of buffer to file "foo" 2775 // :4,33w! foo // write a portion of buffer to file "foo"
@@ -2512,58 +2793,61 @@ static void colon(char *buf)
2512 2793
2513 li = i = 0; 2794 li = i = 0;
2514 b = e = -1; 2795 b = e = -1;
2515 q = text; // assume 1,$ for the range
2516 r = end - 1;
2517 li = count_lines(text, end - 1); 2796 li = count_lines(text, end - 1);
2518 fn = current_filename; 2797 fn = current_filename;
2519 2798
2520 // look for optional address(es) :. :1 :1,9 :'q,'a :% 2799 // look for optional address(es) :. :1 :1,9 :'q,'a :%
2800 buf1 = buf;
2521 buf = get_address(buf, &b, &e); 2801 buf = get_address(buf, &b, &e);
2522 2802 if (buf == NULL) {
2523# if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC 2803 status_line_bold("Bad address: %s", buf1);
2524 // remember orig command line 2804 goto ret;
2525 orig_buf = buf; 2805 }
2526# endif
2527 2806
2528 // get the COMMAND into cmd[] 2807 // get the COMMAND into cmd[]
2808 strcpy(cmd, buf);
2529 buf1 = cmd; 2809 buf1 = cmd;
2530 while (*buf != '\0') { 2810 while (!isspace(*buf1) && *buf1 != '\0') {
2531 if (isspace(*buf)) 2811 buf1++;
2532 break;
2533 *buf1++ = *buf++;
2534 } 2812 }
2535 *buf1 = '\0'; 2813 cmdend = buf1;
2536 // get any ARGuments 2814 // get any ARGuments
2537 while (isblank(*buf)) 2815 while (isblank(*buf1))
2538 buf++; 2816 buf1++;
2539 strcpy(args, buf); 2817 args = buf1;
2818 *cmdend = '\0';
2540 useforce = FALSE; 2819 useforce = FALSE;
2541 buf1 = last_char_is(cmd, '!'); 2820 if (cmdend > cmd && cmdend[-1] == '!') {
2542 if (buf1) {
2543 useforce = TRUE; 2821 useforce = TRUE;
2544 *buf1 = '\0'; // get rid of ! 2822 cmdend[-1] = '\0'; // get rid of !
2545 }
2546 if (b >= 0) {
2547 // if there is only one addr, then the addr
2548 // is the line number of the single line the
2549 // user wants. So, reset the end
2550 // pointer to point at end of the "b" line
2551 q = find_line(b); // what line is #b
2552 r = end_line(q);
2553 li = 1;
2554 } 2823 }
2555 if (e >= 0) { 2824 // assume the command will want a range, certain commands
2556 // we were given two addrs. change the 2825 // (read, substitute) need to adjust these assumptions
2557 // end pointer to the addr given by user. 2826 if (e < 0) {
2558 r = find_line(e); // what line is #e 2827 q = text; // no addr, use 1,$ for the range
2559 r = end_line(r); 2828 r = end - 1;
2560 li = e - b + 1; 2829 } else {
2830 // at least one addr was given, get its details
2831 q = r = find_line(e);
2832 if (b < 0) {
2833 // if there is only one addr, then it's the line
2834 // number of the single line the user wants.
2835 // Reset the end pointer to the end of that line.
2836 r = end_line(q);
2837 li = 1;
2838 } else {
2839 // we were given two addrs. change the
2840 // start pointer to the addr given by user.
2841 q = find_line(b); // what line is #b
2842 r = end_line(r);
2843 li = e - b + 1;
2844 }
2561 } 2845 }
2562 // ------------ now look for the command ------------ 2846 // ------------ now look for the command ------------
2563 i = strlen(cmd); 2847 i = strlen(cmd);
2564 if (i == 0) { // :123CR goto line #123 2848 if (i == 0) { // :123CR goto line #123
2565 if (b >= 0) { 2849 if (e >= 0) {
2566 dot = find_line(b); // what line is #b 2850 dot = find_line(e); // what line is #e
2567 dot_skip_over_ws(); 2851 dot_skip_over_ws();
2568 } 2852 }
2569 } 2853 }
@@ -2571,9 +2855,12 @@ static void colon(char *buf)
2571 else if (cmd[0] == '!') { // run a cmd 2855 else if (cmd[0] == '!') { // run a cmd
2572 int retcode; 2856 int retcode;
2573 // :!ls run the <cmd> 2857 // :!ls run the <cmd>
2858 exp = expand_args(buf + 1);
2859 if (exp == NULL)
2860 goto ret;
2574 go_bottom_and_clear_to_eol(); 2861 go_bottom_and_clear_to_eol();
2575 cookmode(); 2862 cookmode();
2576 retcode = system(orig_buf + 1); // run the cmd 2863 retcode = system(exp); // run the cmd
2577 if (retcode) 2864 if (retcode)
2578 printf("\nshell returned %i\n\n", retcode); 2865 printf("\nshell returned %i\n\n", retcode);
2579 rawmode(); 2866 rawmode();
@@ -2581,16 +2868,16 @@ static void colon(char *buf)
2581 } 2868 }
2582# endif 2869# endif
2583 else if (cmd[0] == '=' && !cmd[1]) { // where is the address 2870 else if (cmd[0] == '=' && !cmd[1]) { // where is the address
2584 if (b < 0) { // no addr given- use defaults 2871 if (e < 0) { // no addr given- use defaults
2585 b = e = count_lines(text, dot); 2872 e = count_lines(text, dot);
2586 } 2873 }
2587 status_line("%d", b); 2874 status_line("%d", e);
2588 } else if (strncmp(cmd, "delete", i) == 0) { // delete lines 2875 } else if (strncmp(cmd, "delete", i) == 0) { // delete lines
2589 if (b < 0) { // no addr given- use defaults 2876 if (e < 0) { // no addr given- use defaults
2590 q = begin_line(dot); // assume .,. for the range 2877 q = begin_line(dot); // assume .,. for the range
2591 r = end_line(dot); 2878 r = end_line(dot);
2592 } 2879 }
2593 dot = yank_delete(q, r, 1, YANKDEL, ALLOW_UNDO); // save, then delete lines 2880 dot = yank_delete(q, r, WHOLE, YANKDEL, ALLOW_UNDO); // save, then delete lines
2594 dot_skip_over_ws(); 2881 dot_skip_over_ws();
2595 } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file 2882 } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file
2596 int size; 2883 int size;
@@ -2602,11 +2889,10 @@ static void colon(char *buf)
2602 } 2889 }
2603 if (args[0]) { 2890 if (args[0]) {
2604 // the user supplied a file name 2891 // the user supplied a file name
2605 fn = args; 2892 fn = exp = expand_args(args);
2606 } else if (current_filename && current_filename[0]) { 2893 if (exp == NULL)
2607 // no user supplied name- use the current filename 2894 goto ret;
2608 // fn = current_filename; was set by default 2895 } else if (current_filename == NULL) {
2609 } else {
2610 // no user file name, no current name- punt 2896 // no user file name, no current name- punt
2611 status_line_bold("No current filename"); 2897 status_line_bold("No current filename");
2612 goto ret; 2898 goto ret;
@@ -2629,7 +2915,7 @@ static void colon(char *buf)
2629 status_line("'%s'%s" 2915 status_line("'%s'%s"
2630 IF_FEATURE_VI_READONLY("%s") 2916 IF_FEATURE_VI_READONLY("%s")
2631 " %uL, %uC", 2917 " %uL, %uC",
2632 current_filename, 2918 fn,
2633 (size < 0 ? " [New file]" : ""), 2919 (size < 0 ? " [New file]" : ""),
2634 IF_FEATURE_VI_READONLY( 2920 IF_FEATURE_VI_READONLY(
2635 ((readonly_mode) ? " [Readonly]" : ""), 2921 ((readonly_mode) ? " [Readonly]" : ""),
@@ -2637,14 +2923,16 @@ static void colon(char *buf)
2637 li, (int)(end - text) 2923 li, (int)(end - text)
2638 ); 2924 );
2639 } else if (strncmp(cmd, "file", i) == 0) { // what File is this 2925 } else if (strncmp(cmd, "file", i) == 0) { // what File is this
2640 if (b != -1 || e != -1) { 2926 if (e >= 0) {
2641 status_line_bold("No address allowed on this command"); 2927 status_line_bold("No address allowed on this command");
2642 goto ret; 2928 goto ret;
2643 } 2929 }
2644 if (args[0]) { 2930 if (args[0]) {
2645 // user wants a new filename 2931 // user wants a new filename
2646 free(current_filename); 2932 exp = expand_args(args);
2647 current_filename = xstrdup(args); 2933 if (exp == NULL)
2934 goto ret;
2935 update_filename(exp);
2648 } else { 2936 } else {
2649 // user wants file status info 2937 // user wants file status info
2650 last_status_cksum = 0; // force status update 2938 last_status_cksum = 0; // force status update
@@ -2657,7 +2945,7 @@ static void colon(char *buf)
2657 rawmode(); 2945 rawmode();
2658 Hit_Return(); 2946 Hit_Return();
2659 } else if (strncmp(cmd, "list", i) == 0) { // literal print line 2947 } else if (strncmp(cmd, "list", i) == 0) { // literal print line
2660 if (b < 0) { // no addr given- use defaults 2948 if (e < 0) { // no addr given- use defaults
2661 q = begin_line(dot); // assume .,. for the range 2949 q = begin_line(dot); // assume .,. for the range
2662 r = end_line(dot); 2950 r = end_line(dot);
2663 } 2951 }
@@ -2724,23 +3012,32 @@ static void colon(char *buf)
2724 } 3012 }
2725 editing = 0; 3013 editing = 0;
2726 } else if (strncmp(cmd, "read", i) == 0) { // read file into text[] 3014 } else if (strncmp(cmd, "read", i) == 0) { // read file into text[]
2727 int size; 3015 int size, num;
2728 3016
2729 fn = args; 3017 if (args[0]) {
2730 if (!fn[0]) { 3018 // the user supplied a file name
2731 status_line_bold("No filename given"); 3019 fn = exp = expand_args(args);
3020 if (exp == NULL)
3021 goto ret;
3022 init_filename(fn);
3023 } else if (current_filename == NULL) {
3024 // no user file name, no current name- punt
3025 status_line_bold("No current filename");
2732 goto ret; 3026 goto ret;
2733 } 3027 }
2734 if (b < 0) { // no addr given- use defaults 3028 if (e < 0) { // no addr given- read after current line
2735 q = begin_line(dot); // assume "dot" 3029 q = begin_line(dot);
2736 } 3030 } else if (e == 0) { // user said ":0r foo"
2737 // read after current line- unless user said ":0r foo" 3031 q = text;
2738 if (b != 0) { 3032 } else { // addr given- read after that line
2739 q = next_line(q); 3033 q = next_line(find_line(e));
2740 // read after last line 3034 // read after last line
2741 if (q == end-1) 3035 if (q == end-1)
2742 ++q; 3036 ++q;
2743 } 3037 }
3038 num = count_lines(text, q);
3039 if (q == end)
3040 num++;
2744 { // dance around potentially-reallocated text[] 3041 { // dance around potentially-reallocated text[]
2745 uintptr_t ofs = q - text; 3042 uintptr_t ofs = q - text;
2746 size = file_insert(fn, q, 0); 3043 size = file_insert(fn, q, 0);
@@ -2757,11 +3054,7 @@ static void colon(char *buf)
2757 IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),) 3054 IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
2758 li, size 3055 li, size
2759 ); 3056 );
2760 if (size > 0) { 3057 dot = find_line(num);
2761 // if the insert is before "dot" then we need to update
2762 if (q <= dot)
2763 dot += size;
2764 }
2765 } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args 3058 } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args
2766 if (modified_count && !useforce) { 3059 if (modified_count && !useforce) {
2767 status_line_bold("No write since last change (:%s! overrides)", cmd); 3060 status_line_bold("No write since last change (:%s! overrides)", cmd);
@@ -2773,20 +3066,21 @@ static void colon(char *buf)
2773# if ENABLE_FEATURE_VI_SET 3066# if ENABLE_FEATURE_VI_SET
2774 } else if (strncmp(cmd, "set", i) == 0) { // set or clear features 3067 } else if (strncmp(cmd, "set", i) == 0) { // set or clear features
2775# if ENABLE_FEATURE_VI_SETOPTS 3068# if ENABLE_FEATURE_VI_SETOPTS
2776 char *argp; 3069 char *argp, *argn, oldch;
2777# endif 3070# endif
2778 i = 0; // offset into args
2779 // only blank is regarded as args delimiter. What about tab '\t'? 3071 // only blank is regarded as args delimiter. What about tab '\t'?
2780 if (!args[0] || strcasecmp(args, "all") == 0) { 3072 if (!args[0] || strcmp(args, "all") == 0) {
2781 // print out values of all options 3073 // print out values of all options
2782# if ENABLE_FEATURE_VI_SETOPTS 3074# if ENABLE_FEATURE_VI_SETOPTS
2783 status_line_bold( 3075 status_line_bold(
2784 "%sautoindent " 3076 "%sautoindent "
3077 "%sexpandtab "
2785 "%sflash " 3078 "%sflash "
2786 "%signorecase " 3079 "%signorecase "
2787 "%sshowmatch " 3080 "%sshowmatch "
2788 "tabstop=%u", 3081 "tabstop=%u",
2789 autoindent ? "" : "no", 3082 autoindent ? "" : "no",
3083 expandtab ? "" : "no",
2790 err_method ? "" : "no", 3084 err_method ? "" : "no",
2791 ignorecase ? "" : "no", 3085 ignorecase ? "" : "no",
2792 showmatch ? "" : "no", 3086 showmatch ? "" : "no",
@@ -2798,20 +3092,15 @@ static void colon(char *buf)
2798# if ENABLE_FEATURE_VI_SETOPTS 3092# if ENABLE_FEATURE_VI_SETOPTS
2799 argp = args; 3093 argp = args;
2800 while (*argp) { 3094 while (*argp) {
2801 if (strncmp(argp, "no", 2) == 0) 3095 i = 0;
2802 i = 2; // ":set noautoindent" 3096 if (argp[0] == 'n' && argp[1] == 'o') // "noXXX"
2803 setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT); 3097 i = 2;
2804 setops(argp, "flash " , i, "fl", VI_ERR_METHOD); 3098 argn = skip_non_whitespace(argp);
2805 setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE); 3099 oldch = *argn;
2806 setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH ); 3100 *argn = '\0';
2807 if (strncmp(argp + i, "tabstop=", 8) == 0) { 3101 setops(argp, i);
2808 int t = 0; 3102 *argn = oldch;
2809 sscanf(argp + i+8, "%u", &t); 3103 argp = skip_whitespace(argn);
2810 if (t > 0 && t <= MAX_TABSTOP)
2811 tabstop = t;
2812 }
2813 argp = skip_non_whitespace(argp);
2814 argp = skip_whitespace(argp);
2815 } 3104 }
2816# endif /* FEATURE_VI_SETOPTS */ 3105# endif /* FEATURE_VI_SETOPTS */
2817# endif /* FEATURE_VI_SET */ 3106# endif /* FEATURE_VI_SET */
@@ -2820,35 +3109,36 @@ static void colon(char *buf)
2820 } else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern 3109 } else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern
2821 char *F, *R, *flags; 3110 char *F, *R, *flags;
2822 size_t len_F, len_R; 3111 size_t len_F, len_R;
2823 int gflag; // global replace flag 3112 int gflag = 0; // global replace flag
2824# if ENABLE_FEATURE_VI_UNDO 3113 int subs = 0; // number of substitutions
2825 int dont_chain_first_item = ALLOW_UNDO; 3114# if ENABLE_FEATURE_VI_VERBOSE_STATUS
3115 int last_line = 0, lines = 0;
2826# endif 3116# endif
2827 3117
2828 // F points to the "find" pattern 3118 // F points to the "find" pattern
2829 // R points to the "replace" pattern 3119 // R points to the "replace" pattern
2830 // replace the cmd line delimiters "/" with NULs 3120 // replace the cmd line delimiters "/" with NULs
2831 c = orig_buf[1]; // what is the delimiter 3121 c = buf[1]; // what is the delimiter
2832 F = orig_buf + 2; // start of "find" 3122 F = buf + 2; // start of "find"
2833 R = strchr(F, c); // middle delimiter 3123 R = strchr(F, c); // middle delimiter
2834 if (!R) 3124 if (!R)
2835 goto colon_s_fail; 3125 goto colon_s_fail;
2836 len_F = R - F; 3126 len_F = R - F;
2837 *R++ = '\0'; // terminate "find" 3127 *R++ = '\0'; // terminate "find"
2838 flags = strchr(R, c); 3128 flags = strchr(R, c);
2839 if (!flags) 3129 if (flags) {
2840 goto colon_s_fail; 3130 *flags++ = '\0'; // terminate "replace"
2841 len_R = flags - R; 3131 gflag = *flags;
2842 *flags++ = '\0'; // terminate "replace" 3132 }
2843 gflag = *flags; 3133 len_R = strlen(R);
2844 3134
2845 q = begin_line(q); 3135 if (e < 0) { // no addr given
2846 if (b < 0) { // maybe :s/foo/bar/
2847 q = begin_line(dot); // start with cur line 3136 q = begin_line(dot); // start with cur line
2848 b = count_lines(text, q); // cur line number 3137 r = end_line(dot);
3138 b = e = count_lines(text, q); // cur line number
3139 } else if (b < 0) { // one addr given
3140 b = e;
2849 } 3141 }
2850 if (e < 0)
2851 e = b; // maybe :.s/foo/bar/
2852 3142
2853 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0 3143 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
2854 char *ls = q; // orig line start 3144 char *ls = q; // orig line start
@@ -2859,14 +3149,21 @@ static void colon(char *buf)
2859 uintptr_t bias; 3149 uintptr_t bias;
2860 // we found the "find" pattern - delete it 3150 // we found the "find" pattern - delete it
2861 // For undo support, the first item should not be chained 3151 // For undo support, the first item should not be chained
2862 text_hole_delete(found, found + len_F - 1, dont_chain_first_item); 3152 text_hole_delete(found, found + len_F - 1,
2863# if ENABLE_FEATURE_VI_UNDO 3153 subs ? ALLOW_UNDO_CHAIN: ALLOW_UNDO);
2864 dont_chain_first_item = ALLOW_UNDO_CHAIN; 3154 // can't do this above, no undo => no third argument
3155 subs++;
3156# if ENABLE_FEATURE_VI_VERBOSE_STATUS
3157 if (last_line != i) {
3158 last_line = i;
3159 ++lines;
3160 }
2865# endif 3161# endif
2866 // insert the "replace" patern 3162 // insert the "replace" patern
2867 bias = string_insert(found, R, ALLOW_UNDO_CHAIN); 3163 bias = string_insert(found, R, ALLOW_UNDO_CHAIN);
2868 found += bias; 3164 found += bias;
2869 ls += bias; 3165 ls += bias;
3166 dot = ls;
2870 //q += bias; - recalculated anyway 3167 //q += bias; - recalculated anyway
2871 // check for "global" :s/foo/bar/g 3168 // check for "global" :s/foo/bar/g
2872 if (gflag == 'g') { 3169 if (gflag == 'g') {
@@ -2878,12 +3175,21 @@ static void colon(char *buf)
2878 } 3175 }
2879 q = next_line(ls); 3176 q = next_line(ls);
2880 } 3177 }
3178 if (subs == 0) {
3179 status_line_bold("No match");
3180 } else {
3181 dot_skip_over_ws();
3182# if ENABLE_FEATURE_VI_VERBOSE_STATUS
3183 if (subs > 1)
3184 status_line("%d substitutions on %d lines", subs, lines);
3185# endif
3186 }
2881# endif /* FEATURE_VI_SEARCH */ 3187# endif /* FEATURE_VI_SEARCH */
2882 } else if (strncmp(cmd, "version", i) == 0) { // show software version 3188 } else if (strncmp(cmd, "version", i) == 0) { // show software version
2883 status_line(BB_VER); 3189 status_line(BB_VER);
2884 } else if (strncmp(cmd, "write", i) == 0 // write text to file 3190 } else if (strncmp(cmd, "write", i) == 0 // write text to file
2885 || strncmp(cmd, "wq", i) == 0 3191 || strcmp(cmd, "wq") == 0
2886 || strncmp(cmd, "wn", i) == 0 3192 || strcmp(cmd, "wn") == 0
2887 || (cmd[0] == 'x' && !cmd[1]) 3193 || (cmd[0] == 'x' && !cmd[1])
2888 ) { 3194 ) {
2889 int size; 3195 int size;
@@ -2891,10 +3197,21 @@ static void colon(char *buf)
2891 3197
2892 // is there a file name to write to? 3198 // is there a file name to write to?
2893 if (args[0]) { 3199 if (args[0]) {
2894 fn = args; 3200 struct stat statbuf;
3201
3202 exp = expand_args(args);
3203 if (exp == NULL)
3204 goto ret;
3205 if (!useforce && (fn == NULL || strcmp(fn, exp) != 0) &&
3206 stat(exp, &statbuf) == 0) {
3207 status_line_bold("File exists (:w! overrides)");
3208 goto ret;
3209 }
3210 fn = exp;
3211 init_filename(fn);
2895 } 3212 }
2896# if ENABLE_FEATURE_VI_READONLY 3213# if ENABLE_FEATURE_VI_READONLY
2897 if (readonly_mode && !useforce) { 3214 else if (readonly_mode && !useforce && fn) {
2898 status_line_bold("'%s' is read only", fn); 3215 status_line_bold("'%s' is read only", fn);
2899 goto ret; 3216 goto ret;
2900 } 3217 }
@@ -2930,10 +3247,20 @@ static void colon(char *buf)
2930 modified_count = 0; 3247 modified_count = 0;
2931 last_modified_count = -1; 3248 last_modified_count = -1;
2932 } 3249 }
2933 if (cmd[0] == 'x' 3250 if (cmd[1] == 'n') {
2934 || cmd[1] == 'q' || cmd[1] == 'n' 3251 editing = 0;
2935 || cmd[1] == 'Q' || cmd[1] == 'N' 3252 } else if (cmd[0] == 'x' || cmd[1] == 'q') {
2936 ) { 3253 // are there other files to edit?
3254 int n = cmdline_filecnt - optind - 1;
3255 if (n > 0) {
3256 if (useforce) {
3257 // force end of argv list
3258 optind = cmdline_filecnt;
3259 } else {
3260 status_line_bold("%u more file(s) to edit", n);
3261 goto ret;
3262 }
3263 }
2937 editing = 0; 3264 editing = 0;
2938 } 3265 }
2939 } 3266 }
@@ -2944,7 +3271,7 @@ static void colon(char *buf)
2944 q = begin_line(dot); // assume .,. for the range 3271 q = begin_line(dot); // assume .,. for the range
2945 r = end_line(dot); 3272 r = end_line(dot);
2946 } 3273 }
2947 text_yank(q, r, YDreg); 3274 text_yank(q, r, YDreg, WHOLE);
2948 li = count_lines(q, r); 3275 li = count_lines(q, r);
2949 status_line("Yank %d lines (%d chars) into [%c]", 3276 status_line("Yank %d lines (%d chars) into [%c]",
2950 li, strlen(reg[YDreg]), what_reg()); 3277 li, strlen(reg[YDreg]), what_reg());
@@ -2954,6 +3281,9 @@ static void colon(char *buf)
2954 not_implemented(cmd); 3281 not_implemented(cmd);
2955 } 3282 }
2956 ret: 3283 ret:
3284# if ENABLE_FEATURE_VI_COLON_EXPAND
3285 free(exp);
3286# endif
2957 dot = bound_dot(dot); // make sure "dot" is valid 3287 dot = bound_dot(dot); // make sure "dot" is valid
2958 return; 3288 return;
2959# if ENABLE_FEATURE_VI_SEARCH 3289# if ENABLE_FEATURE_VI_SEARCH
@@ -3067,83 +3397,109 @@ static void int_handler(int sig)
3067 3397
3068static void do_cmd(int c); 3398static void do_cmd(int c);
3069 3399
3070static int find_range(char **start, char **stop, char c) 3400static int at_eof(const char *s)
3071{ 3401{
3072 char *save_dot, *p, *q, *t; 3402 // does 's' point to end of file, even with no terminating newline?
3073 int cnt, multiline = 0, forward; 3403 return ((s == end - 2 && s[1] == '\n') || s == end - 1);
3404}
3405
3406static int find_range(char **start, char **stop, int cmd)
3407{
3408 char *p, *q, *t;
3409 int buftype = -1;
3410 int c;
3074 3411
3075 save_dot = dot;
3076 p = q = dot; 3412 p = q = dot;
3077 3413
3078 // will a 'G' command move forwards or backwards? 3414#if ENABLE_FEATURE_VI_YANKMARK
3079 forward = cmdcnt == 0 || cmdcnt > count_lines(text, dot); 3415 if (cmd == 'Y') {
3416 c = 'y';
3417 } else
3418#endif
3419 {
3420 c = get_motion_char();
3421 }
3080 3422
3081 if (strchr("cdy><", c)) { 3423#if ENABLE_FEATURE_VI_YANKMARK
3424 if ((cmd == 'Y' || cmd == c) && strchr("cdy><", c)) {
3425#else
3426 if (cmd == c && strchr("cd><", c)) {
3427#endif
3082 // these cmds operate on whole lines 3428 // these cmds operate on whole lines
3083 p = q = begin_line(p); 3429 buftype = WHOLE;
3084 for (cnt = 1; cnt < cmdcnt; cnt++) { 3430 if (--cmdcnt > 0)
3085 q = next_line(q); 3431 do_cmd('j');
3086 } 3432 } else if (strchr("^%$0bBeEfFtThnN/?|{}\b\177", c)) {
3087 q = end_line(q); 3433 // Most operate on char positions within a line. Of those that
3088 } else if (strchr("^%$0bBeEfth\b\177", c)) { 3434 // don't '%' needs no special treatment, search commands are
3089 // These cmds operate on char positions 3435 // marked as MULTI and "{}" are handled below.
3436 buftype = strchr("nN/?", c) ? MULTI : PARTIAL;
3090 do_cmd(c); // execute movement cmd 3437 do_cmd(c); // execute movement cmd
3091 q = dot; 3438 if (p == dot) // no movement is an error
3439 buftype = -1;
3092 } else if (strchr("wW", c)) { 3440 } else if (strchr("wW", c)) {
3441 buftype = MULTI;
3093 do_cmd(c); // execute movement cmd 3442 do_cmd(c); // execute movement cmd
3094 // if we are at the next word's first char 3443 // step back one char, but not if we're at end of file,
3095 // step back one char 3444 // or if we are at EOF and search was for 'w' and we're at
3096 // but check the possibilities when it is true 3445 // the start of a 'W' word.
3097 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0])) 3446 if (dot > p && (!at_eof(dot) || (c == 'w' && ispunct(*dot))))
3098 || (ispunct(dot[-1]) && !ispunct(dot[0])) 3447 dot--;
3099 || (isalnum(dot[-1]) && !isalnum(dot[0])))) 3448 t = dot;
3100 dot--; // move back off of next word 3449 // don't include trailing WS as part of word
3101 if (dot > text && *dot == '\n') 3450 while (dot > p && isspace(*dot)) {
3102 dot--; // stay off NL 3451 if (*dot-- == '\n')
3103 q = dot; 3452 t = dot;
3104 } else if (strchr("H-k{", c) || (c == 'G' && !forward)) { 3453 }
3105 // these operate on multi-lines backwards 3454 // for non-change operations WS after NL is not part of word
3106 q = end_line(dot); // find NL 3455 if (cmd != 'c' && dot != t && *dot != '\n')
3107 do_cmd(c); // execute movement cmd 3456 dot = t;
3108 dot_begin(); 3457 } else if (strchr("GHL+-jk'\r\n", c)) {
3109 p = dot; 3458 // these operate on whole lines
3110 } else if (strchr("L+j}\r\n", c) || (c == 'G' && forward)) { 3459 buftype = WHOLE;
3111 // these operate on multi-lines forwards
3112 p = begin_line(dot);
3113 do_cmd(c); // execute movement cmd 3460 do_cmd(c); // execute movement cmd
3114 dot_end(); // find NL 3461 } else if (c == ' ' || c == 'l') {
3115 q = dot;
3116 } else /* if (c == ' ' || c == 'l') */ {
3117 // forward motion by character 3462 // forward motion by character
3118 int tmpcnt = (cmdcnt ?: 1); 3463 int tmpcnt = (cmdcnt ?: 1);
3464 buftype = PARTIAL;
3119 do_cmd(c); // execute movement cmd 3465 do_cmd(c); // execute movement cmd
3120 // exclude last char unless range isn't what we expected 3466 // exclude last char unless range isn't what we expected
3121 // this indicates we've hit EOL 3467 // this indicates we've hit EOL
3122 if (tmpcnt == dot - p) 3468 if (tmpcnt == dot - p)
3123 dot--; 3469 dot--;
3124 q = dot;
3125 } 3470 }
3471
3472 if (buftype == -1) {
3473 if (c != 27)
3474 indicate_error();
3475 return buftype;
3476 }
3477
3478 q = dot;
3126 if (q < p) { 3479 if (q < p) {
3127 t = q; 3480 t = q;
3128 q = p; 3481 q = p;
3129 p = t; 3482 p = t;
3130 } 3483 }
3131 3484
3132 // backward char movements don't include start position 3485 // movements which don't include end of range
3133 if (q > p && strchr("^0bBh\b\177", c)) q--; 3486 if (q > p) {
3134 3487 if (strchr("^0bBFThnN/?|\b\177", c)) {
3135 multiline = 0; 3488 q--;
3136 for (t = p; t <= q; t++) { 3489 } else if (strchr("{}", c)) {
3137 if (*t == '\n') { 3490 buftype = (p == begin_line(p) && (*q == '\n' || at_eof(q))) ?
3138 multiline = 1; 3491 WHOLE : MULTI;
3139 break; 3492 if (!at_eof(q)) {
3493 q--;
3494 if (q > p && p != begin_line(p))
3495 q--;
3496 }
3140 } 3497 }
3141 } 3498 }
3142 3499
3143 *start = p; 3500 *start = p;
3144 *stop = q; 3501 *stop = q;
3145 dot = save_dot; 3502 return buftype;
3146 return multiline;
3147} 3503}
3148 3504
3149//--------------------------------------------------------------------- 3505//---------------------------------------------------------------------
@@ -3174,11 +3530,19 @@ static void do_cmd(int c)
3174 int dir; 3530 int dir;
3175 int cnt, i, j; 3531 int cnt, i, j;
3176 int c1; 3532 int c1;
3533#if ENABLE_FEATURE_VI_YANKMARK
3534 char *orig_dot = dot;
3535#endif
3536#if ENABLE_FEATURE_VI_UNDO
3537 int allow_undo = ALLOW_UNDO;
3538 int undo_del = UNDO_DEL;
3539#endif
3177 3540
3178// c1 = c; // quiet the compiler 3541// c1 = c; // quiet the compiler
3179// cnt = yf = 0; // quiet the compiler 3542// cnt = yf = 0; // quiet the compiler
3180// p = q = save_dot = buf; // quiet the compiler 3543// p = q = save_dot = buf; // quiet the compiler
3181 memset(buf, '\0', sizeof(buf)); 3544 memset(buf, '\0', sizeof(buf));
3545 keep_index = FALSE;
3182 3546
3183 show_status_line(); 3547 show_status_line();
3184 3548
@@ -3208,7 +3572,7 @@ static void do_cmd(int c)
3208 } else { 3572 } else {
3209 if (1 <= c || Isprint(c)) { 3573 if (1 <= c || Isprint(c)) {
3210 if (c != 27) 3574 if (c != 27)
3211 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char 3575 dot = yank_delete(dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO); // delete char
3212 dot = char_insert(dot, c, ALLOW_UNDO_CHAIN); // insert new char 3576 dot = char_insert(dot, c, ALLOW_UNDO_CHAIN); // insert new char
3213 } 3577 }
3214 goto dc1; 3578 goto dc1;
@@ -3254,11 +3618,9 @@ static void do_cmd(int c)
3254 //case '*': // *- 3618 //case '*': // *-
3255 //case '=': // =- 3619 //case '=': // =-
3256 //case '@': // @- 3620 //case '@': // @-
3257 //case 'F': // F-
3258 //case 'K': // K- 3621 //case 'K': // K-
3259 //case 'Q': // Q- 3622 //case 'Q': // Q-
3260 //case 'S': // S- 3623 //case 'S': // S-
3261 //case 'T': // T-
3262 //case 'V': // V- 3624 //case 'V': // V-
3263 //case '[': // [- 3625 //case '[': // [-
3264 //case '\\': // \- 3626 //case '\\': // \-
@@ -3303,9 +3665,10 @@ static void do_cmd(int c)
3303 case KEYCODE_DOWN: // cursor key Down 3665 case KEYCODE_DOWN: // cursor key Down
3304 do { 3666 do {
3305 dot_next(); // go to next B-o-l 3667 dot_next(); // go to next B-o-l
3306 // try stay in same col
3307 dot = move_to_col(dot, ccol + offset);
3308 } while (--cmdcnt > 0); 3668 } while (--cmdcnt > 0);
3669 // try to stay in saved column
3670 dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex);
3671 keep_index = TRUE;
3309 break; 3672 break;
3310 case 12: // ctrl-L force redraw whole screen 3673 case 12: // ctrl-L force redraw whole screen
3311 case 18: // ctrl-R force redraw 3674 case 18: // ctrl-R force redraw
@@ -3315,8 +3678,8 @@ static void do_cmd(int c)
3315 case '+': // +- goto next line 3678 case '+': // +- goto next line
3316 do { 3679 do {
3317 dot_next(); 3680 dot_next();
3318 dot_skip_over_ws();
3319 } while (--cmdcnt > 0); 3681 } while (--cmdcnt > 0);
3682 dot_skip_over_ws();
3320 break; 3683 break;
3321 case 21: // ctrl-U scroll up half screen 3684 case 21: // ctrl-U scroll up half screen
3322 dot_scroll((rows - 2) / 2, -1); 3685 dot_scroll((rows - 2) / 2, -1);
@@ -3363,6 +3726,9 @@ static void do_cmd(int c)
3363 dot = swap_context(dot); // swap current and previous context 3726 dot = swap_context(dot); // swap current and previous context
3364 dot_begin(); // go to B-o-l 3727 dot_begin(); // go to B-o-l
3365 dot_skip_over_ws(); 3728 dot_skip_over_ws();
3729#if ENABLE_FEATURE_VI_YANKMARK
3730 orig_dot = dot; // this doesn't update stored contexts
3731#endif
3366 } else { 3732 } else {
3367 indicate_error(); 3733 indicate_error();
3368 } 3734 }
@@ -3387,12 +3753,14 @@ static void do_cmd(int c)
3387 status_line_bold("Nothing in register %c", what_reg()); 3753 status_line_bold("Nothing in register %c", what_reg());
3388 break; 3754 break;
3389 } 3755 }
3756 cnt = 0;
3757 i = cmdcnt ?: 1;
3390 // are we putting whole lines or strings 3758 // are we putting whole lines or strings
3391 if (strchr(p, '\n') != NULL) { 3759 if (regtype[YDreg] == WHOLE) {
3392 if (c == 'P') { 3760 if (c == 'P') {
3393 dot_begin(); // putting lines- Put above 3761 dot_begin(); // putting lines- Put above
3394 } 3762 }
3395 if (c == 'p') { 3763 else /* if ( c == 'p') */ {
3396 // are we putting after very last line? 3764 // are we putting after very last line?
3397 if (end_line(dot) == (end - 1)) { 3765 if (end_line(dot) == (end - 1)) {
3398 dot = end; // force dot to end of text[] 3766 dot = end; // force dot to end of text[]
@@ -3403,8 +3771,21 @@ static void do_cmd(int c)
3403 } else { 3771 } else {
3404 if (c == 'p') 3772 if (c == 'p')
3405 dot_right(); // move to right, can move to NL 3773 dot_right(); // move to right, can move to NL
3774 // how far to move cursor if register doesn't have a NL
3775 if (strchr(p, '\n') == NULL)
3776 cnt = i * strlen(p) - 1;
3406 } 3777 }
3407 string_insert(dot, p, ALLOW_UNDO); // insert the string 3778 do {
3779 // dot is adjusted if text[] is reallocated so we don't have to
3780 string_insert(dot, p, allow_undo); // insert the string
3781# if ENABLE_FEATURE_VI_UNDO
3782 allow_undo = ALLOW_UNDO_CHAIN;
3783# endif
3784 } while (--cmdcnt > 0);
3785 dot += cnt;
3786# if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS
3787 yank_status("Put", p, i);
3788# endif
3408 end_cmd_q(); // stop adding to q 3789 end_cmd_q(); // stop adding to q
3409 break; 3790 break;
3410 case 'U': // U- Undo; replace current line with original version 3791 case 'U': // U- Undo; replace current line with original version
@@ -3415,6 +3796,9 @@ static void do_cmd(int c)
3415 p += string_insert(p, reg[Ureg], ALLOW_UNDO_CHAIN); // insert orig line 3796 p += string_insert(p, reg[Ureg], ALLOW_UNDO_CHAIN); // insert orig line
3416 dot = p; 3797 dot = p;
3417 dot_skip_over_ws(); 3798 dot_skip_over_ws();
3799# if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS
3800 yank_status("Undo", reg[Ureg], 1);
3801# endif
3418 } 3802 }
3419 break; 3803 break;
3420#endif /* FEATURE_VI_YANKMARK */ 3804#endif /* FEATURE_VI_YANKMARK */
@@ -3431,6 +3815,8 @@ static void do_cmd(int c)
3431 break; 3815 break;
3432 dot_next(); 3816 dot_next();
3433 } 3817 }
3818 cindex = C_END;
3819 keep_index = TRUE;
3434 break; 3820 break;
3435 case '%': // %- find matching char of pair () [] {} 3821 case '%': // %- find matching char of pair () [] {}
3436 for (q = dot; q < end && *q != '\n'; q++) { 3822 for (q = dot; q < end && *q != '\n'; q++) {
@@ -3449,129 +3835,109 @@ static void do_cmd(int c)
3449 indicate_error(); 3835 indicate_error();
3450 break; 3836 break;
3451 case 'f': // f- forward to a user specified char 3837 case 'f': // f- forward to a user specified char
3452 last_forward_char = get_one_char(); // get the search char 3838 case 'F': // F- backward to a user specified char
3453 // 3839 case 't': // t- move to char prior to next x
3454 // dont separate these two commands. 'f' depends on ';' 3840 case 'T': // T- move to char after previous x
3455 // 3841 last_search_char = get_one_char(); // get the search char
3456 //**** fall through to ... ';' 3842 last_search_cmd = c;
3457 case ';': // ;- look at rest of line for last forward char 3843 // fall through
3458 do { 3844 case ';': // ;- look at rest of line for last search char
3459 if (last_forward_char == 0) 3845 case ',': // ,- repeat latest search in opposite direction
3460 break; 3846 dot_to_char(c != ',' ? last_search_cmd : last_search_cmd ^ 0x20);
3461 q = dot + 1;
3462 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3463 q++;
3464 }
3465 if (*q == last_forward_char)
3466 dot = q;
3467 } while (--cmdcnt > 0);
3468 break;
3469 case ',': // repeat latest 'f' in opposite direction
3470 if (last_forward_char == 0)
3471 break;
3472 do {
3473 q = dot - 1;
3474 while (q >= text && *q != '\n' && *q != last_forward_char) {
3475 q--;
3476 }
3477 if (q >= text && *q == last_forward_char)
3478 dot = q;
3479 } while (--cmdcnt > 0);
3480 break; 3847 break;
3481
3482 case '-': // -- goto prev line 3848 case '-': // -- goto prev line
3483 do { 3849 do {
3484 dot_prev(); 3850 dot_prev();
3485 dot_skip_over_ws();
3486 } while (--cmdcnt > 0); 3851 } while (--cmdcnt > 0);
3852 dot_skip_over_ws();
3487 break; 3853 break;
3488#if ENABLE_FEATURE_VI_DOT_CMD 3854#if ENABLE_FEATURE_VI_DOT_CMD
3489 case '.': // .- repeat the last modifying command 3855 case '.': // .- repeat the last modifying command
3490 // Stuff the last_modifying_cmd back into stdin 3856 // Stuff the last_modifying_cmd back into stdin
3491 // and let it be re-executed. 3857 // and let it be re-executed.
3492 if (lmc_len != 0) { 3858 if (lmc_len != 0) {
3493 ioq = ioq_start = xstrndup(last_modifying_cmd, lmc_len); 3859 if (cmdcnt) // update saved count if current count is non-zero
3860 dotcnt = cmdcnt;
3861 last_modifying_cmd[lmc_len] = '\0';
3862 ioq = ioq_start = xasprintf("%u%s", dotcnt, last_modifying_cmd);
3494 } 3863 }
3495 break; 3864 break;
3496#endif 3865#endif
3497#if ENABLE_FEATURE_VI_SEARCH 3866#if ENABLE_FEATURE_VI_SEARCH
3498 case '?': // /- search for a pattern 3867 case 'N': // N- backward search for last pattern
3499 case '/': // /- search for a pattern 3868 dir = last_search_pattern[0] == '/' ? BACK : FORWARD;
3869 goto dc4; // now search for pattern
3870 break;
3871 case '?': // ?- backward search for a pattern
3872 case '/': // /- forward search for a pattern
3500 buf[0] = c; 3873 buf[0] = c;
3501 buf[1] = '\0'; 3874 buf[1] = '\0';
3502 q = get_input_line(buf); // get input line- use "status line" 3875 q = get_input_line(buf); // get input line- use "status line"
3503 if (q[0] && !q[1]) { 3876 if (!q[0]) // user changed mind and erased the "/"- do nothing
3877 break;
3878 if (!q[1]) { // if no pat re-use old pat
3504 if (last_search_pattern[0]) 3879 if (last_search_pattern[0])
3505 last_search_pattern[0] = c; 3880 last_search_pattern[0] = c;
3506 goto dc3; // if no pat re-use old pat 3881 } else { // strlen(q) > 1: new pat- save it and find
3507 }
3508 if (q[0]) { // strlen(q) > 1: new pat- save it and find
3509 // there is a new pat
3510 free(last_search_pattern); 3882 free(last_search_pattern);
3511 last_search_pattern = xstrdup(q); 3883 last_search_pattern = xstrdup(q);
3512 goto dc3; // now find the pattern
3513 }
3514 // user changed mind and erased the "/"- do nothing
3515 break;
3516 case 'N': // N- backward search for last pattern
3517 dir = BACK; // assume BACKWARD search
3518 p = dot - 1;
3519 if (last_search_pattern[0] == '?') {
3520 dir = FORWARD;
3521 p = dot + 1;
3522 } 3884 }
3523 goto dc4; // now search for pattern 3885 // fall through
3524 break;
3525 case 'n': // n- repeat search for last pattern 3886 case 'n': // n- repeat search for last pattern
3526 // search rest of text[] starting at next char 3887 // search rest of text[] starting at next char
3527 // if search fails return orignal "p" not the "p+1" address 3888 // if search fails "dot" is unchanged
3528 do { 3889 dir = last_search_pattern[0] == '/' ? FORWARD : BACK;
3529 const char *msg;
3530 dc3:
3531 dir = FORWARD; // assume FORWARD search
3532 p = dot + 1;
3533 if (last_search_pattern[0] == '?') {
3534 dir = BACK;
3535 p = dot - 1;
3536 }
3537 dc4: 3890 dc4:
3538 q = char_search(p, last_search_pattern + 1, (dir << 1) | FULL); 3891 if (last_search_pattern[1] == '\0') {
3892 status_line_bold("No previous search");
3893 break;
3894 }
3895 do {
3896 q = char_search(dot + dir, last_search_pattern + 1,
3897 (dir << 1) | FULL);
3539 if (q != NULL) { 3898 if (q != NULL) {
3540 dot = q; // good search, update "dot" 3899 dot = q; // good search, update "dot"
3541 msg = NULL;
3542 goto dc2;
3543 }
3544 // no pattern found between "dot" and "end"- continue at top
3545 p = text;
3546 if (dir == BACK) {
3547 p = end - 1;
3548 }
3549 q = char_search(p, last_search_pattern + 1, (dir << 1) | FULL);
3550 if (q != NULL) { // found something
3551 dot = q; // found new pattern- goto it
3552 msg = "search hit BOTTOM, continuing at TOP";
3553 if (dir == BACK) {
3554 msg = "search hit TOP, continuing at BOTTOM";
3555 }
3556 } else { 3900 } else {
3557 msg = "Pattern not found"; 3901 // no pattern found between "dot" and top/bottom of file
3902 // continue from other end of file
3903 const char *msg;
3904 q = char_search(dir == FORWARD ? text : end - 1,
3905 last_search_pattern + 1, (dir << 1) | FULL);
3906 if (q != NULL) { // found something
3907 dot = q; // found new pattern- goto it
3908 msg = "search hit %s, continuing at %s";
3909 } else { // pattern is nowhere in file
3910 cmdcnt = 0; // force exit from loop
3911 msg = "Pattern not found";
3912 }
3913 if (dir == FORWARD)
3914 status_line_bold(msg, "BOTTOM", "TOP");
3915 else
3916 status_line_bold(msg, "TOP", "BOTTOM");
3558 } 3917 }
3559 dc2:
3560 if (msg)
3561 status_line_bold("%s", msg);
3562 } while (--cmdcnt > 0); 3918 } while (--cmdcnt > 0);
3563 break; 3919 break;
3564 case '{': // {- move backward paragraph 3920 case '{': // {- move backward paragraph
3565 q = char_search(dot, "\n\n", ((unsigned)BACK << 1) | FULL);
3566 if (q != NULL) { // found blank line
3567 dot = next_line(q); // move to next blank line
3568 }
3569 break;
3570 case '}': // }- move forward paragraph 3921 case '}': // }- move forward paragraph
3571 q = char_search(dot, "\n\n", (FORWARD << 1) | FULL); 3922 dir = c == '}' ? FORWARD : BACK;
3572 if (q != NULL) { // found blank line 3923 do {
3573 dot = next_line(q); // move to next blank line 3924 int skip = TRUE; // initially skip consecutive empty lines
3574 } 3925 while (dir == FORWARD ? dot < end - 1 : dot > text) {
3926 if (*dot == '\n' && dot[dir] == '\n') {
3927 if (!skip) {
3928 if (dir == FORWARD)
3929 ++dot; // move to next blank line
3930 goto dc2;
3931 }
3932 }
3933 else {
3934 skip = FALSE;
3935 }
3936 dot += dir;
3937 }
3938 goto dc6; // end of file
3939 dc2: continue;
3940 } while (--cmdcnt > 0);
3575 break; 3941 break;
3576#endif /* FEATURE_VI_SEARCH */ 3942#endif /* FEATURE_VI_SEARCH */
3577 case '0': // 0- goto beginning of line 3943 case '0': // 0- goto beginning of line
@@ -3597,28 +3963,31 @@ static void do_cmd(int c)
3597 case '<': // <- Left shift something 3963 case '<': // <- Left shift something
3598 case '>': // >- Right shift something 3964 case '>': // >- Right shift something
3599 cnt = count_lines(text, dot); // remember what line we are on 3965 cnt = count_lines(text, dot); // remember what line we are on
3600 c1 = get_one_char(); // get the type of thing to delete 3966 if (find_range(&p, &q, c) == -1)
3601 find_range(&p, &q, c1); 3967 goto dc6;
3602 yank_delete(p, q, 1, YANKONLY, NO_UNDO); // save copy before change
3603 p = begin_line(p);
3604 q = end_line(q);
3605 i = count_lines(p, q); // # of lines we are shifting 3968 i = count_lines(p, q); // # of lines we are shifting
3606 for ( ; i > 0; i--, p = next_line(p)) { 3969 for (p = begin_line(p); i > 0; i--, p = next_line(p)) {
3607 if (c == '<') { 3970 if (c == '<') {
3608 // shift left- remove tab or 8 spaces 3971 // shift left- remove tab or tabstop spaces
3609 if (*p == '\t') { 3972 if (*p == '\t') {
3610 // shrink buffer 1 char 3973 // shrink buffer 1 char
3611 text_hole_delete(p, p, NO_UNDO); 3974 text_hole_delete(p, p, allow_undo);
3612 } else if (*p == ' ') { 3975 } else if (*p == ' ') {
3613 // we should be calculating columns, not just SPACE 3976 // we should be calculating columns, not just SPACE
3614 for (j = 0; *p == ' ' && j < tabstop; j++) { 3977 for (j = 0; *p == ' ' && j < tabstop; j++) {
3615 text_hole_delete(p, p, NO_UNDO); 3978 text_hole_delete(p, p, allow_undo);
3979#if ENABLE_FEATURE_VI_UNDO
3980 allow_undo = ALLOW_UNDO_CHAIN;
3981#endif
3616 } 3982 }
3617 } 3983 }
3618 } else if (c == '>') { 3984 } else /* if (c == '>') */ {
3619 // shift right -- add tab or 8 spaces 3985 // shift right -- add tab or tabstop spaces
3620 char_insert(p, '\t', ALLOW_UNDO); 3986 char_insert(p, '\t', allow_undo);
3621 } 3987 }
3988#if ENABLE_FEATURE_VI_UNDO
3989 allow_undo = ALLOW_UNDO_CHAIN;
3990#endif
3622 } 3991 }
3623 dot = find_line(cnt); // what line were we on 3992 dot = find_line(cnt); // what line were we on
3624 dot_skip_over_ws(); 3993 dot_skip_over_ws();
@@ -3652,7 +4021,7 @@ static void do_cmd(int c)
3652 save_dot = dot; 4021 save_dot = dot;
3653 dot = dollar_line(dot); // move to before NL 4022 dot = dollar_line(dot); // move to before NL
3654 // copy text into a register and delete 4023 // copy text into a register and delete
3655 dot = yank_delete(save_dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete to e-o-l 4024 dot = yank_delete(save_dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO); // delete to e-o-l
3656 if (c == 'C') 4025 if (c == 'C')
3657 goto dc_i; // start inserting 4026 goto dc_i; // start inserting
3658#if ENABLE_FEATURE_VI_DOT_CMD 4027#if ENABLE_FEATURE_VI_DOT_CMD
@@ -3679,6 +4048,7 @@ static void do_cmd(int c)
3679 if (cmdcnt > 0) { 4048 if (cmdcnt > 0) {
3680 dot = find_line(cmdcnt); // what line is #cmdcnt 4049 dot = find_line(cmdcnt); // what line is #cmdcnt
3681 } 4050 }
4051 dot_begin();
3682 dot_skip_over_ws(); 4052 dot_skip_over_ws();
3683 break; 4053 break;
3684 case 'H': // H- goto top line on screen 4054 case 'H': // H- goto top line on screen
@@ -3735,20 +4105,21 @@ static void do_cmd(int c)
3735 dot = screenbegin; 4105 dot = screenbegin;
3736 for (cnt = 0; cnt < (rows-1) / 2; cnt++) 4106 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3737 dot = next_line(dot); 4107 dot = next_line(dot);
4108 dot_skip_over_ws();
3738 break; 4109 break;
3739 case 'O': // O- open a empty line above 4110 case 'O': // O- open an empty line above
3740 // 0i\n ESC -i 4111 dot_begin();
3741 p = begin_line(dot); 4112 set_openabove();
3742 if (p[-1] == '\n') { 4113 goto dc3;
4114 case 'o': // o- open an empty line below
4115 dot_end();
4116 dc3:
4117 dot = char_insert(dot, '\n', ALLOW_UNDO);
4118 if (c == 'O' && !autoindent) {
4119 // done in char_insert() for openabove+autoindent
3743 dot_prev(); 4120 dot_prev();
3744 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3745 dot_end();
3746 dot = char_insert(dot, '\n', ALLOW_UNDO);
3747 } else {
3748 dot_begin(); // 0
3749 dot = char_insert(dot, '\n', ALLOW_UNDO); // i\n ESC
3750 dot_prev(); // -
3751 } 4121 }
4122 clear_openabove();
3752 goto dc_i; 4123 goto dc_i;
3753 break; 4124 break;
3754 case 'R': // R- continuous Replace char 4125 case 'R': // R- continuous Replace char
@@ -3758,7 +4129,7 @@ static void do_cmd(int c)
3758 break; 4129 break;
3759 case KEYCODE_DELETE: 4130 case KEYCODE_DELETE:
3760 if (dot < end - 1) 4131 if (dot < end - 1)
3761 dot = yank_delete(dot, dot, 1, YANKDEL, ALLOW_UNDO); 4132 dot = yank_delete(dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO);
3762 break; 4133 break;
3763 case 'X': // X- delete char before dot 4134 case 'X': // X- delete char before dot
3764 case 'x': // x- delete the current char 4135 case 'x': // x- delete the current char
@@ -3770,7 +4141,10 @@ static void do_cmd(int c)
3770 if (dot[dir] != '\n') { 4141 if (dot[dir] != '\n') {
3771 if (c == 'X') 4142 if (c == 'X')
3772 dot--; // delete prev char 4143 dot--; // delete prev char
3773 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char 4144 dot = yank_delete(dot, dot, PARTIAL, YANKDEL, allow_undo); // delete char
4145#if ENABLE_FEATURE_VI_UNDO
4146 allow_undo = ALLOW_UNDO_CHAIN;
4147#endif
3774 } 4148 }
3775 } while (--cmdcnt > 0); 4149 } while (--cmdcnt > 0);
3776 end_cmd_q(); // stop adding to q 4150 end_cmd_q(); // stop adding to q
@@ -3785,7 +4159,7 @@ static void do_cmd(int c)
3785 break; 4159 break;
3786 } 4160 }
3787 if (modified_count) { 4161 if (modified_count) {
3788 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) { 4162 if (ENABLE_FEATURE_VI_READONLY && readonly_mode && current_filename) {
3789 status_line_bold("'%s' is read only", current_filename); 4163 status_line_bold("'%s' is read only", current_filename);
3790 break; 4164 break;
3791 } 4165 }
@@ -3799,6 +4173,14 @@ static void do_cmd(int c)
3799 } else { 4173 } else {
3800 editing = 0; 4174 editing = 0;
3801 } 4175 }
4176 // are there other files to edit?
4177 j = cmdline_filecnt - optind - 1;
4178 if (editing == 0 && j > 0) {
4179 editing = 1;
4180 modified_count = 0;
4181 last_modified_count = -1;
4182 status_line_bold("%u more file(s) to edit", j);
4183 }
3802 break; 4184 break;
3803 case '^': // ^- move to first non-blank on line 4185 case '^': // ^- move to first non-blank on line
3804 dot_begin(); 4186 dot_begin();
@@ -3830,103 +4212,76 @@ static void do_cmd(int c)
3830 case 'Y': // Y- Yank a line 4212 case 'Y': // Y- Yank a line
3831#endif 4213#endif
3832 { 4214 {
3833 int yf, ml, whole = 0; 4215 int yf = YANKDEL; // assume either "c" or "d"
3834 yf = YANKDEL; // assume either "c" or "d" 4216 int buftype;
3835#if ENABLE_FEATURE_VI_YANKMARK 4217#if ENABLE_FEATURE_VI_YANKMARK
4218# if ENABLE_FEATURE_VI_VERBOSE_STATUS
4219 char *savereg = reg[YDreg];
4220# endif
3836 if (c == 'y' || c == 'Y') 4221 if (c == 'y' || c == 'Y')
3837 yf = YANKONLY; 4222 yf = YANKONLY;
3838#endif 4223#endif
3839 c1 = 'y';
3840 if (c != 'Y')
3841 c1 = get_one_char(); // get the type of thing to delete
3842 // determine range, and whether it spans lines 4224 // determine range, and whether it spans lines
3843 ml = find_range(&p, &q, c1); 4225 buftype = find_range(&p, &q, c);
3844 place_cursor(0, 0); 4226 if (buftype == -1) // invalid range
3845 if (c1 == 27) { // ESC- user changed mind and wants out 4227 goto dc6;
3846 c = c1 = 27; // Escape- do nothing 4228 if (buftype == WHOLE) {
3847 } else if (strchr("wW", c1)) { 4229 save_dot = p; // final cursor position is start of range
3848 ml = 0; // multi-line ranges aren't allowed for words 4230 p = begin_line(p);
3849 if (c == 'c') { 4231 q = end_line(q);
3850 // don't include trailing WS as part of word
3851 while (isspace(*q) && q > p) {
3852 q--;
3853 }
3854 }
3855 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word
3856 } else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
3857 // partial line copy text into a register and delete
3858 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word
3859 } else if (strchr("cdykjGHL+-{}\r\n", c1)) {
3860 // whole line copy text into a register and delete
3861 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete lines
3862 whole = 1;
3863 } else {
3864 // could not recognize object
3865 c = c1 = 27; // error-
3866 ml = 0;
3867 indicate_error();
3868 } 4232 }
3869 if (ml && whole) { 4233 dot = yank_delete(p, q, buftype, yf, ALLOW_UNDO); // delete word
4234 if (buftype == WHOLE) {
3870 if (c == 'c') { 4235 if (c == 'c') {
3871 dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN); 4236 dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN);
3872 // on the last line of file don't move to prev line 4237 // on the last line of file don't move to prev line
3873 if (whole && dot != (end-1)) { 4238 if (dot != (end-1)) {
3874 dot_prev(); 4239 dot_prev();
3875 } 4240 }
3876 } else if (c == 'd') { 4241 } else {
3877 dot_begin(); 4242 dot = save_dot;
3878 dot_skip_over_ws();
3879 } 4243 }
3880 } 4244 }
3881 if (c1 != 27) { 4245 // if CHANGING, not deleting, start inserting after the delete
3882 // if CHANGING, not deleting, start inserting after the delete 4246 if (c == 'c') {
3883 if (c == 'c') { 4247 goto dc_i; // start inserting
3884 strcpy(buf, "Change");
3885 goto dc_i; // start inserting
3886 }
3887 if (c == 'd') {
3888 strcpy(buf, "Delete");
3889 }
3890#if ENABLE_FEATURE_VI_YANKMARK
3891 if (c == 'y' || c == 'Y') {
3892 strcpy(buf, "Yank");
3893 }
3894 p = reg[YDreg];
3895 q = p + strlen(p);
3896 for (cnt = 0; p <= q; p++) {
3897 if (*p == '\n')
3898 cnt++;
3899 }
3900 status_line("%s %u lines (%u chars) using [%c]",
3901 buf, cnt, (unsigned)strlen(reg[YDreg]), what_reg());
3902#endif
3903 end_cmd_q(); // stop adding to q
3904 } 4248 }
4249#if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS
4250 // only update status if a yank has actually happened
4251 if (reg[YDreg] != savereg)
4252 yank_status(c == 'd' ? "Delete" : "Yank", reg[YDreg], 1);
4253#endif
4254 dc6:
4255 end_cmd_q(); // stop adding to q
3905 break; 4256 break;
3906 } 4257 }
3907 case 'k': // k- goto prev line, same col 4258 case 'k': // k- goto prev line, same col
3908 case KEYCODE_UP: // cursor key Up 4259 case KEYCODE_UP: // cursor key Up
3909 do { 4260 do {
3910 dot_prev(); 4261 dot_prev();
3911 dot = move_to_col(dot, ccol + offset); // try stay in same col
3912 } while (--cmdcnt > 0); 4262 } while (--cmdcnt > 0);
4263 // try to stay in saved column
4264 dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex);
4265 keep_index = TRUE;
3913 break; 4266 break;
3914 case 'r': // r- replace the current char with user input 4267 case 'r': // r- replace the current char with user input
3915 c1 = get_one_char(); // get the replacement char 4268 c1 = get_one_char(); // get the replacement char
3916 if (*dot != '\n') { 4269 if (c1 != 27) {
3917 dot = text_hole_delete(dot, dot, ALLOW_UNDO); 4270 if (end_line(dot) - dot < (cmdcnt ?: 1)) {
3918 dot = char_insert(dot, c1, ALLOW_UNDO_CHAIN); 4271 indicate_error();
4272 goto dc6;
4273 }
4274 do {
4275 dot = text_hole_delete(dot, dot, allow_undo);
4276#if ENABLE_FEATURE_VI_UNDO
4277 allow_undo = ALLOW_UNDO_CHAIN;
4278#endif
4279 dot = char_insert(dot, c1, allow_undo);
4280 } while (--cmdcnt > 0);
3919 dot_left(); 4281 dot_left();
3920 } 4282 }
3921 end_cmd_q(); // stop adding to q 4283 end_cmd_q(); // stop adding to q
3922 break; 4284 break;
3923 case 't': // t- move to char prior to next x
3924 last_forward_char = get_one_char();
3925 do_cmd(';');
3926 if (*dot == last_forward_char)
3927 dot_left();
3928 last_forward_char = 0;
3929 break;
3930 case 'w': // w- forward a word 4285 case 'w': // w- forward a word
3931 do { 4286 do {
3932 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM 4287 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
@@ -3957,14 +4312,11 @@ static void do_cmd(int c)
3957 case '~': // ~- flip the case of letters a-z -> A-Z 4312 case '~': // ~- flip the case of letters a-z -> A-Z
3958 do { 4313 do {
3959#if ENABLE_FEATURE_VI_UNDO 4314#if ENABLE_FEATURE_VI_UNDO
3960 if (islower(*dot)) { 4315 if (isalpha(*dot)) {
3961 undo_push(dot, 1, UNDO_DEL); 4316 undo_push(dot, 1, undo_del);
3962 *dot = toupper(*dot); 4317 *dot = islower(*dot) ? toupper(*dot) : tolower(*dot);
3963 undo_push(dot, 1, UNDO_INS_CHAIN);
3964 } else if (isupper(*dot)) {
3965 undo_push(dot, 1, UNDO_DEL);
3966 *dot = tolower(*dot);
3967 undo_push(dot, 1, UNDO_INS_CHAIN); 4318 undo_push(dot, 1, UNDO_INS_CHAIN);
4319 undo_del = UNDO_DEL_CHAIN;
3968 } 4320 }
3969#else 4321#else
3970 if (islower(*dot)) { 4322 if (islower(*dot)) {
@@ -4012,7 +4364,8 @@ static void do_cmd(int c)
4012 dot = bound_dot(dot); // make sure "dot" is valid 4364 dot = bound_dot(dot); // make sure "dot" is valid
4013 } 4365 }
4014#if ENABLE_FEATURE_VI_YANKMARK 4366#if ENABLE_FEATURE_VI_YANKMARK
4015 check_context(c); // update the current context 4367 if (dot != orig_dot)
4368 check_context(c); // update the current context
4016#endif 4369#endif
4017 4370
4018 if (!isdigit(c)) 4371 if (!isdigit(c))
@@ -4270,7 +4623,6 @@ static void edit_file(char *fn)
4270 mark[26] = mark[27] = text; // init "previous context" 4623 mark[26] = mark[27] = text; // init "previous context"
4271#endif 4624#endif
4272 4625
4273 last_forward_char = '\0';
4274#if ENABLE_FEATURE_VI_CRASHME 4626#if ENABLE_FEATURE_VI_CRASHME
4275 last_input_char = '\0'; 4627 last_input_char = '\0';
4276#endif 4628#endif
@@ -4297,7 +4649,6 @@ static void edit_file(char *fn)
4297#if ENABLE_FEATURE_VI_DOT_CMD 4649#if ENABLE_FEATURE_VI_DOT_CMD
4298 free(ioq_start); 4650 free(ioq_start);
4299 ioq_start = NULL; 4651 ioq_start = NULL;
4300 lmc_len = 0;
4301 adding2q = 0; 4652 adding2q = 0;
4302#endif 4653#endif
4303 4654
@@ -4345,7 +4696,7 @@ static void edit_file(char *fn)
4345 // save a copy of the current line- for the 'U" command 4696 // save a copy of the current line- for the 'U" command
4346 if (begin_line(dot) != cur_line) { 4697 if (begin_line(dot) != cur_line) {
4347 cur_line = begin_line(dot); 4698 cur_line = begin_line(dot);
4348 text_yank(begin_line(dot), end_line(dot), Ureg); 4699 text_yank(begin_line(dot), end_line(dot), Ureg, PARTIAL);
4349 } 4700 }
4350#endif 4701#endif
4351#if ENABLE_FEATURE_VI_DOT_CMD 4702#if ENABLE_FEATURE_VI_DOT_CMD
@@ -4408,10 +4759,10 @@ int vi_main(int argc, char **argv)
4408 } 4759 }
4409#endif 4760#endif
4410 4761
4411 // autoindent is not default in vim 7.3 4762 // 0: all of our options are disabled by default in vim
4412 vi_setops = /*VI_AUTOINDENT |*/ VI_SHOWMATCH | VI_IGNORECASE; 4763 //vi_setops = 0;
4413 // 1- process $HOME/.exrc file (not inplemented yet) 4764 // 1- process EXINIT variable from environment
4414 // 2- process EXINIT variable from environment 4765 // 2- if EXINIT is unset process $HOME/.exrc file (not inplemented yet)
4415 // 3- process command line args 4766 // 3- process command line args
4416#if ENABLE_FEATURE_VI_COLON 4767#if ENABLE_FEATURE_VI_COLON
4417 { 4768 {
diff --git a/include/libbb.h b/include/libbb.h
index 63e99cfe2..3b2d23df8 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -204,6 +204,29 @@ int klogctl(int type, char *b, int len);
204# define MINGW_SPECIAL(a) a 204# define MINGW_SPECIAL(a) a
205#endif 205#endif
206 206
207#if __GNUC_PREREQ(5,0)
208/* Since musl is apparently unable to get it right and would use
209 * a function call to a single-instruction function of "bswap %eax",
210 * reroute to gcc builtins:
211 */
212# undef bswap_16
213# undef bswap_32
214# undef bswap_64
215# define bswap_16(x) __builtin_bswap16(x)
216# define bswap_32(x) __builtin_bswap32(x)
217# define bswap_64(x) __builtin_bswap64(x)
218# if BB_LITTLE_ENDIAN
219# undef ntohs
220# undef htons
221# undef ntohl
222# undef htonl
223# define ntohs(x) __builtin_bswap16(x)
224# define htons(x) __builtin_bswap16(x)
225# define ntohl(x) __builtin_bswap32(x)
226# define htonl(x) __builtin_bswap32(x)
227# endif
228#endif
229
207/* Busybox does not use threads, we can speed up stdio. */ 230/* Busybox does not use threads, we can speed up stdio. */
208#ifdef HAVE_UNLOCKED_STDIO 231#ifdef HAVE_UNLOCKED_STDIO
209# undef getc 232# undef getc
@@ -662,7 +685,7 @@ uoff_t FAST_FUNC get_volume_size_in_bytes(int fd,
662 unsigned override_units, 685 unsigned override_units,
663 int extend); 686 int extend);
664 687
665void xpipe(int filedes[2]) FAST_FUNC; 688void xpipe(int *filedes) FAST_FUNC;
666/* In this form code with pipes is much more readable */ 689/* In this form code with pipes is much more readable */
667struct fd_pair { int rd; int wr; }; 690struct fd_pair { int rd; int wr; };
668#define piped_pair(pair) pipe(&((pair).rd)) 691#define piped_pair(pair) pipe(&((pair).rd))
diff --git a/include/platform.h b/include/platform.h
index d992b2633..b2d918643 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -216,6 +216,7 @@
216#endif 216#endif
217 217
218#if ULONG_MAX > 0xffffffff 218#if ULONG_MAX > 0xffffffff
219/* inline 64-bit bswap only on 64-bit arches */
219# define bb_bswap_64(x) bswap_64(x) 220# define bb_bswap_64(x) bswap_64(x)
220#endif 221#endif
221 222
@@ -445,6 +446,7 @@ typedef unsigned smalluint;
445#define HAVE_NET_ETHERNET_H 1 446#define HAVE_NET_ETHERNET_H 1
446#define HAVE_SYS_STATFS_H 1 447#define HAVE_SYS_STATFS_H 1
447#define HAVE_PRINTF_PERCENTM 1 448#define HAVE_PRINTF_PERCENTM 1
449#define HAVE_WAIT3 1
448 450
449#if defined(__UCLIBC__) 451#if defined(__UCLIBC__)
450# if UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32) 452# if UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32)
@@ -578,6 +580,9 @@ typedef unsigned smalluint;
578# undef HAVE_STPCPY 580# undef HAVE_STPCPY
579# undef HAVE_STPNCPY 581# undef HAVE_STPNCPY
580# endif 582# endif
583# if __ANDROID_API__ >= 21
584# undef HAVE_WAIT3
585# endif
581# undef HAVE_MEMPCPY 586# undef HAVE_MEMPCPY
582# undef HAVE_STRCHRNUL 587# undef HAVE_STRCHRNUL
583# undef HAVE_STRVERSCMP 588# undef HAVE_STRVERSCMP
diff --git a/klibc-utils/resume.c b/klibc-utils/resume.c
index 2bf50f9b8..8c4ab26c4 100644
--- a/klibc-utils/resume.c
+++ b/klibc-utils/resume.c
@@ -103,10 +103,17 @@ int resume_main(int argc UNUSED_PARAM, char **argv)
103 if (major(resume_device) == 0) { 103 if (major(resume_device) == 0) {
104 bb_error_msg_and_die("invalid resume device: %s", argv[0]); 104 bb_error_msg_and_die("invalid resume device: %s", argv[0]);
105 } 105 }
106
106 ofs = (argv[1] ? xstrtoull(argv[1], 0) : 0); 107 ofs = (argv[1] ? xstrtoull(argv[1], 0) : 0);
108 /* Old kernels have no /sys/power/resume_offset, set it only if necessary */
109 if (ofs != 0) {
110 fd = xopen("/sys/power/resume_offset", O_WRONLY);
111 s = xasprintf("%llu", ofs);
112 xwrite_str(fd, s);
113 }
107 114
108 fd = xopen("/sys/power/resume", O_WRONLY); 115 fd = xopen("/sys/power/resume", O_WRONLY);
109 s = xasprintf("%u:%u:%llu", major(resume_device), minor(resume_device), ofs); 116 s = xasprintf("%u:%u", major(resume_device), minor(resume_device));
110 117
111 xwrite_str(fd, s); 118 xwrite_str(fd, s);
112 /* if write() returns, resume did not succeed */ 119 /* if write() returns, resume did not succeed */
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index 193a78e31..f53d74834 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -278,9 +278,14 @@ void lbb_prepare(const char *applet
278 && strcmp(argv[1], "--help") == 0 278 && strcmp(argv[1], "--help") == 0
279 && !is_prefixed_with(applet, "busybox") 279 && !is_prefixed_with(applet, "busybox")
280 ) { 280 ) {
281 /* Special case. POSIX says "test --help" 281 /* Special cases. POSIX says "test --help"
282 * should be no different from e.g. "test --foo". */ 282 * should be no different from e.g. "test --foo".
283 if (!ENABLE_TEST || strcmp(applet_name, "test") != 0) 283 */
284 if (!(ENABLE_TEST && strcmp(applet_name, "test") == 0)
285 && !(ENABLE_TRUE && strcmp(applet_name, "true") == 0)
286 && !(ENABLE_FALSE && strcmp(applet_name, "false") == 0)
287 && !(ENABLE_ECHO && strcmp(applet_name, "echo") == 0)
288 )
284 bb_show_usage(); 289 bb_show_usage();
285 } 290 }
286#endif 291#endif
@@ -1021,15 +1026,21 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
1021 if (!argv[2]) 1026 if (!argv[2])
1022 goto help; 1027 goto help;
1023 /* convert to "<applet> --help" */ 1028 /* convert to "<applet> --help" */
1024 argv[0] = argv[2]; 1029 applet_name = argv[0] = argv[2];
1025 argv[2] = NULL; 1030 argv[2] = NULL;
1031 if (find_applet_by_name(applet_name) >= 0) {
1032 /* Make "--help foo" exit with 0: */
1033 xfunc_error_retval = 0;
1034 bb_show_usage();
1035 } /* else: unknown applet, fall through (causes "applet not found" later) */
1026 } else { 1036 } else {
1027 /* "busybox <applet> arg1 arg2 ..." */ 1037 /* "busybox <applet> arg1 arg2 ..." */
1028 argv++; 1038 argv++;
1039 /* We support "busybox /a/path/to/applet args..." too. Allows for
1040 * "#!/bin/busybox"-style wrappers
1041 */
1042 applet_name = bb_get_last_path_component_nostrip(argv[0]);
1029 } 1043 }
1030 /* We support "busybox /a/path/to/applet args..." too. Allows for
1031 * "#!/bin/busybox"-style wrappers */
1032 applet_name = bb_get_last_path_component_nostrip(argv[0]);
1033 run_applet_and_exit(applet_name, argv); 1044 run_applet_and_exit(applet_name, argv);
1034} 1045}
1035# endif 1046# endif
@@ -1040,7 +1051,7 @@ void FAST_FUNC show_usage_if_dash_dash_help(int applet_no, char **argv)
1040 /* Special case. POSIX says "test --help" 1051 /* Special case. POSIX says "test --help"
1041 * should be no different from e.g. "test --foo". 1052 * should be no different from e.g. "test --foo".
1042 * Thus for "test", we skip --help check. 1053 * Thus for "test", we skip --help check.
1043 * "true" and "false" are also special. 1054 * "true", "false", "echo" are also special.
1044 */ 1055 */
1045 if (1 1056 if (1
1046# if defined APPLET_NO_test 1057# if defined APPLET_NO_test
@@ -1052,6 +1063,9 @@ void FAST_FUNC show_usage_if_dash_dash_help(int applet_no, char **argv)
1052# if defined APPLET_NO_false 1063# if defined APPLET_NO_false
1053 && applet_no != APPLET_NO_false 1064 && applet_no != APPLET_NO_false
1054# endif 1065# endif
1066# if defined APPLET_NO_echo
1067 && applet_no != APPLET_NO_echo
1068# endif
1055# if ENABLE_PLATFORM_MINGW32 && defined APPLET_NO_busybox 1069# if ENABLE_PLATFORM_MINGW32 && defined APPLET_NO_busybox
1056 && applet_no != APPLET_NO_busybox 1070 && applet_no != APPLET_NO_busybox
1057# endif 1071# endif
diff --git a/libbb/duration.c b/libbb/duration.c
index 086da15fb..a6a29ddae 100644
--- a/libbb/duration.c
+++ b/libbb/duration.c
@@ -37,8 +37,18 @@ duration_t FAST_FUNC parse_duration_str(char *str)
37 if (strchr(str, '.')) { 37 if (strchr(str, '.')) {
38 double d; 38 double d;
39 char *pp; 39 char *pp;
40 int len = strspn(str, "0123456789."); 40 int len;
41 char sv = str[len]; 41 char sv;
42
43# if ENABLE_LOCALE_SUPPORT
44 /* Undo busybox.c: on input, we want to use dot
45 * as fractional separator in strtod(),
46 * regardless of current locale
47 */
48 setlocale(LC_NUMERIC, "C");
49# endif
50 len = strspn(str, "0123456789.");
51 sv = str[len];
42 str[len] = '\0'; 52 str[len] = '\0';
43 errno = 0; 53 errno = 0;
44 d = strtod(str, &pp); 54 d = strtod(str, &pp);
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index c20436270..e7de32250 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -1157,7 +1157,7 @@ static NOINLINE int build_match_prefix(char *match_buf)
1157 continue; 1157 continue;
1158 for (--i; i >= 0; i--) { 1158 for (--i; i >= 0; i--) {
1159 int cur = int_buf[i]; 1159 int cur = int_buf[i];
1160 if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') { 1160 if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&' || cur == '=') {
1161 remove_chunk(int_buf, 0, i + 1); 1161 remove_chunk(int_buf, 0, i + 1);
1162 break; 1162 break;
1163 } 1163 }
@@ -1426,7 +1426,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1426 strcpy(&command[cursor_mb], chosen_match + match_pfx_len); 1426 strcpy(&command[cursor_mb], chosen_match + match_pfx_len);
1427 len = load_string(command); 1427 len = load_string(command);
1428 /* add match and tail */ 1428 /* add match and tail */
1429 sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf); 1429 stpcpy(stpcpy(&command[cursor_mb], chosen_match + match_pfx_len), match_buf);
1430 command_len = load_string(command); 1430 command_len = load_string(command);
1431 /* write out the matched command */ 1431 /* write out the matched command */
1432 /* paranoia: load_string can return 0 on conv error, 1432 /* paranoia: load_string can return 0 on conv error,
diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c
index 47dbdd1b6..d7d8b1092 100644
--- a/libbb/xfuncs_printf.c
+++ b/libbb/xfuncs_printf.c
@@ -226,7 +226,7 @@ int FAST_FUNC rename_or_warn(const char *oldpath, const char *newpath)
226 return n; 226 return n;
227} 227}
228 228
229void FAST_FUNC xpipe(int filedes[2]) 229void FAST_FUNC xpipe(int *filedes)
230{ 230{
231 if (pipe(filedes)) 231 if (pipe(filedes))
232 bb_simple_perror_msg_and_die("can't create pipe"); 232 bb_simple_perror_msg_and_die("can't create pipe");
diff --git a/loginutils/login.c b/loginutils/login.c
index 21c32fc25..ce87e318a 100644
--- a/loginutils/login.c
+++ b/loginutils/login.c
@@ -60,6 +60,11 @@
60//usage: "\n -f Don't authenticate (user already authenticated)" 60//usage: "\n -f Don't authenticate (user already authenticated)"
61//usage: "\n -h HOST Host user came from (for network logins)" 61//usage: "\n -h HOST Host user came from (for network logins)"
62//usage: "\n -p Preserve environment" 62//usage: "\n -p Preserve environment"
63//usage: "\n"
64//usage: "\n$LOGIN_TIMEOUT Seconds (default 60, 0 - disable)"
65//usage: IF_LOGIN_SCRIPTS(
66//usage: "\n$LOGIN_PRE_SUID_SCRIPT Execute before user ID change"
67//usage: )
63 68
64#include "libbb.h" 69#include "libbb.h"
65#include "common_bufsiz.h" 70#include "common_bufsiz.h"
@@ -130,7 +135,6 @@ static const struct pam_conv conv = {
130#endif 135#endif
131 136
132enum { 137enum {
133 TIMEOUT = 60,
134 EMPTY_USERNAME_COUNT = 10, 138 EMPTY_USERNAME_COUNT = 10,
135 /* Some users found 32 chars limit to be too low: */ 139 /* Some users found 32 chars limit to be too low: */
136 USERNAME_SIZE = 64, 140 USERNAME_SIZE = 64,
@@ -139,6 +143,7 @@ enum {
139 143
140struct globals { 144struct globals {
141 struct termios tty_attrs; 145 struct termios tty_attrs;
146 int timeout;
142} FIX_ALIASING; 147} FIX_ALIASING;
143#define G (*(struct globals*)bb_common_bufsiz1) 148#define G (*(struct globals*)bb_common_bufsiz1)
144#define INIT_G() do { setup_common_bufsiz(); } while (0) 149#define INIT_G() do { setup_common_bufsiz(); } while (0)
@@ -302,7 +307,7 @@ static void alarm_handler(int sig UNUSED_PARAM)
302 * when you are back at shell prompt, echo will be still off. 307 * when you are back at shell prompt, echo will be still off.
303 */ 308 */
304 tcsetattr_stdin_TCSANOW(&G.tty_attrs); 309 tcsetattr_stdin_TCSANOW(&G.tty_attrs);
305 printf("\r\nLogin timed out after %u seconds\r\n", TIMEOUT); 310 printf("\r\nLogin timed out after %u seconds\r\n", G.timeout);
306 fflush_all(); 311 fflush_all();
307 /* unix API is brain damaged regarding O_NONBLOCK, 312 /* unix API is brain damaged regarding O_NONBLOCK,
308 * we should undo it, or else we can affect other processes */ 313 * we should undo it, or else we can affect other processes */
@@ -345,6 +350,8 @@ int login_main(int argc UNUSED_PARAM, char **argv)
345 350
346 INIT_G(); 351 INIT_G();
347 352
353 G.timeout = xatoi_positive(getenv("LOGIN_TIMEOUT") ? : "60");
354
348 /* More of suid paranoia if called by non-root: */ 355 /* More of suid paranoia if called by non-root: */
349 /* Clear dangerous stuff, set PATH */ 356 /* Clear dangerous stuff, set PATH */
350 run_by_root = !sanitize_env_if_suid(); 357 run_by_root = !sanitize_env_if_suid();
@@ -376,7 +383,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
376 383
377 /* We install timeout handler only _after_ we saved G.tty_attrs */ 384 /* We install timeout handler only _after_ we saved G.tty_attrs */
378 signal(SIGALRM, alarm_handler); 385 signal(SIGALRM, alarm_handler);
379 alarm(TIMEOUT); 386 alarm(G.timeout);
380 387
381 /* Find out and memorize our tty name */ 388 /* Find out and memorize our tty name */
382 full_tty = xmalloc_ttyname(STDIN_FILENO); 389 full_tty = xmalloc_ttyname(STDIN_FILENO);
@@ -435,6 +442,9 @@ int login_main(int argc UNUSED_PARAM, char **argv)
435 } 442 }
436 /* check that the account is healthy */ 443 /* check that the account is healthy */
437 pamret = pam_acct_mgmt(pamh, 0); 444 pamret = pam_acct_mgmt(pamh, 0);
445 if (pamret == PAM_NEW_AUTHTOK_REQD) {
446 pamret = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
447 }
438 if (pamret != PAM_SUCCESS) { 448 if (pamret != PAM_SUCCESS) {
439 failed_msg = "acct_mgmt"; 449 failed_msg = "acct_mgmt";
440 goto pam_auth_failed; 450 goto pam_auth_failed;
diff --git a/miscutils/bc.c b/miscutils/bc.c
index 4eb0f2d30..d74ab1da2 100644
--- a/miscutils/bc.c
+++ b/miscutils/bc.c
@@ -108,14 +108,14 @@
108 108
109//See www.gnu.org/software/bc/manual/bc.html 109//See www.gnu.org/software/bc/manual/bc.html
110//usage:#define bc_trivial_usage 110//usage:#define bc_trivial_usage
111//usage: "[-sqlw] FILE..." 111//usage: "[-sqlw] [FILE...]"
112//usage: 112//usage:
113//usage:#define bc_full_usage "\n" 113//usage:#define bc_full_usage "\n"
114//usage: "\nArbitrary precision calculator" 114//usage: "\nArbitrary precision calculator"
115//usage: "\n" 115//usage: "\n"
116///////: "\n -i Interactive" - has no effect for now 116///////: "\n -i Interactive" - has no effect for now
117//usage: "\n -q Quiet" 117//usage: "\n -q Quiet"
118//usage: "\n -l Load standard math library" 118//usage: "\n -l Load standard library"
119//usage: "\n -s Be POSIX compatible" 119//usage: "\n -s Be POSIX compatible"
120//usage: "\n -w Warn if extensions are used" 120//usage: "\n -w Warn if extensions are used"
121///////: "\n -v Version" 121///////: "\n -v Version"
diff --git a/miscutils/dc.c b/miscutils/dc.c
index 918f2b5c8..d6369fd15 100644
--- a/miscutils/dc.c
+++ b/miscutils/dc.c
@@ -229,6 +229,7 @@ static void stack_machine(const char *argument)
229 const struct op *o; 229 const struct op *o;
230 230
231 next: 231 next:
232//TODO: needs setlocale(LC_NUMERIC, "C")?
232 number = strtod(argument, &end); 233 number = strtod(argument, &end);
233 if (end != argument) { 234 if (end != argument) {
234 argument = end; 235 argument = end;
diff --git a/miscutils/time.c b/miscutils/time.c
index 12c540211..ac37a1375 100644
--- a/miscutils/time.c
+++ b/miscutils/time.c
@@ -42,6 +42,13 @@
42 42
43#include "libbb.h" 43#include "libbb.h"
44 44
45#ifndef HAVE_WAIT3
46static pid_t wait3(int *status, int options, struct rusage *rusage)
47{
48 return wait4(-1, status, options, rusage);
49}
50#endif
51
45/* Information on the resources used by a child process. */ 52/* Information on the resources used by a child process. */
46typedef struct { 53typedef struct {
47 int waitstatus; 54 int waitstatus;
diff --git a/miscutils/watchdog.c b/miscutils/watchdog.c
index 0ed10bcf1..d8e9c78f5 100644
--- a/miscutils/watchdog.c
+++ b/miscutils/watchdog.c
@@ -18,6 +18,21 @@
18//config: watchdog applet ever fails to write the magic character within a 18//config: watchdog applet ever fails to write the magic character within a
19//config: certain amount of time, the watchdog device assumes the system has 19//config: certain amount of time, the watchdog device assumes the system has
20//config: hung, and will cause the hardware to reboot. 20//config: hung, and will cause the hardware to reboot.
21//config:
22//config:config FEATURE_WATCHDOG_OPEN_TWICE
23//config: bool "Open watchdog device twice, closing it gracefully in between"
24//config: depends on WATCHDOG
25//config: default n # this behavior was essentially a hack for a broken driver
26//config: help
27//config: When enabled, the watchdog device is opened and then immediately
28//config: magic-closed, before being opened a second time. This may be necessary
29//config: for some watchdog devices, but can cause spurious warnings in the
30//config: kernel log if the nowayout feature is enabled. If this workaround
31//config: is really needed for you machine to work properly, consider whether
32//config: it should be fixed in the kernel driver instead. Even when disabled,
33//config: the behaviour is easily emulated with a "printf 'V' > /dev/watchdog"
34//config: immediately before starting the busybox watchdog daemon. Say n unless
35//config: you know that you absolutely need this.
21 36
22//applet:IF_WATCHDOG(APPLET(watchdog, BB_DIR_SBIN, BB_SUID_DROP)) 37//applet:IF_WATCHDOG(APPLET(watchdog, BB_DIR_SBIN, BB_SUID_DROP))
23 38
@@ -50,10 +65,6 @@
50# define WDIOS_ENABLECARD 2 65# define WDIOS_ENABLECARD 2
51#endif 66#endif
52 67
53#define OPT_FOREGROUND (1 << 0)
54#define OPT_STIMER (1 << 1)
55#define OPT_HTIMER (1 << 2)
56
57static void shutdown_watchdog(void) 68static void shutdown_watchdog(void)
58{ 69{
59 static const char V = 'V'; 70 static const char V = 'V';
@@ -73,6 +84,7 @@ static void watchdog_open(const char* device)
73 /* Use known fd # - avoid needing global 'int fd' */ 84 /* Use known fd # - avoid needing global 'int fd' */
74 xmove_fd(xopen(device, O_WRONLY), 3); 85 xmove_fd(xopen(device, O_WRONLY), 3);
75 86
87#if ENABLE_FEATURE_WATCHDOG_OPEN_TWICE
76 /* If the watchdog driver can do something other than cause a reboot 88 /* If the watchdog driver can do something other than cause a reboot
77 * on a timeout, then it's possible this program may be starting from 89 * on a timeout, then it's possible this program may be starting from
78 * a state when the watchdog hadn't been previously stopped with 90 * a state when the watchdog hadn't been previously stopped with
@@ -82,6 +94,7 @@ static void watchdog_open(const char* device)
82 shutdown_watchdog(); 94 shutdown_watchdog();
83 95
84 xmove_fd(xopen(device, O_WRONLY), 3); 96 xmove_fd(xopen(device, O_WRONLY), 3);
97#endif
85} 98}
86 99
87int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 100int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -100,6 +113,9 @@ int watchdog_main(int argc UNUSED_PARAM, char **argv)
100 char *st_arg; 113 char *st_arg;
101 char *ht_arg; 114 char *ht_arg;
102 115
116#define OPT_FOREGROUND (1 << 0)
117#define OPT_STIMER (1 << 1)
118#define OPT_HTIMER (1 << 2)
103 opts = getopt32(argv, "^" "Ft:T:" "\0" "=1"/*must have exactly 1 arg*/, 119 opts = getopt32(argv, "^" "Ft:T:" "\0" "=1"/*must have exactly 1 arg*/,
104 &st_arg, &ht_arg 120 &st_arg, &ht_arg
105 ); 121 );
@@ -132,7 +148,7 @@ int watchdog_main(int argc UNUSED_PARAM, char **argv)
132#if 0 148#if 0
133 ioctl_or_warn(3, WDIOC_GETTIMEOUT, &htimer_duration); 149 ioctl_or_warn(3, WDIOC_GETTIMEOUT, &htimer_duration);
134 printf("watchdog: SW timer is %dms, HW timer is %ds\n", 150 printf("watchdog: SW timer is %dms, HW timer is %ds\n",
135 stimer_duration, htimer_duration * 1000); 151 stimer_duration, htimer_duration);
136#endif 152#endif
137 153
138 write_pidfile_std_path_and_ext("watchdog"); 154 write_pidfile_std_path_and_ext("watchdog");
diff --git a/networking/brctl.c b/networking/brctl.c
index e1f3e6445..c83aac6e0 100644
--- a/networking/brctl.c
+++ b/networking/brctl.c
@@ -89,6 +89,7 @@ static unsigned str_to_jiffies(const char *time_str)
89{ 89{
90 double dd; 90 double dd;
91 char *endptr; 91 char *endptr;
92//TODO: needs setlocale(LC_NUMERIC, "C")?
92 dd = /*bb_*/strtod(time_str, &endptr); 93 dd = /*bb_*/strtod(time_str, &endptr);
93 if (endptr == time_str || dd < 0) 94 if (endptr == time_str || dd < 0)
94 bb_error_msg_and_die(bb_msg_invalid_arg_to, time_str, "timespec"); 95 bb_error_msg_and_die(bb_msg_invalid_arg_to, time_str, "timespec");
diff --git a/networking/httpd.c b/networking/httpd.c
index 08313bbc7..fcc49853a 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -312,6 +312,12 @@
312 312
313#define DEBUG 0 313#define DEBUG 0
314 314
315#if DEBUG
316# define dbg(...) fprintf(stderr, __VA_ARGS__)
317#else
318# define dbg(...) ((void)0)
319#endif
320
315#define IOBUF_SIZE 8192 321#define IOBUF_SIZE 8192
316#define MAX_HTTP_HEADERS_SIZE (32*1024) 322#define MAX_HTTP_HEADERS_SIZE (32*1024)
317 323
@@ -353,13 +359,6 @@ typedef struct Htaccess_Proxy {
353 char *url_to; 359 char *url_to;
354} Htaccess_Proxy; 360} Htaccess_Proxy;
355 361
356typedef enum CGI_type {
357 CGI_NONE = 0,
358 CGI_NORMAL,
359 CGI_INDEX,
360 CGI_INTERPRETER,
361} CGI_type;
362
363enum { 362enum {
364 HTTP_OK = 200, 363 HTTP_OK = 200,
365 HTTP_PARTIAL_CONTENT = 206, 364 HTTP_PARTIAL_CONTENT = 206,
@@ -564,7 +563,6 @@ enum {
564enum { 563enum {
565 SEND_HEADERS = (1 << 0), 564 SEND_HEADERS = (1 << 0),
566 SEND_BODY = (1 << 1), 565 SEND_BODY = (1 << 1),
567 SEND_HEADERS_AND_BODY = SEND_HEADERS + SEND_BODY,
568}; 566};
569static void send_file_and_exit(const char *url, int what) NORETURN; 567static void send_file_and_exit(const char *url, int what) NORETURN;
570 568
@@ -696,7 +694,7 @@ enum {
696 SIGNALED_PARSE = 1, /* path will be "/etc" */ 694 SIGNALED_PARSE = 1, /* path will be "/etc" */
697 SUBDIR_PARSE = 2, /* path will be derived from URL */ 695 SUBDIR_PARSE = 2, /* path will be derived from URL */
698}; 696};
699static void parse_conf(const char *path, int flag) 697static int parse_conf(const char *path, int flag)
700{ 698{
701 /* internally used extra flag state */ 699 /* internally used extra flag state */
702 enum { TRY_CURDIR_PARSE = 3 }; 700 enum { TRY_CURDIR_PARSE = 3 };
@@ -737,7 +735,7 @@ static void parse_conf(const char *path, int flag)
737 while ((f = fopen_for_read(filename)) == NULL) { 735 while ((f = fopen_for_read(filename)) == NULL) {
738 if (flag >= SUBDIR_PARSE) { /* SUBDIR or TRY_CURDIR */ 736 if (flag >= SUBDIR_PARSE) { /* SUBDIR or TRY_CURDIR */
739 /* config file not found, no changes to config */ 737 /* config file not found, no changes to config */
740 return; 738 return -1;
741 } 739 }
742 if (flag == FIRST_PARSE) { 740 if (flag == FIRST_PARSE) {
743 /* -c CONFFILE given, but CONFFILE doesn't exist? */ 741 /* -c CONFFILE given, but CONFFILE doesn't exist? */
@@ -1000,6 +998,7 @@ static void parse_conf(const char *path, int flag)
1000 } /* while (fgets) */ 998 } /* while (fgets) */
1001 999
1002 fclose(f); 1000 fclose(f);
1001 return 0;
1003} 1002}
1004 1003
1005#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR 1004#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
@@ -1186,8 +1185,7 @@ static void send_headers(unsigned responseNum)
1186 fprintf(stderr, "headers: '%s'\n", iobuf); 1185 fprintf(stderr, "headers: '%s'\n", iobuf);
1187 } 1186 }
1188 full_write(STDOUT_FILENO, iobuf, len); 1187 full_write(STDOUT_FILENO, iobuf, len);
1189 if (DEBUG) 1188 dbg("writing error page: '%s'\n", error_page);
1190 fprintf(stderr, "writing error page: '%s'\n", error_page);
1191 return send_file_and_exit(error_page, SEND_BODY); 1189 return send_file_and_exit(error_page, SEND_BODY);
1192 } 1190 }
1193#endif 1191#endif
@@ -1542,8 +1540,7 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1542 } 1540 }
1543 if (full_write(STDOUT_FILENO, rbuf, count) != count) 1541 if (full_write(STDOUT_FILENO, rbuf, count) != count)
1544 break; 1542 break;
1545 if (DEBUG) 1543 dbg("cgi read %d bytes: '%.*s'\n", count, count, rbuf);
1546 fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
1547 } /* if (pfd[FROM_CGI].revents) */ 1544 } /* if (pfd[FROM_CGI].revents) */
1548 } /* while (1) */ 1545 } /* while (1) */
1549 log_and_exit(); 1546 log_and_exit();
@@ -1789,8 +1786,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1789 /* file_size and last_mod are already populated */ 1786 /* file_size and last_mod are already populated */
1790 } 1787 }
1791 if (fd < 0) { 1788 if (fd < 0) {
1792 if (DEBUG) 1789 dbg("can't open '%s'\n", url);
1793 bb_perror_msg("can't open '%s'", url);
1794 /* Error pages are sent by using send_file_and_exit(SEND_BODY). 1790 /* Error pages are sent by using send_file_and_exit(SEND_BODY).
1795 * IOW: it is unsafe to call send_headers_and_exit 1791 * IOW: it is unsafe to call send_headers_and_exit
1796 * if what is SEND_BODY! Can recurse! */ 1792 * if what is SEND_BODY! Can recurse! */
@@ -1803,8 +1799,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1803 sprintf(G.etag, "\"%"LL_FMT"x-%"LL_FMT"x\"", (unsigned long long)last_mod, (unsigned long long)file_size); 1799 sprintf(G.etag, "\"%"LL_FMT"x-%"LL_FMT"x\"", (unsigned long long)last_mod, (unsigned long long)file_size);
1804 1800
1805 if (G.if_none_match) { 1801 if (G.if_none_match) {
1806 if (DEBUG) 1802 dbg("If-None-Match:'%s' file's ETag:'%s'\n", G.if_none_match, G.etag);
1807 bb_perror_msg("If-None-Match and file's ETag are: '%s' '%s'\n", G.if_none_match, G.etag);
1808 /* Weak ETag comparision. 1803 /* Weak ETag comparision.
1809 * If-None-Match may have many ETags but they are quoted so we can use simple substring search */ 1804 * If-None-Match may have many ETags but they are quoted so we can use simple substring search */
1810 if (strstr(G.if_none_match, G.etag)) 1805 if (strstr(G.if_none_match, G.etag))
@@ -1880,9 +1875,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1880 } 1875 }
1881 } 1876 }
1882 1877
1883 if (DEBUG) 1878 dbg("sending file '%s' content-type:%s\n", url, found_mime_type);
1884 bb_error_msg("sending file '%s' content-type: %s",
1885 url, found_mime_type);
1886 1879
1887#if ENABLE_FEATURE_HTTPD_RANGES 1880#if ENABLE_FEATURE_HTTPD_RANGES
1888 if (what == SEND_BODY /* err pages and ranges don't mix */ 1881 if (what == SEND_BODY /* err pages and ranges don't mix */
@@ -1952,9 +1945,7 @@ static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip)
1952 Htaccess_IP *cur; 1945 Htaccess_IP *cur;
1953 1946
1954 for (cur = G.ip_a_d; cur; cur = cur->next) { 1947 for (cur = G.ip_a_d; cur; cur = cur->next) {
1955#if DEBUG 1948 dbg("checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n",
1956 fprintf(stderr,
1957 "checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n",
1958 rmt_ip_str, 1949 rmt_ip_str,
1959 (unsigned char)(cur->ip >> 24), 1950 (unsigned char)(cur->ip >> 24),
1960 (unsigned char)(cur->ip >> 16), 1951 (unsigned char)(cur->ip >> 16),
@@ -1965,7 +1956,6 @@ static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip)
1965 (unsigned char)(cur->mask >> 8), 1956 (unsigned char)(cur->mask >> 8),
1966 (unsigned char)(cur->mask) 1957 (unsigned char)(cur->mask)
1967 ); 1958 );
1968#endif
1969 if ((remote_ip & cur->mask) == cur->ip) { 1959 if ((remote_ip & cur->mask) == cur->ip) {
1970 if (cur->allow_deny == 'A') 1960 if (cur->allow_deny == 'A')
1971 return; 1961 return;
@@ -2061,8 +2051,7 @@ static int check_user_passwd(const char *path, char *user_and_passwd)
2061#endif 2051#endif
2062 continue; 2052 continue;
2063 2053
2064 if (DEBUG) 2054 dbg("checkPerm: '%s' ? '%s'\n", dir_prefix, user_and_passwd);
2065 fprintf(stderr, "checkPerm: '%s' ? '%s'\n", dir_prefix, user_and_passwd);
2066 2055
2067 /* If it's not a prefix match, continue searching */ 2056 /* If it's not a prefix match, continue searching */
2068 len = strlen(dir_prefix); 2057 len = strlen(dir_prefix);
@@ -2227,7 +2216,6 @@ static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM)
2227static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN; 2216static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN;
2228static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) 2217static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2229{ 2218{
2230 static const char request_GET[] ALIGN1 = "GET";
2231 struct stat sb; 2219 struct stat sb;
2232 char *urlcopy; 2220 char *urlcopy;
2233 char *urlp; 2221 char *urlp;
@@ -2238,14 +2226,21 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2238#if ENABLE_FEATURE_HTTPD_CGI 2226#if ENABLE_FEATURE_HTTPD_CGI
2239 unsigned total_headers_len; 2227 unsigned total_headers_len;
2240#endif 2228#endif
2241#if ENABLE_FEATURE_HTTPD_CGI
2242 static const char request_HEAD[] ALIGN1 = "HEAD";
2243 const char *prequest; 2229 const char *prequest;
2244 unsigned long length = 0; 2230 static const char request_GET[] ALIGN1 = "GET";
2245 enum CGI_type cgi_type = CGI_NONE; 2231 static const char request_HEAD[] ALIGN1 = "HEAD";
2246#elif ENABLE_FEATURE_HTTPD_PROXY 2232#if ENABLE_FEATURE_HTTPD_CGI
2247#define prequest request_GET 2233 static const char request_POST[] ALIGN1 = "POST";
2248 unsigned long length = 0; 2234 unsigned long POST_length;
2235 enum CGI_type {
2236 CGI_NONE = 0,
2237 CGI_NORMAL,
2238 CGI_INDEX,
2239 CGI_INTERPRETER,
2240 } cgi_type = CGI_NONE;
2241#endif
2242#if ENABLE_FEATURE_HTTPD_PROXY
2243 Htaccess_Proxy *proxy_entry;
2249#endif 2244#endif
2250#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 2245#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
2251 smallint authorized = -1; 2246 smallint authorized = -1;
@@ -2297,35 +2292,33 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2297 signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit); 2292 signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit);
2298#endif 2293#endif
2299 2294
2300 if (!get_line()) /* EOF or error or empty line */ 2295 if (!get_line()) { /* EOF or error or empty line */
2301 send_headers_and_exit(HTTP_BAD_REQUEST); 2296 /* Observed Firefox to "speculatively" open
2297 * extra connections to a new site on first access,
2298 * they are closed in ~5 seconds with nothing
2299 * being sent at all.
2300 * (Presumably it's a method to decrease latency?)
2301 */
2302 if (verbose > 2)
2303 bb_simple_error_msg("eof on read, closing");
2304 /* Don't bother generating error page in this case,
2305 * just close the socket.
2306 */
2307 //send_headers_and_exit(HTTP_BAD_REQUEST);
2308 _exit(xfunc_error_retval);
2309 }
2310 dbg("Request:'%s'\n", iobuf);
2302 2311
2303 /* Determine type of request (GET/POST) */ 2312 /* Find URL */
2304 // rfc2616: method and URI is separated by exactly one space 2313 // rfc2616: method and URI is separated by exactly one space
2305 //urlp = strpbrk(iobuf, " \t"); - no, tab isn't allowed 2314 //urlp = strpbrk(iobuf, " \t"); - no, tab isn't allowed
2306 urlp = strchr(iobuf, ' '); 2315 urlp = strchr(iobuf, ' ');
2307 if (urlp == NULL) 2316 if (urlp == NULL)
2308 send_headers_and_exit(HTTP_BAD_REQUEST); 2317 send_headers_and_exit(HTTP_BAD_REQUEST);
2309 *urlp++ = '\0'; 2318 *urlp++ = '\0';
2310#if ENABLE_FEATURE_HTTPD_CGI
2311 prequest = request_GET;
2312 if (strcasecmp(iobuf, prequest) != 0) {
2313 prequest = request_HEAD;
2314 if (strcasecmp(iobuf, prequest) != 0) {
2315 prequest = "POST";
2316 if (strcasecmp(iobuf, prequest) != 0)
2317 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2318 }
2319 }
2320#else
2321 if (strcasecmp(iobuf, request_GET) != 0)
2322 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2323#endif
2324 // rfc2616: method and URI is separated by exactly one space
2325 //urlp = skip_whitespace(urlp); - should not be necessary 2319 //urlp = skip_whitespace(urlp); - should not be necessary
2326 if (urlp[0] != '/') 2320 if (urlp[0] != '/')
2327 send_headers_and_exit(HTTP_BAD_REQUEST); 2321 send_headers_and_exit(HTTP_BAD_REQUEST);
2328
2329 /* Find end of URL */ 2322 /* Find end of URL */
2330 HTTP_slash = strchr(urlp, ' '); 2323 HTTP_slash = strchr(urlp, ' ');
2331 /* Is it " HTTP/"? */ 2324 /* Is it " HTTP/"? */
@@ -2333,48 +2326,62 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2333 send_headers_and_exit(HTTP_BAD_REQUEST); 2326 send_headers_and_exit(HTTP_BAD_REQUEST);
2334 *HTTP_slash++ = '\0'; 2327 *HTTP_slash++ = '\0';
2335 2328
2336 /* Copy URL from after "GET "/"POST " to stack-allocated char[] */
2337 urlcopy = alloca((HTTP_slash - urlp) + 2 + strlen(index_page));
2338 /*if (urlcopy == NULL)
2339 * send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);*/
2340 strcpy(urlcopy, urlp);
2341 /* NB: urlcopy ptr is never changed after this */
2342
2343#if ENABLE_FEATURE_HTTPD_PROXY 2329#if ENABLE_FEATURE_HTTPD_PROXY
2344 { 2330 proxy_entry = find_proxy_entry(urlp);
2331 if (proxy_entry) {
2345 int proxy_fd; 2332 int proxy_fd;
2346 len_and_sockaddr *lsa; 2333 len_and_sockaddr *lsa;
2347 Htaccess_Proxy *proxy_entry = find_proxy_entry(urlcopy); 2334
2348 2335 if (verbose > 1)
2349 if (proxy_entry) { 2336 bb_error_msg("proxy:%s", urlp);
2350 if (verbose > 1) 2337 lsa = host2sockaddr(proxy_entry->host_port, 80);
2351 bb_error_msg("proxy:%s", urlcopy); 2338 if (!lsa)
2352 lsa = host2sockaddr(proxy_entry->host_port, 80); 2339 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
2353 if (!lsa) 2340 proxy_fd = socket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
2354 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); 2341 if (proxy_fd < 0)
2355 proxy_fd = socket(lsa->u.sa.sa_family, SOCK_STREAM, 0); 2342 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
2356 if (proxy_fd < 0) 2343 if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0)
2357 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); 2344 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
2358 if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0) 2345 /* Disable peer header reading timeout */
2359 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); 2346 alarm(0);
2360 /* Disable peer header reading timeout */ 2347 /* Config directive was of the form:
2361 alarm(0); 2348 * P:/url:[http://]hostname[:port]/new/path
2362 /* Config directive was of the form: 2349 * When /urlSFX is requested, reverse proxy it
2363 * P:/url:[http://]hostname[:port]/new/path 2350 * to http://hostname[:port]/new/pathSFX
2364 * When /urlSFX is requested, reverse proxy it 2351 */
2365 * to http://hostname[:port]/new/pathSFX 2352 fdprintf(proxy_fd, "%s %s%s %s\r\n",
2366 */ 2353 iobuf, /* "GET" / "POST" / etc */
2367 fdprintf(proxy_fd, "%s %s%s %s\r\n", 2354 proxy_entry->url_to, /* "/new/path" */
2368 prequest, /* "GET" or "POST" */ 2355 urlp + strlen(proxy_entry->url_from), /* "SFX" */
2369 proxy_entry->url_to, /* "/new/path" */ 2356 HTTP_slash /* "HTTP/xyz" */
2370 urlcopy + strlen(proxy_entry->url_from), /* "SFX" */ 2357 );
2371 HTTP_slash /* "HTTP/xyz" */ 2358 cgi_io_loop_and_exit(proxy_fd, proxy_fd, /*max POST length:*/ INT_MAX);
2372 );
2373 cgi_io_loop_and_exit(proxy_fd, proxy_fd, /*max POST length:*/ INT_MAX);
2374 }
2375 } 2359 }
2376#endif 2360#endif
2377 2361
2362 /* Determine type of request (GET/POST/...) */
2363 prequest = request_GET;
2364 if (strcasecmp(iobuf, prequest) == 0)
2365 goto found;
2366 prequest = request_HEAD;
2367 if (strcasecmp(iobuf, prequest) == 0)
2368 goto found;
2369#if !ENABLE_FEATURE_HTTPD_CGI
2370 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2371#else
2372 prequest = request_POST;
2373 if (strcasecmp(iobuf, prequest) == 0)
2374 goto found;
2375 /* For CGI, allow DELETE, PUT, OPTIONS, etc too */
2376 prequest = alloca(16);
2377 safe_strncpy((char*)prequest, iobuf, 16);
2378#endif
2379 found:
2380 /* Copy URL to stack-allocated char[] */
2381 urlcopy = alloca((HTTP_slash - urlp) + 2 + strlen(index_page));
2382 strcpy(urlcopy, urlp);
2383 /* NB: urlcopy ptr is never changed after this */
2384
2378 /* Extract url args if present */ 2385 /* Extract url args if present */
2379 g_query = strchr(urlcopy, '?'); 2386 g_query = strchr(urlcopy, '?');
2380 if (g_query) 2387 if (g_query)
@@ -2393,7 +2400,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2393 /* Algorithm stolen from libbb bb_simplify_path(), 2400 /* Algorithm stolen from libbb bb_simplify_path(),
2394 * but don't strdup, retain trailing slash, protect root */ 2401 * but don't strdup, retain trailing slash, protect root */
2395 urlp = tptr = urlcopy; 2402 urlp = tptr = urlcopy;
2396 for (;;) { 2403 while (1) {
2397 if (*urlp == '/') { 2404 if (*urlp == '/') {
2398 /* skip duplicate (or initial) slash */ 2405 /* skip duplicate (or initial) slash */
2399 if (*tptr == '/') { 2406 if (*tptr == '/') {
@@ -2424,13 +2431,6 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2424 tptr++; 2431 tptr++;
2425 } 2432 }
2426 2433
2427 /* If URL is a directory, add '/' */
2428 if (urlp[-1] != '/') {
2429 if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) {
2430 found_moved_temporarily = urlcopy;
2431 }
2432 }
2433
2434 /* Log it */ 2434 /* Log it */
2435 if (verbose > 1) 2435 if (verbose > 1)
2436 bb_error_msg("url:%s", urlcopy); 2436 bb_error_msg("url:%s", urlcopy);
@@ -2439,11 +2439,9 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2439 while ((tptr = strchr(tptr + 1, '/')) != NULL) { 2439 while ((tptr = strchr(tptr + 1, '/')) != NULL) {
2440 /* have path1/path2 */ 2440 /* have path1/path2 */
2441 *tptr = '\0'; 2441 *tptr = '\0';
2442 if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) { 2442 /* may have subdir config */
2443 /* may have subdir config */ 2443 if (parse_conf(urlcopy + 1, SUBDIR_PARSE) == 0)
2444 parse_conf(urlcopy + 1, SUBDIR_PARSE);
2445 if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip); 2444 if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
2446 }
2447 *tptr = '/'; 2445 *tptr = '/';
2448 } 2446 }
2449 2447
@@ -2470,36 +2468,45 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2470 strcpy(urlp, index_page); 2468 strcpy(urlp, index_page);
2471 } 2469 }
2472 if (stat(tptr, &sb) == 0) { 2470 if (stat(tptr, &sb) == 0) {
2471 /* If URL is a directory with no slash, set up
2472 * "HTTP/1.1 302 Found" "Location: /dir/" reply */
2473 if (urlp[-1] != '/' && S_ISDIR(sb.st_mode)) {
2474 found_moved_temporarily = urlcopy;
2475 } else {
2473#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 2476#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
2474 char *suffix = strrchr(tptr, '.'); 2477 char *suffix = strrchr(tptr, '.');
2475 if (suffix) { 2478 if (suffix) {
2476 Htaccess *cur; 2479 Htaccess *cur;
2477 for (cur = script_i; cur; cur = cur->next) { 2480 for (cur = script_i; cur; cur = cur->next) {
2478 if (strcmp(cur->before_colon + 1, suffix) == 0) { 2481 if (strcmp(cur->before_colon + 1, suffix) == 0) {
2479 cgi_type = CGI_INTERPRETER; 2482 cgi_type = CGI_INTERPRETER;
2480 break; 2483 break;
2484 }
2481 } 2485 }
2482 } 2486 }
2483 }
2484#endif 2487#endif
2485 if (!found_moved_temporarily) {
2486 file_size = sb.st_size; 2488 file_size = sb.st_size;
2487 last_mod = sb.st_mtime; 2489 last_mod = sb.st_mtime;
2488 } 2490 }
2489 } 2491 }
2490#if ENABLE_FEATURE_HTTPD_CGI 2492#if ENABLE_FEATURE_HTTPD_CGI
2491 else if (urlp[-1] == '/') { 2493 else if (urlp[-1] == '/') {
2492 /* It's a dir URL and there is no index.html 2494 /* It's a dir URL and there is no index.html */
2493 * Try cgi-bin/index.cgi */ 2495 /* Is there cgi-bin/index.cgi? */
2494 if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) { 2496 if (access("/cgi-bin/index.cgi"+1, X_OK) != 0)
2495 cgi_type = CGI_INDEX; 2497 send_headers_and_exit(HTTP_NOT_FOUND); /* no */
2496 } 2498 cgi_type = CGI_INDEX;
2497 } 2499 }
2498#endif 2500#endif
2501
2502#if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CGI
2503 /* check_user_passwd() would be confused by added .../index.html, truncate it */
2499 urlp[0] = '\0'; 2504 urlp[0] = '\0';
2505#endif
2500 2506
2501#if ENABLE_FEATURE_HTTPD_CGI 2507#if ENABLE_FEATURE_HTTPD_CGI
2502 total_headers_len = 0; 2508 total_headers_len = 0;
2509 POST_length = 0;
2503#endif 2510#endif
2504 2511
2505 /* Read until blank line */ 2512 /* Read until blank line */
@@ -2513,26 +2520,18 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2513 if (total_headers_len >= MAX_HTTP_HEADERS_SIZE) 2520 if (total_headers_len >= MAX_HTTP_HEADERS_SIZE)
2514 send_headers_and_exit(HTTP_ENTITY_TOO_LARGE); 2521 send_headers_and_exit(HTTP_ENTITY_TOO_LARGE);
2515#endif 2522#endif
2516 if (DEBUG) 2523 dbg("header:'%s'\n", iobuf);
2517 bb_error_msg("header: '%s'", iobuf); 2524#if ENABLE_FEATURE_HTTPD_CGI
2518#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY 2525 /* Only POST needs to know POST_length */
2519 /* Try and do our best to parse more lines */ 2526 if (prequest == request_POST && STRNCASECMP(iobuf, "Content-Length:") == 0) {
2520 if (STRNCASECMP(iobuf, "Content-Length:") == 0) { 2527 tptr = skip_whitespace(iobuf + sizeof("Content-Length:") - 1);
2521 /* extra read only for POST */ 2528 if (!tptr[0])
2522 if (prequest != request_GET 2529 send_headers_and_exit(HTTP_BAD_REQUEST);
2523# if ENABLE_FEATURE_HTTPD_CGI 2530 /* not using strtoul: it ignores leading minus! */
2524 && prequest != request_HEAD 2531 POST_length = bb_strtou(tptr, NULL, 10);
2525# endif 2532 /* length is "ulong", but we need to pass it to int later */
2526 ) { 2533 if (errno || POST_length > INT_MAX)
2527 tptr = skip_whitespace(iobuf + sizeof("Content-Length:") - 1); 2534 send_headers_and_exit(HTTP_BAD_REQUEST);
2528 if (!tptr[0])
2529 send_headers_and_exit(HTTP_BAD_REQUEST);
2530 /* not using strtoul: it ignores leading minus! */
2531 length = bb_strtou(tptr, NULL, 10);
2532 /* length is "ulong", but we need to pass it to int later */
2533 if (errno || length > INT_MAX)
2534 send_headers_and_exit(HTTP_BAD_REQUEST);
2535 }
2536 continue; 2535 continue;
2537 } 2536 }
2538#endif 2537#endif
@@ -2646,37 +2645,36 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2646 send_headers_and_exit(HTTP_UNAUTHORIZED); 2645 send_headers_and_exit(HTTP_UNAUTHORIZED);
2647#endif 2646#endif
2648 2647
2649 if (found_moved_temporarily) { 2648 if (found_moved_temporarily)
2650 send_headers_and_exit(HTTP_MOVED_TEMPORARILY); 2649 send_headers_and_exit(HTTP_MOVED_TEMPORARILY);
2651 }
2652
2653 tptr = urlcopy + 1; /* skip first '/' */
2654 2650
2655#if ENABLE_FEATURE_HTTPD_CGI 2651#if ENABLE_FEATURE_HTTPD_CGI
2656 if (cgi_type != CGI_NONE) { 2652 if (cgi_type != CGI_NONE) {
2657 send_cgi_and_exit( 2653 send_cgi_and_exit(
2658 (cgi_type == CGI_INDEX) ? "/cgi-bin/index.cgi" 2654 (cgi_type == CGI_INDEX) ? "/cgi-bin/index.cgi"
2659 /*CGI_NORMAL or CGI_INTERPRETER*/ : urlcopy, 2655 /*CGI_NORMAL or CGI_INTERPRETER*/ : urlcopy,
2660 urlcopy, prequest, length 2656 urlcopy, prequest, POST_length
2661 ); 2657 );
2662 } 2658 }
2663#endif 2659#endif
2664 2660
2665 if (urlp[-1] == '/') {
2666 strcpy(urlp, index_page);
2667 }
2668
2669#if ENABLE_FEATURE_HTTPD_CGI 2661#if ENABLE_FEATURE_HTTPD_CGI
2670 if (prequest != request_GET && prequest != request_HEAD) { 2662 if (prequest != request_GET && prequest != request_HEAD) {
2671 /* POST for files does not make sense */ 2663 /* POST / DELETE / PUT / OPTIONS for files do not make sense */
2672 send_headers_and_exit(HTTP_NOT_IMPLEMENTED); 2664 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2673 } 2665 }
2674 send_file_and_exit(tptr,
2675 (prequest != request_HEAD ? SEND_HEADERS_AND_BODY : SEND_HEADERS)
2676 );
2677#else 2666#else
2678 send_file_and_exit(tptr, SEND_HEADERS_AND_BODY); 2667 /* !CGI: it can be only GET or HEAD */
2679#endif 2668#endif
2669
2670#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
2671 /* Restore truncated .../index.html */
2672 if (urlp[-1] == '/')
2673 urlp[0] = index_page[0];
2674#endif
2675 send_file_and_exit(urlcopy + 1,
2676 (prequest != request_HEAD ? (SEND_HEADERS + SEND_BODY) : SEND_HEADERS)
2677 );
2680} 2678}
2681 2679
2682 2680
@@ -2705,6 +2703,13 @@ static void mini_httpd(int server_socket)
2705 n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len); 2703 n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len);
2706 if (n < 0) 2704 if (n < 0)
2707 continue; 2705 continue;
2706//TODO: we can reject connects from denied IPs right away;
2707//also, we might want to do one MSG_DONTWAIT'ed recv() here
2708//to detect immediate EOF,
2709//to avoid forking a whole new process for attackers
2710//who open and close lots of connections.
2711//(OTOH, the real mitigtion for this sort of thing is
2712//to ratelimit connects in iptables)
2708 2713
2709 /* set the KEEPALIVE option to cull dead connections */ 2714 /* set the KEEPALIVE option to cull dead connections */
2710 setsockopt_keepalive(n); 2715 setsockopt_keepalive(n);
diff --git a/networking/inetd.c b/networking/inetd.c
index febfb7b73..e5352a555 100644
--- a/networking/inetd.c
+++ b/networking/inetd.c
@@ -1697,7 +1697,7 @@ static void FAST_FUNC chargen_dg(int s, servtab_t *sep)
1697 * we must add 2208988800 seconds to this figure to make up for 1697 * we must add 2208988800 seconds to this figure to make up for
1698 * some seventy years Bell Labs was asleep. 1698 * some seventy years Bell Labs was asleep.
1699 */ 1699 */
1700static uint32_t machtime(void) 1700static NOINLINE uint32_t machtime(void)
1701{ 1701{
1702 struct timeval tv; 1702 struct timeval tv;
1703 1703
diff --git a/networking/ntpd.c b/networking/ntpd.c
index 9c15999f3..6bf6c4e07 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -127,24 +127,15 @@
127 */ 127 */
128#define MAX_VERBOSE 3 128#define MAX_VERBOSE 3
129 129
130
131/* High-level description of the algorithm: 130/* High-level description of the algorithm:
132 * 131 *
133 * We start running with very small poll_exp, BURSTPOLL, 132 * We start running with very small poll_exp, BURSTPOLL,
134 * in order to quickly accumulate INITIAL_SAMPLES datapoints 133 * in order to quickly accumulate INITIAL_SAMPLES datapoints
135 * for each peer. Then, time is stepped if the offset is larger 134 * for each peer. Then, time is stepped if the offset is larger
136 * than STEP_THRESHOLD, otherwise it isn't; anyway, we enlarge 135 * than STEP_THRESHOLD, otherwise it isn't stepped.
137 * poll_exp to MINPOLL and enter frequency measurement step:
138 * we collect new datapoints but ignore them for WATCH_THRESHOLD
139 * seconds. After WATCH_THRESHOLD seconds we look at accumulated
140 * offset and estimate frequency drift.
141 *
142 * (frequency measurement step seems to not be strictly needed,
143 * it is conditionally disabled with USING_INITIAL_FREQ_ESTIMATION
144 * define set to 0)
145 * 136 *
146 * After this, we enter "steady state": we collect a datapoint, 137 * Then poll_exp is set to MINPOLL, and we enter "steady state": we collect
147 * we select the best peer, if this datapoint is not a new one 138 * a datapoint, we select the best peer, if this datapoint is not a new one
148 * (IOW: if this datapoint isn't for selected peer), sleep 139 * (IOW: if this datapoint isn't for selected peer), sleep
149 * and collect another one; otherwise, use its offset to update 140 * and collect another one; otherwise, use its offset to update
150 * frequency drift, if offset is somewhat large, reduce poll_exp, 141 * frequency drift, if offset is somewhat large, reduce poll_exp,
@@ -169,7 +160,7 @@
169 * datapoints after the step. 160 * datapoints after the step.
170 */ 161 */
171 162
172#define INITIAL_SAMPLES 4 /* how many samples do we want for init */ 163#define INITIAL_SAMPLES 3 /* how many samples do we want for init */
173#define MIN_FREQHOLD 10 /* adjust offset, but not freq in this many first adjustments */ 164#define MIN_FREQHOLD 10 /* adjust offset, but not freq in this many first adjustments */
174#define BAD_DELAY_GROWTH 4 /* drop packet if its delay grew by more than this factor */ 165#define BAD_DELAY_GROWTH 4 /* drop packet if its delay grew by more than this factor */
175 166
@@ -189,13 +180,10 @@
189// ^^^^ used to be 0.125. 180// ^^^^ used to be 0.125.
190// Since Linux 2.6.26 (circa 2006), kernel accepts (-0.5s, +0.5s) range 181// Since Linux 2.6.26 (circa 2006), kernel accepts (-0.5s, +0.5s) range
191 182
192/* Stepout threshold (sec). std ntpd uses 900 (11 mins (!)) */
193//UNUSED: #define WATCH_THRESHOLD 128
194/* NB: set WATCH_THRESHOLD to ~60 when debugging to save time) */
195//UNUSED: #define PANIC_THRESHOLD 1000 /* panic threshold (sec) */
196 183
197/* 184// #define PANIC_THRESHOLD 1000 /* panic threshold (sec) */
198 * If we got |offset| > BIGOFF from a peer, cap next query interval 185
186/* If we got |offset| > BIGOFF from a peer, cap next query interval
199 * for this peer by this many seconds: 187 * for this peer by this many seconds:
200 */ 188 */
201#define BIGOFF STEP_THRESHOLD 189#define BIGOFF STEP_THRESHOLD
@@ -204,18 +192,16 @@
204#define FREQ_TOLERANCE 0.000015 /* frequency tolerance (15 PPM) */ 192#define FREQ_TOLERANCE 0.000015 /* frequency tolerance (15 PPM) */
205#define BURSTPOLL 0 /* initial poll */ 193#define BURSTPOLL 0 /* initial poll */
206#define MINPOLL 5 /* minimum poll interval. std ntpd uses 6 (6: 64 sec) */ 194#define MINPOLL 5 /* minimum poll interval. std ntpd uses 6 (6: 64 sec) */
207/* 195/* If offset > discipline_jitter * POLLADJ_GATE, and poll interval is > 2^BIGPOLL,
208 * If offset > discipline_jitter * POLLADJ_GATE, and poll interval is > 2^BIGPOLL,
209 * then it is decreased _at once_. (If <= 2^BIGPOLL, it will be decreased _eventually_). 196 * then it is decreased _at once_. (If <= 2^BIGPOLL, it will be decreased _eventually_).
210 */ 197 */
211#define BIGPOLL 9 /* 2^9 sec ~= 8.5 min */ 198#define BIGPOLL 9 /* 2^9 sec ~= 8.5 min */
212#define MAXPOLL 12 /* maximum poll interval (12: 1.1h, 17: 36.4h). std ntpd uses 17 */ 199#define MAXPOLL 12 /* maximum poll interval (12: 1.1h, 17: 36.4h). std ntpd uses 17 */
213/* 200/* Actively lower poll when we see such big offsets.
214 * Actively lower poll when we see such big offsets.
215 * With SLEW_THRESHOLD = 0.125, it means we try to sync more aggressively 201 * With SLEW_THRESHOLD = 0.125, it means we try to sync more aggressively
216 * if offset increases over ~0.04 sec 202 * if offset increases over ~0.04 sec
217 */ 203 */
218//#define POLLDOWN_OFFSET (SLEW_THRESHOLD / 3) 204// #define POLLDOWN_OFFSET (SLEW_THRESHOLD / 3)
219#define MINDISP 0.01 /* minimum dispersion (sec) */ 205#define MINDISP 0.01 /* minimum dispersion (sec) */
220#define MAXDISP 16 /* maximum dispersion (sec) */ 206#define MAXDISP 16 /* maximum dispersion (sec) */
221#define MAXSTRAT 16 /* maximum stratum (infinity metric) */ 207#define MAXSTRAT 16 /* maximum stratum (infinity metric) */
@@ -223,7 +209,16 @@
223#define MIN_SELECTED 1 /* minimum intersection survivors */ 209#define MIN_SELECTED 1 /* minimum intersection survivors */
224#define MIN_CLUSTERED 3 /* minimum cluster survivors */ 210#define MIN_CLUSTERED 3 /* minimum cluster survivors */
225 211
226#define MAXDRIFT 0.000500 /* frequency drift we can correct (500 PPM) */ 212/* Correct frequency ourself (0) or let kernel do it (1)? */
213#define USING_KERNEL_PLL_LOOP 1
214// /* frequency drift we can correct (500 PPM) */
215// #define MAXDRIFT 0.000500
216// /* Compromise Allan intercept (sec). doc uses 1500, std ntpd uses 512 */
217// #define ALLAN 512
218// /* PLL loop gain */
219// #define PLL 65536
220// /* FLL loop gain [why it depends on MAXPOLL??] */
221// #define FLL (MAXPOLL + 1)
227 222
228/* Poll-adjust threshold. 223/* Poll-adjust threshold.
229 * When we see that offset is small enough compared to discipline jitter, 224 * When we see that offset is small enough compared to discipline jitter,
@@ -239,12 +234,6 @@
239 */ 234 */
240#define POLLADJ_GATE 4 235#define POLLADJ_GATE 4
241#define TIMECONST_HACK_GATE 2 236#define TIMECONST_HACK_GATE 2
242/* Compromise Allan intercept (sec). doc uses 1500, std ntpd uses 512 */
243#define ALLAN 512
244/* PLL loop gain */
245#define PLL 65536
246/* FLL loop gain [why it depends on MAXPOLL??] */
247#define FLL (MAXPOLL + 1)
248/* Parameter averaging constant */ 237/* Parameter averaging constant */
249#define AVG 4 238#define AVG 4
250 239
@@ -372,9 +361,6 @@ typedef struct {
372 char p_hostname[1]; 361 char p_hostname[1];
373} peer_t; 362} peer_t;
374 363
375
376#define USING_KERNEL_PLL_LOOP 1
377
378enum { 364enum {
379 OPT_n = (1 << 0), 365 OPT_n = (1 << 0),
380 OPT_q = (1 << 1), 366 OPT_q = (1 << 1),
@@ -453,7 +439,7 @@ struct globals {
453 */ 439 */
454#define G_precision_exp -9 440#define G_precision_exp -9
455 /* 441 /*
456 * G_precision_exp is used only for construction outgoing packets. 442 * G_precision_exp is used only for constructing outgoing packets.
457 * It's ok to set G_precision_sec to a slightly different value 443 * It's ok to set G_precision_sec to a slightly different value
458 * (One which is "nicer looking" in logs). 444 * (One which is "nicer looking" in logs).
459 * Exact value would be (1.0 / (1 << (- G_precision_exp))): 445 * Exact value would be (1.0 / (1 << (- G_precision_exp))):
@@ -484,7 +470,6 @@ struct globals {
484}; 470};
485#define G (*ptr_to_globals) 471#define G (*ptr_to_globals)
486 472
487
488#define VERB1 if (MAX_VERBOSE && G.verbose) 473#define VERB1 if (MAX_VERBOSE && G.verbose)
489#define VERB2 if (MAX_VERBOSE >= 2 && G.verbose >= 2) 474#define VERB2 if (MAX_VERBOSE >= 2 && G.verbose >= 2)
490#define VERB3 if (MAX_VERBOSE >= 3 && G.verbose >= 3) 475#define VERB3 if (MAX_VERBOSE >= 3 && G.verbose >= 3)
@@ -567,13 +552,13 @@ gettime1900d(void)
567} 552}
568 553
569static void 554static void
570d_to_tv(double d, struct timeval *tv) 555d_to_tv(struct timeval *tv, double d)
571{ 556{
572 tv->tv_sec = (long)d; 557 tv->tv_sec = (long)d;
573 tv->tv_usec = (d - tv->tv_sec) * 1000000; 558 tv->tv_usec = (d - tv->tv_sec) * 1000000;
574} 559}
575 560
576static double 561static NOINLINE double
577lfp_to_d(l_fixedpt_t lfp) 562lfp_to_d(l_fixedpt_t lfp)
578{ 563{
579 double ret; 564 double ret;
@@ -582,7 +567,7 @@ lfp_to_d(l_fixedpt_t lfp)
582 ret = (double)lfp.int_partl + ((double)lfp.fractionl / UINT_MAX); 567 ret = (double)lfp.int_partl + ((double)lfp.fractionl / UINT_MAX);
583 return ret; 568 return ret;
584} 569}
585static double 570static NOINLINE double
586sfp_to_d(s_fixedpt_t sfp) 571sfp_to_d(s_fixedpt_t sfp)
587{ 572{
588 double ret; 573 double ret;
@@ -592,25 +577,25 @@ sfp_to_d(s_fixedpt_t sfp)
592 return ret; 577 return ret;
593} 578}
594#if ENABLE_FEATURE_NTPD_SERVER 579#if ENABLE_FEATURE_NTPD_SERVER
595static l_fixedpt_t 580static NOINLINE void
596d_to_lfp(double d) 581d_to_lfp(l_fixedpt_t *lfp, double d)
597{ 582{
598 l_fixedpt_t lfp; 583 uint32_t intl;
599 lfp.int_partl = (uint32_t)d; 584 uint32_t frac;
600 lfp.fractionl = (uint32_t)((d - lfp.int_partl) * UINT_MAX); 585 intl = (uint32_t)d;
601 lfp.int_partl = htonl(lfp.int_partl); 586 frac = (uint32_t)((d - intl) * UINT_MAX);
602 lfp.fractionl = htonl(lfp.fractionl); 587 lfp->int_partl = htonl(intl);
603 return lfp; 588 lfp->fractionl = htonl(frac);
604} 589}
605static s_fixedpt_t 590static NOINLINE void
606d_to_sfp(double d) 591d_to_sfp(s_fixedpt_t *sfp, double d)
607{ 592{
608 s_fixedpt_t sfp; 593 uint16_t ints;
609 sfp.int_parts = (uint16_t)d; 594 uint16_t frac;
610 sfp.fractions = (uint16_t)((d - sfp.int_parts) * USHRT_MAX); 595 ints = (uint16_t)d;
611 sfp.int_parts = htons(sfp.int_parts); 596 frac = (uint16_t)((d - ints) * USHRT_MAX);
612 sfp.fractions = htons(sfp.fractions); 597 sfp->int_parts = htons(ints);
613 return sfp; 598 sfp->fractions = htons(frac);
614} 599}
615#endif 600#endif
616 601
@@ -989,7 +974,6 @@ send_query_to_peer(peer_t *p)
989 set_next(p, RESPONSE_INTERVAL); 974 set_next(p, RESPONSE_INTERVAL);
990} 975}
991 976
992
993/* Note that there is no provision to prevent several run_scripts 977/* Note that there is no provision to prevent several run_scripts
994 * to be started in quick succession. In fact, it happens rather often 978 * to be started in quick succession. In fact, it happens rather often
995 * if initial syncronization results in a step. 979 * if initial syncronization results in a step.
@@ -1053,7 +1037,7 @@ step_time(double offset)
1053 1037
1054 xgettimeofday(&tvc); 1038 xgettimeofday(&tvc);
1055 dtime = tvc.tv_sec + (1.0e-6 * tvc.tv_usec) + offset; 1039 dtime = tvc.tv_sec + (1.0e-6 * tvc.tv_usec) + offset;
1056 d_to_tv(dtime, &tvn); 1040 d_to_tv(&tvn, dtime);
1057 xsettimeofday(&tvn); 1041 xsettimeofday(&tvn);
1058 1042
1059 VERB2 { 1043 VERB2 {
@@ -1767,7 +1751,6 @@ update_local_clock(peer_t *p)
1767 return 1; /* "ok to increase poll interval" */ 1751 return 1; /* "ok to increase poll interval" */
1768} 1752}
1769 1753
1770
1771/* 1754/*
1772 * We've got a new reply packet from a peer, process it 1755 * We've got a new reply packet from a peer, process it
1773 * (helpers first) 1756 * (helpers first)
@@ -2134,17 +2117,17 @@ recv_and_process_client_pkt(void /*int fd*/)
2134 msg.m_ppoll = G.poll_exp; 2117 msg.m_ppoll = G.poll_exp;
2135 msg.m_precision_exp = G_precision_exp; 2118 msg.m_precision_exp = G_precision_exp;
2136 /* this time was obtained between poll() and recv() */ 2119 /* this time was obtained between poll() and recv() */
2137 msg.m_rectime = d_to_lfp(G.cur_time); 2120 d_to_lfp(&msg.m_rectime, G.cur_time);
2138 msg.m_xmttime = d_to_lfp(gettime1900d()); /* this instant */ 2121 d_to_lfp(&msg.m_xmttime, gettime1900d()); /* this instant */
2139 if (G.peer_cnt == 0) { 2122 if (G.peer_cnt == 0) {
2140 /* we have no peers: "stratum 1 server" mode. reftime = our own time */ 2123 /* we have no peers: "stratum 1 server" mode. reftime = our own time */
2141 G.reftime = G.cur_time; 2124 G.reftime = G.cur_time;
2142 } 2125 }
2143 msg.m_reftime = d_to_lfp(G.reftime); 2126 d_to_lfp(&msg.m_reftime, G.reftime);
2144 msg.m_orgtime = query_xmttime; 2127 msg.m_orgtime = query_xmttime;
2145 msg.m_rootdelay = d_to_sfp(G.rootdelay); 2128 d_to_sfp(&msg.m_rootdelay, G.rootdelay);
2146//simple code does not do this, fix simple code! 2129//simple code does not do this, fix simple code!
2147 msg.m_rootdisp = d_to_sfp(G.rootdisp); 2130 d_to_sfp(&msg.m_rootdisp, G.rootdisp);
2148 //version = (query_status & VERSION_MASK); /* ... >> VERSION_SHIFT - done below instead */ 2131 //version = (query_status & VERSION_MASK); /* ... >> VERSION_SHIFT - done below instead */
2149 msg.m_refid = G.refid; // (version > (3 << VERSION_SHIFT)) ? G.refid : G.refid3; 2132 msg.m_refid = G.refid; // (version > (3 << VERSION_SHIFT)) ? G.refid : G.refid3;
2150 2133
diff --git a/networking/tls.c b/networking/tls.c
index b05afe1c9..a1b12f9ed 100644
--- a/networking/tls.c
+++ b/networking/tls.c
@@ -18,6 +18,7 @@
18//kbuild:lib-$(CONFIG_TLS) += tls_aesgcm.o 18//kbuild:lib-$(CONFIG_TLS) += tls_aesgcm.o
19//kbuild:lib-$(CONFIG_TLS) += tls_rsa.o 19//kbuild:lib-$(CONFIG_TLS) += tls_rsa.o
20//kbuild:lib-$(CONFIG_TLS) += tls_fe.o 20//kbuild:lib-$(CONFIG_TLS) += tls_fe.o
21//kbuild:lib-$(CONFIG_TLS) += tls_sp_c32.o
21 22
22#include "tls.h" 23#include "tls.h"
23 24
@@ -265,8 +266,9 @@ enum {
265 GOT_CERT_RSA_KEY_ALG = 1 << 1, 266 GOT_CERT_RSA_KEY_ALG = 1 << 1,
266 GOT_CERT_ECDSA_KEY_ALG = 1 << 2, // so far unused 267 GOT_CERT_ECDSA_KEY_ALG = 1 << 2, // so far unused
267 GOT_EC_KEY = 1 << 3, 268 GOT_EC_KEY = 1 << 3,
268 ENCRYPTION_AESGCM = 1 << 4, // else AES-SHA (or NULL-SHA if ALLOW_RSA_NULL_SHA256=1) 269 GOT_EC_CURVE_X25519 = 1 << 4, // else P256
269 ENCRYPT_ON_WRITE = 1 << 5, 270 ENCRYPTION_AESGCM = 1 << 5, // else AES-SHA (or NULL-SHA if ALLOW_RSA_NULL_SHA256=1)
271 ENCRYPT_ON_WRITE = 1 << 6,
270}; 272};
271 273
272struct record_hdr { 274struct record_hdr {
@@ -285,7 +287,11 @@ struct tls_handshake_data {
285//TODO: store just the DER key here, parse/use/delete it when sending client key 287//TODO: store just the DER key here, parse/use/delete it when sending client key
286//this way it will stay key type agnostic here. 288//this way it will stay key type agnostic here.
287 psRsaKey_t server_rsa_pub_key; 289 psRsaKey_t server_rsa_pub_key;
288 uint8_t ecc_pub_key32[32]; 290
291 /* peer's elliptic curve key data */
292 /* for x25519, it contains one point in first 32 bytes */
293 /* for P256, it contains x,y point pair, each 32 bytes long */
294 uint8_t ecc_pub_key32[2 * 32];
289 295
290/* HANDSHAKE HASH: */ 296/* HANDSHAKE HASH: */
291 //unsigned saved_client_hello_size; 297 //unsigned saved_client_hello_size;
@@ -1526,20 +1532,13 @@ static void send_client_hello_and_alloc_hsd(tls_state_t *tls, const char *sni)
1526 }; 1532 };
1527 static const uint8_t supported_groups[] = { 1533 static const uint8_t supported_groups[] = {
1528 0x00,0x0a, //extension_type: "supported_groups" 1534 0x00,0x0a, //extension_type: "supported_groups"
1529 0x00,0x04, //ext len 1535 0x00,0x06, //ext len
1530 0x00,0x02, //list len 1536 0x00,0x04, //list len
1531 0x00,0x1d, //curve_x25519 (RFC 7748) 1537 0x00,0x17, //curve_secp256r1 (aka P256)
1532 //0x00,0x1e, //curve_x448 (RFC 7748)
1533 //0x00,0x17, //curve_secp256r1
1534 //0x00,0x18, //curve_secp384r1 1538 //0x00,0x18, //curve_secp384r1
1535 //0x00,0x19, //curve_secp521r1 1539 //0x00,0x19, //curve_secp521r1
1536//TODO: implement secp256r1 (at least): dl.fedoraproject.org immediately aborts 1540 0x00,0x1d, //curve_x25519 (RFC 7748)
1537//if only x25519/x448 are advertised, seems to support only secpNNNr1 curves: 1541 //0x00,0x1e, //curve_x448 (RFC 7748)
1538// openssl s_client -connect dl.fedoraproject.org:443 -debug -tls1_2 -cipher ECDHE-RSA-AES128-GCM-SHA256
1539//Peer signing digest: SHA512
1540//Peer signature type: RSA
1541//Server Temp Key: ECDH, P-256, 256 bits
1542//TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
1543 }; 1542 };
1544 //static const uint8_t signature_algorithms[] = { 1543 //static const uint8_t signature_algorithms[] = {
1545 // 000d 1544 // 000d
@@ -1877,12 +1876,32 @@ static void process_server_key(tls_state_t *tls, int len)
1877 if (len < (1+2+1+32)) tls_error_die(tls); 1876 if (len < (1+2+1+32)) tls_error_die(tls);
1878 keybuf += 4; 1877 keybuf += 4;
1879 1878
1880 /* So far we only support curve_x25519 */ 1879#if BB_BIG_ENDIAN
1880# define _0x03001741 0x03001741
1881# define _0x03001d20 0x03001d20
1882#else
1883# define _0x03001741 0x41170003
1884# define _0x03001d20 0x201d0003
1885#endif
1881 move_from_unaligned32(t32, keybuf); 1886 move_from_unaligned32(t32, keybuf);
1882 if (t32 != htonl(0x03001d20)) 1887 keybuf += 4;
1883 bb_simple_error_msg_and_die("elliptic curve is not x25519"); 1888 switch (t32) {
1889 case _0x03001d20: //curve_x25519
1890 tls->flags |= GOT_EC_CURVE_X25519;
1891 memcpy(tls->hsd->ecc_pub_key32, keybuf, 32);
1892 break;
1893 case _0x03001741: //curve_secp256r1 (aka P256)
1894 /* P256 point can be transmitted odd- or even-compressed
1895 * (first byte is 3 or 2) or uncompressed (4).
1896 */
1897 if (*keybuf++ != 4)
1898 bb_simple_error_msg_and_die("compressed EC points not supported");
1899 memcpy(tls->hsd->ecc_pub_key32, keybuf, 2 * 32);
1900 break;
1901 default:
1902 bb_error_msg_and_die("elliptic curve is not x25519 or P256: 0x%08x", t32);
1903 }
1884 1904
1885 memcpy(tls->hsd->ecc_pub_key32, keybuf + 4, 32);
1886 tls->flags |= GOT_EC_KEY; 1905 tls->flags |= GOT_EC_KEY;
1887 dbg("got eccPubKey\n"); 1906 dbg("got eccPubKey\n");
1888} 1907}
@@ -1918,9 +1937,7 @@ static void send_client_key_exchange(tls_state_t *tls)
1918 }; 1937 };
1919//FIXME: better size estimate 1938//FIXME: better size estimate
1920 struct client_key_exchange *record = tls_get_zeroed_outbuf(tls, sizeof(*record)); 1939 struct client_key_exchange *record = tls_get_zeroed_outbuf(tls, sizeof(*record));
1921 uint8_t rsa_premaster[RSA_PREMASTER_SIZE]; 1940 uint8_t premaster[RSA_PREMASTER_SIZE > EC_CURVE_KEYSIZE ? RSA_PREMASTER_SIZE : EC_CURVE_KEYSIZE];
1922 uint8_t x25519_premaster[CURVE25519_KEYSIZE];
1923 uint8_t *premaster;
1924 int premaster_size; 1941 int premaster_size;
1925 int len; 1942 int len;
1926 1943
@@ -1929,19 +1946,19 @@ static void send_client_key_exchange(tls_state_t *tls)
1929 if (!(tls->flags & GOT_CERT_RSA_KEY_ALG)) 1946 if (!(tls->flags & GOT_CERT_RSA_KEY_ALG))
1930 bb_simple_error_msg("server cert is not RSA"); 1947 bb_simple_error_msg("server cert is not RSA");
1931 1948
1932 tls_get_random(rsa_premaster, sizeof(rsa_premaster)); 1949 tls_get_random(premaster, RSA_PREMASTER_SIZE);
1933 if (TLS_DEBUG_FIXED_SECRETS) 1950 if (TLS_DEBUG_FIXED_SECRETS)
1934 memset(rsa_premaster, 0x44, sizeof(rsa_premaster)); 1951 memset(premaster, 0x44, RSA_PREMASTER_SIZE);
1935 // RFC 5246 1952 // RFC 5246
1936 // "Note: The version number in the PreMasterSecret is the version 1953 // "Note: The version number in the PreMasterSecret is the version
1937 // offered by the client in the ClientHello.client_version, not the 1954 // offered by the client in the ClientHello.client_version, not the
1938 // version negotiated for the connection." 1955 // version negotiated for the connection."
1939 rsa_premaster[0] = TLS_MAJ; 1956 premaster[0] = TLS_MAJ;
1940 rsa_premaster[1] = TLS_MIN; 1957 premaster[1] = TLS_MIN;
1941 dump_hex("premaster:%s\n", rsa_premaster, sizeof(rsa_premaster)); 1958 dump_hex("premaster:%s\n", premaster, sizeof(premaster));
1942 len = psRsaEncryptPub(/*pool:*/ NULL, 1959 len = psRsaEncryptPub(/*pool:*/ NULL,
1943 /* psRsaKey_t* */ &tls->hsd->server_rsa_pub_key, 1960 /* psRsaKey_t* */ &tls->hsd->server_rsa_pub_key,
1944 rsa_premaster, /*inlen:*/ sizeof(rsa_premaster), 1961 premaster, /*inlen:*/ RSA_PREMASTER_SIZE,
1945 record->key + 2, sizeof(record->key) - 2, 1962 record->key + 2, sizeof(record->key) - 2,
1946 data_param_ignored 1963 data_param_ignored
1947 ); 1964 );
@@ -1949,33 +1966,36 @@ static void send_client_key_exchange(tls_state_t *tls)
1949 record->key[0] = len >> 8; 1966 record->key[0] = len >> 8;
1950 record->key[1] = len & 0xff; 1967 record->key[1] = len & 0xff;
1951 len += 2; 1968 len += 2;
1952 premaster = rsa_premaster; 1969 premaster_size = RSA_PREMASTER_SIZE;
1953 premaster_size = sizeof(rsa_premaster);
1954 } else { 1970 } else {
1955 /* ECDHE */ 1971 /* ECDHE */
1956 static const uint8_t basepoint9[CURVE25519_KEYSIZE] ALIGN1 = {9};
1957 uint8_t privkey[CURVE25519_KEYSIZE]; //[32]
1958
1959 if (!(tls->flags & GOT_EC_KEY)) 1972 if (!(tls->flags & GOT_EC_KEY))
1960 bb_simple_error_msg("server did not provide EC key"); 1973 bb_simple_error_msg_and_die("server did not provide EC key");
1961 1974
1962 /* Generate random private key, see RFC 7748 */ 1975 if (tls->flags & GOT_EC_CURVE_X25519) {
1963 tls_get_random(privkey, sizeof(privkey)); 1976 /* ECDHE, curve x25519 */
1964 privkey[0] &= 0xf8; 1977 dbg("computing x25519_premaster\n");
1965 privkey[CURVE25519_KEYSIZE-1] = ((privkey[CURVE25519_KEYSIZE-1] & 0x7f) | 0x40); 1978 curve_x25519_compute_pubkey_and_premaster(
1966 1979 record->key + 1, premaster,
1967 /* Compute public key */ 1980 /*point:*/ tls->hsd->ecc_pub_key32
1968 curve25519(record->key + 1, privkey, basepoint9); 1981 );
1969 1982 len = CURVE25519_KEYSIZE;
1970 /* Compute premaster using peer's public key */ 1983 //record->key[0] = len;
1971 dbg("computing x25519_premaster\n"); 1984 //len++;
1972 curve25519(x25519_premaster, privkey, tls->hsd->ecc_pub_key32); 1985 //premaster_size = CURVE25519_KEYSIZE;
1973 1986 } else {
1974 len = CURVE25519_KEYSIZE; 1987 /* ECDHE, curve P256 */
1988 dbg("computing P256_premaster\n");
1989 curve_P256_compute_pubkey_and_premaster(
1990 record->key + 2, premaster,
1991 /*point:*/ tls->hsd->ecc_pub_key32
1992 );
1993 record->key[1] = 4; /* "uncompressed point" */
1994 len = 1 + P256_KEYSIZE * 2;
1995 }
1975 record->key[0] = len; 1996 record->key[0] = len;
1976 len++; 1997 len++;
1977 premaster = x25519_premaster; 1998 premaster_size = P256_KEYSIZE; // = CURVE25519_KEYSIZE = 32
1978 premaster_size = sizeof(x25519_premaster);
1979 } 1999 }
1980 2000
1981 record->type = HANDSHAKE_CLIENT_KEY_EXCHANGE; 2001 record->type = HANDSHAKE_CLIENT_KEY_EXCHANGE;
diff --git a/networking/tls.h b/networking/tls.h
index d4ac1bef8..215e92b02 100644
--- a/networking/tls.h
+++ b/networking/tls.h
@@ -105,4 +105,15 @@ void xorbuf_aligned_AES_BLOCK_SIZE(void* buf, const void* mask) FAST_FUNC;
105#include "tls_aes.h" 105#include "tls_aes.h"
106#include "tls_aesgcm.h" 106#include "tls_aesgcm.h"
107#include "tls_rsa.h" 107#include "tls_rsa.h"
108#include "tls_fe.h" 108
109#define EC_CURVE_KEYSIZE 32
110#define P256_KEYSIZE 32
111#define CURVE25519_KEYSIZE 32
112
113void curve_x25519_compute_pubkey_and_premaster(
114 uint8_t *pubkey32, uint8_t *premaster32,
115 const uint8_t *peerkey32) FAST_FUNC;
116
117void curve_P256_compute_pubkey_and_premaster(
118 uint8_t *pubkey2x32, uint8_t *premaster32,
119 const uint8_t *peerkey2x32) FAST_FUNC;
diff --git a/networking/tls_fe.c b/networking/tls_fe.c
index f810e112a..ecb410281 100644
--- a/networking/tls_fe.c
+++ b/networking/tls_fe.c
@@ -64,8 +64,8 @@ static void fprime_select(byte *dst, const byte *zero, const byte *one, byte con
64#endif 64#endif
65 65
66static void fe_select(byte *dst, 66static void fe_select(byte *dst,
67 const byte *zero, const byte *one, 67 const byte *zero, const byte *one,
68 byte condition) 68 byte condition)
69{ 69{
70 const byte mask = -condition; 70 const byte mask = -condition;
71 int i; 71 int i;
@@ -108,26 +108,26 @@ static void raw_try_sub(byte *x, const byte *p)
108#if 0 //UNUSED 108#if 0 //UNUSED
109static int prime_msb(const byte *p) 109static int prime_msb(const byte *p)
110{ 110{
111 int i; 111 int i;
112 byte x; 112 byte x;
113 int shift = 1; 113 int shift = 1;
114 int z = F25519_SIZE - 1; 114 int z = F25519_SIZE - 1;
115 115
116 /* 116 /*
117 Test for any hot bits. 117 Test for any hot bits.
118 As soon as one instance is encountered set shift to 0. 118 As soon as one instance is encountered set shift to 0.
119 */ 119 */
120 for (i = F25519_SIZE - 1; i >= 0; i--) { 120 for (i = F25519_SIZE - 1; i >= 0; i--) {
121 shift &= ((shift ^ ((-p[i] | p[i]) >> 7)) & 1); 121 shift &= ((shift ^ ((-p[i] | p[i]) >> 7)) & 1);
122 z -= shift; 122 z -= shift;
123 } 123 }
124 x = p[z]; 124 x = p[z];
125 z <<= 3; 125 z <<= 3;
126 shift = 1; 126 shift = 1;
127 for (i = 0; i < 8; i++) { 127 for (i = 0; i < 8; i++) {
128 shift &= ((-(x >> i) | (x >> i)) >> (7 - i) & 1); 128 shift &= ((-(x >> i) | (x >> i)) >> (7 - i) & 1);
129 z += shift; 129 z += shift;
130 } 130 }
131 131
132 return z - 1; 132 return z - 1;
133} 133}
@@ -163,11 +163,11 @@ static void fprime_mul(byte *r, const byte *a, const byte *b,
163 const byte bit = (b[i >> 3] >> (i & 7)) & 1; 163 const byte bit = (b[i >> 3] >> (i & 7)) & 1;
164 byte plusa[F25519_SIZE]; 164 byte plusa[F25519_SIZE];
165 165
166 for (j = 0; j < F25519_SIZE; j++) { 166 for (j = 0; j < F25519_SIZE; j++) {
167 c |= ((word16)r[j]) << 1; 167 c |= ((word16)r[j]) << 1;
168 r[j] = (byte)c; 168 r[j] = (byte)c;
169 c >>= 8; 169 c >>= 8;
170 } 170 }
171 raw_try_sub(r, modulus); 171 raw_try_sub(r, modulus);
172 172
173 fprime_copy(plusa, r); 173 fprime_copy(plusa, r);
@@ -315,7 +315,7 @@ static void fe_mul__distinct(byte *r, const byte *a, const byte *b)
315 315
316 for (; j < F25519_SIZE; j++) 316 for (; j < F25519_SIZE; j++)
317 c += ((word32)a[j]) * 317 c += ((word32)a[j]) *
318 ((word32)b[i + F25519_SIZE - j]) * 38; 318 ((word32)b[i + F25519_SIZE - j]) * 38;
319 319
320 r[i] = c; 320 r[i] = c;
321 } 321 }
@@ -474,9 +474,9 @@ static void fe_sqrt(byte *r, const byte *a)
474 474
475/* Differential addition */ 475/* Differential addition */
476static void xc_diffadd(byte *x5, byte *z5, 476static void xc_diffadd(byte *x5, byte *z5,
477 const byte *x1, const byte *z1, 477 const byte *x1, const byte *z1,
478 const byte *x2, const byte *z2, 478 const byte *x2, const byte *z2,
479 const byte *x3, const byte *z3) 479 const byte *x3, const byte *z3)
480{ 480{
481 /* Explicit formulas database: dbl-1987-m3 481 /* Explicit formulas database: dbl-1987-m3
482 * 482 *
@@ -516,7 +516,7 @@ static void xc_diffadd(byte *x5, byte *z5,
516 516
517/* Double an X-coordinate */ 517/* Double an X-coordinate */
518static void xc_double(byte *x3, byte *z3, 518static void xc_double(byte *x3, byte *z3,
519 const byte *x1, const byte *z1) 519 const byte *x1, const byte *z1)
520{ 520{
521 /* Explicit formulas database: dbl-1987-m 521 /* Explicit formulas database: dbl-1987-m
522 * 522 *
@@ -544,11 +544,14 @@ static void xc_double(byte *x3, byte *z3,
544 fe_mul_c(z3, x1sq, 4); 544 fe_mul_c(z3, x1sq, 4);
545} 545}
546 546
547void FAST_FUNC curve25519(byte *result, const byte *e, const byte *q) 547static void curve25519(byte *result, const byte *e, const byte *q)
548{ 548{
549 int i; 549 int i;
550 550
551 struct { 551 struct {
552 /* for bbox's special case of q == NULL meaning "use basepoint" */
553 /*static const*/ uint8_t basepoint9[CURVE25519_KEYSIZE]; // = {9};
554
552 /* from wolfssl-3.15.3/wolfssl/wolfcrypt/fe_operations.h */ 555 /* from wolfssl-3.15.3/wolfssl/wolfcrypt/fe_operations.h */
553 /*static const*/ byte f25519_one[F25519_SIZE]; // = {1}; 556 /*static const*/ byte f25519_one[F25519_SIZE]; // = {1};
554 557
@@ -559,6 +562,7 @@ void FAST_FUNC curve25519(byte *result, const byte *e, const byte *q)
559 byte xm1[F25519_SIZE]; // = {1}; 562 byte xm1[F25519_SIZE]; // = {1};
560 byte zm1[F25519_SIZE]; // = {0}; 563 byte zm1[F25519_SIZE]; // = {0};
561 } z; 564 } z;
565#define basepoint9 z.basepoint9
562#define f25519_one z.f25519_one 566#define f25519_one z.f25519_one
563#define xm z.xm 567#define xm z.xm
564#define zm z.zm 568#define zm z.zm
@@ -569,6 +573,11 @@ void FAST_FUNC curve25519(byte *result, const byte *e, const byte *q)
569 zm[0] = 1; 573 zm[0] = 1;
570 xm1[0] = 1; 574 xm1[0] = 1;
571 575
576 if (!q) {
577 basepoint9[0] = 9;
578 q = basepoint9;
579 }
580
572 /* Note: bit 254 is assumed to be 1 */ 581 /* Note: bit 254 is assumed to be 1 */
573 lm_copy(xm, q); 582 lm_copy(xm, q);
574 583
@@ -599,3 +608,23 @@ void FAST_FUNC curve25519(byte *result, const byte *e, const byte *q)
599 fe_mul__distinct(result, zm1, xm); 608 fe_mul__distinct(result, zm1, xm);
600 fe_normalize(result); 609 fe_normalize(result);
601} 610}
611
612/* interface to bbox's TLS code: */
613
614void FAST_FUNC curve_x25519_compute_pubkey_and_premaster(
615 uint8_t *pubkey, uint8_t *premaster,
616 const uint8_t *peerkey32)
617{
618 uint8_t privkey[CURVE25519_KEYSIZE]; //[32]
619
620 /* Generate random private key, see RFC 7748 */
621 tls_get_random(privkey, sizeof(privkey));
622 privkey[0] &= 0xf8;
623 privkey[CURVE25519_KEYSIZE-1] = ((privkey[CURVE25519_KEYSIZE-1] & 0x7f) | 0x40);
624
625 /* Compute public key */
626 curve25519(pubkey, privkey, NULL /* "use base point of x25519" */);
627
628 /* Compute premaster using peer's public key */
629 curve25519(premaster, privkey, peerkey32);
630}
diff --git a/networking/tls_fe.h b/networking/tls_fe.h
deleted file mode 100644
index fe8cff228..000000000
--- a/networking/tls_fe.h
+++ /dev/null
@@ -1,7 +0,0 @@
1/*
2 * Copyright (C) 2018 Denys Vlasenko
3 *
4 * Licensed under GPLv2, see file LICENSE in this source tree.
5 */
6#define CURVE25519_KEYSIZE 32
7void curve25519(uint8_t *result, const uint8_t *e, const uint8_t *q) FAST_FUNC;
diff --git a/networking/tls_pstm.c b/networking/tls_pstm.c
index e5544ab11..99929031d 100644
--- a/networking/tls_pstm.c
+++ b/networking/tls_pstm.c
@@ -438,9 +438,9 @@ int32 FAST_FUNC pstm_read_unsigned_bin(pstm_int *a, unsigned char *b, int32 c)
438 int32 idx = (c - 1) & ~3; 438 int32 idx = (c - 1) & ~3;
439 switch (c % 4) { 439 switch (c % 4) {
440 case 0: do { pd[idx+0] = *b++; 440 case 0: do { pd[idx+0] = *b++;
441 case 3: pd[idx+1] = *b++; 441 case 3: pd[idx+1] = *b++;
442 case 2: pd[idx+2] = *b++; 442 case 2: pd[idx+2] = *b++;
443 case 1: pd[idx+3] = *b++; 443 case 1: pd[idx+3] = *b++;
444 idx -= 4; 444 idx -= 4;
445 } while ((c -= 4) > 0); 445 } while ((c -= 4) > 0);
446 } 446 }
@@ -1427,7 +1427,7 @@ static int32 pstm_div(psPool_t *pool, pstm_int *a, pstm_int *b, pstm_int *c,
1427 } 1427 }
1428 1428
1429 /* while (q{i-t-1} * (yt * b + y{t-1})) > 1429 /* while (q{i-t-1} * (yt * b + y{t-1})) >
1430 xi * b**2 + xi-1 * b + xi-2 1430 xi * b**2 + xi-1 * b + xi-2
1431 1431
1432 do q{i-t-1} -= 1; 1432 do q{i-t-1} -= 1;
1433 */ 1433 */
diff --git a/networking/tls_pstm_montgomery_reduce.c b/networking/tls_pstm_montgomery_reduce.c
index 20f9c26d5..4181a0590 100644
--- a/networking/tls_pstm_montgomery_reduce.c
+++ b/networking/tls_pstm_montgomery_reduce.c
@@ -62,6 +62,7 @@
62#define LOOP_START \ 62#define LOOP_START \
63 mu = c[x] * mp 63 mu = c[x] * mp
64 64
65#if 0
65#define INNERMUL \ 66#define INNERMUL \
66asm( \ 67asm( \
67 "movl %5,%%eax \n\t" \ 68 "movl %5,%%eax \n\t" \
@@ -74,15 +75,38 @@ asm( \
74:"=g"(_c[LO]), "=r"(cy) \ 75:"=g"(_c[LO]), "=r"(cy) \
75:"0"(_c[LO]), "1"(cy), "g"(mu), "g"(*tmpm++) \ 76:"0"(_c[LO]), "1"(cy), "g"(mu), "g"(*tmpm++) \
76: "%eax", "%edx", "cc") 77: "%eax", "%edx", "cc")
78/*
79 * The above generated "error: 'asm' operand has impossible constraints" on Android.
80 * Do they reserve in their ABI a register for something, and there aren't enough left?
81 */
82#else
83/* Let's avoid two explicit "movl" by telling compiler to put input value of *tmpm++
84 * into EAX, and to expect cy result in EDX:
85 */
86#define INNERMUL \
87asm( \
88 "mull %4 \n\t" \
89 "addl %3,%%eax \n\t" \
90 "adcl $0,%%edx \n\t" \
91 "addl %%eax,%0 \n\t" \
92 "adcl $0,%%edx \n\t" \
93:"=g"(_c[LO]), "=&d"(cy) \
94:"0"(_c[LO]), "g"(cy), "g"(mu), "a"(*tmpm++) \
95:"cc")
96/* This doesn't tell compiler that we clobber EAX, but it probably won't need
97 * the value of *tmpm anyway, thus won't try to reuse EAX contents.
98 * TODO: fix it with dummy "=a"(clobbered_eax) output?
99 */
100#endif
77 101
78#define PROPCARRY \ 102#define PROPCARRY \
79asm( \ 103asm( \
80 "addl %1,%0 \n\t" \ 104 "addl %1,%0 \n\t" \
81 "setb %%al \n\t" \ 105 "sbb %1,%1 \n\t" \
82 "movzbl %%al,%1 \n\t" \ 106 "neg %1 \n\t" \
83:"=g"(_c[LO]), "=r"(cy) \ 107:"=g"(_c[LO]), "=r"(cy) \
84:"0"(_c[LO]), "1"(cy) \ 108:"0"(_c[LO]), "1"(cy) \
85: "%eax", "cc") 109:"cc")
86 110
87/******************************************************************************/ 111/******************************************************************************/
88#elif defined(PSTM_X86_64) 112#elif defined(PSTM_X86_64)
diff --git a/networking/tls_rsa.c b/networking/tls_rsa.c
index 5fda1cb49..2dd5a02f4 100644
--- a/networking/tls_rsa.c
+++ b/networking/tls_rsa.c
@@ -15,7 +15,7 @@
15 pkcs1Pad(in, inlen, out, outlen, cryptType) 15 pkcs1Pad(in, inlen, out, outlen, cryptType)
16static //bbox 16static //bbox
17int32 pkcs1Pad(unsigned char *in, uint32 inlen, unsigned char *out, 17int32 pkcs1Pad(unsigned char *in, uint32 inlen, unsigned char *out,
18 uint32 outlen, int32 cryptType, void *userPtr) 18 uint32 outlen, int32 cryptType, void *userPtr)
19{ 19{
20 unsigned char *c; 20 unsigned char *c;
21 int32 randomLen; 21 int32 randomLen;
@@ -60,7 +60,7 @@ int32 pkcs1Pad(unsigned char *in, uint32 inlen, unsigned char *out,
60 psRsaCrypt( in, inlen, out, outlen, key, type) 60 psRsaCrypt( in, inlen, out, outlen, key, type)
61static //bbox 61static //bbox
62int32 psRsaCrypt(psPool_t *pool, const unsigned char *in, uint32 inlen, 62int32 psRsaCrypt(psPool_t *pool, const unsigned char *in, uint32 inlen,
63 unsigned char *out, uint32 *outlen, psRsaKey_t *key, int32 type, 63 unsigned char *out, uint32 *outlen, psRsaKey_t *key, int32 type,
64 void *data) 64 void *data)
65{ 65{
66 pstm_int tmp, tmpa, tmpb; 66 pstm_int tmp, tmpa, tmpb;
diff --git a/networking/tls_sp_c32.c b/networking/tls_sp_c32.c
new file mode 100644
index 000000000..5a84852a5
--- /dev/null
+++ b/networking/tls_sp_c32.c
@@ -0,0 +1,927 @@
1/*
2 * Copyright (C) 2021 Denys Vlasenko
3 *
4 * Licensed under GPLv2, see file LICENSE in this source tree.
5 */
6#include "tls.h"
7
8#define SP_DEBUG 0
9#define FIXED_SECRET 0
10#define FIXED_PEER_PUBKEY 0
11
12#if SP_DEBUG
13# define dbg(...) fprintf(stderr, __VA_ARGS__)
14static void dump_hex(const char *fmt, const void *vp, int len)
15{
16 char hexbuf[32 * 1024 + 4];
17 const uint8_t *p = vp;
18
19 bin2hex(hexbuf, (void*)p, len)[0] = '\0';
20 dbg(fmt, hexbuf);
21}
22#else
23# define dbg(...) ((void)0)
24# define dump_hex(...) ((void)0)
25#endif
26
27#undef DIGIT_BIT
28#define DIGIT_BIT 32
29typedef int32_t sp_digit;
30
31/* The code below is taken from parts of
32 * wolfssl-3.15.3/wolfcrypt/src/sp_c32.c
33 * and heavily modified.
34 * Header comment is kept intact:
35 */
36
37/* sp.c
38 *
39 * Copyright (C) 2006-2018 wolfSSL Inc.
40 *
41 * This file is part of wolfSSL.
42 *
43 * wolfSSL is free software; you can redistribute it and/or modify
44 * it under the terms of the GNU General Public License as published by
45 * the Free Software Foundation; either version 2 of the License, or
46 * (at your option) any later version.
47 *
48 * wolfSSL is distributed in the hope that it will be useful,
49 * but WITHOUT ANY WARRANTY; without even the implied warranty of
50 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51 * GNU General Public License for more details.
52 *
53 * You should have received a copy of the GNU General Public License
54 * along with this program; if not, write to the Free Software
55 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
56 */
57
58/* Implementation by Sean Parkinson. */
59
60typedef struct sp_point {
61 sp_digit x[2 * 10];
62 sp_digit y[2 * 10];
63 sp_digit z[2 * 10];
64 int infinity;
65} sp_point;
66
67/* The modulus (prime) of the curve P256. */
68static const sp_digit p256_mod[10] = {
69 0x3ffffff,0x3ffffff,0x3ffffff,0x003ffff,0x0000000,
70 0x0000000,0x0000000,0x0000400,0x3ff0000,0x03fffff,
71};
72
73#define p256_mp_mod ((sp_digit)0x000001)
74
75/* Write r as big endian to byte aray.
76 * Fixed length number of bytes written: 32
77 *
78 * r A single precision integer.
79 * a Byte array.
80 */
81static void sp_256_to_bin(sp_digit* r, uint8_t* a)
82{
83 int i, j, s = 0, b;
84
85 for (i = 0; i < 9; i++) {
86 r[i+1] += r[i] >> 26;
87 r[i] &= 0x3ffffff;
88 }
89 j = 256 / 8 - 1;
90 a[j] = 0;
91 for (i = 0; i < 10 && j >= 0; i++) {
92 b = 0;
93 a[j--] |= r[i] << s; b += 8 - s;
94 if (j < 0)
95 break;
96 while (b < 26) {
97 a[j--] = r[i] >> b; b += 8;
98 if (j < 0)
99 break;
100 }
101 s = 8 - (b - 26);
102 if (j >= 0)
103 a[j] = 0;
104 if (s != 0)
105 j++;
106 }
107}
108
109/* Read big endian unsigned byte aray into r.
110 *
111 * r A single precision integer.
112 * a Byte array.
113 * n Number of bytes in array to read.
114 */
115static void sp_256_from_bin(sp_digit* r, int max, const uint8_t* a, int n)
116{
117 int i, j = 0, s = 0;
118
119 r[0] = 0;
120 for (i = n-1; i >= 0; i--) {
121 r[j] |= ((sp_digit)a[i]) << s;
122 if (s >= 18) {
123 r[j] &= 0x3ffffff;
124 s = 26 - s;
125 if (j + 1 >= max)
126 break;
127 r[++j] = a[i] >> s;
128 s = 8 - s;
129 }
130 else
131 s += 8;
132 }
133
134 for (j++; j < max; j++)
135 r[j] = 0;
136}
137
138/* Convert a point of big-endian 32-byte x,y pair to type sp_point. */
139static void sp_256_point_from_bin2x32(sp_point* p, const uint8_t *bin2x32)
140{
141 memset(p, 0, sizeof(*p));
142 /*p->infinity = 0;*/
143 sp_256_from_bin(p->x, 2 * 10, bin2x32, 32);
144 sp_256_from_bin(p->y, 2 * 10, bin2x32 + 32, 32);
145 //static const uint8_t one[1] = { 1 };
146 //sp_256_from_bin(p->z, 2 * 10, one, 1);
147 p->z[0] = 1;
148}
149
150/* Compare a with b.
151 *
152 * return -ve, 0 or +ve if a is less than, equal to or greater than b
153 * respectively.
154 */
155static sp_digit sp_256_cmp_10(const sp_digit* a, const sp_digit* b)
156{
157 sp_digit r;
158 int i;
159 for (i = 9; i >= 0; i--) {
160 r = a[i] - b[i];
161 if (r != 0)
162 break;
163 }
164 return r;
165}
166
167/* Compare two numbers to determine if they are equal.
168 *
169 * return 1 when equal and 0 otherwise.
170 */
171static int sp_256_cmp_equal_10(const sp_digit* a, const sp_digit* b)
172{
173 return sp_256_cmp_10(a, b) == 0;
174}
175
176/* Normalize the values in each word to 26 bits. */
177static void sp_256_norm_10(sp_digit* a)
178{
179 int i;
180 for (i = 0; i < 9; i++) {
181 a[i+1] += a[i] >> 26;
182 a[i] &= 0x3ffffff;
183 }
184}
185
186/* Add b to a into r. (r = a + b) */
187static void sp_256_add_10(sp_digit* r, const sp_digit* a, const sp_digit* b)
188{
189 int i;
190 for (i = 0; i < 10; i++)
191 r[i] = a[i] + b[i];
192}
193
194/* Sub b from a into r. (r = a - b) */
195static void sp_256_sub_10(sp_digit* r, const sp_digit* a, const sp_digit* b)
196{
197 int i;
198 for (i = 0; i < 10; i++)
199 r[i] = a[i] - b[i];
200}
201
202/* Shift number left one bit. Bottom bit is lost. */
203static void sp_256_rshift1_10(sp_digit* r, sp_digit* a)
204{
205 int i;
206 for (i = 0; i < 9; i++)
207 r[i] = ((a[i] >> 1) | (a[i + 1] << 25)) & 0x3ffffff;
208 r[9] = a[9] >> 1;
209}
210
211/* Mul a by scalar b and add into r. (r += a * b) */
212static void sp_256_mul_add_10(sp_digit* r, const sp_digit* a, sp_digit b)
213{
214 int64_t tb = b;
215 int64_t t = 0;
216 int i;
217
218 for (i = 0; i < 10; i++) {
219 t += (tb * a[i]) + r[i];
220 r[i] = t & 0x3ffffff;
221 t >>= 26;
222 }
223 r[10] += t;
224}
225
226/* Multiply a and b into r. (r = a * b) */
227static void sp_256_mul_10(sp_digit* r, const sp_digit* a, const sp_digit* b)
228{
229 int i, j, k;
230 int64_t c;
231
232 c = ((int64_t)a[9]) * b[9];
233 r[19] = (sp_digit)(c >> 26);
234 c = (c & 0x3ffffff) << 26;
235 for (k = 17; k >= 0; k--) {
236 for (i = 9; i >= 0; i--) {
237 j = k - i;
238 if (j >= 10)
239 break;
240 if (j < 0)
241 continue;
242 c += ((int64_t)a[i]) * b[j];
243 }
244 r[k + 2] += c >> 52;
245 r[k + 1] = (c >> 26) & 0x3ffffff;
246 c = (c & 0x3ffffff) << 26;
247 }
248 r[0] = (sp_digit)(c >> 26);
249}
250
251/* Square a and put result in r. (r = a * a) */
252static void sp_256_sqr_10(sp_digit* r, const sp_digit* a)
253{
254 int i, j, k;
255 int64_t c;
256
257 c = ((int64_t)a[9]) * a[9];
258 r[19] = (sp_digit)(c >> 26);
259 c = (c & 0x3ffffff) << 26;
260 for (k = 17; k >= 0; k--) {
261 for (i = 9; i >= 0; i--) {
262 j = k - i;
263 if (j >= 10 || i <= j)
264 break;
265 if (j < 0)
266 continue;
267 c += ((int64_t)a[i]) * a[j] * 2;
268 }
269 if (i == j)
270 c += ((int64_t)a[i]) * a[i];
271 r[k + 2] += c >> 52;
272 r[k + 1] = (c >> 26) & 0x3ffffff;
273 c = (c & 0x3ffffff) << 26;
274 }
275 r[0] = (sp_digit)(c >> 26);
276}
277
278/* Divide the number by 2 mod the modulus (prime). (r = a / 2 % m) */
279static void sp_256_div2_10(sp_digit* r, const sp_digit* a, const sp_digit* m)
280{
281 if (a[0] & 1)
282 sp_256_add_10(r, a, m);
283 sp_256_norm_10(r);
284 sp_256_rshift1_10(r, r);
285}
286
287/* Add two Montgomery form numbers (r = a + b % m) */
288static void sp_256_mont_add_10(sp_digit* r, const sp_digit* a, const sp_digit* b,
289 const sp_digit* m)
290{
291 sp_256_add_10(r, a, b);
292 sp_256_norm_10(r);
293 if ((r[9] >> 22) > 0)
294 sp_256_sub_10(r, r, m);
295 sp_256_norm_10(r);
296}
297
298/* Subtract two Montgomery form numbers (r = a - b % m) */
299static void sp_256_mont_sub_10(sp_digit* r, const sp_digit* a, const sp_digit* b,
300 const sp_digit* m)
301{
302 sp_256_sub_10(r, a, b);
303 if (r[9] >> 22)
304 sp_256_add_10(r, r, m);
305 sp_256_norm_10(r);
306}
307
308/* Double a Montgomery form number (r = a + a % m) */
309static void sp_256_mont_dbl_10(sp_digit* r, const sp_digit* a, const sp_digit* m)
310{
311 sp_256_add_10(r, a, a);
312 sp_256_norm_10(r);
313 if ((r[9] >> 22) > 0)
314 sp_256_sub_10(r, r, m);
315 sp_256_norm_10(r);
316}
317
318/* Triple a Montgomery form number (r = a + a + a % m) */
319static void sp_256_mont_tpl_10(sp_digit* r, const sp_digit* a, const sp_digit* m)
320{
321 sp_256_add_10(r, a, a);
322 sp_256_norm_10(r);
323 if ((r[9] >> 22) > 0)
324 sp_256_sub_10(r, r, m);
325 sp_256_norm_10(r);
326 sp_256_add_10(r, r, a);
327 sp_256_norm_10(r);
328 if ((r[9] >> 22) > 0)
329 sp_256_sub_10(r, r, m);
330 sp_256_norm_10(r);
331}
332
333/* Shift the result in the high 256 bits down to the bottom. */
334static void sp_256_mont_shift_10(sp_digit* r, const sp_digit* a)
335{
336 int i;
337 sp_digit n, s;
338
339 s = a[10];
340 n = a[9] >> 22;
341 for (i = 0; i < 9; i++) {
342 n += (s & 0x3ffffff) << 4;
343 r[i] = n & 0x3ffffff;
344 n >>= 26;
345 s = a[11 + i] + (s >> 26);
346 }
347 n += s << 4;
348 r[9] = n;
349 memset(&r[10], 0, sizeof(*r) * 10);
350}
351
352/* Reduce the number back to 256 bits using Montgomery reduction.
353 *
354 * a A single precision number to reduce in place.
355 * m The single precision number representing the modulus.
356 * mp The digit representing the negative inverse of m mod 2^n.
357 */
358static void sp_256_mont_reduce_10(sp_digit* a, const sp_digit* m, sp_digit mp)
359{
360 int i;
361 sp_digit mu;
362
363 if (mp != 1) {
364 for (i = 0; i < 9; i++) {
365 mu = (a[i] * mp) & 0x3ffffff;
366 sp_256_mul_add_10(a+i, m, mu);
367 a[i+1] += a[i] >> 26;
368 }
369 mu = (a[i] * mp) & 0x3fffffl;
370 sp_256_mul_add_10(a+i, m, mu);
371 a[i+1] += a[i] >> 26;
372 a[i] &= 0x3ffffff;
373 }
374 else {
375 for (i = 0; i < 9; i++) {
376 mu = a[i] & 0x3ffffff;
377 sp_256_mul_add_10(a+i, p256_mod, mu);
378 a[i+1] += a[i] >> 26;
379 }
380 mu = a[i] & 0x3fffffl;
381 sp_256_mul_add_10(a+i, p256_mod, mu);
382 a[i+1] += a[i] >> 26;
383 a[i] &= 0x3ffffff;
384 }
385
386 sp_256_mont_shift_10(a, a);
387 if ((a[9] >> 22) > 0)
388 sp_256_sub_10(a, a, m);
389 sp_256_norm_10(a);
390}
391
392/* Multiply two Montogmery form numbers mod the modulus (prime).
393 * (r = a * b mod m)
394 *
395 * r Result of multiplication.
396 * a First number to multiply in Montogmery form.
397 * b Second number to multiply in Montogmery form.
398 * m Modulus (prime).
399 * mp Montogmery mulitplier.
400 */
401static void sp_256_mont_mul_10(sp_digit* r, const sp_digit* a, const sp_digit* b,
402 const sp_digit* m, sp_digit mp)
403{
404 sp_256_mul_10(r, a, b);
405 sp_256_mont_reduce_10(r, m, mp);
406}
407
408/* Square the Montgomery form number. (r = a * a mod m)
409 *
410 * r Result of squaring.
411 * a Number to square in Montogmery form.
412 * m Modulus (prime).
413 * mp Montogmery mulitplier.
414 */
415static void sp_256_mont_sqr_10(sp_digit* r, const sp_digit* a, const sp_digit* m,
416 sp_digit mp)
417{
418 sp_256_sqr_10(r, a);
419 sp_256_mont_reduce_10(r, m, mp);
420}
421
422/* Invert the number, in Montgomery form, modulo the modulus (prime) of the
423 * P256 curve. (r = 1 / a mod m)
424 *
425 * r Inverse result.
426 * a Number to invert.
427 */
428#if 0
429/* Mod-2 for the P256 curve. */
430static const uint32_t p256_mod_2[8] = {
431 0xfffffffd,0xffffffff,0xffffffff,0x00000000,
432 0x00000000,0x00000000,0x00000001,0xffffffff,
433};
434//Bit pattern:
435//2 2 2 2 2 2 2 1...1
436//5 5 4 3 2 1 0 9...0 9...1
437//543210987654321098765432109876543210987654321098765432109876543210...09876543210...09876543210
438//111111111111111111111111111111110000000000000000000000000000000100...00000111111...11111111101
439#endif
440static void sp_256_mont_inv_10(sp_digit* r, sp_digit* a)
441{
442 sp_digit t[2*10]; //can be just [10]?
443 int i;
444
445 memcpy(t, a, sizeof(sp_digit) * 10);
446 for (i = 254; i >= 0; i--) {
447 sp_256_mont_sqr_10(t, t, p256_mod, p256_mp_mod);
448 /*if (p256_mod_2[i / 32] & ((sp_digit)1 << (i % 32)))*/
449 if (i >= 224 || i == 192 || (i <= 95 && i != 1))
450 sp_256_mont_mul_10(t, t, a, p256_mod, p256_mp_mod);
451 }
452 memcpy(r, t, sizeof(sp_digit) * 10);
453}
454
455/* Multiply a number by Montogmery normalizer mod modulus (prime).
456 *
457 * r The resulting Montgomery form number.
458 * a The number to convert.
459 */
460static void sp_256_mod_mul_norm_10(sp_digit* r, const sp_digit* a)
461{
462 int64_t t[8];
463 int64_t o;
464 uint32_t a32;
465
466 /* 1 1 0 -1 -1 -1 -1 0 */
467 /* 0 1 1 0 -1 -1 -1 -1 */
468 /* 0 0 1 1 0 -1 -1 -1 */
469 /* -1 -1 0 2 2 1 0 -1 */
470 /* 0 -1 -1 0 2 2 1 0 */
471 /* 0 0 -1 -1 0 2 2 1 */
472 /* -1 -1 0 0 0 1 3 2 */
473 /* 1 0 -1 -1 -1 -1 0 3 */
474 // t[] should be calculated from "a" (converted from 26-bit to 32-bit vector a32[8])
475 // according to the above matrix:
476 //t[0] = 0 + a32[0] + a32[1] - a32[3] - a32[4] - a32[5] - a32[6] ;
477 //t[1] = 0 + a32[1] + a32[2] - a32[4] - a32[5] - a32[6] - a32[7] ;
478 //t[2] = 0 + a32[2] + a32[3] - a32[5] - a32[6] - a32[7] ;
479 //t[3] = 0 - a32[0] - a32[1] + 2*a32[3] + 2*a32[4] + a32[5] - a32[7] ;
480 //t[4] = 0 - a32[1] - a32[2] + 2*a32[4] + 2*a32[5] + a32[6] ;
481 //t[5] = 0 - a32[2] - a32[3] + 2*a32[5] + 2*a32[6] + a32[7] ;
482 //t[6] = 0 - a32[0] - a32[1] + a32[5] + 3*a32[6] + 2*a32[7];
483 //t[7] = 0 + a32[0] - a32[2] - a32[3] - a32[4] - a32[5] + 3*a32[7];
484 // We can do it "piecemeal" after each a32[i] is known, no need to store entire a32[8] vector:
485
486#define A32 (int64_t)a32
487 a32 = a[0] | (a[1] << 26);
488 t[0] = 0 + A32;
489 t[3] = 0 - A32;
490 t[6] = 0 - A32;
491 t[7] = 0 + A32;
492
493 a32 = (a[1] >> 6) | (a[2] << 20);
494 t[0] += A32 ;
495 t[1] = 0 + A32;
496 t[3] -= A32 ;
497 t[4] = 0 - A32;
498 t[6] -= A32 ;
499
500 a32 = (a[2] >> 12) | (a[3] << 14);
501 t[1] += A32 ;
502 t[2] = 0 + A32;
503 t[4] -= A32 ;
504 t[5] = 0 - A32;
505 t[7] -= A32 ;
506
507 a32 = (a[3] >> 18) | (a[4] << 8);
508 t[0] -= A32 ;
509 t[2] += A32 ;
510 t[3] += 2*A32;
511 t[5] -= A32 ;
512 t[7] -= A32 ;
513
514 a32 = (a[4] >> 24) | (a[5] << 2) | (a[6] << 28);
515 t[0] -= A32 ;
516 t[1] -= A32 ;
517 t[3] += 2*A32;
518 t[4] += 2*A32;
519 t[7] -= A32 ;
520
521 a32 = (a[6] >> 4) | (a[7] << 22);
522 t[0] -= A32 ;
523 t[1] -= A32 ;
524 t[2] -= A32 ;
525 t[3] += A32 ;
526 t[4] += 2*A32;
527 t[5] += 2*A32;
528 t[6] += A32 ;
529 t[7] -= A32 ;
530
531 a32 = (a[7] >> 10) | (a[8] << 16);
532 t[0] -= A32 ;
533 t[1] -= A32 ;
534 t[2] -= A32 ;
535 t[4] += A32 ;
536 t[5] += 2*A32;
537 t[6] += 3*A32;
538
539 a32 = (a[8] >> 16) | (a[9] << 10);
540 t[1] -= A32 ;
541 t[2] -= A32 ;
542 t[3] -= A32 ;
543 t[5] += A32 ;
544 t[6] += 2*A32;
545 t[7] += 3*A32;
546#undef A32
547
548 t[1] += t[0] >> 32; t[0] &= 0xffffffff;
549 t[2] += t[1] >> 32; t[1] &= 0xffffffff;
550 t[3] += t[2] >> 32; t[2] &= 0xffffffff;
551 t[4] += t[3] >> 32; t[3] &= 0xffffffff;
552 t[5] += t[4] >> 32; t[4] &= 0xffffffff;
553 t[6] += t[5] >> 32; t[5] &= 0xffffffff;
554 t[7] += t[6] >> 32; t[6] &= 0xffffffff;
555 o = t[7] >> 32; t[7] &= 0xffffffff;
556 t[0] += o;
557 t[3] -= o;
558 t[6] -= o;
559 t[7] += o;
560 t[1] += t[0] >> 32; //t[0] &= 0xffffffff;
561 t[2] += t[1] >> 32; //t[1] &= 0xffffffff;
562 t[3] += t[2] >> 32; //t[2] &= 0xffffffff;
563 t[4] += t[3] >> 32; //t[3] &= 0xffffffff;
564 t[5] += t[4] >> 32; //t[4] &= 0xffffffff;
565 t[6] += t[5] >> 32; //t[5] &= 0xffffffff;
566 t[7] += t[6] >> 32; //t[6] &= 0xffffffff; - (uint32_t)t[i] casts below accomplish masking
567
568 r[0] = 0x3ffffff & ((sp_digit)((uint32_t)t[0]));
569 r[1] = 0x3ffffff & ((sp_digit)((uint32_t)t[0] >> 26) | ((sp_digit)t[1] << 6));
570 r[2] = 0x3ffffff & ((sp_digit)((uint32_t)t[1] >> 20) | ((sp_digit)t[2] << 12));
571 r[3] = 0x3ffffff & ((sp_digit)((uint32_t)t[2] >> 14) | ((sp_digit)t[3] << 18));
572 r[4] = 0x3ffffff & ((sp_digit)((uint32_t)t[3] >> 8) | ((sp_digit)t[4] << 24));
573 r[5] = 0x3ffffff & ((sp_digit)((uint32_t)t[4] >> 2));
574 r[6] = 0x3ffffff & ((sp_digit)((uint32_t)t[4] >> 28) | ((sp_digit)t[5] << 4));
575 r[7] = 0x3ffffff & ((sp_digit)((uint32_t)t[5] >> 22) | ((sp_digit)t[6] << 10));
576 r[8] = 0x3ffffff & ((sp_digit)((uint32_t)t[6] >> 16) | ((sp_digit)t[7] << 16));
577 r[9] = ((sp_digit)((uint32_t)t[7] >> 10));
578}
579
580/* Map the Montgomery form projective co-ordinate point to an affine point.
581 *
582 * r Resulting affine co-ordinate point.
583 * p Montgomery form projective co-ordinate point.
584 */
585static void sp_256_map_10(sp_point* r, sp_point* p)
586{
587 sp_digit t1[2*10];
588 sp_digit t2[2*10];
589
590 sp_256_mont_inv_10(t1, p->z);
591
592 sp_256_mont_sqr_10(t2, t1, p256_mod, p256_mp_mod);
593 sp_256_mont_mul_10(t1, t2, t1, p256_mod, p256_mp_mod);
594
595 /* x /= z^2 */
596 sp_256_mont_mul_10(r->x, p->x, t2, p256_mod, p256_mp_mod);
597 memset(r->x + 10, 0, sizeof(r->x) / 2);
598 sp_256_mont_reduce_10(r->x, p256_mod, p256_mp_mod);
599 /* Reduce x to less than modulus */
600 if (sp_256_cmp_10(r->x, p256_mod) >= 0)
601 sp_256_sub_10(r->x, r->x, p256_mod);
602 sp_256_norm_10(r->x);
603
604 /* y /= z^3 */
605 sp_256_mont_mul_10(r->y, p->y, t1, p256_mod, p256_mp_mod);
606 memset(r->y + 10, 0, sizeof(r->y) / 2);
607 sp_256_mont_reduce_10(r->y, p256_mod, p256_mp_mod);
608 /* Reduce y to less than modulus */
609 if (sp_256_cmp_10(r->y, p256_mod) >= 0)
610 sp_256_sub_10(r->y, r->y, p256_mod);
611 sp_256_norm_10(r->y);
612
613 memset(r->z, 0, sizeof(r->z));
614 r->z[0] = 1;
615}
616
617/* Double the Montgomery form projective point p.
618 *
619 * r Result of doubling point.
620 * p Point to double.
621 */
622static void sp_256_proj_point_dbl_10(sp_point* r, sp_point* p)
623{
624 sp_point tp;
625 sp_digit t1[2*10];
626 sp_digit t2[2*10];
627
628 /* Put point to double into result */
629 if (r != p)
630 *r = *p; /* struct copy */
631
632 if (r->infinity) {
633 /* If infinity, don't double (work on dummy value) */
634 r = &tp;
635 }
636 /* T1 = Z * Z */
637 sp_256_mont_sqr_10(t1, r->z, p256_mod, p256_mp_mod);
638 /* Z = Y * Z */
639 sp_256_mont_mul_10(r->z, r->y, r->z, p256_mod, p256_mp_mod);
640 /* Z = 2Z */
641 sp_256_mont_dbl_10(r->z, r->z, p256_mod);
642 /* T2 = X - T1 */
643 sp_256_mont_sub_10(t2, r->x, t1, p256_mod);
644 /* T1 = X + T1 */
645 sp_256_mont_add_10(t1, r->x, t1, p256_mod);
646 /* T2 = T1 * T2 */
647 sp_256_mont_mul_10(t2, t1, t2, p256_mod, p256_mp_mod);
648 /* T1 = 3T2 */
649 sp_256_mont_tpl_10(t1, t2, p256_mod);
650 /* Y = 2Y */
651 sp_256_mont_dbl_10(r->y, r->y, p256_mod);
652 /* Y = Y * Y */
653 sp_256_mont_sqr_10(r->y, r->y, p256_mod, p256_mp_mod);
654 /* T2 = Y * Y */
655 sp_256_mont_sqr_10(t2, r->y, p256_mod, p256_mp_mod);
656 /* T2 = T2/2 */
657 sp_256_div2_10(t2, t2, p256_mod);
658 /* Y = Y * X */
659 sp_256_mont_mul_10(r->y, r->y, r->x, p256_mod, p256_mp_mod);
660 /* X = T1 * T1 */
661 sp_256_mont_mul_10(r->x, t1, t1, p256_mod, p256_mp_mod);
662 /* X = X - Y */
663 sp_256_mont_sub_10(r->x, r->x, r->y, p256_mod);
664 /* X = X - Y */
665 sp_256_mont_sub_10(r->x, r->x, r->y, p256_mod);
666 /* Y = Y - X */
667 sp_256_mont_sub_10(r->y, r->y, r->x, p256_mod);
668 /* Y = Y * T1 */
669 sp_256_mont_mul_10(r->y, r->y, t1, p256_mod, p256_mp_mod);
670 /* Y = Y - T2 */
671 sp_256_mont_sub_10(r->y, r->y, t2, p256_mod);
672}
673
674/* Add two Montgomery form projective points.
675 *
676 * r Result of addition.
677 * p Frist point to add.
678 * q Second point to add.
679 */
680static void sp_256_proj_point_add_10(sp_point* r, sp_point* p, sp_point* q)
681{
682 sp_digit t1[2*10];
683 sp_digit t2[2*10];
684 sp_digit t3[2*10];
685 sp_digit t4[2*10];
686 sp_digit t5[2*10];
687
688 /* Ensure only the first point is the same as the result. */
689 if (q == r) {
690 sp_point* a = p;
691 p = q;
692 q = a;
693 }
694
695 /* Check double */
696 sp_256_sub_10(t1, p256_mod, q->y);
697 sp_256_norm_10(t1);
698 if (sp_256_cmp_equal_10(p->x, q->x)
699 && sp_256_cmp_equal_10(p->z, q->z)
700 && (sp_256_cmp_equal_10(p->y, q->y) || sp_256_cmp_equal_10(p->y, t1))
701 ) {
702 sp_256_proj_point_dbl_10(r, p);
703 }
704 else {
705 sp_point tp;
706 sp_point *v;
707
708 v = r;
709 if (p->infinity | q->infinity) {
710 memset(&tp, 0, sizeof(tp));
711 v = &tp;
712 }
713
714 *r = p->infinity ? *q : *p; /* struct copy */
715
716 /* U1 = X1*Z2^2 */
717 sp_256_mont_sqr_10(t1, q->z, p256_mod, p256_mp_mod);
718 sp_256_mont_mul_10(t3, t1, q->z, p256_mod, p256_mp_mod);
719 sp_256_mont_mul_10(t1, t1, v->x, p256_mod, p256_mp_mod);
720 /* U2 = X2*Z1^2 */
721 sp_256_mont_sqr_10(t2, v->z, p256_mod, p256_mp_mod);
722 sp_256_mont_mul_10(t4, t2, v->z, p256_mod, p256_mp_mod);
723 sp_256_mont_mul_10(t2, t2, q->x, p256_mod, p256_mp_mod);
724 /* S1 = Y1*Z2^3 */
725 sp_256_mont_mul_10(t3, t3, v->y, p256_mod, p256_mp_mod);
726 /* S2 = Y2*Z1^3 */
727 sp_256_mont_mul_10(t4, t4, q->y, p256_mod, p256_mp_mod);
728 /* H = U2 - U1 */
729 sp_256_mont_sub_10(t2, t2, t1, p256_mod);
730 /* R = S2 - S1 */
731 sp_256_mont_sub_10(t4, t4, t3, p256_mod);
732 /* Z3 = H*Z1*Z2 */
733 sp_256_mont_mul_10(v->z, v->z, q->z, p256_mod, p256_mp_mod);
734 sp_256_mont_mul_10(v->z, v->z, t2, p256_mod, p256_mp_mod);
735 /* X3 = R^2 - H^3 - 2*U1*H^2 */
736 sp_256_mont_sqr_10(v->x, t4, p256_mod, p256_mp_mod);
737 sp_256_mont_sqr_10(t5, t2, p256_mod, p256_mp_mod);
738 sp_256_mont_mul_10(v->y, t1, t5, p256_mod, p256_mp_mod);
739 sp_256_mont_mul_10(t5, t5, t2, p256_mod, p256_mp_mod);
740 sp_256_mont_sub_10(v->x, v->x, t5, p256_mod);
741 sp_256_mont_dbl_10(t1, v->y, p256_mod);
742 sp_256_mont_sub_10(v->x, v->x, t1, p256_mod);
743 /* Y3 = R*(U1*H^2 - X3) - S1*H^3 */
744 sp_256_mont_sub_10(v->y, v->y, v->x, p256_mod);
745 sp_256_mont_mul_10(v->y, v->y, t4, p256_mod, p256_mp_mod);
746 sp_256_mont_mul_10(t5, t5, t3, p256_mod, p256_mp_mod);
747 sp_256_mont_sub_10(v->y, v->y, t5, p256_mod);
748 }
749}
750
751/* Multiply the point by the scalar and return the result.
752 * If map is true then convert result to affine co-ordinates.
753 *
754 * r Resulting point.
755 * g Point to multiply.
756 * k Scalar to multiply by.
757 * map Indicates whether to convert result to affine.
758 */
759static void sp_256_ecc_mulmod_10(sp_point* r, const sp_point* g, const sp_digit* k /*, int map*/)
760{
761 enum { map = 1 }; /* we always convert result to affine coordinates */
762 sp_point t[3];
763 sp_digit n;
764 int i;
765 int c, y;
766
767 memset(t, 0, sizeof(t));
768
769 /* t[0] = {0, 0, 1} * norm */
770 t[0].infinity = 1;
771 /* t[1] = {g->x, g->y, g->z} * norm */
772 sp_256_mod_mul_norm_10(t[1].x, g->x);
773 sp_256_mod_mul_norm_10(t[1].y, g->y);
774 sp_256_mod_mul_norm_10(t[1].z, g->z);
775
776 i = 9;
777 c = 22;
778 n = k[i--] << (26 - c);
779 for (; ; c--) {
780 if (c == 0) {
781 if (i == -1)
782 break;
783
784 n = k[i--];
785 c = 26;
786 }
787
788 y = (n >> 25) & 1;
789 n <<= 1;
790
791 sp_256_proj_point_add_10(&t[y^1], &t[0], &t[1]);
792 memcpy(&t[2], &t[y], sizeof(sp_point));
793 sp_256_proj_point_dbl_10(&t[2], &t[2]);
794 memcpy(&t[y], &t[2], sizeof(sp_point));
795 }
796
797 if (map)
798 sp_256_map_10(r, &t[0]);
799 else
800 memcpy(r, &t[0], sizeof(sp_point));
801
802 memset(t, 0, sizeof(t)); //paranoia
803}
804
805/* Multiply the base point of P256 by the scalar and return the result.
806 * If map is true then convert result to affine co-ordinates.
807 *
808 * r Resulting point.
809 * k Scalar to multiply by.
810 * map Indicates whether to convert result to affine.
811 */
812static void sp_256_ecc_mulmod_base_10(sp_point* r, sp_digit* k /*, int map*/)
813{
814 /* Since this function is called only once, save space:
815 * don't have "static const sp_point p256_base = {...}",
816 * it would have more zeros than data.
817 */
818 static const uint8_t p256_base_bin[] = {
819 /* x (big-endian) */
820 0x6b,0x17,0xd1,0xf2,0xe1,0x2c,0x42,0x47,0xf8,0xbc,0xe6,0xe5,0x63,0xa4,0x40,0xf2,0x77,0x03,0x7d,0x81,0x2d,0xeb,0x33,0xa0,0xf4,0xa1,0x39,0x45,0xd8,0x98,0xc2,0x96,
821 /* y */
822 0x4f,0xe3,0x42,0xe2,0xfe,0x1a,0x7f,0x9b,0x8e,0xe7,0xeb,0x4a,0x7c,0x0f,0x9e,0x16,0x2b,0xce,0x33,0x57,0x6b,0x31,0x5e,0xce,0xcb,0xb6,0x40,0x68,0x37,0xbf,0x51,0xf5,
823 /* z will be set to 1, infinity flag to "false" */
824 };
825 sp_point p256_base;
826
827 sp_256_point_from_bin2x32(&p256_base, p256_base_bin);
828
829 sp_256_ecc_mulmod_10(r, &p256_base, k /*, map*/);
830}
831
832/* Multiply the point by the scalar and serialize the X ordinate.
833 * The number is 0 padded to maximum size on output.
834 *
835 * priv Scalar to multiply the point by.
836 * pub2x32 Point to multiply.
837 * out32 Buffer to hold X ordinate.
838 */
839static void sp_ecc_secret_gen_256(const sp_digit priv[10], const uint8_t *pub2x32, uint8_t* out32)
840{
841 sp_point point[1];
842
843#if FIXED_PEER_PUBKEY
844 memset((void*)pub2x32, 0x55, 64);
845#endif
846 dump_hex("peerkey %s\n", pub2x32, 32); /* in TLS, this is peer's public key */
847 dump_hex(" %s\n", pub2x32 + 32, 32);
848
849 sp_256_point_from_bin2x32(point, pub2x32);
850 dump_hex("point->x %s\n", point->x, sizeof(point->x));
851 dump_hex("point->y %s\n", point->y, sizeof(point->y));
852
853 sp_256_ecc_mulmod_10(point, point, priv);
854
855 sp_256_to_bin(point->x, out32);
856 dump_hex("out32: %s\n", out32, 32);
857}
858
859/* Generates a scalar that is in the range 1..order-1. */
860#define SIMPLIFY 1
861/* Add 1 to a. (a = a + 1) */
862static void sp_256_add_one_10(sp_digit* a)
863{
864 a[0]++;
865 sp_256_norm_10(a);
866}
867static void sp_256_ecc_gen_k_10(sp_digit k[10])
868{
869#if !SIMPLIFY
870 /* The order of the curve P256 minus 2. */
871 static const sp_digit p256_order2[10] = {
872 0x063254f,0x272b0bf,0x1e84f3b,0x2b69c5e,0x3bce6fa,
873 0x3ffffff,0x3ffffff,0x00003ff,0x3ff0000,0x03fffff,
874 };
875#endif
876 uint8_t buf[32];
877
878 for (;;) {
879 tls_get_random(buf, sizeof(buf));
880#if FIXED_SECRET
881 memset(buf, 0x77, sizeof(buf));
882#endif
883 sp_256_from_bin(k, 10, buf, sizeof(buf));
884#if !SIMPLIFY
885 if (sp_256_cmp_10(k, p256_order2) < 0)
886 break;
887#else
888 /* non-loopy version (and not needing p256_order2[]):
889 * if most-significant word seems that k can be larger
890 * than p256_order2, fix it up:
891 */
892 if (k[9] >= 0x03fffff)
893 k[9] = 0x03ffffe;
894 break;
895#endif
896 }
897 sp_256_add_one_10(k);
898#undef SIMPLIFY
899}
900
901/* Makes a random EC key pair. */
902static void sp_ecc_make_key_256(sp_digit privkey[10], uint8_t *pubkey)
903{
904 sp_point point[1];
905
906 sp_256_ecc_gen_k_10(privkey);
907 sp_256_ecc_mulmod_base_10(point, privkey);
908 sp_256_to_bin(point->x, pubkey);
909 sp_256_to_bin(point->y, pubkey + 32);
910
911 memset(point, 0, sizeof(point)); //paranoia
912}
913
914void FAST_FUNC curve_P256_compute_pubkey_and_premaster(
915 uint8_t *pubkey2x32, uint8_t *premaster32,
916 const uint8_t *peerkey2x32)
917{
918 sp_digit privkey[10];
919
920 sp_ecc_make_key_256(privkey, pubkey2x32);
921 dump_hex("pubkey: %s\n", pubkey2x32, 32);
922 dump_hex(" %s\n", pubkey2x32 + 32, 32);
923
924 /* Combine our privkey and peer's public key to generate premaster */
925 sp_ecc_secret_gen_256(privkey, /*x,y:*/peerkey2x32, premaster32);
926 dump_hex("premaster: %s\n", premaster32, 32);
927}
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index 4bc719001..f2d6907ad 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -273,17 +273,27 @@ uint8_t* FAST_FUNC udhcp_scan_options(struct dhcp_packet *packet, struct dhcp_sc
273 break; 273 break;
274 } 274 }
275 275
276 if (scan_state->rem <= OPT_LEN) 276 if (scan_state->rem <= OPT_LEN) /* [len] byte exists? */
277 goto complain; /* complain and return NULL */ 277 goto complain; /* no, complain and return NULL */
278 len = 2 + scan_state->optionptr[OPT_LEN]; 278 len = scan_state->optionptr[OPT_LEN];
279 /* Skip options with zero length.
280 * Users report that DHCP server on a TrendNet router (unknown model)
281 * provides a zero-length option 12 (Host Name)
282 * (this violates RFC 2132 section 3.14).
283 */
284 if (len == 0) {
285 scan_state->rem -= 2;
286 scan_state->optionptr += 2;
287 continue;
288 }
289 len += 2;
279 scan_state->rem -= len; 290 scan_state->rem -= len;
280 /* So far no valid option with length 0 known. */ 291 if (scan_state->rem < 0) /* option is longer than options field? */
281 if (scan_state->rem < 0 || scan_state->optionptr[OPT_LEN] == 0) 292 goto complain; /* yes, complain and return NULL */
282 goto complain; /* complain and return NULL */
283 293
284 if (scan_state->optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) { 294 if (scan_state->optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) {
285 if (len >= 3) 295 /* len is known to be >= 3 now, [data] byte exists */
286 scan_state->overload |= scan_state->optionptr[OPT_DATA]; 296 scan_state->overload |= scan_state->optionptr[OPT_DATA];
287 } else { 297 } else {
288 uint8_t *return_ptr = scan_state->optionptr; 298 uint8_t *return_ptr = scan_state->optionptr;
289 scan_state->optionptr += len; 299 scan_state->optionptr += len;
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 76b087b92..0a5cae310 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -1040,7 +1040,6 @@ static int d6_raw_socket(int ifindex)
1040 log2("opening raw socket on ifindex %d", ifindex); 1040 log2("opening raw socket on ifindex %d", ifindex);
1041 1041
1042 fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)); 1042 fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6));
1043 log3("got raw socket fd %d", fd);
1044 1043
1045 memset(&sock, 0, sizeof(sock)); /* let's be deterministic */ 1044 memset(&sock, 0, sizeof(sock)); /* let's be deterministic */
1046 sock.sll_family = AF_PACKET; 1045 sock.sll_family = AF_PACKET;
@@ -1087,29 +1086,6 @@ static void change_listen_mode(int new_mode)
1087 /* else LISTEN_NONE: client_data.sockfd stays closed */ 1086 /* else LISTEN_NONE: client_data.sockfd stays closed */
1088} 1087}
1089 1088
1090/* Called only on SIGUSR1 */
1091static void perform_renew(void)
1092{
1093 bb_simple_info_msg("performing DHCP renew");
1094 switch (client_data.state) {
1095 case BOUND:
1096 change_listen_mode(LISTEN_KERNEL);
1097 case RENEWING:
1098 case REBINDING:
1099 client_data.state = RENEW_REQUESTED;
1100 break;
1101 case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
1102 d6_run_script_no_option("deconfig");
1103 case REQUESTING:
1104 case RELEASED:
1105 change_listen_mode(LISTEN_RAW);
1106 client_data.state = INIT_SELECTING;
1107 break;
1108 case INIT_SELECTING:
1109 break;
1110 }
1111}
1112
1113static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6) 1089static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
1114{ 1090{
1115 /* send release packet */ 1091 /* send release packet */
@@ -1220,7 +1196,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1220 uint32_t xid = 0; 1196 uint32_t xid = 0;
1221 int packet_num; 1197 int packet_num;
1222 int timeout; /* must be signed */ 1198 int timeout; /* must be signed */
1223 unsigned already_waited_sec; 1199 int lease_remaining; /* must be signed */
1224 unsigned opt; 1200 unsigned opt;
1225 int retval; 1201 int retval;
1226 1202
@@ -1348,19 +1324,16 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1348 change_listen_mode(LISTEN_RAW); 1324 change_listen_mode(LISTEN_RAW);
1349 packet_num = 0; 1325 packet_num = 0;
1350 timeout = 0; 1326 timeout = 0;
1351 already_waited_sec = 0; 1327 lease_remaining = 0;
1352 1328
1353 /* Main event loop. select() waits on signal pipe and possibly 1329 /* Main event loop. select() waits on signal pipe and possibly
1354 * on sockfd. 1330 * on sockfd.
1355 * "continue" statements in code below jump to the top of the loop. 1331 * "continue" statements in code below jump to the top of the loop.
1356 */ 1332 */
1357 for (;;) { 1333 for (;;) {
1358 int tv;
1359 struct pollfd pfds[2]; 1334 struct pollfd pfds[2];
1360 struct d6_packet packet; 1335 struct d6_packet packet;
1361 uint8_t *packet_end; 1336 uint8_t *packet_end;
1362 /* silence "uninitialized!" warning */
1363 unsigned timestamp_before_wait = timestamp_before_wait;
1364 1337
1365 //bb_error_msg("sockfd:%d, listen_mode:%d", client_data.sockfd, client_data.listen_mode); 1338 //bb_error_msg("sockfd:%d, listen_mode:%d", client_data.sockfd, client_data.listen_mode);
1366 1339
@@ -1373,17 +1346,24 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1373 1346
1374 udhcp_sp_fd_set(pfds, client_data.sockfd); 1347 udhcp_sp_fd_set(pfds, client_data.sockfd);
1375 1348
1376 tv = timeout - already_waited_sec;
1377 retval = 0; 1349 retval = 0;
1378 /* If we already timed out, fall through with retval = 0, else... */ 1350 /* If we already timed out, fall through with retval = 0, else... */
1379 if (tv > 0) { 1351 if (timeout > 0) {
1380 log1("waiting %u seconds", tv); 1352 unsigned diff;
1381 timestamp_before_wait = (unsigned)monotonic_sec(); 1353
1382 retval = poll(pfds, 2, tv < INT_MAX/1000 ? tv * 1000 : INT_MAX); 1354 if (timeout > INT_MAX/1000)
1355 timeout = INT_MAX/1000;
1356 log1("waiting %u seconds", timeout);
1357 diff = (unsigned)monotonic_sec();
1358 retval = poll(pfds, 2, timeout * 1000);
1383 if (retval < 0) { 1359 if (retval < 0) {
1384 /* EINTR? A signal was caught, don't panic */ 1360 /* EINTR? A signal was caught, don't panic */
1385 if (errno == EINTR) { 1361 if (errno == EINTR) {
1386 already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait; 1362 diff = (unsigned)monotonic_sec() - diff;
1363 lease_remaining -= diff;
1364 if (lease_remaining < 0)
1365 lease_remaining = 0;
1366 timeout -= diff;
1387 continue; 1367 continue;
1388 } 1368 }
1389 /* Else: an error occured, panic! */ 1369 /* Else: an error occured, panic! */
@@ -1410,9 +1390,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1410 1390
1411 memcpy(clientid_mac_ptr, client_data.client_mac, 6); 1391 memcpy(clientid_mac_ptr, client_data.client_mac, 6);
1412 1392
1413 /* We will restart the wait in any case */
1414 already_waited_sec = 0;
1415
1416 switch (client_data.state) { 1393 switch (client_data.state) {
1417 case INIT_SELECTING: 1394 case INIT_SELECTING:
1418 if (!discover_retries || packet_num < discover_retries) { 1395 if (!discover_retries || packet_num < discover_retries) {
@@ -1477,7 +1454,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1477 case RENEW_REQUESTED: /* manual (SIGUSR1) renew */ 1454 case RENEW_REQUESTED: /* manual (SIGUSR1) renew */
1478 case_RENEW_REQUESTED: 1455 case_RENEW_REQUESTED:
1479 case RENEWING: 1456 case RENEWING:
1480 if (timeout >= 60) { 1457 if (packet_num < 3) {
1458 packet_num++;
1481 /* send an unicast renew request */ 1459 /* send an unicast renew request */
1482 /* Sometimes observed to fail (EADDRNOTAVAIL) to bind 1460 /* Sometimes observed to fail (EADDRNOTAVAIL) to bind
1483 * a new UDP socket for sending inside send_renew. 1461 * a new UDP socket for sending inside send_renew.
@@ -1491,7 +1469,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1491 send_d6_info_request(xid); 1469 send_d6_info_request(xid);
1492 else 1470 else
1493 send_d6_renew(xid, &srv6_buf, requested_ipv6); 1471 send_d6_renew(xid, &srv6_buf, requested_ipv6);
1494 timeout >>= 1; 1472 timeout = discover_timeout;
1473 /* ^^^ used to be = lease_remaining / 2 - WAY too long */
1495 continue; 1474 continue;
1496 } 1475 }
1497 /* Timed out, enter rebinding state */ 1476 /* Timed out, enter rebinding state */
@@ -1503,12 +1482,12 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1503 change_listen_mode(LISTEN_RAW); 1482 change_listen_mode(LISTEN_RAW);
1504 /* Lease is *really* about to run out, 1483 /* Lease is *really* about to run out,
1505 * try to find DHCP server using broadcast */ 1484 * try to find DHCP server using broadcast */
1506 if (timeout > 0) { 1485 if (lease_remaining > 0) {
1507 if (opt & OPT_l) 1486 if (opt & OPT_l)
1508 send_d6_info_request(xid); 1487 send_d6_info_request(xid);
1509 else /* send a broadcast renew request */ 1488 else /* send a broadcast renew request */
1510 send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6); 1489 send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6);
1511 timeout >>= 1; 1490 timeout = discover_timeout;
1512 continue; 1491 continue;
1513 } 1492 }
1514 /* Timed out, enter init state */ 1493 /* Timed out, enter init state */
@@ -1516,7 +1495,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1516 d6_run_script_no_option("deconfig"); 1495 d6_run_script_no_option("deconfig");
1517 client_data.state = INIT_SELECTING; 1496 client_data.state = INIT_SELECTING;
1518 client_data.first_secs = 0; /* make secs field count from 0 */ 1497 client_data.first_secs = 0; /* make secs field count from 0 */
1519 /*timeout = 0; - already is */ 1498 timeout = 0;
1520 packet_num = 0; 1499 packet_num = 0;
1521 continue; 1500 continue;
1522 /* case RELEASED: */ 1501 /* case RELEASED: */
@@ -1532,22 +1511,26 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1532 switch (udhcp_sp_read()) { 1511 switch (udhcp_sp_read()) {
1533 case SIGUSR1: 1512 case SIGUSR1:
1534 client_data.first_secs = 0; /* make secs field count from 0 */ 1513 client_data.first_secs = 0; /* make secs field count from 0 */
1535 already_waited_sec = 0; 1514 bb_simple_info_msg("performing DHCP renew");
1536 perform_renew(); 1515
1537 if (client_data.state == RENEW_REQUESTED) { 1516 switch (client_data.state) {
1538 /* We might be either on the same network 1517 /* Try to renew/rebind */
1539 * (in which case renew might work), 1518 case BOUND:
1540 * or we might be on a completely different one 1519 case RENEWING:
1541 * (in which case renew won't ever succeed). 1520 case REBINDING:
1542 * For the second case, must make sure timeout 1521 change_listen_mode(LISTEN_KERNEL);
1543 * is not too big, or else we can send 1522 client_data.state = RENEW_REQUESTED;
1544 * futile renew requests for hours.
1545 */
1546 if (timeout > 60)
1547 timeout = 60;
1548 goto case_RENEW_REQUESTED; 1523 goto case_RENEW_REQUESTED;
1549 } 1524
1550 /* Start things over */ 1525 /* Start things over */
1526 case RENEW_REQUESTED: /* two or more SIGUSR1 received */
1527 d6_run_script_no_option("deconfig");
1528 /* case REQUESTING: break; */
1529 /* case RELEASED: break; */
1530 /* case INIT_SELECTING: break; */
1531 }
1532 change_listen_mode(LISTEN_RAW);
1533 client_data.state = INIT_SELECTING;
1551 packet_num = 0; 1534 packet_num = 0;
1552 /* Kill any timeouts, user wants this to hurry along */ 1535 /* Kill any timeouts, user wants this to hurry along */
1553 timeout = 0; 1536 timeout = 0;
@@ -1579,10 +1562,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1579 sleep(discover_timeout); /* 3 seconds by default */ 1562 sleep(discover_timeout); /* 3 seconds by default */
1580 change_listen_mode(client_data.listen_mode); /* just close and reopen */ 1563 change_listen_mode(client_data.listen_mode); /* just close and reopen */
1581 } 1564 }
1582 /* If this packet will turn out to be unrelated/bogus,
1583 * we will go back and wait for next one.
1584 * Be sure timeout is properly decreased. */
1585 already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
1586 if (len < 0) 1565 if (len < 0)
1587 continue; 1566 continue;
1588 packet_end = (uint8_t*)&packet + len; 1567 packet_end = (uint8_t*)&packet + len;
@@ -1609,6 +1588,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1609 case RENEW_REQUESTED: 1588 case RENEW_REQUESTED:
1610 case REBINDING: 1589 case REBINDING:
1611 if (packet.d6_msg_type == D6_MSG_REPLY) { 1590 if (packet.d6_msg_type == D6_MSG_REPLY) {
1591 unsigned start;
1612 uint32_t lease_seconds; 1592 uint32_t lease_seconds;
1613 struct d6_option *option; 1593 struct d6_option *option;
1614 unsigned address_timeout; 1594 unsigned address_timeout;
@@ -1631,7 +1611,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1631 requested_ipv6 = NULL; 1611 requested_ipv6 = NULL;
1632 timeout = 0; 1612 timeout = 0;
1633 packet_num = 0; 1613 packet_num = 0;
1634 already_waited_sec = 0;
1635 continue; 1614 continue;
1636 } 1615 }
1637 option = d6_copy_option(packet.d6_options, packet_end, D6_OPT_SERVERID); 1616 option = d6_copy_option(packet.d6_options, packet_end, D6_OPT_SERVERID);
@@ -1650,7 +1629,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1650 client_data.state = REQUESTING; 1629 client_data.state = REQUESTING;
1651 timeout = 0; 1630 timeout = 0;
1652 packet_num = 0; 1631 packet_num = 0;
1653 already_waited_sec = 0;
1654 continue; 1632 continue;
1655 } 1633 }
1656 /* It's a D6_MSG_REPLY */ 1634 /* It's a D6_MSG_REPLY */
@@ -1814,21 +1792,26 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1814 address_timeout = prefix_timeout; 1792 address_timeout = prefix_timeout;
1815 if (!prefix_timeout) 1793 if (!prefix_timeout)
1816 prefix_timeout = address_timeout; 1794 prefix_timeout = address_timeout;
1817 /* note: "int timeout" will not overflow even with 0xffffffff inputs here: */ 1795 lease_remaining = (prefix_timeout < address_timeout ? prefix_timeout : address_timeout);
1818 timeout = (prefix_timeout < address_timeout ? prefix_timeout : address_timeout) / 2; 1796 if (lease_remaining < 0) /* signed overflow? */
1797 lease_remaining = INT_MAX;
1819 if (opt & OPT_l) { 1798 if (opt & OPT_l) {
1820 /* TODO: request OPTION_INFORMATION_REFRESH_TIME (32) 1799 /* TODO: request OPTION_INFORMATION_REFRESH_TIME (32)
1821 * and use its value instead of the default 1 day. 1800 * and use its value instead of the default 1 day.
1822 */ 1801 */
1823 timeout = 24 * 60 * 60; 1802 lease_remaining = 24 * 60 * 60;
1824 } 1803 }
1825 /* paranoia: must not be too small */ 1804 /* paranoia: must not be too small */
1826 /* timeout > 60 - ensures at least one unicast renew attempt */ 1805 if (lease_remaining < 30)
1827 if (timeout < 61) 1806 lease_remaining = 30;
1828 timeout = 61; 1807
1829 /* enter bound state */ 1808 /* enter bound state */
1809 start = monotonic_sec();
1830 d6_run_script(packet.d6_options, packet_end, 1810 d6_run_script(packet.d6_options, packet_end,
1831 (client_data.state == REQUESTING ? "bound" : "renew")); 1811 (client_data.state == REQUESTING ? "bound" : "renew"));
1812 timeout = (unsigned)lease_remaining / 2;
1813 timeout -= (unsigned)monotonic_sec() - start;
1814 packet_num = 0;
1832 1815
1833 client_data.state = BOUND; 1816 client_data.state = BOUND;
1834 change_listen_mode(LISTEN_NONE); 1817 change_listen_mode(LISTEN_NONE);
@@ -1844,7 +1827,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1844 opt = ((opt & ~OPT_b) | OPT_f); 1827 opt = ((opt & ~OPT_b) | OPT_f);
1845 } 1828 }
1846#endif 1829#endif
1847 already_waited_sec = 0;
1848 continue; /* back to main loop */ 1830 continue; /* back to main loop */
1849 } 1831 }
1850 continue; 1832 continue;
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index bbcbd1fca..6666cbce6 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -1027,7 +1027,6 @@ static int udhcp_raw_socket(int ifindex)
1027 * SOCK_DGRAM: remove link-layer headers on input (SOCK_RAW keeps them) 1027 * SOCK_DGRAM: remove link-layer headers on input (SOCK_RAW keeps them)
1028 * ETH_P_IP: want to receive only packets with IPv4 eth type 1028 * ETH_P_IP: want to receive only packets with IPv4 eth type
1029 */ 1029 */
1030 log3("got raw socket fd %d", fd);
1031 1030
1032 memset(&sock, 0, sizeof(sock)); /* let's be deterministic */ 1031 memset(&sock, 0, sizeof(sock)); /* let's be deterministic */
1033 sock.sll_family = AF_PACKET; 1032 sock.sll_family = AF_PACKET;
@@ -1122,29 +1121,6 @@ static void change_listen_mode(int new_mode)
1122 /* else LISTEN_NONE: client_data.sockfd stays closed */ 1121 /* else LISTEN_NONE: client_data.sockfd stays closed */
1123} 1122}
1124 1123
1125/* Called only on SIGUSR1 */
1126static void perform_renew(void)
1127{
1128 bb_simple_info_msg("performing DHCP renew");
1129 switch (client_data.state) {
1130 case BOUND:
1131 change_listen_mode(LISTEN_KERNEL);
1132 case RENEWING:
1133 case REBINDING:
1134 client_data.state = RENEW_REQUESTED;
1135 break;
1136 case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
1137 udhcp_run_script(NULL, "deconfig");
1138 case REQUESTING:
1139 case RELEASED:
1140 change_listen_mode(LISTEN_RAW);
1141 client_data.state = INIT_SELECTING;
1142 break;
1143 case INIT_SELECTING:
1144 break;
1145 }
1146}
1147
1148static void perform_release(uint32_t server_addr, uint32_t requested_ip) 1124static void perform_release(uint32_t server_addr, uint32_t requested_ip)
1149{ 1125{
1150 char buffer[sizeof("255.255.255.255")]; 1126 char buffer[sizeof("255.255.255.255")];
@@ -1247,7 +1223,6 @@ static void client_background(void)
1247//usage: "\n USR1 Renew lease" 1223//usage: "\n USR1 Renew lease"
1248//usage: "\n USR2 Release lease" 1224//usage: "\n USR2 Release lease"
1249 1225
1250
1251int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1226int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1252int udhcpc_main(int argc UNUSED_PARAM, char **argv) 1227int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1253{ 1228{
@@ -1266,7 +1241,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1266 uint32_t xid = xid; /* for compiler */ 1241 uint32_t xid = xid; /* for compiler */
1267 int packet_num; 1242 int packet_num;
1268 int timeout; /* must be signed */ 1243 int timeout; /* must be signed */
1269 unsigned already_waited_sec; 1244 int lease_remaining; /* must be signed */
1270 unsigned opt; 1245 unsigned opt;
1271 IF_FEATURE_UDHCPC_ARPING(unsigned arpping_ms;) 1246 IF_FEATURE_UDHCPC_ARPING(unsigned arpping_ms;)
1272 int retval; 1247 int retval;
@@ -1411,18 +1386,15 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1411 change_listen_mode(LISTEN_RAW); 1386 change_listen_mode(LISTEN_RAW);
1412 packet_num = 0; 1387 packet_num = 0;
1413 timeout = 0; 1388 timeout = 0;
1414 already_waited_sec = 0; 1389 lease_remaining = 0;
1415 1390
1416 /* Main event loop. select() waits on signal pipe and possibly 1391 /* Main event loop. select() waits on signal pipe and possibly
1417 * on sockfd. 1392 * on sockfd.
1418 * "continue" statements in code below jump to the top of the loop. 1393 * "continue" statements in code below jump to the top of the loop.
1419 */ 1394 */
1420 for (;;) { 1395 for (;;) {
1421 int tv;
1422 struct pollfd pfds[2]; 1396 struct pollfd pfds[2];
1423 struct dhcp_packet packet; 1397 struct dhcp_packet packet;
1424 /* silence "uninitialized!" warning */
1425 unsigned timestamp_before_wait = timestamp_before_wait;
1426 1398
1427 //bb_error_msg("sockfd:%d, listen_mode:%d", client_data.sockfd, client_data.listen_mode); 1399 //bb_error_msg("sockfd:%d, listen_mode:%d", client_data.sockfd, client_data.listen_mode);
1428 1400
@@ -1435,17 +1407,24 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1435 1407
1436 udhcp_sp_fd_set(pfds, client_data.sockfd); 1408 udhcp_sp_fd_set(pfds, client_data.sockfd);
1437 1409
1438 tv = timeout - already_waited_sec;
1439 retval = 0; 1410 retval = 0;
1440 /* If we already timed out, fall through with retval = 0, else... */ 1411 /* If we already timed out, fall through with retval = 0, else... */
1441 if (tv > 0) { 1412 if (timeout > 0) {
1442 log1("waiting %u seconds", tv); 1413 unsigned diff;
1443 timestamp_before_wait = (unsigned)monotonic_sec(); 1414
1444 retval = poll(pfds, 2, tv < INT_MAX/1000 ? tv * 1000 : INT_MAX); 1415 if (timeout > INT_MAX/1000)
1416 timeout = INT_MAX/1000;
1417 log1("waiting %u seconds", timeout);
1418 diff = (unsigned)monotonic_sec();
1419 retval = poll(pfds, 2, timeout * 1000);
1445 if (retval < 0) { 1420 if (retval < 0) {
1446 /* EINTR? A signal was caught, don't panic */ 1421 /* EINTR? A signal was caught, don't panic */
1447 if (errno == EINTR) { 1422 if (errno == EINTR) {
1448 already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait; 1423 diff = (unsigned)monotonic_sec() - diff;
1424 lease_remaining -= diff;
1425 if (lease_remaining < 0)
1426 lease_remaining = 0;
1427 timeout -= diff;
1449 continue; 1428 continue;
1450 } 1429 }
1451 /* Else: an error occurred, panic! */ 1430 /* Else: an error occurred, panic! */
@@ -1472,9 +1451,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1472 if (clientid_mac_ptr) 1451 if (clientid_mac_ptr)
1473 memcpy(clientid_mac_ptr, client_data.client_mac, 6); 1452 memcpy(clientid_mac_ptr, client_data.client_mac, 6);
1474 1453
1475 /* We will restart the wait in any case */
1476 already_waited_sec = 0;
1477
1478 switch (client_data.state) { 1454 switch (client_data.state) {
1479 case INIT_SELECTING: 1455 case INIT_SELECTING:
1480 if (!discover_retries || packet_num < discover_retries) { 1456 if (!discover_retries || packet_num < discover_retries) {
@@ -1536,7 +1512,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1536 case RENEW_REQUESTED: /* manual (SIGUSR1) renew */ 1512 case RENEW_REQUESTED: /* manual (SIGUSR1) renew */
1537 case_RENEW_REQUESTED: 1513 case_RENEW_REQUESTED:
1538 case RENEWING: 1514 case RENEWING:
1539 if (timeout >= 60) { 1515 if (packet_num < 3) {
1516 packet_num++;
1540 /* send an unicast renew request */ 1517 /* send an unicast renew request */
1541 /* Sometimes observed to fail (EADDRNOTAVAIL) to bind 1518 /* Sometimes observed to fail (EADDRNOTAVAIL) to bind
1542 * a new UDP socket for sending inside send_renew. 1519 * a new UDP socket for sending inside send_renew.
@@ -1547,14 +1524,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1547 * into INIT_SELECTING state. 1524 * into INIT_SELECTING state.
1548 */ 1525 */
1549 if (send_renew(xid, server_addr, requested_ip) >= 0) { 1526 if (send_renew(xid, server_addr, requested_ip) >= 0) {
1550 timeout >>= 1; 1527 timeout = discover_timeout;
1551//TODO: the timeout to receive an answer for our renew should not be selected 1528 /* ^^^ used to be = lease_remaining / 2 - WAY too long */
1552//with "timeout = lease_seconds / 2; ...; timeout = timeout / 2": it is often huge.
1553//Waiting e.g. 4*3600 seconds for a reply does not make sense
1554//(if reply isn't coming, we keep an open socket for hours),
1555//it should be something like 10 seconds.
1556//Also, it's probably best to try sending renew in kernel mode a few (3-5) times
1557//and fall back to raw mode if it does not work.
1558 continue; 1529 continue;
1559 } 1530 }
1560 /* else: error sending. 1531 /* else: error sending.
@@ -1563,6 +1534,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1563 * which wasn't reachable (and probably did not exist). 1534 * which wasn't reachable (and probably did not exist).
1564 */ 1535 */
1565 } 1536 }
1537//TODO: if 3 renew's failed (no reply) but remaining lease is large,
1538//it might make sense to make a large pause (~1 hour?) and try later?
1566 /* Timed out or error, enter rebinding state */ 1539 /* Timed out or error, enter rebinding state */
1567 log1s("entering rebinding state"); 1540 log1s("entering rebinding state");
1568 client_data.state = REBINDING; 1541 client_data.state = REBINDING;
@@ -1572,10 +1545,10 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1572 change_listen_mode(LISTEN_RAW); 1545 change_listen_mode(LISTEN_RAW);
1573 /* Lease is *really* about to run out, 1546 /* Lease is *really* about to run out,
1574 * try to find DHCP server using broadcast */ 1547 * try to find DHCP server using broadcast */
1575 if (timeout > 0) { 1548 if (lease_remaining > 0) {
1576 /* send a broadcast renew request */ 1549 /* send a broadcast renew request */
1577 send_renew(xid, 0 /*INADDR_ANY*/, requested_ip); 1550 send_renew(xid, 0 /*INADDR_ANY*/, requested_ip);
1578 timeout >>= 1; 1551 timeout = discover_timeout;
1579 continue; 1552 continue;
1580 } 1553 }
1581 /* Timed out, enter init state */ 1554 /* Timed out, enter init state */
@@ -1583,7 +1556,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1583 udhcp_run_script(NULL, "deconfig"); 1556 udhcp_run_script(NULL, "deconfig");
1584 client_data.state = INIT_SELECTING; 1557 client_data.state = INIT_SELECTING;
1585 client_data.first_secs = 0; /* make secs field count from 0 */ 1558 client_data.first_secs = 0; /* make secs field count from 0 */
1586 /*timeout = 0; - already is */ 1559 timeout = 0;
1587 packet_num = 0; 1560 packet_num = 0;
1588 continue; 1561 continue;
1589 /* case RELEASED: */ 1562 /* case RELEASED: */
@@ -1599,22 +1572,26 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1599 switch (udhcp_sp_read()) { 1572 switch (udhcp_sp_read()) {
1600 case SIGUSR1: 1573 case SIGUSR1:
1601 client_data.first_secs = 0; /* make secs field count from 0 */ 1574 client_data.first_secs = 0; /* make secs field count from 0 */
1602 already_waited_sec = 0; 1575 bb_simple_info_msg("performing DHCP renew");
1603 perform_renew(); 1576
1604 if (client_data.state == RENEW_REQUESTED) { 1577 switch (client_data.state) {
1605 /* We might be either on the same network 1578 /* Try to renew/rebind */
1606 * (in which case renew might work), 1579 case BOUND:
1607 * or we might be on a completely different one 1580 case RENEWING:
1608 * (in which case renew won't ever succeed). 1581 case REBINDING:
1609 * For the second case, must make sure timeout 1582 change_listen_mode(LISTEN_KERNEL);
1610 * is not too big, or else we can send 1583 client_data.state = RENEW_REQUESTED;
1611 * futile renew requests for hours.
1612 */
1613 if (timeout > 60)
1614 timeout = 60;
1615 goto case_RENEW_REQUESTED; 1584 goto case_RENEW_REQUESTED;
1616 } 1585
1617 /* Start things over */ 1586 /* Start things over */
1587 case RENEW_REQUESTED: /* two or more SIGUSR1 received */
1588 udhcp_run_script(NULL, "deconfig");
1589 /* case REQUESTING: break; */
1590 /* case RELEASED: break; */
1591 /* case INIT_SELECTING: break; */
1592 }
1593 change_listen_mode(LISTEN_RAW);
1594 client_data.state = INIT_SELECTING;
1618 packet_num = 0; 1595 packet_num = 0;
1619 /* Kill any timeouts, user wants this to hurry along */ 1596 /* Kill any timeouts, user wants this to hurry along */
1620 timeout = 0; 1597 timeout = 0;
@@ -1646,10 +1623,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1646 sleep(discover_timeout); /* 3 seconds by default */ 1623 sleep(discover_timeout); /* 3 seconds by default */
1647 change_listen_mode(client_data.listen_mode); /* just close and reopen */ 1624 change_listen_mode(client_data.listen_mode); /* just close and reopen */
1648 } 1625 }
1649 /* If this packet will turn out to be unrelated/bogus,
1650 * we will go back and wait for next one.
1651 * Be sure timeout is properly decreased. */
1652 already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
1653 if (len < 0) 1626 if (len < 0)
1654 continue; 1627 continue;
1655 } 1628 }
@@ -1722,7 +1695,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1722 client_data.state = REQUESTING; 1695 client_data.state = REQUESTING;
1723 timeout = 0; 1696 timeout = 0;
1724 packet_num = 0; 1697 packet_num = 0;
1725 already_waited_sec = 0;
1726 } 1698 }
1727 continue; 1699 continue;
1728 case REQUESTING: 1700 case REQUESTING:
@@ -1731,28 +1703,38 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1731 case REBINDING: 1703 case REBINDING:
1732 if (*message == DHCPACK) { 1704 if (*message == DHCPACK) {
1733 unsigned start; 1705 unsigned start;
1734 uint32_t lease_seconds;
1735 struct in_addr temp_addr; 1706 struct in_addr temp_addr;
1736 char server_str[sizeof("255.255.255.255")]; 1707 char server_str[sizeof("255.255.255.255")];
1737 uint8_t *temp; 1708 uint8_t *temp;
1738 1709
1710 temp_addr.s_addr = server_addr;
1711 strcpy(server_str, inet_ntoa(temp_addr));
1712 temp_addr.s_addr = packet.yiaddr;
1713
1739 temp = udhcp_get_option32(&packet, DHCP_LEASE_TIME); 1714 temp = udhcp_get_option32(&packet, DHCP_LEASE_TIME);
1740 if (!temp) { 1715 if (!temp) {
1741 bb_simple_info_msg("no lease time with ACK, using 1 hour lease"); 1716 lease_remaining = 60 * 60;
1742 lease_seconds = 60 * 60;
1743 } else { 1717 } else {
1718 uint32_t lease;
1744 /* it IS unaligned sometimes, don't "optimize" */ 1719 /* it IS unaligned sometimes, don't "optimize" */
1745 move_from_unaligned32(lease_seconds, temp); 1720 move_from_unaligned32(lease, temp);
1746 lease_seconds = ntohl(lease_seconds); 1721 lease_remaining = ntohl(lease);
1747 /* paranoia: must not be too small and not prone to overflows */
1748 /* timeout > 60 - ensures at least one unicast renew attempt */
1749 if (lease_seconds < 2 * 61)
1750 lease_seconds = 2 * 61;
1751 //if (lease_seconds > 0x7fffffff)
1752 // lease_seconds = 0x7fffffff;
1753 //^^^not necessary since "timeout = lease_seconds / 2"
1754 //does not overflow even for 0xffffffff.
1755 } 1722 }
1723 /* Log message _before_ we sanitize lease */
1724 bb_info_msg("lease of %s obtained from %s, lease time %u%s",
1725 inet_ntoa(temp_addr), server_str, (unsigned)lease_remaining,
1726 temp ? "" : " (default)"
1727 );
1728 /* paranoia: must not be too small and not prone to overflows */
1729 /* NB: 60s leases _are_ used in real world
1730 * (temporary IPs while ISP modem initializes)
1731 * do not break this case by bumping it up.
1732 */
1733 if (lease_remaining < 0) /* signed overflow? */
1734 lease_remaining = INT_MAX;
1735 if (lease_remaining < 30)
1736 lease_remaining = 30;
1737 requested_ip = packet.yiaddr;
1756#if ENABLE_FEATURE_UDHCPC_ARPING 1738#if ENABLE_FEATURE_UDHCPC_ARPING
1757 if (opt & OPT_a) { 1739 if (opt & OPT_a) {
1758/* RFC 2131 3.1 paragraph 5: 1740/* RFC 2131 3.1 paragraph 5:
@@ -1764,7 +1746,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1764 * address is already in use (e.g., through the use of ARP), 1746 * address is already in use (e.g., through the use of ARP),
1765 * the client MUST send a DHCPDECLINE message to the server and restarts 1747 * the client MUST send a DHCPDECLINE message to the server and restarts
1766 * the configuration process..." */ 1748 * the configuration process..." */
1767 if (!arpping(packet.yiaddr, 1749 if (!arpping(requested_ip,
1768 NULL, 1750 NULL,
1769 (uint32_t) 0, 1751 (uint32_t) 0,
1770 client_data.client_mac, 1752 client_data.client_mac,
@@ -1783,27 +1765,18 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1783 requested_ip = 0; 1765 requested_ip = 0;
1784 timeout = tryagain_timeout; 1766 timeout = tryagain_timeout;
1785 packet_num = 0; 1767 packet_num = 0;
1786 already_waited_sec = 0;
1787 continue; /* back to main loop */ 1768 continue; /* back to main loop */
1788 } 1769 }
1789 } 1770 }
1790#endif 1771#endif
1791 /* enter bound state */
1792 temp_addr.s_addr = server_addr;
1793 strcpy(server_str, inet_ntoa(temp_addr));
1794 temp_addr.s_addr = packet.yiaddr;
1795 bb_info_msg("lease of %s obtained from %s, lease time %u",
1796 inet_ntoa(temp_addr), server_str, (unsigned)lease_seconds);
1797 requested_ip = packet.yiaddr;
1798 1772
1773 /* enter bound state */
1799 start = monotonic_sec(); 1774 start = monotonic_sec();
1800 udhcp_run_script(&packet, client_data.state == REQUESTING ? "bound" : "renew"); 1775 udhcp_run_script(&packet, client_data.state == REQUESTING ? "bound" : "renew");
1801 already_waited_sec = (unsigned)monotonic_sec() - start; 1776 timeout = (unsigned)lease_remaining / 2;
1802 timeout = lease_seconds / 2; 1777//TODO: why / 2?
1803 if ((unsigned)timeout < already_waited_sec) { 1778 timeout -= (unsigned)monotonic_sec() - start;
1804 /* Something went wrong. Back to discover state */ 1779 packet_num = 0;
1805 timeout = already_waited_sec = 0;
1806 }
1807 1780
1808 client_data.state = BOUND; 1781 client_data.state = BOUND;
1809 change_listen_mode(LISTEN_NONE); 1782 change_listen_mode(LISTEN_NONE);
@@ -1856,7 +1829,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1856 requested_ip = 0; 1829 requested_ip = 0;
1857 timeout = 0; 1830 timeout = 0;
1858 packet_num = 0; 1831 packet_num = 0;
1859 already_waited_sec = 0;
1860 } 1832 }
1861 continue; 1833 continue;
1862 /* case BOUND: - ignore all packets */ 1834 /* case BOUND: - ignore all packets */
diff --git a/networking/udhcp/signalpipe.c b/networking/udhcp/signalpipe.c
index 7df671245..774c4beee 100644
--- a/networking/udhcp/signalpipe.c
+++ b/networking/udhcp/signalpipe.c
@@ -65,7 +65,7 @@ void FAST_FUNC udhcp_sp_setup(void)
65/* Quick little function to setup the pfds. 65/* Quick little function to setup the pfds.
66 * Limited in that you can only pass one extra fd. 66 * Limited in that you can only pass one extra fd.
67 */ 67 */
68void FAST_FUNC udhcp_sp_fd_set(struct pollfd pfds[2], int extra_fd) 68void FAST_FUNC udhcp_sp_fd_set(struct pollfd *pfds, int extra_fd)
69{ 69{
70 pfds[0].fd = READ_FD; 70 pfds[0].fd = READ_FD;
71 pfds[0].events = POLLIN; 71 pfds[0].events = POLLIN;
diff --git a/networking/wget.c b/networking/wget.c
index 270eab141..a5369be22 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -25,6 +25,13 @@
25//config: default y 25//config: default y
26//config: depends on WGET 26//config: depends on WGET
27//config: 27//config:
28//config:config FEATURE_WGET_FTP
29//config: bool "Enable FTP protocol (+1k)"
30//config: default y
31//config: depends on WGET
32//config: help
33//config: To support FTPS, enable FEATURE_WGET_HTTPS as well.
34//config:
28//config:config FEATURE_WGET_AUTHENTICATION 35//config:config FEATURE_WGET_AUTHENTICATION
29//config: bool "Enable HTTP authentication" 36//config: bool "Enable HTTP authentication"
30//config: default y 37//config: default y
@@ -48,12 +55,12 @@
48//config: 55//config:
49//config:config FEATURE_WGET_HTTPS 56//config:config FEATURE_WGET_HTTPS
50//config: bool "Support HTTPS using internal TLS code" 57//config: bool "Support HTTPS using internal TLS code"
51//it also enables FTPS support, but it's not well tested yet
52//config: default y 58//config: default y
53//config: depends on WGET 59//config: depends on WGET
54//config: select TLS 60//config: select TLS
55//config: help 61//config: help
56//config: wget will use internal TLS code to connect to https:// URLs. 62//config: wget will use internal TLS code to connect to https:// URLs.
63//config: It also enables FTPS support, but it's not well tested yet.
57//config: Note: 64//config: Note:
58//config: On NOMMU machines, ssl_helper applet should be available 65//config: On NOMMU machines, ssl_helper applet should be available
59//config: in the $PATH for this to work. Make sure to select that applet. 66//config: in the $PATH for this to work. Make sure to select that applet.
@@ -173,6 +180,7 @@
173 180
174 181
175#define SSL_SUPPORTED (ENABLE_FEATURE_WGET_OPENSSL || ENABLE_FEATURE_WGET_HTTPS) 182#define SSL_SUPPORTED (ENABLE_FEATURE_WGET_OPENSSL || ENABLE_FEATURE_WGET_HTTPS)
183#define FTPS_SUPPORTED (ENABLE_FEATURE_WGET_FTP && ENABLE_FEATURE_WGET_HTTPS)
176 184
177struct host_info { 185struct host_info {
178 char *allocated; 186 char *allocated;
@@ -182,14 +190,16 @@ struct host_info {
182 char *host; 190 char *host;
183 int port; 191 int port;
184}; 192};
185static const char P_FTP[] ALIGN1 = "ftp";
186static const char P_HTTP[] ALIGN1 = "http"; 193static const char P_HTTP[] ALIGN1 = "http";
187#if SSL_SUPPORTED 194#if SSL_SUPPORTED
188# if ENABLE_FEATURE_WGET_HTTPS
189static const char P_FTPS[] ALIGN1 = "ftps";
190# endif
191static const char P_HTTPS[] ALIGN1 = "https"; 195static const char P_HTTPS[] ALIGN1 = "https";
192#endif 196#endif
197#if ENABLE_FEATURE_WGET_FTP
198static const char P_FTP[] ALIGN1 = "ftp";
199#endif
200#if FTPS_SUPPORTED
201static const char P_FTPS[] ALIGN1 = "ftps";
202#endif
193 203
194#if ENABLE_FEATURE_WGET_LONG_OPTIONS 204#if ENABLE_FEATURE_WGET_LONG_OPTIONS
195/* User-specified headers prevent using our corresponding built-in headers. */ 205/* User-specified headers prevent using our corresponding built-in headers. */
@@ -482,6 +492,7 @@ static char fgets_trim_sanitize(FILE *fp, const char *fmt)
482 return c; 492 return c;
483} 493}
484 494
495#if ENABLE_FEATURE_WGET_FTP
485static int ftpcmd(const char *s1, const char *s2, FILE *fp) 496static int ftpcmd(const char *s1, const char *s2, FILE *fp)
486{ 497{
487 int result; 498 int result;
@@ -513,6 +524,7 @@ static int ftpcmd(const char *s1, const char *s2, FILE *fp)
513 G.wget_buf[3] = ' '; 524 G.wget_buf[3] = ' ';
514 return result; 525 return result;
515} 526}
527#endif
516 528
517static void parse_url(const char *src_url, struct host_info *h) 529static void parse_url(const char *src_url, struct host_info *h)
518{ 530{
@@ -521,30 +533,31 @@ static void parse_url(const char *src_url, struct host_info *h)
521 free(h->allocated); 533 free(h->allocated);
522 h->allocated = url = xstrdup(src_url); 534 h->allocated = url = xstrdup(src_url);
523 535
524 h->protocol = P_FTP; 536 h->protocol = P_HTTP;
525 p = strstr(url, "://"); 537 p = strstr(url, "://");
526 if (p) { 538 if (p) {
527 *p = '\0'; 539 *p = '\0';
528 h->host = p + 3; 540 h->host = p + 3;
541#if ENABLE_FEATURE_WGET_FTP
529 if (strcmp(url, P_FTP) == 0) { 542 if (strcmp(url, P_FTP) == 0) {
530 h->port = bb_lookup_std_port(P_FTP, "tcp", 21); 543 h->port = bb_lookup_std_port(P_FTP, "tcp", 21);
544 h->protocol = P_FTP;
531 } else 545 } else
532#if SSL_SUPPORTED 546#endif
533# if ENABLE_FEATURE_WGET_HTTPS 547#if FTPS_SUPPORTED
534 if (strcmp(url, P_FTPS) == 0) { 548 if (strcmp(url, P_FTPS) == 0) {
535 h->port = bb_lookup_std_port(P_FTPS, "tcp", 990); 549 h->port = bb_lookup_std_port(P_FTPS, "tcp", 990);
536 h->protocol = P_FTPS; 550 h->protocol = P_FTPS;
537 } else 551 } else
538# endif 552#endif
553#if SSL_SUPPORTED
539 if (strcmp(url, P_HTTPS) == 0) { 554 if (strcmp(url, P_HTTPS) == 0) {
540 h->port = bb_lookup_std_port(P_HTTPS, "tcp", 443); 555 h->port = bb_lookup_std_port(P_HTTPS, "tcp", 443);
541 h->protocol = P_HTTPS; 556 h->protocol = P_HTTPS;
542 } else 557 } else
543#endif 558#endif
544 if (strcmp(url, P_HTTP) == 0) { 559 if (strcmp(url, P_HTTP) == 0) {
545 http: 560 goto http;
546 h->port = bb_lookup_std_port(P_HTTP, "tcp", 80);
547 h->protocol = P_HTTP;
548 } else { 561 } else {
549 *p = ':'; 562 *p = ':';
550 bb_error_msg_and_die("not an http or ftp url: %s", url); 563 bb_error_msg_and_die("not an http or ftp url: %s", url);
@@ -552,7 +565,8 @@ static void parse_url(const char *src_url, struct host_info *h)
552 } else { 565 } else {
553 // GNU wget is user-friendly and falls back to http:// 566 // GNU wget is user-friendly and falls back to http://
554 h->host = url; 567 h->host = url;
555 goto http; 568 http:
569 h->port = bb_lookup_std_port(P_HTTP, "tcp", 80);
556 } 570 }
557 571
558 // FYI: 572 // FYI:
@@ -829,6 +843,7 @@ static void spawn_ssl_client(const char *host, int network_fd, int flags)
829# endif 843# endif
830#endif 844#endif
831 845
846#if ENABLE_FEATURE_WGET_FTP
832static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa) 847static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
833{ 848{
834 FILE *sfp; 849 FILE *sfp;
@@ -836,7 +851,7 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
836 int port; 851 int port;
837 852
838 sfp = open_socket(lsa); 853 sfp = open_socket(lsa);
839#if ENABLE_FEATURE_WGET_HTTPS 854#if FTPS_SUPPORTED
840 if (target->protocol == P_FTPS) 855 if (target->protocol == P_FTPS)
841 spawn_ssl_client(target->host, fileno(sfp), TLSLOOP_EXIT_ON_LOCAL_EOF); 856 spawn_ssl_client(target->host, fileno(sfp), TLSLOOP_EXIT_ON_LOCAL_EOF);
842#endif 857#endif
@@ -892,7 +907,7 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
892 907
893 *dfpp = open_socket(lsa); 908 *dfpp = open_socket(lsa);
894 909
895#if ENABLE_FEATURE_WGET_HTTPS 910#if FTPS_SUPPORTED
896 if (target->protocol == P_FTPS) { 911 if (target->protocol == P_FTPS) {
897 /* "PROT P" enables encryption of data stream. 912 /* "PROT P" enables encryption of data stream.
898 * Without it (or with "PROT C"), data is sent unencrypted. 913 * Without it (or with "PROT C"), data is sent unencrypted.
@@ -918,6 +933,7 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
918 933
919 return sfp; 934 return sfp;
920} 935}
936#endif
921 937
922static void NOINLINE retrieve_file_data(FILE *dfp) 938static void NOINLINE retrieve_file_data(FILE *dfp)
923{ 939{
@@ -1363,6 +1379,8 @@ However, in real world it was observed that some web servers
1363 case 301: 1379 case 301:
1364 case 302: 1380 case 302:
1365 case 303: 1381 case 303:
1382 case 307:
1383 case 308:
1366 break; 1384 break;
1367 1385
1368 case 206: /* Partial Content */ 1386 case 206: /* Partial Content */
@@ -1440,10 +1458,12 @@ However, in real world it was observed that some web servers
1440 /* For HTTP, data is pumped over the same connection */ 1458 /* For HTTP, data is pumped over the same connection */
1441 dfp = sfp; 1459 dfp = sfp;
1442 } else { 1460 } else {
1461#if ENABLE_FEATURE_WGET_FTP
1443 /* 1462 /*
1444 * FTP session 1463 * FTP session
1445 */ 1464 */
1446 sfp = prepare_ftp_session(&dfp, &target, lsa); 1465 sfp = prepare_ftp_session(&dfp, &target, lsa);
1466#endif
1447 } 1467 }
1448 1468
1449 free(lsa); 1469 free(lsa);
@@ -1461,6 +1481,7 @@ However, in real world it was observed that some web servers
1461 fprintf(stderr, "remote file exists\n"); 1481 fprintf(stderr, "remote file exists\n");
1462 } 1482 }
1463 1483
1484#if ENABLE_FEATURE_WGET_FTP
1464 if (dfp != sfp) { 1485 if (dfp != sfp) {
1465 /* It's ftp. Close data connection properly */ 1486 /* It's ftp. Close data connection properly */
1466 fclose(dfp); 1487 fclose(dfp);
@@ -1468,6 +1489,7 @@ However, in real world it was observed that some web servers
1468 bb_error_msg_and_die("ftp error: %s", G.wget_buf); 1489 bb_error_msg_and_die("ftp error: %s", G.wget_buf);
1469 /* ftpcmd("QUIT", NULL, sfp); - why bother? */ 1490 /* ftpcmd("QUIT", NULL, sfp); - why bother? */
1470 } 1491 }
1492#endif
1471 fclose(sfp); 1493 fclose(sfp);
1472 1494
1473 free(server.allocated); 1495 free(server.allocated);
diff --git a/scripts/basic/docproc.c b/scripts/basic/docproc.c
index bfc1a9844..2c7a19b83 100644
--- a/scripts/basic/docproc.c
+++ b/scripts/basic/docproc.c
@@ -197,10 +197,10 @@ void find_export_symbols(char * filename)
197 perror(real_filename); 197 perror(real_filename);
198 } 198 }
199 while (fgets(line, MAXLINESZ, fp)) { 199 while (fgets(line, MAXLINESZ, fp)) {
200 char *p; 200 unsigned char *p;
201 char *e; 201 unsigned char *e;
202 if (((p = strstr(line, "EXPORT_SYMBOL_GPL")) != 0) || 202 if (((p = (unsigned char *)strstr(line, "EXPORT_SYMBOL_GPL")) != 0) ||
203 ((p = strstr(line, "EXPORT_SYMBOL")) != 0)) { 203 ((p = (unsigned char *)strstr(line, "EXPORT_SYMBOL")) != 0)) {
204 /* Skip EXPORT_SYMBOL{_GPL} */ 204 /* Skip EXPORT_SYMBOL{_GPL} */
205 while (isalnum(*p) || *p == '_') 205 while (isalnum(*p) || *p == '_')
206 p++; 206 p++;
@@ -217,7 +217,7 @@ void find_export_symbols(char * filename)
217 while (isalnum(*e) || *e == '_') 217 while (isalnum(*e) || *e == '_')
218 e++; 218 e++;
219 *e = '\0'; 219 *e = '\0';
220 add_new_symbol(sym, p); 220 add_new_symbol(sym, (char*)p);
221 } 221 }
222 } 222 }
223 fclose(fp); 223 fclose(fp);
@@ -281,7 +281,7 @@ void singfunc(char * filename, char * line)
281 281
282 /* Split line up in individual parameters preceded by FUNCTION */ 282 /* Split line up in individual parameters preceded by FUNCTION */
283 for (i=0; line[i]; i++) { 283 for (i=0; line[i]; i++) {
284 if (isspace(line[i])) { 284 if (isspace((unsigned char) line[i])) {
285 line[i] = '\0'; 285 line[i] = '\0';
286 startofsym = 1; 286 startofsym = 1;
287 continue; 287 continue;
@@ -308,10 +308,10 @@ void singfunc(char * filename, char * line)
308void parse_file(FILE *infile) 308void parse_file(FILE *infile)
309{ 309{
310 char line[MAXLINESZ]; 310 char line[MAXLINESZ];
311 char * s; 311 unsigned char * s;
312 while (fgets(line, MAXLINESZ, infile)) { 312 while (fgets(line, MAXLINESZ, infile)) {
313 if (line[0] == '!') { 313 if (line[0] == '!') {
314 s = line + 2; 314 s = (unsigned char *)line + 2;
315 switch (line[1]) { 315 switch (line[1]) {
316 case 'E': 316 case 'E':
317 while (*s && !isspace(*s)) s++; 317 while (*s && !isspace(*s)) s++;
@@ -335,7 +335,7 @@ void parse_file(FILE *infile)
335 /* function names */ 335 /* function names */
336 while (isspace(*s)) 336 while (isspace(*s))
337 s++; 337 s++;
338 singlefunctions(line +2, s); 338 singlefunctions(line +2, (char*)s);
339 break; 339 break;
340 default: 340 default:
341 defaultline(line); 341 defaultline(line);
diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c
index 9f461a65b..64fd92f06 100644
--- a/scripts/basic/fixdep.c
+++ b/scripts/basic/fixdep.c
@@ -281,10 +281,10 @@ void use_config(char *m, int slen)
281void parse_config_file(char *map, size_t len) 281void parse_config_file(char *map, size_t len)
282{ 282{
283 /* modified for bbox */ 283 /* modified for bbox */
284 char *end_3 = map + len - 3; /* 3 == length of "IF_" */ 284 unsigned char *end_3 = (unsigned char *)map + len - 3; /* 3 == length of "IF_" */
285 char *end_7 = map + len - 7; 285 unsigned char *end_7 = (unsigned char *)map + len - 7;
286 char *p = map; 286 unsigned char *p = (unsigned char *)map;
287 char *q; 287 unsigned char *q;
288 int off; 288 int off;
289 289
290 for (; p <= end_3; p++) { 290 for (; p <= end_3; p++) {
@@ -318,7 +318,7 @@ void parse_config_file(char *map, size_t len)
318 break; 318 break;
319 } 319 }
320 if (q != p) { 320 if (q != p) {
321 use_config(p, q-p); 321 use_config((char*)p, q - p);
322 } 322 }
323 } 323 }
324} 324}
@@ -391,7 +391,7 @@ void parse_dep_file(void *map, size_t len)
391 while (p < end && *p != ' ') p++; 391 while (p < end && *p != ' ') p++;
392 if (p == m) break; 392 if (p == m) break;
393 if (p == end) { 393 if (p == end) {
394 do p--; while (p != m && !isalnum(*p)); 394 do p--; while (p != m && !isalnum((unsigned char)*p));
395 p++; 395 p++;
396 } 396 }
397 if (p == m) break; 397 if (p == m) break;
diff --git a/scripts/basic/split-include.c b/scripts/basic/split-include.c
index 9a9260f2c..290bea2fb 100644
--- a/scripts/basic/split-include.c
+++ b/scripts/basic/split-include.c
@@ -118,7 +118,7 @@ int main(int argc, const char * argv [])
118 /* We found #define CONFIG_foo or #undef CONFIG_foo. 118 /* We found #define CONFIG_foo or #undef CONFIG_foo.
119 * Make the output file name. */ 119 * Make the output file name. */
120 str_config += sizeof(" CONFIG_") - 1; 120 str_config += sizeof(" CONFIG_") - 1;
121 for (itarget = 0; !isspace(str_config[itarget]); itarget++) 121 for (itarget = 0; !isspace((unsigned char)str_config[itarget]); itarget++)
122 { 122 {
123 int c = (unsigned char) str_config[itarget]; 123 int c = (unsigned char) str_config[itarget];
124 if (isupper(c)) c = tolower(c); 124 if (isupper(c)) c = tolower(c);
@@ -133,7 +133,7 @@ int main(int argc, const char * argv [])
133 is_same = 0; 133 is_same = 0;
134 if ((fp_target = fopen(ptarget, "r")) != NULL) 134 if ((fp_target = fopen(ptarget, "r")) != NULL)
135 { 135 {
136 if (!fgets(old_line, buffer_size, fp_target)) 136 if (!fgets(old_line, buffer_size, fp_target) && ferror(fp_target))
137 ERROR_EXIT(ptarget); 137 ERROR_EXIT(ptarget);
138 if (fclose(fp_target) != 0) 138 if (fclose(fp_target) != 0)
139 ERROR_EXIT(ptarget); 139 ERROR_EXIT(ptarget);
diff --git a/scripts/gcc-version.sh b/scripts/gcc-version.sh
index 34510804f..0eb27c7a6 100755
--- a/scripts/gcc-version.sh
+++ b/scripts/gcc-version.sh
@@ -7,6 +7,6 @@
7# 7#
8 8
9compiler="$*" 9compiler="$*"
10 10# tr -d '\r': fix up msdos-style line endings (Cygwin et al)
11MAJ_MIN=$(echo __GNUC__ __GNUC_MINOR__ | $compiler -E -xc - | tail -n 1) 11MAJ_MIN=$(echo __GNUC__ __GNUC_MINOR__ | $compiler -E -xc - | tr -d '\r' | tail -n 1)
12printf '%02d%02d\n' $MAJ_MIN 12printf '%02d%02d\n' $MAJ_MIN
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index 80bd55a68..4680932d7 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -44,7 +44,7 @@ static void strip(char *str)
44 char *p = str; 44 char *p = str;
45 int l; 45 int l;
46 46
47 while ((isspace(*p))) 47 while ((isspace((unsigned char)*p)))
48 p++; 48 p++;
49 l = strlen(p); 49 l = strlen(p);
50 if (p != str) 50 if (p != str)
@@ -52,7 +52,7 @@ static void strip(char *str)
52 if (!l) 52 if (!l)
53 return; 53 return;
54 p = str + l - 1; 54 p = str + l - 1;
55 while ((isspace(*p))) 55 while ((isspace((unsigned char)*p)))
56 *p-- = 0; 56 *p-- = 0;
57} 57}
58 58
@@ -406,7 +406,7 @@ static int conf_choice(struct menu *menu)
406 } 406 }
407 if (!line[0]) 407 if (!line[0])
408 cnt = def; 408 cnt = def;
409 else if (isdigit(line[0])) 409 else if (isdigit((unsigned char)line[0]))
410 cnt = atoi(line); 410 cnt = atoi(line);
411 else 411 else
412 continue; 412 continue;
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index b92c2324e..d6c1621b8 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -54,7 +54,7 @@ static char *conf_expand_value(const char *in)
54 strncat(res_value, in, src - in); 54 strncat(res_value, in, src - in);
55 src++; 55 src++;
56 dst = name; 56 dst = name;
57 while (isalnum(*src) || *src == '_') 57 while (isalnum((unsigned char)*src) || *src == '_')
58 *dst++ = *src++; 58 *dst++ = *src++;
59 *dst = 0; 59 *dst = 0;
60 sym = sym_lookup(name, 0); 60 sym = sym_lookup(name, 0);
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index d945f2cd5..55afeb763 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -790,7 +790,7 @@ static void conf(struct menu *menu)
790 if (!type) 790 if (!type)
791 continue; 791 continue;
792 792
793 for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++) 793 for (i = 0; input_buf[i] && !isspace((unsigned char)input_buf[i]); i++)
794 ; 794 ;
795 if (i >= sizeof(active_entry)) 795 if (i >= sizeof(active_entry))
796 i = sizeof(active_entry) - 1; 796 i = sizeof(active_entry) - 1;
diff --git a/selinux/chcon.c b/selinux/chcon.c
index 2e4f94c0f..e1778a36a 100644
--- a/selinux/chcon.c
+++ b/selinux/chcon.c
@@ -26,7 +26,7 @@
26//usage: ) 26//usage: )
27//usage: 27//usage:
28//usage:#define chcon_full_usage "\n\n" 28//usage:#define chcon_full_usage "\n\n"
29//usage: "Change the security context of each FILE to CONTEXT\n" 29//usage: "Change the security context of FILEs to CONTEXT\n"
30//usage: "\n -v Verbose" 30//usage: "\n -v Verbose"
31//usage: "\n -c Report changes made" 31//usage: "\n -c Report changes made"
32//usage: "\n -h Affect symlinks instead of their targets" 32//usage: "\n -h Affect symlinks instead of their targets"
diff --git a/shell/hush.c b/shell/hush.c
index 5eb6fa396..144ad3edd 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -127,11 +127,6 @@
127//config: help 127//config: help
128//config: Enable {abc,def} extension. 128//config: Enable {abc,def} extension.
129//config: 129//config:
130//config:config HUSH_LINENO_VAR
131//config: bool "$LINENO variable"
132//config: default y
133//config: depends on HUSH_BASH_COMPAT
134//config:
135//config:config HUSH_BASH_SOURCE_CURDIR 130//config:config HUSH_BASH_SOURCE_CURDIR
136//config: bool "'source' and '.' builtins search current directory after $PATH" 131//config: bool "'source' and '.' builtins search current directory after $PATH"
137//config: default n # do not encourage non-standard behavior 132//config: default n # do not encourage non-standard behavior
@@ -139,6 +134,11 @@
139//config: help 134//config: help
140//config: This is not compliant with standards. Avoid if possible. 135//config: This is not compliant with standards. Avoid if possible.
141//config: 136//config:
137//config:config HUSH_LINENO_VAR
138//config: bool "$LINENO variable (bashism)"
139//config: default y
140//config: depends on SHELL_HUSH
141//config:
142//config:config HUSH_INTERACTIVE 142//config:config HUSH_INTERACTIVE
143//config: bool "Interactive mode" 143//config: bool "Interactive mode"
144//config: default y 144//config: default y
@@ -2670,6 +2670,8 @@ static int get_user_input(struct in_str *i)
2670 } 2670 }
2671 if (r < 0) { 2671 if (r < 0) {
2672 /* EOF/error detected */ 2672 /* EOF/error detected */
2673 /* ^D on interactive input goes to next line before exiting: */
2674 write(STDOUT_FILENO, "\n", 1);
2673 i->p = NULL; 2675 i->p = NULL;
2674 i->peek_buf[0] = r = EOF; 2676 i->peek_buf[0] = r = EOF;
2675 return r; 2677 return r;
diff --git a/testsuite/dd/dd-count-bytes b/testsuite/dd/dd-count-bytes
new file mode 100644
index 000000000..0730cba5e
--- /dev/null
+++ b/testsuite/dd/dd-count-bytes
@@ -0,0 +1 @@
test "$(echo I WANT | busybox dd count=3 iflag=count_bytes 2>/dev/null)" = "I W"
diff --git a/testsuite/start-stop-daemon.tests b/testsuite/start-stop-daemon.tests
index 2ddb7fefb..0757b1288 100755
--- a/testsuite/start-stop-daemon.tests
+++ b/testsuite/start-stop-daemon.tests
@@ -21,8 +21,13 @@ testing "start-stop-daemon without -x and -a" \
21 "1\n" \ 21 "1\n" \
22 "" "" 22 "" ""
23 23
24# This runs /bin/false with argv[0..2] of { "qwerty", "false", NULL }.
25#
24# Unfortunately, this does not actually check argv[0] correctness, 26# Unfortunately, this does not actually check argv[0] correctness,
25# but at least it checks that pathname to exec() is correct 27# but at least it checks that pathname to exec() is correct
28#
29# NB: this fails if /bin/false is a busybox symlink:
30# busybox looks at argv[0] and says "qwerty: applet not found"
26testing "start-stop-daemon with both -x and -a" \ 31testing "start-stop-daemon with both -x and -a" \
27 'start-stop-daemon -S -x /bin/false -a qwerty false 2>&1; echo $?' \ 32 'start-stop-daemon -S -x /bin/false -a qwerty false 2>&1; echo $?' \
28 "1\n" \ 33 "1\n" \