diff options
Diffstat (limited to 'libbb')
97 files changed, 8274 insertions, 0 deletions
diff --git a/libbb/Config.in b/libbb/Config.in new file mode 100644 index 000000000..92ee55cbc --- /dev/null +++ b/libbb/Config.in | |||
@@ -0,0 +1,29 @@ | |||
1 | # | ||
2 | # For a description of the syntax of this configuration file, | ||
3 | # see scripts/kbuild/config-language.txt. | ||
4 | # | ||
5 | |||
6 | menu "Busybox Library Tuning" | ||
7 | |||
8 | config PASSWORD_MINLEN | ||
9 | int "Minimum password length" | ||
10 | default 6 | ||
11 | range 5 32 | ||
12 | help | ||
13 | Minimum allowable password length. | ||
14 | |||
15 | config MD5_SIZE_VS_SPEED | ||
16 | int " MD5: Trade Bytes for Speed" | ||
17 | default 2 | ||
18 | range 0 3 | ||
19 | help | ||
20 | Trade binary size versus speed for the md5sum algorithm. | ||
21 | Approximate values running uClibc and hashing | ||
22 | linux-2.4.4.tar.bz2 were: | ||
23 | user times (sec) text size (386) | ||
24 | 0 (fastest) 1.1 6144 | ||
25 | 1 1.4 5392 | ||
26 | 2 3.0 5088 | ||
27 | 3 (smallest) 5.1 4912 | ||
28 | |||
29 | endmenu | ||
diff --git a/libbb/Kbuild b/libbb/Kbuild new file mode 100644 index 000000000..c15615302 --- /dev/null +++ b/libbb/Kbuild | |||
@@ -0,0 +1,117 @@ | |||
1 | # Makefile for busybox | ||
2 | # | ||
3 | # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> | ||
4 | # | ||
5 | # Licensed under the GPL v2, see the file LICENSE in this tarball. | ||
6 | |||
7 | lib-y:= | ||
8 | |||
9 | lib-y += ask_confirmation.o | ||
10 | lib-y += bb_askpass.o | ||
11 | lib-y += bb_do_delay.o | ||
12 | lib-y += bb_pwd.o | ||
13 | lib-y += bb_strtonum.o | ||
14 | lib-y += change_identity.o | ||
15 | lib-y += chomp.o | ||
16 | lib-y += compare_string_array.o | ||
17 | lib-y += concat_path_file.o | ||
18 | lib-y += concat_subpath_file.o | ||
19 | lib-y += copy_file.o | ||
20 | lib-y += copyfd.o | ||
21 | lib-y += crc32.o | ||
22 | lib-y += create_icmp6_socket.o | ||
23 | lib-y += create_icmp_socket.o | ||
24 | lib-y += default_error_retval.o | ||
25 | lib-y += device_open.o | ||
26 | lib-y += dump.o | ||
27 | lib-y += error_msg.o | ||
28 | lib-y += error_msg_and_die.o | ||
29 | lib-y += execable.o | ||
30 | lib-y += fclose_nonstdin.o | ||
31 | lib-y += fflush_stdout_and_exit.o | ||
32 | lib-y += fgets_str.o | ||
33 | lib-y += find_pid_by_name.o | ||
34 | lib-y += find_root_device.o | ||
35 | lib-y += full_write.o | ||
36 | lib-y += get_console.o | ||
37 | lib-y += get_last_path_component.o | ||
38 | lib-y += get_line_from_file.o | ||
39 | lib-y += getopt32.o | ||
40 | lib-y += herror_msg.o | ||
41 | lib-y += herror_msg_and_die.o | ||
42 | lib-y += human_readable.o | ||
43 | lib-y += inet_common.o | ||
44 | lib-y += info_msg.o | ||
45 | lib-y += inode_hash.o | ||
46 | lib-y += isdirectory.o | ||
47 | lib-y += kernel_version.o | ||
48 | lib-y += last_char_is.o | ||
49 | lib-y += llist.o | ||
50 | lib-y += login.o | ||
51 | lib-y += make_directory.o | ||
52 | lib-y += makedev.o | ||
53 | lib-y += md5.o | ||
54 | lib-y += messages.o | ||
55 | lib-y += mode_string.o | ||
56 | lib-y += mtab_file.o | ||
57 | lib-y += obscure.o | ||
58 | lib-y += parse_mode.o | ||
59 | lib-y += perror_msg.o | ||
60 | lib-y += perror_msg_and_die.o | ||
61 | lib-y += perror_nomsg.o | ||
62 | lib-y += perror_nomsg_and_die.o | ||
63 | lib-y += process_escape_sequence.o | ||
64 | lib-y += procps.o | ||
65 | lib-y += read.o | ||
66 | lib-y += recursive_action.o | ||
67 | lib-y += remove_file.o | ||
68 | lib-y += restricted_shell.o | ||
69 | lib-y += run_shell.o | ||
70 | lib-y += safe_strncpy.o | ||
71 | lib-y += safe_write.o | ||
72 | lib-y += setup_environment.o | ||
73 | lib-y += sha1.o | ||
74 | lib-y += simplify_path.o | ||
75 | lib-y += skip_whitespace.o | ||
76 | lib-y += speed_table.o | ||
77 | lib-y += trim.o | ||
78 | lib-y += u_signal_names.o | ||
79 | lib-y += uuencode.o | ||
80 | lib-y += vdprintf.o | ||
81 | lib-y += verror_msg.o | ||
82 | lib-y += vfork_daemon_rexec.o | ||
83 | lib-y += vherror_msg.o | ||
84 | lib-y += vinfo_msg.o | ||
85 | lib-y += vperror_msg.o | ||
86 | lib-y += warn_ignoring_args.o | ||
87 | lib-y += wfopen.o | ||
88 | lib-y += wfopen_input.o | ||
89 | lib-y += xatonum.o | ||
90 | lib-y += xconnect.o | ||
91 | lib-y += xfuncs.o | ||
92 | lib-y += xgetcwd.o | ||
93 | lib-y += xgethostbyname.o | ||
94 | lib-y += xgethostbyname2.o | ||
95 | lib-y += xreadlink.o | ||
96 | |||
97 | # conditionally compiled objects: | ||
98 | lib-$(CONFIG_FEATURE_MOUNT_LOOP) += loop.o | ||
99 | lib-$(CONFIG_LOSETUP) += loop.o | ||
100 | lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o | ||
101 | lib-$(CONFIG_PASSWD) += pw_encrypt.o | ||
102 | lib-$(CONFIG_SULOGIN) += pw_encrypt.o | ||
103 | lib-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) += pw_encrypt.o | ||
104 | lib-$(CONFIG_VLOCK) += correct_password.o | ||
105 | lib-$(CONFIG_SU) += correct_password.o | ||
106 | lib-$(CONFIG_LOGIN) += correct_password.o | ||
107 | lib-$(CONFIG_DF) += find_mount_point.o | ||
108 | lib-$(CONFIG_EJECT) += find_mount_point.o | ||
109 | |||
110 | # We shouldn't build xregcomp.c if we don't need it - this ensures we don't | ||
111 | # require regex.h to be in the include dir even if we don't need it thereby | ||
112 | # allowing us to build busybox even if uclibc regex support is disabled. | ||
113 | |||
114 | lib-$(CONFIG_AWK) += xregcomp.o | ||
115 | lib-$(CONFIG_SED) += xregcomp.o | ||
116 | lib-$(CONFIG_LESS) += xregcomp.o | ||
117 | lib-$(CONFIG_DEVFSD) += xregcomp.o | ||
diff --git a/libbb/README b/libbb/README new file mode 100644 index 000000000..4f28f7e34 --- /dev/null +++ b/libbb/README | |||
@@ -0,0 +1,11 @@ | |||
1 | Please see the LICENSE file for copyright information (GPLv2) | ||
2 | |||
3 | libbb is BusyBox's utility library. All of this stuff used to be stuffed into | ||
4 | a single file named utility.c. When I split utility.c to create libbb, some of | ||
5 | the very oldest stuff ended up without their original copyright and licensing | ||
6 | information (which is now lost in the mists of time). If you see something | ||
7 | that you wrote that is mis-attributed, do let me know so we can fix that up. | ||
8 | |||
9 | Erik Andersen | ||
10 | <andersen@codepoet.org> | ||
11 | |||
diff --git a/libbb/ask_confirmation.c b/libbb/ask_confirmation.c new file mode 100644 index 000000000..4642fa036 --- /dev/null +++ b/libbb/ask_confirmation.c | |||
@@ -0,0 +1,36 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * bb_ask_confirmation implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | /* Read a line from stdin. If the first non-whitespace char is 'y' or 'Y', | ||
11 | * return 1. Otherwise return 0. | ||
12 | */ | ||
13 | |||
14 | #include <stdio.h> | ||
15 | #include <ctype.h> | ||
16 | #include "libbb.h" | ||
17 | |||
18 | int bb_ask_confirmation(void) | ||
19 | { | ||
20 | int retval = 0; | ||
21 | int first = 1; | ||
22 | int c; | ||
23 | |||
24 | while (((c = getchar()) != EOF) && (c != '\n')) { | ||
25 | /* Make sure we get the actual function call for isspace, | ||
26 | * as speed is not critical here. */ | ||
27 | if (first && !(isspace)(c)) { | ||
28 | --first; | ||
29 | if ((c == 'y') || (c == 'Y')) { | ||
30 | ++retval; | ||
31 | } | ||
32 | } | ||
33 | } | ||
34 | |||
35 | return retval; | ||
36 | } | ||
diff --git a/libbb/bb_askpass.c b/libbb/bb_askpass.c new file mode 100644 index 000000000..097a0a290 --- /dev/null +++ b/libbb/bb_askpass.c | |||
@@ -0,0 +1,75 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Ask for a password | ||
4 | * I use a static buffer in this function. Plan accordingly. | ||
5 | * | ||
6 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include <stdio.h> | ||
12 | #include <string.h> | ||
13 | #include <unistd.h> | ||
14 | #include <fcntl.h> | ||
15 | #include <signal.h> | ||
16 | #include <termios.h> | ||
17 | #include <sys/ioctl.h> | ||
18 | |||
19 | #include "libbb.h" | ||
20 | |||
21 | /* do nothing signal handler */ | ||
22 | static void askpass_timeout(int ATTRIBUTE_UNUSED ignore) | ||
23 | { | ||
24 | } | ||
25 | |||
26 | char *bb_askpass(int timeout, const char * prompt) | ||
27 | { | ||
28 | static char passwd[64]; | ||
29 | |||
30 | char *ret; | ||
31 | int i; | ||
32 | struct sigaction sa; | ||
33 | struct termios old, new; | ||
34 | |||
35 | tcgetattr(STDIN_FILENO, &old); | ||
36 | tcflush(STDIN_FILENO, TCIFLUSH); | ||
37 | |||
38 | memset(passwd, 0, sizeof(passwd)); | ||
39 | |||
40 | fputs(prompt, stdout); | ||
41 | fflush(stdout); | ||
42 | |||
43 | tcgetattr(STDIN_FILENO, &new); | ||
44 | new.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY); | ||
45 | new.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP); | ||
46 | tcsetattr(STDIN_FILENO, TCSANOW, &new); | ||
47 | |||
48 | if (timeout) { | ||
49 | sa.sa_flags = 0; | ||
50 | sa.sa_handler = askpass_timeout; | ||
51 | sigaction(SIGALRM, &sa, NULL); | ||
52 | alarm(timeout); | ||
53 | } | ||
54 | |||
55 | ret = NULL; | ||
56 | if (read(STDIN_FILENO, passwd, sizeof(passwd)-1) > 0) { | ||
57 | ret = passwd; | ||
58 | i = 0; | ||
59 | /* Last byte is guaranteed to be 0 | ||
60 | (read did not overwrite it) */ | ||
61 | do { | ||
62 | if (passwd[i] == '\r' || passwd[i] == '\n') | ||
63 | passwd[i] = '\0'; | ||
64 | } while (passwd[i++]); | ||
65 | } | ||
66 | |||
67 | if (timeout) { | ||
68 | alarm(0); | ||
69 | } | ||
70 | |||
71 | tcsetattr(STDIN_FILENO, TCSANOW, &old); | ||
72 | puts(""); | ||
73 | fflush(stdout); | ||
74 | return ret; | ||
75 | } | ||
diff --git a/libbb/bb_do_delay.c b/libbb/bb_do_delay.c new file mode 100644 index 000000000..e14b67a19 --- /dev/null +++ b/libbb/bb_do_delay.c | |||
@@ -0,0 +1,24 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Busybox utility routines. | ||
4 | * | ||
5 | * Copyright (C) 2005 by Tito Ragusa <tito-wolit@tiscali.it> | ||
6 | * | ||
7 | * Licensed under the GPL v2, see the file LICENSE in this tarball. | ||
8 | */ | ||
9 | |||
10 | #include <time.h> | ||
11 | #include <unistd.h> | ||
12 | #include "libbb.h" | ||
13 | |||
14 | void bb_do_delay(int seconds) | ||
15 | { | ||
16 | time_t start, now; | ||
17 | |||
18 | time(&start); | ||
19 | now = start; | ||
20 | while (difftime(now, start) < seconds) { | ||
21 | sleep(seconds); | ||
22 | time(&now); | ||
23 | } | ||
24 | } | ||
diff --git a/libbb/bb_pwd.c b/libbb/bb_pwd.c new file mode 100644 index 000000000..b5125b0f4 --- /dev/null +++ b/libbb/bb_pwd.c | |||
@@ -0,0 +1,130 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * password utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
8 | */ | ||
9 | |||
10 | #include <stdio.h> | ||
11 | #include <string.h> | ||
12 | #include <assert.h> | ||
13 | #include "libbb.h" | ||
14 | |||
15 | /* | ||
16 | * if bufsize is > 0 char *buffer cannot be set to NULL. | ||
17 | * If idname is not NULL it is written on the static | ||
18 | * allocated buffer (and a pointer to it is returned). | ||
19 | * if idname is NULL, id as string is written to the static | ||
20 | * allocated buffer and NULL is returned. | ||
21 | * if bufsize is = 0 char *buffer can be set to NULL. | ||
22 | * If idname exists a pointer to it is returned, | ||
23 | * else NULL is returned. | ||
24 | * if bufsize is < 0 char *buffer can be set to NULL. | ||
25 | * If idname exists a pointer to it is returned, | ||
26 | * else an error message is printed and the program exits. | ||
27 | */ | ||
28 | |||
29 | /* internal function for bb_getpwuid and bb_getgrgid */ | ||
30 | static char * bb_getug(char *buffer, char *idname, long id, int bufsize, char prefix) | ||
31 | { | ||
32 | if (bufsize > 0 ) { | ||
33 | assert(buffer!=NULL); | ||
34 | if(idname) { | ||
35 | return safe_strncpy(buffer, idname, bufsize); | ||
36 | } | ||
37 | snprintf(buffer, bufsize, "%ld", id); | ||
38 | } else if (bufsize < 0 && !idname) { | ||
39 | bb_error_msg_and_die("unknown %cid %ld", prefix, id); | ||
40 | } | ||
41 | return idname; | ||
42 | } | ||
43 | |||
44 | /* Hacked by Tito Ragusa (c) 2004 <farmatito@tiscali.it> to make it more | ||
45 | * flexible : | ||
46 | * | ||
47 | * if bufsize is > 0 char *group cannot be set to NULL. | ||
48 | * On success groupname is written on static allocated buffer | ||
49 | * group (and a pointer to it is returned). | ||
50 | * On failure gid as string is written to static allocated | ||
51 | * buffer group and NULL is returned. | ||
52 | * if bufsize is = 0 char *group can be set to NULL. | ||
53 | * On success groupname is returned. | ||
54 | * On failure NULL is returned. | ||
55 | * if bufsize is < 0 char *group can be set to NULL. | ||
56 | * On success groupname is returned. | ||
57 | * On failure an error message is printed and | ||
58 | * the program exits. | ||
59 | */ | ||
60 | |||
61 | /* gets a groupname given a gid */ | ||
62 | char * bb_getgrgid(char *group, long gid, int bufsize) | ||
63 | { | ||
64 | struct group *mygroup = getgrgid(gid); | ||
65 | |||
66 | return bb_getug(group, (mygroup) ? | ||
67 | mygroup->gr_name : (char *)mygroup, gid, bufsize, 'g'); | ||
68 | } | ||
69 | |||
70 | /* returns a gid given a group name */ | ||
71 | long bb_xgetgrnam(const char *name) | ||
72 | { | ||
73 | struct group *mygroup; | ||
74 | |||
75 | mygroup = getgrnam(name); | ||
76 | if (mygroup==NULL) | ||
77 | bb_error_msg_and_die("unknown group name: %s", name); | ||
78 | |||
79 | return mygroup->gr_gid; | ||
80 | } | ||
81 | |||
82 | /* returns a uid given a username */ | ||
83 | long bb_xgetpwnam(const char *name) | ||
84 | { | ||
85 | struct passwd *myuser; | ||
86 | |||
87 | myuser = getpwnam(name); | ||
88 | if (myuser==NULL) | ||
89 | bb_error_msg_and_die("unknown user name: %s", name); | ||
90 | |||
91 | return myuser->pw_uid; | ||
92 | } | ||
93 | |||
94 | /* Hacked by Tito Ragusa (c) 2004 <farmatito@tiscali.it> to make it more | ||
95 | * flexible : | ||
96 | * | ||
97 | * if bufsize is > 0 char *name cannot be set to NULL. | ||
98 | * On success username is written on the static allocated | ||
99 | * buffer name (and a pointer to it is returned). | ||
100 | * On failure uid as string is written to the static | ||
101 | * allocated buffer name and NULL is returned. | ||
102 | * if bufsize is = 0 char *name can be set to NULL. | ||
103 | * On success username is returned. | ||
104 | * On failure NULL is returned. | ||
105 | * if bufsize is < 0 char *name can be set to NULL | ||
106 | * On success username is returned. | ||
107 | * On failure an error message is printed and | ||
108 | * the program exits. | ||
109 | */ | ||
110 | |||
111 | /* gets a username given a uid */ | ||
112 | char * bb_getpwuid(char *name, long uid, int bufsize) | ||
113 | { | ||
114 | struct passwd *myuser = getpwuid(uid); | ||
115 | |||
116 | return bb_getug(name, myuser ? myuser->pw_name : (char *)myuser, | ||
117 | uid, bufsize, 'u'); | ||
118 | } | ||
119 | |||
120 | unsigned long get_ug_id(const char *s, | ||
121 | long (*__bb_getxxnam)(const char *)) | ||
122 | { | ||
123 | unsigned long r; | ||
124 | |||
125 | r = bb_strtoul(s, NULL, 10); | ||
126 | if (errno) | ||
127 | r = __bb_getxxnam(s); | ||
128 | |||
129 | return r; | ||
130 | } | ||
diff --git a/libbb/bb_strtonum.c b/libbb/bb_strtonum.c new file mode 100644 index 000000000..6fbd1f87d --- /dev/null +++ b/libbb/bb_strtonum.c | |||
@@ -0,0 +1,155 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | /* On exit: errno = 0 only if there was non-empty, '\0' terminated value | ||
13 | * errno = EINVAL if value was not '\0' terminated, but othervise ok | ||
14 | * Return value is still valid, caller should just check whether end[0] | ||
15 | * is a valid terminating char for particular case. OTOH, if caller | ||
16 | * requires '\0' terminated input, [s]he can just check errno == 0. | ||
17 | * errno = ERANGE if value had alphanumeric terminating char ("1234abcg"). | ||
18 | * errno = ERANGE if value is out of range, missing, etc. | ||
19 | * errno = ERANGE if value had minus sign for strtouXX (even "-0" is not ok ) | ||
20 | */ | ||
21 | |||
22 | static unsigned long long ret_ERANGE(void) | ||
23 | { | ||
24 | errno = ERANGE; /* this ain't as small as it looks (on glibc) */ | ||
25 | return ULLONG_MAX; | ||
26 | } | ||
27 | |||
28 | static unsigned long long handle_errors(unsigned long long v, char **endp, char *endptr) | ||
29 | { | ||
30 | if (endp) *endp = endptr; | ||
31 | |||
32 | /* Check for the weird "feature": | ||
33 | * a "-" string is apparently a valid "number" for strto[u]l[l]! | ||
34 | * It returns zero and errno is 0! :( */ | ||
35 | if (endptr[-1] == '-') | ||
36 | return ret_ERANGE(); | ||
37 | |||
38 | /* errno is already set to ERANGE by strtoXXX if value overflowed */ | ||
39 | if (endptr[0]) { | ||
40 | /* "1234abcg" or out-of-range? */ | ||
41 | if (isalnum(endptr[0]) || errno) | ||
42 | return ret_ERANGE(); | ||
43 | /* good number, just suspicious terminator */ | ||
44 | errno = EINVAL; | ||
45 | } | ||
46 | return v; | ||
47 | } | ||
48 | |||
49 | |||
50 | unsigned long long bb_strtoull(const char *arg, char **endp, int base) | ||
51 | { | ||
52 | unsigned long long v; | ||
53 | char *endptr; | ||
54 | |||
55 | /* strtoul(" -4200000000") returns 94967296, errno 0 (!) */ | ||
56 | /* I don't think that this is right. Preventing this... */ | ||
57 | if (!isalnum(arg[0])) return ret_ERANGE(); | ||
58 | |||
59 | /* not 100% correct for lib func, but convenient for the caller */ | ||
60 | errno = 0; | ||
61 | v = strtoull(arg, &endptr, base); | ||
62 | return handle_errors(v, endp, endptr); | ||
63 | } | ||
64 | |||
65 | long long bb_strtoll(const char *arg, char **endp, int base) | ||
66 | { | ||
67 | unsigned long long v; | ||
68 | char *endptr; | ||
69 | |||
70 | if (arg[0] != '-' && !isalnum(arg[0])) return ret_ERANGE(); | ||
71 | errno = 0; | ||
72 | v = strtoll(arg, &endptr, base); | ||
73 | return handle_errors(v, endp, endptr); | ||
74 | } | ||
75 | |||
76 | #if ULONG_MAX != ULLONG_MAX | ||
77 | unsigned long bb_strtoul(const char *arg, char **endp, int base) | ||
78 | { | ||
79 | unsigned long v; | ||
80 | char *endptr; | ||
81 | |||
82 | if (!isalnum(arg[0])) return ret_ERANGE(); | ||
83 | errno = 0; | ||
84 | v = strtoul(arg, &endptr, base); | ||
85 | return handle_errors(v, endp, endptr); | ||
86 | } | ||
87 | |||
88 | long bb_strtol(const char *arg, char **endp, int base) | ||
89 | { | ||
90 | long v; | ||
91 | char *endptr; | ||
92 | |||
93 | if (arg[0] != '-' && !isalnum(arg[0])) return ret_ERANGE(); | ||
94 | errno = 0; | ||
95 | v = strtol(arg, &endptr, base); | ||
96 | return handle_errors(v, endp, endptr); | ||
97 | } | ||
98 | #endif | ||
99 | |||
100 | #if UINT_MAX != ULONG_MAX | ||
101 | unsigned bb_strtou(const char *arg, char **endp, int base) | ||
102 | { | ||
103 | unsigned long v; | ||
104 | char *endptr; | ||
105 | |||
106 | if (!isalnum(arg[0])) return ret_ERANGE(); | ||
107 | errno = 0; | ||
108 | v = strtoul(arg, &endptr, base); | ||
109 | if (v > UINT_MAX) return ret_ERANGE(); | ||
110 | return handle_errors(v, endp, endptr); | ||
111 | } | ||
112 | |||
113 | int bb_strtoi(const char *arg, char **endp, int base) | ||
114 | { | ||
115 | long v; | ||
116 | char *endptr; | ||
117 | |||
118 | if (arg[0] != '-' && !isalnum(arg[0])) return ret_ERANGE(); | ||
119 | errno = 0; | ||
120 | v = strtol(arg, &endptr, base); | ||
121 | if (v > INT_MAX) return ret_ERANGE(); | ||
122 | if (v < INT_MIN) return ret_ERANGE(); | ||
123 | return handle_errors(v, endp, endptr); | ||
124 | } | ||
125 | #endif | ||
126 | |||
127 | /* Floating point */ | ||
128 | |||
129 | #if 0 | ||
130 | |||
131 | #include <math.h> /* just for HUGE_VAL */ | ||
132 | #define NOT_DIGIT(a) (((unsigned char)(a-'0')) > 9) | ||
133 | double bb_strtod(const char *arg, char **endp) | ||
134 | { | ||
135 | double v; | ||
136 | char *endptr; | ||
137 | |||
138 | if (arg[0] != '-' && NOT_DIGIT(arg[0])) goto err; | ||
139 | errno = 0; | ||
140 | v = strtod(arg, &endptr); | ||
141 | if (endp) *endp = endptr; | ||
142 | if (endptr[0]) { | ||
143 | /* "1234abcg" or out-of-range? */ | ||
144 | if (isalnum(endptr[0]) || errno) { | ||
145 | err: | ||
146 | errno = ERANGE; | ||
147 | return HUGE_VAL; | ||
148 | } | ||
149 | /* good number, just suspicious terminator */ | ||
150 | errno = EINVAL; | ||
151 | } | ||
152 | return v; | ||
153 | } | ||
154 | |||
155 | #endif | ||
diff --git a/libbb/change_identity.c b/libbb/change_identity.c new file mode 100644 index 000000000..3f888f523 --- /dev/null +++ b/libbb/change_identity.c | |||
@@ -0,0 +1,51 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com> | ||
4 | * All rights reserved. | ||
5 | * | ||
6 | * Redistribution and use in source and binary forms, with or without | ||
7 | * modification, are permitted provided that the following conditions | ||
8 | * are met: | ||
9 | * 1. Redistributions of source code must retain the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer. | ||
11 | * 2. Redistributions in binary form must reproduce the above copyright | ||
12 | * notice, this list of conditions and the following disclaimer in the | ||
13 | * documentation and/or other materials provided with the distribution. | ||
14 | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors | ||
15 | * may be used to endorse or promote products derived from this software | ||
16 | * without specific prior written permission. | ||
17 | * | ||
18 | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND | ||
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
21 | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE | ||
22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
28 | * SUCH DAMAGE. | ||
29 | */ | ||
30 | |||
31 | #include "libbb.h" | ||
32 | |||
33 | |||
34 | /* Become the user and group(s) specified by PW. */ | ||
35 | const char *change_identity_e2str(const struct passwd *pw) | ||
36 | { | ||
37 | if (initgroups(pw->pw_name, pw->pw_gid) == -1) | ||
38 | return "cannot set groups"; | ||
39 | endgrent(); /* ?? */ | ||
40 | xsetgid(pw->pw_gid); | ||
41 | xsetuid(pw->pw_uid); | ||
42 | return NULL; | ||
43 | } | ||
44 | |||
45 | void change_identity(const struct passwd *pw) | ||
46 | { | ||
47 | const char *err_msg = change_identity_e2str(pw); | ||
48 | |||
49 | if (err_msg) | ||
50 | bb_perror_msg_and_die("%s", err_msg); | ||
51 | } | ||
diff --git a/libbb/chomp.c b/libbb/chomp.c new file mode 100644 index 000000000..eab770760 --- /dev/null +++ b/libbb/chomp.c | |||
@@ -0,0 +1,19 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) many different people. | ||
6 | * If you wrote this, please acknowledge your work. | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include "libbb.h" | ||
12 | |||
13 | void chomp(char *s) | ||
14 | { | ||
15 | char *lc = last_char_is(s, '\n'); | ||
16 | |||
17 | if (lc) | ||
18 | *lc = 0; | ||
19 | } | ||
diff --git a/libbb/compare_string_array.c b/libbb/compare_string_array.c new file mode 100644 index 000000000..d15578ca3 --- /dev/null +++ b/libbb/compare_string_array.c | |||
@@ -0,0 +1,37 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
4 | */ | ||
5 | |||
6 | #include "libbb.h" | ||
7 | |||
8 | /* returns the array index of the string */ | ||
9 | /* (index of first match is returned, or -1) */ | ||
10 | int index_in_str_array(const char * const string_array[], const char *key) | ||
11 | { | ||
12 | int i; | ||
13 | |||
14 | for (i = 0; string_array[i] != 0; i++) { | ||
15 | if (strcmp(string_array[i], key) == 0) { | ||
16 | return i; | ||
17 | } | ||
18 | } | ||
19 | return -1; | ||
20 | } | ||
21 | |||
22 | /* returns the array index of the string, even if it matches only a beginning */ | ||
23 | /* (index of first match is returned, or -1) */ | ||
24 | int index_in_substr_array(const char * const string_array[], const char *key) | ||
25 | { | ||
26 | int i; | ||
27 | int len = strlen(key); | ||
28 | if (!len) | ||
29 | return -1; | ||
30 | |||
31 | for (i = 0; string_array[i] != 0; i++) { | ||
32 | if (strncmp(string_array[i], key, len) == 0) { | ||
33 | return i; | ||
34 | } | ||
35 | } | ||
36 | return -1; | ||
37 | } | ||
diff --git a/libbb/concat_path_file.c b/libbb/concat_path_file.c new file mode 100644 index 000000000..9aae601a4 --- /dev/null +++ b/libbb/concat_path_file.c | |||
@@ -0,0 +1,27 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) many different people. | ||
6 | * If you wrote this, please acknowledge your work. | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | /* concatenate path and file name to new allocation buffer, | ||
12 | * not adding '/' if path name already has '/' | ||
13 | */ | ||
14 | |||
15 | #include "libbb.h" | ||
16 | |||
17 | char *concat_path_file(const char *path, const char *filename) | ||
18 | { | ||
19 | char *lc; | ||
20 | |||
21 | if (!path) | ||
22 | path = ""; | ||
23 | lc = last_char_is(path, '/'); | ||
24 | while (*filename == '/') | ||
25 | filename++; | ||
26 | return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename); | ||
27 | } | ||
diff --git a/libbb/concat_subpath_file.c b/libbb/concat_subpath_file.c new file mode 100644 index 000000000..6ebc01bf5 --- /dev/null +++ b/libbb/concat_subpath_file.c | |||
@@ -0,0 +1,23 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | /* | ||
11 | This function make special for recursive actions with usage | ||
12 | concat_path_file(path, filename) | ||
13 | and skiping "." and ".." directory entries | ||
14 | */ | ||
15 | |||
16 | #include "libbb.h" | ||
17 | |||
18 | char *concat_subpath_file(const char *path, const char *f) | ||
19 | { | ||
20 | if(f && *f == '.' && (!f[1] || (f[1] == '.' && !f[2]))) | ||
21 | return NULL; | ||
22 | return concat_path_file(path, f); | ||
23 | } | ||
diff --git a/libbb/copy_file.c b/libbb/copy_file.c new file mode 100644 index 000000000..0135831fe --- /dev/null +++ b/libbb/copy_file.c | |||
@@ -0,0 +1,282 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini copy_file implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include "libbb.h" | ||
12 | |||
13 | static int retry_overwrite(const char *dest, int flags) | ||
14 | { | ||
15 | if (!(flags & (FILEUTILS_FORCE|FILEUTILS_INTERACTIVE))) { | ||
16 | fprintf(stderr, "'%s' exists\n", dest); | ||
17 | return -1; | ||
18 | } | ||
19 | if (flags & FILEUTILS_INTERACTIVE) { | ||
20 | fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest); | ||
21 | if (!bb_ask_confirmation()) | ||
22 | return 0; // not allowed to overwrite | ||
23 | } | ||
24 | if (unlink(dest) < 0) { | ||
25 | bb_perror_msg("cannot remove '%s'", dest); | ||
26 | return -1; // error | ||
27 | } | ||
28 | return 1; // ok (to try again) | ||
29 | } | ||
30 | |||
31 | int copy_file(const char *source, const char *dest, int flags) | ||
32 | { | ||
33 | struct stat source_stat; | ||
34 | struct stat dest_stat; | ||
35 | int status = 0; | ||
36 | signed char dest_exists = 0; | ||
37 | signed char ovr; | ||
38 | |||
39 | #define FLAGS_DEREF (flags & FILEUTILS_DEREFERENCE) | ||
40 | |||
41 | if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) { | ||
42 | // This may be a dangling symlink. | ||
43 | // Making [sym]links to dangling symlinks works, so... | ||
44 | if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) | ||
45 | goto make_links; | ||
46 | bb_perror_msg("cannot stat '%s'", source); | ||
47 | return -1; | ||
48 | } | ||
49 | |||
50 | if (lstat(dest, &dest_stat) < 0) { | ||
51 | if (errno != ENOENT) { | ||
52 | bb_perror_msg("cannot stat '%s'", dest); | ||
53 | return -1; | ||
54 | } | ||
55 | } else { | ||
56 | if (source_stat.st_dev == dest_stat.st_dev | ||
57 | && source_stat.st_ino == dest_stat.st_ino | ||
58 | ) { | ||
59 | bb_error_msg("'%s' and '%s' are the same file", source, dest); | ||
60 | return -1; | ||
61 | } | ||
62 | dest_exists = 1; | ||
63 | } | ||
64 | |||
65 | if (S_ISDIR(source_stat.st_mode)) { | ||
66 | DIR *dp; | ||
67 | struct dirent *d; | ||
68 | mode_t saved_umask = 0; | ||
69 | |||
70 | if (!(flags & FILEUTILS_RECUR)) { | ||
71 | bb_error_msg("omitting directory '%s'", source); | ||
72 | return -1; | ||
73 | } | ||
74 | |||
75 | /* Create DEST. */ | ||
76 | if (dest_exists) { | ||
77 | if (!S_ISDIR(dest_stat.st_mode)) { | ||
78 | bb_error_msg("target '%s' is not a directory", dest); | ||
79 | return -1; | ||
80 | } | ||
81 | } else { | ||
82 | mode_t mode; | ||
83 | saved_umask = umask(0); | ||
84 | |||
85 | mode = source_stat.st_mode; | ||
86 | if (!(flags & FILEUTILS_PRESERVE_STATUS)) | ||
87 | mode = source_stat.st_mode & ~saved_umask; | ||
88 | mode |= S_IRWXU; | ||
89 | |||
90 | if (mkdir(dest, mode) < 0) { | ||
91 | umask(saved_umask); | ||
92 | bb_perror_msg("cannot create directory '%s'", dest); | ||
93 | return -1; | ||
94 | } | ||
95 | |||
96 | umask(saved_umask); | ||
97 | } | ||
98 | |||
99 | /* Recursively copy files in SOURCE. */ | ||
100 | dp = opendir(source); | ||
101 | if (dp == NULL) { | ||
102 | status = -1; | ||
103 | goto preserve_status; | ||
104 | } | ||
105 | |||
106 | while ((d = readdir(dp)) != NULL) { | ||
107 | char *new_source, *new_dest; | ||
108 | |||
109 | new_source = concat_subpath_file(source, d->d_name); | ||
110 | if (new_source == NULL) | ||
111 | continue; | ||
112 | new_dest = concat_path_file(dest, d->d_name); | ||
113 | if (copy_file(new_source, new_dest, flags) < 0) | ||
114 | status = -1; | ||
115 | free(new_source); | ||
116 | free(new_dest); | ||
117 | } | ||
118 | closedir(dp); | ||
119 | |||
120 | if (!dest_exists | ||
121 | && chmod(dest, source_stat.st_mode & ~saved_umask) < 0 | ||
122 | ) { | ||
123 | bb_perror_msg("cannot change permissions of '%s'", dest); | ||
124 | status = -1; | ||
125 | } | ||
126 | |||
127 | } else if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) { | ||
128 | int (*lf)(const char *oldpath, const char *newpath); | ||
129 | make_links: | ||
130 | // Hmm... maybe | ||
131 | // if (DEREF && MAKE_SOFTLINK) source = realpath(source) ? | ||
132 | // (but realpath returns NULL on dangling symlinks...) | ||
133 | lf = (flags & FILEUTILS_MAKE_SOFTLINK) ? symlink : link; | ||
134 | if (lf(source, dest) < 0) { | ||
135 | ovr = retry_overwrite(dest, flags); | ||
136 | if (ovr <= 0) | ||
137 | return ovr; | ||
138 | if (lf(source, dest) < 0) { | ||
139 | bb_perror_msg("cannot create link '%s'", dest); | ||
140 | return -1; | ||
141 | } | ||
142 | } | ||
143 | return 0; | ||
144 | |||
145 | } else if (S_ISREG(source_stat.st_mode) | ||
146 | // Huh? DEREF uses stat, which never returns links IIRC... | ||
147 | || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) | ||
148 | ) { | ||
149 | int src_fd; | ||
150 | int dst_fd; | ||
151 | if (ENABLE_FEATURE_PRESERVE_HARDLINKS) { | ||
152 | char *link_name; | ||
153 | |||
154 | if (!FLAGS_DEREF | ||
155 | && is_in_ino_dev_hashtable(&source_stat, &link_name) | ||
156 | ) { | ||
157 | if (link(link_name, dest) < 0) { | ||
158 | ovr = retry_overwrite(dest, flags); | ||
159 | if (ovr <= 0) | ||
160 | return ovr; | ||
161 | if (link(link_name, dest) < 0) { | ||
162 | bb_perror_msg("cannot create link '%s'", dest); | ||
163 | return -1; | ||
164 | } | ||
165 | } | ||
166 | return 0; | ||
167 | } | ||
168 | // TODO: probably is_in_.. and add_to_... | ||
169 | // can be combined: find_or_add_... | ||
170 | add_to_ino_dev_hashtable(&source_stat, dest); | ||
171 | } | ||
172 | |||
173 | src_fd = open(source, O_RDONLY); | ||
174 | if (src_fd == -1) { | ||
175 | bb_perror_msg("cannot open '%s'", source); | ||
176 | return -1; | ||
177 | } | ||
178 | |||
179 | // POSIX: if exists and -i, ask (w/o -i assume yes). | ||
180 | // Then open w/o EXCL. | ||
181 | // If open still fails and -f, try unlink, then try open again. | ||
182 | // Result: a mess: | ||
183 | // If dest is a softlink, we overwrite softlink's destination! | ||
184 | // (or fail, if it points to dir/nonexistent location/etc). | ||
185 | // This is strange, but POSIX-correct. | ||
186 | // coreutils cp has --remove-destination to override this... | ||
187 | dst_fd = open(dest, (flags & FILEUTILS_INTERACTIVE) | ||
188 | ? O_WRONLY|O_CREAT|O_TRUNC|O_EXCL | ||
189 | : O_WRONLY|O_CREAT|O_TRUNC, source_stat.st_mode); | ||
190 | if (dst_fd == -1) { | ||
191 | // We would not do POSIX insanity. -i asks, | ||
192 | // then _unlinks_ the offender. Presto. | ||
193 | // Or else we will end up having 3 open()s! | ||
194 | ovr = retry_overwrite(dest, flags); | ||
195 | if (ovr <= 0) { | ||
196 | close(src_fd); | ||
197 | return ovr; | ||
198 | } | ||
199 | dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, source_stat.st_mode); | ||
200 | if (dst_fd == -1) { | ||
201 | bb_perror_msg("cannot open '%s'", dest); | ||
202 | close(src_fd); | ||
203 | return -1; | ||
204 | } | ||
205 | } | ||
206 | |||
207 | if (bb_copyfd_eof(src_fd, dst_fd) == -1) | ||
208 | status = -1; | ||
209 | if (close(dst_fd) < 0) { | ||
210 | bb_perror_msg("cannot close '%s'", dest); | ||
211 | status = -1; | ||
212 | } | ||
213 | if (close(src_fd) < 0) { | ||
214 | bb_perror_msg("cannot close '%s'", source); | ||
215 | status = -1; | ||
216 | } | ||
217 | |||
218 | } else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) | ||
219 | || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) | ||
220 | || S_ISLNK(source_stat.st_mode) | ||
221 | ) { | ||
222 | // We are lazy here, a bit lax with races... | ||
223 | if (dest_exists) { | ||
224 | ovr = retry_overwrite(dest, flags); | ||
225 | if (ovr <= 0) | ||
226 | return ovr; | ||
227 | } | ||
228 | if (S_ISFIFO(source_stat.st_mode)) { | ||
229 | if (mkfifo(dest, source_stat.st_mode) < 0) { | ||
230 | bb_perror_msg("cannot create fifo '%s'", dest); | ||
231 | return -1; | ||
232 | } | ||
233 | } else if (S_ISLNK(source_stat.st_mode)) { | ||
234 | char *lpath; | ||
235 | |||
236 | lpath = xreadlink(source); | ||
237 | if (symlink(lpath, dest) < 0) { | ||
238 | bb_perror_msg("cannot create symlink '%s'", dest); | ||
239 | free(lpath); | ||
240 | return -1; | ||
241 | } | ||
242 | free(lpath); | ||
243 | |||
244 | if (flags & FILEUTILS_PRESERVE_STATUS) | ||
245 | if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) | ||
246 | bb_perror_msg("cannot preserve %s of '%s'", "ownership", dest); | ||
247 | |||
248 | return 0; | ||
249 | |||
250 | } else { | ||
251 | if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) { | ||
252 | bb_perror_msg("cannot create '%s'", dest); | ||
253 | return -1; | ||
254 | } | ||
255 | } | ||
256 | } else { | ||
257 | bb_error_msg("internal error: unrecognized file type"); | ||
258 | return -1; | ||
259 | } | ||
260 | |||
261 | preserve_status: | ||
262 | |||
263 | if (flags & FILEUTILS_PRESERVE_STATUS | ||
264 | /* Cannot happen: */ | ||
265 | /* && !(flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) */ | ||
266 | ) { | ||
267 | struct utimbuf times; | ||
268 | |||
269 | times.actime = source_stat.st_atime; | ||
270 | times.modtime = source_stat.st_mtime; | ||
271 | if (utime(dest, ×) < 0) | ||
272 | bb_perror_msg("cannot preserve %s of '%s'", "times", dest); | ||
273 | if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) { | ||
274 | source_stat.st_mode &= ~(S_ISUID | S_ISGID); | ||
275 | bb_perror_msg("cannot preserve %s of '%s'", "ownership", dest); | ||
276 | } | ||
277 | if (chmod(dest, source_stat.st_mode) < 0) | ||
278 | bb_perror_msg("cannot preserve %s of '%s'", "permissions", dest); | ||
279 | } | ||
280 | |||
281 | return status; | ||
282 | } | ||
diff --git a/libbb/copyfd.c b/libbb/copyfd.c new file mode 100644 index 000000000..c6b886647 --- /dev/null +++ b/libbb/copyfd.c | |||
@@ -0,0 +1,86 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <errno.h> | ||
11 | #include <stdlib.h> | ||
12 | #include <string.h> | ||
13 | #include <unistd.h> | ||
14 | |||
15 | #include "libbb.h" | ||
16 | |||
17 | |||
18 | #if BUFSIZ < 4096 | ||
19 | #undef BUFSIZ | ||
20 | #define BUFSIZ 4096 | ||
21 | #endif | ||
22 | |||
23 | |||
24 | static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size) | ||
25 | { | ||
26 | int status = -1; | ||
27 | off_t total = 0; | ||
28 | RESERVE_CONFIG_BUFFER(buffer, BUFSIZ); | ||
29 | |||
30 | if (src_fd < 0) goto out; | ||
31 | |||
32 | if (!size) { | ||
33 | size = BUFSIZ; | ||
34 | status = 1; /* copy until eof */ | ||
35 | } | ||
36 | |||
37 | while (1) { | ||
38 | ssize_t rd; | ||
39 | |||
40 | rd = safe_read(src_fd, buffer, size > BUFSIZ ? BUFSIZ : size); | ||
41 | |||
42 | if (!rd) { /* eof - all done. */ | ||
43 | status = 0; | ||
44 | break; | ||
45 | } | ||
46 | if (rd < 0) { | ||
47 | bb_perror_msg(bb_msg_read_error); | ||
48 | break; | ||
49 | } | ||
50 | /* dst_fd == -1 is a fake, else... */ | ||
51 | if (dst_fd >= 0) { | ||
52 | ssize_t wr = full_write(dst_fd, buffer, rd); | ||
53 | if (wr < rd) { | ||
54 | bb_perror_msg(bb_msg_write_error); | ||
55 | break; | ||
56 | } | ||
57 | } | ||
58 | total += rd; | ||
59 | if (status < 0) { | ||
60 | size -= rd; | ||
61 | if (!size) { | ||
62 | status = 0; | ||
63 | break; | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | |||
68 | out: | ||
69 | RELEASE_CONFIG_BUFFER(buffer); | ||
70 | |||
71 | return status ? -1 : total; | ||
72 | } | ||
73 | |||
74 | |||
75 | off_t bb_copyfd_size(int fd1, int fd2, off_t size) | ||
76 | { | ||
77 | if (size) { | ||
78 | return bb_full_fd_action(fd1, fd2, size); | ||
79 | } | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | off_t bb_copyfd_eof(int fd1, int fd2) | ||
84 | { | ||
85 | return bb_full_fd_action(fd1, fd2, 0); | ||
86 | } | ||
diff --git a/libbb/correct_password.c b/libbb/correct_password.c new file mode 100644 index 000000000..fd7e0b56c --- /dev/null +++ b/libbb/correct_password.c | |||
@@ -0,0 +1,65 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com> | ||
4 | * All rights reserved. | ||
5 | * | ||
6 | * Redistribution and use in source and binary forms, with or without | ||
7 | * modification, are permitted provided that the following conditions | ||
8 | * are met: | ||
9 | * 1. Redistributions of source code must retain the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer. | ||
11 | * 2. Redistributions in binary form must reproduce the above copyright | ||
12 | * notice, this list of conditions and the following disclaimer in the | ||
13 | * documentation and/or other materials provided with the distribution. | ||
14 | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors | ||
15 | * may be used to endorse or promote products derived from this software | ||
16 | * without specific prior written permission. | ||
17 | * | ||
18 | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND | ||
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
21 | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE | ||
22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
28 | * SUCH DAMAGE. | ||
29 | */ | ||
30 | |||
31 | #include "libbb.h" | ||
32 | |||
33 | /* Ask the user for a password. | ||
34 | Return 1 if the user gives the correct password for entry PW, | ||
35 | 0 if not. Return 1 without asking for a password if run by UID 0 | ||
36 | or if PW has an empty password. */ | ||
37 | |||
38 | int correct_password(const struct passwd *pw) | ||
39 | { | ||
40 | char *unencrypted, *encrypted, *correct; | ||
41 | |||
42 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
43 | if (!strcmp(pw->pw_passwd, "x") || !strcmp(pw->pw_passwd, "*")) { | ||
44 | struct spwd *sp = getspnam(pw->pw_name); | ||
45 | |||
46 | if (!sp) | ||
47 | bb_error_msg_and_die("no valid shadow password"); | ||
48 | |||
49 | correct = sp->sp_pwdp; | ||
50 | } | ||
51 | else | ||
52 | #endif | ||
53 | correct = pw->pw_passwd; | ||
54 | |||
55 | if (!correct || correct[0] == '\0') | ||
56 | return 1; | ||
57 | |||
58 | unencrypted = bb_askpass(0, "Password: "); | ||
59 | if (!unencrypted) { | ||
60 | return 0; | ||
61 | } | ||
62 | encrypted = crypt(unencrypted, correct); | ||
63 | memset(unencrypted, 0, strlen(unencrypted)); | ||
64 | return (!strcmp(encrypted, correct)) ? 1 : 0; | ||
65 | } | ||
diff --git a/libbb/crc32.c b/libbb/crc32.c new file mode 100644 index 000000000..1e4a57e8a --- /dev/null +++ b/libbb/crc32.c | |||
@@ -0,0 +1,39 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * CRC32 table fill function | ||
4 | * Copyright (C) 2006 by Rob Sullivan <cogito.ergo.cogito@gmail.com> | ||
5 | * (I can't really claim much credit however, as the algorithm is | ||
6 | * very well-known) | ||
7 | * | ||
8 | * The following function creates a CRC32 table depending on whether | ||
9 | * a big-endian (0x04c11db7) or little-endian (0xedb88320) CRC32 is | ||
10 | * required. Admittedly, there are other CRC32 polynomials floating | ||
11 | * around, but Busybox doesn't use them. | ||
12 | * | ||
13 | * endian = 1: big-endian | ||
14 | * endian = 0: little-endian | ||
15 | */ | ||
16 | |||
17 | #include "libbb.h" | ||
18 | |||
19 | uint32_t *crc32_filltable(int endian) | ||
20 | { | ||
21 | |||
22 | uint32_t *crc_table = xmalloc(256 * sizeof(uint32_t)); | ||
23 | uint32_t polynomial = endian ? 0x04c11db7 : 0xedb88320; | ||
24 | uint32_t c; | ||
25 | int i, j; | ||
26 | |||
27 | for (i = 0; i < 256; i++) { | ||
28 | c = endian ? (i << 24) : i; | ||
29 | for (j = 8; j; j--) { | ||
30 | if (endian) | ||
31 | c = (c&0x80000000) ? ((c << 1) ^ polynomial) : (c << 1); | ||
32 | else | ||
33 | c = (c&1) ? ((c >> 1) ^ polynomial) : (c >> 1); | ||
34 | } | ||
35 | *crc_table++ = c; | ||
36 | } | ||
37 | |||
38 | return crc_table - 256; | ||
39 | } | ||
diff --git a/libbb/create_icmp6_socket.c b/libbb/create_icmp6_socket.c new file mode 100644 index 000000000..c3d1b5578 --- /dev/null +++ b/libbb/create_icmp6_socket.c | |||
@@ -0,0 +1,39 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * create raw socket for icmp (IPv6 version) protocol test permission | ||
6 | * and drop root privileges if running setuid | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <sys/types.h> | ||
11 | #include <netdb.h> | ||
12 | #include <sys/socket.h> | ||
13 | #include <errno.h> | ||
14 | #include <unistd.h> | ||
15 | #include "libbb.h" | ||
16 | |||
17 | #ifdef CONFIG_FEATURE_IPV6 | ||
18 | int create_icmp6_socket(void) | ||
19 | { | ||
20 | struct protoent *proto; | ||
21 | int sock; | ||
22 | |||
23 | proto = getprotobyname("ipv6-icmp"); | ||
24 | /* if getprotobyname failed, just silently force | ||
25 | * proto->p_proto to have the correct value for "ipv6-icmp" */ | ||
26 | if ((sock = socket(AF_INET6, SOCK_RAW, | ||
27 | (proto ? proto->p_proto : IPPROTO_ICMPV6))) < 0) { | ||
28 | if (errno == EPERM) | ||
29 | bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); | ||
30 | else | ||
31 | bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket); | ||
32 | } | ||
33 | |||
34 | /* drop root privs if running setuid */ | ||
35 | xsetuid(getuid()); | ||
36 | |||
37 | return sock; | ||
38 | } | ||
39 | #endif | ||
diff --git a/libbb/create_icmp_socket.c b/libbb/create_icmp_socket.c new file mode 100644 index 000000000..431c4d8a7 --- /dev/null +++ b/libbb/create_icmp_socket.c | |||
@@ -0,0 +1,37 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * create raw socket for icmp protocol test permission | ||
6 | * and drop root privileges if running setuid | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <sys/types.h> | ||
11 | #include <netdb.h> | ||
12 | #include <sys/socket.h> | ||
13 | #include <errno.h> | ||
14 | #include <unistd.h> | ||
15 | #include "libbb.h" | ||
16 | |||
17 | int create_icmp_socket(void) | ||
18 | { | ||
19 | struct protoent *proto; | ||
20 | int sock; | ||
21 | |||
22 | proto = getprotobyname("icmp"); | ||
23 | /* if getprotobyname failed, just silently force | ||
24 | * proto->p_proto to have the correct value for "icmp" */ | ||
25 | if ((sock = socket(AF_INET, SOCK_RAW, | ||
26 | (proto ? proto->p_proto : 1))) < 0) { /* 1 == ICMP */ | ||
27 | if (errno == EPERM) | ||
28 | bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); | ||
29 | else | ||
30 | bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket); | ||
31 | } | ||
32 | |||
33 | /* drop root privs if running setuid */ | ||
34 | xsetuid(getuid()); | ||
35 | |||
36 | return sock; | ||
37 | } | ||
diff --git a/libbb/default_error_retval.c b/libbb/default_error_retval.c new file mode 100644 index 000000000..f4e46a4b5 --- /dev/null +++ b/libbb/default_error_retval.c | |||
@@ -0,0 +1,19 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
4 | * | ||
5 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
6 | */ | ||
7 | |||
8 | /* Seems silly to copyright a global variable. ;-) Oh well. | ||
9 | * | ||
10 | * At least one applet (cmp) returns a value different from the typical | ||
11 | * EXIT_FAILURE values (1) when an error occurs. So, make it configurable | ||
12 | * by the applet. I suppose we could use a wrapper function to set it, but | ||
13 | * that too seems silly. | ||
14 | */ | ||
15 | |||
16 | #include <stdlib.h> | ||
17 | #include "libbb.h" | ||
18 | |||
19 | int xfunc_error_retval = EXIT_FAILURE; | ||
diff --git a/libbb/device_open.c b/libbb/device_open.c new file mode 100644 index 000000000..c788b6982 --- /dev/null +++ b/libbb/device_open.c | |||
@@ -0,0 +1,33 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdio.h> | ||
11 | #include <fcntl.h> | ||
12 | #include "libbb.h" | ||
13 | |||
14 | |||
15 | /* try to open up the specified device */ | ||
16 | int device_open(const char *device, int mode) | ||
17 | { | ||
18 | int m, f, fd = -1; | ||
19 | |||
20 | m = mode | O_NONBLOCK; | ||
21 | |||
22 | /* Retry up to 5 times */ | ||
23 | /* TODO: explain why it can't be considered insane */ | ||
24 | for (f = 0; f < 5; f++) | ||
25 | if ((fd = open(device, m, 0600)) >= 0) | ||
26 | break; | ||
27 | if (fd < 0) | ||
28 | return fd; | ||
29 | /* Reset original flags. */ | ||
30 | if (m != mode) | ||
31 | fcntl(fd, F_SETFL, mode); | ||
32 | return fd; | ||
33 | } | ||
diff --git a/libbb/dump.c b/libbb/dump.c new file mode 100644 index 000000000..10710894e --- /dev/null +++ b/libbb/dump.c | |||
@@ -0,0 +1,803 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Support code for the hexdump and od applets, | ||
4 | * based on code from util-linux v 2.11l | ||
5 | * | ||
6 | * Copyright (c) 1989 | ||
7 | * The Regents of the University of California. All rights reserved. | ||
8 | * | ||
9 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
10 | * | ||
11 | * Original copyright notice is retained at the end of this file. | ||
12 | */ | ||
13 | |||
14 | #include "libbb.h" | ||
15 | #include "dump.h" | ||
16 | |||
17 | enum _vflag bb_dump_vflag = FIRST; | ||
18 | FS *bb_dump_fshead; /* head of format strings */ | ||
19 | static FU *endfu; | ||
20 | static char **_argv; | ||
21 | static off_t savaddress; /* saved address/offset in stream */ | ||
22 | static off_t eaddress; /* end address */ | ||
23 | static off_t address; /* address/offset in stream */ | ||
24 | off_t bb_dump_skip; /* bytes to skip */ | ||
25 | static int exitval; /* final exit value */ | ||
26 | int bb_dump_blocksize; /* data block size */ | ||
27 | int bb_dump_length = -1; /* max bytes to read */ | ||
28 | |||
29 | static const char index_str[] = ".#-+ 0123456789"; | ||
30 | |||
31 | static const char size_conv_str[] = | ||
32 | "\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG"; | ||
33 | |||
34 | static const char lcc[] = "diouxX"; | ||
35 | |||
36 | int bb_dump_size(FS * fs) | ||
37 | { | ||
38 | FU *fu; | ||
39 | int bcnt, cur_size; | ||
40 | char *fmt; | ||
41 | const char *p; | ||
42 | int prec; | ||
43 | |||
44 | /* figure out the data block bb_dump_size needed for each format unit */ | ||
45 | for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) { | ||
46 | if (fu->bcnt) { | ||
47 | cur_size += fu->bcnt * fu->reps; | ||
48 | continue; | ||
49 | } | ||
50 | for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) { | ||
51 | if (*fmt != '%') | ||
52 | continue; | ||
53 | /* | ||
54 | * bb_dump_skip any special chars -- save precision in | ||
55 | * case it's a %s format. | ||
56 | */ | ||
57 | while (strchr(index_str + 1, *++fmt)); | ||
58 | if (*fmt == '.' && isdigit(*++fmt)) { | ||
59 | prec = atoi(fmt); | ||
60 | while (isdigit(*++fmt)); | ||
61 | } | ||
62 | if (!(p = strchr(size_conv_str + 12, *fmt))) { | ||
63 | if (*fmt == 's') { | ||
64 | bcnt += prec; | ||
65 | } else if (*fmt == '_') { | ||
66 | ++fmt; | ||
67 | if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) { | ||
68 | bcnt += 1; | ||
69 | } | ||
70 | } | ||
71 | } else { | ||
72 | bcnt += size_conv_str[p - (size_conv_str + 12)]; | ||
73 | } | ||
74 | } | ||
75 | cur_size += bcnt * fu->reps; | ||
76 | } | ||
77 | return cur_size; | ||
78 | } | ||
79 | |||
80 | static void rewrite(FS * fs) | ||
81 | { | ||
82 | enum { NOTOKAY, USEBCNT, USEPREC } sokay; | ||
83 | PR *pr, **nextpr = NULL; | ||
84 | FU *fu; | ||
85 | char *p1, *p2, *p3; | ||
86 | char savech, *fmtp; | ||
87 | const char *byte_count_str; | ||
88 | int nconv, prec = 0; | ||
89 | |||
90 | for (fu = fs->nextfu; fu; fu = fu->nextfu) { | ||
91 | /* | ||
92 | * break each format unit into print units; each | ||
93 | * conversion character gets its own. | ||
94 | */ | ||
95 | for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { | ||
96 | /* NOSTRICT */ | ||
97 | /* DBU:[dvae@cray.com] calloc so that forward ptrs start out NULL*/ | ||
98 | pr = xzalloc(sizeof(PR)); | ||
99 | if (!fu->nextpr) | ||
100 | fu->nextpr = pr; | ||
101 | /* ignore nextpr -- its unused inside the loop and is | ||
102 | * uninitialized 1st time thru. | ||
103 | */ | ||
104 | |||
105 | /* bb_dump_skip preceding text and up to the next % sign */ | ||
106 | for (p1 = fmtp; *p1 && *p1 != '%'; ++p1); | ||
107 | |||
108 | /* only text in the string */ | ||
109 | if (!*p1) { | ||
110 | pr->fmt = fmtp; | ||
111 | pr->flags = F_TEXT; | ||
112 | break; | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * get precision for %s -- if have a byte count, don't | ||
117 | * need it. | ||
118 | */ | ||
119 | if (fu->bcnt) { | ||
120 | sokay = USEBCNT; | ||
121 | /* bb_dump_skip to conversion character */ | ||
122 | for (++p1; strchr(index_str, *p1); ++p1); | ||
123 | } else { | ||
124 | /* bb_dump_skip any special chars, field width */ | ||
125 | while (strchr(index_str + 1, *++p1)); | ||
126 | if (*p1 == '.' && isdigit(*++p1)) { | ||
127 | sokay = USEPREC; | ||
128 | prec = atoi(p1); | ||
129 | while (isdigit(*++p1)); | ||
130 | } else | ||
131 | sokay = NOTOKAY; | ||
132 | } | ||
133 | |||
134 | p2 = p1 + 1; /* set end pointer */ | ||
135 | |||
136 | /* | ||
137 | * figure out the byte count for each conversion; | ||
138 | * rewrite the format as necessary, set up blank- | ||
139 | * pbb_dump_adding for end of data. | ||
140 | */ | ||
141 | |||
142 | if (*p1 == 'c') { | ||
143 | pr->flags = F_CHAR; | ||
144 | DO_BYTE_COUNT_1: | ||
145 | byte_count_str = "\001"; | ||
146 | DO_BYTE_COUNT: | ||
147 | if (fu->bcnt) { | ||
148 | do { | ||
149 | if (fu->bcnt == *byte_count_str) { | ||
150 | break; | ||
151 | } | ||
152 | } while (*++byte_count_str); | ||
153 | } | ||
154 | /* Unlike the original, output the remainder of the format string. */ | ||
155 | if (!*byte_count_str) { | ||
156 | bb_error_msg_and_die("bad byte count for conversion character %s", p1); | ||
157 | } | ||
158 | pr->bcnt = *byte_count_str; | ||
159 | } else if (*p1 == 'l') { | ||
160 | ++p2; | ||
161 | ++p1; | ||
162 | DO_INT_CONV: | ||
163 | { | ||
164 | const char *e; | ||
165 | if (!(e = strchr(lcc, *p1))) { | ||
166 | goto DO_BAD_CONV_CHAR; | ||
167 | } | ||
168 | pr->flags = F_INT; | ||
169 | if (e > lcc + 1) { | ||
170 | pr->flags = F_UINT; | ||
171 | } | ||
172 | byte_count_str = "\004\002\001"; | ||
173 | goto DO_BYTE_COUNT; | ||
174 | } | ||
175 | /* NOTREACHED */ | ||
176 | } else if (strchr(lcc, *p1)) { | ||
177 | goto DO_INT_CONV; | ||
178 | } else if (strchr("eEfgG", *p1)) { | ||
179 | pr->flags = F_DBL; | ||
180 | byte_count_str = "\010\004"; | ||
181 | goto DO_BYTE_COUNT; | ||
182 | } else if (*p1 == 's') { | ||
183 | pr->flags = F_STR; | ||
184 | if (sokay == USEBCNT) { | ||
185 | pr->bcnt = fu->bcnt; | ||
186 | } else if (sokay == USEPREC) { | ||
187 | pr->bcnt = prec; | ||
188 | } else { /* NOTOKAY */ | ||
189 | bb_error_msg_and_die("%%s requires a precision or a byte count"); | ||
190 | } | ||
191 | } else if (*p1 == '_') { | ||
192 | ++p2; | ||
193 | switch (p1[1]) { | ||
194 | case 'A': | ||
195 | endfu = fu; | ||
196 | fu->flags |= F_IGNORE; | ||
197 | /* FALLTHROUGH */ | ||
198 | case 'a': | ||
199 | pr->flags = F_ADDRESS; | ||
200 | ++p2; | ||
201 | if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) { | ||
202 | goto DO_BAD_CONV_CHAR; | ||
203 | } | ||
204 | *p1 = p1[2]; | ||
205 | break; | ||
206 | case 'c': | ||
207 | pr->flags = F_C; | ||
208 | /* *p1 = 'c'; set in conv_c */ | ||
209 | goto DO_BYTE_COUNT_1; | ||
210 | case 'p': | ||
211 | pr->flags = F_P; | ||
212 | *p1 = 'c'; | ||
213 | goto DO_BYTE_COUNT_1; | ||
214 | case 'u': | ||
215 | pr->flags = F_U; | ||
216 | /* *p1 = 'c'; set in conv_u */ | ||
217 | goto DO_BYTE_COUNT_1; | ||
218 | default: | ||
219 | goto DO_BAD_CONV_CHAR; | ||
220 | } | ||
221 | } else { | ||
222 | DO_BAD_CONV_CHAR: | ||
223 | bb_error_msg_and_die("bad conversion character %%%s", p1); | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | * copy to PR format string, set conversion character | ||
228 | * pointer, update original. | ||
229 | */ | ||
230 | savech = *p2; | ||
231 | p1[1] = '\0'; | ||
232 | pr->fmt = xstrdup(fmtp); | ||
233 | *p2 = savech; | ||
234 | pr->cchar = pr->fmt + (p1 - fmtp); | ||
235 | |||
236 | /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost. | ||
237 | * Skip subsequent text and up to the next % sign and tack the | ||
238 | * additional text onto fmt: eg. if fmt is "%x is a HEX number", | ||
239 | * we lose the " is a HEX number" part of fmt. | ||
240 | */ | ||
241 | for (p3 = p2; *p3 && *p3 != '%'; p3++); | ||
242 | if (p3 > p2) | ||
243 | { | ||
244 | savech = *p3; | ||
245 | *p3 = '\0'; | ||
246 | pr->fmt = xrealloc(pr->fmt, strlen(pr->fmt)+(p3-p2)+1); | ||
247 | strcat(pr->fmt, p2); | ||
248 | *p3 = savech; | ||
249 | p2 = p3; | ||
250 | } | ||
251 | |||
252 | fmtp = p2; | ||
253 | |||
254 | /* only one conversion character if byte count */ | ||
255 | if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) { | ||
256 | bb_error_msg_and_die("byte count with multiple conversion characters"); | ||
257 | } | ||
258 | } | ||
259 | /* | ||
260 | * if format unit byte count not specified, figure it out | ||
261 | * so can adjust rep count later. | ||
262 | */ | ||
263 | if (!fu->bcnt) | ||
264 | for (pr = fu->nextpr; pr; pr = pr->nextpr) | ||
265 | fu->bcnt += pr->bcnt; | ||
266 | } | ||
267 | /* | ||
268 | * if the format string interprets any data at all, and it's | ||
269 | * not the same as the bb_dump_blocksize, and its last format unit | ||
270 | * interprets any data at all, and has no iteration count, | ||
271 | * repeat it as necessary. | ||
272 | * | ||
273 | * if, rep count is greater than 1, no trailing whitespace | ||
274 | * gets output from the last iteration of the format unit. | ||
275 | */ | ||
276 | for (fu = fs->nextfu;; fu = fu->nextfu) { | ||
277 | if (!fu->nextfu && fs->bcnt < bb_dump_blocksize && | ||
278 | !(fu->flags & F_SETREP) && fu->bcnt) | ||
279 | fu->reps += (bb_dump_blocksize - fs->bcnt) / fu->bcnt; | ||
280 | if (fu->reps > 1) { | ||
281 | for (pr = fu->nextpr;; pr = pr->nextpr) | ||
282 | if (!pr->nextpr) | ||
283 | break; | ||
284 | for (p1 = pr->fmt, p2 = NULL; *p1; ++p1) | ||
285 | p2 = isspace(*p1) ? p1 : NULL; | ||
286 | if (p2) | ||
287 | pr->nospace = p2; | ||
288 | } | ||
289 | if (!fu->nextfu) | ||
290 | break; | ||
291 | } | ||
292 | } | ||
293 | |||
294 | static void do_skip(char *fname, int statok) | ||
295 | { | ||
296 | struct stat sbuf; | ||
297 | |||
298 | if (statok) { | ||
299 | if (fstat(STDIN_FILENO, &sbuf)) { | ||
300 | bb_perror_msg_and_die("%s", fname); | ||
301 | } | ||
302 | if ((!(S_ISCHR(sbuf.st_mode) || | ||
303 | S_ISBLK(sbuf.st_mode) || | ||
304 | S_ISFIFO(sbuf.st_mode))) && bb_dump_skip >= sbuf.st_size) { | ||
305 | /* If bb_dump_size valid and bb_dump_skip >= size */ | ||
306 | bb_dump_skip -= sbuf.st_size; | ||
307 | address += sbuf.st_size; | ||
308 | return; | ||
309 | } | ||
310 | } | ||
311 | if (fseek(stdin, bb_dump_skip, SEEK_SET)) { | ||
312 | bb_perror_msg_and_die("%s", fname); | ||
313 | } | ||
314 | savaddress = address += bb_dump_skip; | ||
315 | bb_dump_skip = 0; | ||
316 | } | ||
317 | |||
318 | static int next(char **argv) | ||
319 | { | ||
320 | static int done; | ||
321 | int statok; | ||
322 | |||
323 | if (argv) { | ||
324 | _argv = argv; | ||
325 | return 1; | ||
326 | } | ||
327 | for (;;) { | ||
328 | if (*_argv) { | ||
329 | if (!(freopen(*_argv, "r", stdin))) { | ||
330 | bb_perror_msg("%s", *_argv); | ||
331 | exitval = 1; | ||
332 | ++_argv; | ||
333 | continue; | ||
334 | } | ||
335 | statok = done = 1; | ||
336 | } else { | ||
337 | if (done++) | ||
338 | return 0; | ||
339 | statok = 0; | ||
340 | } | ||
341 | if (bb_dump_skip) | ||
342 | do_skip(statok ? *_argv : "stdin", statok); | ||
343 | if (*_argv) | ||
344 | ++_argv; | ||
345 | if (!bb_dump_skip) | ||
346 | return 1; | ||
347 | } | ||
348 | /* NOTREACHED */ | ||
349 | } | ||
350 | |||
351 | static unsigned char *get(void) | ||
352 | { | ||
353 | static int ateof = 1; | ||
354 | static unsigned char *curp=NULL, *savp; /*DBU:[dave@cray.com]initialize curp */ | ||
355 | int n; | ||
356 | int need, nread; | ||
357 | unsigned char *tmpp; | ||
358 | |||
359 | if (!curp) { | ||
360 | address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/ | ||
361 | curp = (unsigned char *) xmalloc(bb_dump_blocksize); | ||
362 | savp = (unsigned char *) xmalloc(bb_dump_blocksize); | ||
363 | } else { | ||
364 | tmpp = curp; | ||
365 | curp = savp; | ||
366 | savp = tmpp; | ||
367 | address = savaddress += bb_dump_blocksize; | ||
368 | } | ||
369 | for (need = bb_dump_blocksize, nread = 0;;) { | ||
370 | /* | ||
371 | * if read the right number of bytes, or at EOF for one file, | ||
372 | * and no other files are available, zero-pad the rest of the | ||
373 | * block and set the end flag. | ||
374 | */ | ||
375 | if (!bb_dump_length || (ateof && !next((char **) NULL))) { | ||
376 | if (need == bb_dump_blocksize) { | ||
377 | return NULL; | ||
378 | } | ||
379 | if (bb_dump_vflag != ALL && !memcmp(curp, savp, nread)) { | ||
380 | if (bb_dump_vflag != DUP) { | ||
381 | puts("*"); | ||
382 | } | ||
383 | return NULL; | ||
384 | } | ||
385 | memset((char *) curp + nread, 0, need); | ||
386 | eaddress = address + nread; | ||
387 | return curp; | ||
388 | } | ||
389 | n = fread((char *) curp + nread, sizeof(unsigned char), | ||
390 | bb_dump_length == -1 ? need : MIN(bb_dump_length, need), stdin); | ||
391 | if (!n) { | ||
392 | if (ferror(stdin)) { | ||
393 | bb_perror_msg("%s", _argv[-1]); | ||
394 | } | ||
395 | ateof = 1; | ||
396 | continue; | ||
397 | } | ||
398 | ateof = 0; | ||
399 | if (bb_dump_length != -1) { | ||
400 | bb_dump_length -= n; | ||
401 | } | ||
402 | if (!(need -= n)) { | ||
403 | if (bb_dump_vflag == ALL || bb_dump_vflag == FIRST | ||
404 | || memcmp(curp, savp, bb_dump_blocksize)) { | ||
405 | if (bb_dump_vflag == DUP || bb_dump_vflag == FIRST) { | ||
406 | bb_dump_vflag = WAIT; | ||
407 | } | ||
408 | return curp; | ||
409 | } | ||
410 | if (bb_dump_vflag == WAIT) { | ||
411 | puts("*"); | ||
412 | } | ||
413 | bb_dump_vflag = DUP; | ||
414 | address = savaddress += bb_dump_blocksize; | ||
415 | need = bb_dump_blocksize; | ||
416 | nread = 0; | ||
417 | } else { | ||
418 | nread += n; | ||
419 | } | ||
420 | } | ||
421 | } | ||
422 | |||
423 | static void bpad(PR * pr) | ||
424 | { | ||
425 | char *p1, *p2; | ||
426 | |||
427 | /* | ||
428 | * remove all conversion flags; '-' is the only one valid | ||
429 | * with %s, and it's not useful here. | ||
430 | */ | ||
431 | pr->flags = F_BPAD; | ||
432 | *pr->cchar = 's'; | ||
433 | for (p1 = pr->fmt; *p1 != '%'; ++p1); | ||
434 | for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1) | ||
435 | if (pr->nospace) pr->nospace--; | ||
436 | while ((*p2++ = *p1++) != 0); | ||
437 | } | ||
438 | |||
439 | static const char conv_str[] = | ||
440 | "\0\\0\0" | ||
441 | "\007\\a\0" /* \a */ | ||
442 | "\b\\b\0" | ||
443 | "\f\\b\0" | ||
444 | "\n\\n\0" | ||
445 | "\r\\r\0" | ||
446 | "\t\\t\0" | ||
447 | "\v\\v\0" | ||
448 | "\0"; | ||
449 | |||
450 | |||
451 | static void conv_c(PR * pr, unsigned char * p) | ||
452 | { | ||
453 | const char *str = conv_str; | ||
454 | char buf[10]; | ||
455 | |||
456 | do { | ||
457 | if (*p == *str) { | ||
458 | ++str; | ||
459 | goto strpr; | ||
460 | } | ||
461 | str += 4; | ||
462 | } while (*str); | ||
463 | |||
464 | if (isprint(*p)) { | ||
465 | *pr->cchar = 'c'; | ||
466 | (void) printf(pr->fmt, *p); | ||
467 | } else { | ||
468 | sprintf(buf, "%03o", (int) *p); | ||
469 | str = buf; | ||
470 | strpr: | ||
471 | *pr->cchar = 's'; | ||
472 | printf(pr->fmt, str); | ||
473 | } | ||
474 | } | ||
475 | |||
476 | static void conv_u(PR * pr, unsigned char * p) | ||
477 | { | ||
478 | static const char list[] = | ||
479 | "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0" | ||
480 | "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_" | ||
481 | "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0" | ||
482 | "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us"; | ||
483 | |||
484 | /* od used nl, not lf */ | ||
485 | if (*p <= 0x1f) { | ||
486 | *pr->cchar = 's'; | ||
487 | printf(pr->fmt, list + (4 * (int)*p)); | ||
488 | } else if (*p == 0x7f) { | ||
489 | *pr->cchar = 's'; | ||
490 | printf(pr->fmt, "del"); | ||
491 | } else if (isprint(*p)) { | ||
492 | *pr->cchar = 'c'; | ||
493 | printf(pr->fmt, *p); | ||
494 | } else { | ||
495 | *pr->cchar = 'x'; | ||
496 | printf(pr->fmt, (int) *p); | ||
497 | } | ||
498 | } | ||
499 | |||
500 | static void display(void) | ||
501 | { | ||
502 | /* extern FU *endfu; */ | ||
503 | FS *fs; | ||
504 | FU *fu; | ||
505 | PR *pr; | ||
506 | int cnt; | ||
507 | unsigned char *bp; | ||
508 | |||
509 | off_t saveaddress; | ||
510 | unsigned char savech = 0, *savebp; | ||
511 | |||
512 | while ((bp = get()) != NULL) { | ||
513 | for (fs = bb_dump_fshead, savebp = bp, saveaddress = address; fs; | ||
514 | fs = fs->nextfs, bp = savebp, address = saveaddress) { | ||
515 | for (fu = fs->nextfu; fu; fu = fu->nextfu) { | ||
516 | if (fu->flags & F_IGNORE) { | ||
517 | break; | ||
518 | } | ||
519 | for (cnt = fu->reps; cnt; --cnt) { | ||
520 | for (pr = fu->nextpr; pr; address += pr->bcnt, | ||
521 | bp += pr->bcnt, pr = pr->nextpr) { | ||
522 | if (eaddress && address >= eaddress && | ||
523 | !(pr->flags & (F_TEXT | F_BPAD))) { | ||
524 | bpad(pr); | ||
525 | } | ||
526 | if (cnt == 1 && pr->nospace) { | ||
527 | savech = *pr->nospace; | ||
528 | *pr->nospace = '\0'; | ||
529 | } | ||
530 | /* PRINT; */ | ||
531 | switch (pr->flags) { | ||
532 | case F_ADDRESS: | ||
533 | printf(pr->fmt, (unsigned int) address); | ||
534 | break; | ||
535 | case F_BPAD: | ||
536 | printf(pr->fmt, ""); | ||
537 | break; | ||
538 | case F_C: | ||
539 | conv_c(pr, bp); | ||
540 | break; | ||
541 | case F_CHAR: | ||
542 | printf(pr->fmt, *bp); | ||
543 | break; | ||
544 | case F_DBL:{ | ||
545 | double dval; | ||
546 | float fval; | ||
547 | |||
548 | switch (pr->bcnt) { | ||
549 | case 4: | ||
550 | memmove((char *) &fval, (char *) bp, | ||
551 | sizeof(fval)); | ||
552 | printf(pr->fmt, fval); | ||
553 | break; | ||
554 | case 8: | ||
555 | memmove((char *) &dval, (char *) bp, | ||
556 | sizeof(dval)); | ||
557 | printf(pr->fmt, dval); | ||
558 | break; | ||
559 | } | ||
560 | break; | ||
561 | } | ||
562 | case F_INT:{ | ||
563 | int ival; | ||
564 | short sval; | ||
565 | |||
566 | switch (pr->bcnt) { | ||
567 | case 1: | ||
568 | printf(pr->fmt, (int) *bp); | ||
569 | break; | ||
570 | case 2: | ||
571 | memmove((char *) &sval, (char *) bp, | ||
572 | sizeof(sval)); | ||
573 | printf(pr->fmt, (int) sval); | ||
574 | break; | ||
575 | case 4: | ||
576 | memmove((char *) &ival, (char *) bp, | ||
577 | sizeof(ival)); | ||
578 | printf(pr->fmt, ival); | ||
579 | break; | ||
580 | } | ||
581 | break; | ||
582 | } | ||
583 | case F_P: | ||
584 | printf(pr->fmt, isprint(*bp) ? *bp : '.'); | ||
585 | break; | ||
586 | case F_STR: | ||
587 | printf(pr->fmt, (char *) bp); | ||
588 | break; | ||
589 | case F_TEXT: | ||
590 | printf(pr->fmt); | ||
591 | break; | ||
592 | case F_U: | ||
593 | conv_u(pr, bp); | ||
594 | break; | ||
595 | case F_UINT:{ | ||
596 | unsigned int ival; | ||
597 | unsigned short sval; | ||
598 | |||
599 | switch (pr->bcnt) { | ||
600 | case 1: | ||
601 | printf(pr->fmt, (unsigned int) * bp); | ||
602 | break; | ||
603 | case 2: | ||
604 | memmove((char *) &sval, (char *) bp, | ||
605 | sizeof(sval)); | ||
606 | printf(pr->fmt, (unsigned int) sval); | ||
607 | break; | ||
608 | case 4: | ||
609 | memmove((char *) &ival, (char *) bp, | ||
610 | sizeof(ival)); | ||
611 | printf(pr->fmt, ival); | ||
612 | break; | ||
613 | } | ||
614 | break; | ||
615 | } | ||
616 | } | ||
617 | if (cnt == 1 && pr->nospace) { | ||
618 | *pr->nospace = savech; | ||
619 | } | ||
620 | } | ||
621 | } | ||
622 | } | ||
623 | } | ||
624 | } | ||
625 | if (endfu) { | ||
626 | /* | ||
627 | * if eaddress not set, error or file bb_dump_size was multiple of | ||
628 | * bb_dump_blocksize, and no partial block ever found. | ||
629 | */ | ||
630 | if (!eaddress) { | ||
631 | if (!address) { | ||
632 | return; | ||
633 | } | ||
634 | eaddress = address; | ||
635 | } | ||
636 | for (pr = endfu->nextpr; pr; pr = pr->nextpr) { | ||
637 | switch (pr->flags) { | ||
638 | case F_ADDRESS: | ||
639 | (void) printf(pr->fmt, (unsigned int) eaddress); | ||
640 | break; | ||
641 | case F_TEXT: | ||
642 | (void) printf(pr->fmt); | ||
643 | break; | ||
644 | } | ||
645 | } | ||
646 | } | ||
647 | } | ||
648 | |||
649 | int bb_dump_dump(char **argv) | ||
650 | { | ||
651 | FS *tfs; | ||
652 | |||
653 | /* figure out the data block bb_dump_size */ | ||
654 | for (bb_dump_blocksize = 0, tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) { | ||
655 | tfs->bcnt = bb_dump_size(tfs); | ||
656 | if (bb_dump_blocksize < tfs->bcnt) { | ||
657 | bb_dump_blocksize = tfs->bcnt; | ||
658 | } | ||
659 | } | ||
660 | /* rewrite the rules, do syntax checking */ | ||
661 | for (tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) { | ||
662 | rewrite(tfs); | ||
663 | } | ||
664 | |||
665 | next(argv); | ||
666 | display(); | ||
667 | |||
668 | return exitval; | ||
669 | } | ||
670 | |||
671 | void bb_dump_add(const char *fmt) | ||
672 | { | ||
673 | const char *p; | ||
674 | char *p1; | ||
675 | char *p2; | ||
676 | static FS **nextfs; | ||
677 | FS *tfs; | ||
678 | FU *tfu, **nextfu; | ||
679 | const char *savep; | ||
680 | |||
681 | /* start new linked list of format units */ | ||
682 | tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */ | ||
683 | if (!bb_dump_fshead) { | ||
684 | bb_dump_fshead = tfs; | ||
685 | } else { | ||
686 | *nextfs = tfs; | ||
687 | } | ||
688 | nextfs = &tfs->nextfs; | ||
689 | nextfu = &tfs->nextfu; | ||
690 | |||
691 | /* take the format string and break it up into format units */ | ||
692 | for (p = fmt;;) { | ||
693 | /* bb_dump_skip leading white space */ | ||
694 | p = skip_whitespace(p); | ||
695 | if (!*p) { | ||
696 | break; | ||
697 | } | ||
698 | |||
699 | /* allocate a new format unit and link it in */ | ||
700 | /* NOSTRICT */ | ||
701 | /* DBU:[dave@cray.com] calloc so that forward pointers start out NULL */ | ||
702 | tfu = xzalloc(sizeof(FU)); | ||
703 | *nextfu = tfu; | ||
704 | nextfu = &tfu->nextfu; | ||
705 | tfu->reps = 1; | ||
706 | |||
707 | /* if leading digit, repetition count */ | ||
708 | if (isdigit(*p)) { | ||
709 | for (savep = p; isdigit(*p); ++p); | ||
710 | if (!isspace(*p) && *p != '/') { | ||
711 | bb_error_msg_and_die("bad format {%s}", fmt); | ||
712 | } | ||
713 | /* may overwrite either white space or slash */ | ||
714 | tfu->reps = atoi(savep); | ||
715 | tfu->flags = F_SETREP; | ||
716 | /* bb_dump_skip trailing white space */ | ||
717 | p = skip_whitespace(++p); | ||
718 | } | ||
719 | |||
720 | /* bb_dump_skip slash and trailing white space */ | ||
721 | if (*p == '/') { | ||
722 | p = skip_whitespace(++p); | ||
723 | } | ||
724 | |||
725 | /* byte count */ | ||
726 | if (isdigit(*p)) { | ||
727 | for (savep = p; isdigit(*p); ++p); | ||
728 | if (!isspace(*p)) { | ||
729 | bb_error_msg_and_die("bad format {%s}", fmt); | ||
730 | } | ||
731 | tfu->bcnt = atoi(savep); | ||
732 | /* bb_dump_skip trailing white space */ | ||
733 | p = skip_whitespace(++p); | ||
734 | } | ||
735 | |||
736 | /* format */ | ||
737 | if (*p != '"') { | ||
738 | bb_error_msg_and_die("bad format {%s}", fmt); | ||
739 | } | ||
740 | for (savep = ++p; *p != '"';) { | ||
741 | if (*p++ == 0) { | ||
742 | bb_error_msg_and_die("bad format {%s}", fmt); | ||
743 | } | ||
744 | } | ||
745 | tfu->fmt = xmalloc(p - savep + 1); | ||
746 | strncpy(tfu->fmt, savep, p - savep); | ||
747 | tfu->fmt[p - savep] = '\0'; | ||
748 | /* escape(tfu->fmt); */ | ||
749 | |||
750 | p1 = tfu->fmt; | ||
751 | |||
752 | /* alphabetic escape sequences have to be done in place */ | ||
753 | for (p2 = p1;; ++p1, ++p2) { | ||
754 | if (!*p1) { | ||
755 | *p2 = *p1; | ||
756 | break; | ||
757 | } | ||
758 | if (*p1 == '\\') { | ||
759 | const char *cs = conv_str + 4; | ||
760 | ++p1; | ||
761 | *p2 = *p1; | ||
762 | do { | ||
763 | if (*p1 == cs[2]) { | ||
764 | *p2 = cs[0]; | ||
765 | break; | ||
766 | } | ||
767 | cs += 4; | ||
768 | } while (*cs); | ||
769 | } | ||
770 | } | ||
771 | |||
772 | p++; | ||
773 | } | ||
774 | } | ||
775 | |||
776 | /* | ||
777 | * Copyright (c) 1989 The Regents of the University of California. | ||
778 | * All rights reserved. | ||
779 | * | ||
780 | * Redistribution and use in source and binary forms, with or without | ||
781 | * modification, are permitted provided that the following conditions | ||
782 | * are met: | ||
783 | * 1. Redistributions of source code must retain the above copyright | ||
784 | * notice, this list of conditions and the following disclaimer. | ||
785 | * 2. Redistributions in binary form must reproduce the above copyright | ||
786 | * notice, this list of conditions and the following disclaimer in the | ||
787 | * documentation and/or other materials provided with the distribution. | ||
788 | * 3. Neither the name of the University nor the names of its contributors | ||
789 | * may be used to endorse or promote products derived from this software | ||
790 | * without specific prior written permission. | ||
791 | * | ||
792 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
793 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
794 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
795 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
796 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
797 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
798 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
799 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
800 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
801 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
802 | * SUCH DAMAGE. | ||
803 | */ | ||
diff --git a/libbb/error_msg.c b/libbb/error_msg.c new file mode 100644 index 000000000..b2141f3a2 --- /dev/null +++ b/libbb/error_msg.c | |||
@@ -0,0 +1,23 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdio.h> | ||
11 | #include <errno.h> | ||
12 | #include <string.h> | ||
13 | #include <stdlib.h> | ||
14 | #include "libbb.h" | ||
15 | |||
16 | void bb_error_msg(const char *s, ...) | ||
17 | { | ||
18 | va_list p; | ||
19 | |||
20 | va_start(p, s); | ||
21 | bb_verror_msg(s, p, NULL); | ||
22 | va_end(p); | ||
23 | } | ||
diff --git a/libbb/error_msg_and_die.c b/libbb/error_msg_and_die.c new file mode 100644 index 000000000..10d953513 --- /dev/null +++ b/libbb/error_msg_and_die.c | |||
@@ -0,0 +1,28 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdio.h> | ||
11 | #include <errno.h> | ||
12 | #include <string.h> | ||
13 | #include <stdlib.h> | ||
14 | #include "libbb.h" | ||
15 | |||
16 | int die_sleep; | ||
17 | |||
18 | void bb_error_msg_and_die(const char *s, ...) | ||
19 | { | ||
20 | va_list p; | ||
21 | |||
22 | va_start(p, s); | ||
23 | bb_verror_msg(s, p, NULL); | ||
24 | va_end(p); | ||
25 | if (die_sleep) | ||
26 | sleep(die_sleep); | ||
27 | exit(xfunc_error_retval); | ||
28 | } | ||
diff --git a/libbb/execable.c b/libbb/execable.c new file mode 100644 index 000000000..817c06736 --- /dev/null +++ b/libbb/execable.c | |||
@@ -0,0 +1,61 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 2006 Gabriel Somlo <somlo at cmu.edu> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | /* check if path points to an executable file; | ||
13 | * return 1 if found; | ||
14 | * return 0 otherwise; | ||
15 | */ | ||
16 | int execable_file(const char *name) | ||
17 | { | ||
18 | struct stat s; | ||
19 | return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode)); | ||
20 | } | ||
21 | |||
22 | /* search $PATH for an executable file; | ||
23 | * return allocated string containing full path if found; | ||
24 | * return NULL otherwise; | ||
25 | */ | ||
26 | char *find_execable(const char *filename) | ||
27 | { | ||
28 | char *path, *p, *n; | ||
29 | |||
30 | p = path = xstrdup(getenv("PATH")); | ||
31 | while (p) { | ||
32 | n = strchr(p, ':'); | ||
33 | if (n) | ||
34 | *n++ = '\0'; | ||
35 | if (*p != '\0') { /* it's not a PATH="foo::bar" situation */ | ||
36 | p = concat_path_file(p, filename); | ||
37 | if (execable_file(p)) { | ||
38 | free(path); | ||
39 | return p; | ||
40 | } | ||
41 | free(p); | ||
42 | } | ||
43 | p = n; | ||
44 | } | ||
45 | free(path); | ||
46 | return NULL; | ||
47 | } | ||
48 | |||
49 | /* search $PATH for an executable file; | ||
50 | * return 1 if found; | ||
51 | * return 0 otherwise; | ||
52 | */ | ||
53 | int exists_execable(const char *filename) | ||
54 | { | ||
55 | char *ret = find_execable(filename); | ||
56 | if (ret) { | ||
57 | free(ret); | ||
58 | return 1; | ||
59 | } | ||
60 | return 0; | ||
61 | } | ||
diff --git a/libbb/fclose_nonstdin.c b/libbb/fclose_nonstdin.c new file mode 100644 index 000000000..88e8474f2 --- /dev/null +++ b/libbb/fclose_nonstdin.c | |||
@@ -0,0 +1,23 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * fclose_nonstdin implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | /* A number of standard utilities can accept multiple command line args | ||
11 | * of '-' for stdin, according to SUSv3. So we encapsulate the check | ||
12 | * here to save a little space. | ||
13 | */ | ||
14 | |||
15 | #include "libbb.h" | ||
16 | |||
17 | int fclose_if_not_stdin(FILE *f) | ||
18 | { | ||
19 | if (f != stdin) { | ||
20 | return fclose(f); | ||
21 | } | ||
22 | return 0; | ||
23 | } | ||
diff --git a/libbb/fflush_stdout_and_exit.c b/libbb/fflush_stdout_and_exit.c new file mode 100644 index 000000000..456ce9513 --- /dev/null +++ b/libbb/fflush_stdout_and_exit.c | |||
@@ -0,0 +1,24 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * fflush_stdout_and_exit implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | /* Attempt to fflush(stdout), and exit with an error code if stdout is | ||
11 | * in an error state. | ||
12 | */ | ||
13 | |||
14 | #include "libbb.h" | ||
15 | |||
16 | void fflush_stdout_and_exit(int retval) | ||
17 | { | ||
18 | if (fflush(stdout)) { | ||
19 | retval = xfunc_error_retval; | ||
20 | } | ||
21 | if (die_sleep) | ||
22 | sleep(die_sleep); | ||
23 | exit(retval); | ||
24 | } | ||
diff --git a/libbb/fgets_str.c b/libbb/fgets_str.c new file mode 100644 index 000000000..1bc6c3b1c --- /dev/null +++ b/libbb/fgets_str.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) many different people. | ||
6 | * If you wrote this, please acknowledge your work. | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include "libbb.h" | ||
12 | |||
13 | /* Read up to (and including) TERMINATING_STRING from FILE and return it. | ||
14 | * Return NULL on EOF. */ | ||
15 | |||
16 | char *xmalloc_fgets_str(FILE *file, const char *terminating_string) | ||
17 | { | ||
18 | char *linebuf = NULL; | ||
19 | const int term_length = strlen(terminating_string); | ||
20 | int end_string_offset; | ||
21 | int linebufsz = 0; | ||
22 | int idx = 0; | ||
23 | int ch; | ||
24 | |||
25 | while (1) { | ||
26 | ch = fgetc(file); | ||
27 | if (ch == EOF) { | ||
28 | free(linebuf); | ||
29 | return NULL; | ||
30 | } | ||
31 | |||
32 | /* grow the line buffer as necessary */ | ||
33 | while (idx > linebufsz - 2) { | ||
34 | linebufsz += 200; | ||
35 | linebuf = xrealloc(linebuf, linebufsz); | ||
36 | } | ||
37 | |||
38 | linebuf[idx] = ch; | ||
39 | idx++; | ||
40 | |||
41 | /* Check for terminating string */ | ||
42 | end_string_offset = idx - term_length; | ||
43 | if (end_string_offset > 0 | ||
44 | && memcmp(&linebuf[end_string_offset], terminating_string, term_length) == 0 | ||
45 | ) { | ||
46 | idx -= term_length; | ||
47 | break; | ||
48 | } | ||
49 | } | ||
50 | linebuf = xrealloc(linebuf, idx + 1); | ||
51 | linebuf[idx] = '\0'; | ||
52 | return linebuf; | ||
53 | } | ||
diff --git a/libbb/find_mount_point.c b/libbb/find_mount_point.c new file mode 100644 index 000000000..cb00b9806 --- /dev/null +++ b/libbb/find_mount_point.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | #include <mntent.h> | ||
12 | |||
13 | /* | ||
14 | * Given a block device, find the mount table entry if that block device | ||
15 | * is mounted. | ||
16 | * | ||
17 | * Given any other file (or directory), find the mount table entry for its | ||
18 | * filesystem. | ||
19 | */ | ||
20 | struct mntent *find_mount_point(const char *name, const char *table) | ||
21 | { | ||
22 | struct stat s; | ||
23 | dev_t mountDevice; | ||
24 | FILE *mountTable; | ||
25 | struct mntent *mountEntry; | ||
26 | |||
27 | if (stat(name, &s) != 0) | ||
28 | return 0; | ||
29 | |||
30 | if ((s.st_mode & S_IFMT) == S_IFBLK) | ||
31 | mountDevice = s.st_rdev; | ||
32 | else | ||
33 | mountDevice = s.st_dev; | ||
34 | |||
35 | |||
36 | mountTable = setmntent(table ? table : bb_path_mtab_file, "r"); | ||
37 | if (!mountTable) | ||
38 | return 0; | ||
39 | |||
40 | while ((mountEntry = getmntent(mountTable)) != 0) { | ||
41 | if (strcmp(name, mountEntry->mnt_dir) == 0 | ||
42 | || strcmp(name, mountEntry->mnt_fsname) == 0 | ||
43 | ) { /* String match. */ | ||
44 | break; | ||
45 | } | ||
46 | if (stat(mountEntry->mnt_fsname, &s) == 0 && s.st_rdev == mountDevice) /* Match the device. */ | ||
47 | break; | ||
48 | if (stat(mountEntry->mnt_dir, &s) == 0 && s.st_dev == mountDevice) /* Match the directory's mount point. */ | ||
49 | break; | ||
50 | } | ||
51 | endmntent(mountTable); | ||
52 | return mountEntry; | ||
53 | } | ||
diff --git a/libbb/find_pid_by_name.c b/libbb/find_pid_by_name.c new file mode 100644 index 000000000..e98616940 --- /dev/null +++ b/libbb/find_pid_by_name.c | |||
@@ -0,0 +1,55 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | /* find_pid_by_name() | ||
13 | * | ||
14 | * Modified by Vladimir Oleynik for use with libbb/procps.c | ||
15 | * This finds the pid of the specified process. | ||
16 | * Currently, it's implemented by rummaging through | ||
17 | * the proc filesystem. | ||
18 | * | ||
19 | * Returns a list of all matching PIDs | ||
20 | * It is the caller's duty to free the returned pidlist. | ||
21 | */ | ||
22 | pid_t* find_pid_by_name(const char* procName) | ||
23 | { | ||
24 | pid_t* pidList; | ||
25 | int i = 0; | ||
26 | procps_status_t* p = NULL; | ||
27 | |||
28 | pidList = xmalloc(sizeof(*pidList)); | ||
29 | while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM))) { | ||
30 | if (strncmp(p->comm, procName, sizeof(p->comm)-1) == 0) { | ||
31 | pidList = xrealloc(pidList, sizeof(*pidList) * (i+2)); | ||
32 | pidList[i++] = p->pid; | ||
33 | } | ||
34 | } | ||
35 | |||
36 | pidList[i] = 0; | ||
37 | return pidList; | ||
38 | } | ||
39 | |||
40 | pid_t *pidlist_reverse(pid_t *pidList) | ||
41 | { | ||
42 | int i = 0; | ||
43 | while (pidList[i]) | ||
44 | i++; | ||
45 | if (--i >= 0) { | ||
46 | pid_t k; | ||
47 | int j; | ||
48 | for (j = 0; i > j; i--, j++) { | ||
49 | k = pidList[i]; | ||
50 | pidList[i] = pidList[j]; | ||
51 | pidList[j] = k; | ||
52 | } | ||
53 | } | ||
54 | return pidList; | ||
55 | } | ||
diff --git a/libbb/find_root_device.c b/libbb/find_root_device.c new file mode 100644 index 000000000..71b79b8d0 --- /dev/null +++ b/libbb/find_root_device.c | |||
@@ -0,0 +1,33 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | char *find_block_device(char *path) | ||
13 | { | ||
14 | DIR *dir; | ||
15 | struct dirent *entry; | ||
16 | struct stat st; | ||
17 | dev_t dev; | ||
18 | char *retpath=NULL; | ||
19 | |||
20 | if(stat(path, &st) || !(dir = opendir("/dev"))) return NULL; | ||
21 | dev = (st.st_mode & S_IFMT) == S_IFBLK ? st.st_rdev : st.st_dev; | ||
22 | while((entry = readdir(dir)) != NULL) { | ||
23 | char devpath[PATH_MAX]; | ||
24 | sprintf(devpath,"/dev/%s", entry->d_name); | ||
25 | if(!stat(devpath, &st) && S_ISBLK(st.st_mode) && st.st_rdev == dev) { | ||
26 | retpath = xstrdup(devpath); | ||
27 | break; | ||
28 | } | ||
29 | } | ||
30 | closedir(dir); | ||
31 | |||
32 | return retpath; | ||
33 | } | ||
diff --git a/libbb/full_write.c b/libbb/full_write.c new file mode 100644 index 000000000..bceac6de5 --- /dev/null +++ b/libbb/full_write.c | |||
@@ -0,0 +1,38 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdio.h> | ||
11 | #include <unistd.h> | ||
12 | #include "libbb.h" | ||
13 | |||
14 | /* | ||
15 | * Write all of the supplied buffer out to a file. | ||
16 | * This does multiple writes as necessary. | ||
17 | * Returns the amount written, or -1 on an error. | ||
18 | */ | ||
19 | ssize_t full_write(int fd, const void *buf, size_t len) | ||
20 | { | ||
21 | ssize_t cc; | ||
22 | ssize_t total; | ||
23 | |||
24 | total = 0; | ||
25 | |||
26 | while (len) { | ||
27 | cc = safe_write(fd, buf, len); | ||
28 | |||
29 | if (cc < 0) | ||
30 | return cc; /* write() returns -1 on failure. */ | ||
31 | |||
32 | total += cc; | ||
33 | buf = ((const char *)buf) + cc; | ||
34 | len -= cc; | ||
35 | } | ||
36 | |||
37 | return total; | ||
38 | } | ||
diff --git a/libbb/get_console.c b/libbb/get_console.c new file mode 100644 index 000000000..52ae3d215 --- /dev/null +++ b/libbb/get_console.c | |||
@@ -0,0 +1,77 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) many different people. If you wrote this, please | ||
6 | * acknowledge your work. | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include <stdio.h> | ||
12 | #include <errno.h> | ||
13 | #include <fcntl.h> | ||
14 | #include <unistd.h> | ||
15 | #include <sys/ioctl.h> | ||
16 | #include "libbb.h" | ||
17 | |||
18 | |||
19 | |||
20 | /* From <linux/kd.h> */ | ||
21 | enum { KDGKBTYPE = 0x4B33 }; /* get keyboard type */ | ||
22 | |||
23 | |||
24 | static int open_a_console(const char *fnam) | ||
25 | { | ||
26 | int fd; | ||
27 | |||
28 | /* try read-write */ | ||
29 | fd = open(fnam, O_RDWR); | ||
30 | |||
31 | /* if failed, try read-only */ | ||
32 | if (fd < 0 && errno == EACCES) | ||
33 | fd = open(fnam, O_RDONLY); | ||
34 | |||
35 | /* if failed, try write-only */ | ||
36 | if (fd < 0 && errno == EACCES) | ||
37 | fd = open(fnam, O_WRONLY); | ||
38 | |||
39 | return fd; | ||
40 | } | ||
41 | |||
42 | /* | ||
43 | * Get an fd for use with kbd/console ioctls. | ||
44 | * We try several things because opening /dev/console will fail | ||
45 | * if someone else used X (which does a chown on /dev/console). | ||
46 | */ | ||
47 | |||
48 | int get_console_fd(void) | ||
49 | { | ||
50 | int fd; | ||
51 | |||
52 | static const char * const choise_console_names[] = { | ||
53 | CONSOLE_DEV, CURRENT_VC, CURRENT_TTY | ||
54 | }; | ||
55 | |||
56 | for (fd = 2; fd >= 0; fd--) { | ||
57 | int fd4name; | ||
58 | int choise_fd; | ||
59 | char arg; | ||
60 | |||
61 | fd4name = open_a_console(choise_console_names[fd]); | ||
62 | chk_std: | ||
63 | choise_fd = fd4name >= 0 ? fd4name : fd; | ||
64 | |||
65 | arg = 0; | ||
66 | if (ioctl(choise_fd, KDGKBTYPE, &arg) == 0) | ||
67 | return choise_fd; | ||
68 | if(fd4name >= 0) { | ||
69 | close(fd4name); | ||
70 | fd4name = -1; | ||
71 | goto chk_std; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | bb_error_msg("cannot get file descriptor referring to console"); | ||
76 | return fd; /* total failure */ | ||
77 | } | ||
diff --git a/libbb/get_last_path_component.c b/libbb/get_last_path_component.c new file mode 100644 index 000000000..311909726 --- /dev/null +++ b/libbb/get_last_path_component.c | |||
@@ -0,0 +1,32 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * bb_get_last_path_component implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2001 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | char *bb_get_last_path_component(char *path) | ||
13 | { | ||
14 | char *first = path; | ||
15 | char *last; | ||
16 | |||
17 | last = path - 1; | ||
18 | |||
19 | while (*path) { | ||
20 | if ((*path != '/') && (path > ++last)) { | ||
21 | last = first = path; | ||
22 | } | ||
23 | ++path; | ||
24 | } | ||
25 | |||
26 | if (*first == '/') { | ||
27 | last = first; | ||
28 | } | ||
29 | last[1] = 0; | ||
30 | |||
31 | return first; | ||
32 | } | ||
diff --git a/libbb/get_line_from_file.c b/libbb/get_line_from_file.c new file mode 100644 index 000000000..3f2c6096e --- /dev/null +++ b/libbb/get_line_from_file.c | |||
@@ -0,0 +1,68 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 2005, 2006 Rob Landley <rob@landley.net> | ||
6 | * Copyright (C) 2004 Erik Andersen <andersen@codepoet.org> | ||
7 | * Copyright (C) 2001 Matt Krai | ||
8 | * | ||
9 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
10 | */ | ||
11 | |||
12 | #include "libbb.h" | ||
13 | |||
14 | /* This function reads an entire line from a text file, up to a newline | ||
15 | * or NUL byte, inclusive. It returns a malloc'ed char * which must be | ||
16 | * stored and free'ed by the caller. If end is null '\n' isn't considered | ||
17 | * end of line. If end isn't null, length of the chunk read is stored in it. */ | ||
18 | |||
19 | char *bb_get_chunk_from_file(FILE * file, int *end) | ||
20 | { | ||
21 | int ch; | ||
22 | int idx = 0; | ||
23 | char *linebuf = NULL; | ||
24 | int linebufsz = 0; | ||
25 | |||
26 | while ((ch = getc(file)) != EOF) { | ||
27 | /* grow the line buffer as necessary */ | ||
28 | if (idx >= linebufsz) { | ||
29 | linebuf = xrealloc(linebuf, linebufsz += 80); | ||
30 | } | ||
31 | linebuf[idx++] = (char) ch; | ||
32 | if (!ch || (end && ch == '\n')) | ||
33 | break; | ||
34 | } | ||
35 | if (end) | ||
36 | *end = idx; | ||
37 | if (linebuf) { | ||
38 | // huh, does fgets discard prior data on error like this? | ||
39 | // I don't think so.... | ||
40 | //if (ferror(file)) { | ||
41 | // free(linebuf); | ||
42 | // return NULL; | ||
43 | //} | ||
44 | linebuf = xrealloc(linebuf, idx+1); | ||
45 | linebuf[idx] = '\0'; | ||
46 | } | ||
47 | return linebuf; | ||
48 | } | ||
49 | |||
50 | /* Get line, including trailing \n if any */ | ||
51 | char *xmalloc_fgets(FILE * file) | ||
52 | { | ||
53 | int i; | ||
54 | |||
55 | return bb_get_chunk_from_file(file, &i); | ||
56 | } | ||
57 | |||
58 | /* Get line. Remove trailing \n */ | ||
59 | char *xmalloc_getline(FILE * file) | ||
60 | { | ||
61 | int i; | ||
62 | char *c = bb_get_chunk_from_file(file, &i); | ||
63 | |||
64 | if (i && c[--i] == '\n') | ||
65 | c[i] = 0; | ||
66 | |||
67 | return c; | ||
68 | } | ||
diff --git a/libbb/getopt32.c b/libbb/getopt32.c new file mode 100644 index 000000000..dddf8121a --- /dev/null +++ b/libbb/getopt32.c | |||
@@ -0,0 +1,529 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * universal getopt32 implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003-2005 Vladimir Oleynik <dzo@simtreas.ru> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | #include <getopt.h> | ||
12 | |||
13 | /* Documentation | ||
14 | |||
15 | uint32_t | ||
16 | getopt32(int argc, char **argv, const char *applet_opts, ...) | ||
17 | |||
18 | The command line options must be declared in const char | ||
19 | *applet_opts as a string of chars, for example: | ||
20 | |||
21 | flags = getopt32(argc, argv, "rnug"); | ||
22 | |||
23 | If one of the given options is found, a flag value is added to | ||
24 | the return value (an unsigned long). | ||
25 | |||
26 | The flag value is determined by the position of the char in | ||
27 | applet_opts string. For example, in the above case: | ||
28 | |||
29 | flags = getopt32(argc, argv, "rnug"); | ||
30 | |||
31 | "r" will add 1 (bit 0) | ||
32 | "n" will add 2 (bit 1) | ||
33 | "u will add 4 (bit 2) | ||
34 | "g" will add 8 (bit 3) | ||
35 | |||
36 | and so on. You can also look at the return value as a bit | ||
37 | field and each option sets one bit. | ||
38 | |||
39 | On exit, global variable optind is set so that if you | ||
40 | will do argc -= optind; argv += optind; then | ||
41 | argc will be equal to number of remaining non-option | ||
42 | arguments, first one would be in argv[0], next in argv[1] and so on | ||
43 | (options and their parameters will be moved into argv[] | ||
44 | positions prior to argv[optind]). | ||
45 | |||
46 | ":" If one of the options requires an argument, then add a ":" | ||
47 | after the char in applet_opts and provide a pointer to store | ||
48 | the argument. For example: | ||
49 | |||
50 | char *pointer_to_arg_for_a; | ||
51 | char *pointer_to_arg_for_b; | ||
52 | char *pointer_to_arg_for_c; | ||
53 | char *pointer_to_arg_for_d; | ||
54 | |||
55 | flags = getopt32(argc, argv, "a:b:c:d:", | ||
56 | &pointer_to_arg_for_a, &pointer_to_arg_for_b, | ||
57 | &pointer_to_arg_for_c, &pointer_to_arg_for_d); | ||
58 | |||
59 | The type of the pointer (char* or llist_t*) may be controlled | ||
60 | by the "::" special separator that is set in the external string | ||
61 | opt_complementary (see below for more info). | ||
62 | |||
63 | "::" If option can have an *optional* argument, then add a "::" | ||
64 | after its char in applet_opts and provide a pointer to store | ||
65 | the argument. Note that optional arguments _must_ | ||
66 | immediately follow the option: -oparam, not -o param. | ||
67 | |||
68 | "+" If the first character in the applet_opts string is a plus, | ||
69 | then option processing will stop as soon as a non-option is | ||
70 | encountered in the argv array. Useful for applets like env | ||
71 | which should not process arguments to subprograms: | ||
72 | env -i ls -d / | ||
73 | Here we want env to process just the '-i', not the '-d'. | ||
74 | |||
75 | const struct option *applet_long_options | ||
76 | |||
77 | This struct allows you to define long options. The syntax for | ||
78 | declaring the array is just like that of getopt's longopts. | ||
79 | (see getopt(3)) | ||
80 | |||
81 | static const struct option applet_long_options[] = { | ||
82 | //name,has_arg,flag,val | ||
83 | { "verbose", 0, 0, 'v' }, | ||
84 | { 0, 0, 0, 0 } | ||
85 | }; | ||
86 | applet_long_options = applet_long_options; | ||
87 | |||
88 | The last member of struct option (val) typically is set to | ||
89 | matching short option from applet_opts. If there is no matching | ||
90 | char in applet_opts, then: | ||
91 | - return bit have next position after short options | ||
92 | - if has_arg is not "no_argument", use ptr for arg also | ||
93 | - opt_complementary affects it too | ||
94 | |||
95 | Note: a good applet will make long options configurable via the | ||
96 | config process and not a required feature. The current standard | ||
97 | is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS. | ||
98 | |||
99 | const char *opt_complementary | ||
100 | |||
101 | ":" The colon (":") is used to separate groups of two or more chars | ||
102 | and/or groups of chars and special characters (stating some | ||
103 | conditions to be checked). | ||
104 | |||
105 | "abc" If groups of two or more chars are specified, the first char | ||
106 | is the main option and the other chars are secondary options. | ||
107 | Their flags will be turned on if the main option is found even | ||
108 | if they are not specifed on the command line. For example: | ||
109 | |||
110 | opt_complementary = "abc"; | ||
111 | flags = getopt32(argc, argv, "abcd") | ||
112 | |||
113 | If getopt() finds "-a" on the command line, then | ||
114 | getopt32's return value will be as if "-a -b -c" were | ||
115 | found. | ||
116 | |||
117 | "ww" Adjacent double options have a counter associated which indicates | ||
118 | the number of occurences of the option. | ||
119 | For example the ps applet needs: | ||
120 | if w is given once, GNU ps sets the width to 132, | ||
121 | if w is given more than once, it is "unlimited" | ||
122 | |||
123 | int w_counter = 0; | ||
124 | opt_complementary = "ww"; | ||
125 | getopt32(argc, argv, "w", &w_counter); | ||
126 | if (w_counter) | ||
127 | width = (w_counter == 1) ? 132 : INT_MAX; | ||
128 | else | ||
129 | get_terminal_width(...&width...); | ||
130 | |||
131 | w_counter is a pointer to an integer. It has to be passed to | ||
132 | getopt32() after all other option argument sinks. | ||
133 | |||
134 | For example: accept multiple -v to indicate the level of verbosity | ||
135 | and for each -b optarg, add optarg to my_b. Finally, if b is given, | ||
136 | turn off c and vice versa: | ||
137 | |||
138 | llist_t *my_b = NULL; | ||
139 | int verbose_level = 0; | ||
140 | opt_complementary = "vv:b::b-c:c-b"; | ||
141 | f = getopt32(argc, argv, "vb:c", &my_b, &verbose_level); | ||
142 | if (f & 2) // -c after -b unsets -b flag | ||
143 | while (my_b) { dosomething_with(my_b->data); my_b = my_b->link; } | ||
144 | if (my_b) // but llist is stored if -b is specified | ||
145 | free_llist(my_b); | ||
146 | if (verbose_level) printf("verbose level is %d\n", verbose_level); | ||
147 | |||
148 | Special characters: | ||
149 | |||
150 | "-" A dash as the first char in a opt_complementary group forces | ||
151 | all arguments to be treated as options, even if they have | ||
152 | no leading dashes. Next char in this case can't be a digit (0-9), | ||
153 | use ':' or end of line. For example: | ||
154 | |||
155 | opt_complementary = "-:w-x:x-w"; | ||
156 | getopt32(argc, argv, "wx"); | ||
157 | |||
158 | Allows any arguments to be given without a dash (./program w x) | ||
159 | as well as with a dash (./program -x). | ||
160 | |||
161 | "--" A double dash at the beginning of opt_complementary means the | ||
162 | argv[1] string should always be treated as options, even if it isn't | ||
163 | prefixed with a "-". This is useful for special syntax in applets | ||
164 | such as "ar" and "tar": | ||
165 | tar xvf foo.tar | ||
166 | |||
167 | "-N" A dash as the first char in a opt_complementary group followed | ||
168 | by a single digit (0-9) means that at least N non-option | ||
169 | arguments must be present on the command line | ||
170 | |||
171 | "=N" An equal sign as the first char in a opt_complementary group followed | ||
172 | by a single digit (0-9) means that exactly N non-option | ||
173 | arguments must be present on the command line | ||
174 | |||
175 | "?N" A "?" as the first char in a opt_complementary group followed | ||
176 | by a single digit (0-9) means that at most N arguments must be present | ||
177 | on the command line. | ||
178 | |||
179 | "V-" An option with dash before colon or end-of-line results in | ||
180 | bb_show_usage being called if this option is encountered. | ||
181 | This is typically used to implement "print verbose usage message | ||
182 | and exit" option. | ||
183 | |||
184 | "-" A dash between two options causes the second of the two | ||
185 | to be unset (and ignored) if it is given on the command line. | ||
186 | |||
187 | [FIXME: what if they are the same? like "x-x"? Is it ever useful?] | ||
188 | |||
189 | For example: | ||
190 | The du applet has the options "-s" and "-d depth". If | ||
191 | getopt32 finds -s, then -d is unset or if it finds -d | ||
192 | then -s is unset. (Note: busybox implements the GNU | ||
193 | "--max-depth" option as "-d".) To obtain this behavior, you | ||
194 | set opt_complementary = "s-d:d-s". Only one flag value is | ||
195 | added to getopt32's return value depending on the | ||
196 | position of the options on the command line. If one of the | ||
197 | two options requires an argument pointer (":" in applet_opts | ||
198 | as in "d:") optarg is set accordingly. | ||
199 | |||
200 | char *smax_print_depth; | ||
201 | |||
202 | opt_complementary = "s-d:d-s:x-x"; | ||
203 | opt = getopt32(argc, argv, "sd:x", &smax_print_depth); | ||
204 | |||
205 | if (opt & 2) | ||
206 | max_print_depth = atoi(smax_print_depth); | ||
207 | if (opt & 4) | ||
208 | printf("Detected odd -x usage\n"); | ||
209 | |||
210 | "--" A double dash between two options, or between an option and a group | ||
211 | of options, means that they are mutually exclusive. Unlike | ||
212 | the "-" case above, an error will be forced if the options | ||
213 | are used together. | ||
214 | |||
215 | For example: | ||
216 | The cut applet must have only one type of list specified, so | ||
217 | -b, -c and -f are mutally exclusive and should raise an error | ||
218 | if specified together. In this case you must set | ||
219 | opt_complementary = "b--cf:c--bf:f--bc". If two of the | ||
220 | mutually exclusive options are found, getopt32's | ||
221 | return value will have the error flag set (BB_GETOPT_ERROR) so | ||
222 | that we can check for it: | ||
223 | |||
224 | if (flags & BB_GETOPT_ERROR) | ||
225 | bb_show_usage(); | ||
226 | |||
227 | "x--x" Variation of the above, it means that -x option should occur | ||
228 | at most once. | ||
229 | |||
230 | "?" A "?" as the first char in a opt_complementary group means: | ||
231 | if BB_GETOPT_ERROR is detected, don't return, call bb_show_usage | ||
232 | and exit instead. Next char after '?' can't be a digit. | ||
233 | |||
234 | "::" A double colon after a char in opt_complementary means that the | ||
235 | option can occur multiple times. Each occurrence will be saved as | ||
236 | a llist_t element instead of char*. | ||
237 | |||
238 | For example: | ||
239 | The grep applet can have one or more "-e pattern" arguments. | ||
240 | In this case you should use getopt32() as follows: | ||
241 | |||
242 | llist_t *patterns = NULL; | ||
243 | |||
244 | (this pointer must be initializated to NULL if the list is empty | ||
245 | as required by *llist_add_to(llist_t *old_head, char *new_item).) | ||
246 | |||
247 | opt_complementary = "e::"; | ||
248 | |||
249 | getopt32(argc, argv, "e:", &patterns); | ||
250 | $ grep -e user -e root /etc/passwd | ||
251 | root:x:0:0:root:/root:/bin/bash | ||
252 | user:x:500:500::/home/user:/bin/bash | ||
253 | |||
254 | "?" An "?" between an option and a group of options means that | ||
255 | at least one of them is required to occur if the first option | ||
256 | occurs in preceding command line arguments. | ||
257 | |||
258 | For example from "id" applet: | ||
259 | |||
260 | // Don't allow -n -r -rn -ug -rug -nug -rnug | ||
261 | opt_complementary = "r?ug:n?ug:?u--g:g--u"; | ||
262 | flags = getopt32(argc, argv, "rnug"); | ||
263 | |||
264 | This example allowed only: | ||
265 | $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng | ||
266 | |||
267 | "X" A opt_complementary group with just a single letter means | ||
268 | that this option is required. If more than one such group exists, | ||
269 | at least one option is required to occur (not all of them). | ||
270 | For example from "start-stop-daemon" applet: | ||
271 | |||
272 | // Don't allow -KS -SK, but -S or -K is required | ||
273 | opt_complementary = "K:S:?K--S:S--K"; | ||
274 | flags = getopt32(argc, argv, "KS...); | ||
275 | |||
276 | |||
277 | Don't forget to use ':'. For example, "?322-22-23X-x-a" | ||
278 | is interpreted as "?3:22:-2:2-2:2-3Xa:2--x" - | ||
279 | max 3 args; count uses of '-2'; min 2 args; if there is | ||
280 | a '-2' option then unset '-3', '-X' and '-a'; if there is | ||
281 | a '-2' and after it a '-x' then error out. | ||
282 | */ | ||
283 | |||
284 | /* Code here assumes that 'unsigned' is at least 32 bits wide */ | ||
285 | |||
286 | const char *opt_complementary; | ||
287 | |||
288 | typedef struct { | ||
289 | int opt; | ||
290 | int list_flg; | ||
291 | unsigned switch_on; | ||
292 | unsigned switch_off; | ||
293 | unsigned incongruously; | ||
294 | unsigned requires; | ||
295 | void **optarg; /* char **optarg or llist_t **optarg */ | ||
296 | int *counter; | ||
297 | } t_complementary; | ||
298 | |||
299 | /* You can set applet_long_options for parse called long options */ | ||
300 | #if ENABLE_GETOPT_LONG | ||
301 | static const struct option bb_default_long_options[] = { | ||
302 | /* { "help", 0, NULL, '?' }, */ | ||
303 | { 0, 0, 0, 0 } | ||
304 | }; | ||
305 | |||
306 | const struct option *applet_long_options = bb_default_long_options; | ||
307 | #endif | ||
308 | |||
309 | uint32_t option_mask32; | ||
310 | |||
311 | uint32_t | ||
312 | getopt32(int argc, char **argv, const char *applet_opts, ...) | ||
313 | { | ||
314 | unsigned flags = 0; | ||
315 | unsigned requires = 0; | ||
316 | t_complementary complementary[33]; | ||
317 | int c; | ||
318 | const unsigned char *s; | ||
319 | t_complementary *on_off; | ||
320 | va_list p; | ||
321 | #if ENABLE_GETOPT_LONG | ||
322 | const struct option *l_o; | ||
323 | #endif | ||
324 | unsigned trigger; | ||
325 | char **pargv = NULL; | ||
326 | int min_arg = 0; | ||
327 | int max_arg = -1; | ||
328 | |||
329 | #define SHOW_USAGE_IF_ERROR 1 | ||
330 | #define ALL_ARGV_IS_OPTS 2 | ||
331 | #define FIRST_ARGV_IS_OPT 4 | ||
332 | #define FREE_FIRST_ARGV_IS_OPT 8 | ||
333 | int spec_flgs = 0; | ||
334 | |||
335 | va_start(p, applet_opts); | ||
336 | |||
337 | c = 0; | ||
338 | on_off = complementary; | ||
339 | memset(on_off, 0, sizeof(complementary)); | ||
340 | |||
341 | /* skip GNU extension */ | ||
342 | s = (const unsigned char *)applet_opts; | ||
343 | if (*s == '+' || *s == '-') | ||
344 | s++; | ||
345 | while (*s) { | ||
346 | if (c >= 32) break; | ||
347 | on_off->opt = *s; | ||
348 | on_off->switch_on = (1 << c); | ||
349 | if (*++s == ':') { | ||
350 | on_off->optarg = va_arg(p, void **); | ||
351 | while (*++s == ':') /* skip */; | ||
352 | } | ||
353 | on_off++; | ||
354 | c++; | ||
355 | } | ||
356 | |||
357 | #if ENABLE_GETOPT_LONG | ||
358 | for (l_o = applet_long_options; l_o->name; l_o++) { | ||
359 | if (l_o->flag) | ||
360 | continue; | ||
361 | for (on_off = complementary; on_off->opt != 0; on_off++) | ||
362 | if (on_off->opt == l_o->val) | ||
363 | goto next_long; | ||
364 | if (c >= 32) break; | ||
365 | on_off->opt = l_o->val; | ||
366 | on_off->switch_on = (1 << c); | ||
367 | if (l_o->has_arg != no_argument) | ||
368 | on_off->optarg = va_arg(p, void **); | ||
369 | c++; | ||
370 | next_long: ; | ||
371 | } | ||
372 | #endif /* ENABLE_GETOPT_LONG */ | ||
373 | for (s = (const unsigned char *)opt_complementary; s && *s; s++) { | ||
374 | t_complementary *pair; | ||
375 | unsigned *pair_switch; | ||
376 | |||
377 | if (*s == ':') | ||
378 | continue; | ||
379 | c = s[1]; | ||
380 | if (*s == '?') { | ||
381 | if (c < '0' || c > '9') { | ||
382 | spec_flgs |= SHOW_USAGE_IF_ERROR; | ||
383 | } else { | ||
384 | max_arg = c - '0'; | ||
385 | s++; | ||
386 | } | ||
387 | continue; | ||
388 | } | ||
389 | if (*s == '-') { | ||
390 | if (c < '0' || c > '9') { | ||
391 | if (c == '-') { | ||
392 | spec_flgs |= FIRST_ARGV_IS_OPT; | ||
393 | s++; | ||
394 | } else | ||
395 | spec_flgs |= ALL_ARGV_IS_OPTS; | ||
396 | } else { | ||
397 | min_arg = c - '0'; | ||
398 | s++; | ||
399 | } | ||
400 | continue; | ||
401 | } | ||
402 | if (*s == '=') { | ||
403 | min_arg = max_arg = c - '0'; | ||
404 | s++; | ||
405 | continue; | ||
406 | } | ||
407 | for (on_off = complementary; on_off->opt; on_off++) | ||
408 | if (on_off->opt == *s) | ||
409 | break; | ||
410 | if (c == ':' && s[2] == ':') { | ||
411 | on_off->list_flg++; | ||
412 | continue; | ||
413 | } | ||
414 | if (c == ':' || c == '\0') { | ||
415 | requires |= on_off->switch_on; | ||
416 | continue; | ||
417 | } | ||
418 | if (c == '-' && (s[2] == ':' || s[2] == '\0')) { | ||
419 | flags |= on_off->switch_on; | ||
420 | on_off->incongruously |= on_off->switch_on; | ||
421 | s++; | ||
422 | continue; | ||
423 | } | ||
424 | if (c == *s) { | ||
425 | on_off->counter = va_arg(p, int *); | ||
426 | s++; | ||
427 | } | ||
428 | pair = on_off; | ||
429 | pair_switch = &(pair->switch_on); | ||
430 | for (s++; *s && *s != ':'; s++) { | ||
431 | if (*s == '?') { | ||
432 | pair_switch = &(pair->requires); | ||
433 | } else if (*s == '-') { | ||
434 | if (pair_switch == &(pair->switch_off)) | ||
435 | pair_switch = &(pair->incongruously); | ||
436 | else | ||
437 | pair_switch = &(pair->switch_off); | ||
438 | } else { | ||
439 | for (on_off = complementary; on_off->opt; on_off++) | ||
440 | if (on_off->opt == *s) { | ||
441 | *pair_switch |= on_off->switch_on; | ||
442 | break; | ||
443 | } | ||
444 | } | ||
445 | } | ||
446 | s--; | ||
447 | } | ||
448 | va_end (p); | ||
449 | |||
450 | #if ENABLE_AR || ENABLE_TAR | ||
451 | if (spec_flgs & FIRST_ARGV_IS_OPT) { | ||
452 | if (argv[1] && argv[1][0] != '-' && argv[1][0] != '\0') { | ||
453 | argv[1] = xasprintf("-%s", argv[1]); | ||
454 | if (ENABLE_FEATURE_CLEAN_UP) | ||
455 | spec_flgs |= FREE_FIRST_ARGV_IS_OPT; | ||
456 | } | ||
457 | } | ||
458 | #endif | ||
459 | /* Note: just "getopt() <= 0" will not work good for | ||
460 | * "fake" short options, like this one: | ||
461 | * wget $'-\203' "Test: test" http://kernel.org/ | ||
462 | * (supposed to act as --header, but doesn't) */ | ||
463 | #if ENABLE_GETOPT_LONG | ||
464 | while ((c = getopt_long(argc, argv, applet_opts, | ||
465 | applet_long_options, NULL)) != -1) { | ||
466 | #else | ||
467 | while ((c = getopt(argc, argv, applet_opts)) != -1) { | ||
468 | #endif /* ENABLE_GETOPT_LONG */ | ||
469 | c &= 0xff; /* fight libc's sign extends */ | ||
470 | loop_arg_is_opt: | ||
471 | for (on_off = complementary; on_off->opt != c; on_off++) { | ||
472 | /* c==0 if long opt have non NULL flag */ | ||
473 | if (on_off->opt == 0 && c != 0) | ||
474 | bb_show_usage(); | ||
475 | } | ||
476 | if (flags & on_off->incongruously) { | ||
477 | if ((spec_flgs & SHOW_USAGE_IF_ERROR)) | ||
478 | bb_show_usage(); | ||
479 | flags |= BB_GETOPT_ERROR; | ||
480 | } | ||
481 | trigger = on_off->switch_on & on_off->switch_off; | ||
482 | flags &= ~(on_off->switch_off ^ trigger); | ||
483 | flags |= on_off->switch_on ^ trigger; | ||
484 | flags ^= trigger; | ||
485 | if (on_off->counter) | ||
486 | (*(on_off->counter))++; | ||
487 | if (on_off->list_flg) { | ||
488 | llist_add_to((llist_t **)(on_off->optarg), optarg); | ||
489 | } else if (on_off->optarg) { | ||
490 | *(char **)(on_off->optarg) = optarg; | ||
491 | } | ||
492 | if (pargv != NULL) | ||
493 | break; | ||
494 | } | ||
495 | |||
496 | if (spec_flgs & ALL_ARGV_IS_OPTS) { | ||
497 | /* process argv is option, for example "ps" applet */ | ||
498 | if (pargv == NULL) | ||
499 | pargv = argv + optind; | ||
500 | while (*pargv) { | ||
501 | c = **pargv; | ||
502 | if (c == '\0') { | ||
503 | pargv++; | ||
504 | } else { | ||
505 | (*pargv)++; | ||
506 | goto loop_arg_is_opt; | ||
507 | } | ||
508 | } | ||
509 | } | ||
510 | |||
511 | #if (ENABLE_AR || ENABLE_TAR) && ENABLE_FEATURE_CLEAN_UP | ||
512 | if (spec_flgs & FREE_FIRST_ARGV_IS_OPT) | ||
513 | free(argv[1]); | ||
514 | #endif | ||
515 | /* check depending requires for given options */ | ||
516 | for (on_off = complementary; on_off->opt; on_off++) { | ||
517 | if (on_off->requires && (flags & on_off->switch_on) && | ||
518 | (flags & on_off->requires) == 0) | ||
519 | bb_show_usage(); | ||
520 | } | ||
521 | if (requires && (flags & requires) == 0) | ||
522 | bb_show_usage(); | ||
523 | argc -= optind; | ||
524 | if (argc < min_arg || (max_arg >= 0 && argc > max_arg)) | ||
525 | bb_show_usage(); | ||
526 | |||
527 | option_mask32 = flags; | ||
528 | return flags; | ||
529 | } | ||
diff --git a/libbb/herror_msg.c b/libbb/herror_msg.c new file mode 100644 index 000000000..1e6908d82 --- /dev/null +++ b/libbb/herror_msg.c | |||
@@ -0,0 +1,22 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdarg.h> | ||
11 | #include <stdlib.h> | ||
12 | |||
13 | #include "libbb.h" | ||
14 | |||
15 | void bb_herror_msg(const char *s, ...) | ||
16 | { | ||
17 | va_list p; | ||
18 | |||
19 | va_start(p, s); | ||
20 | bb_vherror_msg(s, p); | ||
21 | va_end(p); | ||
22 | } | ||
diff --git a/libbb/herror_msg_and_die.c b/libbb/herror_msg_and_die.c new file mode 100644 index 000000000..f62ddd2ea --- /dev/null +++ b/libbb/herror_msg_and_die.c | |||
@@ -0,0 +1,25 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdarg.h> | ||
11 | #include <stdlib.h> | ||
12 | |||
13 | #include "libbb.h" | ||
14 | |||
15 | void bb_herror_msg_and_die(const char *s, ...) | ||
16 | { | ||
17 | va_list p; | ||
18 | |||
19 | va_start(p, s); | ||
20 | bb_vherror_msg(s, p); | ||
21 | va_end(p); | ||
22 | if (die_sleep) | ||
23 | sleep(die_sleep); | ||
24 | exit(xfunc_error_retval); | ||
25 | } | ||
diff --git a/libbb/human_readable.c b/libbb/human_readable.c new file mode 100644 index 000000000..ff1b55141 --- /dev/null +++ b/libbb/human_readable.c | |||
@@ -0,0 +1,89 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * June 30, 2001 Manuel Novoa III | ||
4 | * | ||
5 | * All-integer version (hey, not everyone has floating point) of | ||
6 | * make_human_readable_str, modified from similar code I had written | ||
7 | * for busybox several months ago. | ||
8 | * | ||
9 | * Notes: | ||
10 | * 1) I'm using an unsigned long long to hold the product size * block_size, | ||
11 | * as df (which calls this routine) could request a representation of a | ||
12 | * partition size in bytes > max of unsigned long. If long longs aren't | ||
13 | * available, it would be possible to do what's needed using polynomial | ||
14 | * representations (say, powers of 1024) and manipulating coefficients. | ||
15 | * The base ten "bytes" output could be handled similarly. | ||
16 | * | ||
17 | * 2) This routine always outputs a decimal point and a tenths digit when | ||
18 | * display_unit != 0. Hence, it isn't uncommon for the returned string | ||
19 | * to have a length of 5 or 6. | ||
20 | * | ||
21 | * It might be nice to add a flag to indicate no decimal digits in | ||
22 | * that case. This could be either an additional parameter, or a | ||
23 | * special value of display_unit. Such a flag would also be nice for du. | ||
24 | * | ||
25 | * Some code to omit the decimal point and tenths digit is sketched out | ||
26 | * and "#if 0"'d below. | ||
27 | */ | ||
28 | |||
29 | #include <stdio.h> | ||
30 | #include "libbb.h" | ||
31 | |||
32 | const char *make_human_readable_str(unsigned long long size, | ||
33 | unsigned long block_size, unsigned long display_unit) | ||
34 | { | ||
35 | /* The code will adjust for additional (appended) units. */ | ||
36 | static const char zero_and_units[] = { '0', 0, 'k', 'M', 'G', 'T' }; | ||
37 | static const char fmt[] = "%llu"; | ||
38 | static const char fmt_tenths[] = "%llu.%d%c"; | ||
39 | |||
40 | static char str[21]; /* Sufficient for 64 bit unsigned integers. */ | ||
41 | |||
42 | unsigned long long val; | ||
43 | int frac; | ||
44 | const char *u; | ||
45 | const char *f; | ||
46 | |||
47 | u = zero_and_units; | ||
48 | f = fmt; | ||
49 | frac = 0; | ||
50 | |||
51 | val = size * block_size; | ||
52 | if (val == 0) { | ||
53 | return u; | ||
54 | } | ||
55 | |||
56 | if (display_unit) { | ||
57 | val += display_unit/2; /* Deal with rounding. */ | ||
58 | val /= display_unit; /* Don't combine with the line above!!! */ | ||
59 | } else { | ||
60 | ++u; | ||
61 | while ((val >= 1024) | ||
62 | && (u < zero_and_units + sizeof(zero_and_units) - 1) | ||
63 | ) { | ||
64 | f = fmt_tenths; | ||
65 | ++u; | ||
66 | frac = (((int)(val % 1024)) * 10 + 1024/2) / 1024; | ||
67 | val /= 1024; | ||
68 | } | ||
69 | if (frac >= 10) { /* We need to round up here. */ | ||
70 | ++val; | ||
71 | frac = 0; | ||
72 | } | ||
73 | #if 0 | ||
74 | /* Sample code to omit decimal point and tenths digit. */ | ||
75 | if ( /* no_tenths */ 1 ) { | ||
76 | if ( frac >= 5 ) { | ||
77 | ++val; | ||
78 | } | ||
79 | f = "%llu%*c" /* fmt_no_tenths */ ; | ||
80 | frac = 1; | ||
81 | } | ||
82 | #endif | ||
83 | } | ||
84 | |||
85 | /* If f==fmt then 'frac' and 'u' are ignored. */ | ||
86 | snprintf(str, sizeof(str), f, val, frac, *u); | ||
87 | |||
88 | return str; | ||
89 | } | ||
diff --git a/libbb/inet_common.c b/libbb/inet_common.c new file mode 100644 index 000000000..d8e00353e --- /dev/null +++ b/libbb/inet_common.c | |||
@@ -0,0 +1,235 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * stolen from net-tools-1.59 and stripped down for busybox by | ||
4 | * Erik Andersen <andersen@codepoet.org> | ||
5 | * | ||
6 | * Heavily modified by Manuel Novoa III Mar 12, 2001 | ||
7 | * | ||
8 | * Version: $Id: inet_common.c,v 1.8 2004/03/10 07:42:38 mjn3 Exp $ | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include "libbb.h" | ||
13 | #include "inet_common.h" | ||
14 | |||
15 | int INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst) | ||
16 | { | ||
17 | struct hostent *hp; | ||
18 | struct netent *np; | ||
19 | |||
20 | /* Grmpf. -FvK */ | ||
21 | s_in->sin_family = AF_INET; | ||
22 | s_in->sin_port = 0; | ||
23 | |||
24 | /* Default is special, meaning 0.0.0.0. */ | ||
25 | if (!strcmp(name, bb_str_default)) { | ||
26 | s_in->sin_addr.s_addr = INADDR_ANY; | ||
27 | return 1; | ||
28 | } | ||
29 | /* Look to see if it's a dotted quad. */ | ||
30 | if (inet_aton(name, &s_in->sin_addr)) { | ||
31 | return 0; | ||
32 | } | ||
33 | /* If we expect this to be a hostname, try hostname database first */ | ||
34 | #ifdef DEBUG | ||
35 | if (hostfirst) { | ||
36 | bb_error_msg("gethostbyname (%s)", name); | ||
37 | } | ||
38 | #endif | ||
39 | if (hostfirst && (hp = gethostbyname(name)) != (struct hostent *) NULL) { | ||
40 | memcpy((char *) &s_in->sin_addr, (char *) hp->h_addr_list[0], | ||
41 | sizeof(struct in_addr)); | ||
42 | return 0; | ||
43 | } | ||
44 | /* Try the NETWORKS database to see if this is a known network. */ | ||
45 | #ifdef DEBUG | ||
46 | bb_error_msg("getnetbyname (%s)", name); | ||
47 | #endif | ||
48 | if ((np = getnetbyname(name)) != (struct netent *) NULL) { | ||
49 | s_in->sin_addr.s_addr = htonl(np->n_net); | ||
50 | return 1; | ||
51 | } | ||
52 | if (hostfirst) { | ||
53 | /* Don't try again */ | ||
54 | return -1; | ||
55 | } | ||
56 | #ifdef DEBUG | ||
57 | res_init(); | ||
58 | _res.options |= RES_DEBUG; | ||
59 | #endif | ||
60 | |||
61 | #ifdef DEBUG | ||
62 | bb_error_msg("gethostbyname (%s)", name); | ||
63 | #endif | ||
64 | if ((hp = gethostbyname(name)) == (struct hostent *) NULL) { | ||
65 | return -1; | ||
66 | } | ||
67 | memcpy((char *) &s_in->sin_addr, (char *) hp->h_addr_list[0], | ||
68 | sizeof(struct in_addr)); | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | /* cache */ | ||
74 | struct addr { | ||
75 | struct sockaddr_in addr; | ||
76 | char *name; | ||
77 | int host; | ||
78 | struct addr *next; | ||
79 | }; | ||
80 | |||
81 | static struct addr *INET_nn = NULL; /* addr-to-name cache */ | ||
82 | |||
83 | /* numeric: & 0x8000: default instead of *, | ||
84 | * & 0x4000: host instead of net, | ||
85 | * & 0x0fff: don't resolve | ||
86 | */ | ||
87 | int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in, | ||
88 | int numeric, unsigned int netmask) | ||
89 | { | ||
90 | struct hostent *ent; | ||
91 | struct netent *np; | ||
92 | struct addr *pn; | ||
93 | unsigned long ad, host_ad; | ||
94 | int host = 0; | ||
95 | |||
96 | /* Grmpf. -FvK */ | ||
97 | if (s_in->sin_family != AF_INET) { | ||
98 | #ifdef DEBUG | ||
99 | bb_error_msg("rresolve: unsupport address family %d !", | ||
100 | s_in->sin_family); | ||
101 | #endif | ||
102 | errno = EAFNOSUPPORT; | ||
103 | return -1; | ||
104 | } | ||
105 | ad = (unsigned long) s_in->sin_addr.s_addr; | ||
106 | #ifdef DEBUG | ||
107 | bb_error_msg("rresolve: %08lx, mask %08x, num %08x", ad, netmask, numeric); | ||
108 | #endif | ||
109 | if (ad == INADDR_ANY) { | ||
110 | if ((numeric & 0x0FFF) == 0) { | ||
111 | if (numeric & 0x8000) | ||
112 | safe_strncpy(name, bb_str_default, len); | ||
113 | else | ||
114 | safe_strncpy(name, "*", len); | ||
115 | return 0; | ||
116 | } | ||
117 | } | ||
118 | if (numeric & 0x0FFF) { | ||
119 | safe_strncpy(name, inet_ntoa(s_in->sin_addr), len); | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | if ((ad & (~netmask)) != 0 || (numeric & 0x4000)) | ||
124 | host = 1; | ||
125 | pn = INET_nn; | ||
126 | while (pn != NULL) { | ||
127 | if (pn->addr.sin_addr.s_addr == ad && pn->host == host) { | ||
128 | safe_strncpy(name, pn->name, len); | ||
129 | #ifdef DEBUG | ||
130 | bb_error_msg("rresolve: found %s %08lx in cache", | ||
131 | (host ? "host" : "net"), ad); | ||
132 | #endif | ||
133 | return 0; | ||
134 | } | ||
135 | pn = pn->next; | ||
136 | } | ||
137 | |||
138 | host_ad = ntohl(ad); | ||
139 | np = NULL; | ||
140 | ent = NULL; | ||
141 | if (host) { | ||
142 | #ifdef DEBUG | ||
143 | bb_error_msg("gethostbyaddr (%08lx)", ad); | ||
144 | #endif | ||
145 | ent = gethostbyaddr((char *) &ad, 4, AF_INET); | ||
146 | if (ent != NULL) { | ||
147 | safe_strncpy(name, ent->h_name, len); | ||
148 | } | ||
149 | } else { | ||
150 | #ifdef DEBUG | ||
151 | bb_error_msg("getnetbyaddr (%08lx)", host_ad); | ||
152 | #endif | ||
153 | np = getnetbyaddr(host_ad, AF_INET); | ||
154 | if (np != NULL) { | ||
155 | safe_strncpy(name, np->n_name, len); | ||
156 | } | ||
157 | } | ||
158 | if ((ent == NULL) && (np == NULL)) { | ||
159 | safe_strncpy(name, inet_ntoa(s_in->sin_addr), len); | ||
160 | } | ||
161 | pn = (struct addr *) xmalloc(sizeof(struct addr)); | ||
162 | pn->addr = *s_in; | ||
163 | pn->next = INET_nn; | ||
164 | pn->host = host; | ||
165 | pn->name = xstrdup(name); | ||
166 | INET_nn = pn; | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | #ifdef CONFIG_FEATURE_IPV6 | ||
172 | |||
173 | int INET6_resolve(const char *name, struct sockaddr_in6 *sin6) | ||
174 | { | ||
175 | struct addrinfo req, *ai; | ||
176 | int s; | ||
177 | |||
178 | memset(&req, '\0', sizeof req); | ||
179 | req.ai_family = AF_INET6; | ||
180 | s = getaddrinfo(name, NULL, &req, &ai); | ||
181 | if (s) { | ||
182 | bb_error_msg("getaddrinfo: %s: %d", name, s); | ||
183 | return -1; | ||
184 | } | ||
185 | memcpy(sin6, ai->ai_addr, sizeof(struct sockaddr_in6)); | ||
186 | |||
187 | freeaddrinfo(ai); | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | #ifndef IN6_IS_ADDR_UNSPECIFIED | ||
193 | # define IN6_IS_ADDR_UNSPECIFIED(a) \ | ||
194 | (((uint32_t *) (a))[0] == 0 && ((uint32_t *) (a))[1] == 0 && \ | ||
195 | ((uint32_t *) (a))[2] == 0 && ((uint32_t *) (a))[3] == 0) | ||
196 | #endif | ||
197 | |||
198 | |||
199 | int INET6_rresolve(char *name, size_t len, struct sockaddr_in6 *sin6, | ||
200 | int numeric) | ||
201 | { | ||
202 | int s; | ||
203 | |||
204 | /* Grmpf. -FvK */ | ||
205 | if (sin6->sin6_family != AF_INET6) { | ||
206 | #ifdef DEBUG | ||
207 | bb_error_msg("rresolve: unsupport address family %d!", | ||
208 | sin6->sin6_family); | ||
209 | #endif | ||
210 | errno = EAFNOSUPPORT; | ||
211 | return -1; | ||
212 | } | ||
213 | if (numeric & 0x7FFF) { | ||
214 | inet_ntop(AF_INET6, &sin6->sin6_addr, name, len); | ||
215 | return 0; | ||
216 | } | ||
217 | if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { | ||
218 | if (numeric & 0x8000) { | ||
219 | strcpy(name, bb_str_default); | ||
220 | } else { | ||
221 | name[0] = '*'; | ||
222 | name[1] = '\0'; | ||
223 | } | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | s = getnameinfo((struct sockaddr *) sin6, sizeof(struct sockaddr_in6), name, len, NULL, 0, 0); | ||
228 | if (s) { | ||
229 | bb_error_msg("getnameinfo failed"); | ||
230 | return -1; | ||
231 | } | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | #endif /* CONFIG_FEATURE_IPV6 */ | ||
diff --git a/libbb/info_msg.c b/libbb/info_msg.c new file mode 100644 index 000000000..78d5c8f32 --- /dev/null +++ b/libbb/info_msg.c | |||
@@ -0,0 +1,23 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdio.h> | ||
11 | #include <errno.h> | ||
12 | #include <string.h> | ||
13 | #include <stdlib.h> | ||
14 | #include "libbb.h" | ||
15 | |||
16 | void bb_info_msg(const char *s, ...) | ||
17 | { | ||
18 | va_list p; | ||
19 | |||
20 | va_start(p, s); | ||
21 | bb_vinfo_msg(s, p); | ||
22 | va_end(p); | ||
23 | } | ||
diff --git a/libbb/inode_hash.c b/libbb/inode_hash.c new file mode 100644 index 000000000..2ac1623f4 --- /dev/null +++ b/libbb/inode_hash.c | |||
@@ -0,0 +1,88 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) many different people. | ||
6 | * If you wrote this, please acknowledge your work. | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include <stdio.h> | ||
12 | #include <stdlib.h> | ||
13 | #include <string.h> | ||
14 | #include "libbb.h" | ||
15 | |||
16 | #define HASH_SIZE 311 /* Should be prime */ | ||
17 | #define hash_inode(i) ((i) % HASH_SIZE) | ||
18 | |||
19 | typedef struct ino_dev_hash_bucket_struct { | ||
20 | struct ino_dev_hash_bucket_struct *next; | ||
21 | ino_t ino; | ||
22 | dev_t dev; | ||
23 | char name[1]; | ||
24 | } ino_dev_hashtable_bucket_t; | ||
25 | |||
26 | static ino_dev_hashtable_bucket_t *ino_dev_hashtable[HASH_SIZE]; | ||
27 | |||
28 | /* | ||
29 | * Return 1 if statbuf->st_ino && statbuf->st_dev are recorded in | ||
30 | * `ino_dev_hashtable', else return 0 | ||
31 | * | ||
32 | * If NAME is a non-NULL pointer to a character pointer, and there is | ||
33 | * a match, then set *NAME to the value of the name slot in that | ||
34 | * bucket. | ||
35 | */ | ||
36 | int is_in_ino_dev_hashtable(const struct stat *statbuf, char **name) | ||
37 | { | ||
38 | ino_dev_hashtable_bucket_t *bucket; | ||
39 | |||
40 | bucket = ino_dev_hashtable[hash_inode(statbuf->st_ino)]; | ||
41 | while (bucket != NULL) { | ||
42 | if ((bucket->ino == statbuf->st_ino) && | ||
43 | (bucket->dev == statbuf->st_dev)) | ||
44 | { | ||
45 | if (name) *name = bucket->name; | ||
46 | return 1; | ||
47 | } | ||
48 | bucket = bucket->next; | ||
49 | } | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | /* Add statbuf to statbuf hash table */ | ||
54 | void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) | ||
55 | { | ||
56 | int i; | ||
57 | size_t s; | ||
58 | ino_dev_hashtable_bucket_t *bucket; | ||
59 | |||
60 | i = hash_inode(statbuf->st_ino); | ||
61 | s = name ? strlen(name) : 0; | ||
62 | bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + s); | ||
63 | bucket->ino = statbuf->st_ino; | ||
64 | bucket->dev = statbuf->st_dev; | ||
65 | if (name) | ||
66 | strcpy(bucket->name, name); | ||
67 | else | ||
68 | bucket->name[0] = '\0'; | ||
69 | bucket->next = ino_dev_hashtable[i]; | ||
70 | ino_dev_hashtable[i] = bucket; | ||
71 | } | ||
72 | |||
73 | #ifdef CONFIG_FEATURE_CLEAN_UP | ||
74 | /* Clear statbuf hash table */ | ||
75 | void reset_ino_dev_hashtable(void) | ||
76 | { | ||
77 | int i; | ||
78 | ino_dev_hashtable_bucket_t *bucket; | ||
79 | |||
80 | for (i = 0; i < HASH_SIZE; i++) { | ||
81 | while (ino_dev_hashtable[i] != NULL) { | ||
82 | bucket = ino_dev_hashtable[i]->next; | ||
83 | free(ino_dev_hashtable[i]); | ||
84 | ino_dev_hashtable[i] = bucket; | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | #endif | ||
diff --git a/libbb/isdirectory.c b/libbb/isdirectory.c new file mode 100644 index 000000000..b35919869 --- /dev/null +++ b/libbb/isdirectory.c | |||
@@ -0,0 +1,39 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Based in part on code from sash, Copyright (c) 1999 by David I. Bell | ||
6 | * Permission has been granted to redistribute this code under the GPL. | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include <sys/stat.h> | ||
12 | #include "libbb.h" | ||
13 | |||
14 | /* | ||
15 | * Return TRUE if a fileName is a directory. | ||
16 | * Nonexistent files return FALSE. | ||
17 | */ | ||
18 | int is_directory(const char *fileName, const int followLinks, struct stat *statBuf) | ||
19 | { | ||
20 | int status; | ||
21 | struct stat astatBuf; | ||
22 | |||
23 | if (statBuf == NULL) { | ||
24 | /* set from auto stack buffer */ | ||
25 | statBuf = &astatBuf; | ||
26 | } | ||
27 | |||
28 | if (followLinks) | ||
29 | status = stat(fileName, statBuf); | ||
30 | else | ||
31 | status = lstat(fileName, statBuf); | ||
32 | |||
33 | if (status < 0 || !(S_ISDIR(statBuf->st_mode))) { | ||
34 | status = FALSE; | ||
35 | } | ||
36 | else status = TRUE; | ||
37 | |||
38 | return status; | ||
39 | } | ||
diff --git a/libbb/kernel_version.c b/libbb/kernel_version.c new file mode 100644 index 000000000..50b82ae18 --- /dev/null +++ b/libbb/kernel_version.c | |||
@@ -0,0 +1,37 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <sys/utsname.h> /* for uname(2) */ | ||
11 | |||
12 | #include "libbb.h" | ||
13 | |||
14 | /* Returns current kernel version encoded as major*65536 + minor*256 + patch, | ||
15 | * so, for example, to check if the kernel is greater than 2.2.11: | ||
16 | * | ||
17 | * if (get_linux_version_code() > KERNEL_VERSION(2,2,11)) { <stuff> } | ||
18 | */ | ||
19 | int get_linux_version_code(void) | ||
20 | { | ||
21 | struct utsname name; | ||
22 | char *s; | ||
23 | int i, r; | ||
24 | |||
25 | if (uname(&name) == -1) { | ||
26 | bb_perror_msg("cannot get system information"); | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | s = name.release; | ||
31 | r = 0; | ||
32 | for (i = 0; i < 3; i++) { | ||
33 | r = r * 256 + atoi(strtok(s, ".")); | ||
34 | s = NULL; | ||
35 | } | ||
36 | return r; | ||
37 | } | ||
diff --git a/libbb/last_char_is.c b/libbb/last_char_is.c new file mode 100644 index 000000000..3616d5916 --- /dev/null +++ b/libbb/last_char_is.c | |||
@@ -0,0 +1,24 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * busybox library eXtended function | ||
4 | * | ||
5 | * Copyright (C) 2001 Larry Doolittle, <ldoolitt@recycle.lbl.gov> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | /* Find out if the last character of a string matches the one given Don't | ||
13 | * underrun the buffer if the string length is 0. Also avoids a possible | ||
14 | * space-hogging inline of strlen() per usage. | ||
15 | */ | ||
16 | char* last_char_is(const char *s, int c) | ||
17 | { | ||
18 | if (s) { | ||
19 | s = strrchr(s, c); | ||
20 | if (s && !s[1]) | ||
21 | return (char*)s; | ||
22 | } | ||
23 | return NULL; | ||
24 | } | ||
diff --git a/libbb/llist.c b/libbb/llist.c new file mode 100644 index 000000000..8a74832ee --- /dev/null +++ b/libbb/llist.c | |||
@@ -0,0 +1,78 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * linked list helper functions. | ||
4 | * | ||
5 | * Copyright (C) 2003 Glenn McGrath | ||
6 | * Copyright (C) 2005 Vladimir Oleynik | ||
7 | * Copyright (C) 2005 Bernhard Fischer | ||
8 | * Copyright (C) 2006 Rob Landley <rob@landley.net> | ||
9 | * | ||
10 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
11 | */ | ||
12 | |||
13 | #include <stdlib.h> | ||
14 | #include "libbb.h" | ||
15 | |||
16 | /* Add data to the start of the linked list. */ | ||
17 | void llist_add_to(llist_t **old_head, void *data) | ||
18 | { | ||
19 | llist_t *new_head = xmalloc(sizeof(llist_t)); | ||
20 | new_head->data = data; | ||
21 | new_head->link = *old_head; | ||
22 | *old_head = new_head; | ||
23 | } | ||
24 | |||
25 | /* Add data to the end of the linked list. */ | ||
26 | void llist_add_to_end(llist_t **list_head, void *data) | ||
27 | { | ||
28 | llist_t *new_item = xmalloc(sizeof(llist_t)); | ||
29 | new_item->data = data; | ||
30 | new_item->link = NULL; | ||
31 | |||
32 | if (!*list_head) *list_head = new_item; | ||
33 | else { | ||
34 | llist_t *tail = *list_head; | ||
35 | while (tail->link) tail = tail->link; | ||
36 | tail->link = new_item; | ||
37 | } | ||
38 | } | ||
39 | |||
40 | /* Remove first element from the list and return it */ | ||
41 | void *llist_pop(llist_t **head) | ||
42 | { | ||
43 | void *data; | ||
44 | |||
45 | if(!*head) data = *head; | ||
46 | else { | ||
47 | void *next = (*head)->link; | ||
48 | data = (*head)->data; | ||
49 | free(*head); | ||
50 | *head = next; | ||
51 | } | ||
52 | |||
53 | return data; | ||
54 | } | ||
55 | |||
56 | /* Recursively free all elements in the linked list. If freeit != NULL | ||
57 | * call it on each datum in the list */ | ||
58 | void llist_free(llist_t *elm, void (*freeit)(void *data)) | ||
59 | { | ||
60 | while (elm) { | ||
61 | void *data = llist_pop(&elm); | ||
62 | if (freeit) freeit(data); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | /* Reverse list order. Useful since getopt32 saves option params | ||
67 | * in reverse order */ | ||
68 | llist_t* rev_llist(llist_t *list) | ||
69 | { | ||
70 | llist_t *new = NULL; | ||
71 | while (list) { | ||
72 | llist_t *next = list->link; | ||
73 | list->link = new; | ||
74 | new = list; | ||
75 | list = next; | ||
76 | } | ||
77 | return new; | ||
78 | } | ||
diff --git a/libbb/login.c b/libbb/login.c new file mode 100644 index 000000000..6ebb9a6a0 --- /dev/null +++ b/libbb/login.c | |||
@@ -0,0 +1,105 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * issue.c: issue printing code | ||
4 | * | ||
5 | * Copyright (C) 2003 Bastian Blank <waldi@tuxbox.org> | ||
6 | * | ||
7 | * Optimize and correcting OCRNL by Vladimir Oleynik <dzo@simtreas.ru> | ||
8 | * | ||
9 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
10 | */ | ||
11 | |||
12 | #include <sys/param.h> /* MAXHOSTNAMELEN */ | ||
13 | #include <stdio.h> | ||
14 | #include <unistd.h> | ||
15 | #include "libbb.h" | ||
16 | |||
17 | #include <sys/utsname.h> | ||
18 | #include <time.h> | ||
19 | |||
20 | #define LOGIN " login: " | ||
21 | |||
22 | static const char fmtstr_d[] = "%A, %d %B %Y"; | ||
23 | static const char fmtstr_t[] = "%H:%M:%S"; | ||
24 | |||
25 | void print_login_issue(const char *issue_file, const char *tty) | ||
26 | { | ||
27 | FILE *fd; | ||
28 | int c; | ||
29 | char buf[256+1]; | ||
30 | const char *outbuf; | ||
31 | time_t t; | ||
32 | struct utsname uts; | ||
33 | |||
34 | time(&t); | ||
35 | uname(&uts); | ||
36 | |||
37 | puts("\r"); /* start a new line */ | ||
38 | |||
39 | fd = fopen(issue_file, "r"); | ||
40 | if (!fd) | ||
41 | return; | ||
42 | while ((c = fgetc(fd)) != EOF) { | ||
43 | outbuf = buf; | ||
44 | buf[0] = c; | ||
45 | buf[1] = '\0'; | ||
46 | if(c == '\n') { | ||
47 | buf[1] = '\r'; | ||
48 | buf[2] = '\0'; | ||
49 | } | ||
50 | if (c == '\\' || c == '%') { | ||
51 | c = fgetc(fd); | ||
52 | switch (c) { | ||
53 | case 's': | ||
54 | outbuf = uts.sysname; | ||
55 | break; | ||
56 | case 'n': | ||
57 | outbuf = uts.nodename; | ||
58 | break; | ||
59 | case 'r': | ||
60 | outbuf = uts.release; | ||
61 | break; | ||
62 | case 'v': | ||
63 | outbuf = uts.version; | ||
64 | break; | ||
65 | case 'm': | ||
66 | outbuf = uts.machine; | ||
67 | break; | ||
68 | case 'D': | ||
69 | case 'o': | ||
70 | c = getdomainname(buf, sizeof(buf) - 1); | ||
71 | buf[c >= 0 ? c : 0] = '\0'; | ||
72 | break; | ||
73 | case 'd': | ||
74 | strftime(buf, sizeof(buf), fmtstr_d, localtime(&t)); | ||
75 | break; | ||
76 | case 't': | ||
77 | strftime(buf, sizeof(buf), fmtstr_t, localtime(&t)); | ||
78 | break; | ||
79 | case 'h': | ||
80 | gethostname(buf, sizeof(buf) - 1); | ||
81 | buf[sizeof(buf) - 1] = '\0'; | ||
82 | break; | ||
83 | case 'l': | ||
84 | outbuf = tty; | ||
85 | break; | ||
86 | default: | ||
87 | buf[0] = c; | ||
88 | } | ||
89 | } | ||
90 | fputs(outbuf, stdout); | ||
91 | } | ||
92 | fclose(fd); | ||
93 | fflush(stdout); | ||
94 | } | ||
95 | |||
96 | void print_login_prompt(void) | ||
97 | { | ||
98 | char buf[MAXHOSTNAMELEN+1]; | ||
99 | |||
100 | if (gethostname(buf, MAXHOSTNAMELEN) == 0) | ||
101 | fputs(buf, stdout); | ||
102 | |||
103 | fputs(LOGIN, stdout); | ||
104 | fflush(stdout); | ||
105 | } | ||
diff --git a/libbb/loop.c b/libbb/loop.c new file mode 100644 index 000000000..14835ec24 --- /dev/null +++ b/libbb/loop.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * Copyright (C) 2005 by Rob Landley <rob@landley.net> | ||
7 | * | ||
8 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
9 | */ | ||
10 | |||
11 | #include "libbb.h" | ||
12 | |||
13 | /* For 2.6, use the cleaned up header to get the 64 bit API. */ | ||
14 | #include <linux/version.h> | ||
15 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) | ||
16 | #include <linux/loop.h> | ||
17 | typedef struct loop_info64 bb_loop_info; | ||
18 | #define BB_LOOP_SET_STATUS LOOP_SET_STATUS64 | ||
19 | #define BB_LOOP_GET_STATUS LOOP_GET_STATUS64 | ||
20 | |||
21 | /* For 2.4 and earlier, use the 32 bit API (and don't trust the headers) */ | ||
22 | #else | ||
23 | /* Stuff stolen from linux/loop.h for 2.4 and earlier kernels*/ | ||
24 | #include <linux/posix_types.h> | ||
25 | #define LO_NAME_SIZE 64 | ||
26 | #define LO_KEY_SIZE 32 | ||
27 | #define LOOP_SET_FD 0x4C00 | ||
28 | #define LOOP_CLR_FD 0x4C01 | ||
29 | #define BB_LOOP_SET_STATUS 0x4C02 | ||
30 | #define BB_LOOP_GET_STATUS 0x4C03 | ||
31 | typedef struct { | ||
32 | int lo_number; | ||
33 | __kernel_dev_t lo_device; | ||
34 | unsigned long lo_inode; | ||
35 | __kernel_dev_t lo_rdevice; | ||
36 | int lo_offset; | ||
37 | int lo_encrypt_type; | ||
38 | int lo_encrypt_key_size; | ||
39 | int lo_flags; | ||
40 | char lo_file_name[LO_NAME_SIZE]; | ||
41 | unsigned char lo_encrypt_key[LO_KEY_SIZE]; | ||
42 | unsigned long lo_init[2]; | ||
43 | char reserved[4]; | ||
44 | } bb_loop_info; | ||
45 | #endif | ||
46 | |||
47 | char *query_loop(const char *device) | ||
48 | { | ||
49 | int fd; | ||
50 | bb_loop_info loopinfo; | ||
51 | char *dev = 0; | ||
52 | |||
53 | fd = open(device, O_RDONLY); | ||
54 | if (fd < 0) return 0; | ||
55 | if (!ioctl(fd, BB_LOOP_GET_STATUS, &loopinfo)) | ||
56 | dev = xasprintf("%ld %s", (long) loopinfo.lo_offset, | ||
57 | (char *)loopinfo.lo_file_name); | ||
58 | close(fd); | ||
59 | |||
60 | return dev; | ||
61 | } | ||
62 | |||
63 | |||
64 | int del_loop(const char *device) | ||
65 | { | ||
66 | int fd, rc; | ||
67 | |||
68 | fd = open(device, O_RDONLY); | ||
69 | if (fd < 0) return 1; | ||
70 | rc = ioctl(fd, LOOP_CLR_FD, 0); | ||
71 | close(fd); | ||
72 | |||
73 | return rc; | ||
74 | } | ||
75 | |||
76 | /* Returns 0 if mounted RW, 1 if mounted read-only, <0 for error. | ||
77 | *device is loop device to use, or if *device==NULL finds a loop device to | ||
78 | mount it on and sets *device to a strdup of that loop device name. This | ||
79 | search will re-use an existing loop device already bound to that | ||
80 | file/offset if it finds one. | ||
81 | */ | ||
82 | int set_loop(char **device, const char *file, unsigned long long offset) | ||
83 | { | ||
84 | char dev[20], *try; | ||
85 | bb_loop_info loopinfo; | ||
86 | struct stat statbuf; | ||
87 | int i, dfd, ffd, mode, rc=-1; | ||
88 | |||
89 | /* Open the file. Barf if this doesn't work. */ | ||
90 | mode = O_RDWR; | ||
91 | ffd = open(file, mode); | ||
92 | if (ffd < 0) { | ||
93 | mode = O_RDONLY; | ||
94 | ffd = open(file, mode); | ||
95 | if (ffd < 0) | ||
96 | return -errno; | ||
97 | } | ||
98 | |||
99 | /* Find a loop device. */ | ||
100 | try = *device ? : dev; | ||
101 | for (i=0;rc;i++) { | ||
102 | sprintf(dev, LOOP_FORMAT, i); | ||
103 | |||
104 | /* Ran out of block devices, return failure. */ | ||
105 | if (stat(try, &statbuf) || !S_ISBLK(statbuf.st_mode)) { | ||
106 | rc=-ENOENT; | ||
107 | break; | ||
108 | } | ||
109 | /* Open the sucker and check its loopiness. */ | ||
110 | dfd = open(try, mode); | ||
111 | if (dfd < 0 && errno == EROFS) { | ||
112 | mode = O_RDONLY; | ||
113 | dfd = open(try, mode); | ||
114 | } | ||
115 | if (dfd < 0) goto try_again; | ||
116 | |||
117 | rc = ioctl(dfd, BB_LOOP_GET_STATUS, &loopinfo); | ||
118 | |||
119 | /* If device free, claim it. */ | ||
120 | if (rc && errno == ENXIO) { | ||
121 | memset(&loopinfo, 0, sizeof(loopinfo)); | ||
122 | safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE); | ||
123 | loopinfo.lo_offset = offset; | ||
124 | /* Associate free loop device with file. */ | ||
125 | if (!ioctl(dfd, LOOP_SET_FD, ffd)) { | ||
126 | if (!ioctl(dfd, BB_LOOP_SET_STATUS, &loopinfo)) rc = 0; | ||
127 | else ioctl(dfd, LOOP_CLR_FD, 0); | ||
128 | } | ||
129 | |||
130 | /* If this block device already set up right, re-use it. | ||
131 | (Yes this is racy, but associating two loop devices with the same | ||
132 | file isn't pretty either. In general, mounting the same file twice | ||
133 | without using losetup manually is problematic.) | ||
134 | */ | ||
135 | } else if (strcmp(file,(char *)loopinfo.lo_file_name) | ||
136 | || offset != loopinfo.lo_offset) { | ||
137 | rc = -1; | ||
138 | } | ||
139 | close(dfd); | ||
140 | try_again: | ||
141 | if (*device) break; | ||
142 | } | ||
143 | close(ffd); | ||
144 | if (!rc) { | ||
145 | if (!*device) *device = strdup(dev); | ||
146 | return mode==O_RDONLY ? 1 : 0; | ||
147 | } | ||
148 | return rc; | ||
149 | } | ||
diff --git a/libbb/make_directory.c b/libbb/make_directory.c new file mode 100644 index 000000000..fbec4e20e --- /dev/null +++ b/libbb/make_directory.c | |||
@@ -0,0 +1,104 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * parse_mode implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | /* Mar 5, 2003 Manuel Novoa III | ||
11 | * | ||
12 | * This is the main work function for the 'mkdir' applet. As such, it | ||
13 | * strives to be SUSv3 compliant in it's behaviour when recursively | ||
14 | * making missing parent dirs, and in it's mode setting of the final | ||
15 | * directory 'path'. | ||
16 | * | ||
17 | * To recursively build all missing intermediate directories, make | ||
18 | * sure that (flags & FILEUTILS_RECUR) is non-zero. Newly created | ||
19 | * intermediate directories will have at least u+wx perms. | ||
20 | * | ||
21 | * To set specific permissions on 'path', pass the appropriate 'mode' | ||
22 | * val. Otherwise, pass -1 to get default permissions. | ||
23 | */ | ||
24 | |||
25 | #include <errno.h> | ||
26 | #include <unistd.h> | ||
27 | #include <sys/stat.h> | ||
28 | #include "libbb.h" | ||
29 | |||
30 | int bb_make_directory (char *path, long mode, int flags) | ||
31 | { | ||
32 | mode_t mask; | ||
33 | const char *fail_msg; | ||
34 | char *s = path; | ||
35 | char c; | ||
36 | struct stat st; | ||
37 | |||
38 | mask = umask(0); | ||
39 | if (mode == -1) { | ||
40 | umask(mask); | ||
41 | mode = (S_IXUSR | S_IXGRP | S_IXOTH | | ||
42 | S_IWUSR | S_IWGRP | S_IWOTH | | ||
43 | S_IRUSR | S_IRGRP | S_IROTH) & ~mask; | ||
44 | } else { | ||
45 | umask(mask & ~0300); | ||
46 | } | ||
47 | |||
48 | do { | ||
49 | c = 0; | ||
50 | |||
51 | if (flags & FILEUTILS_RECUR) { /* Get the parent. */ | ||
52 | /* Bypass leading non-'/'s and then subsequent '/'s. */ | ||
53 | while (*s) { | ||
54 | if (*s == '/') { | ||
55 | do { | ||
56 | ++s; | ||
57 | } while (*s == '/'); | ||
58 | c = *s; /* Save the current char */ | ||
59 | *s = 0; /* and replace it with nul. */ | ||
60 | break; | ||
61 | } | ||
62 | ++s; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | if (mkdir(path, 0777) < 0) { | ||
67 | /* If we failed for any other reason than the directory | ||
68 | * already exists, output a diagnostic and return -1.*/ | ||
69 | if (errno != EEXIST | ||
70 | || !(flags & FILEUTILS_RECUR) | ||
71 | || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) { | ||
72 | fail_msg = "create"; | ||
73 | umask(mask); | ||
74 | break; | ||
75 | } | ||
76 | /* Since the directory exists, don't attempt to change | ||
77 | * permissions if it was the full target. Note that | ||
78 | * this is not an error conditon. */ | ||
79 | if (!c) { | ||
80 | umask(mask); | ||
81 | return 0; | ||
82 | } | ||
83 | } | ||
84 | |||
85 | if (!c) { | ||
86 | /* Done. If necessary, updated perms on the newly | ||
87 | * created directory. Failure to update here _is_ | ||
88 | * an error.*/ | ||
89 | umask(mask); | ||
90 | if ((mode != -1) && (chmod(path, mode) < 0)){ | ||
91 | fail_msg = "set permissions of"; | ||
92 | break; | ||
93 | } | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | /* Remove any inserted nul from the path (recursive mode). */ | ||
98 | *s = c; | ||
99 | |||
100 | } while (1); | ||
101 | |||
102 | bb_perror_msg ("cannot %s directory '%s'", fail_msg, path); | ||
103 | return -1; | ||
104 | } | ||
diff --git a/libbb/makedev.c b/libbb/makedev.c new file mode 100644 index 000000000..4903e4783 --- /dev/null +++ b/libbb/makedev.c | |||
@@ -0,0 +1,20 @@ | |||
1 | /* | ||
2 | * Utility routines. | ||
3 | * | ||
4 | * Copyright (C) 2006 Denis Vlasenko | ||
5 | * | ||
6 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. | ||
7 | */ | ||
8 | |||
9 | /* We do not include libbb.h - #define makedev() is there! */ | ||
10 | #include <features.h> | ||
11 | #include <sys/sysmacros.h> | ||
12 | |||
13 | #ifdef __GLIBC__ | ||
14 | /* At least glibc has horrendously large inline for this, so wrap it */ | ||
15 | /* uclibc people please check - do we need "&& !__UCLIBC__" above? */ | ||
16 | unsigned long long bb_makedev(unsigned int major, unsigned int minor) | ||
17 | { | ||
18 | return makedev(major, minor); | ||
19 | } | ||
20 | #endif | ||
diff --git a/libbb/md5.c b/libbb/md5.c new file mode 100644 index 000000000..e672559cf --- /dev/null +++ b/libbb/md5.c | |||
@@ -0,0 +1,450 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * md5.c - Compute MD5 checksum of strings according to the | ||
4 | * definition of MD5 in RFC 1321 from April 1992. | ||
5 | * | ||
6 | * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. | ||
7 | * | ||
8 | * Copyright (C) 1995-1999 Free Software Foundation, Inc. | ||
9 | * Copyright (C) 2001 Manuel Novoa III | ||
10 | * Copyright (C) 2003 Glenn L. McGrath | ||
11 | * Copyright (C) 2003 Erik Andersen | ||
12 | * | ||
13 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
14 | */ | ||
15 | |||
16 | #include "libbb.h" | ||
17 | |||
18 | #if CONFIG_MD5_SIZE_VS_SPEED < 0 || CONFIG_MD5_SIZE_VS_SPEED > 3 | ||
19 | # define MD5_SIZE_VS_SPEED 2 | ||
20 | #else | ||
21 | # define MD5_SIZE_VS_SPEED CONFIG_MD5_SIZE_VS_SPEED | ||
22 | #endif | ||
23 | |||
24 | /* Initialize structure containing state of computation. | ||
25 | * (RFC 1321, 3.3: Step 3) | ||
26 | */ | ||
27 | void md5_begin(md5_ctx_t *ctx) | ||
28 | { | ||
29 | ctx->A = 0x67452301; | ||
30 | ctx->B = 0xefcdab89; | ||
31 | ctx->C = 0x98badcfe; | ||
32 | ctx->D = 0x10325476; | ||
33 | |||
34 | ctx->total = 0; | ||
35 | ctx->buflen = 0; | ||
36 | } | ||
37 | |||
38 | /* These are the four functions used in the four steps of the MD5 algorithm | ||
39 | * and defined in the RFC 1321. The first function is a little bit optimized | ||
40 | * (as found in Colin Plumbs public domain implementation). | ||
41 | * #define FF(b, c, d) ((b & c) | (~b & d)) | ||
42 | */ | ||
43 | # define FF(b, c, d) (d ^ (b & (c ^ d))) | ||
44 | # define FG(b, c, d) FF (d, b, c) | ||
45 | # define FH(b, c, d) (b ^ c ^ d) | ||
46 | # define FI(b, c, d) (c ^ (b | ~d)) | ||
47 | |||
48 | /* Hash a single block, 64 bytes long and 4-byte aligned. */ | ||
49 | static void md5_hash_block(const void *buffer, md5_ctx_t *ctx) | ||
50 | { | ||
51 | uint32_t correct_words[16]; | ||
52 | const uint32_t *words = buffer; | ||
53 | |||
54 | # if MD5_SIZE_VS_SPEED > 0 | ||
55 | static const uint32_t C_array[] = { | ||
56 | /* round 1 */ | ||
57 | 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, | ||
58 | 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, | ||
59 | 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, | ||
60 | 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, | ||
61 | /* round 2 */ | ||
62 | 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, | ||
63 | 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, | ||
64 | 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, | ||
65 | 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, | ||
66 | /* round 3 */ | ||
67 | 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, | ||
68 | 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, | ||
69 | 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, | ||
70 | 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, | ||
71 | /* round 4 */ | ||
72 | 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, | ||
73 | 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, | ||
74 | 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, | ||
75 | 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 | ||
76 | }; | ||
77 | |||
78 | static const char P_array[] = { | ||
79 | # if MD5_SIZE_VS_SPEED > 1 | ||
80 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */ | ||
81 | # endif /* MD5_SIZE_VS_SPEED > 1 */ | ||
82 | 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */ | ||
83 | 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */ | ||
84 | 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */ | ||
85 | }; | ||
86 | |||
87 | # if MD5_SIZE_VS_SPEED > 1 | ||
88 | static const char S_array[] = { | ||
89 | 7, 12, 17, 22, | ||
90 | 5, 9, 14, 20, | ||
91 | 4, 11, 16, 23, | ||
92 | 6, 10, 15, 21 | ||
93 | }; | ||
94 | # endif /* MD5_SIZE_VS_SPEED > 1 */ | ||
95 | # endif | ||
96 | |||
97 | uint32_t A = ctx->A; | ||
98 | uint32_t B = ctx->B; | ||
99 | uint32_t C = ctx->C; | ||
100 | uint32_t D = ctx->D; | ||
101 | |||
102 | /* Process all bytes in the buffer with 64 bytes in each round of | ||
103 | the loop. */ | ||
104 | uint32_t *cwp = correct_words; | ||
105 | uint32_t A_save = A; | ||
106 | uint32_t B_save = B; | ||
107 | uint32_t C_save = C; | ||
108 | uint32_t D_save = D; | ||
109 | |||
110 | # if MD5_SIZE_VS_SPEED > 1 | ||
111 | # define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) | ||
112 | |||
113 | const uint32_t *pc; | ||
114 | const char *pp; | ||
115 | const char *ps; | ||
116 | int i; | ||
117 | uint32_t temp; | ||
118 | |||
119 | for (i = 0; i < 16; i++) { | ||
120 | cwp[i] = SWAP_LE32(words[i]); | ||
121 | } | ||
122 | words += 16; | ||
123 | |||
124 | # if MD5_SIZE_VS_SPEED > 2 | ||
125 | pc = C_array; | ||
126 | pp = P_array; | ||
127 | ps = S_array - 4; | ||
128 | |||
129 | for (i = 0; i < 64; i++) { | ||
130 | if ((i & 0x0f) == 0) | ||
131 | ps += 4; | ||
132 | temp = A; | ||
133 | switch (i >> 4) { | ||
134 | case 0: | ||
135 | temp += FF(B, C, D); | ||
136 | break; | ||
137 | case 1: | ||
138 | temp += FG(B, C, D); | ||
139 | break; | ||
140 | case 2: | ||
141 | temp += FH(B, C, D); | ||
142 | break; | ||
143 | case 3: | ||
144 | temp += FI(B, C, D); | ||
145 | } | ||
146 | temp += cwp[(int) (*pp++)] + *pc++; | ||
147 | CYCLIC(temp, ps[i & 3]); | ||
148 | temp += B; | ||
149 | A = D; | ||
150 | D = C; | ||
151 | C = B; | ||
152 | B = temp; | ||
153 | } | ||
154 | # else | ||
155 | pc = C_array; | ||
156 | pp = P_array; | ||
157 | ps = S_array; | ||
158 | |||
159 | for (i = 0; i < 16; i++) { | ||
160 | temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++; | ||
161 | CYCLIC(temp, ps[i & 3]); | ||
162 | temp += B; | ||
163 | A = D; | ||
164 | D = C; | ||
165 | C = B; | ||
166 | B = temp; | ||
167 | } | ||
168 | |||
169 | ps += 4; | ||
170 | for (i = 0; i < 16; i++) { | ||
171 | temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++; | ||
172 | CYCLIC(temp, ps[i & 3]); | ||
173 | temp += B; | ||
174 | A = D; | ||
175 | D = C; | ||
176 | C = B; | ||
177 | B = temp; | ||
178 | } | ||
179 | ps += 4; | ||
180 | for (i = 0; i < 16; i++) { | ||
181 | temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++; | ||
182 | CYCLIC(temp, ps[i & 3]); | ||
183 | temp += B; | ||
184 | A = D; | ||
185 | D = C; | ||
186 | C = B; | ||
187 | B = temp; | ||
188 | } | ||
189 | ps += 4; | ||
190 | for (i = 0; i < 16; i++) { | ||
191 | temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++; | ||
192 | CYCLIC(temp, ps[i & 3]); | ||
193 | temp += B; | ||
194 | A = D; | ||
195 | D = C; | ||
196 | C = B; | ||
197 | B = temp; | ||
198 | } | ||
199 | |||
200 | # endif /* MD5_SIZE_VS_SPEED > 2 */ | ||
201 | # else | ||
202 | /* First round: using the given function, the context and a constant | ||
203 | the next context is computed. Because the algorithms processing | ||
204 | unit is a 32-bit word and it is determined to work on words in | ||
205 | little endian byte order we perhaps have to change the byte order | ||
206 | before the computation. To reduce the work for the next steps | ||
207 | we store the swapped words in the array CORRECT_WORDS. */ | ||
208 | |||
209 | # define OP(a, b, c, d, s, T) \ | ||
210 | do \ | ||
211 | { \ | ||
212 | a += FF (b, c, d) + (*cwp++ = SWAP_LE32(*words)) + T; \ | ||
213 | ++words; \ | ||
214 | CYCLIC (a, s); \ | ||
215 | a += b; \ | ||
216 | } \ | ||
217 | while (0) | ||
218 | |||
219 | /* It is unfortunate that C does not provide an operator for | ||
220 | cyclic rotation. Hope the C compiler is smart enough. */ | ||
221 | /* gcc 2.95.4 seems to be --aaronl */ | ||
222 | # define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) | ||
223 | |||
224 | /* Before we start, one word to the strange constants. | ||
225 | They are defined in RFC 1321 as | ||
226 | |||
227 | T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 | ||
228 | */ | ||
229 | |||
230 | # if MD5_SIZE_VS_SPEED == 1 | ||
231 | const uint32_t *pc; | ||
232 | const char *pp; | ||
233 | int i; | ||
234 | # endif /* MD5_SIZE_VS_SPEED */ | ||
235 | |||
236 | /* Round 1. */ | ||
237 | # if MD5_SIZE_VS_SPEED == 1 | ||
238 | pc = C_array; | ||
239 | for (i = 0; i < 4; i++) { | ||
240 | OP(A, B, C, D, 7, *pc++); | ||
241 | OP(D, A, B, C, 12, *pc++); | ||
242 | OP(C, D, A, B, 17, *pc++); | ||
243 | OP(B, C, D, A, 22, *pc++); | ||
244 | } | ||
245 | # else | ||
246 | OP(A, B, C, D, 7, 0xd76aa478); | ||
247 | OP(D, A, B, C, 12, 0xe8c7b756); | ||
248 | OP(C, D, A, B, 17, 0x242070db); | ||
249 | OP(B, C, D, A, 22, 0xc1bdceee); | ||
250 | OP(A, B, C, D, 7, 0xf57c0faf); | ||
251 | OP(D, A, B, C, 12, 0x4787c62a); | ||
252 | OP(C, D, A, B, 17, 0xa8304613); | ||
253 | OP(B, C, D, A, 22, 0xfd469501); | ||
254 | OP(A, B, C, D, 7, 0x698098d8); | ||
255 | OP(D, A, B, C, 12, 0x8b44f7af); | ||
256 | OP(C, D, A, B, 17, 0xffff5bb1); | ||
257 | OP(B, C, D, A, 22, 0x895cd7be); | ||
258 | OP(A, B, C, D, 7, 0x6b901122); | ||
259 | OP(D, A, B, C, 12, 0xfd987193); | ||
260 | OP(C, D, A, B, 17, 0xa679438e); | ||
261 | OP(B, C, D, A, 22, 0x49b40821); | ||
262 | # endif /* MD5_SIZE_VS_SPEED == 1 */ | ||
263 | |||
264 | /* For the second to fourth round we have the possibly swapped words | ||
265 | in CORRECT_WORDS. Redefine the macro to take an additional first | ||
266 | argument specifying the function to use. */ | ||
267 | # undef OP | ||
268 | # define OP(f, a, b, c, d, k, s, T) \ | ||
269 | do \ | ||
270 | { \ | ||
271 | a += f (b, c, d) + correct_words[k] + T; \ | ||
272 | CYCLIC (a, s); \ | ||
273 | a += b; \ | ||
274 | } \ | ||
275 | while (0) | ||
276 | |||
277 | /* Round 2. */ | ||
278 | # if MD5_SIZE_VS_SPEED == 1 | ||
279 | pp = P_array; | ||
280 | for (i = 0; i < 4; i++) { | ||
281 | OP(FG, A, B, C, D, (int) (*pp++), 5, *pc++); | ||
282 | OP(FG, D, A, B, C, (int) (*pp++), 9, *pc++); | ||
283 | OP(FG, C, D, A, B, (int) (*pp++), 14, *pc++); | ||
284 | OP(FG, B, C, D, A, (int) (*pp++), 20, *pc++); | ||
285 | } | ||
286 | # else | ||
287 | OP(FG, A, B, C, D, 1, 5, 0xf61e2562); | ||
288 | OP(FG, D, A, B, C, 6, 9, 0xc040b340); | ||
289 | OP(FG, C, D, A, B, 11, 14, 0x265e5a51); | ||
290 | OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa); | ||
291 | OP(FG, A, B, C, D, 5, 5, 0xd62f105d); | ||
292 | OP(FG, D, A, B, C, 10, 9, 0x02441453); | ||
293 | OP(FG, C, D, A, B, 15, 14, 0xd8a1e681); | ||
294 | OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8); | ||
295 | OP(FG, A, B, C, D, 9, 5, 0x21e1cde6); | ||
296 | OP(FG, D, A, B, C, 14, 9, 0xc33707d6); | ||
297 | OP(FG, C, D, A, B, 3, 14, 0xf4d50d87); | ||
298 | OP(FG, B, C, D, A, 8, 20, 0x455a14ed); | ||
299 | OP(FG, A, B, C, D, 13, 5, 0xa9e3e905); | ||
300 | OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8); | ||
301 | OP(FG, C, D, A, B, 7, 14, 0x676f02d9); | ||
302 | OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a); | ||
303 | # endif /* MD5_SIZE_VS_SPEED == 1 */ | ||
304 | |||
305 | /* Round 3. */ | ||
306 | # if MD5_SIZE_VS_SPEED == 1 | ||
307 | for (i = 0; i < 4; i++) { | ||
308 | OP(FH, A, B, C, D, (int) (*pp++), 4, *pc++); | ||
309 | OP(FH, D, A, B, C, (int) (*pp++), 11, *pc++); | ||
310 | OP(FH, C, D, A, B, (int) (*pp++), 16, *pc++); | ||
311 | OP(FH, B, C, D, A, (int) (*pp++), 23, *pc++); | ||
312 | } | ||
313 | # else | ||
314 | OP(FH, A, B, C, D, 5, 4, 0xfffa3942); | ||
315 | OP(FH, D, A, B, C, 8, 11, 0x8771f681); | ||
316 | OP(FH, C, D, A, B, 11, 16, 0x6d9d6122); | ||
317 | OP(FH, B, C, D, A, 14, 23, 0xfde5380c); | ||
318 | OP(FH, A, B, C, D, 1, 4, 0xa4beea44); | ||
319 | OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9); | ||
320 | OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60); | ||
321 | OP(FH, B, C, D, A, 10, 23, 0xbebfbc70); | ||
322 | OP(FH, A, B, C, D, 13, 4, 0x289b7ec6); | ||
323 | OP(FH, D, A, B, C, 0, 11, 0xeaa127fa); | ||
324 | OP(FH, C, D, A, B, 3, 16, 0xd4ef3085); | ||
325 | OP(FH, B, C, D, A, 6, 23, 0x04881d05); | ||
326 | OP(FH, A, B, C, D, 9, 4, 0xd9d4d039); | ||
327 | OP(FH, D, A, B, C, 12, 11, 0xe6db99e5); | ||
328 | OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8); | ||
329 | OP(FH, B, C, D, A, 2, 23, 0xc4ac5665); | ||
330 | # endif /* MD5_SIZE_VS_SPEED == 1 */ | ||
331 | |||
332 | /* Round 4. */ | ||
333 | # if MD5_SIZE_VS_SPEED == 1 | ||
334 | for (i = 0; i < 4; i++) { | ||
335 | OP(FI, A, B, C, D, (int) (*pp++), 6, *pc++); | ||
336 | OP(FI, D, A, B, C, (int) (*pp++), 10, *pc++); | ||
337 | OP(FI, C, D, A, B, (int) (*pp++), 15, *pc++); | ||
338 | OP(FI, B, C, D, A, (int) (*pp++), 21, *pc++); | ||
339 | } | ||
340 | # else | ||
341 | OP(FI, A, B, C, D, 0, 6, 0xf4292244); | ||
342 | OP(FI, D, A, B, C, 7, 10, 0x432aff97); | ||
343 | OP(FI, C, D, A, B, 14, 15, 0xab9423a7); | ||
344 | OP(FI, B, C, D, A, 5, 21, 0xfc93a039); | ||
345 | OP(FI, A, B, C, D, 12, 6, 0x655b59c3); | ||
346 | OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92); | ||
347 | OP(FI, C, D, A, B, 10, 15, 0xffeff47d); | ||
348 | OP(FI, B, C, D, A, 1, 21, 0x85845dd1); | ||
349 | OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f); | ||
350 | OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0); | ||
351 | OP(FI, C, D, A, B, 6, 15, 0xa3014314); | ||
352 | OP(FI, B, C, D, A, 13, 21, 0x4e0811a1); | ||
353 | OP(FI, A, B, C, D, 4, 6, 0xf7537e82); | ||
354 | OP(FI, D, A, B, C, 11, 10, 0xbd3af235); | ||
355 | OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb); | ||
356 | OP(FI, B, C, D, A, 9, 21, 0xeb86d391); | ||
357 | # endif /* MD5_SIZE_VS_SPEED == 1 */ | ||
358 | # endif /* MD5_SIZE_VS_SPEED > 1 */ | ||
359 | |||
360 | /* Add the starting values of the context. */ | ||
361 | A += A_save; | ||
362 | B += B_save; | ||
363 | C += C_save; | ||
364 | D += D_save; | ||
365 | |||
366 | /* Put checksum in context given as argument. */ | ||
367 | ctx->A = A; | ||
368 | ctx->B = B; | ||
369 | ctx->C = C; | ||
370 | ctx->D = D; | ||
371 | } | ||
372 | |||
373 | /* Feed data through a temporary buffer to call md5_hash_aligned_block() | ||
374 | * with chunks of data that are 4-byte aligned and a multiple of 64 bytes. | ||
375 | * This function's internal buffer remembers previous data until it has 64 | ||
376 | * bytes worth to pass on. Call md5_end() to flush this buffer. */ | ||
377 | |||
378 | void md5_hash(const void *buffer, size_t len, md5_ctx_t *ctx) | ||
379 | { | ||
380 | char *buf=(char *)buffer; | ||
381 | |||
382 | /* RFC 1321 specifies the possible length of the file up to 2^64 bits, | ||
383 | * Here we only track the number of bytes. */ | ||
384 | |||
385 | ctx->total += len; | ||
386 | |||
387 | // Process all input. | ||
388 | |||
389 | while (len) { | ||
390 | int i = 64 - ctx->buflen; | ||
391 | |||
392 | // Copy data into aligned buffer. | ||
393 | |||
394 | if (i > len) i = len; | ||
395 | memcpy(ctx->buffer + ctx->buflen, buf, i); | ||
396 | len -= i; | ||
397 | ctx->buflen += i; | ||
398 | buf += i; | ||
399 | |||
400 | // When buffer fills up, process it. | ||
401 | |||
402 | if (ctx->buflen == 64) { | ||
403 | md5_hash_block(ctx->buffer, ctx); | ||
404 | ctx->buflen = 0; | ||
405 | } | ||
406 | } | ||
407 | } | ||
408 | |||
409 | /* Process the remaining bytes in the buffer and put result from CTX | ||
410 | * in first 16 bytes following RESBUF. The result is always in little | ||
411 | * endian byte order, so that a byte-wise output yields to the wanted | ||
412 | * ASCII representation of the message digest. | ||
413 | * | ||
414 | * IMPORTANT: On some systems it is required that RESBUF is correctly | ||
415 | * aligned for a 32 bits value. | ||
416 | */ | ||
417 | void *md5_end(void *resbuf, md5_ctx_t *ctx) | ||
418 | { | ||
419 | char *buf = ctx->buffer; | ||
420 | int i; | ||
421 | |||
422 | /* Pad data to block size. */ | ||
423 | |||
424 | buf[ctx->buflen++] = 0x80; | ||
425 | memset(buf + ctx->buflen, 0, 128 - ctx->buflen); | ||
426 | |||
427 | /* Put the 64-bit file length in *bits* at the end of the buffer. */ | ||
428 | ctx->total <<= 3; | ||
429 | if (ctx->buflen > 56) buf += 64; | ||
430 | for (i = 0; i < 8; i++) buf[56 + i] = ctx->total >> (i*8); | ||
431 | |||
432 | /* Process last bytes. */ | ||
433 | if (buf != ctx->buffer) md5_hash_block(ctx->buffer, ctx); | ||
434 | md5_hash_block(buf, ctx); | ||
435 | |||
436 | /* Put result from CTX in first 16 bytes following RESBUF. The result is | ||
437 | * always in little endian byte order, so that a byte-wise output yields | ||
438 | * to the wanted ASCII representation of the message digest. | ||
439 | * | ||
440 | * IMPORTANT: On some systems it is required that RESBUF is correctly | ||
441 | * aligned for a 32 bits value. | ||
442 | */ | ||
443 | ((uint32_t *) resbuf)[0] = SWAP_LE32(ctx->A); | ||
444 | ((uint32_t *) resbuf)[1] = SWAP_LE32(ctx->B); | ||
445 | ((uint32_t *) resbuf)[2] = SWAP_LE32(ctx->C); | ||
446 | ((uint32_t *) resbuf)[3] = SWAP_LE32(ctx->D); | ||
447 | |||
448 | return resbuf; | ||
449 | } | ||
450 | |||
diff --git a/libbb/messages.c b/libbb/messages.c new file mode 100644 index 000000000..c640faf5b --- /dev/null +++ b/libbb/messages.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
4 | * | ||
5 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
6 | */ | ||
7 | |||
8 | #include "libbb.h" | ||
9 | |||
10 | #ifndef BB_EXTRA_VERSION | ||
11 | #define BANNER "BusyBox v" BB_VER " (" BB_BT ")" | ||
12 | #else | ||
13 | #define BANNER "BusyBox v" BB_VER " (" BB_EXTRA_VERSION ")" | ||
14 | #endif | ||
15 | const char BB_BANNER[] = BANNER; | ||
16 | const char bb_msg_full_version[] = BANNER " multi-call binary"; | ||
17 | |||
18 | const char bb_msg_memory_exhausted[] = "memory exhausted"; | ||
19 | const char bb_msg_invalid_date[] = "invalid date '%s'"; | ||
20 | const char bb_msg_write_error[] = "write error"; | ||
21 | const char bb_msg_read_error[] = "read error"; | ||
22 | const char bb_msg_unknown[] = "(unknown)"; | ||
23 | const char bb_msg_can_not_create_raw_socket[] = "can't create raw socket"; | ||
24 | const char bb_msg_perm_denied_are_you_root[] = "permission denied. (are you root?)"; | ||
25 | const char bb_msg_requires_arg[] = "%s requires an argument"; | ||
26 | const char bb_msg_invalid_arg[] = "invalid argument '%s' to '%s'"; | ||
27 | const char bb_msg_standard_input[] = "standard input"; | ||
28 | const char bb_msg_standard_output[] = "standard output"; | ||
29 | |||
30 | const char bb_str_default[] = "default"; | ||
31 | |||
32 | const char bb_path_passwd_file[] = "/etc/passwd"; | ||
33 | const char bb_path_shadow_file[] = "/etc/shadow"; | ||
34 | const char bb_path_group_file[] = "/etc/group"; | ||
35 | const char bb_path_gshadow_file[] = "/etc/gshadow"; | ||
36 | const char bb_path_nologin_file[] = "/etc/nologin"; | ||
37 | const char bb_path_securetty_file[] = "/etc/securetty"; | ||
38 | const char bb_path_motd_file[] = "/etc/motd"; | ||
39 | const char bb_default_login_shell[] = LIBBB_DEFAULT_LOGIN_SHELL; | ||
40 | const char bb_dev_null[] = "/dev/null"; | ||
41 | |||
42 | #include <utmp.h> | ||
43 | /* This is usually something like "/var/adm/wtmp" or "/var/log/wtmp" */ | ||
44 | const char bb_path_wtmp_file[] = | ||
45 | #if defined _PATH_WTMP | ||
46 | _PATH_WTMP; | ||
47 | #elif defined WTMP_FILE | ||
48 | WTMP_FILE; | ||
49 | #else | ||
50 | # error unknown path to wtmp file | ||
51 | #endif | ||
52 | |||
53 | char bb_common_bufsiz1[BUFSIZ+1]; | ||
diff --git a/libbb/mode_string.c b/libbb/mode_string.c new file mode 100644 index 000000000..01029bfee --- /dev/null +++ b/libbb/mode_string.c | |||
@@ -0,0 +1,128 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * mode_string implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | /* Aug 13, 2003 | ||
11 | * Fix a bug reported by junkio@cox.net involving the mode_chars index. | ||
12 | */ | ||
13 | |||
14 | |||
15 | #include <assert.h> | ||
16 | #include <sys/stat.h> | ||
17 | |||
18 | #include "libbb.h" | ||
19 | |||
20 | #if ( S_ISUID != 04000 ) || ( S_ISGID != 02000 ) || ( S_ISVTX != 01000 ) \ | ||
21 | || ( S_IRUSR != 00400 ) || ( S_IWUSR != 00200 ) || ( S_IXUSR != 00100 ) \ | ||
22 | || ( S_IRGRP != 00040 ) || ( S_IWGRP != 00020 ) || ( S_IXGRP != 00010 ) \ | ||
23 | || ( S_IROTH != 00004 ) || ( S_IWOTH != 00002 ) || ( S_IXOTH != 00001 ) | ||
24 | #error permission bitflag value assumption(s) violated! | ||
25 | #endif | ||
26 | |||
27 | #if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \ | ||
28 | || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 ) \ | ||
29 | || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \ | ||
30 | || ( S_IFIFO != 0010000 ) | ||
31 | #warning mode type bitflag value assumption(s) violated! falling back to larger version | ||
32 | |||
33 | #if (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX) == 07777 | ||
34 | #undef mode_t | ||
35 | #define mode_t unsigned short | ||
36 | #endif | ||
37 | |||
38 | static const mode_t mode_flags[] = { | ||
39 | S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID, | ||
40 | S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID, | ||
41 | S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX | ||
42 | }; | ||
43 | |||
44 | /* The static const char arrays below are duplicated for the two cases | ||
45 | * because moving them ahead of the mode_flags declaration cause a text | ||
46 | * size increase with the gcc version I'm using. */ | ||
47 | |||
48 | /* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C', | ||
49 | * and 'B' types don't appear to be available on linux. So I removed them. */ | ||
50 | static const char type_chars[16] = "?pc?d?b?-?l?s???"; | ||
51 | /* 0123456789abcdef */ | ||
52 | static const char mode_chars[7] = "rwxSTst"; | ||
53 | |||
54 | const char *bb_mode_string(int mode) | ||
55 | { | ||
56 | static char buf[12]; | ||
57 | char *p = buf; | ||
58 | |||
59 | int i, j, k; | ||
60 | |||
61 | *p = type_chars[ (mode >> 12) & 0xf ]; | ||
62 | i = 0; | ||
63 | do { | ||
64 | j = k = 0; | ||
65 | do { | ||
66 | *++p = '-'; | ||
67 | if (mode & mode_flags[i+j]) { | ||
68 | *p = mode_chars[j]; | ||
69 | k = j; | ||
70 | } | ||
71 | } while (++j < 3); | ||
72 | if (mode & mode_flags[i+j]) { | ||
73 | *p = mode_chars[3 + (k & 2) + ((i&8) >> 3)]; | ||
74 | } | ||
75 | i += 4; | ||
76 | } while (i < 12); | ||
77 | |||
78 | /* Note: We don't bother with nul termination because bss initialization | ||
79 | * should have taken care of that for us. If the user scribbled in buf | ||
80 | * memory, they deserve whatever happens. But we'll at least assert. */ | ||
81 | assert(buf[10] == 0); | ||
82 | |||
83 | return buf; | ||
84 | } | ||
85 | |||
86 | #else | ||
87 | |||
88 | /* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C', | ||
89 | * and 'B' types don't appear to be available on linux. So I removed them. */ | ||
90 | static const char type_chars[16] = "?pc?d?b?-?l?s???"; | ||
91 | /* 0123456789abcdef */ | ||
92 | static const char mode_chars[7] = "rwxSTst"; | ||
93 | |||
94 | const char *bb_mode_string(int mode) | ||
95 | { | ||
96 | static char buf[12]; | ||
97 | char *p = buf; | ||
98 | |||
99 | int i, j, k, m; | ||
100 | |||
101 | *p = type_chars[ (mode >> 12) & 0xf ]; | ||
102 | i = 0; | ||
103 | m = 0400; | ||
104 | do { | ||
105 | j = k = 0; | ||
106 | do { | ||
107 | *++p = '-'; | ||
108 | if (mode & m) { | ||
109 | *p = mode_chars[j]; | ||
110 | k = j; | ||
111 | } | ||
112 | m >>= 1; | ||
113 | } while (++j < 3); | ||
114 | ++i; | ||
115 | if (mode & (010000 >> i)) { | ||
116 | *p = mode_chars[3 + (k & 2) + (i == 3)]; | ||
117 | } | ||
118 | } while (i < 3); | ||
119 | |||
120 | /* Note: We don't bother with nul termination because bss initialization | ||
121 | * should have taken care of that for us. If the user scribbled in buf | ||
122 | * memory, they deserve whatever happens. But we'll at least assert. */ | ||
123 | assert(buf[10] == 0); | ||
124 | |||
125 | return buf; | ||
126 | } | ||
127 | |||
128 | #endif | ||
diff --git a/libbb/mtab.c b/libbb/mtab.c new file mode 100644 index 000000000..18386efb5 --- /dev/null +++ b/libbb/mtab.c | |||
@@ -0,0 +1,52 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <mntent.h> | ||
11 | #include "libbb.h" | ||
12 | |||
13 | #if ENABLE_FEATURE_MTAB_SUPPORT | ||
14 | void erase_mtab(const char *name) | ||
15 | { | ||
16 | struct mntent *entries = NULL; | ||
17 | int i, count = 0; | ||
18 | FILE *mountTable; | ||
19 | struct mntent *m; | ||
20 | |||
21 | mountTable = setmntent(bb_path_mtab_file, "r"); | ||
22 | /* Bummer. Fall back on trying the /proc filesystem */ | ||
23 | if (!mountTable) mountTable = setmntent("/proc/mounts", "r"); | ||
24 | if (!mountTable) { | ||
25 | bb_perror_msg(bb_path_mtab_file); | ||
26 | return; | ||
27 | } | ||
28 | |||
29 | while ((m = getmntent(mountTable)) != 0) { | ||
30 | i = count++; | ||
31 | entries = xrealloc(entries, count * sizeof(entries[0])); | ||
32 | entries[i].mnt_fsname = xstrdup(m->mnt_fsname); | ||
33 | entries[i].mnt_dir = xstrdup(m->mnt_dir); | ||
34 | entries[i].mnt_type = xstrdup(m->mnt_type); | ||
35 | entries[i].mnt_opts = xstrdup(m->mnt_opts); | ||
36 | entries[i].mnt_freq = m->mnt_freq; | ||
37 | entries[i].mnt_passno = m->mnt_passno; | ||
38 | } | ||
39 | endmntent(mountTable); | ||
40 | |||
41 | mountTable = setmntent(bb_path_mtab_file, "w"); | ||
42 | if (mountTable) { | ||
43 | for (i = 0; i < count; i++) { | ||
44 | if (strcmp(entries[i].mnt_fsname, name) != 0 | ||
45 | && strcmp(entries[i].mnt_dir, name) != 0) | ||
46 | addmntent(mountTable, &entries[i]); | ||
47 | } | ||
48 | endmntent(mountTable); | ||
49 | } else if (errno != EROFS) | ||
50 | bb_perror_msg(bb_path_mtab_file); | ||
51 | } | ||
52 | #endif | ||
diff --git a/libbb/mtab_file.c b/libbb/mtab_file.c new file mode 100644 index 000000000..67367e3d7 --- /dev/null +++ b/libbb/mtab_file.c | |||
@@ -0,0 +1,17 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdio.h> | ||
11 | #include "libbb.h" | ||
12 | |||
13 | |||
14 | /* Busybox mount uses either /proc/mounts or /etc/mtab to | ||
15 | * get the list of currently mounted filesystems */ | ||
16 | const char bb_path_mtab_file[] = | ||
17 | USE_FEATURE_MTAB_SUPPORT("/etc/mtab")SKIP_FEATURE_MTAB_SUPPORT("/proc/mounts"); | ||
diff --git a/libbb/obscure.c b/libbb/obscure.c new file mode 100644 index 000000000..2599095df --- /dev/null +++ b/libbb/obscure.c | |||
@@ -0,0 +1,170 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini weak password checker implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2006 Tito Ragusa <farmatito@tiscali.it> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | /* A good password: | ||
11 | 1) should contain at least six characters (man passwd); | ||
12 | 2) empty passwords are not permitted; | ||
13 | 3) should contain a mix of four different types of characters | ||
14 | upper case letters, | ||
15 | lower case letters, | ||
16 | numbers, | ||
17 | special characters such as !@#$%^&*,;". | ||
18 | This password types should not be permitted: | ||
19 | a) pure numbers: birthdates, social security number, license plate, phone numbers; | ||
20 | b) words and all letters only passwords (uppercase, lowercase or mixed) | ||
21 | as palindromes, consecutive or repetitive letters | ||
22 | or adjacent letters on your keyboard; | ||
23 | c) username, real name, company name or (e-mail?) address | ||
24 | in any form (as-is, reversed, capitalized, doubled, etc.). | ||
25 | (we can check only against username, gecos and hostname) | ||
26 | d) common and obvious letter-number replacements | ||
27 | (e.g. replace the letter O with number 0) | ||
28 | such as "M1cr0$0ft" or "P@ssw0rd" (CAVEAT: we cannot check for them | ||
29 | without the use of a dictionary). | ||
30 | |||
31 | For each missing type of characters an increase of password length is | ||
32 | requested. | ||
33 | |||
34 | If user is root we warn only. | ||
35 | |||
36 | CAVEAT: some older versions of crypt() truncates passwords to 8 chars, | ||
37 | so that aaaaaaaa1Q$ is equal to aaaaaaaa making it possible to fool | ||
38 | some of our checks. We don't test for this special case as newer versions | ||
39 | of crypt do not truncate passwords. | ||
40 | */ | ||
41 | |||
42 | #include "libbb.h" | ||
43 | |||
44 | static int string_checker_helper(const char *p1, const char *p2) __attribute__ ((__pure__)); | ||
45 | |||
46 | static int string_checker_helper(const char *p1, const char *p2) | ||
47 | { | ||
48 | /* as-is or capitalized */ | ||
49 | if (strcasecmp(p1, p2) == 0 | ||
50 | /* as sub-string */ | ||
51 | || strcasestr(p2, p1) != NULL | ||
52 | /* invert in case haystack is shorter than needle */ | ||
53 | || strcasestr(p1, p2) != NULL) | ||
54 | return 1; | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static int string_checker(const char *p1, const char *p2) | ||
59 | { | ||
60 | int size; | ||
61 | /* check string */ | ||
62 | int ret = string_checker_helper(p1, p2); | ||
63 | /* Make our own copy */ | ||
64 | char *p = xstrdup(p1); | ||
65 | /* reverse string */ | ||
66 | size = strlen(p); | ||
67 | |||
68 | while (size--) { | ||
69 | *p = p1[size]; | ||
70 | p++; | ||
71 | } | ||
72 | /* restore pointer */ | ||
73 | p -= strlen(p1); | ||
74 | /* check reversed string */ | ||
75 | ret |= string_checker_helper(p, p2); | ||
76 | /* clean up */ | ||
77 | memset(p, 0, strlen(p1)); | ||
78 | free(p); | ||
79 | return ret; | ||
80 | } | ||
81 | |||
82 | #define LOWERCASE 1 | ||
83 | #define UPPERCASE 2 | ||
84 | #define NUMBERS 4 | ||
85 | #define SPECIAL 8 | ||
86 | |||
87 | static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw) | ||
88 | { | ||
89 | int i; | ||
90 | int c; | ||
91 | int length; | ||
92 | int mixed = 0; | ||
93 | /* Add 2 for each type of characters to the minlen of password */ | ||
94 | int size = CONFIG_PASSWORD_MINLEN + 8; | ||
95 | const char *p; | ||
96 | char hostname[255]; | ||
97 | |||
98 | /* size */ | ||
99 | if (!new_p || (length = strlen(new_p)) < CONFIG_PASSWORD_MINLEN) | ||
100 | return "too short"; | ||
101 | |||
102 | /* no username as-is, as sub-string, reversed, capitalized, doubled */ | ||
103 | if (string_checker(new_p, pw->pw_name)) { | ||
104 | return "similar to username"; | ||
105 | } | ||
106 | /* no gecos as-is, as sub-string, reversed, capitalized, doubled */ | ||
107 | if (*pw->pw_gecos && string_checker(new_p, pw->pw_gecos)) { | ||
108 | return "similar to gecos"; | ||
109 | } | ||
110 | /* hostname as-is, as sub-string, reversed, capitalized, doubled */ | ||
111 | if (gethostname(hostname, 255) == 0) { | ||
112 | hostname[254] = '\0'; | ||
113 | if (string_checker(new_p, hostname)) { | ||
114 | return "similar to hostname"; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | /* Should / Must contain a mix of: */ | ||
119 | for (i = 0; i < length; i++) { | ||
120 | if (islower(new_p[i])) { /* a-z */ | ||
121 | mixed |= LOWERCASE; | ||
122 | } else if (isupper(new_p[i])) { /* A-Z */ | ||
123 | mixed |= UPPERCASE; | ||
124 | } else if (isdigit(new_p[i])) { /* 0-9 */ | ||
125 | mixed |= NUMBERS; | ||
126 | } else { /* special characters */ | ||
127 | mixed |= SPECIAL; | ||
128 | } | ||
129 | /* More than 50% similar characters ? */ | ||
130 | c = 0; | ||
131 | p = new_p; | ||
132 | while (1) { | ||
133 | if ((p = strchr(p, new_p[i])) == NULL) { | ||
134 | break; | ||
135 | } | ||
136 | c++; | ||
137 | if (!++p) { | ||
138 | break; /* move past the matched char if possible */ | ||
139 | } | ||
140 | } | ||
141 | |||
142 | if (c >= (length / 2)) { | ||
143 | return "too many similar characters"; | ||
144 | } | ||
145 | } | ||
146 | for (i=0; i<4; i++) | ||
147 | if (mixed & (1<<i)) size -= 2; | ||
148 | if (length < size) | ||
149 | return "too weak"; | ||
150 | |||
151 | if (old_p && old_p[0] != '\0') { | ||
152 | /* check vs. old password */ | ||
153 | if (string_checker(new_p, old_p)) { | ||
154 | return "similar to old password"; | ||
155 | } | ||
156 | } | ||
157 | return NULL; | ||
158 | } | ||
159 | |||
160 | int obscure(const char *old, const char *newval, const struct passwd *pw) | ||
161 | { | ||
162 | const char *msg; | ||
163 | |||
164 | msg = obscure_msg(old, newval, pw); | ||
165 | if (msg) { | ||
166 | printf("Bad password: %s\n", msg); | ||
167 | return 1; | ||
168 | } | ||
169 | return 0; | ||
170 | } | ||
diff --git a/libbb/parse_mode.c b/libbb/parse_mode.c new file mode 100644 index 000000000..356d95db6 --- /dev/null +++ b/libbb/parse_mode.c | |||
@@ -0,0 +1,164 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * parse_mode implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | /* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */ | ||
11 | |||
12 | #include <stdlib.h> | ||
13 | #include <assert.h> | ||
14 | #include <sys/stat.h> | ||
15 | #include "libbb.h" | ||
16 | |||
17 | #define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) | ||
18 | |||
19 | int bb_parse_mode(const char *s, mode_t *current_mode) | ||
20 | { | ||
21 | static const mode_t who_mask[] = { | ||
22 | S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */ | ||
23 | S_ISUID | S_IRWXU, /* u */ | ||
24 | S_ISGID | S_IRWXG, /* g */ | ||
25 | S_IRWXO /* o */ | ||
26 | }; | ||
27 | |||
28 | static const mode_t perm_mask[] = { | ||
29 | S_IRUSR | S_IRGRP | S_IROTH, /* r */ | ||
30 | S_IWUSR | S_IWGRP | S_IWOTH, /* w */ | ||
31 | S_IXUSR | S_IXGRP | S_IXOTH, /* x */ | ||
32 | S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */ | ||
33 | S_ISUID | S_ISGID, /* s */ | ||
34 | S_ISVTX /* t */ | ||
35 | }; | ||
36 | |||
37 | static const char who_chars[] = "augo"; | ||
38 | static const char perm_chars[] = "rwxXst"; | ||
39 | |||
40 | const char *p; | ||
41 | |||
42 | mode_t wholist; | ||
43 | mode_t permlist; | ||
44 | mode_t mask; | ||
45 | mode_t new_mode; | ||
46 | char op; | ||
47 | |||
48 | assert(s); | ||
49 | |||
50 | if (((unsigned int)(*s - '0')) < 8) { | ||
51 | unsigned long tmp; | ||
52 | char *e; | ||
53 | |||
54 | tmp = strtol(s, &e, 8); | ||
55 | if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */ | ||
56 | return 0; | ||
57 | } | ||
58 | *current_mode = tmp; | ||
59 | return 1; | ||
60 | } | ||
61 | |||
62 | mask = umask(0); | ||
63 | umask(mask); | ||
64 | |||
65 | new_mode = *current_mode; | ||
66 | |||
67 | /* Note: We allow empty clauses, and hence empty modes. | ||
68 | * We treat an empty mode as no change to perms. */ | ||
69 | |||
70 | while (*s) { /* Process clauses. */ | ||
71 | |||
72 | if (*s == ',') { /* We allow empty clauses. */ | ||
73 | ++s; | ||
74 | continue; | ||
75 | } | ||
76 | |||
77 | /* Get a wholist. */ | ||
78 | wholist = 0; | ||
79 | |||
80 | WHO_LIST: | ||
81 | p = who_chars; | ||
82 | do { | ||
83 | if (*p == *s) { | ||
84 | wholist |= who_mask[(int)(p-who_chars)]; | ||
85 | if (!*++s) { | ||
86 | return 0; | ||
87 | } | ||
88 | goto WHO_LIST; | ||
89 | } | ||
90 | } while (*++p); | ||
91 | |||
92 | do { /* Process action list. */ | ||
93 | if ((*s != '+') && (*s != '-')) { | ||
94 | if (*s != '=') { | ||
95 | return 0; | ||
96 | } | ||
97 | /* Since op is '=', clear all bits corresponding to the | ||
98 | * wholist, of all file bits if wholist is empty. */ | ||
99 | permlist = ~FILEMODEBITS; | ||
100 | if (wholist) { | ||
101 | permlist = ~wholist; | ||
102 | } | ||
103 | new_mode &= permlist; | ||
104 | } | ||
105 | op = *s++; | ||
106 | |||
107 | /* Check for permcopy. */ | ||
108 | p = who_chars + 1; /* Skip 'a' entry. */ | ||
109 | do { | ||
110 | if (*p == *s) { | ||
111 | int i = 0; | ||
112 | permlist = who_mask[(int)(p-who_chars)] | ||
113 | & (S_IRWXU | S_IRWXG | S_IRWXO) | ||
114 | & new_mode; | ||
115 | do { | ||
116 | if (permlist & perm_mask[i]) { | ||
117 | permlist |= perm_mask[i]; | ||
118 | } | ||
119 | } while (++i < 3); | ||
120 | ++s; | ||
121 | goto GOT_ACTION; | ||
122 | } | ||
123 | } while (*++p); | ||
124 | |||
125 | /* It was not a permcopy, so get a permlist. */ | ||
126 | permlist = 0; | ||
127 | |||
128 | PERM_LIST: | ||
129 | p = perm_chars; | ||
130 | do { | ||
131 | if (*p == *s) { | ||
132 | if ((*p != 'X') | ||
133 | || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH)) | ||
134 | ) { | ||
135 | permlist |= perm_mask[(int)(p-perm_chars)]; | ||
136 | } | ||
137 | if (!*++s) { | ||
138 | break; | ||
139 | } | ||
140 | goto PERM_LIST; | ||
141 | } | ||
142 | } while (*++p); | ||
143 | |||
144 | GOT_ACTION: | ||
145 | if (permlist) { /* The permlist was nonempty. */ | ||
146 | mode_t tmp = ~mask; | ||
147 | if (wholist) { | ||
148 | tmp = wholist; | ||
149 | } | ||
150 | permlist &= tmp; | ||
151 | |||
152 | if (op == '-') { | ||
153 | new_mode &= ~permlist; | ||
154 | } else { | ||
155 | new_mode |= permlist; | ||
156 | } | ||
157 | } | ||
158 | } while (*s && (*s != ',')); | ||
159 | } | ||
160 | |||
161 | *current_mode = new_mode; | ||
162 | |||
163 | return 1; | ||
164 | } | ||
diff --git a/libbb/perror_msg.c b/libbb/perror_msg.c new file mode 100644 index 000000000..7fb0830be --- /dev/null +++ b/libbb/perror_msg.c | |||
@@ -0,0 +1,23 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdio.h> | ||
11 | #include <errno.h> | ||
12 | #include <string.h> | ||
13 | #include <stdlib.h> | ||
14 | #include "libbb.h" | ||
15 | |||
16 | void bb_perror_msg(const char *s, ...) | ||
17 | { | ||
18 | va_list p; | ||
19 | |||
20 | va_start(p, s); | ||
21 | bb_vperror_msg(s, p); | ||
22 | va_end(p); | ||
23 | } | ||
diff --git a/libbb/perror_msg_and_die.c b/libbb/perror_msg_and_die.c new file mode 100644 index 000000000..2303ba211 --- /dev/null +++ b/libbb/perror_msg_and_die.c | |||
@@ -0,0 +1,26 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdio.h> | ||
11 | #include <errno.h> | ||
12 | #include <string.h> | ||
13 | #include <stdlib.h> | ||
14 | #include "libbb.h" | ||
15 | |||
16 | void bb_perror_msg_and_die(const char *s, ...) | ||
17 | { | ||
18 | va_list p; | ||
19 | |||
20 | va_start(p, s); | ||
21 | bb_vperror_msg(s, p); | ||
22 | va_end(p); | ||
23 | if (die_sleep) | ||
24 | sleep(die_sleep); | ||
25 | exit(xfunc_error_retval); | ||
26 | } | ||
diff --git a/libbb/perror_nomsg.c b/libbb/perror_nomsg.c new file mode 100644 index 000000000..3aefd5301 --- /dev/null +++ b/libbb/perror_nomsg.c | |||
@@ -0,0 +1,16 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * bb_perror_nomsg implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | void bb_perror_nomsg(void) | ||
13 | { | ||
14 | /* Ignore the gcc warning about a null format string. */ | ||
15 | bb_perror_msg(NULL); | ||
16 | } | ||
diff --git a/libbb/perror_nomsg_and_die.c b/libbb/perror_nomsg_and_die.c new file mode 100644 index 000000000..e5623c2a9 --- /dev/null +++ b/libbb/perror_nomsg_and_die.c | |||
@@ -0,0 +1,17 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * bb_perror_nomsg_and_die implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stddef.h> | ||
11 | #include "libbb.h" | ||
12 | |||
13 | void bb_perror_nomsg_and_die(void) | ||
14 | { | ||
15 | /* Ignore the gcc warning about a null format string. */ | ||
16 | bb_perror_msg_and_die(NULL); | ||
17 | } | ||
diff --git a/libbb/process_escape_sequence.c b/libbb/process_escape_sequence.c new file mode 100644 index 000000000..138e751f5 --- /dev/null +++ b/libbb/process_escape_sequence.c | |||
@@ -0,0 +1,89 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * and Vladimir Oleynik <dzo@simtreas.ru> | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include <stdio.h> | ||
12 | #include <limits.h> | ||
13 | #include <ctype.h> | ||
14 | #include "libbb.h" | ||
15 | |||
16 | #define WANT_HEX_ESCAPES 1 | ||
17 | |||
18 | /* Usual "this only works for ascii compatible encodings" disclaimer. */ | ||
19 | #undef _tolower | ||
20 | #define _tolower(X) ((X)|((char) 0x20)) | ||
21 | |||
22 | char bb_process_escape_sequence(const char **ptr) | ||
23 | { | ||
24 | static const char charmap[] = { | ||
25 | 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', 0, | ||
26 | '\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\', '\\' }; | ||
27 | |||
28 | const char *p; | ||
29 | const char *q; | ||
30 | unsigned int num_digits; | ||
31 | unsigned int r; | ||
32 | unsigned int n; | ||
33 | unsigned int d; | ||
34 | unsigned int base; | ||
35 | |||
36 | num_digits = n = 0; | ||
37 | base = 8; | ||
38 | q = *ptr; | ||
39 | |||
40 | #ifdef WANT_HEX_ESCAPES | ||
41 | if (*q == 'x') { | ||
42 | ++q; | ||
43 | base = 16; | ||
44 | ++num_digits; | ||
45 | } | ||
46 | #endif | ||
47 | |||
48 | do { | ||
49 | d = (unsigned int)(*q - '0'); | ||
50 | #ifdef WANT_HEX_ESCAPES | ||
51 | if (d >= 10) { | ||
52 | d = ((unsigned int)(_tolower(*q) - 'a')) + 10; | ||
53 | } | ||
54 | #endif | ||
55 | |||
56 | if (d >= base) { | ||
57 | #ifdef WANT_HEX_ESCAPES | ||
58 | if ((base == 16) && (!--num_digits)) { | ||
59 | /* return '\\'; */ | ||
60 | --q; | ||
61 | } | ||
62 | #endif | ||
63 | break; | ||
64 | } | ||
65 | |||
66 | r = n * base + d; | ||
67 | if (r > UCHAR_MAX) { | ||
68 | break; | ||
69 | } | ||
70 | |||
71 | n = r; | ||
72 | ++q; | ||
73 | } while (++num_digits < 3); | ||
74 | |||
75 | if (num_digits == 0) { /* mnemonic escape sequence? */ | ||
76 | p = charmap; | ||
77 | do { | ||
78 | if (*p == *q) { | ||
79 | q++; | ||
80 | break; | ||
81 | } | ||
82 | } while (*++p); | ||
83 | n = *(p+(sizeof(charmap)/2)); | ||
84 | } | ||
85 | |||
86 | *ptr = q; | ||
87 | |||
88 | return (char) n; | ||
89 | } | ||
diff --git a/libbb/procps.c b/libbb/procps.c new file mode 100644 index 000000000..017710ff4 --- /dev/null +++ b/libbb/procps.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright 1998 by Albert Cahalan; all rights reserved. | ||
6 | * Copyright (C) 2002 by Vladimir Oleynik <dzo@simtreas.ru> | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include "libbb.h" | ||
12 | |||
13 | |||
14 | typedef struct unsigned_to_name_map_t { | ||
15 | unsigned id; | ||
16 | char name[12]; | ||
17 | } unsigned_to_name_map_t; | ||
18 | |||
19 | typedef struct cache_t { | ||
20 | unsigned_to_name_map_t *cache; | ||
21 | int size; | ||
22 | } cache_t; | ||
23 | |||
24 | static cache_t username, groupname; | ||
25 | |||
26 | static void clear_cache(cache_t *cp) | ||
27 | { | ||
28 | free(cp->cache); | ||
29 | cp->cache = NULL; | ||
30 | cp->size = 0; | ||
31 | } | ||
32 | void clear_username_cache(void) | ||
33 | { | ||
34 | clear_cache(&username); | ||
35 | clear_cache(&groupname); | ||
36 | } | ||
37 | |||
38 | #if 0 /* more generic, but we don't need that yet */ | ||
39 | /* Returns -N-1 if not found. */ | ||
40 | /* cp->cache[N] is allocated and must be filled in this case */ | ||
41 | static int get_cached(cache_t *cp, unsigned id) | ||
42 | { | ||
43 | int i; | ||
44 | for (i = 0; i < cp->size; i++) | ||
45 | if (cp->cache[i].id == id) | ||
46 | return i; | ||
47 | i = cp->size++; | ||
48 | cp->cache = xrealloc(cp->cache, cp->size * sizeof(*cp->cache)); | ||
49 | cp->cache[i++].id = id; | ||
50 | return -i; | ||
51 | } | ||
52 | #endif | ||
53 | |||
54 | typedef char* ug_func(char *name, long uid, int bufsize); | ||
55 | static char* get_cached(cache_t *cp, unsigned id, ug_func* fp) | ||
56 | { | ||
57 | int i; | ||
58 | for (i = 0; i < cp->size; i++) | ||
59 | if (cp->cache[i].id == id) | ||
60 | return cp->cache[i].name; | ||
61 | i = cp->size++; | ||
62 | cp->cache = xrealloc(cp->cache, cp->size * sizeof(*cp->cache)); | ||
63 | cp->cache[i].id = id; | ||
64 | fp(cp->cache[i].name, id, sizeof(cp->cache[i].name)); | ||
65 | return cp->cache[i].name; | ||
66 | } | ||
67 | const char* get_cached_username(uid_t uid) | ||
68 | { | ||
69 | return get_cached(&username, uid, bb_getpwuid); | ||
70 | } | ||
71 | const char* get_cached_groupname(gid_t gid) | ||
72 | { | ||
73 | return get_cached(&groupname, gid, bb_getgrgid); | ||
74 | } | ||
75 | |||
76 | |||
77 | #define PROCPS_BUFSIZE 1024 | ||
78 | |||
79 | static int read_to_buf(const char *filename, void *buf) | ||
80 | { | ||
81 | ssize_t ret; | ||
82 | ret = open_read_close(filename, buf, PROCPS_BUFSIZE-1); | ||
83 | ((char *)buf)[ret > 0 ? ret : 0] = '\0'; | ||
84 | return ret; | ||
85 | } | ||
86 | |||
87 | procps_status_t* alloc_procps_scan(int flags) | ||
88 | { | ||
89 | procps_status_t* sp = xzalloc(sizeof(procps_status_t)); | ||
90 | sp->dir = xopendir("/proc"); | ||
91 | return sp; | ||
92 | } | ||
93 | |||
94 | void free_procps_scan(procps_status_t* sp) | ||
95 | { | ||
96 | closedir(sp->dir); | ||
97 | free(sp->cmd); | ||
98 | free(sp); | ||
99 | } | ||
100 | |||
101 | void BUG_comm_size(void); | ||
102 | procps_status_t* procps_scan(procps_status_t* sp, int flags) | ||
103 | { | ||
104 | struct dirent *entry; | ||
105 | char buf[PROCPS_BUFSIZE]; | ||
106 | char filename[sizeof("/proc//cmdline") + sizeof(int)*3]; | ||
107 | char *filename_tail; | ||
108 | long tasknice; | ||
109 | unsigned pid; | ||
110 | int n; | ||
111 | struct stat sb; | ||
112 | |||
113 | if (!sp) | ||
114 | sp = alloc_procps_scan(flags); | ||
115 | |||
116 | for (;;) { | ||
117 | entry = readdir(sp->dir); | ||
118 | if (entry == NULL) { | ||
119 | free_procps_scan(sp); | ||
120 | return NULL; | ||
121 | } | ||
122 | pid = bb_strtou(entry->d_name, NULL, 10); | ||
123 | if (errno) | ||
124 | continue; | ||
125 | |||
126 | /* After this point we have to break, not continue | ||
127 | * ("continue" would mean that current /proc/NNN | ||
128 | * is not a valid process info) */ | ||
129 | |||
130 | memset(&sp->rss, 0, sizeof(*sp) - offsetof(procps_status_t, rss)); | ||
131 | |||
132 | sp->pid = pid; | ||
133 | if (!(flags & ~PSSCAN_PID)) break; | ||
134 | |||
135 | filename_tail = filename + sprintf(filename, "/proc/%d", pid); | ||
136 | |||
137 | if (flags & PSSCAN_UIDGID) { | ||
138 | if (stat(filename, &sb)) | ||
139 | break; | ||
140 | /* Need comment - is this effective or read UID/GID? */ | ||
141 | sp->uid = sb.st_uid; | ||
142 | sp->gid = sb.st_gid; | ||
143 | } | ||
144 | |||
145 | if (flags & PSSCAN_STAT) { | ||
146 | char *cp; | ||
147 | /* see proc(5) for some details on this */ | ||
148 | strcpy(filename_tail, "/stat"); | ||
149 | n = read_to_buf(filename, buf); | ||
150 | if (n < 0) | ||
151 | break; | ||
152 | cp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */ | ||
153 | if (!cp || cp[1] != ' ') | ||
154 | break; | ||
155 | cp[0] = '\0'; | ||
156 | if (sizeof(sp->comm) < 16) | ||
157 | BUG_comm_size(); | ||
158 | sscanf(buf, "%*s (%15c", sp->comm); | ||
159 | n = sscanf(cp+2, | ||
160 | "%c %u " /* state, ppid */ | ||
161 | "%u %u %*s %*s " /* pgid, sid, tty, tpgid */ | ||
162 | "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */ | ||
163 | "%lu %lu " /* utime, stime */ | ||
164 | "%*s %*s %*s " /* cutime, cstime, priority */ | ||
165 | "%ld " /* nice */ | ||
166 | "%*s %*s %*s " /* timeout, it_real_value, start_time */ | ||
167 | "%*s " /* vsize */ | ||
168 | "%lu", /* rss */ | ||
169 | sp->state, &sp->ppid, | ||
170 | &sp->pgid, &sp->sid, | ||
171 | &sp->utime, &sp->stime, | ||
172 | &tasknice, | ||
173 | &sp->rss); | ||
174 | if (n != 8) | ||
175 | break; | ||
176 | |||
177 | if (sp->rss == 0 && sp->state[0] != 'Z') | ||
178 | sp->state[1] = 'W'; | ||
179 | else | ||
180 | sp->state[1] = ' '; | ||
181 | if (tasknice < 0) | ||
182 | sp->state[2] = '<'; | ||
183 | else if (tasknice > 0) | ||
184 | sp->state[2] = 'N'; | ||
185 | else | ||
186 | sp->state[2] = ' '; | ||
187 | |||
188 | #ifdef PAGE_SHIFT | ||
189 | sp->rss <<= (PAGE_SHIFT - 10); /* 2**10 = 1kb */ | ||
190 | #else | ||
191 | sp->rss *= (getpagesize() >> 10); /* 2**10 = 1kb */ | ||
192 | #endif | ||
193 | } | ||
194 | |||
195 | if (flags & PSSCAN_CMD) { | ||
196 | free(sp->cmd); | ||
197 | sp->cmd = NULL; | ||
198 | strcpy(filename_tail, "/cmdline"); | ||
199 | n = read_to_buf(filename, buf); | ||
200 | if (n <= 0) | ||
201 | break; | ||
202 | if (buf[n-1] == '\n') { | ||
203 | if (!--n) | ||
204 | break; | ||
205 | buf[n] = '\0'; | ||
206 | } | ||
207 | do { | ||
208 | n--; | ||
209 | if ((unsigned char)(buf[n]) < ' ') | ||
210 | buf[n] = ' '; | ||
211 | } while (n); | ||
212 | sp->cmd = strdup(buf); | ||
213 | } | ||
214 | break; | ||
215 | } | ||
216 | return sp; | ||
217 | } | ||
218 | /* from kernel: | ||
219 | // pid comm S ppid pgid sid tty_nr tty_pgrp flg | ||
220 | sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ | ||
221 | %lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \ | ||
222 | %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu\n", | ||
223 | task->pid, | ||
224 | tcomm, | ||
225 | state, | ||
226 | ppid, | ||
227 | pgid, | ||
228 | sid, | ||
229 | tty_nr, | ||
230 | tty_pgrp, | ||
231 | task->flags, | ||
232 | min_flt, | ||
233 | |||
234 | cmin_flt, | ||
235 | maj_flt, | ||
236 | cmaj_flt, | ||
237 | cputime_to_clock_t(utime), | ||
238 | cputime_to_clock_t(stime), | ||
239 | cputime_to_clock_t(cutime), | ||
240 | cputime_to_clock_t(cstime), | ||
241 | priority, | ||
242 | nice, | ||
243 | num_threads, | ||
244 | // 0, | ||
245 | start_time, | ||
246 | vsize, | ||
247 | mm ? get_mm_rss(mm) : 0, | ||
248 | rsslim, | ||
249 | mm ? mm->start_code : 0, | ||
250 | mm ? mm->end_code : 0, | ||
251 | mm ? mm->start_stack : 0, | ||
252 | esp, | ||
253 | eip, | ||
254 | the rest is some obsolete cruft | ||
255 | */ | ||
diff --git a/libbb/pw_encrypt.c b/libbb/pw_encrypt.c new file mode 100644 index 000000000..d546bc883 --- /dev/null +++ b/libbb/pw_encrypt.c | |||
@@ -0,0 +1,29 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routine. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | #include <crypt.h> | ||
12 | |||
13 | char *pw_encrypt(const char *clear, const char *salt) | ||
14 | { | ||
15 | static char cipher[128]; | ||
16 | char *cp; | ||
17 | |||
18 | #if 0 /* was CONFIG_FEATURE_SHA1_PASSWORDS, but there is no such thing??? */ | ||
19 | if (strncmp(salt, "$2$", 3) == 0) { | ||
20 | return sha1_crypt(clear); | ||
21 | } | ||
22 | #endif | ||
23 | cp = (char *) crypt(clear, salt); | ||
24 | /* if crypt (a nonstandard crypt) returns a string too large, | ||
25 | truncate it so we don't overrun buffers and hope there is | ||
26 | enough security in what's left */ | ||
27 | safe_strncpy(cipher, cp, sizeof(cipher)); | ||
28 | return cipher; | ||
29 | } | ||
diff --git a/libbb/read.c b/libbb/read.c new file mode 100644 index 000000000..b3648b4d7 --- /dev/null +++ b/libbb/read.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | ssize_t safe_read(int fd, void *buf, size_t count) | ||
13 | { | ||
14 | ssize_t n; | ||
15 | |||
16 | do { | ||
17 | n = read(fd, buf, count); | ||
18 | } while (n < 0 && errno == EINTR); | ||
19 | |||
20 | return n; | ||
21 | } | ||
22 | |||
23 | /* | ||
24 | * Read all of the supplied buffer from a file. | ||
25 | * This does multiple reads as necessary. | ||
26 | * Returns the amount read, or -1 on an error. | ||
27 | * A short read is returned on an end of file. | ||
28 | */ | ||
29 | ssize_t full_read(int fd, void *buf, size_t len) | ||
30 | { | ||
31 | ssize_t cc; | ||
32 | ssize_t total; | ||
33 | |||
34 | total = 0; | ||
35 | |||
36 | while (len) { | ||
37 | cc = safe_read(fd, buf, len); | ||
38 | |||
39 | if (cc < 0) | ||
40 | return cc; /* read() returns -1 on failure. */ | ||
41 | |||
42 | if (cc == 0) | ||
43 | break; | ||
44 | |||
45 | buf = ((char *)buf) + cc; | ||
46 | total += cc; | ||
47 | len -= cc; | ||
48 | } | ||
49 | |||
50 | return total; | ||
51 | } | ||
52 | |||
53 | // Die with an error message if we can't read the entire buffer. | ||
54 | void xread(int fd, void *buf, size_t count) | ||
55 | { | ||
56 | if (count) { | ||
57 | ssize_t size = full_read(fd, buf, count); | ||
58 | if (size != count) | ||
59 | bb_error_msg_and_die("short read"); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | // Die with an error message if we can't read one character. | ||
64 | unsigned char xread_char(int fd) | ||
65 | { | ||
66 | char tmp; | ||
67 | |||
68 | xread(fd, &tmp, 1); | ||
69 | |||
70 | return tmp; | ||
71 | } | ||
72 | |||
73 | // Read one line a-la fgets. Works only on seekable streams | ||
74 | char *reads(int fd, char *buffer, size_t size) | ||
75 | { | ||
76 | char *p; | ||
77 | |||
78 | if (size < 2) | ||
79 | return NULL; | ||
80 | size = full_read(fd, buffer, size-1); | ||
81 | if ((ssize_t)size <= 0) | ||
82 | return NULL; | ||
83 | |||
84 | buffer[size] = '\0'; | ||
85 | p = strchr(buffer, '\n'); | ||
86 | if (p) { | ||
87 | off_t offset; | ||
88 | *p++ = '\0'; | ||
89 | // avoid incorrect (unsigned) widening | ||
90 | offset = (off_t)(p-buffer) - (off_t)size; | ||
91 | // set fd position the right after the \n | ||
92 | if (offset && lseek(fd, offset, SEEK_CUR) == (off_t)-1) | ||
93 | return NULL; | ||
94 | } | ||
95 | return buffer; | ||
96 | } | ||
97 | |||
98 | ssize_t read_close(int fd, void *buf, size_t size) | ||
99 | { | ||
100 | int e; | ||
101 | size = full_read(fd, buf, size); | ||
102 | e = errno; | ||
103 | close(fd); | ||
104 | errno = e; | ||
105 | return size; | ||
106 | } | ||
107 | |||
108 | ssize_t open_read_close(const char *filename, void *buf, size_t size) | ||
109 | { | ||
110 | int fd = open(filename, O_RDONLY); | ||
111 | if (fd < 0) | ||
112 | return fd; | ||
113 | return read_close(fd, buf, size); | ||
114 | } | ||
115 | |||
116 | void *xmalloc_open_read_close(const char *filename, size_t *sizep) | ||
117 | { | ||
118 | char *buf; | ||
119 | size_t size = sizep ? *sizep : INT_MAX; | ||
120 | int fd = xopen(filename, O_RDONLY); | ||
121 | off_t len = xlseek(fd, 0, SEEK_END); | ||
122 | xlseek(fd, 0, SEEK_SET); | ||
123 | |||
124 | if (len > size) | ||
125 | bb_error_msg_and_die("file '%s' is too big", filename); | ||
126 | size = len; | ||
127 | buf = xmalloc(size+1); | ||
128 | size = read_close(fd, buf, size); | ||
129 | if ((ssize_t)size < 0) | ||
130 | bb_perror_msg_and_die("'%s'", filename); | ||
131 | buf[size] = '\0'; | ||
132 | if (sizep) *sizep = size; | ||
133 | return buf; | ||
134 | } | ||
diff --git a/libbb/recursive_action.c b/libbb/recursive_action.c new file mode 100644 index 000000000..121a3dffd --- /dev/null +++ b/libbb/recursive_action.c | |||
@@ -0,0 +1,126 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | #undef DEBUG_RECURS_ACTION | ||
13 | |||
14 | /* | ||
15 | * Walk down all the directories under the specified | ||
16 | * location, and do something (something specified | ||
17 | * by the fileAction and dirAction function pointers). | ||
18 | * | ||
19 | * Unfortunately, while nftw(3) could replace this and reduce | ||
20 | * code size a bit, nftw() wasn't supported before GNU libc 2.1, | ||
21 | * and so isn't sufficiently portable to take over since glibc2.1 | ||
22 | * is so stinking huge. | ||
23 | */ | ||
24 | |||
25 | static int true_action(const char *fileName, struct stat *statbuf, void* userData, int depth) | ||
26 | { | ||
27 | return TRUE; | ||
28 | } | ||
29 | |||
30 | /* fileAction return value of 0 on any file in directory will make | ||
31 | * recursive_action() return 0, but it doesn't stop directory traversal | ||
32 | * (fileAction/dirAction will be called on each file). | ||
33 | * | ||
34 | * if !depthFirst, dirAction return value of 0 (FALSE) or 2 (SKIP) | ||
35 | * prevents recursion into that directory, instead | ||
36 | * recursive_action() returns 0 (if FALSE) or 1 (if SKIP). | ||
37 | * | ||
38 | * followLinks=0/1 differs mainly in handling of links to dirs. | ||
39 | * 0: lstat(statbuf). Calls fileAction on link name even if points to dir. | ||
40 | * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir. | ||
41 | */ | ||
42 | |||
43 | int recursive_action(const char *fileName, | ||
44 | int recurse, int followLinks, int depthFirst, | ||
45 | int (*fileAction)(const char *fileName, struct stat *statbuf, void* userData, int depth), | ||
46 | int (*dirAction)(const char *fileName, struct stat *statbuf, void* userData, int depth), | ||
47 | void* userData, | ||
48 | int depth) | ||
49 | { | ||
50 | struct stat statbuf; | ||
51 | int status; | ||
52 | DIR *dir; | ||
53 | struct dirent *next; | ||
54 | |||
55 | if (!fileAction) fileAction = true_action; | ||
56 | if (!dirAction) dirAction = true_action; | ||
57 | |||
58 | status = (followLinks ? stat : lstat)(fileName, &statbuf); | ||
59 | |||
60 | if (status < 0) { | ||
61 | #ifdef DEBUG_RECURS_ACTION | ||
62 | bb_error_msg("status=%d followLinks=%d TRUE=%d", | ||
63 | status, followLinks, TRUE); | ||
64 | #endif | ||
65 | bb_perror_msg("%s", fileName); | ||
66 | return FALSE; | ||
67 | } | ||
68 | |||
69 | /* If S_ISLNK(m), then we know that !S_ISDIR(m). | ||
70 | * Then we can skip checking first part: if it is true, then | ||
71 | * (!dir) is also true! */ | ||
72 | if ( /* (!followLinks && S_ISLNK(statbuf.st_mode)) || */ | ||
73 | !S_ISDIR(statbuf.st_mode) | ||
74 | ) { | ||
75 | return fileAction(fileName, &statbuf, userData, depth); | ||
76 | } | ||
77 | |||
78 | /* It's a directory (or a link to one, and followLinks is set) */ | ||
79 | |||
80 | if (!recurse) { | ||
81 | return dirAction(fileName, &statbuf, userData, depth); | ||
82 | } | ||
83 | |||
84 | if (!depthFirst) { | ||
85 | status = dirAction(fileName, &statbuf, userData, depth); | ||
86 | if (!status) { | ||
87 | bb_perror_msg("%s", fileName); | ||
88 | return FALSE; | ||
89 | } | ||
90 | if (status == SKIP) | ||
91 | return TRUE; | ||
92 | } | ||
93 | |||
94 | dir = opendir(fileName); | ||
95 | if (!dir) { | ||
96 | /* findutils-4.1.20 reports this */ | ||
97 | /* (i.e. it doesn't silently return with exit code 1) */ | ||
98 | /* To trigger: "find -exec rm -rf {} \;" */ | ||
99 | bb_perror_msg("%s", fileName); | ||
100 | return FALSE; | ||
101 | } | ||
102 | status = TRUE; | ||
103 | while ((next = readdir(dir)) != NULL) { | ||
104 | char *nextFile; | ||
105 | |||
106 | nextFile = concat_subpath_file(fileName, next->d_name); | ||
107 | if (nextFile == NULL) | ||
108 | continue; | ||
109 | if (!recursive_action(nextFile, TRUE, followLinks, depthFirst, | ||
110 | fileAction, dirAction, userData, depth+1)) { | ||
111 | status = FALSE; | ||
112 | } | ||
113 | free(nextFile); | ||
114 | } | ||
115 | closedir(dir); | ||
116 | if (depthFirst) { | ||
117 | if (!dirAction(fileName, &statbuf, userData, depth)) { | ||
118 | bb_perror_msg("%s", fileName); | ||
119 | return FALSE; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | if (!status) | ||
124 | return FALSE; | ||
125 | return TRUE; | ||
126 | } | ||
diff --git a/libbb/remove_file.c b/libbb/remove_file.c new file mode 100644 index 000000000..ab159a481 --- /dev/null +++ b/libbb/remove_file.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini remove_file implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdio.h> | ||
11 | #include <time.h> | ||
12 | #include <utime.h> | ||
13 | #include <dirent.h> | ||
14 | #include <errno.h> | ||
15 | #include <unistd.h> | ||
16 | #include <stdlib.h> | ||
17 | #include <string.h> | ||
18 | #include <getopt.h> | ||
19 | #include "libbb.h" | ||
20 | |||
21 | int remove_file(const char *path, int flags) | ||
22 | { | ||
23 | struct stat path_stat; | ||
24 | int path_exists = 1; | ||
25 | |||
26 | if (lstat(path, &path_stat) < 0) { | ||
27 | if (errno != ENOENT) { | ||
28 | bb_perror_msg("cannot stat '%s'", path); | ||
29 | return -1; | ||
30 | } | ||
31 | |||
32 | path_exists = 0; | ||
33 | } | ||
34 | |||
35 | if (!path_exists) { | ||
36 | if (!(flags & FILEUTILS_FORCE)) { | ||
37 | bb_perror_msg("cannot remove '%s'", path); | ||
38 | return -1; | ||
39 | } | ||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | if (S_ISDIR(path_stat.st_mode)) { | ||
44 | DIR *dp; | ||
45 | struct dirent *d; | ||
46 | int status = 0; | ||
47 | |||
48 | if (!(flags & FILEUTILS_RECUR)) { | ||
49 | bb_error_msg("%s: is a directory", path); | ||
50 | return -1; | ||
51 | } | ||
52 | |||
53 | if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && | ||
54 | isatty(0)) || | ||
55 | (flags & FILEUTILS_INTERACTIVE)) { | ||
56 | fprintf(stderr, "%s: descend into directory '%s'? ", applet_name, | ||
57 | path); | ||
58 | if (!bb_ask_confirmation()) | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | if ((dp = opendir(path)) == NULL) { | ||
63 | return -1; | ||
64 | } | ||
65 | |||
66 | while ((d = readdir(dp)) != NULL) { | ||
67 | char *new_path; | ||
68 | |||
69 | new_path = concat_subpath_file(path, d->d_name); | ||
70 | if(new_path == NULL) | ||
71 | continue; | ||
72 | if (remove_file(new_path, flags) < 0) | ||
73 | status = -1; | ||
74 | free(new_path); | ||
75 | } | ||
76 | |||
77 | if (closedir(dp) < 0) { | ||
78 | bb_perror_msg("cannot close '%s'", path); | ||
79 | return -1; | ||
80 | } | ||
81 | |||
82 | if (flags & FILEUTILS_INTERACTIVE) { | ||
83 | fprintf(stderr, "%s: remove directory '%s'? ", applet_name, path); | ||
84 | if (!bb_ask_confirmation()) | ||
85 | return status; | ||
86 | } | ||
87 | |||
88 | if (rmdir(path) < 0) { | ||
89 | bb_perror_msg("cannot remove '%s'", path); | ||
90 | return -1; | ||
91 | } | ||
92 | |||
93 | return status; | ||
94 | } else { | ||
95 | if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && | ||
96 | !S_ISLNK(path_stat.st_mode) && | ||
97 | isatty(0)) || | ||
98 | (flags & FILEUTILS_INTERACTIVE)) { | ||
99 | fprintf(stderr, "%s: remove '%s'? ", applet_name, path); | ||
100 | if (!bb_ask_confirmation()) | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | if (unlink(path) < 0) { | ||
105 | bb_perror_msg("cannot remove '%s'", path); | ||
106 | return -1; | ||
107 | } | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | } | ||
diff --git a/libbb/restricted_shell.c b/libbb/restricted_shell.c new file mode 100644 index 000000000..74a64140f --- /dev/null +++ b/libbb/restricted_shell.c | |||
@@ -0,0 +1,57 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com> | ||
4 | * All rights reserved. | ||
5 | * | ||
6 | * Redistribution and use in source and binary forms, with or without | ||
7 | * modification, are permitted provided that the following conditions | ||
8 | * are met: | ||
9 | * 1. Redistributions of source code must retain the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer. | ||
11 | * 2. Redistributions in binary form must reproduce the above copyright | ||
12 | * notice, this list of conditions and the following disclaimer in the | ||
13 | * documentation and/or other materials provided with the distribution. | ||
14 | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors | ||
15 | * may be used to endorse or promote products derived from this software | ||
16 | * without specific prior written permission. | ||
17 | * | ||
18 | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND | ||
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
21 | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE | ||
22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
28 | * SUCH DAMAGE. | ||
29 | */ | ||
30 | |||
31 | #include <stdio.h> | ||
32 | #include <errno.h> | ||
33 | #include <unistd.h> | ||
34 | #include <string.h> | ||
35 | #include <stdlib.h> | ||
36 | #include <syslog.h> | ||
37 | #include <ctype.h> | ||
38 | #include "libbb.h" | ||
39 | |||
40 | |||
41 | |||
42 | /* Return 1 if SHELL is a restricted shell (one not returned by | ||
43 | getusershell), else 0, meaning it is a standard shell. */ | ||
44 | |||
45 | int restricted_shell ( const char *shell ) | ||
46 | { | ||
47 | char *line; | ||
48 | |||
49 | setusershell ( ); | ||
50 | while (( line = getusershell ( ))) { | ||
51 | if (( *line != '#' ) && ( strcmp ( line, shell ) == 0 )) | ||
52 | break; | ||
53 | } | ||
54 | endusershell ( ); | ||
55 | return line ? 0 : 1; | ||
56 | } | ||
57 | |||
diff --git a/libbb/run_shell.c b/libbb/run_shell.c new file mode 100644 index 000000000..6be09088d --- /dev/null +++ b/libbb/run_shell.c | |||
@@ -0,0 +1,101 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com> | ||
4 | * All rights reserved. | ||
5 | * | ||
6 | * Redistribution and use in source and binary forms, with or without | ||
7 | * modification, are permitted provided that the following conditions | ||
8 | * are met: | ||
9 | * 1. Redistributions of source code must retain the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer. | ||
11 | * 2. Redistributions in binary form must reproduce the above copyright | ||
12 | * notice, this list of conditions and the following disclaimer in the | ||
13 | * documentation and/or other materials provided with the distribution. | ||
14 | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors | ||
15 | * may be used to endorse or promote products derived from this software | ||
16 | * without specific prior written permission. | ||
17 | * | ||
18 | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND | ||
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
21 | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE | ||
22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
28 | * SUCH DAMAGE. | ||
29 | */ | ||
30 | |||
31 | #include <stdio.h> | ||
32 | #include <errno.h> | ||
33 | #include <unistd.h> | ||
34 | #include <string.h> | ||
35 | #include <stdlib.h> | ||
36 | #include <syslog.h> | ||
37 | #include <ctype.h> | ||
38 | #include "libbb.h" | ||
39 | #ifdef CONFIG_SELINUX | ||
40 | #include <selinux/selinux.h> /* for setexeccon */ | ||
41 | #endif | ||
42 | |||
43 | #ifdef CONFIG_SELINUX | ||
44 | static security_context_t current_sid; | ||
45 | |||
46 | void | ||
47 | renew_current_security_context(void) | ||
48 | { | ||
49 | if (current_sid) | ||
50 | freecon(current_sid); /* Release old context */ | ||
51 | getcon(¤t_sid); /* update */ | ||
52 | } | ||
53 | void | ||
54 | set_current_security_context(security_context_t sid) | ||
55 | { | ||
56 | if (current_sid) | ||
57 | freecon(current_sid); /* Release old context */ | ||
58 | current_sid = sid; | ||
59 | } | ||
60 | |||
61 | #endif | ||
62 | |||
63 | /* Run SHELL, or DEFAULT_SHELL if SHELL is empty. | ||
64 | If COMMAND is nonzero, pass it to the shell with the -c option. | ||
65 | If ADDITIONAL_ARGS is nonzero, pass it to the shell as more | ||
66 | arguments. */ | ||
67 | |||
68 | void run_shell(const char *shell, int loginshell, const char *command, const char **additional_args) | ||
69 | { | ||
70 | const char **args; | ||
71 | int argno = 1; | ||
72 | int additional_args_cnt = 0; | ||
73 | |||
74 | for (args = additional_args; args && *args; args++) | ||
75 | additional_args_cnt++; | ||
76 | |||
77 | args = xmalloc(sizeof(char*) * (4 + additional_args_cnt)); | ||
78 | |||
79 | args[0] = bb_get_last_path_component(xstrdup(shell)); | ||
80 | |||
81 | if (loginshell) | ||
82 | args[0] = xasprintf("-%s", args[0]); | ||
83 | |||
84 | if (command) { | ||
85 | args[argno++] = "-c"; | ||
86 | args[argno++] = command; | ||
87 | } | ||
88 | if (additional_args) { | ||
89 | for (; *additional_args; ++additional_args) | ||
90 | args[argno++] = *additional_args; | ||
91 | } | ||
92 | args[argno] = NULL; | ||
93 | #ifdef CONFIG_SELINUX | ||
94 | if (current_sid && !setexeccon(current_sid)) { | ||
95 | freecon(current_sid); | ||
96 | execve(shell, (char **) args, environ); | ||
97 | } else | ||
98 | #endif | ||
99 | execv(shell, (char **) args); | ||
100 | bb_perror_msg_and_die("cannot run %s", shell); | ||
101 | } | ||
diff --git a/libbb/safe_strncpy.c b/libbb/safe_strncpy.c new file mode 100644 index 000000000..42bc16ea0 --- /dev/null +++ b/libbb/safe_strncpy.c | |||
@@ -0,0 +1,21 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <string.h> | ||
11 | #include "libbb.h" | ||
12 | |||
13 | |||
14 | |||
15 | /* Like strncpy but make sure the resulting string is always 0 terminated. */ | ||
16 | char * safe_strncpy(char *dst, const char *src, size_t size) | ||
17 | { | ||
18 | if (!size) return dst; | ||
19 | dst[--size] = '\0'; | ||
20 | return strncpy(dst, src, size); | ||
21 | } | ||
diff --git a/libbb/safe_write.c b/libbb/safe_write.c new file mode 100644 index 000000000..c81f1247f --- /dev/null +++ b/libbb/safe_write.c | |||
@@ -0,0 +1,26 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdio.h> | ||
11 | #include <errno.h> | ||
12 | #include <unistd.h> | ||
13 | #include "libbb.h" | ||
14 | |||
15 | |||
16 | |||
17 | ssize_t safe_write(int fd, const void *buf, size_t count) | ||
18 | { | ||
19 | ssize_t n; | ||
20 | |||
21 | do { | ||
22 | n = write(fd, buf, count); | ||
23 | } while (n < 0 && errno == EINTR); | ||
24 | |||
25 | return n; | ||
26 | } | ||
diff --git a/libbb/setup_environment.c b/libbb/setup_environment.c new file mode 100644 index 000000000..874a58efa --- /dev/null +++ b/libbb/setup_environment.c | |||
@@ -0,0 +1,83 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com> | ||
4 | * All rights reserved. | ||
5 | * | ||
6 | * Redistribution and use in source and binary forms, with or without | ||
7 | * modification, are permitted provided that the following conditions | ||
8 | * are met: | ||
9 | * 1. Redistributions of source code must retain the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer. | ||
11 | * 2. Redistributions in binary form must reproduce the above copyright | ||
12 | * notice, this list of conditions and the following disclaimer in the | ||
13 | * documentation and/or other materials provided with the distribution. | ||
14 | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors | ||
15 | * may be used to endorse or promote products derived from this software | ||
16 | * without specific prior written permission. | ||
17 | * | ||
18 | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND | ||
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
21 | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE | ||
22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
28 | * SUCH DAMAGE. | ||
29 | */ | ||
30 | |||
31 | #include <stdio.h> | ||
32 | #include <errno.h> | ||
33 | #include <unistd.h> | ||
34 | #include <string.h> | ||
35 | #include <stdlib.h> | ||
36 | #include <syslog.h> | ||
37 | #include <ctype.h> | ||
38 | #include "libbb.h" | ||
39 | |||
40 | |||
41 | |||
42 | #define DEFAULT_LOGIN_PATH "/bin:/usr/bin" | ||
43 | #define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin" | ||
44 | |||
45 | void setup_environment(const char *shell, int loginshell, int changeenv, const struct passwd *pw) | ||
46 | { | ||
47 | if (loginshell) { | ||
48 | const char *term; | ||
49 | |||
50 | /* Change the current working directory to be the home directory | ||
51 | * of the user. It is a fatal error for this process to be unable | ||
52 | * to change to that directory. There is no "default" home | ||
53 | * directory. | ||
54 | * Some systems default to HOME=/ | ||
55 | */ | ||
56 | if (chdir(pw->pw_dir)) { | ||
57 | xchdir("/"); | ||
58 | fputs("warning: cannot change to home directory\n", stderr); | ||
59 | } | ||
60 | |||
61 | /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. | ||
62 | Unset all other environment variables. */ | ||
63 | term = getenv("TERM"); | ||
64 | clearenv(); | ||
65 | if (term) | ||
66 | xsetenv("TERM", term); | ||
67 | xsetenv("HOME", pw->pw_dir); | ||
68 | xsetenv("SHELL", shell); | ||
69 | xsetenv("USER", pw->pw_name); | ||
70 | xsetenv("LOGNAME", pw->pw_name); | ||
71 | xsetenv("PATH", (pw->pw_uid ? DEFAULT_LOGIN_PATH : DEFAULT_ROOT_LOGIN_PATH)); | ||
72 | } | ||
73 | else if (changeenv) { | ||
74 | /* Set HOME, SHELL, and if not becoming a super-user, | ||
75 | USER and LOGNAME. */ | ||
76 | xsetenv("HOME", pw->pw_dir); | ||
77 | xsetenv("SHELL", shell); | ||
78 | if (pw->pw_uid) { | ||
79 | xsetenv("USER", pw->pw_name); | ||
80 | xsetenv("LOGNAME", pw->pw_name); | ||
81 | } | ||
82 | } | ||
83 | } | ||
diff --git a/libbb/sha1.c b/libbb/sha1.c new file mode 100644 index 000000000..34813e24a --- /dev/null +++ b/libbb/sha1.c | |||
@@ -0,0 +1,178 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Based on shasum from http://www.netsw.org/crypto/hash/ | ||
4 | * Majorly hacked up to use Dr Brian Gladman's sha1 code | ||
5 | * | ||
6 | * Copyright (C) 2002 Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK. | ||
7 | * Copyright (C) 2003 Glenn L. McGrath | ||
8 | * Copyright (C) 2003 Erik Andersen | ||
9 | * | ||
10 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
11 | * | ||
12 | * --------------------------------------------------------------------------- | ||
13 | * Issue Date: 10/11/2002 | ||
14 | * | ||
15 | * This is a byte oriented version of SHA1 that operates on arrays of bytes | ||
16 | * stored in memory. It runs at 22 cycles per byte on a Pentium P4 processor | ||
17 | */ | ||
18 | |||
19 | #include <fcntl.h> | ||
20 | #include <limits.h> | ||
21 | #include <stdio.h> | ||
22 | #include <stdint.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <string.h> | ||
25 | #include <unistd.h> | ||
26 | |||
27 | #include "libbb.h" | ||
28 | |||
29 | #define SHA1_BLOCK_SIZE 64 | ||
30 | #define SHA1_DIGEST_SIZE 20 | ||
31 | #define SHA1_HASH_SIZE SHA1_DIGEST_SIZE | ||
32 | #define SHA2_GOOD 0 | ||
33 | #define SHA2_BAD 1 | ||
34 | |||
35 | #define rotl32(x,n) (((x) << n) | ((x) >> (32 - n))) | ||
36 | |||
37 | #define SHA1_MASK (SHA1_BLOCK_SIZE - 1) | ||
38 | |||
39 | /* reverse byte order in 32-bit words */ | ||
40 | #define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) | ||
41 | #define parity(x,y,z) ((x) ^ (y) ^ (z)) | ||
42 | #define maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y)))) | ||
43 | |||
44 | /* A normal version as set out in the FIPS. This version uses */ | ||
45 | /* partial loop unrolling and is optimised for the Pentium 4 */ | ||
46 | #define rnd(f,k) \ | ||
47 | do { \ | ||
48 | t = a; a = rotl32(a,5) + f(b,c,d) + e + k + w[i]; \ | ||
49 | e = d; d = c; c = rotl32(b, 30); b = t; \ | ||
50 | } while(0) | ||
51 | |||
52 | static void sha1_compile(sha1_ctx_t *ctx) | ||
53 | { | ||
54 | uint32_t w[80], i, a, b, c, d, e, t; | ||
55 | |||
56 | /* note that words are compiled from the buffer into 32-bit */ | ||
57 | /* words in big-endian order so an order reversal is needed */ | ||
58 | /* here on little endian machines */ | ||
59 | for (i = 0; i < SHA1_BLOCK_SIZE / 4; ++i) | ||
60 | w[i] = htonl(ctx->wbuf[i]); | ||
61 | |||
62 | for (i = SHA1_BLOCK_SIZE / 4; i < 80; ++i) | ||
63 | w[i] = rotl32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); | ||
64 | |||
65 | a = ctx->hash[0]; | ||
66 | b = ctx->hash[1]; | ||
67 | c = ctx->hash[2]; | ||
68 | d = ctx->hash[3]; | ||
69 | e = ctx->hash[4]; | ||
70 | |||
71 | for (i = 0; i < 20; ++i) { | ||
72 | rnd(ch, 0x5a827999); | ||
73 | } | ||
74 | |||
75 | for (i = 20; i < 40; ++i) { | ||
76 | rnd(parity, 0x6ed9eba1); | ||
77 | } | ||
78 | |||
79 | for (i = 40; i < 60; ++i) { | ||
80 | rnd(maj, 0x8f1bbcdc); | ||
81 | } | ||
82 | |||
83 | for (i = 60; i < 80; ++i) { | ||
84 | rnd(parity, 0xca62c1d6); | ||
85 | } | ||
86 | |||
87 | ctx->hash[0] += a; | ||
88 | ctx->hash[1] += b; | ||
89 | ctx->hash[2] += c; | ||
90 | ctx->hash[3] += d; | ||
91 | ctx->hash[4] += e; | ||
92 | } | ||
93 | |||
94 | void sha1_begin(sha1_ctx_t *ctx) | ||
95 | { | ||
96 | ctx->count[0] = ctx->count[1] = 0; | ||
97 | ctx->hash[0] = 0x67452301; | ||
98 | ctx->hash[1] = 0xefcdab89; | ||
99 | ctx->hash[2] = 0x98badcfe; | ||
100 | ctx->hash[3] = 0x10325476; | ||
101 | ctx->hash[4] = 0xc3d2e1f0; | ||
102 | } | ||
103 | |||
104 | /* SHA1 hash data in an array of bytes into hash buffer and call the */ | ||
105 | /* hash_compile function as required. */ | ||
106 | void sha1_hash(const void *data, size_t length, sha1_ctx_t *ctx) | ||
107 | { | ||
108 | uint32_t pos = (uint32_t) (ctx->count[0] & SHA1_MASK); | ||
109 | uint32_t freeb = SHA1_BLOCK_SIZE - pos; | ||
110 | const unsigned char *sp = data; | ||
111 | |||
112 | if ((ctx->count[0] += length) < length) | ||
113 | ++(ctx->count[1]); | ||
114 | |||
115 | while (length >= freeb) { /* tranfer whole blocks while possible */ | ||
116 | memcpy(((unsigned char *) ctx->wbuf) + pos, sp, freeb); | ||
117 | sp += freeb; | ||
118 | length -= freeb; | ||
119 | freeb = SHA1_BLOCK_SIZE; | ||
120 | pos = 0; | ||
121 | sha1_compile(ctx); | ||
122 | } | ||
123 | |||
124 | memcpy(((unsigned char *) ctx->wbuf) + pos, sp, length); | ||
125 | } | ||
126 | |||
127 | void *sha1_end(void *resbuf, sha1_ctx_t *ctx) | ||
128 | { | ||
129 | /* SHA1 Final padding and digest calculation */ | ||
130 | #if BB_BIG_ENDIAN | ||
131 | static uint32_t mask[4] = { 0x00000000, 0xff000000, 0xffff0000, 0xffffff00 }; | ||
132 | static uint32_t bits[4] = { 0x80000000, 0x00800000, 0x00008000, 0x00000080 }; | ||
133 | #else | ||
134 | static uint32_t mask[4] = { 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff }; | ||
135 | static uint32_t bits[4] = { 0x00000080, 0x00008000, 0x00800000, 0x80000000 }; | ||
136 | #endif | ||
137 | |||
138 | uint8_t *hval = resbuf; | ||
139 | uint32_t i, cnt = (uint32_t) (ctx->count[0] & SHA1_MASK); | ||
140 | |||
141 | /* mask out the rest of any partial 32-bit word and then set */ | ||
142 | /* the next byte to 0x80. On big-endian machines any bytes in */ | ||
143 | /* the buffer will be at the top end of 32 bit words, on little */ | ||
144 | /* endian machines they will be at the bottom. Hence the AND */ | ||
145 | /* and OR masks above are reversed for little endian systems */ | ||
146 | ctx->wbuf[cnt >> 2] = | ||
147 | (ctx->wbuf[cnt >> 2] & mask[cnt & 3]) | bits[cnt & 3]; | ||
148 | |||
149 | /* we need 9 or more empty positions, one for the padding byte */ | ||
150 | /* (above) and eight for the length count. If there is not */ | ||
151 | /* enough space pad and empty the buffer */ | ||
152 | if (cnt > SHA1_BLOCK_SIZE - 9) { | ||
153 | if (cnt < 60) | ||
154 | ctx->wbuf[15] = 0; | ||
155 | sha1_compile(ctx); | ||
156 | cnt = 0; | ||
157 | } else /* compute a word index for the empty buffer positions */ | ||
158 | cnt = (cnt >> 2) + 1; | ||
159 | |||
160 | while (cnt < 14) /* and zero pad all but last two positions */ | ||
161 | ctx->wbuf[cnt++] = 0; | ||
162 | |||
163 | /* assemble the eight byte counter in the buffer in big-endian */ | ||
164 | /* format */ | ||
165 | |||
166 | ctx->wbuf[14] = htonl((ctx->count[1] << 3) | (ctx->count[0] >> 29)); | ||
167 | ctx->wbuf[15] = htonl(ctx->count[0] << 3); | ||
168 | |||
169 | sha1_compile(ctx); | ||
170 | |||
171 | /* extract the hash value as bytes in case the hash buffer is */ | ||
172 | /* misaligned for 32-bit words */ | ||
173 | |||
174 | for (i = 0; i < SHA1_DIGEST_SIZE; ++i) | ||
175 | hval[i] = (unsigned char) (ctx->hash[i >> 2] >> 8 * (~i & 3)); | ||
176 | |||
177 | return resbuf; | ||
178 | } | ||
diff --git a/libbb/simplify_path.c b/libbb/simplify_path.c new file mode 100644 index 000000000..b714c66b6 --- /dev/null +++ b/libbb/simplify_path.c | |||
@@ -0,0 +1,50 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * bb_simplify_path implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2001 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | char *bb_simplify_path(const char *path) | ||
13 | { | ||
14 | char *s, *start, *p; | ||
15 | |||
16 | if (path[0] == '/') | ||
17 | start = xstrdup(path); | ||
18 | else { | ||
19 | s = xgetcwd(NULL); | ||
20 | start = concat_path_file(s, path); | ||
21 | free(s); | ||
22 | } | ||
23 | p = s = start; | ||
24 | |||
25 | do { | ||
26 | if (*p == '/') { | ||
27 | if (*s == '/') { /* skip duplicate (or initial) slash */ | ||
28 | continue; | ||
29 | } else if (*s == '.') { | ||
30 | if (s[1] == '/' || s[1] == 0) { /* remove extra '.' */ | ||
31 | continue; | ||
32 | } else if ((s[1] == '.') && (s[2] == '/' || s[2] == 0)) { | ||
33 | ++s; | ||
34 | if (p > start) { | ||
35 | while (*--p != '/'); /* omit previous dir */ | ||
36 | } | ||
37 | continue; | ||
38 | } | ||
39 | } | ||
40 | } | ||
41 | *++p = *s; | ||
42 | } while (*++s); | ||
43 | |||
44 | if ((p == start) || (*p != '/')) { /* not a trailing slash */ | ||
45 | ++p; /* so keep last character */ | ||
46 | } | ||
47 | *p = 0; | ||
48 | |||
49 | return start; | ||
50 | } | ||
diff --git a/libbb/skip_whitespace.c b/libbb/skip_whitespace.c new file mode 100644 index 000000000..02c1f5828 --- /dev/null +++ b/libbb/skip_whitespace.c | |||
@@ -0,0 +1,18 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * skip_whitespace implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <ctype.h> | ||
11 | #include "libbb.h" | ||
12 | |||
13 | char *skip_whitespace(const char *s) | ||
14 | { | ||
15 | while (isspace(*s)) ++s; | ||
16 | |||
17 | return (char *) s; | ||
18 | } | ||
diff --git a/libbb/speed_table.c b/libbb/speed_table.c new file mode 100644 index 000000000..6137b7731 --- /dev/null +++ b/libbb/speed_table.c | |||
@@ -0,0 +1,117 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * compact speed_t <-> speed functions for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <termios.h> | ||
11 | #include "libbb.h" | ||
12 | |||
13 | struct speed_map { | ||
14 | unsigned short speed; | ||
15 | unsigned short value; | ||
16 | }; | ||
17 | |||
18 | static const struct speed_map speeds[] = { | ||
19 | {B0, 0}, | ||
20 | {B50, 50}, | ||
21 | {B75, 75}, | ||
22 | {B110, 110}, | ||
23 | {B134, 134}, | ||
24 | {B150, 150}, | ||
25 | {B200, 200}, | ||
26 | {B300, 300}, | ||
27 | {B600, 600}, | ||
28 | {B1200, 1200}, | ||
29 | {B1800, 1800}, | ||
30 | {B2400, 2400}, | ||
31 | {B4800, 4800}, | ||
32 | {B9600, 9600}, | ||
33 | #ifdef B19200 | ||
34 | {B19200, 19200}, | ||
35 | #elif defined(EXTA) | ||
36 | {EXTA, 19200}, | ||
37 | #endif | ||
38 | #ifdef B38400 | ||
39 | {B38400, 38400/256 + 0x8000U}, | ||
40 | #elif defined(EXTB) | ||
41 | {EXTB, 38400/256 + 0x8000U}, | ||
42 | #endif | ||
43 | #ifdef B57600 | ||
44 | {B57600, 57600/256 + 0x8000U}, | ||
45 | #endif | ||
46 | #ifdef B115200 | ||
47 | {B115200, 115200/256 + 0x8000U}, | ||
48 | #endif | ||
49 | #ifdef B230400 | ||
50 | {B230400, 230400/256 + 0x8000U}, | ||
51 | #endif | ||
52 | #ifdef B460800 | ||
53 | {B460800, 460800/256 + 0x8000U}, | ||
54 | #endif | ||
55 | }; | ||
56 | |||
57 | enum { NUM_SPEEDS = (sizeof(speeds) / sizeof(struct speed_map)) }; | ||
58 | |||
59 | unsigned int tty_baud_to_value(speed_t speed) | ||
60 | { | ||
61 | int i = 0; | ||
62 | |||
63 | do { | ||
64 | if (speed == speeds[i].speed) { | ||
65 | if (speeds[i].value & 0x8000U) { | ||
66 | return ((unsigned long) (speeds[i].value) & 0x7fffU) * 256; | ||
67 | } | ||
68 | return speeds[i].value; | ||
69 | } | ||
70 | } while (++i < NUM_SPEEDS); | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | speed_t tty_value_to_baud(unsigned int value) | ||
76 | { | ||
77 | int i = 0; | ||
78 | |||
79 | do { | ||
80 | if (value == tty_baud_to_value(speeds[i].speed)) { | ||
81 | return speeds[i].speed; | ||
82 | } | ||
83 | } while (++i < NUM_SPEEDS); | ||
84 | |||
85 | return (speed_t) - 1; | ||
86 | } | ||
87 | |||
88 | #if 0 | ||
89 | /* testing code */ | ||
90 | #include <stdio.h> | ||
91 | |||
92 | int main(void) | ||
93 | { | ||
94 | unsigned long v; | ||
95 | speed_t s; | ||
96 | |||
97 | for (v = 0 ; v < 500000 ; v++) { | ||
98 | s = tty_value_to_baud(v); | ||
99 | if (s == (speed_t) -1) { | ||
100 | continue; | ||
101 | } | ||
102 | printf("v = %lu -- s = %0lo\n", v, (unsigned long) s); | ||
103 | } | ||
104 | |||
105 | printf("-------------------------------\n"); | ||
106 | |||
107 | for (s = 0 ; s < 010017+1 ; s++) { | ||
108 | v = tty_baud_to_value(s); | ||
109 | if (!v) { | ||
110 | continue; | ||
111 | } | ||
112 | printf("v = %lu -- s = %0lo\n", v, (unsigned long) s); | ||
113 | } | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | #endif | ||
diff --git a/libbb/trim.c b/libbb/trim.c new file mode 100644 index 000000000..d36391540 --- /dev/null +++ b/libbb/trim.c | |||
@@ -0,0 +1,31 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) many different people. | ||
6 | * If you wrote this, please acknowledge your work. | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include <stdio.h> | ||
12 | #include <string.h> | ||
13 | #include <ctype.h> | ||
14 | #include "libbb.h" | ||
15 | |||
16 | |||
17 | void trim(char *s) | ||
18 | { | ||
19 | size_t len = strlen(s); | ||
20 | size_t lws; | ||
21 | |||
22 | /* trim trailing whitespace */ | ||
23 | while (len && isspace(s[len-1])) --len; | ||
24 | |||
25 | /* trim leading whitespace */ | ||
26 | if(len) { | ||
27 | lws = strspn(s, " \n\r\t\v"); | ||
28 | memmove(s, s + lws, len -= lws); | ||
29 | } | ||
30 | s[len] = 0; | ||
31 | } | ||
diff --git a/libbb/u_signal_names.c b/libbb/u_signal_names.c new file mode 100644 index 000000000..88311dd9c --- /dev/null +++ b/libbb/u_signal_names.c | |||
@@ -0,0 +1,58 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Signal name/number conversion routines. | ||
4 | * | ||
5 | * Copyright 2006 Rob Landley <rob@landley.net> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | static const struct signal_name { | ||
13 | int number; | ||
14 | char name[5]; | ||
15 | } signals[] = { | ||
16 | // SUSv3 says kill must support these, and specifies the numerical values, | ||
17 | // http://www.opengroup.org/onlinepubs/009695399/utilities/kill.html | ||
18 | {0, "0"}, {1, "HUP"}, {2, "INT"}, {3, "QUIT"}, {6, "ABRT"}, {9, "KILL"}, | ||
19 | {14, "ALRM"}, {15, "TERM"}, | ||
20 | // And Posix adds the following: | ||
21 | {SIGILL, "ILL"}, {SIGTRAP, "TRAP"}, {SIGFPE, "FPE"}, {SIGUSR1, "USR1"}, | ||
22 | {SIGSEGV, "SEGV"}, {SIGUSR2, "USR2"}, {SIGPIPE, "PIPE"}, {SIGCHLD, "CHLD"}, | ||
23 | {SIGCONT, "CONT"}, {SIGSTOP, "STOP"}, {SIGTSTP, "TSTP"}, {SIGTTIN, "TTIN"}, | ||
24 | {SIGTTOU, "TTOU"} | ||
25 | }; | ||
26 | |||
27 | // Convert signal name to number. | ||
28 | |||
29 | int get_signum(const char *name) | ||
30 | { | ||
31 | int i; | ||
32 | |||
33 | i = atoi(name); | ||
34 | if (i) return i; | ||
35 | for (i = 0; i < sizeof(signals) / sizeof(struct signal_name); i++) | ||
36 | if (!strcasecmp(signals[i].name, name) || | ||
37 | (!strncasecmp(signals[i].name, "SIG", 3) | ||
38 | && !strcasecmp(signals[i].name+3, signals[i].name))) | ||
39 | return signals[i].number; | ||
40 | return -1; | ||
41 | } | ||
42 | |||
43 | // Convert signal number to name | ||
44 | |||
45 | const char *get_signame(int number) | ||
46 | { | ||
47 | int i; | ||
48 | static char buf[8]; | ||
49 | |||
50 | for (i=0; i < sizeof(signals) / sizeof(struct signal_name); i++) { | ||
51 | if (number == signals[i].number) { | ||
52 | return signals[i].name; | ||
53 | } | ||
54 | } | ||
55 | |||
56 | itoa_to_buf(number, buf, 8); | ||
57 | return buf; | ||
58 | } | ||
diff --git a/libbb/uuencode.c b/libbb/uuencode.c new file mode 100644 index 000000000..38401205b --- /dev/null +++ b/libbb/uuencode.c | |||
@@ -0,0 +1,63 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright 2006 Rob Landley <rob@landley.net> | ||
4 | * | ||
5 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
6 | */ | ||
7 | |||
8 | #include "libbb.h" | ||
9 | |||
10 | /* Conversion table. for base 64 */ | ||
11 | const char bb_uuenc_tbl_base64[65] = { | ||
12 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', | ||
13 | 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', | ||
14 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', | ||
15 | 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', | ||
16 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', | ||
17 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', | ||
18 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', | ||
19 | '4', '5', '6', '7', '8', '9', '+', '/', | ||
20 | '=' /* termination character */ | ||
21 | }; | ||
22 | |||
23 | const char bb_uuenc_tbl_std[65] = { | ||
24 | '`', '!', '"', '#', '$', '%', '&', '\'', | ||
25 | '(', ')', '*', '+', ',', '-', '.', '/', | ||
26 | '0', '1', '2', '3', '4', '5', '6', '7', | ||
27 | '8', '9', ':', ';', '<', '=', '>', '?', | ||
28 | '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', | ||
29 | 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', | ||
30 | 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', | ||
31 | 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', | ||
32 | '`' /* termination character */ | ||
33 | }; | ||
34 | |||
35 | /* | ||
36 | * Encode the string S of length LENGTH to base64 format and place it | ||
37 | * to STORE. STORE will be 0-terminated, and must point to a writable | ||
38 | * buffer of at least 1+BASE64_LENGTH(length) bytes. | ||
39 | * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3)) | ||
40 | */ | ||
41 | void bb_uuencode(const unsigned char *s, char *store, const int length, const char *tbl) | ||
42 | { | ||
43 | int i; | ||
44 | char *p = store; | ||
45 | |||
46 | /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ | ||
47 | for (i = 0; i < length; i += 3) { | ||
48 | *p++ = tbl[s[0] >> 2]; | ||
49 | *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; | ||
50 | *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)]; | ||
51 | *p++ = tbl[s[2] & 0x3f]; | ||
52 | s += 3; | ||
53 | } | ||
54 | /* Pad the result if necessary... */ | ||
55 | if (i == length + 1) { | ||
56 | *(p - 1) = tbl[64]; | ||
57 | } | ||
58 | else if (i == length + 2) { | ||
59 | *(p - 1) = *(p - 2) = tbl[64]; | ||
60 | } | ||
61 | /* ...and zero-terminate it. */ | ||
62 | *p = '\0'; | ||
63 | } | ||
diff --git a/libbb/vdprintf.c b/libbb/vdprintf.c new file mode 100644 index 000000000..ffcb7a444 --- /dev/null +++ b/libbb/vdprintf.c | |||
@@ -0,0 +1,25 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <stdio.h> | ||
11 | #include <unistd.h> | ||
12 | #include "libbb.h" | ||
13 | |||
14 | |||
15 | |||
16 | #if (__GLIBC__ < 2) | ||
17 | int vdprintf(int d, const char *format, va_list ap) | ||
18 | { | ||
19 | char buf[BUF_SIZE]; | ||
20 | int len; | ||
21 | |||
22 | len = vsprintf(buf, format, ap); | ||
23 | return write(d, buf, len); | ||
24 | } | ||
25 | #endif | ||
diff --git a/libbb/verror_msg.c b/libbb/verror_msg.c new file mode 100644 index 000000000..0f018c517 --- /dev/null +++ b/libbb/verror_msg.c | |||
@@ -0,0 +1,46 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | #include <syslog.h> | ||
12 | |||
13 | int logmode = LOGMODE_STDIO; | ||
14 | const char *msg_eol = "\n"; | ||
15 | |||
16 | void bb_verror_msg(const char *s, va_list p, const char* strerr) | ||
17 | { | ||
18 | /* va_copy is used because it is not portable | ||
19 | * to use va_list p twice */ | ||
20 | va_list p2; | ||
21 | va_copy(p2, p); | ||
22 | |||
23 | if (logmode & LOGMODE_STDIO) { | ||
24 | fflush(stdout); | ||
25 | fprintf(stderr, "%s: ", applet_name); | ||
26 | vfprintf(stderr, s, p); | ||
27 | if (!strerr) | ||
28 | fputs(msg_eol, stderr); | ||
29 | else | ||
30 | fprintf(stderr, "%s%s%s", | ||
31 | s ? ": " : "", | ||
32 | strerr, msg_eol); | ||
33 | } | ||
34 | if (ENABLE_FEATURE_SYSLOG && (logmode & LOGMODE_SYSLOG)) { | ||
35 | if (!strerr) | ||
36 | vsyslog(LOG_ERR, s, p2); | ||
37 | else { | ||
38 | char *msg; | ||
39 | if (vasprintf(&msg, s, p2) < 0) | ||
40 | bb_error_msg_and_die(bb_msg_memory_exhausted); | ||
41 | syslog(LOG_ERR, "%s: %s", msg, strerr); | ||
42 | free(msg); | ||
43 | } | ||
44 | } | ||
45 | va_end(p2); | ||
46 | } | ||
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c new file mode 100644 index 000000000..ebd32f8cd --- /dev/null +++ b/libbb/vfork_daemon_rexec.c | |||
@@ -0,0 +1,67 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Rexec program for system have fork() as vfork() with foreground option | ||
4 | * | ||
5 | * Copyright (C) Vladimir N. Oleynik <dzo@simtreas.ru> | ||
6 | * Copyright (C) 2003 Russ Dill <Russ.Dill@asu.edu> | ||
7 | * | ||
8 | * daemon() portion taken from uClibc: | ||
9 | * | ||
10 | * Copyright (c) 1991, 1993 | ||
11 | * The Regents of the University of California. All rights reserved. | ||
12 | * | ||
13 | * Modified for uClibc by Erik Andersen <andersee@debian.org> | ||
14 | * | ||
15 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
16 | */ | ||
17 | |||
18 | #include <unistd.h> | ||
19 | #include <stdio.h> | ||
20 | #include <fcntl.h> | ||
21 | #include <paths.h> | ||
22 | #include "libbb.h" | ||
23 | |||
24 | |||
25 | #ifdef BB_NOMMU | ||
26 | void vfork_daemon_rexec(int nochdir, int noclose, | ||
27 | int argc, char **argv, char *foreground_opt) | ||
28 | { | ||
29 | int fd; | ||
30 | char **vfork_args; | ||
31 | int a = 0; | ||
32 | |||
33 | setsid(); | ||
34 | |||
35 | if (!nochdir) | ||
36 | xchdir("/"); | ||
37 | |||
38 | if (!noclose && (fd = open(bb_dev_null, O_RDWR, 0)) != -1) { | ||
39 | dup2(fd, STDIN_FILENO); | ||
40 | dup2(fd, STDOUT_FILENO); | ||
41 | dup2(fd, STDERR_FILENO); | ||
42 | if (fd > 2) | ||
43 | close(fd); | ||
44 | } | ||
45 | |||
46 | vfork_args = xcalloc(sizeof(char *), argc + 3); | ||
47 | vfork_args[a++] = CONFIG_BUSYBOX_EXEC_PATH; | ||
48 | while(*argv) { | ||
49 | vfork_args[a++] = *argv; | ||
50 | argv++; | ||
51 | } | ||
52 | vfork_args[a] = foreground_opt; | ||
53 | switch (vfork()) { | ||
54 | case 0: /* child */ | ||
55 | /* Make certain we are not a session leader, or else we | ||
56 | * might reacquire a controlling terminal */ | ||
57 | if (vfork()) | ||
58 | _exit(0); | ||
59 | execv(vfork_args[0], vfork_args); | ||
60 | bb_perror_msg_and_die("execv %s", vfork_args[0]); | ||
61 | case -1: /* error */ | ||
62 | bb_perror_msg_and_die("vfork"); | ||
63 | default: /* parent */ | ||
64 | exit(0); | ||
65 | } | ||
66 | } | ||
67 | #endif /* BB_NOMMU */ | ||
diff --git a/libbb/vherror_msg.c b/libbb/vherror_msg.c new file mode 100644 index 000000000..04446a090 --- /dev/null +++ b/libbb/vherror_msg.c | |||
@@ -0,0 +1,15 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | void bb_vherror_msg(const char *s, va_list p) | ||
13 | { | ||
14 | bb_verror_msg(s, p, hstrerror(h_errno)); | ||
15 | } | ||
diff --git a/libbb/vinfo_msg.c b/libbb/vinfo_msg.c new file mode 100644 index 000000000..fa2798625 --- /dev/null +++ b/libbb/vinfo_msg.c | |||
@@ -0,0 +1,26 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | #include <syslog.h> | ||
12 | |||
13 | void bb_vinfo_msg(const char *s, va_list p) | ||
14 | { | ||
15 | /* va_copy is used because it is not portable | ||
16 | * to use va_list p twice */ | ||
17 | va_list p2; | ||
18 | va_copy(p2, p); | ||
19 | if (logmode & LOGMODE_STDIO) { | ||
20 | vprintf(s, p); | ||
21 | fputs(msg_eol, stdout); | ||
22 | } | ||
23 | if (ENABLE_FEATURE_SYSLOG && (logmode & LOGMODE_SYSLOG)) | ||
24 | vsyslog(LOG_INFO, s, p2); | ||
25 | va_end(p2); | ||
26 | } | ||
diff --git a/libbb/vperror_msg.c b/libbb/vperror_msg.c new file mode 100644 index 000000000..c3f79c23b --- /dev/null +++ b/libbb/vperror_msg.c | |||
@@ -0,0 +1,15 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | void bb_vperror_msg(const char *s, va_list p) | ||
13 | { | ||
14 | bb_verror_msg(s, p, strerror(errno)); | ||
15 | } | ||
diff --git a/libbb/warn_ignoring_args.c b/libbb/warn_ignoring_args.c new file mode 100644 index 000000000..be78a4414 --- /dev/null +++ b/libbb/warn_ignoring_args.c | |||
@@ -0,0 +1,17 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * warn_ignoring_args implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | void bb_warn_ignoring_args(int n) | ||
13 | { | ||
14 | if (n) { | ||
15 | bb_error_msg("ignoring all arguments"); | ||
16 | } | ||
17 | } | ||
diff --git a/libbb/wfopen.c b/libbb/wfopen.c new file mode 100644 index 000000000..26e6a13e2 --- /dev/null +++ b/libbb/wfopen.c | |||
@@ -0,0 +1,20 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | FILE *fopen_or_warn(const char *path, const char *mode) | ||
13 | { | ||
14 | FILE *fp = fopen(path, mode); | ||
15 | if (!fp) { | ||
16 | bb_perror_msg("%s", path); | ||
17 | errno = 0; | ||
18 | } | ||
19 | return fp; | ||
20 | } | ||
diff --git a/libbb/wfopen_input.c b/libbb/wfopen_input.c new file mode 100644 index 000000000..3da855fe6 --- /dev/null +++ b/libbb/wfopen_input.c | |||
@@ -0,0 +1,31 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * wfopen_input implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | /* A number of applets need to open a file for reading, where the filename | ||
11 | * is a command line arg. Since often that arg is '-' (meaning stdin), | ||
12 | * we avoid testing everywhere by consolidating things in this routine. | ||
13 | * | ||
14 | * Note: We also consider "" to main stdin (for 'cmp' at least). | ||
15 | */ | ||
16 | |||
17 | #include "libbb.h" | ||
18 | |||
19 | FILE *fopen_or_warn_stdin(const char *filename) | ||
20 | { | ||
21 | FILE *fp = stdin; | ||
22 | |||
23 | if (filename != bb_msg_standard_input | ||
24 | && filename[0] | ||
25 | && (filename[0] != '-' || filename[1]) | ||
26 | ) { | ||
27 | fp = fopen_or_warn(filename, "r"); | ||
28 | } | ||
29 | |||
30 | return fp; | ||
31 | } | ||
diff --git a/libbb/xatonum.c b/libbb/xatonum.c new file mode 100644 index 000000000..35607c317 --- /dev/null +++ b/libbb/xatonum.c | |||
@@ -0,0 +1,69 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * ascii-to-numbers implementations for busybox | ||
4 | * | ||
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | #define type long long | ||
13 | #define xstrtou(rest) xstrtoull##rest | ||
14 | #define xstrto(rest) xstrtoll##rest | ||
15 | #define xatou(rest) xatoull##rest | ||
16 | #define xato(rest) xatoll##rest | ||
17 | #define XSTR_UTYPE_MAX ULLONG_MAX | ||
18 | #define XSTR_TYPE_MAX LLONG_MAX | ||
19 | #define XSTR_TYPE_MIN LLONG_MIN | ||
20 | #define XSTR_STRTOU strtoull | ||
21 | #include "xatonum_template.c" | ||
22 | |||
23 | #if ULONG_MAX != ULLONG_MAX | ||
24 | #define type long | ||
25 | #define xstrtou(rest) xstrtoul##rest | ||
26 | #define xstrto(rest) xstrtol##rest | ||
27 | #define xatou(rest) xatoul##rest | ||
28 | #define xato(rest) xatol##rest | ||
29 | #define XSTR_UTYPE_MAX ULONG_MAX | ||
30 | #define XSTR_TYPE_MAX LONG_MAX | ||
31 | #define XSTR_TYPE_MIN LONG_MIN | ||
32 | #define XSTR_STRTOU strtoul | ||
33 | #include "xatonum_template.c" | ||
34 | #endif | ||
35 | |||
36 | #if UINT_MAX != ULONG_MAX | ||
37 | extern inline unsigned bb_strtoui(const char *str, char **end, int b) | ||
38 | { | ||
39 | unsigned long v = strtoul(str, end, b); | ||
40 | if (v > UINT_MAX) { | ||
41 | errno = ERANGE; | ||
42 | return UINT_MAX; | ||
43 | } | ||
44 | return v; | ||
45 | } | ||
46 | #define type int | ||
47 | #define xstrtou(rest) xstrtou##rest | ||
48 | #define xstrto(rest) xstrtoi##rest | ||
49 | #define xatou(rest) xatou##rest | ||
50 | #define xato(rest) xatoi##rest | ||
51 | #define XSTR_UTYPE_MAX UINT_MAX | ||
52 | #define XSTR_TYPE_MAX INT_MAX | ||
53 | #define XSTR_TYPE_MIN INT_MIN | ||
54 | /* libc has no strtoui, so we need to create/use our own */ | ||
55 | #define XSTR_STRTOU bb_strtoui | ||
56 | #include "xatonum_template.c" | ||
57 | #endif | ||
58 | |||
59 | /* A few special cases */ | ||
60 | |||
61 | int xatoi_u(const char *numstr) | ||
62 | { | ||
63 | return xatou_range(numstr, 0, INT_MAX); | ||
64 | } | ||
65 | |||
66 | uint16_t xatou16(const char *numstr) | ||
67 | { | ||
68 | return xatou_range(numstr, 0, 0xffff); | ||
69 | } | ||
diff --git a/libbb/xatonum_template.c b/libbb/xatonum_template.c new file mode 100644 index 000000000..53ba544eb --- /dev/null +++ b/libbb/xatonum_template.c | |||
@@ -0,0 +1,185 @@ | |||
1 | /* | ||
2 | You need to define the following (example): | ||
3 | |||
4 | #define type long | ||
5 | #define xstrtou(rest) xstrtoul##rest | ||
6 | #define xstrto(rest) xstrtol##rest | ||
7 | #define xatou(rest) xatoul##rest | ||
8 | #define xato(rest) xatol##rest | ||
9 | #define XSTR_UTYPE_MAX ULONG_MAX | ||
10 | #define XSTR_TYPE_MAX LONG_MAX | ||
11 | #define XSTR_TYPE_MIN LONG_MIN | ||
12 | #define XSTR_STRTOU strtoul | ||
13 | */ | ||
14 | |||
15 | unsigned type xstrtou(_range_sfx)(const char *numstr, int base, | ||
16 | unsigned type lower, | ||
17 | unsigned type upper, | ||
18 | const struct suffix_mult *suffixes) | ||
19 | { | ||
20 | unsigned type r; | ||
21 | int old_errno; | ||
22 | char *e; | ||
23 | |||
24 | /* Disallow '-' and any leading whitespace. Speed isn't critical here | ||
25 | * since we're parsing commandline args. So make sure we get the | ||
26 | * actual isspace function rather than a lnumstrer macro implementaion. */ | ||
27 | if ((*numstr == '-') || (isspace)(*numstr)) | ||
28 | goto inval; | ||
29 | |||
30 | /* Since this is a lib function, we're not allowed to reset errno to 0. | ||
31 | * Doing so could break an app that is deferring checking of errno. | ||
32 | * So, save the old value so that we can restore it if successful. */ | ||
33 | old_errno = errno; | ||
34 | errno = 0; | ||
35 | r = XSTR_STRTOU(numstr, &e, base); | ||
36 | /* Do the initial validity check. Note: The standards do not | ||
37 | * guarantee that errno is set if no digits were found. So we | ||
38 | * must test for this explicitly. */ | ||
39 | if (errno || (numstr == e)) | ||
40 | goto inval; /* error / no digits / illegal trailing chars */ | ||
41 | |||
42 | errno = old_errno; /* Ok. So restore errno. */ | ||
43 | |||
44 | /* Do optional suffix parsing. Allow 'empty' suffix tables. | ||
45 | * Note that we also allow nul suffixes with associated multipliers, | ||
46 | * to allow for scaling of the numstr by some default multiplier. */ | ||
47 | if (suffixes) { | ||
48 | while (suffixes->suffix) { | ||
49 | if (strcmp(suffixes->suffix, e) == 0) { | ||
50 | if (XSTR_UTYPE_MAX / suffixes->mult < r) | ||
51 | goto range; /* overflow! */ | ||
52 | ++e; | ||
53 | r *= suffixes->mult; | ||
54 | break; | ||
55 | } | ||
56 | ++suffixes; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | /* Note: trailing space is an error. | ||
61 | It would be easy enough to allow though if desired. */ | ||
62 | if (*e) | ||
63 | goto inval; | ||
64 | /* Finally, check for range limits. */ | ||
65 | if (r >= lower && r <= upper) | ||
66 | return r; | ||
67 | range: | ||
68 | bb_error_msg_and_die("number %s is not in %llu..%llu range", | ||
69 | numstr, (unsigned long long)lower, | ||
70 | (unsigned long long)upper); | ||
71 | inval: | ||
72 | bb_error_msg_and_die("invalid number '%s'", numstr); | ||
73 | } | ||
74 | |||
75 | unsigned type xstrtou(_range)(const char *numstr, int base, | ||
76 | unsigned type lower, | ||
77 | unsigned type upper) | ||
78 | { | ||
79 | return xstrtou(_range_sfx)(numstr, base, lower, upper, NULL); | ||
80 | } | ||
81 | |||
82 | unsigned type xstrtou(_sfx)(const char *numstr, int base, | ||
83 | const struct suffix_mult *suffixes) | ||
84 | { | ||
85 | return xstrtou(_range_sfx)(numstr, base, 0, XSTR_UTYPE_MAX, suffixes); | ||
86 | } | ||
87 | |||
88 | unsigned type xstrtou()(const char *numstr, int base) | ||
89 | { | ||
90 | return xstrtou(_range_sfx)(numstr, base, 0, XSTR_UTYPE_MAX, NULL); | ||
91 | } | ||
92 | |||
93 | unsigned type xatou(_range_sfx)(const char *numstr, | ||
94 | unsigned type lower, | ||
95 | unsigned type upper, | ||
96 | const struct suffix_mult *suffixes) | ||
97 | { | ||
98 | return xstrtou(_range_sfx)(numstr, 10, lower, upper, suffixes); | ||
99 | } | ||
100 | |||
101 | unsigned type xatou(_range)(const char *numstr, | ||
102 | unsigned type lower, | ||
103 | unsigned type upper) | ||
104 | { | ||
105 | return xstrtou(_range_sfx)(numstr, 10, lower, upper, NULL); | ||
106 | } | ||
107 | |||
108 | unsigned type xatou(_sfx)(const char *numstr, | ||
109 | const struct suffix_mult *suffixes) | ||
110 | { | ||
111 | return xstrtou(_range_sfx)(numstr, 10, 0, XSTR_UTYPE_MAX, suffixes); | ||
112 | } | ||
113 | |||
114 | unsigned type xatou()(const char *numstr) | ||
115 | { | ||
116 | return xatou(_sfx)(numstr, NULL); | ||
117 | } | ||
118 | |||
119 | /* Signed ones */ | ||
120 | |||
121 | type xstrto(_range_sfx)(const char *numstr, int base, | ||
122 | type lower, | ||
123 | type upper, | ||
124 | const struct suffix_mult *suffixes) | ||
125 | { | ||
126 | unsigned type u = XSTR_TYPE_MAX; | ||
127 | type r; | ||
128 | const char *p = numstr; | ||
129 | |||
130 | if ((p[0] == '-') && (p[1] != '+')) { | ||
131 | ++p; | ||
132 | ++u; /* two's complement */ | ||
133 | } | ||
134 | |||
135 | r = xstrtou(_range_sfx)(p, base, 0, u, suffixes); | ||
136 | |||
137 | if (*numstr == '-') { | ||
138 | r = -r; | ||
139 | } | ||
140 | |||
141 | if (r < lower || r > upper) { | ||
142 | bb_error_msg_and_die("number %s is not in %lld..%lld range", | ||
143 | numstr, (long long)lower, (long long)upper); | ||
144 | } | ||
145 | |||
146 | return r; | ||
147 | } | ||
148 | |||
149 | type xstrto(_range)(const char *numstr, int base, type lower, type upper) | ||
150 | { | ||
151 | return xstrto(_range_sfx)(numstr, base, lower, upper, NULL); | ||
152 | } | ||
153 | |||
154 | type xato(_range_sfx)(const char *numstr, | ||
155 | type lower, | ||
156 | type upper, | ||
157 | const struct suffix_mult *suffixes) | ||
158 | { | ||
159 | return xstrto(_range_sfx)(numstr, 10, lower, upper, suffixes); | ||
160 | } | ||
161 | |||
162 | type xato(_range)(const char *numstr, type lower, type upper) | ||
163 | { | ||
164 | return xstrto(_range_sfx)(numstr, 10, lower, upper, NULL); | ||
165 | } | ||
166 | |||
167 | type xato(_sfx)(const char *numstr, const struct suffix_mult *suffixes) | ||
168 | { | ||
169 | return xstrto(_range_sfx)(numstr, 10, XSTR_TYPE_MIN, XSTR_TYPE_MAX, suffixes); | ||
170 | } | ||
171 | |||
172 | type xato()(const char *numstr) | ||
173 | { | ||
174 | return xstrto(_range_sfx)(numstr, 10, XSTR_TYPE_MIN, XSTR_TYPE_MAX, NULL); | ||
175 | } | ||
176 | |||
177 | #undef type | ||
178 | #undef xstrtou | ||
179 | #undef xstrto | ||
180 | #undef xatou | ||
181 | #undef xato | ||
182 | #undef XSTR_UTYPE_MAX | ||
183 | #undef XSTR_TYPE_MAX | ||
184 | #undef XSTR_TYPE_MIN | ||
185 | #undef XSTR_STRTOU | ||
diff --git a/libbb/xconnect.c b/libbb/xconnect.c new file mode 100644 index 000000000..b85648007 --- /dev/null +++ b/libbb/xconnect.c | |||
@@ -0,0 +1,153 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Connect to host at port using address resolution from getaddrinfo | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | #include "libbb.h" | ||
10 | |||
11 | /* Return network byte ordered port number for a service. | ||
12 | * If "port" is a number use it as the port. | ||
13 | * If "port" is a name it is looked up in /etc/services, if it isnt found return | ||
14 | * default_port | ||
15 | */ | ||
16 | unsigned short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port) | ||
17 | { | ||
18 | unsigned short port_nr = htons(default_port); | ||
19 | if (port) { | ||
20 | char *endptr; | ||
21 | int old_errno; | ||
22 | long port_long; | ||
23 | |||
24 | /* Since this is a lib function, we're not allowed to reset errno to 0. | ||
25 | * Doing so could break an app that is deferring checking of errno. */ | ||
26 | old_errno = errno; | ||
27 | errno = 0; | ||
28 | port_long = strtol(port, &endptr, 10); | ||
29 | if (errno != 0 || *endptr!='\0' || endptr==port || port_long < 0 || port_long > 65535) { | ||
30 | struct servent *tserv = getservbyname(port, protocol); | ||
31 | if (tserv) { | ||
32 | port_nr = tserv->s_port; | ||
33 | } | ||
34 | } else { | ||
35 | port_nr = htons(port_long); | ||
36 | } | ||
37 | errno = old_errno; | ||
38 | } | ||
39 | return port_nr; | ||
40 | } | ||
41 | |||
42 | void bb_lookup_host(struct sockaddr_in *s_in, const char *host) | ||
43 | { | ||
44 | struct hostent *he; | ||
45 | |||
46 | memset(s_in, 0, sizeof(struct sockaddr_in)); | ||
47 | s_in->sin_family = AF_INET; | ||
48 | he = xgethostbyname(host); | ||
49 | memcpy(&(s_in->sin_addr), he->h_addr_list[0], he->h_length); | ||
50 | } | ||
51 | |||
52 | void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) | ||
53 | { | ||
54 | if (connect(s, s_addr, addrlen) < 0) { | ||
55 | if (ENABLE_FEATURE_CLEAN_UP) close(s); | ||
56 | if (s_addr->sa_family == AF_INET) | ||
57 | bb_perror_msg_and_die("cannot connect to remote host (%s)", | ||
58 | inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); | ||
59 | bb_perror_msg_and_die("cannot connect to remote host"); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | int xconnect_tcp_v4(struct sockaddr_in *s_addr) | ||
64 | { | ||
65 | int s = xsocket(AF_INET, SOCK_STREAM, 0); | ||
66 | xconnect(s, (struct sockaddr*) s_addr, sizeof(*s_addr)); | ||
67 | return s; | ||
68 | } | ||
69 | |||
70 | static const int one = 1; | ||
71 | int setsockopt_reuseaddr(int fd) | ||
72 | { | ||
73 | return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); | ||
74 | } | ||
75 | int setsockopt_broadcast(int fd) | ||
76 | { | ||
77 | return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)); | ||
78 | } | ||
79 | |||
80 | int dotted2sockaddr(const char *dotted, struct sockaddr* sp, int socklen) | ||
81 | { | ||
82 | union { | ||
83 | struct in_addr a4; | ||
84 | #if ENABLE_FEATURE_IPV6 | ||
85 | struct in6_addr a6; | ||
86 | #endif | ||
87 | } a; | ||
88 | |||
89 | /* TODO maybe: port spec? like n.n.n.n:nn */ | ||
90 | |||
91 | #if ENABLE_FEATURE_IPV6 | ||
92 | if (socklen >= sizeof(struct sockaddr_in6) | ||
93 | && inet_pton(AF_INET6, dotted, &a.a6) > 0 | ||
94 | ) { | ||
95 | ((struct sockaddr_in6*)sp)->sin6_family = AF_INET6; | ||
96 | ((struct sockaddr_in6*)sp)->sin6_addr = a.a6; | ||
97 | /* ((struct sockaddr_in6*)sp)->sin6_port = */ | ||
98 | return 0; /* success */ | ||
99 | } | ||
100 | #endif | ||
101 | if (socklen >= sizeof(struct sockaddr_in) | ||
102 | && inet_pton(AF_INET, dotted, &a.a4) > 0 | ||
103 | ) { | ||
104 | ((struct sockaddr_in*)sp)->sin_family = AF_INET; | ||
105 | ((struct sockaddr_in*)sp)->sin_addr = a.a4; | ||
106 | /* ((struct sockaddr_in*)sp)->sin_port = */ | ||
107 | return 0; /* success */ | ||
108 | } | ||
109 | return 1; | ||
110 | } | ||
111 | |||
112 | int xsocket_stream_ip4or6(sa_family_t *fp) | ||
113 | { | ||
114 | int fd; | ||
115 | #if ENABLE_FEATURE_IPV6 | ||
116 | fd = socket(AF_INET6, SOCK_STREAM, 0); | ||
117 | if (fp) *fp = AF_INET6; | ||
118 | if (fd < 0) | ||
119 | #endif | ||
120 | { | ||
121 | fd = xsocket(AF_INET, SOCK_STREAM, 0); | ||
122 | if (fp) *fp = AF_INET; | ||
123 | } | ||
124 | return fd; | ||
125 | } | ||
126 | |||
127 | int create_and_bind_socket_ip4or6(const char *hostaddr, int port) | ||
128 | { | ||
129 | int fd; | ||
130 | sockaddr_inet sa; | ||
131 | |||
132 | memset(&sa, 0, sizeof(sa)); | ||
133 | if (hostaddr) { | ||
134 | if (dotted2sockaddr(hostaddr, &sa.sa, sizeof(sa))) | ||
135 | bb_error_msg_and_die("bad address '%s'", hostaddr); | ||
136 | /* user specified bind addr dictates family */ | ||
137 | fd = xsocket(sa.sa.sa_family, SOCK_STREAM, 0); | ||
138 | } else | ||
139 | fd = xsocket_stream_ip4or6(&sa.sa.sa_family); | ||
140 | setsockopt_reuseaddr(fd); | ||
141 | |||
142 | /* if (port >= 0) { */ | ||
143 | #if ENABLE_FEATURE_IPV6 | ||
144 | if (sa.sa.sa_family == AF_INET6 /* && !sa.sin6.sin6_port */) | ||
145 | sa.sin6.sin6_port = htons(port); | ||
146 | #endif | ||
147 | if (sa.sa.sa_family == AF_INET /* && !sa.sin.sin_port */) | ||
148 | sa.sin.sin_port = htons(port); | ||
149 | /* } */ | ||
150 | |||
151 | xbind(fd, &sa.sa, sizeof(sa)); | ||
152 | return fd; | ||
153 | } | ||
diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c new file mode 100644 index 000000000..313e32814 --- /dev/null +++ b/libbb/xfuncs.c | |||
@@ -0,0 +1,521 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * Copyright (C) 2006 Rob Landley | ||
7 | * Copyright (C) 2006 Denis Vlasenko | ||
8 | * | ||
9 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. | ||
10 | */ | ||
11 | |||
12 | #include "busybox.h" | ||
13 | |||
14 | /* All the functions starting with "x" call bb_error_msg_and_die() if they | ||
15 | * fail, so callers never need to check for errors. If it returned, it | ||
16 | * succeeded. */ | ||
17 | |||
18 | #ifndef DMALLOC | ||
19 | /* dmalloc provides variants of these that do abort() on failure. | ||
20 | * Since dmalloc's prototypes overwrite the impls here as they are | ||
21 | * included after these prototypes in libbb.h, all is well. | ||
22 | */ | ||
23 | // Die if we can't allocate size bytes of memory. | ||
24 | void *xmalloc(size_t size) | ||
25 | { | ||
26 | void *ptr = malloc(size); | ||
27 | if (ptr == NULL && size != 0) | ||
28 | bb_error_msg_and_die(bb_msg_memory_exhausted); | ||
29 | return ptr; | ||
30 | } | ||
31 | |||
32 | // Die if we can't resize previously allocated memory. (This returns a pointer | ||
33 | // to the new memory, which may or may not be the same as the old memory. | ||
34 | // It'll copy the contents to a new chunk and free the old one if necessary.) | ||
35 | void *xrealloc(void *ptr, size_t size) | ||
36 | { | ||
37 | ptr = realloc(ptr, size); | ||
38 | if (ptr == NULL && size != 0) | ||
39 | bb_error_msg_and_die(bb_msg_memory_exhausted); | ||
40 | return ptr; | ||
41 | } | ||
42 | #endif /* DMALLOC */ | ||
43 | |||
44 | // Die if we can't allocate and zero size bytes of memory. | ||
45 | void *xzalloc(size_t size) | ||
46 | { | ||
47 | void *ptr = xmalloc(size); | ||
48 | memset(ptr, 0, size); | ||
49 | return ptr; | ||
50 | } | ||
51 | |||
52 | // Die if we can't copy a string to freshly allocated memory. | ||
53 | char * xstrdup(const char *s) | ||
54 | { | ||
55 | char *t; | ||
56 | |||
57 | if (s == NULL) | ||
58 | return NULL; | ||
59 | |||
60 | t = strdup(s); | ||
61 | |||
62 | if (t == NULL) | ||
63 | bb_error_msg_and_die(bb_msg_memory_exhausted); | ||
64 | |||
65 | return t; | ||
66 | } | ||
67 | |||
68 | // Die if we can't allocate n+1 bytes (space for the null terminator) and copy | ||
69 | // the (possibly truncated to length n) string into it. | ||
70 | char * xstrndup(const char *s, int n) | ||
71 | { | ||
72 | int m; | ||
73 | char *t; | ||
74 | |||
75 | if (ENABLE_DEBUG && s == NULL) | ||
76 | bb_error_msg_and_die("xstrndup bug"); | ||
77 | |||
78 | /* We can just xmalloc(n+1) and strncpy into it, */ | ||
79 | /* but think about xstrndup("abc", 10000) wastage! */ | ||
80 | m = n; | ||
81 | t = (char*) s; | ||
82 | while (m) { | ||
83 | if (!*t) break; | ||
84 | m--; t++; | ||
85 | } | ||
86 | n = n - m; | ||
87 | t = xmalloc(n + 1); | ||
88 | t[n] = '\0'; | ||
89 | |||
90 | return memcpy(t,s,n); | ||
91 | } | ||
92 | |||
93 | // Die if we can't open a file and return a FILE * to it. | ||
94 | // Notice we haven't got xfread(), This is for use with fscanf() and friends. | ||
95 | FILE *xfopen(const char *path, const char *mode) | ||
96 | { | ||
97 | FILE *fp = fopen(path, mode); | ||
98 | if (fp == NULL) | ||
99 | bb_perror_msg_and_die("%s", path); | ||
100 | return fp; | ||
101 | } | ||
102 | |||
103 | // Die if we can't open an existing file and return an fd. | ||
104 | int xopen(const char *pathname, int flags) | ||
105 | { | ||
106 | //if (ENABLE_DEBUG && (flags & O_CREAT)) | ||
107 | // bb_error_msg_and_die("xopen() with O_CREAT"); | ||
108 | |||
109 | return xopen3(pathname, flags, 0666); | ||
110 | } | ||
111 | |||
112 | // Die if we can't open a new file and return an fd. | ||
113 | int xopen3(const char *pathname, int flags, int mode) | ||
114 | { | ||
115 | int ret; | ||
116 | |||
117 | ret = open(pathname, flags, mode); | ||
118 | if (ret < 0) { | ||
119 | bb_perror_msg_and_die("%s", pathname); | ||
120 | } | ||
121 | return ret; | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | int ndelay_off(int fd) | ||
126 | { | ||
127 | return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK); | ||
128 | } | ||
129 | */ | ||
130 | // Turn on nonblocking I/O on a fd | ||
131 | int ndelay_on(int fd) | ||
132 | { | ||
133 | return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK); | ||
134 | } | ||
135 | |||
136 | // Die with an error message if we can't write the entire buffer. | ||
137 | void xwrite(int fd, const void *buf, size_t count) | ||
138 | { | ||
139 | if (count) { | ||
140 | ssize_t size = full_write(fd, buf, count); | ||
141 | if (size != count) | ||
142 | bb_error_msg_and_die("short write"); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | // Die with an error message if we can't lseek to the right spot. | ||
147 | off_t xlseek(int fd, off_t offset, int whence) | ||
148 | { | ||
149 | off_t off = lseek(fd, offset, whence); | ||
150 | if (off == (off_t)-1) { | ||
151 | if (whence == SEEK_SET) | ||
152 | bb_perror_msg_and_die("lseek(%"OFF_FMT"u)", offset); | ||
153 | bb_perror_msg_and_die("lseek"); | ||
154 | } | ||
155 | return off; | ||
156 | } | ||
157 | |||
158 | // Die with supplied filename if this FILE * has ferror set. | ||
159 | void die_if_ferror(FILE *fp, const char *fn) | ||
160 | { | ||
161 | if (ferror(fp)) { | ||
162 | bb_error_msg_and_die("%s: I/O error", fn); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | // Die with an error message if stdout has ferror set. | ||
167 | void die_if_ferror_stdout(void) | ||
168 | { | ||
169 | die_if_ferror(stdout, bb_msg_standard_output); | ||
170 | } | ||
171 | |||
172 | // Die with an error message if we have trouble flushing stdout. | ||
173 | void xfflush_stdout(void) | ||
174 | { | ||
175 | if (fflush(stdout)) { | ||
176 | bb_perror_msg_and_die(bb_msg_standard_output); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | // This does a fork/exec in one call, using vfork(). Return PID of new child, | ||
181 | // -1 for failure. Runs argv[0], searching path if that has no / in it. | ||
182 | pid_t spawn(char **argv) | ||
183 | { | ||
184 | static int failed; | ||
185 | pid_t pid; | ||
186 | void *app = ENABLE_FEATURE_SH_STANDALONE_SHELL ? find_applet_by_name(argv[0]) : 0; | ||
187 | |||
188 | // Be nice to nommu machines. | ||
189 | failed = 0; | ||
190 | pid = vfork(); | ||
191 | if (pid < 0) return pid; | ||
192 | if (!pid) { | ||
193 | execvp(app ? CONFIG_BUSYBOX_EXEC_PATH : *argv, argv); | ||
194 | |||
195 | // We're sharing a stack with blocked parent, let parent know we failed | ||
196 | // and then exit to unblock parent (but don't run atexit() stuff, which | ||
197 | // would screw up parent.) | ||
198 | |||
199 | failed = -1; | ||
200 | _exit(0); | ||
201 | } | ||
202 | return failed ? failed : pid; | ||
203 | } | ||
204 | |||
205 | // Die with an error message if we can't spawn a child process. | ||
206 | pid_t xspawn(char **argv) | ||
207 | { | ||
208 | pid_t pid = spawn(argv); | ||
209 | if (pid < 0) bb_perror_msg_and_die("%s", *argv); | ||
210 | return pid; | ||
211 | } | ||
212 | |||
213 | // Wait for the specified child PID to exit, returning child's error return. | ||
214 | int wait4pid(int pid) | ||
215 | { | ||
216 | int status; | ||
217 | |||
218 | if (pid == -1 || waitpid(pid, &status, 0) == -1) return -1; | ||
219 | if (WIFEXITED(status)) return WEXITSTATUS(status); | ||
220 | if (WIFSIGNALED(status)) return WTERMSIG(status); | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | void xsetenv(const char *key, const char *value) | ||
225 | { | ||
226 | if (setenv(key, value, 1)) | ||
227 | bb_error_msg_and_die(bb_msg_memory_exhausted); | ||
228 | } | ||
229 | |||
230 | // Converts unsigned long long value into compact 4-char | ||
231 | // representation. Examples: "1234", "1.2k", " 27M", "123T" | ||
232 | // Fifth char is always '\0' | ||
233 | void smart_ulltoa5(unsigned long long ul, char buf[5]) | ||
234 | { | ||
235 | char *fmt; | ||
236 | char c; | ||
237 | unsigned v,idx = 0; | ||
238 | ul *= 10; | ||
239 | if (ul > 9999*10) { // do not scale if 9999 or less | ||
240 | while (ul >= 10000) { | ||
241 | ul /= 1024; | ||
242 | idx++; | ||
243 | } | ||
244 | } | ||
245 | v = ul; // ullong divisions are expensive, avoid them | ||
246 | |||
247 | fmt = " 123456789"; | ||
248 | if (!idx) { // 9999 or less: use 1234 format | ||
249 | c = buf[0] = " 123456789"[v/10000]; | ||
250 | if (c!=' ') fmt = "0123456789"; | ||
251 | c = buf[1] = fmt[v/1000%10]; | ||
252 | if (c!=' ') fmt = "0123456789"; | ||
253 | buf[2] = fmt[v/100%10]; | ||
254 | buf[3] = "0123456789"[v/10%10]; | ||
255 | } else { | ||
256 | if (v>=10*10) { // scaled value is >=10: use 123M format | ||
257 | c = buf[0] = " 123456789"[v/1000]; | ||
258 | if (c!=' ') fmt = "0123456789"; | ||
259 | buf[1] = fmt[v/100%10]; | ||
260 | buf[2] = "0123456789"[v/10%10]; | ||
261 | } else { // scaled value is <10: use 1.2M format | ||
262 | buf[0] = "0123456789"[v/10]; | ||
263 | buf[1] = '.'; | ||
264 | buf[2] = "0123456789"[v%10]; | ||
265 | } | ||
266 | // see http://en.wikipedia.org/wiki/Tera | ||
267 | buf[3] = " kMGTPEZY"[idx]; | ||
268 | } | ||
269 | buf[4] = '\0'; | ||
270 | } | ||
271 | |||
272 | // Convert unsigned integer to ascii, writing into supplied buffer. A | ||
273 | // truncated result is always null terminated (unless buflen is 0), and | ||
274 | // contains the first few digits of the result ala strncpy. | ||
275 | void BUG_sizeof_unsigned_not_4(void); | ||
276 | void utoa_to_buf(unsigned n, char *buf, unsigned buflen) | ||
277 | { | ||
278 | unsigned i, out, res; | ||
279 | if (sizeof(unsigned) != 4) | ||
280 | BUG_sizeof_unsigned_not_4(); | ||
281 | if (buflen) { | ||
282 | out = 0; | ||
283 | for (i = 1000000000; i; i /= 10) { | ||
284 | res = n / i; | ||
285 | if (res || out || i == 1) { | ||
286 | if (!--buflen) break; | ||
287 | out++; | ||
288 | n -= res*i; | ||
289 | *buf++ = '0' + res; | ||
290 | } | ||
291 | } | ||
292 | *buf = '\0'; | ||
293 | } | ||
294 | } | ||
295 | |||
296 | // Convert signed integer to ascii, like utoa_to_buf() | ||
297 | void itoa_to_buf(int n, char *buf, unsigned buflen) | ||
298 | { | ||
299 | if (buflen && n<0) { | ||
300 | n = -n; | ||
301 | *buf++ = '-'; | ||
302 | buflen--; | ||
303 | } | ||
304 | utoa_to_buf((unsigned)n, buf, buflen); | ||
305 | } | ||
306 | |||
307 | // The following two functions use a static buffer, so calling either one a | ||
308 | // second time will overwrite previous results. | ||
309 | // | ||
310 | // The largest 32 bit integer is -2 billion plus null terminator, or 12 bytes. | ||
311 | // Int should always be 32 bits on any remotely Unix-like system, see | ||
312 | // http://www.unix.org/whitepapers/64bit.html for the reasons why. | ||
313 | |||
314 | static char local_buf[12]; | ||
315 | |||
316 | // Convert unsigned integer to ascii using a static buffer (returned). | ||
317 | char *utoa(unsigned n) | ||
318 | { | ||
319 | utoa_to_buf(n, local_buf, sizeof(local_buf)); | ||
320 | |||
321 | return local_buf; | ||
322 | } | ||
323 | |||
324 | // Convert signed integer to ascii using a static buffer (returned). | ||
325 | char *itoa(int n) | ||
326 | { | ||
327 | itoa_to_buf(n, local_buf, sizeof(local_buf)); | ||
328 | |||
329 | return local_buf; | ||
330 | } | ||
331 | |||
332 | // Die with an error message if we can't set gid. (Because resource limits may | ||
333 | // limit this user to a given number of processes, and if that fills up the | ||
334 | // setgid() will fail and we'll _still_be_root_, which is bad.) | ||
335 | void xsetgid(gid_t gid) | ||
336 | { | ||
337 | if (setgid(gid)) bb_error_msg_and_die("setgid"); | ||
338 | } | ||
339 | |||
340 | // Die with an error message if we can't set uid. (See xsetgid() for why.) | ||
341 | void xsetuid(uid_t uid) | ||
342 | { | ||
343 | if (setuid(uid)) bb_error_msg_and_die("setuid"); | ||
344 | } | ||
345 | |||
346 | // Return how long the file at fd is, if there's any way to determine it. | ||
347 | off_t fdlength(int fd) | ||
348 | { | ||
349 | off_t bottom = 0, top = 0, pos; | ||
350 | long size; | ||
351 | |||
352 | // If the ioctl works for this, return it. | ||
353 | |||
354 | if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512; | ||
355 | |||
356 | // FIXME: explain why lseek(SEEK_END) is not used here! | ||
357 | |||
358 | // If not, do a binary search for the last location we can read. (Some | ||
359 | // block devices don't do BLKGETSIZE right.) | ||
360 | |||
361 | do { | ||
362 | char temp; | ||
363 | |||
364 | pos = bottom + (top - bottom) / 2; | ||
365 | |||
366 | // If we can read from the current location, it's bigger. | ||
367 | |||
368 | if (lseek(fd, pos, SEEK_SET)>=0 && safe_read(fd, &temp, 1)==1) { | ||
369 | if (bottom == top) bottom = top = (top+1) * 2; | ||
370 | else bottom = pos; | ||
371 | |||
372 | // If we can't, it's smaller. | ||
373 | |||
374 | } else { | ||
375 | if (bottom == top) { | ||
376 | if (!top) return 0; | ||
377 | bottom = top/2; | ||
378 | } | ||
379 | else top = pos; | ||
380 | } | ||
381 | } while (bottom + 1 != top); | ||
382 | |||
383 | return pos + 1; | ||
384 | } | ||
385 | |||
386 | // Die with an error message if we can't malloc() enough space and do an | ||
387 | // sprintf() into that space. | ||
388 | char *xasprintf(const char *format, ...) | ||
389 | { | ||
390 | va_list p; | ||
391 | int r; | ||
392 | char *string_ptr; | ||
393 | |||
394 | #if 1 | ||
395 | // GNU extension | ||
396 | va_start(p, format); | ||
397 | r = vasprintf(&string_ptr, format, p); | ||
398 | va_end(p); | ||
399 | #else | ||
400 | // Bloat for systems that haven't got the GNU extension. | ||
401 | va_start(p, format); | ||
402 | r = vsnprintf(NULL, 0, format, p); | ||
403 | va_end(p); | ||
404 | string_ptr = xmalloc(r+1); | ||
405 | va_start(p, format); | ||
406 | r = vsnprintf(string_ptr, r+1, format, p); | ||
407 | va_end(p); | ||
408 | #endif | ||
409 | |||
410 | if (r < 0) bb_error_msg_and_die(bb_msg_memory_exhausted); | ||
411 | return string_ptr; | ||
412 | } | ||
413 | |||
414 | // Die with an error message if we can't copy an entire FILE * to stdout, then | ||
415 | // close that file. | ||
416 | void xprint_and_close_file(FILE *file) | ||
417 | { | ||
418 | fflush(stdout); | ||
419 | // copyfd outputs error messages for us. | ||
420 | if (bb_copyfd_eof(fileno(file), 1) == -1) | ||
421 | exit(xfunc_error_retval); | ||
422 | |||
423 | fclose(file); | ||
424 | } | ||
425 | |||
426 | // Die if we can't chdir to a new path. | ||
427 | void xchdir(const char *path) | ||
428 | { | ||
429 | if (chdir(path)) | ||
430 | bb_perror_msg_and_die("chdir(%s)", path); | ||
431 | } | ||
432 | |||
433 | // Print a warning message if opendir() fails, but don't die. | ||
434 | DIR *warn_opendir(const char *path) | ||
435 | { | ||
436 | DIR *dp; | ||
437 | |||
438 | if ((dp = opendir(path)) == NULL) { | ||
439 | bb_perror_msg("cannot open '%s'", path); | ||
440 | return NULL; | ||
441 | } | ||
442 | return dp; | ||
443 | } | ||
444 | |||
445 | // Die with an error message if opendir() fails. | ||
446 | DIR *xopendir(const char *path) | ||
447 | { | ||
448 | DIR *dp; | ||
449 | |||
450 | if ((dp = opendir(path)) == NULL) | ||
451 | bb_perror_msg_and_die("cannot open '%s'", path); | ||
452 | return dp; | ||
453 | } | ||
454 | |||
455 | #ifndef BB_NOMMU | ||
456 | // Die with an error message if we can't daemonize. | ||
457 | void xdaemon(int nochdir, int noclose) | ||
458 | { | ||
459 | if (daemon(nochdir, noclose)) | ||
460 | bb_perror_msg_and_die("daemon"); | ||
461 | } | ||
462 | #endif | ||
463 | |||
464 | // Die with an error message if we can't open a new socket. | ||
465 | int xsocket(int domain, int type, int protocol) | ||
466 | { | ||
467 | int r = socket(domain, type, protocol); | ||
468 | |||
469 | if (r < 0) bb_perror_msg_and_die("socket"); | ||
470 | |||
471 | return r; | ||
472 | } | ||
473 | |||
474 | // Die with an error message if we can't bind a socket to an address. | ||
475 | void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) | ||
476 | { | ||
477 | if (bind(sockfd, my_addr, addrlen)) bb_perror_msg_and_die("bind"); | ||
478 | } | ||
479 | |||
480 | // Die with an error message if we can't listen for connections on a socket. | ||
481 | void xlisten(int s, int backlog) | ||
482 | { | ||
483 | if (listen(s, backlog)) bb_perror_msg_and_die("listen"); | ||
484 | } | ||
485 | |||
486 | // xstat() - a stat() which dies on failure with meaningful error message | ||
487 | void xstat(char *name, struct stat *stat_buf) | ||
488 | { | ||
489 | if (stat(name, stat_buf)) | ||
490 | bb_perror_msg_and_die("can't stat '%s'", name); | ||
491 | } | ||
492 | |||
493 | /* It is perfectly ok to pass in a NULL for either width or for | ||
494 | * height, in which case that value will not be set. */ | ||
495 | int get_terminal_width_height(int fd, int *width, int *height) | ||
496 | { | ||
497 | struct winsize win = { 0, 0, 0, 0 }; | ||
498 | int ret = ioctl(fd, TIOCGWINSZ, &win); | ||
499 | |||
500 | if (height) { | ||
501 | if (!win.ws_row) { | ||
502 | char *s = getenv("LINES"); | ||
503 | if (s) win.ws_row = atoi(s); | ||
504 | } | ||
505 | if (win.ws_row <= 1 || win.ws_row >= 30000) | ||
506 | win.ws_row = 24; | ||
507 | *height = (int) win.ws_row; | ||
508 | } | ||
509 | |||
510 | if (width) { | ||
511 | if (!win.ws_col) { | ||
512 | char *s = getenv("COLUMNS"); | ||
513 | if (s) win.ws_col = atoi(s); | ||
514 | } | ||
515 | if (win.ws_col <= 1 || win.ws_col >= 30000) | ||
516 | win.ws_col = 80; | ||
517 | *width = (int) win.ws_col; | ||
518 | } | ||
519 | |||
520 | return ret; | ||
521 | } | ||
diff --git a/libbb/xgetcwd.c b/libbb/xgetcwd.c new file mode 100644 index 000000000..0ac450d3b --- /dev/null +++ b/libbb/xgetcwd.c | |||
@@ -0,0 +1,44 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * xgetcwd.c -- return current directory with unlimited length | ||
4 | * Copyright (C) 1992, 1996 Free Software Foundation, Inc. | ||
5 | * Written by David MacKenzie <djm@gnu.ai.mit.edu>. | ||
6 | * | ||
7 | * Special function for busybox written by Vladimir Oleynik <dzo@simtreas.ru> | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | /* Amount to increase buffer size by in each try. */ | ||
13 | #define PATH_INCR 32 | ||
14 | |||
15 | /* Return the current directory, newly allocated, arbitrarily long. | ||
16 | Return NULL and set errno on error. | ||
17 | If argument is not NULL (previous usage allocate memory), call free() | ||
18 | */ | ||
19 | |||
20 | char * | ||
21 | xgetcwd(char *cwd) | ||
22 | { | ||
23 | char *ret; | ||
24 | unsigned path_max; | ||
25 | |||
26 | path_max = (unsigned) PATH_MAX; | ||
27 | path_max += 2; /* The getcwd docs say to do this. */ | ||
28 | |||
29 | if (cwd==0) | ||
30 | cwd = xmalloc(path_max); | ||
31 | |||
32 | while ((ret = getcwd(cwd, path_max)) == NULL && errno == ERANGE) { | ||
33 | path_max += PATH_INCR; | ||
34 | cwd = xrealloc(cwd, path_max); | ||
35 | } | ||
36 | |||
37 | if (ret == NULL) { | ||
38 | free(cwd); | ||
39 | bb_perror_msg("getcwd"); | ||
40 | return NULL; | ||
41 | } | ||
42 | |||
43 | return cwd; | ||
44 | } | ||
diff --git a/libbb/xgethostbyname.c b/libbb/xgethostbyname.c new file mode 100644 index 000000000..c3158c339 --- /dev/null +++ b/libbb/xgethostbyname.c | |||
@@ -0,0 +1,19 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini xgethostbyname implementation. | ||
4 | * | ||
5 | * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>. | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include <netdb.h> | ||
11 | #include "libbb.h" | ||
12 | |||
13 | struct hostent *xgethostbyname(const char *name) | ||
14 | { | ||
15 | struct hostent *retval = gethostbyname(name); | ||
16 | if (!retval) | ||
17 | bb_herror_msg_and_die("%s", name); | ||
18 | return retval; | ||
19 | } | ||
diff --git a/libbb/xgethostbyname2.c b/libbb/xgethostbyname2.c new file mode 100644 index 000000000..83d538669 --- /dev/null +++ b/libbb/xgethostbyname2.c | |||
@@ -0,0 +1,22 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini xgethostbyname2 implementation. | ||
4 | * | ||
5 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
6 | */ | ||
7 | |||
8 | #include <netdb.h> | ||
9 | #include "libbb.h" | ||
10 | |||
11 | |||
12 | #ifdef CONFIG_FEATURE_IPV6 | ||
13 | struct hostent *xgethostbyname2(const char *name, int af) | ||
14 | { | ||
15 | struct hostent *retval; | ||
16 | |||
17 | if ((retval = gethostbyname2(name, af)) == NULL) | ||
18 | bb_herror_msg_and_die("%s", name); | ||
19 | |||
20 | return retval; | ||
21 | } | ||
22 | #endif | ||
diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c new file mode 100644 index 000000000..14863d9d5 --- /dev/null +++ b/libbb/xreadlink.c | |||
@@ -0,0 +1,38 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * xreadlink.c - safe implementation of readlink. | ||
4 | * Returns a NULL on failure... | ||
5 | */ | ||
6 | |||
7 | #include <stdio.h> | ||
8 | |||
9 | /* | ||
10 | * NOTE: This function returns a malloced char* that you will have to free | ||
11 | * yourself. You have been warned. | ||
12 | */ | ||
13 | |||
14 | #include <unistd.h> | ||
15 | #include "libbb.h" | ||
16 | |||
17 | char *xreadlink(const char *path) | ||
18 | { | ||
19 | enum { GROWBY = 80 }; /* how large we will grow strings by */ | ||
20 | |||
21 | char *buf = NULL; | ||
22 | int bufsize = 0, readsize = 0; | ||
23 | |||
24 | do { | ||
25 | buf = xrealloc(buf, bufsize += GROWBY); | ||
26 | readsize = readlink(path, buf, bufsize); /* 1st try */ | ||
27 | if (readsize == -1) { | ||
28 | bb_perror_msg("%s", path); | ||
29 | free(buf); | ||
30 | return NULL; | ||
31 | } | ||
32 | } | ||
33 | while (bufsize < readsize + 1); | ||
34 | |||
35 | buf[readsize] = '\0'; | ||
36 | |||
37 | return buf; | ||
38 | } | ||
diff --git a/libbb/xregcomp.c b/libbb/xregcomp.c new file mode 100644 index 000000000..4bcb9aedf --- /dev/null +++ b/libbb/xregcomp.c | |||
@@ -0,0 +1,26 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) many different people. | ||
6 | * If you wrote this, please acknowledge your work. | ||
7 | * | ||
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
9 | */ | ||
10 | |||
11 | #include <stdio.h> | ||
12 | #include "libbb.h" | ||
13 | #include "xregex.h" | ||
14 | |||
15 | |||
16 | |||
17 | void xregcomp(regex_t *preg, const char *regex, int cflags) | ||
18 | { | ||
19 | int ret; | ||
20 | if ((ret = regcomp(preg, regex, cflags)) != 0) { | ||
21 | int errmsgsz = regerror(ret, preg, NULL, 0); | ||
22 | char *errmsg = xmalloc(errmsgsz); | ||
23 | regerror(ret, preg, errmsg, errmsgsz); | ||
24 | bb_error_msg_and_die("xregcomp: %s", errmsg); | ||
25 | } | ||
26 | } | ||