Signed-off-by: gong-guilin <gongguilin@h-partners.com>
This commit is contained in:
gong-guilin
2026-01-20 16:30:38 +08:00
parent 7a7c9f0d93
commit 5a3ee2ca35
19 changed files with 443 additions and 1192 deletions
-2
View File
@@ -106,7 +106,6 @@ if (defined(ohos_lite)) {
"toys/other/pwdx.c",
"toys/other/readahead.c",
"toys/other/readlink.c",
"toys/other/realpath.c",
"toys/other/reboot.c",
"toys/other/reset.c",
"toys/other/rev.c",
@@ -601,7 +600,6 @@ if (defined(ohos_lite)) {
"toys/other/pwdx.c",
"toys/other/readahead.c",
"toys/other/readlink.c",
"toys/other/realpath.c",
"toys/other/reboot.c",
"toys/other/reset.c",
"toys/other/rev.c",
+2 -2
View File
@@ -3,8 +3,8 @@
"Name" : "toybox",
"License" : "BSD Zero Clause License",
"License File" : "LICENSE",
"Version Number" : "0.8.10",
"Owner" : "liu.limin@huawei.com",
"Version Number" : "0.8.12",
"Owner" : "yanmingzhen1@huawei.com",
"Upstream URL" : "http://landley.net/toybox",
"Description" : "Toybox combines common Linux command line utilities together into a single BSD-licensed executable that's simple, small, fast, reasonably standards-compliant, and powerful enough to turn Android into a development environment."
}
+40 -53
View File
@@ -2268,34 +2268,29 @@
#undef FLAG_r
#endif
// netcat ^tElLw#<1W#<1p#<1>65535q#<1O:o:s:f:46uUnz[!tlL][!Lw][!Lu][!46U][!oO] ^tElLw#<1W#<1p#<1>65535q#<1O:o:s:f:46uUnz[!tlL][!Lw][!Lu][!46U][!oO]
// netcat ^tlLw#<1W#<1p#<1>65535q#<1s:f:46uU[!tlL][!Lw][!46U] ^tlLw#<1W#<1p#<1>65535q#<1s:f:46uU[!tlL][!Lw][!46U]
#undef OPTSTR_netcat
#ifdef TOYBOX_OH_ADAPT
/* fix "netcat -u" fail problem */
#define OPTSTR_netcat "^tElLw#<1W#<1p#<1>65535q#<1s:f:46uUn[!tlL][!Lw][!Lu][!46U]"
#else
#define OPTSTR_netcat "^tElLw#<1W#<1p#<1>65535q#<1O:o:s:f:46uUnz[!tlL][!Lw][!Lu][!46U][!oO]"
#define OPTSTR_netcat "^tElLw#<1W#<1p#<1>65535q#<1s:f:46uU[!tlL][!Lw][!46U]"
#endif
#ifdef CLEANUP_netcat
#undef CLEANUP_netcat
#undef FOR_netcat
#undef FLAG_z
#undef FLAG_n
#undef FLAG_U
#undef FLAG_u
#undef FLAG_6
#undef FLAG_4
#undef FLAG_f
#undef FLAG_s
#undef FLAG_o
#undef FLAG_O
#undef FLAG_q
#undef FLAG_p
#undef FLAG_W
#undef FLAG_w
#undef FLAG_L
#undef FLAG_l
#undef FLAG_E
#undef FLAG_t
#endif
@@ -2450,9 +2445,9 @@
#undef FLAG_d
#endif
// patch >2(no-backup-if-mismatch)(dry-run)F#g#fulp#v(verbose)@d:i:Rs(quiet)[!sv] >2(no-backup-if-mismatch)(dry-run)F#g#fulp#v(verbose)@d:i:Rs(quiet)[!sv]
// patch (no-backup-if-mismatch)(dry-run)g#fulp#d:i:Rs(quiet) (no-backup-if-mismatch)(dry-run)xg#fulp#d:i:Rs(quiet)
#undef OPTSTR_patch
#define OPTSTR_patch ">2(no-backup-if-mismatch)(dry-run)F#g#fulp#v(verbose)@d:i:Rs(quiet)[!sv]"
#define OPTSTR_patch "(no-backup-if-mismatch)(dry-run)g#fulp#d:i:Rs(quiet)"
#ifdef CLEANUP_patch
#undef CLEANUP_patch
#undef FOR_patch
@@ -2460,13 +2455,12 @@
#undef FLAG_R
#undef FLAG_i
#undef FLAG_d
#undef FLAG_v
#undef FLAG_p
#undef FLAG_l
#undef FLAG_u
#undef FLAG_f
#undef FLAG_g
#undef FLAG_F
#undef FLAG_x
#undef FLAG_dry_run
#undef FLAG_no_backup_if_mismatch
#endif
@@ -6053,28 +6047,24 @@
#endif
#ifdef FOR_netcat
#define CLEANUP_netcat
#ifndef TT
#define TT this.netcat
#endif
#define FLAG_z (1LL<<0)
#define FLAG_n (1LL<<1)
#define FLAG_U (1LL<<2)
#define FLAG_u (1LL<<3)
#define FLAG_6 (1LL<<4)
#define FLAG_4 (1LL<<5)
#define FLAG_f (1LL<<6)
#define FLAG_s (1LL<<7)
#define FLAG_o (1LL<<8)
#define FLAG_O (1LL<<9)
#define FLAG_q (1LL<<10)
#define FLAG_p (1LL<<11)
#define FLAG_W (1LL<<12)
#define FLAG_w (1LL<<13)
#define FLAG_L (1LL<<14)
#define FLAG_l (1LL<<15)
#define FLAG_E (1LL<<16)
#define FLAG_t (1LL<<17)
#define FLAG_n (1LL<<0)
#define FLAG_U (1LL<<1)
#define FLAG_u (1LL<<2)
#define FLAG_6 (1LL<<3)
#define FLAG_4 (1LL<<4)
#define FLAG_f (1LL<<5)
#define FLAG_s (1LL<<6)
#define FLAG_q (1LL<<7)
#define FLAG_p (1LL<<8)
#define FLAG_W (1LL<<9)
#define FLAG_w (1LL<<10)
#define FLAG_L (1LL<<11)
#define FLAG_l (1LL<<12)
#define FLAG_E (1LL<<13)
#define FLAG_t (1LL<<14)
#endif
#ifdef FOR_netstat
@@ -6217,23 +6207,21 @@
#endif
#ifdef FOR_patch
#define CLEANUP_patch
#ifndef TT
#define TT this.patch
#endif
#define FLAG_s (1LL<<0)
#define FLAG_R (1LL<<1)
#define FLAG_i (1LL<<2)
#define FLAG_d (1LL<<3)
#define FLAG_v (1LL<<4)
#define FLAG_p (1LL<<5)
#define FLAG_l (1LL<<6)
#define FLAG_u (1LL<<7)
#define FLAG_f (1LL<<8)
#define FLAG_g (1LL<<9)
#define FLAG_F (1LL<<10)
#define FLAG_dry_run (1LL<<11)
#define FLAG_no_backup_if_mismatch (1LL<<12)
#define FLAG_s (1<<0)
#define FLAG_R (1<<1)
#define FLAG_i (1<<2)
#define FLAG_d (1<<3)
#define FLAG_p (1<<4)
#define FLAG_l (1<<5)
#define FLAG_u (1<<6)
#define FLAG_f (1<<7)
#define FLAG_g (1<<8)
#define FLAG_x (FORCED_FLAG<<9)
#define FLAG_dry_run (1<<10)
#define FLAG_no_backup_if_mismatch (1<<11)
#endif
#ifdef FOR_pgrep
@@ -6913,18 +6901,17 @@
#endif
#ifdef FOR_sysctl
#define CLEANUP_sysctl
#ifndef TT
#define TT this.sysctl
#endif
#define FLAG_A (1LL<<0)
#define FLAG_a (1LL<<1)
#define FLAG_p (1LL<<2)
#define FLAG_w (1LL<<3)
#define FLAG_q (1LL<<4)
#define FLAG_N (1LL<<5)
#define FLAG_e (1LL<<6)
#define FLAG_n (1LL<<7)
#define FLAG_A (1<<0)
#define FLAG_a (1<<1)
#define FLAG_p (1<<2)
#define FLAG_w (1<<3)
#define FLAG_q (1<<4)
#define FLAG_N (1<<5)
#define FLAG_e (1<<6)
#define FLAG_n (1<<7)
#endif
#ifdef FOR_syslogd
+10 -8
View File
@@ -96,12 +96,11 @@ struct microcom_data {
struct termios old_stdin, old_fd;
};
struct netcat_data {
char *f, *s, *o, *O;
long q, p, W, w;
// toys/net/netcat.c
unsigned ofd, olast, opos, ocount[2];
char obuf[16];
struct netcat_data {
char *f, *s;
long q, p, W, w;
};
struct netstat_data {
@@ -1233,12 +1232,15 @@ struct paste_data {
int files;
};
// toys/posix/patch.c
struct patch_data {
char *i, *d;
long v, p, g, F;
long p, g;
void *current_hunk;
long oldline, oldlen, newline, newlen, linenum, outnum;
struct double_list *current_hunk;
long oldline, oldlen, newline, newlen;
long linenum;
int context, state, filein, fileout, filepatch, hunknum;
char *tempname;
};
+23 -3
View File
@@ -129,7 +129,11 @@
#define HELP_rfkill "usage: rfkill COMMAND [DEVICE]\n\nEnable/disable wireless devices.\n\nCommands:\nlist [DEVICE] List current state\nblock DEVICE Disable device\nunblock DEVICE Enable device\n\nDEVICE is an index number, or one of:\nall, wlan(wifi), bluetooth, uwb(ultrawideband), wimax, wwan, gps, fm."
#ifdef TOYBOX_OH_ADAPT
#define HELP_ping "usage: ping/ping6 [OPTIONS] HOST\n\nCheck network connectivity by sending packets to a host and reporting\nits response.\n\nSend ICMP ECHO_REQUEST packets to ipv4 or ipv6 addresses and prints each\necho it receives back, with round trip time. Returns true if host alive.\n\nOptions:\n-4, -6 Force IPv4 or IPv6\n-c CNT Send CNT many packets (default 3, 0 = infinite)\n-f Flood (print . and \\b to show drops, default -c 15 -i 0.2)\n-i TIME Interval between packets (default 1, need root for < .2)\n-I IFACE/IP Source interface or address\n-m MARK Tag outgoing packets using SO_MARK\n-q Quiet (stops after one returns true if host is alive)\n-s SIZE Data SIZE in bytes (default 56)\n-t TTL Set Time To Live (number of hops)\n-W SEC Seconds to wait for response after last -c packet (default 3)\n-w SEC Exit after this many seconds"
#else
#define HELP_ping "usage: ping [OPTIONS] HOST\n\nCheck network connectivity by sending packets to a host and reporting\nits response.\n\nSend ICMP ECHO_REQUEST packets to ipv4 or ipv6 addresses and prints each\necho it receives back, with round trip time. Returns true if host alive.\n\nOptions:\n-4, -6 Force IPv4 or IPv6\n-c CNT Send CNT many packets (default 3, 0 = infinite)\n-f Flood (print . and \\b to show drops, default -c 15 -i 0.2)\n-i TIME Interval between packets (default 1, need root for < .2)\n-I IFACE/IP Source interface or address\n-m MARK Tag outgoing packets using SO_MARK\n-q Quiet (stops after one returns true if host is alive)\n-s SIZE Data SIZE in bytes (default 56)\n-t TTL Set Time To Live (number of hops)\n-W SEC Seconds to wait for response after last -c packet (default 3)\n-w SEC Exit after this many seconds"
#endif
#define HELP_netstat "usage: netstat [-pWrxwutneal]\n\nDisplay networking information. Default is netstat -tuwx\n\n-r Routing table\n-a All sockets (not just connected)\n-l Listening server sockets\n-t TCP sockets\n-u UDP sockets\n-w Raw sockets\n-x Unix sockets\n-e Extended info\n-n Don't resolve names\n-W Wide display\n-p Show PID/program name of sockets"
@@ -137,7 +141,7 @@
/* Remove unsupported -f and -t option */
#define HELP_netcat "usage: netcat [-46ELUl] [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|COMMAND...}\n\nForward stdin/stdout to a file or network connection.\n\n-4 Force IPv4\n-6 Force IPv6\n-E Forward stderr\n-L Listen and background each incoming connection (server mode)\n-U Use a UNIX domain socket\n-W SECONDS timeout for more data on an idle connection\n-l Listen for one incoming connection, then exit\n-p Local port number\n-q Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet\n-s Local source address\n-u Use UDP\n-w SECONDS timeout to establish connection\n\nWhen listening the COMMAND line is executed as a child process to handle\nan incoming connection. With no COMMAND -l forwards the connection\nto stdin/stdout. If no -p specified, -l prints the port it bound to and\nbackgrounds itself (returning immediately).\n\nFor a quick-and-dirty server, try something like:\nnetcat -s 127.0.0.1 -p 1234 -tL sh -l"
#else
#define HELP_netcat "usage: netcat [-46ELlntUu] [-pqWw #] [-s addr] [-o FILE] {IPADDR PORTNUM|-f FILENAME|COMMAND...}\n\nForward stdin/stdout to a file or network connection.\n\n-4 Force IPv4\n-6 Force IPv6\n-E Forward stderr\n-f Use FILENAME (ala /dev/ttyS0) instead of network\n-L Listen and background each incoming connection (server mode)\n-l Listen for one incoming connection, then exit\n-n No DNS lookup\n-o Hex dump to FILE (show packets, -o- writes hex only to stdout)\n-O Hex dump to FILE (streaming mode)\n-p Local port number\n-q Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet\n-s Local source address\n-t Allocate tty\n-u Use UDP\n-U Use a UNIX domain socket\n-W SECONDS timeout for more data on an idle connection\n-w SECONDS timeout to establish connection\n-z zero-I/O mode [used for scanning]\n\nWhen listening the COMMAND line is executed as a child process to handle\nan incoming connection. With no COMMAND -l forwards the connection\nto stdin/stdout. If no -p specified, -l prints the port it bound to and\nbackgrounds itself (returning immediately).\n\nFor a quick-and-dirty server, try something like:\nnetcat -s 127.0.0.1 -p 1234 -tL sh -l\n\nOr use \"stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho\" with\nnetcat -f to connect to a serial port."
#define HELP_netcat "usage: netcat [-46ELUlt] [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME|COMMAND...}\n\nForward stdin/stdout to a file or network connection.\n\n-4 Force IPv4\n-6 Force IPv6\n-E Forward stderr\n-L Listen and background each incoming connection (server mode)\n-U Use a UNIX domain socket\n-W SECONDS timeout for more data on an idle connection\n-f Use FILENAME (ala /dev/ttyS0) instead of network\n-l Listen for one incoming connection, then exit\n-p Local port number\n-q Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet\n-s Local source address\n-t Allocate tty\n-u Use UDP\n-w SECONDS timeout to establish connection\n\nUse \"stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho\" with\nnetcat -f to connect to a serial port.\n\nWhen listening the COMMAND line is executed as a child process to handle\nan incoming connection. With no COMMAND -l forwards the connection\nto stdin/stdout. If no -p specified, -l prints the port it bound to and\nbackgrounds itself (returning immediately).\n\nFor a quick-and-dirty server, try something like:\nnetcat -s 127.0.0.1 -p 1234 -tL sh -l"
#endif
#define HELP_microcom "usage: microcom [-s SPEED] [-X] DEVICE\n\nSimple serial console. Hit CTRL-] for menu.\n\n-s Set baud rate to SPEED\n-X Ignore ^] menu escape"
@@ -150,7 +154,11 @@
#define HELP_ftpput "An ftpget that defaults to -s instead of -g"
#ifdef TOYBOX_OH_ADAPT
#define HELP_ftpget "usage: ftpget/ftpput [-cvgslLmMdD] [-P PORT] [-p PASSWORD] [-u USER] HOST [LOCAL] REMOTE\n\nTalk to ftp server. By default get REMOTE file via passive anonymous\ntransfer, optionally saving under a LOCAL name. Can also send, list, etc.\n\n-c Continue partial transfer\n-p Use PORT instead of \"21\"\n-P Use PASSWORD instead of \"ftpget@\"\n-u Use USER instead of \"anonymous\"\n-v Verbose\n\nWays to interact with FTP server:\n-d Delete file\n-D Remove directory\n-g Get file (default)\n-l List directory\n-L List (filenames only)\n-m Move file on server from LOCAL to REMOTE\n-M mkdir\n-s Send file"
#else
#define HELP_ftpget "usage: ftpget [-cvgslLmMdD] [-P PORT] [-p PASSWORD] [-u USER] HOST [LOCAL] REMOTE\n\nTalk to ftp server. By default get REMOTE file via passive anonymous\ntransfer, optionally saving under a LOCAL name. Can also send, list, etc.\n\n-c Continue partial transfer\n-p Use PORT instead of \"21\"\n-P Use PASSWORD instead of \"ftpget@\"\n-u Use USER instead of \"anonymous\"\n-v Verbose\n\nWays to interact with FTP server:\n-d Delete file\n-D Remove directory\n-g Get file (default)\n-l List directory\n-L List (filenames only)\n-m Move file on server from LOCAL to REMOTE\n-M mkdir\n-s Send file"
#endif
#define HELP_yes "usage: yes [args...]\n\nRepeatedly output line until killed. If no args, output 'y'."
@@ -188,7 +196,7 @@
#define HELP_tac "usage: tac [FILE...]\n\nOutput lines in reverse order."
#define HELP_sysctl "usage: sysctl [-aeNnqw] [-p [FILE] | KEY[=VALUE]...]\n\nRead/write system control data (under /proc/sys).\n\n-a Show all values\n-e Don't warn about unknown keys\n-N Don't print key values\n-n Don't print key names\n-p Read values from FILE (default /etc/sysctl.conf)\n-q Don't show value after write\n-w Only write values (object to reading)"
#define HELP_sysctl "usage: sysctl [-aAeNnqw] [-p [FILE] | KEY[=VALUE]...]\n\nRead/write system control data (under /proc/sys).\n\n-a,A Show all values\n-e Don't warn about unknown keys\n-N Don't print key values\n-n Don't print key names\n-p Read values from FILE (default /etc/sysctl.conf)\n-q Don't show value after write\n-w Only write values (object to reading)"
#define HELP_switch_root "usage: switch_root [-c /dev/console] NEW_ROOT NEW_INIT...\n\nUse from PID 1 under initramfs to free initramfs, chroot to NEW_ROOT,\nand exec NEW_INIT.\n\n-c Redirect console to device in NEW_ROOT\n-h Hang instead of exiting on failure (avoids kernel panic)"
@@ -386,7 +394,11 @@
#define HELP_useradd "usage: useradd [-SDH] [-h DIR] [-s SHELL] [-G GRP] [-g NAME] [-u UID] USER [GROUP]\n\nCreate new user, or add USER to GROUP\n\n-D Don't assign a password\n-g NAME Real name\n-G GRP Add user to existing group\n-h DIR Home directory\n-H Don't create home directory\n-s SHELL Login shell\n-S Create a system user\n-u UID User id"
#ifdef TOYBOX_OH_ADAPT
#define HELP_traceroute "usage: traceroute [-46FUIldnvr] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES]\n[-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE] [-z PAUSE_MSEC] HOST [BYTES]\n\nusage: traceroute6 [-dnrv] [-m MAXTTL] [-p PORT] [-q PROBES][-s SRC_IP] [-t TOS] [-w WAIT_SEC]\n [-i IFACE] HOST [BYTES]\n\nTrace the route to HOST\n\n-4,-6 Force IP or IPv6 name resolution\n-F Set the don't fragment bit (supports IPV4 only)\n-U Use UDP datagrams instead of ICMP ECHO (supports IPV4 only)\n-I Use ICMP ECHO instead of UDP datagrams (supports IPV4 only)\n-l Display the TTL value of the returned packet (supports IPV4 only)\n-d Set SO_DEBUG options to socket\n-n Print numeric addresses\n-v verbose\n-r Bypass routing tables, send directly to HOST\n-m Max time-to-live (max number of hops)(RANGE 1 to 255)\n-p Base UDP port number used in probes(default 33434)(RANGE 1 to 65535)\n-q Number of probes per TTL (default 3)(RANGE 1 to 255)\n-s IP address to use as the source address\n-t Type-of-service in probe packets (default 0)(RANGE 0 to 255)\n-w Time in seconds to wait for a response (default 3)(RANGE 0 to 86400)\n-g Loose source route gateway (8 max) (supports IPV4 only)\n-z Pause Time in ms (default 0)(RANGE 0 to 86400) (supports IPV4 only)\n-f Start from the 1ST_TTL hop (instead from 1)(RANGE 1 to 255) (supports IPV4 only)\n-i Specify a network interface to operate with"
#else
#define HELP_traceroute "usage: traceroute [-46FUIldnvr] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES]\n[-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE] [-z PAUSE_MSEC] HOST [BYTES]\n\ntraceroute6 [-dnrv] [-m MAXTTL] [-p PORT] [-q PROBES][-s SRC_IP] [-t TOS] [-w WAIT_SEC]\n [-i IFACE] HOST [BYTES]\n\nTrace the route to HOST\n\n-4,-6 Force IP or IPv6 name resolution\n-F Set the don't fragment bit (supports IPV4 only)\n-U Use UDP datagrams instead of ICMP ECHO (supports IPV4 only)\n-I Use ICMP ECHO instead of UDP datagrams (supports IPV4 only)\n-l Display the TTL value of the returned packet (supports IPV4 only)\n-d Set SO_DEBUG options to socket\n-n Print numeric addresses\n-v verbose\n-r Bypass routing tables, send directly to HOST\n-m Max time-to-live (max number of hops)(RANGE 1 to 255)\n-p Base UDP port number used in probes(default 33434)(RANGE 1 to 65535)\n-q Number of probes per TTL (default 3)(RANGE 1 to 255)\n-s IP address to use as the source address\n-t Type-of-service in probe packets (default 0)(RANGE 0 to 255)\n-w Time in seconds to wait for a response (default 3)(RANGE 0 to 86400)\n-g Loose source route gateway (8 max) (supports IPV4 only)\n-z Pause Time in ms (default 0)(RANGE 0 to 86400) (supports IPV4 only)\n-f Start from the 1ST_TTL hop (instead from 1)(RANGE 1 to 255) (supports IPV4 only)\n-i Specify a network interface to operate with"
#endif
#define HELP_tr "usage: tr [-cdst] SET1 [SET2]\n\nTranslate, squeeze, or delete characters from stdin, writing to stdout\n\n-c/-C Take complement of SET1\n-d Delete input characters coded SET1\n-s Squeeze multiple output characters of SET2 into one character\n-t Truncate SET1 to length of SET2"
@@ -551,7 +563,11 @@
#define HELP_arch "usage: arch\n\nPrint machine (hardware) name, same as uname -m."
#ifdef TOYBOX_OH_ADAPT
#define HELP_ulimit "usage: ulimit/prlimit [-P PID] [-SHRacdefilmnpqrstuv] [LIMIT]\n\nPrint or set resource limits for process number PID. If no LIMIT specified\n(or read-only -ap selected) display current value (sizes in bytes).\nDefault is ulimit -P $PPID -Sf\" (show soft filesize of your shell).\n\n-P PID to affect (default $PPID) -a Show all limits\n-S Set/show soft limit -H Set/show hard (maximum) limit\n\n-c Core file size (blocks) -d Process data segment (KiB)\n-e Max scheduling priority -f File size (KiB)\n-i Pending signal count -l Locked memory (KiB)\n-m Resident Set Size (KiB) -n Number of open files\n-p Pipe buffer (512 bytes) -q POSIX message queues\n-r Max realtime priority -R Realtime latency (us)\n-s Stack size (KiB) -t Total CPU time (s)\n-u Maximum processes (this UID) -v Virtual memory size (KiB)"
#else
#define HELP_ulimit "usage: ulimit [-P PID] [-SHRacdefilmnpqrstuv] [LIMIT]\n\nPrint or set resource limits for process number PID. If no LIMIT specified\n(or read-only -ap selected) display current value (sizes in bytes).\nDefault is ulimit -P $PPID -Sf\" (show soft filesize of your shell).\n\n-P PID to affect (default $PPID) -a Show all limits\n-S Set/show soft limit -H Set/show hard (maximum) limit\n\n-c Core file size (blocks) -d Process data segment (KiB)\n-e Max scheduling priority -f File size (KiB)\n-i Pending signal count -l Locked memory (KiB)\n-m Resident Set Size (KiB) -n Number of open files\n-p Pipe buffer (512 bytes) -q POSIX message queues\n-r Max realtime priority -R Realtime latency (us)\n-s Stack size (KiB) -t Total CPU time (s)\n-u Maximum processes (this UID) -v Virtual memory size (KiB)"
#endif
#define HELP_tty "usage: tty [-s]\n\nShow filename of terminal connected to stdin. If none print \"not a tty\"\nand exit with nonzero status.\n\n-s Silent, exit code only"
@@ -601,7 +617,7 @@
#define HELP_printf "usage: printf FORMAT [ARGUMENT...]\n\nFormat and print ARGUMENT(s) according to FORMAT, using C printf syntax\n(% escapes for cdeEfgGiosuxX, \\ escapes for abefnrtv0 or \\OCTAL or \\xHEX)."
#define HELP_patch "usage: patch [-Rlsuv] [-d DIR] [-i FILE] [-p DEPTH] [-F FUZZ] [--dry-run] [FILE [PATCH]]\n\nApply a unified diff to one or more files.\n\n-d Modify files in DIR\n-F Fuzz factor (number of non-matching context lines allowed per hunk)\n-i Input patch from FILE (default=stdin)\n-l Loose match (ignore whitespace)\n-p Number of '/' to strip from start of file paths (default=all)\n-R Reverse patch\n-s Silent except for errors\n-v Verbose (-vv to see decisions)\n--dry-run Don't change files, just confirm patch applies\n\nOnly handles \"unified\" diff format (-u is assumed and ignored). Only\nmodifies files when all hunks to that file apply. Prints failed hunks\nto stderr, and exits with nonzero status if any hunks fail.\n\nFiles compared against /dev/null (or with a date <= the unix epoch) are\ncreated/deleted as appropriate. Default -F value is the number of\nleading/trailing context lines minus one (usually 2)."
#define HELP_patch "usage: patch [-d DIR] [-i file] [-p depth] [-Rlsu] [--dry-run]\n\nApply a unified diff to one or more files.\n\n-d Modify files in DIR\n-i Input file (default=stdin)\n-l Loose match (ignore whitespace)\n-p Number of '/' to strip from start of file paths (default=all)\n-R Reverse patch\n-s Silent except for errors\n-u Ignored (only handles \"unified\" diffs)\n--dry-run Don't change files, just confirm patch applies\n\nThis version of patch only handles unified diffs, and only modifies\na file when all hunks to that file apply. Patch prints failed hunks\nto stderr, and exits with nonzero status if any hunks fail.\n\nA file compared against /dev/null (or with a date <= the epoch) is\ncreated/deleted as appropriate."
#define HELP_paste "usage: paste [-s] [-d DELIMITERS] [FILE...]\n\nMerge corresponding lines from each input file.\n\n-d List of delimiter characters to separate fields with (default is \\t)\n-s Sequential mode: turn each input file into one line of output"
@@ -635,7 +651,11 @@
#define HELP_whoami "usage: whoami\n\nPrint the current user name."
#ifdef TOYBOX_OH_ADAPT
#define HELP_logname "usage: logname/whoami\n\nPrint the current user name."
#else
#define HELP_logname "usage: logname\n\nPrint the current user name."
#endif
#define HELP_groups "usage: groups [user]\n\nPrint the groups a user is in."
+2 -2
View File
@@ -186,7 +186,7 @@ USE_MV(NEWTOY(mv, "<1x(swap)v(verbose)nF(remove-destination)fit:T[-ni]", TOYFLAG
USE_NBD_CLIENT(OLDTOY(nbd-client, nbd_client, TOYFLAG_USR|TOYFLAG_BIN))
USE_NBD_CLIENT(NEWTOY(nbd_client, "<3>3b#<1>4294967295=4096ns", 0))
USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN))
USE_NETCAT(NEWTOY(netcat, "^tElLw#<1W#<1p#<1>65535q#<1O:o:s:f:46uUnz[!tlL][!Lw][!Lu][!46U][!oO]", TOYFLAG_BIN))
USE_NETCAT(NEWTOY(netcat, "^tElLw#<1W#<1p#<1>65535q#<1s:f:46uUn[!tlL][!Lw][!Lu][!46U]", TOYFLAG_BIN))
USE_NETSTAT(NEWTOY(netstat, "pWrxwutneal", TOYFLAG_BIN))
USE_NICE(NEWTOY(nice, "^<1n#", TOYFLAG_BIN))
USE_NL(NEWTOY(nl, "v#=1l#w#<0=6b:n:s:E", TOYFLAG_USR|TOYFLAG_BIN))
@@ -199,7 +199,7 @@ USE_OPENVT(NEWTOY(openvt, "^<1c#<1>63sw", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
USE_PARTPROBE(NEWTOY(partprobe, "<1", TOYFLAG_SBIN))
USE_PASSWD(NEWTOY(passwd, ">1a:dlu", TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
USE_PASTE(NEWTOY(paste, "d:s", TOYFLAG_USR|TOYFLAG_BIN))
USE_PATCH(NEWTOY(patch, ">2(no-backup-if-mismatch)(dry-run)F#g#fulp#v(verbose)@d:i:Rs(quiet)[!sv]", TOYFLAG_USR|TOYFLAG_BIN))
USE_PATCH(NEWTOY(patch, "(no-backup-if-mismatch)(dry-run)"USE_TOYBOX_DEBUG("x")"g#fulp#d:i:Rs(quiet)", TOYFLAG_USR|TOYFLAG_BIN))
USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
USE_PIDOF(NEWTOY(pidof, "so:x", TOYFLAG_BIN))
#ifdef TOYBOX_OH_ADAPT
+19
View File
@@ -1565,3 +1565,22 @@ int smemcmp(char *one, char *two, unsigned long len)
return ii;
}
// Slow, but small.
char *get_line(int fd)
{
char c, *buf = NULL;
long len = 0;
for (;;) {
if (1>read(fd, &c, 1)) break;
if (!(len & 63)) buf=xrealloc(buf, len+65);
if ((buf[len++]=c) == '\n') break;
}
if (buf) {
buf[len]=0;
if (buf[--len]=='\n') buf[len]=0;
}
return buf;
}
+3 -3
View File
@@ -362,9 +362,7 @@ void xconnect(int fd, const struct sockaddr *sa, socklen_t len);
int xconnectany(struct addrinfo *ai);
int xbindany(struct addrinfo *ai);
int xpoll(struct pollfd *fds, int nfds, int timeout);
int pollinate(int in1, int in2, int out1, int out2,
void (*callback)(int fd, void *buf, size_t len),
int timeout, int shutdown_timeout);
int pollinate(int in1, int in2, int out1, int out2, int timeout, int shutdown_timeout);
char *ntop(struct sockaddr *sa);
void xsendto(int sockfd, void *buf, size_t len, struct sockaddr *dest);
int xrecvwait(int fd, char *buf, int len, union socksaddr *sa, int timeout);
@@ -441,3 +439,5 @@ pid_t __attribute__((returns_twice)) xvforkwrap(pid_t pid);
#define minof(a, b) ({typeof(a) aa = (a); typeof(b) bb = (b); aa<bb ? aa : bb;})
#define maxof(a, b) ({typeof(a) aa = (a); typeof(b) bb = (b); aa>bb ? aa : bb;})
char *get_line(int fd);
+2 -7
View File
@@ -96,9 +96,7 @@ int xpoll(struct pollfd *fds, int nfds, int timeout)
// Loop forwarding data from in1 to out1 and in2 to out2, handling
// half-connection shutdown. timeouts return if no data for X ms.
// Returns 0: both closed, 1 shutdown_timeout, 2 timeout
int pollinate(int in1, int in2, int out1, int out2,
void (*callback)(int fd, void *buf, size_t len),
int timeout, int shutdown_timeout)
int pollinate(int in1, int in2, int out1, int out2, int timeout, int shutdown_timeout)
{
struct pollfd pollfds[2];
int i, pollcount = 2;
@@ -116,10 +114,7 @@ int pollinate(int in1, int in2, int out1, int out2,
if (pollfds[i].revents & POLLIN) {
int len = read(pollfds[i].fd, libbuf, sizeof(libbuf));
if (len<1) pollfds[i].revents = POLLHUP;
else {
callback(i ? out2 : out1, libbuf, len);
continue;
}
else xwrite(i ? out2 : out1, libbuf, len);
}
if (pollfds[i].revents & POLLHUP) {
// Close half-connection. This is needed for things like
+3
View File
@@ -95,10 +95,13 @@ void show_help(FILE *out, int flags)
ZHELP_LEN);
else hd = (void *)help_data;
#ifdef TOYBOX_OH_ADAPT
#else
if (flags & HELP_HEADER)
fprintf(out, "Toybox %s"USE_TOYBOX(" multicall binary")"%s\n\n",
toybox_version, (CFG_TOYBOX && i) ? " (see toybox --help)"
: " (see https://landley.net/toybox)");
#endif
for (;;) {
s = (void *)help_data;
-201
View File
@@ -1,201 +0,0 @@
/* netcat.c - Forward stdin/stdout to a file or network connection.
*
* Copyright 2007 Rob Landley <rob@landley.net>
*
* TODO: genericize for telnet/microcom/tail-f, fix -t
USE_NETCAT(NEWTOY(netcat, "^tElLw#<1W#<1p#<1>65535q#<1s:f:46uUn[!tlL][!Lw][!Lu][!46U]", TOYFLAG_BIN))
USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN))
config NETCAT
bool "netcat"
default y
help
usage: netcat [-46ElLtUu] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME|COMMAND...}
Forward stdin/stdout to a file or network connection.
-4 Force IPv4
-6 Force IPv6
-E Forward stderr
-f Use FILENAME (ala /dev/ttyS0) instead of network
-l Listen for one incoming connection, then exit
-L Listen and background each incoming connection (server mode)
-n No DNS lookup
-p Local port number
-q Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet
-s Local source address
-t Allocate tty
-u Use UDP
-U Use a UNIX domain socket
-w SECONDS timeout to establish connection
-W SECONDS timeout for more data on an idle connection
When listening the COMMAND line is executed as a child process to handle
an incoming connection. With no COMMAND -l forwards the connection
to stdin/stdout. If no -p specified, -l prints the port it bound to and
backgrounds itself (returning immediately).
For a quick-and-dirty server, try something like:
netcat -s 127.0.0.1 -p 1234 -tL sh -l
Or use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with
netcat -f to connect to a serial port.
*/
#define FOR_netcat
#include "toys.h"
GLOBALS(
char *f, *s;
long q, p, W, w;
)
static void timeout(int signum)
{
if (TT.w) error_exit("Timeout");
xexit();
}
// open AF_UNIX socket
static int usock(char *name, int type, int out)
{
int sockfd;
struct sockaddr_un sockaddr;
memset(&sockaddr, 0, sizeof(struct sockaddr_un));
if (strlen(name) + 1 > sizeof(sockaddr.sun_path))
error_exit("socket path too long %s", name);
strcpy(sockaddr.sun_path, name);
sockaddr.sun_family = AF_UNIX;
sockfd = xsocket(AF_UNIX, type, 0);
(out?xconnect:xbind)(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
return sockfd;
}
void netcat_main(void)
{
#ifdef TOYBOX_OH_ADAPT
/* Remove unsupported -f and -t option */
if (toys.optflags & FLAG_t) error_exit("Unknown option 't'");
if (toys.optflags & FLAG_f) error_exit("Unknown option 'f'");
#endif
int sockfd = -1, in1 = 0, in2 = 0, out1 = 1, out2 = 1, family = AF_UNSPEC,
type = FLAG(u) ? SOCK_DGRAM : SOCK_STREAM;
socklen_t len;
pid_t child;
// Addjust idle and quit_delay to ms or -1 for no timeout
TT.W = TT.W ? TT.W*1000 : -1;
TT.q = TT.q ? TT.q*1000 : -1;
xsignal(SIGCHLD, SIG_IGN);
if (TT.w) {
xsignal(SIGALRM, timeout);
alarm(TT.w);
}
// The argument parsing logic can't make "<2" conditional on other
// arguments like -f and -l, so do it by hand here.
if (FLAG(f) ? toys.optc : (!FLAG(l) && !FLAG(L) && toys.optc!=2-FLAG(U)))
help_exit("bad argument count");
if (FLAG(4)) family = AF_INET;
else if (FLAG(6)) family = AF_INET6;
else if (FLAG(U)) family = AF_UNIX;
if (TT.f) in1 = out2 = xopen(TT.f, O_RDWR);
else {
// Setup socket
if (!FLAG(l) && !FLAG(L)) {
if (FLAG(U)) sockfd = usock(toys.optargs[0], type, 1);
else sockfd = xconnectany(xgetaddrinfo(toys.optargs[0],
toys.optargs[1], family, type, 0, AI_NUMERICHOST*FLAG(n)));
// We have a connection. Disarm timeout and start poll/send loop.
alarm(0);
in1 = out2 = sockfd;
pollinate(in1, in2, out1, out2, TT.W, TT.q);
} else {
// Listen for incoming connections
if (FLAG(U)) {
if (!FLAG(s)) error_exit("-s must be provided if using -U with -L/-l");
sockfd = usock(TT.s, type, 0);
} else {
sprintf(toybuf, "%ld", TT.p);
sockfd = xbindany(xgetaddrinfo(TT.s, toybuf, family, type, 0, 0));
}
if (!FLAG(u) && listen(sockfd, 5)) perror_exit("listen");
if (!TT.p && !FLAG(U)) {
struct sockaddr* address = (void*)toybuf;
short port_be;
len = sizeof(struct sockaddr_storage);
getsockname(sockfd, address, &len);
if (address->sa_family == AF_INET)
port_be = ((struct sockaddr_in*)address)->sin_port;
else if (address->sa_family == AF_INET6)
port_be = ((struct sockaddr_in6*)address)->sin6_port;
else perror_exit("getsockname: bad family");
dprintf(1, "%d\n", SWAP_BE16(port_be));
// Return immediately if no -p and -Ll has arguments, so wrapper
// script can use port number.
if (CFG_TOYBOX_FORK && toys.optc && xfork()) goto cleanup;
}
do {
len = sizeof(struct sockaddr_storage);
if (FLAG(u)) {
if (-1 == recvfrom(in1 = dup(sockfd), &child, 1, MSG_PEEK,
(void *)toybuf, &len)) perror_exit("recvfrom");
} else if ((in1 = accept(sockfd, 0, 0))<0) perror_exit("accept");
out2 = in1;
child = 0;
// We have a connection. Disarm timeout.
alarm(0);
// Fork a child as necessary. Parent cleans up and continues here.
if (toys.optc && FLAG(L)) NOEXIT(child = XVFORK());
if (child) {
close(in1);
continue;
}
if (FLAG(u))
xconnect(in1, (void *)toybuf, sizeof(struct sockaddr_storage));
// Cleanup and redirect for exec
if (toys.optc) {
// Do we need a tty?
// TODO nommu and -t only affects server mode...
// if (FLAG(t)) child = forkpty(&fdout, NULL, NULL, NULL);
close(sockfd);
dup2(in1, 0);
dup2(in1, 1);
if (FLAG(E)) dup2(in1, 2);
if (in1>2) close(in1);
xexec(toys.optargs);
// Copy stdin/out
} else {
pollinate(in1, in2, out1, out2, TT.W, TT.q);
close(in1);
}
} while (FLAG(L));
}
}
cleanup:
if (CFG_TOYBOX_FREE) {
close(in1);
close(sockfd);
}
}
+43
View File
@@ -0,0 +1,43 @@
/* readlink.c - Return string representation of a symbolic link.
*
* Copyright 2007 Rob Landley <rob@landley.net>
USE_READLINK(NEWTOY(readlink, "<1nqmef(canonicalize)[-mef]", TOYFLAG_USR|TOYFLAG_BIN))
config READLINK
bool "readlink"
default y
help
usage: readlink FILE...
With no options, show what symlink points to, return error if not symlink.
Options for producing canonical paths (all symlinks/./.. resolved):
-e Canonical path to existing entry (fail if missing)
-f Full path (fail if directory missing)
-m Ignore missing entries, show where it would be
-n No trailing newline
-q Quiet (no output, just error code)
*/
#define FOR_readlink
#include "toys.h"
void readlink_main(void)
{
char **arg, *s;
for (arg = toys.optargs; *arg; arg++) {
// Calculating full canonical path?
// Take advantage of flag positions to calculate m = -1, f = 0, e = 1
if (toys.optflags & (FLAG_f|FLAG_e|FLAG_m))
s = xabspath(*arg, (toys.optflags&(FLAG_f|FLAG_e))-1);
else s = xreadlink(*arg);
if (s) {
if (!FLAG(q)) xprintf(FLAG(n) ? "%s" : "%s\n", s);
if (CFG_TOYBOX_FREE) free(s);
} else toys.exitval = 1;
}
}
-155
View File
@@ -1,155 +0,0 @@
/* sysctl.c - A utility to read and manipulate the sysctl parameters.
*
* Copyright 2014 Bilal Qureshi <bilal.jmi@gmail.com>
* Copyright 2014 Kyungwan Han <asura321@gmail.com>
*
* No Standard
USE_SYSCTL(NEWTOY(sysctl, "^neNqwpaA[!ap][!aq][!aw][+aA]", TOYFLAG_SBIN))
config SYSCTL
bool "sysctl"
default y
help
usage: sysctl [-aAeNnqw] [-p [FILE] | KEY[=VALUE]...]
Read/write system control data (under /proc/sys).
-a,A Show all values
-e Don't warn about unknown keys
-N Don't print key values
-n Don't print key names
-p Read values from FILE (default /etc/sysctl.conf)
-q Don't show value after write
-w Only write values (object to reading)
*/
#define FOR_sysctl
#include "toys.h"
// Null terminate at =, return value
static char *split_key(char *key)
{
char *value = strchr(key, '=');
if (value) *(value++)=0;
return value;
}
static void replace_char(char *str, char old, char new)
{
for (; *str; str++) if (*str == old) *str = new;
}
static void key_error(char *key)
{
if (errno == ENOENT) {
if (!(toys.optflags & FLAG_e)) error_msg("unknown key '%s'", key);
} else perror_msg("key '%s'", key);
}
static int write_key(char *path, char *key, char *value)
{
int fd = open(path, O_WRONLY);
if (fd < 0) {
key_error(key);
return 0;
}
xwrite(fd, value, strlen(value));
xclose(fd);
return 1;
}
// Display all keys under a path
static int do_show_keys(struct dirtree *dt)
{
char *path, *data, *key;
if (!dirtree_notdotdot(dt)) return 0; // Skip . and ..
if (S_ISDIR(dt->st.st_mode)) return DIRTREE_RECURSE;
path = dirtree_path(dt, 0);
data = readfile(path, 0, 0);
replace_char(key = path + 10, '/', '.'); // skip "/proc/sys/"
if (!data) key_error(key);
else {
// Print the parts that aren't switched off by flags.
if (!(toys.optflags & FLAG_n)) xprintf("%s", key);
if (!(toys.optflags & (FLAG_N|FLAG_n))) xprintf(" = ");
for (key = data+strlen(data); key > data && isspace(*--key); *key = 0);
if (!(toys.optflags & FLAG_N)) xprintf("%s", data);
if ((toys.optflags & (FLAG_N|FLAG_n)) != (FLAG_N|FLAG_n)) xputc('\n');
}
free(data);
free(path);
return 0;
}
// Read/write entries under a key. Accepts "key=value" in key if !value
static void process_key(char *key, char *value)
{
char *path;
if (!value) value = split_key(key);
if ((toys.optflags & FLAG_w) && !value) {
error_msg("'%s' not key=value", key);
return;
}
path = xmprintf("/proc/sys/%s", key);
replace_char(path, '.', '/');
// Note: failure to assign to a non-leaf node suppresses the display.
if (!(value && (!write_key(path, key, value) || (toys.optflags & FLAG_q)))) {
if (!access(path, R_OK)) dirtree_read(path, do_show_keys);
else key_error(key);
}
free(path);
}
void sysctl_main()
{
char **args = 0;
// Display all keys
if (toys.optflags & FLAG_a) dirtree_read("/proc/sys", do_show_keys);
// read file
else if (toys.optflags & FLAG_p) {
FILE *fp = xfopen(*toys.optargs ? *toys.optargs : "/etc/sysctl.conf", "r");
size_t len;
for (;;) {
char *line = 0, *key, *val;
if (-1 == (len = getline(&line, &len, fp))) break;
key = line;
while (isspace(*key)) key++;
if (*key == '#' || *key == ';' || !*key) continue;
while (len && isspace(line[len-1])) line[--len] = 0;
if (!(val = split_key(line))) {
error_msg("'%s' not key=value", line);
continue;
}
// Trim whitespace around =
len = (val-line)-1;
while (len && isspace(line[len-1])) line[--len] = 0;
while (isspace(*val)) val++;;
process_key(key, val);
free(line);
}
fclose(fp);
// Loop through arguments, displaying or assigning as appropriate
} else {
if (!*toys.optargs) help_exit("Needs 1 arg");
for (args = toys.optargs; *args; args++) process_key(*args, 0);
}
}
-449
View File
@@ -1,449 +0,0 @@
/* patch.c - Apply a "universal" diff.
*
* Copyright 2007 Rob Landley <rob@landley.net>
*
* see http://opengroup.org/onlinepubs/9699919799/utilities/patch.html
* (But only does -u, because who still cares about "ed"?)
*
* TODO:
* -b backup
* -N ignore already applied
* -d chdir first
* -D define wrap #ifdef and #ifndef around changes
* -o outfile output here instead of in place
* -r rejectfile write rejected hunks to this file
*
* -E remove empty files --remove-empty-files
* -F fuzz (number, default 2)
* [file] which file to patch
USE_PATCH(NEWTOY(patch, "(no-backup-if-mismatch)(dry-run)"USE_TOYBOX_DEBUG("x")"g#fulp#d:i:Rs(quiet)", TOYFLAG_USR|TOYFLAG_BIN))
config PATCH
bool "patch"
default y
help
usage: patch [-d DIR] [-i file] [-p depth] [-Rlsu] [--dry-run]
Apply a unified diff to one or more files.
-d Modify files in DIR
-i Input file (default=stdin)
-l Loose match (ignore whitespace)
-p Number of '/' to strip from start of file paths (default=all)
-R Reverse patch
-s Silent except for errors
-u Ignored (only handles "unified" diffs)
--dry-run Don't change files, just confirm patch applies
This version of patch only handles unified diffs, and only modifies
a file when all hunks to that file apply. Patch prints failed hunks
to stderr, and exits with nonzero status if any hunks fail.
A file compared against /dev/null (or with a date <= the epoch) is
created/deleted as appropriate.
*/
#define FOR_patch
#include "toys.h"
GLOBALS(
char *i, *d;
long p, g;
struct double_list *current_hunk;
long oldline, oldlen, newline, newlen;
long linenum;
int context, state, filein, fileout, filepatch, hunknum;
char *tempname;
)
// Dispose of a line of input, either by writing it out or discarding it.
// state < 2: just free
// state = 2: write whole line to stderr
// state = 3: write whole line to fileout
// state > 3: write line+1 to fileout when *line != state
static void do_line(void *data)
{
struct double_list *dlist = (struct double_list *)data;
if (TT.state>1 && *dlist->data != TT.state) {
char *s = dlist->data+(TT.state>3);
int i = TT.state == 2 ? 2 : TT.fileout;
xwrite(i, s, strlen(s));
xwrite(i, "\n", 1);
}
if (FLAG(x)) fprintf(stderr, "DO %d: %s\n", TT.state, dlist->data);
free(dlist->data);
free(data);
}
static void finish_oldfile(void)
{
if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname);
TT.fileout = TT.filein = -1;
}
static void fail_hunk(void)
{
if (!TT.current_hunk) return;
fprintf(stderr, "Hunk %d FAILED %ld/%ld.\n",
TT.hunknum, TT.oldline, TT.newline);
toys.exitval = 1;
// If we got to this point, we've seeked to the end. Discard changes to
// this file and advance to next file.
TT.state = 2;
llist_traverse(TT.current_hunk, do_line);
TT.current_hunk = NULL;
if (!FLAG(dry_run)) delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
TT.state = 0;
}
// Compare ignoring whitespace. Just returns 0/1, no > or <
static int loosecmp(char *aa, char *bb)
{
int a = 0, b = 0;
for (;;) {
while (isspace(aa[a])) a++;
while (isspace(bb[b])) b++;
if (aa[a] != bb[b]) return 1;
if (!aa[a]) return 0;
a++, b++;
}
}
// Given a hunk of a unified diff, make the appropriate change to the file.
// This does not use the location information, but instead treats a hunk
// as a sort of regex. Copies data from input to output until it finds
// the change to be made, then outputs the changed data and returns.
// (Finding EOF first is an error.) This is a single pass operation, so
// multiple hunks must occur in order in the file.
static int apply_one_hunk(void)
{
struct double_list *plist, *buf = NULL, *check;
int matcheof, trailing = 0, reverse = FLAG(R), backwarn = 0;
int (*lcmp)(char *aa, char *bb);
lcmp = FLAG(l) ? (void *)loosecmp : (void *)strcmp;
dlist_terminate(TT.current_hunk);
// Match EOF if there aren't as many ending context lines as beginning
for (plist = TT.current_hunk; plist; plist = plist->next) {
if (plist->data[0]==' ') trailing++;
else trailing = 0;
if (FLAG(x)) fprintf(stderr, "HUNK:%s\n", plist->data);
}
matcheof = !trailing || trailing < TT.context;
if (FLAG(x)) fprintf(stderr,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
// Loop through input data searching for this hunk. Match all context
// lines and all lines to be removed until we've found the end of a
// complete hunk.
plist = TT.current_hunk;
buf = NULL;
for (;;) {
char *data = get_line(TT.filein);
TT.linenum++;
// Figure out which line of hunk to compare with next. (Skip lines
// of the hunk we'd be adding.)
while (plist && *plist->data == "+-"[reverse]) {
if (data && !lcmp(data, plist->data+1))
if (!backwarn) backwarn = TT.linenum;
plist = plist->next;
}
// Is this EOF?
if (!data) {
if (FLAG(x)) fprintf(stderr, "INEOF\n");
// Does this hunk need to match EOF?
if (!plist && matcheof) break;
if (backwarn && !FLAG(s))
fprintf(stderr, "Possibly reversed hunk %d at %ld\n",
TT.hunknum, TT.linenum);
// File ended before we found a place for this hunk.
fail_hunk();
goto done;
} else if (FLAG(x)) fprintf(stderr, "IN: %s\n", data);
check = dlist_add(&buf, data);
// Compare this line with next expected line of hunk.
// A match can fail because the next line doesn't match, or because
// we hit the end of a hunk that needed EOF, and this isn't EOF.
// If match failed, flush first line of buffered data and
// recheck buffered data for a new match until we find one or run
// out of buffer.
for (;;) {
if (!plist || lcmp(check->data, plist->data+1)) {
// Match failed. Write out first line of buffered data and
// recheck remaining buffered data for a new match.
if (FLAG(x)) {
int bug = 0;
if (!plist) fprintf(stderr, "NULL plist\n");
else {
while (plist->data[bug] == check->data[bug]) bug++;
fprintf(stderr, "NOT(%d:%d!=%d): %s\n", bug, plist->data[bug],
check->data[bug], plist->data);
}
}
// If this hunk must match start of file, fail if it didn't.
if (!TT.context || trailing>TT.context) {
fail_hunk();
goto done;
}
TT.state = 3;
do_line(check = dlist_pop(&buf));
plist = TT.current_hunk;
// If we've reached the end of the buffer without confirming a
// match, read more lines.
if (!buf) break;
check = buf;
} else {
if (FLAG(x)) fprintf(stderr, "MAYBE: %s\n", plist->data);
// This line matches. Advance plist, detect successful match.
plist = plist->next;
if (!plist && !matcheof) goto out;
check = check->next;
if (check == buf) break;
}
}
}
out:
// We have a match. Emit changed data.
TT.state = "-+"[reverse];
llist_traverse(TT.current_hunk, do_line);
TT.current_hunk = NULL;
TT.state = 1;
done:
if (buf) {
dlist_terminate(buf);
llist_traverse(buf, do_line);
}
return TT.state;
}
// read a filename that has been quoted or escaped
char *unquote_file(char *filename) {
char *s = filename, *result, *t, *u;
int quote = 0, ch;
// quoted and escaped filenames are larger than the original
result = xmalloc(strlen(filename) + 1);
t = result;
if (*s == '"') {
s++;
quote = 1;
}
for (; *s && !(quote && *s == '"' && !s[1]); s++) {
// don't accept escape sequences unless the filename is quoted
if (quote && *s == '\\' && s[1]) {
if (s[1] >= '0' && s[1] < '8') {
*t++ = strtoul(s + 1, &u, 8);
s = u - 1;
} else {
ch = unescape(s[1]);
*t++ = ch ? ch : s[1];
s++;
}
} else *t++ = *s;
}
*t = 0;
return result;
}
// Read a patch file and find hunks, opening/creating/deleting files.
// Call apply_one_hunk() on each hunk.
// state 0: Not in a hunk, look for +++.
// state 1: Found +++ file indicator, look for @@
// state 2: In hunk: counting initial context lines
// state 3: In hunk: getting body
void patch_main(void)
{
int reverse = FLAG(R), state = 0, patchlinenum = 0, strip = 0;
char *oldname = NULL, *newname = NULL;
if (TT.i) TT.filepatch = xopenro(TT.i);
TT.filein = TT.fileout = -1;
if (TT.d) xchdir(TT.d);
// Loop through the lines in the patch
for (;;) {
char *patchline;
patchline = get_line(TT.filepatch);
if (!patchline) break;
// Other versions of patch accept damaged patches, so we need to also.
if (strip || !patchlinenum++) {
int len = strlen(patchline);
if (patchline[len-1] == '\r') {
if (!strip && !FLAG(s)) fprintf(stderr, "Removing DOS newlines\n");
strip = 1;
patchline[len-1]=0;
}
}
if (!*patchline) {
free(patchline);
patchline = xstrdup(" ");
}
// Are we assembling a hunk?
if (state >= 2) {
if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
dlist_add(&TT.current_hunk, patchline);
if (*patchline != '+') TT.oldlen--;
if (*patchline != '-') TT.newlen--;
// Context line?
if (*patchline==' ' && state==2) TT.context++;
else state=3;
// If we've consumed all expected hunk lines, apply the hunk.
if (!TT.oldlen && !TT.newlen) state = apply_one_hunk();
continue;
}
dlist_terminate(TT.current_hunk);
fail_hunk();
state = 0;
continue;
}
// Open a new file?
if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) {
char *s, **name = &oldname;
int i;
if (*patchline == '+') {
name = &newname;
state = 1;
}
free(*name);
finish_oldfile();
// Trim date from end of filename (if any). We don't care.
for (s = patchline+4; *s && *s!='\t'; s++);
i = atoi(s);
if (i>1900 && i<=1970) *name = xstrdup("/dev/null");
else {
*s = 0;
*name = unquote_file(patchline+4);
}
// We defer actually opening the file because svn produces broken
// patches that don't signal they want to create a new file the
// way the patch man page says, so you have to read the first hunk
// and _guess_.
// Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@
// but a missing ,value means the value is 1.
} else if (state == 1 && !strncmp("@@ -", patchline, 4)) {
int i;
char *s = patchline+4;
// Read oldline[,oldlen] +newline[,newlen]
TT.oldlen = TT.newlen = 1;
TT.oldline = strtol(s, &s, 10);
if (*s == ',') TT.oldlen=strtol(s+1, &s, 10);
TT.newline = strtol(s+2, &s, 10);
if (*s == ',') TT.newlen = strtol(s+1, &s, 10);
TT.context = 0;
state = 2;
// If this is the first hunk, open the file.
if (TT.filein == -1) {
int oldsum, newsum, del = 0;
char *name;
oldsum = TT.oldline + TT.oldlen;
newsum = TT.newline + TT.newlen;
name = reverse ? oldname : newname;
// We're deleting oldname if new file is /dev/null (before -p)
// or if new hunk is empty (zero context) after patching
if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum))
{
name = reverse ? newname : oldname;
del++;
}
// handle -p path truncation.
for (i = 0, s = name; *s;) {
if (FLAG(p) && TT.p == i) break;
if (*s++ != '/') continue;
while (*s == '/') s++;
name = s;
i++;
}
if (del) {
if (!FLAG(s)) printf("removing %s\n", name);
xunlink(name);
state = 0;
// If we've got a file to open, do so.
} else if (!FLAG(p) || i <= TT.p) {
// If the old file was null, we're creating a new one.
if ((!strcmp(oldname, "/dev/null") || !oldsum) && access(name, F_OK))
{
if (!FLAG(s)) printf("creating %s\n", name);
if (mkpath(name)) perror_exit("mkpath %s", name);
TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666);
} else {
if (!FLAG(s)) printf("patching %s\n", name);
TT.filein = xopenro(name);
}
if (FLAG(dry_run)) TT.fileout = xopen("/dev/null", O_RDWR);
else TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname);
TT.linenum = 0;
TT.hunknum = 0;
}
}
TT.hunknum++;
continue;
}
// If we didn't continue above, discard this line.
free(patchline);
}
finish_oldfile();
if (CFG_TOYBOX_FREE) {
close(TT.filepatch);
free(oldname);
free(newname);
}
}
+14 -77
View File
@@ -2,16 +2,16 @@
*
* Copyright 2007 Rob Landley <rob@landley.net>
*
* TODO: genericize for telnet/microcom/tail-f, fix -t with login_tty()
* TODO: genericize for telnet/microcom/tail-f, fix -t
USE_NETCAT(NEWTOY(netcat, "^tElLw#<1W#<1p#<1>65535q#<1O:o:s:f:46uUnz[!tlL][!Lw][!Lu][!46U][!oO]", TOYFLAG_BIN))
USE_NETCAT(NEWTOY(netcat, "^tElLw#<1W#<1p#<1>65535q#<1s:f:46uUn[!tlL][!Lw][!Lu][!46U]", TOYFLAG_BIN))
USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN))
config NETCAT
bool "netcat"
default y
help
usage: netcat [-46ELlntUu] [-pqWw #] [-s addr] [-o FILE] {IPADDR PORTNUM|-f FILENAME|COMMAND...}
usage: netcat [-46ElLtUu] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME|COMMAND...}
Forward stdin/stdout to a file or network connection.
@@ -19,20 +19,17 @@ config NETCAT
-6 Force IPv6
-E Forward stderr
-f Use FILENAME (ala /dev/ttyS0) instead of network
-L Listen and background each incoming connection (server mode)
-l Listen for one incoming connection, then exit
-L Listen and background each incoming connection (server mode)
-n No DNS lookup
-o Hex dump to FILE (show packets, -o- writes hex only to stdout)
-O Hex dump to FILE (streaming mode)
-p Local port number
-q Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet
-s Local source address
-t Allocate tty
-u Use UDP
-U Use a UNIX domain socket
-W SECONDS timeout for more data on an idle connection
-w SECONDS timeout to establish connection
-z zero-I/O mode [used for scanning]
-W SECONDS timeout for more data on an idle connection
When listening the COMMAND line is executed as a child process to handle
an incoming connection. With no COMMAND -l forwards the connection
@@ -50,11 +47,8 @@ config NETCAT
#include "toys.h"
GLOBALS(
char *f, *s, *o, *O;
char *f, *s;
long q, p, W, w;
unsigned ofd, olast, opos, ocount[2];
char obuf[16];
)
static void timeout(int signum)
@@ -82,49 +76,6 @@ static int usock(char *name, int type, int out)
return sockfd;
}
// Hex dump accumulated buffer data
void oflush(void)
{
char *s = toybuf;
unsigned *oc = TT.ocount+(TT.olast==1), uu;
if (!TT.opos) return;
s += sprintf(toybuf, "%c %08x", 60+2*(TT.olast==1), *oc);
for (uu = 0; uu<16; uu++) {
s += sprintf(s, uu<TT.opos ? " %02x" : " ", TT.obuf[uu]);
if (TT.obuf[uu]-32u>95) TT.obuf[uu] = '.';
}
dprintf(TT.ofd, "%s # %.*s\n", toybuf, TT.opos, TT.obuf);
*oc += TT.opos;
TT.opos = 0;
}
// Write data to output, and hex dump to -o if enabled.
void ohexwrite(int fd, void *buf, size_t len)
{
// Hex dump if -o specified. Output is always to fd 1, input != 1.
if (TT.ofd) {
int i = 0, j;
if (TT.olast != fd) oflush();
TT.olast = fd;
while (i<len) {
j = minof(16-TT.opos, len-i);
memcpy(TT.obuf+TT.opos, buf+i, j);
TT.opos += j;
i += j;
if (TT.opos==16 || !TT.O) oflush();
}
// Don't write data to stdout when -o goes to stdout.
if (TT.ofd==1 && fd==1) return;
}
// Pass along raw data
xwrite(fd, buf, len);
}
void netcat_main(void)
{
#ifdef TOYBOX_OH_ADAPT
@@ -138,15 +89,7 @@ void netcat_main(void)
socklen_t len;
pid_t child;
// -o - disables normal writes to stdout, just gives hex dump.
if (TT.O) TT.o = TT.O;
if (TT.o) {
if (!strcmp(TT.o, "-")) TT.ofd = 1;
else TT.ofd = xcreate(TT.o, O_CREAT|O_TRUNC|O_WRONLY, 0666);
sigatexit(oflush);
}
// Adjust idle and quit_delay to ms or -1 for no timeout
// Addjust idle and quit_delay to ms or -1 for no timeout
TT.W = TT.W ? TT.W*1000 : -1;
TT.q = TT.q ? TT.q*1000 : -1;
@@ -165,24 +108,18 @@ void netcat_main(void)
else if (FLAG(6)) family = AF_INET6;
else if (FLAG(U)) family = AF_UNIX;
if (TT.f) {
in1 = out2 = xopen(TT.f, O_RDWR);
alarm(0);
pollinate(in1, in2, out1, out2, ohexwrite, TT.W, TT.q);
} else {
if (TT.f) in1 = out2 = xopen(TT.f, O_RDWR);
else {
// Setup socket
if (!FLAG(l) && !FLAG(L)) {
if (FLAG(U)) sockfd = usock(toys.optargs[0], type, 1);
else sockfd = xconnectany(xgetaddrinfo(toys.optargs[0], toys.optargs[1],
family, type, 0, AI_NUMERICHOST*FLAG(n)));
// Do not perform any I/O in zero mode
if (FLAG(z)) goto cleanup;
else sockfd = xconnectany(xgetaddrinfo(toys.optargs[0],
toys.optargs[1], family, type, 0, AI_NUMERICHOST*FLAG(n)));
// We have a connection. Disarm timeout and start poll/send loop.
alarm(0);
in1 = out2 = sockfd;
pollinate(in1, in2, out1, out2, ohexwrite, TT.W, TT.q);
pollinate(in1, in2, out1, out2, TT.W, TT.q);
} else {
// Listen for incoming connections
if (FLAG(U)) {
@@ -249,7 +186,7 @@ void netcat_main(void)
// Copy stdin/out
} else {
pollinate(in1, in2, out1, out2, ohexwrite, TT.W, TT.q);
pollinate(in1, in2, out1, out2, TT.W, TT.q);
close(in1);
}
} while (FLAG(L));
@@ -261,4 +198,4 @@ cleanup:
close(in1);
close(sockfd);
}
}
}
+97 -15
View File
@@ -2,13 +2,14 @@
*
* Copyright 2007 Rob Landley <rob@landley.net>
USE_READLINK(NEWTOY(readlink, "<1nqmef(canonicalize)[-mef]", TOYFLAG_USR|TOYFLAG_BIN))
USE_READLINK(NEWTOY(readlink, "<1vnf(canonicalize)emqz[-mef][-qv]", TOYFLAG_USR|TOYFLAG_BIN))
USE_REALPATH(NEWTOY(realpath, "<1(relative-base):R(relative-to):s(no-symlinks)LPemqz[-Ps][-LP][-me]", TOYFLAG_USR|TOYFLAG_BIN))
config READLINK
bool "readlink"
default y
help
usage: readlink FILE...
usage: readlink [-efmnqz] FILE...
With no options, show what symlink points to, return error if not symlink.
@@ -18,26 +19,107 @@ config READLINK
-f Full path (fail if directory missing)
-m Ignore missing entries, show where it would be
-n No trailing newline
-q Quiet (no output, just error code)
-q Quiet (no error messages)
-z NUL instead of newline
config REALPATH
bool "realpath"
default y
help
usage: realpath [-LPemqsz] [--relative-base DIR] [-R DIR] FILE...
Display the canonical absolute pathname
-R Show ../path relative to DIR (--relative-to)
-L Logical path (resolve .. before symlinks)
-P Physical path (default)
-e Canonical path to existing entry (fail if missing)
-m Ignore missing entries, show where it would be
-q Quiet (no error messages)
-s Don't expand symlinks
-z NUL instead of newline
--relative-base If path under DIR trim off prefix
*/
#define FOR_readlink
#define FOR_realpath
#define FORCE_FLAGS
#include "toys.h"
void readlink_main(void)
GLOBALS(
char *R, *relative_base;
)
// test TT.relative_base -RsmLP
// Trim .. out early for -s and -L. TODO: in place in the input string.
static char *resolve(char *arg)
{
int flags = FLAG(e) ? ABS_FILE : FLAG(m) ? 0 : ABS_PATH;
char *s, *ss = 0, *dd = 0;
if (FLAG(s)) flags |= ABS_KEEP;
else if (FLAG(L)) arg = dd = xabspath(arg, ABS_KEEP);
if (!(s = xabspath(arg, flags)) && !FLAG(q)) perror_msg_raw(arg);
free(dd);
// Trim off this prefix if path under here
if (TT.relative_base) {
ss = s;
if (strstart(&ss, TT.relative_base) && (!*ss || *ss=='/')) {
if (*ss=='/') ss++;
ss = xstrdup(!*ss ? "." : ss);
} else ss = 0;
} else if (TT.R) ss = relative_path(TT.R, s, 0);
if (ss) {
free(s);
s = ss;
}
return s;
}
// Resolve command line arguments that can't take part in their own resolution
static char *presolve(char **s)
{
char *ss = *s;
if (ss) {
*s = 0;
if (!(*s = resolve(ss))) xexit();
}
return ss;
}
// Uses realpath flag context: flags (1 = resolve, 2 = -n)
static void do_paths(int flags)
{
char **arg, *s;
for (arg = toys.optargs; *arg; arg++) {
// Calculating full canonical path?
// Take advantage of flag positions to calculate m = -1, f = 0, e = 1
if (toys.optflags & (FLAG_f|FLAG_e|FLAG_m))
s = xabspath(*arg, (toys.optflags&(FLAG_f|FLAG_e))-1);
else s = xreadlink(*arg);
if (!presolve(&TT.relative_base)) presolve(&TT.R);
if (s) {
if (!FLAG(q)) xprintf(FLAG(n) ? "%s" : "%s\n", s);
if (CFG_TOYBOX_FREE) free(s);
} else toys.exitval = 1;
for (arg = toys.optargs; *arg; arg++) {
if (!(s = (flags&1) ? resolve(*arg) : xreadlink(*arg))) toys.exitval = 1;
else xprintf(((flags&2) && !arg[1]) ? "%s" : "%s%c", s, '\n'*!FLAG(z));
free(s);
}
}
void realpath_main(void)
{
do_paths(1);
}
#define FOR_readlink
#include "generated/flags.h"
// Convert readlink flag context to realpath (feeding in -nf separately)
void readlink_main(void)
{
int nf = (toys.optflags/FLAG_f)|!!(FLAG(m)|FLAG(e));
toys.optflags &= FLAG_f-1;
if (!FLAG(v)) toys.optflags |= FLAG_q;
do_paths(nf);
}
+19 -13
View File
@@ -4,18 +4,18 @@
* Copyright 2014 Kyungwan Han <asura321@gmail.com>
*
* No Standard
USE_SYSCTL(NEWTOY(sysctl, "^neNqwpaA[!ap][!aq][!aw][+aA]", TOYFLAG_SBIN))
config SYSCTL
bool "sysctl"
default y
help
usage: sysctl [-aeNnqw] [-p [FILE] | KEY[=VALUE]...]
usage: sysctl [-aAeNnqw] [-p [FILE] | KEY[=VALUE]...]
Read/write system control data (under /proc/sys).
-a Show all values
-a,A Show all values
-e Don't warn about unknown keys
-N Don't print key values
-n Don't print key names
@@ -43,8 +43,9 @@ static void replace_char(char *str, char old, char new)
static void key_error(char *key)
{
if (errno != ENOENT) perror_msg("key '%s'", key);
else if (!FLAG(e)) error_msg("unknown key '%s'", key);
if (errno == ENOENT) {
if (!(toys.optflags & FLAG_e)) error_msg("unknown key '%s'", key);
} else perror_msg("key '%s'", key);
}
static int write_key(char *path, char *key, char *value)
@@ -76,11 +77,11 @@ static int do_show_keys(struct dirtree *dt)
if (!data) key_error(key);
else {
// Print the parts that aren't switched off by flags.
if (!FLAG(n)) xprintf("%s", key);
if (!FLAG(N) && !FLAG(n)) xprintf(" = ");
if (!(toys.optflags & FLAG_n)) xprintf("%s", key);
if (!(toys.optflags & (FLAG_N|FLAG_n))) xprintf(" = ");
for (key = data+strlen(data); key > data && isspace(*--key); *key = 0);
if (!FLAG(N)) xprintf("%s", data);
if (!FLAG(N) || !FLAG(n)) xputc('\n');
if (!(toys.optflags & FLAG_N)) xprintf("%s", data);
if ((toys.optflags & (FLAG_N|FLAG_n)) != (FLAG_N|FLAG_n)) xputc('\n');
}
free(data);
@@ -95,11 +96,16 @@ static void process_key(char *key, char *value)
char *path;
if (!value) value = split_key(key);
if (FLAG(w) && !value) return error_msg("'%s' not key=value", key);
if ((toys.optflags & FLAG_w) && !value) {
error_msg("'%s' not key=value", key);
return;
}
path = xmprintf("/proc/sys/%s", key);
replace_char(path, '.', '/');
// Note: failure to assign to a non-leaf node suppresses the display.
if (!(value && (!write_key(path, key, value) || FLAG(q)))) {
if (!(value && (!write_key(path, key, value) || (toys.optflags & FLAG_q)))) {
if (!access(path, R_OK)) dirtree_read(path, do_show_keys);
else key_error(key);
}
@@ -111,10 +117,10 @@ void sysctl_main()
char **args = 0;
// Display all keys
if (FLAG(a)) dirtree_read("/proc/sys", do_show_keys);
if (toys.optflags & FLAG_a) dirtree_read("/proc/sys", do_show_keys);
// read file
else if (FLAG(p)) {
else if (toys.optflags & FLAG_p) {
FILE *fp = xfopen(*toys.optargs ? *toys.optargs : "/etc/sysctl.conf", "r");
size_t len;
+166 -202
View File
@@ -8,39 +8,40 @@
* TODO:
* -b backup
* -N ignore already applied
* -d chdir first
* -D define wrap #ifdef and #ifndef around changes
* -o outfile output here instead of in place
* -r rejectfile write rejected hunks to this file
*
* -E remove empty files --remove-empty-files
* git syntax (rename, etc)
* -F fuzz (number, default 2)
* [file] which file to patch
USE_PATCH(NEWTOY(patch, ">2(no-backup-if-mismatch)(dry-run)F#g#fulp#v(verbose)@d:i:Rs(quiet)[!sv]", TOYFLAG_USR|TOYFLAG_BIN))
USE_PATCH(NEWTOY(patch, "(no-backup-if-mismatch)(dry-run)"USE_TOYBOX_DEBUG("x")"g#fulp#d:i:Rs(quiet)", TOYFLAG_USR|TOYFLAG_BIN))
config PATCH
bool "patch"
default y
help
usage: patch [-Rlsuv] [-d DIR] [-i FILE] [-p DEPTH] [-F FUZZ] [--dry-run] [FILE [PATCH]]
usage: patch [-d DIR] [-i file] [-p depth] [-Rlsu] [--dry-run]
Apply a unified diff to one or more files.
-d Modify files in DIR
-F Fuzz factor (number of non-matching context lines allowed per hunk)
-i Input patch from FILE (default=stdin)
-i Input file (default=stdin)
-l Loose match (ignore whitespace)
-p Number of '/' to strip from start of file paths (default=all)
-R Reverse patch
-s Silent except for errors
-v Verbose (-vv to see decisions)
-u Ignored (only handles "unified" diffs)
--dry-run Don't change files, just confirm patch applies
Only handles "unified" diff format (-u is assumed and ignored). Only
modifies files when all hunks to that file apply. Prints failed hunks
This version of patch only handles unified diffs, and only modifies
a file when all hunks to that file apply. Patch prints failed hunks
to stderr, and exits with nonzero status if any hunks fail.
Files compared against /dev/null (or with a date <= the unix epoch) are
created/deleted as appropriate. Default -F value is the number of
leading/trailing context lines minus one (usually 2).
A file compared against /dev/null (or with a date <= the epoch) is
created/deleted as appropriate.
*/
#define FOR_patch
@@ -48,33 +49,15 @@ config PATCH
GLOBALS(
char *i, *d;
long v, p, g, F;
long p, g;
void *current_hunk;
long oldline, oldlen, newline, newlen, linenum, outnum;
struct double_list *current_hunk;
long oldline, oldlen, newline, newlen;
long linenum;
int context, state, filein, fileout, filepatch, hunknum;
char *tempname;
)
// TODO xgetline() instead, but replace_tempfile() wants fd...
char *get_line(int fd)
{
char c, *buf = 0;
long len = 0;
for (;;) {
if (1>read(fd, &c, 1)) break;
if (!(len & 63)) buf=xrealloc(buf, len+65);
if ((buf[len++]=c) == '\n') break;
}
if (buf) {
buf[len]=0;
if (buf[--len]=='\n') buf[len]=0;
}
return buf;
}
// Dispose of a line of input, either by writing it out or discarding it.
// state < 2: just free
@@ -84,14 +67,20 @@ char *get_line(int fd)
static void do_line(void *data)
{
struct double_list *dlist = data;
struct double_list *dlist = (struct double_list *)data;
TT.outnum++;
if (TT.state>1)
if (0>dprintf(TT.state==2 ? 2 : TT.fileout,"%s\n",dlist->data+(TT.state>3)))
perror_exit("write");
if (TT.state>1 && *dlist->data != TT.state) {
char *s = dlist->data+(TT.state>3);
int i = TT.state == 2 ? 2 : TT.fileout;
llist_free_double(data);
xwrite(i, s, strlen(s));
xwrite(i, "\n", 1);
}
if (FLAG(x)) fprintf(stderr, "DO %d: %s\n", TT.state, dlist->data);
free(dlist->data);
free(data);
}
static void finish_oldfile(void)
@@ -113,7 +102,7 @@ static void fail_hunk(void)
TT.state = 2;
llist_traverse(TT.current_hunk, do_line);
TT.current_hunk = 0;
TT.current_hunk = NULL;
if (!FLAG(dry_run)) delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
TT.state = 0;
}
@@ -134,168 +123,156 @@ static int loosecmp(char *aa, char *bb)
// Given a hunk of a unified diff, make the appropriate change to the file.
// This does not use the location information, but instead treats a hunk
// as a sort of regex. Copies data from input to output until it finds
// as a sort of regex. Copies data from input to output until it finds
// the change to be made, then outputs the changed data and returns.
// (Finding EOF first is an error.) This is a single pass operation, so
// (Finding EOF first is an error.) This is a single pass operation, so
// multiple hunks must occur in order in the file.
static int apply_one_hunk(void)
{
struct double_list *plist, *buf = 0, *check = 0;
int matcheof, trail = 0, allfuzz = 0, fuzz, ii;
int (*lcmp)(char *aa, char *bb) = FLAG(l) ? (void *)loosecmp : (void *)strcmp;
long backwarn = 0;
char *data = toybuf;
struct double_list *plist, *buf = NULL, *check;
int matcheof, trailing = 0, reverse = FLAG(R), backwarn = 0;
int (*lcmp)(char *aa, char *bb);
if (TT.v>1) printf("START %d\n", TT.hunknum);
lcmp = FLAG(l) ? (void *)loosecmp : (void *)strcmp;
dlist_terminate(TT.current_hunk);
// Match EOF if there aren't as many ending context lines as beginning
dlist_terminate(TT.current_hunk);
for (fuzz = 0, plist = TT.current_hunk; plist; plist = plist->next) {
char *s = plist->data, c = *s;
if (c==' ') trail++;
else trail = 0;
// Only allow fuzz if 2 context lines have multiple nonwhitespace chars.
// avoids the "all context was blank or } lines" issue. Removed lines
// count as context since they're matched.
if (c==' ' || c=="-+"[FLAG(R)]) {
while (isspace(*++s));
if (*s && s[1] && !isspace(s[1])) fuzz++;
}
for (plist = TT.current_hunk; plist; plist = plist->next) {
if (plist->data[0]==' ') trailing++;
else trailing = 0;
if (FLAG(x)) fprintf(stderr, "HUNK:%s\n", plist->data);
}
matcheof = !trail || trail < TT.context;
if (FLAG(F) && !TT.F) fuzz = 0;
if (fuzz>1) allfuzz = TT.F ? : TT.context ? TT.context-1 : 0;
matcheof = !trailing || trailing < TT.context;
if (FLAG(x)) fprintf(stderr,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
// Loop through input data searching for this hunk. Match all context
// lines and lines to be removed until we've found end of complete hunk.
// lines and all lines to be removed until we've found the end of a
// complete hunk.
plist = TT.current_hunk;
fuzz = 0;
buf = NULL;
for (;;) {
if (data) {
data = get_line(TT.filein);
check = data ? dlist_add(&buf, data) : 0;
TT.linenum++;
char *data = get_line(TT.filein);
TT.linenum++;
// Figure out which line of hunk to compare with next. (Skip lines
// of the hunk we'd be adding.)
while (plist && *plist->data == "+-"[reverse]) {
if (data && !lcmp(data, plist->data+1))
if (!backwarn) backwarn = TT.linenum;
plist = plist->next;
}
if (TT.v>1) printf("READ[%ld] %s\n", TT.linenum, data ? : "(NULL)");
// Compare buffered line(s) with expected lines of hunk. Match can fail
// because next line doesn't match, or because we hit end of a hunk that
// needed EOF and this isn't EOF.
// Is this EOF?
if (!data) {
if (FLAG(x)) fprintf(stderr, "INEOF\n");
// Does this hunk need to match EOF?
if (!plist && matcheof) break;
if (backwarn && !FLAG(s))
fprintf(stderr, "Possibly reversed hunk %d at %ld\n",
TT.hunknum, TT.linenum);
// File ended before we found a place for this hunk.
fail_hunk();
goto done;
} else if (FLAG(x)) fprintf(stderr, "IN: %s\n", data);
check = dlist_add(&buf, data);
// Compare this line with next expected line of hunk.
// A match can fail because the next line doesn't match, or because
// we hit the end of a hunk that needed EOF, and this isn't EOF.
// If match failed, flush first line of buffered data and
// recheck buffered data for a new match until we find one or run
// out of buffer.
for (;;) {
// Find hunk line to match (skip added lines) and detect reverse matches
while (plist && *plist->data == "+-"[FLAG(R)]) {
// TODO: proper backwarn = full hunk applies in reverse, not just 1 line
if (data) {
ii = strcspn(data, " \t");
if (data[ii+!!data[ii]] && !lcmp(data, plist->data+1))
backwarn = TT.linenum;
if (!plist || lcmp(check->data, plist->data+1)) {
// Match failed. Write out first line of buffered data and
// recheck remaining buffered data for a new match.
if (FLAG(x)) {
int bug = 0;
if (!plist) fprintf(stderr, "NULL plist\n");
else {
while (plist->data[bug] == check->data[bug]) bug++;
fprintf(stderr, "NOT(%d:%d!=%d): %s\n", bug, plist->data[bug],
check->data[bug], plist->data);
}
}
// If this hunk must match start of file, fail if it didn't.
if (!TT.context || trailing>TT.context) {
fail_hunk();
goto done;
}
TT.state = 3;
do_line(check = dlist_pop(&buf));
plist = TT.current_hunk;
// If we've reached the end of the buffer without confirming a
// match, read more lines.
if (!buf) break;
check = buf;
} else {
if (FLAG(x)) fprintf(stderr, "MAYBE: %s\n", plist->data);
// This line matches. Advance plist, detect successful match.
plist = plist->next;
if (!plist && !matcheof) goto out;
check = check->next;
if (check == buf) break;
}
if (TT.v>1 && plist)
printf("HUNK %s\nLINE %s\n", plist->data+1, check ? check->data : "");
// End of hunk?
if (!plist) {
if (TT.v>1) printf("END OF HUNK\n");
if (matcheof == !data) goto out;
// Compare line and handle match
} else if (check && !lcmp(check->data, plist->data+1)) {
if (TT.v>1) printf("MATCH\n");
handle_match:
plist = plist->next;
if ((check = check->next) == buf) {
if (plist || matcheof) break;
goto out;
} else continue;
}
// Did we hit EOF?
if (!data) {
if (TT.v>1) printf("EOF\n");
if (backwarn && !FLAG(s))
fprintf(stderr, "Possibly reversed hunk %d at %ld\n",
TT.hunknum, backwarn);
// File ended before we found a place for this hunk.
fail_hunk();
goto done;
}
if (TT.v>1) printf("NOT MATCH\n");
// Match failed: can we fuzz it?
if (plist && *plist->data == ' ' && fuzz<allfuzz) {
fuzz++;
if (TT.v>1) printf("FUZZ %d %s\n", fuzz, check->data);
goto handle_match;
}
// If this hunk must match start of file, fail if it didn't.
if (!TT.context || trail>TT.context) {
fail_hunk();
goto done;
}
// Write out first line of buffer and recheck rest for new match.
TT.state = 3;
if (TT.v>1) printf("WRITE %s\n", buf->data);
do_line(check = dlist_pop(&buf));
plist = TT.current_hunk;
fuzz = 0;
// If end of the buffer without finishing a match, read more lines.
if (!buf) break;
check = buf;
}
}
out:
if (TT.v) xprintf("Hunk #%d succeeded at %ld.\n", TT.hunknum, TT.linenum);
// We have a match. Emit changed data.
TT.state = "-+"[FLAG(R)];
while ((plist = dlist_pop(&TT.current_hunk))) {
if (TT.state == *plist->data || *plist->data == ' ') {
if (*plist->data == ' ') dprintf(TT.fileout, "%s\n", buf->data);
llist_free_double(dlist_pop(&buf));
} else dprintf(TT.fileout, "%s\n", plist->data+1);
llist_free_double(plist);
}
TT.current_hunk = 0;
TT.state = "-+"[reverse];
llist_traverse(TT.current_hunk, do_line);
TT.current_hunk = NULL;
TT.state = 1;
done:
llist_traverse(buf, do_line);
if (buf) {
dlist_terminate(buf);
llist_traverse(buf, do_line);
}
return TT.state;
}
// read a filename that has been quoted or escaped
static char *unquote_file(char *filename)
{
char *s = filename, *t, *newfile;
// Return copy of file that wasn't quoted
if (*s++ != '"' || !*s) return xstrdup(filename);
char *unquote_file(char *filename) {
char *s = filename, *result, *t, *u;
int quote = 0, ch;
// quoted and escaped filenames are larger than the original
for (t = newfile = xmalloc(strlen(s) + 1); *s != '"'; s++) {
if (!s[1]) error_exit("bad %s", filename);
result = xmalloc(strlen(filename) + 1);
t = result;
if (*s == '"') {
s++;
quote = 1;
}
for (; *s && !(quote && *s == '"' && !s[1]); s++) {
// don't accept escape sequences unless the filename is quoted
if (*s != '\\') *t++ = *s;
else if (*++s >= '0' && *s < '8') {
*t++ = strtoul(s, &s, 8);
s--;
} else {
if (!(*t = unescape(*s))) *t = *s;;
t++;
}
if (quote && *s == '\\' && s[1]) {
if (s[1] >= '0' && s[1] < '8') {
*t++ = strtoul(s + 1, &u, 8);
s = u - 1;
} else {
ch = unescape(s[1]);
*t++ = ch ? ch : s[1];
s++;
}
} else *t++ = *s;
}
*t = 0;
return newfile;
return result;
}
// Read a patch file and find hunks, opening/creating/deleting files.
@@ -308,28 +285,28 @@ static char *unquote_file(char *filename)
void patch_main(void)
{
int state = 0, patchlinenum = 0, strip = 0;
char *oldname = 0, *newname = 0;
int reverse = FLAG(R), state = 0, patchlinenum = 0, strip = 0;
char *oldname = NULL, *newname = NULL;
if (toys.optc == 2) TT.i = toys.optargs[1];
if (TT.i) TT.filepatch = xopenro(TT.i);
TT.filein = TT.fileout = -1;
if (TT.d) xchdir(TT.d);
// Loop through the lines in the patch file (-i or stdin) collecting hunks
// Loop through the lines in the patch
for (;;) {
char *patchline;
if (!(patchline = get_line(TT.filepatch))) break;
patchline = get_line(TT.filepatch);
if (!patchline) break;
// Other versions of patch accept damaged patches, so we need to also.
if (strip || !patchlinenum++) {
int len = strlen(patchline);
if (len && patchline[len-1] == '\r') {
if (patchline[len-1] == '\r') {
if (!strip && !FLAG(s)) fprintf(stderr, "Removing DOS newlines\n");
strip = 1;
patchline[len-1] = 0;
patchline[len-1]=0;
}
}
if (!*patchline) {
@@ -340,7 +317,7 @@ void patch_main(void)
// Are we assembling a hunk?
if (state >= 2) {
if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
dlist_add((void *)&TT.current_hunk, patchline);
dlist_add(&TT.current_hunk, patchline);
if (*patchline != '+') TT.oldlen--;
if (*patchline != '-') TT.newlen--;
@@ -351,11 +328,11 @@ void patch_main(void)
// If we've consumed all expected hunk lines, apply the hunk.
if (!TT.oldlen && !TT.newlen) state = apply_one_hunk();
} else {
dlist_terminate(TT.current_hunk);
fail_hunk();
state = 0;
continue;
}
dlist_terminate(TT.current_hunk);
fail_hunk();
state = 0;
continue;
}
@@ -372,7 +349,7 @@ void patch_main(void)
free(*name);
finish_oldfile();
// Trim date from end of filename (if any). Date<=epoch means delete.
// Trim date from end of filename (if any). We don't care.
for (s = patchline+4; *s && *s!='\t'; s++);
i = atoi(s);
if (i>1900 && i<=1970) *name = xstrdup("/dev/null");
@@ -411,24 +388,13 @@ void patch_main(void)
oldsum = TT.oldline + TT.oldlen;
newsum = TT.newline + TT.newlen;
// If an original file was provided on the command line, it overrides
// *all* files mentioned in the patch, not just the first.
if (toys.optc) {
char **which = FLAG(R) ? &oldname : &newname;
free(*which);
*which = xstrdup(toys.optargs[0]);
// The supplied path should be taken literally with or without -p.
toys.optflags |= FLAG_p;
TT.p = 0;
}
name = FLAG(R) ? oldname : newname;
name = reverse ? oldname : newname;
// We're deleting oldname if new file is /dev/null (before -p)
// or if new hunk is empty (zero context) after patching
if (!strcmp(name, "/dev/null") || !(FLAG(R) ? oldsum : newsum)) {
name = FLAG(R) ? newname : oldname;
if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum))
{
name = reverse ? newname : oldname;
del++;
}
@@ -443,7 +409,7 @@ void patch_main(void)
if (del) {
if (!FLAG(s)) printf("removing %s\n", name);
if (!FLAG(dry_run)) xunlink(name);
xunlink(name);
state = 0;
// If we've got a file to open, do so.
} else if (!FLAG(p) || i <= TT.p) {
@@ -451,18 +417,16 @@ void patch_main(void)
if ((!strcmp(oldname, "/dev/null") || !oldsum) && access(name, F_OK))
{
if (!FLAG(s)) printf("creating %s\n", name);
if (FLAG(dry_run)) TT.filein = xopen("/dev/null", O_RDWR);
else {
if (mkpath(name)) perror_exit("mkpath %s", name);
TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666);
}
if (mkpath(name)) perror_exit("mkpath %s", name);
TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666);
} else {
if (!FLAG(s)) printf("patching %s\n", name);
TT.filein = xopenro(name);
}
if (FLAG(dry_run)) TT.fileout = xopen("/dev/null", O_RDWR);
else TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname);
TT.linenum = TT.outnum = TT.hunknum = 0;
TT.linenum = 0;
TT.hunknum = 0;
}
}