diff options
author | Ron Yorston <rmy@pobox.com> | 2021-05-14 08:17:12 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2021-05-14 08:17:12 +0100 |
commit | a3f5a1b7f4275f713acf22f534f95c0da8392e53 (patch) | |
tree | 49b65422a3e9c33f508da9ccf3ae79d324bd9e96 | |
parent | 375cda9a88024135d630ca8990d9aff4ea414e89 (diff) | |
parent | 7de0ab21d939a5a304157f75918d0318a95261a3 (diff) | |
download | busybox-w32-a3f5a1b7f4275f713acf22f534f95c0da8392e53.tar.gz busybox-w32-a3f5a1b7f4275f713acf22f534f95c0da8392e53.tar.bz2 busybox-w32-a3f5a1b7f4275f713acf22f534f95c0da8392e53.zip |
Merge branch 'busybox' into merge
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 | |||
153 | endif | 153 | endif |
154 | 154 | ||
155 | ifneq ($(CONFIG_PLATFORM_MINGW32),y) | 155 | ifneq ($(CONFIG_PLATFORM_MINGW32),y) |
156 | # libm may be needed for dc, awk, ntpd | ||
157 | LDLIBS += 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: |
160 | CRYPT_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) | 162 | CRYPT_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) |
163 | RT_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) | ||
161 | ifeq ($(CRYPT_AVAILABLE),y) | 164 | ifeq ($(CRYPT_AVAILABLE),y) |
162 | LDLIBS += m rt crypt | 165 | LDLIBS += crypt |
163 | else | ||
164 | LDLIBS += m rt | ||
165 | endif | 166 | endif |
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() |
168 | ifeq ($(RT_AVAILABLE),y) | ||
169 | LDLIBS += rt | ||
170 | endif | ||
168 | endif | 171 | endif |
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) | |||
56 | static int str_isalnum_(const char *s) | 56 | static 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) | ||
223 | static void huft_free(huft_t *p) | 225 | static 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) | ||
294 | static huft_t* huft_build(const unsigned *b, const unsigned n, | 301 | static 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 | ||
159 | static void dd_output_status(int UNUSED_PARAM cur_signal) | 161 | static 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; | |||
85 | int touch_main(int argc UNUSED_PARAM, char **argv) | 76 | int 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[] |
247 | static const char modifying_cmds[] ALIGN1 = "aAcCdDiIJoOpPrRs""xX<>~"; | 262 | static 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 | ||
803 | static int prev_tabstop(int col) | ||
804 | { | ||
805 | return col - ((col % tabstop) ?: tabstop); | ||
806 | } | ||
807 | |||
808 | static 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 | |||
817 | static 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 ------------------------------ |
745 | static void screen_erase(void) | 828 | static 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 -------------------------- |
772 | static NOINLINE void sync_cursor(char *d, int *row, int *col) | 855 | static 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 | ||
1196 | static 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) |
1115 | static char *get_input_line(const char *prompt) | 1217 | static 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 |
1346 | static char *text_yank(char *p, char *q, int dest) // copy text into a register | 1448 | // copy text into a register |
1449 | static 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 | ||
1372 | static void check_context(char cmd) | 1479 | static 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 | ||
1504 | static 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 |
1408 | static void undo_push(char *, unsigned, unsigned char); | 1521 | static 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 |
1535 | static void undo_push(char *src, unsigned length, uint8_t u_type) | 1648 | static 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 | ||
1899 | static 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 | |||
1788 | static void dot_scroll(int cnt, int dir) | 1926 | static 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" | |||
1827 | static void start_new_cmd_q(char c) | 1965 | static 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 | } |
1839 | static void end_cmd_q(void) | 1973 | static 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 |
1856 | static char *yank_delete(char *start, char *stop, int dist, int yf, int undo) | 1989 | static 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 |
2034 | static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' | 2161 | static 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 | ||
2270 | static 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 | |||
2285 | static 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 |
2107 | static int init_text_buffer(char *fn) | 2306 | static 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 |
2315 | static char *get_one_address(char *p, int *addr) // get colon addr, if present | 2501 | static 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 | ||
2374 | static 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. | ||
2596 | static 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 |
2403 | static void setops(const char *args, const char *opname, int flg_no, | 2632 | static 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 | ||
2670 | static 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 | ||
3068 | static void do_cmd(int c); | 3398 | static void do_cmd(int c); |
3069 | 3399 | ||
3070 | static int find_range(char **start, char **stop, char c) | 3400 | static 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 | |||
3406 | static 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 | ||
665 | void xpipe(int filedes[2]) FAST_FUNC; | 688 | void 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 */ |
667 | struct fd_pair { int rd; int wr; }; | 690 | struct 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 | ||
229 | void FAST_FUNC xpipe(int filedes[2]) | 229 | void 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 | ||
132 | enum { | 137 | enum { |
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 | ||
140 | struct globals { | 144 | struct 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 | ||
46 | static 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. */ |
46 | typedef struct { | 53 | typedef 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 | |||
57 | static void shutdown_watchdog(void) | 68 | static 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 | ||
87 | int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 100 | int 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 | ||
356 | typedef enum CGI_type { | ||
357 | CGI_NONE = 0, | ||
358 | CGI_NORMAL, | ||
359 | CGI_INDEX, | ||
360 | CGI_INTERPRETER, | ||
361 | } CGI_type; | ||
362 | |||
363 | enum { | 362 | enum { |
364 | HTTP_OK = 200, | 363 | HTTP_OK = 200, |
365 | HTTP_PARTIAL_CONTENT = 206, | 364 | HTTP_PARTIAL_CONTENT = 206, |
@@ -564,7 +563,6 @@ enum { | |||
564 | enum { | 563 | enum { |
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 | }; |
569 | static void send_file_and_exit(const char *url, int what) NORETURN; | 567 | static 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 | }; |
699 | static void parse_conf(const char *path, int flag) | 697 | static 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) | |||
2227 | static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN; | 2216 | static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN; |
2228 | static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) | 2217 | static 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 | */ |
1700 | static uint32_t machtime(void) | 1700 | static 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 | |||
378 | enum { | 364 | enum { |
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 | ||
569 | static void | 554 | static void |
570 | d_to_tv(double d, struct timeval *tv) | 555 | d_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 | ||
576 | static double | 561 | static NOINLINE double |
577 | lfp_to_d(l_fixedpt_t lfp) | 562 | lfp_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 | } |
585 | static double | 570 | static NOINLINE double |
586 | sfp_to_d(s_fixedpt_t sfp) | 571 | sfp_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 |
595 | static l_fixedpt_t | 580 | static NOINLINE void |
596 | d_to_lfp(double d) | 581 | d_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 | } |
605 | static s_fixedpt_t | 590 | static NOINLINE void |
606 | d_to_sfp(double d) | 591 | d_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 | ||
272 | struct record_hdr { | 274 | struct 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 | |||
113 | void curve_x25519_compute_pubkey_and_premaster( | ||
114 | uint8_t *pubkey32, uint8_t *premaster32, | ||
115 | const uint8_t *peerkey32) FAST_FUNC; | ||
116 | |||
117 | void 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 | ||
66 | static void fe_select(byte *dst, | 66 | static 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 |
109 | static int prime_msb(const byte *p) | 109 | static 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 */ |
476 | static void xc_diffadd(byte *x5, byte *z5, | 476 | static 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 */ |
518 | static void xc_double(byte *x3, byte *z3, | 518 | static 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 | ||
547 | void FAST_FUNC curve25519(byte *result, const byte *e, const byte *q) | 547 | static 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 | |||
614 | void 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 | ||
7 | void 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 \ |
66 | asm( \ | 67 | asm( \ |
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 \ | ||
87 | asm( \ | ||
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 \ |
79 | asm( \ | 103 | asm( \ |
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) |
16 | static //bbox | 16 | static //bbox |
17 | int32 pkcs1Pad(unsigned char *in, uint32 inlen, unsigned char *out, | 17 | int32 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) |
61 | static //bbox | 61 | static //bbox |
62 | int32 psRsaCrypt(psPool_t *pool, const unsigned char *in, uint32 inlen, | 62 | int32 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__) | ||
14 | static 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 | ||
29 | typedef 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 | |||
60 | typedef 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. */ | ||
68 | static 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 | */ | ||
81 | static 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 | */ | ||
115 | static 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. */ | ||
139 | static 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 | */ | ||
155 | static 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 | */ | ||
171 | static 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. */ | ||
177 | static 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) */ | ||
187 | static 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) */ | ||
195 | static 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. */ | ||
203 | static 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) */ | ||
212 | static 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) */ | ||
227 | static 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) */ | ||
252 | static 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) */ | ||
279 | static 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) */ | ||
288 | static 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) */ | ||
299 | static 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) */ | ||
309 | static 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) */ | ||
319 | static 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. */ | ||
334 | static 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 | */ | ||
358 | static 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 | */ | ||
401 | static 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 | */ | ||
415 | static 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. */ | ||
430 | static 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 | ||
440 | static 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 | */ | ||
460 | static 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 | */ | ||
585 | static 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 | */ | ||
622 | static 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 | */ | ||
680 | static 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 | */ | ||
759 | static 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 | */ | ||
812 | static 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 | */ | ||
839 | static 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) */ | ||
862 | static void sp_256_add_one_10(sp_digit* a) | ||
863 | { | ||
864 | a[0]++; | ||
865 | sp_256_norm_10(a); | ||
866 | } | ||
867 | static 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. */ | ||
902 | static 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 | |||
914 | void 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 */ | ||
1091 | static 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 | |||
1113 | static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6) | 1089 | static 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 */ | ||
1126 | static 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 | |||
1148 | static void perform_release(uint32_t server_addr, uint32_t requested_ip) | 1124 | static 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 | |||
1251 | int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1226 | int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1252 | int udhcpc_main(int argc UNUSED_PARAM, char **argv) | 1227 | int 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 | */ |
68 | void FAST_FUNC udhcp_sp_fd_set(struct pollfd pfds[2], int extra_fd) | 68 | void 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 | ||
177 | struct host_info { | 185 | struct 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 | }; |
185 | static const char P_FTP[] ALIGN1 = "ftp"; | ||
186 | static const char P_HTTP[] ALIGN1 = "http"; | 193 | static const char P_HTTP[] ALIGN1 = "http"; |
187 | #if SSL_SUPPORTED | 194 | #if SSL_SUPPORTED |
188 | # if ENABLE_FEATURE_WGET_HTTPS | ||
189 | static const char P_FTPS[] ALIGN1 = "ftps"; | ||
190 | # endif | ||
191 | static const char P_HTTPS[] ALIGN1 = "https"; | 195 | static const char P_HTTPS[] ALIGN1 = "https"; |
192 | #endif | 196 | #endif |
197 | #if ENABLE_FEATURE_WGET_FTP | ||
198 | static const char P_FTP[] ALIGN1 = "ftp"; | ||
199 | #endif | ||
200 | #if FTPS_SUPPORTED | ||
201 | static 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 | ||
485 | static int ftpcmd(const char *s1, const char *s2, FILE *fp) | 496 | static 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 | ||
517 | static void parse_url(const char *src_url, struct host_info *h) | 529 | static 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 | ||
832 | static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa) | 847 | static 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 | ||
922 | static void NOINLINE retrieve_file_data(FILE *dfp) | 938 | static 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) | |||
308 | void parse_file(FILE *infile) | 308 | void 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) | |||
281 | void parse_config_file(char *map, size_t len) | 281 | void 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 | ||
9 | compiler="$*" | 9 | compiler="$*" |
10 | 10 | # tr -d '\r': fix up msdos-style line endings (Cygwin et al) | |
11 | MAJ_MIN=$(echo __GNUC__ __GNUC_MINOR__ | $compiler -E -xc - | tail -n 1) | 11 | MAJ_MIN=$(echo __GNUC__ __GNUC_MINOR__ | $compiler -E -xc - | tr -d '\r' | tail -n 1) |
12 | printf '%02d%02d\n' $MAJ_MIN | 12 | printf '%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" | ||
26 | testing "start-stop-daemon with both -x and -a" \ | 31 | testing "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" \ |