aboutsummaryrefslogtreecommitdiff
path: root/coreutils
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-06-12 08:12:33 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-06-12 08:12:33 +0000
commitcc5e090f12fb4e3834fb1a55bc91d7618af8ce78 (patch)
tree34813e8836287c21cb893ab7d3aee666db415d62 /coreutils
parentaa198dd39cad6cb41fbf6c8b64301b581a9ba206 (diff)
downloadbusybox-w32-cc5e090f12fb4e3834fb1a55bc91d7618af8ce78.tar.gz
busybox-w32-cc5e090f12fb4e3834fb1a55bc91d7618af8ce78.tar.bz2
busybox-w32-cc5e090f12fb4e3834fb1a55bc91d7618af8ce78.zip
move several applets to more correct ex-project. No code changes.
Diffstat (limited to 'coreutils')
-rw-r--r--coreutils/Config.in61
-rw-r--r--coreutils/Kbuild4
-rw-r--r--coreutils/cmp.c145
-rw-r--r--coreutils/diff.c1282
-rw-r--r--coreutils/readlink.c50
-rw-r--r--coreutils/watch.c80
6 files changed, 65 insertions, 1557 deletions
diff --git a/coreutils/Config.in b/coreutils/Config.in
index be5e9527b..cb5241ef6 100644
--- a/coreutils/Config.in
+++ b/coreutils/Config.in
@@ -65,13 +65,6 @@ config CKSUM
65 help 65 help
66 cksum is used to calculate the CRC32 checksum of a file. 66 cksum is used to calculate the CRC32 checksum of a file.
67 67
68config CMP
69 bool "cmp"
70 default n
71 help
72 cmp is used to compare two files and returns the result
73 to standard output.
74
75config COMM 68config COMM
76 bool "comm" 69 bool "comm"
77 default n 70 default n
@@ -142,38 +135,6 @@ config DF
142 df reports the amount of disk space used and available 135 df reports the amount of disk space used and available
143 on filesystems. 136 on filesystems.
144 137
145config DIFF
146 bool "diff"
147 default n
148 help
149 diff compares two files or directories and outputs the
150 differences between them in a form that can be given to
151 the patch command.
152
153config FEATURE_DIFF_BINARY
154 bool "Enable checks for binary files"
155 default y
156 depends on DIFF
157 help
158 This option enables support for checking for binary files
159 before a comparison is carried out.
160
161config FEATURE_DIFF_DIR
162 bool "Enable directory support"
163 default y
164 depends on DIFF
165 help
166 This option enables support for directory and subdirectory
167 comparison.
168
169config FEATURE_DIFF_MINIMAL
170 bool "Enable -d option to find smaller sets of changes"
171 default n
172 depends on DIFF
173 help
174 Enabling this option allows the use of -d to make diff
175 try hard to find the smallest possible set of changes.
176
177config DIRNAME 138config DIRNAME
178 bool "dirname" 139 bool "dirname"
179 default n 140 default n
@@ -474,6 +435,20 @@ config PWD
474 help 435 help
475 pwd is used to print the current directory. 436 pwd is used to print the current directory.
476 437
438config READLINK
439 bool "readlink"
440 default n
441 help
442 This program reads a symbolic link and returns the name
443 of the file it points to
444
445config FEATURE_READLINK_FOLLOW
446 bool "Enable canonicalization by following all symlinks (-f)"
447 default n
448 depends on READLINK
449 help
450 Enable the readlink option (-f).
451
477config REALPATH 452config REALPATH
478 bool "realpath" 453 bool "realpath"
479 default n 454 default n
@@ -704,14 +679,6 @@ config UUENCODE
704 help 679 help
705 uuencode is used to uuencode a file. 680 uuencode is used to uuencode a file.
706 681
707config WATCH
708 bool "watch"
709 default n
710 select DATE
711 help
712 watch is used to execute a program periodically, showing
713 output to the screen.
714
715config WC 682config WC
716 bool "wc" 683 bool "wc"
717 default n 684 default n
diff --git a/coreutils/Kbuild b/coreutils/Kbuild
index 1c6e6ed23..fd67d6c85 100644
--- a/coreutils/Kbuild
+++ b/coreutils/Kbuild
@@ -18,14 +18,12 @@ lib-$(CONFIG_CHMOD) += chmod.o
18lib-$(CONFIG_CHOWN) += chown.o 18lib-$(CONFIG_CHOWN) += chown.o
19lib-$(CONFIG_CHROOT) += chroot.o 19lib-$(CONFIG_CHROOT) += chroot.o
20lib-$(CONFIG_CKSUM) += cksum.o 20lib-$(CONFIG_CKSUM) += cksum.o
21lib-$(CONFIG_CMP) += cmp.o
22lib-$(CONFIG_COMM) += comm.o 21lib-$(CONFIG_COMM) += comm.o
23lib-$(CONFIG_CP) += cp.o 22lib-$(CONFIG_CP) += cp.o
24lib-$(CONFIG_CUT) += cut.o 23lib-$(CONFIG_CUT) += cut.o
25lib-$(CONFIG_DATE) += date.o 24lib-$(CONFIG_DATE) += date.o
26lib-$(CONFIG_DD) += dd.o 25lib-$(CONFIG_DD) += dd.o
27lib-$(CONFIG_DF) += df.o 26lib-$(CONFIG_DF) += df.o
28lib-$(CONFIG_DIFF) += diff.o
29lib-$(CONFIG_DIRNAME) += dirname.o 27lib-$(CONFIG_DIRNAME) += dirname.o
30lib-$(CONFIG_DOS2UNIX) += dos2unix.o 28lib-$(CONFIG_DOS2UNIX) += dos2unix.o
31lib-$(CONFIG_DU) += du.o 29lib-$(CONFIG_DU) += du.o
@@ -54,6 +52,7 @@ lib-$(CONFIG_OD) += od.o
54lib-$(CONFIG_PRINTENV) += printenv.o 52lib-$(CONFIG_PRINTENV) += printenv.o
55lib-$(CONFIG_PRINTF) += printf.o 53lib-$(CONFIG_PRINTF) += printf.o
56lib-$(CONFIG_PWD) += pwd.o 54lib-$(CONFIG_PWD) += pwd.o
55lib-$(CONFIG_READLINK) += readlink.o
57lib-$(CONFIG_REALPATH) += realpath.o 56lib-$(CONFIG_REALPATH) += realpath.o
58lib-$(CONFIG_RM) += rm.o 57lib-$(CONFIG_RM) += rm.o
59lib-$(CONFIG_RMDIR) += rmdir.o 58lib-$(CONFIG_RMDIR) += rmdir.o
@@ -79,7 +78,6 @@ lib-$(CONFIG_UNIQ) += uniq.o
79lib-$(CONFIG_USLEEP) += usleep.o 78lib-$(CONFIG_USLEEP) += usleep.o
80lib-$(CONFIG_UUDECODE) += uudecode.o 79lib-$(CONFIG_UUDECODE) += uudecode.o
81lib-$(CONFIG_UUENCODE) += uuencode.o 80lib-$(CONFIG_UUENCODE) += uuencode.o
82lib-$(CONFIG_WATCH) += watch.o
83lib-$(CONFIG_WC) += wc.o 81lib-$(CONFIG_WC) += wc.o
84lib-$(CONFIG_WHO) += who.o 82lib-$(CONFIG_WHO) += who.o
85lib-$(CONFIG_WHOAMI) += whoami.o 83lib-$(CONFIG_WHOAMI) += whoami.o
diff --git a/coreutils/cmp.c b/coreutils/cmp.c
deleted file mode 100644
index e5dda80ff..000000000
--- a/coreutils/cmp.c
+++ /dev/null
@@ -1,145 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini cmp implementation for busybox
4 *
5 * Copyright (C) 2000,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/* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */
11/* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */
12
13/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
14 *
15 * Original version majorly reworked for SUSv3 compliance, bug fixes, and
16 * size optimizations. Changes include:
17 * 1) Now correctly distinguishes between errors and actual file differences.
18 * 2) Proper handling of '-' args.
19 * 3) Actual error checking of i/o.
20 * 4) Accept SUSv3 -l option. Note that we use the slightly nicer gnu format
21 * in the '-l' case.
22 */
23
24#include "libbb.h"
25
26static FILE *cmp_xfopen_input(const char *filename)
27{
28 FILE *fp;
29
30 fp = fopen_or_warn_stdin(filename);
31 if (fp)
32 return fp;
33 xfunc_die(); /* We already output an error message. */
34}
35
36static const char fmt_eof[] = "cmp: EOF on %s\n";
37static const char fmt_differ[] = "%s %s differ: char %"OFF_FMT"d, line %d\n";
38// This fmt_l_opt uses gnu-isms. SUSv3 would be "%.0s%.0s%"OFF_FMT"d %o %o\n"
39static const char fmt_l_opt[] = "%.0s%.0s%"OFF_FMT"d %3o %3o\n";
40
41static const char opt_chars[] = "sl";
42#define CMP_OPT_s (1<<0)
43#define CMP_OPT_l (1<<1)
44
45int cmp_main(int argc, char **argv);
46int cmp_main(int argc, char **argv)
47{
48 FILE *fp1, *fp2, *outfile = stdout;
49 const char *filename1, *filename2 = "-";
50 USE_DESKTOP(off_t skip1 = 0, skip2 = 0;)
51 off_t char_pos = 0;
52 int line_pos = 1; /* Hopefully won't overflow... */
53 const char *fmt;
54 int c1, c2;
55 unsigned opt;
56 int retval = 0;
57
58 xfunc_error_retval = 2; /* 1 is returned if files are different. */
59
60 opt_complementary = "?:-1"
61 USE_DESKTOP(":?4")
62 SKIP_DESKTOP(":?2")
63 ":l--s:s--l";
64 opt = getopt32(argc, argv, opt_chars);
65 argv += optind;
66
67 filename1 = *argv;
68 fp1 = cmp_xfopen_input(filename1);
69
70 if (*++argv) {
71 filename2 = *argv;
72#if ENABLE_DESKTOP
73 if (*++argv) {
74 skip1 = XATOOFF(*argv);
75 if (*++argv) {
76 skip2 = XATOOFF(*argv);
77 }
78 }
79#endif
80 }
81
82 fp2 = cmp_xfopen_input(filename2);
83 if (fp1 == fp2) { /* Paranoia check... stdin == stdin? */
84 /* Note that we don't bother reading stdin. Neither does gnu wc.
85 * But perhaps we should, so that other apps down the chain don't
86 * get the input. Consider 'echo hello | (cmp - - && cat -)'.
87 */
88 return 0;
89 }
90
91 if (opt & CMP_OPT_l)
92 fmt = fmt_l_opt;
93 else
94 fmt = fmt_differ;
95
96#if ENABLE_DESKTOP
97 while (skip1) { getc(fp1); skip1--; }
98 while (skip2) { getc(fp2); skip2--; }
99#endif
100 do {
101 c1 = getc(fp1);
102 c2 = getc(fp2);
103 ++char_pos;
104 if (c1 != c2) { /* Remember: a read error may have occurred. */
105 retval = 1; /* But assume the files are different for now. */
106 if (c2 == EOF) {
107 /* We know that fp1 isn't at EOF or in an error state. But to
108 * save space below, things are setup to expect an EOF in fp1
109 * if an EOF occurred. So, swap things around.
110 */
111 fp1 = fp2;
112 filename1 = filename2;
113 c1 = c2;
114 }
115 if (c1 == EOF) {
116 die_if_ferror(fp1, filename1);
117 fmt = fmt_eof; /* Well, no error, so it must really be EOF. */
118 outfile = stderr;
119 /* There may have been output to stdout (option -l), so
120 * make sure we fflush before writing to stderr. */
121 xfflush_stdout();
122 }
123 if (!(opt & CMP_OPT_s)) {
124 if (opt & CMP_OPT_l) {
125 line_pos = c1; /* line_pos is unused in the -l case. */
126 }
127 fprintf(outfile, fmt, filename1, filename2, char_pos, line_pos, c2);
128 if (opt) { /* This must be -l since not -s. */
129 /* If we encountered an EOF,
130 * the while check will catch it. */
131 continue;
132 }
133 }
134 break;
135 }
136 if (c1 == '\n') {
137 ++line_pos;
138 }
139 } while (c1 != EOF);
140
141 die_if_ferror(fp1, filename1);
142 die_if_ferror(fp2, filename2);
143
144 fflush_stdout_and_exit(retval);
145}
diff --git a/coreutils/diff.c b/coreutils/diff.c
deleted file mode 100644
index 830c15ea6..000000000
--- a/coreutils/diff.c
+++ /dev/null
@@ -1,1282 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini diff implementation for busybox, adapted from OpenBSD diff.
4 *
5 * Copyright (C) 2006 by Robert Sullivan <cogito.ergo.cogito@hotmail.com>
6 * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
7 *
8 * Sponsored in part by the Defense Advanced Research Projects
9 * Agency (DARPA) and Air Force Research Laboratory, Air Force
10 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
11 *
12 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
13 */
14
15#include "libbb.h"
16
17#define FSIZE_MAX 32768
18
19/*
20 * Output flags
21 */
22#define D_HEADER 1 /* Print a header/footer between files */
23#define D_EMPTY1 2 /* Treat first file as empty (/dev/null) */
24#define D_EMPTY2 4 /* Treat second file as empty (/dev/null) */
25
26/*
27 * Status values for print_status() and diffreg() return values
28 * Guide:
29 * D_SAME - files are the same
30 * D_DIFFER - files differ
31 * D_BINARY - binary files differ
32 * D_COMMON - subdirectory common to both dirs
33 * D_ONLY - file only exists in one dir
34 * D_MISMATCH1 - path1 a dir, path2 a file
35 * D_MISMATCH2 - path1 a file, path2 a dir
36 * D_ERROR - error occurred
37 * D_SKIPPED1 - skipped path1 as it is a special file
38 * D_SKIPPED2 - skipped path2 as it is a special file
39 */
40
41#define D_SAME 0
42#define D_DIFFER (1<<0)
43#define D_BINARY (1<<1)
44#define D_COMMON (1<<2)
45#define D_ONLY (1<<3)
46#define D_MISMATCH1 (1<<4)
47#define D_MISMATCH2 (1<<5)
48#define D_ERROR (1<<6)
49#define D_SKIPPED1 (1<<7)
50#define D_SKIPPED2 (1<<8)
51
52/* Command line options */
53#define FLAG_a (1<<0)
54#define FLAG_b (1<<1)
55#define FLAG_d (1<<2)
56#define FLAG_i (1<<3)
57#define FLAG_L (1<<4)
58#define FLAG_N (1<<5)
59#define FLAG_q (1<<6)
60#define FLAG_r (1<<7)
61#define FLAG_s (1<<8)
62#define FLAG_S (1<<9)
63#define FLAG_t (1<<10)
64#define FLAG_T (1<<11)
65#define FLAG_U (1<<12)
66#define FLAG_w (1<<13)
67
68struct cand {
69 int x;
70 int y;
71 int pred;
72};
73
74struct line {
75 int serial;
76 int value;
77};
78
79/*
80 * The following struct is used to record change information
81 * doing a "context" or "unified" diff. (see routine "change" to
82 * understand the highly mnemonic field names)
83 */
84struct context_vec {
85 int a; /* start line in old file */
86 int b; /* end line in old file */
87 int c; /* start line in new file */
88 int d; /* end line in new file */
89};
90
91struct globals {
92 USE_FEATURE_DIFF_DIR(char **dl;)
93 USE_FEATURE_DIFF_DIR(int dl_count;)
94 /* This is the default number of lines of context. */
95 int context;
96 size_t max_context;
97 int status;
98 char *start;
99 const char *label1;
100 const char *label2;
101 struct line *file[2];
102 int *J; /* will be overlaid on class */
103 int *class; /* will be overlaid on file[0] */
104 int *klist; /* will be overlaid on file[0] after class */
105 int *member; /* will be overlaid on file[1] */
106 int clen;
107 int len[2];
108 int pref, suff; /* length of prefix and suffix */
109 int slen[2];
110 bool anychange;
111 long *ixnew; /* will be overlaid on file[1] */
112 long *ixold; /* will be overlaid on klist */
113 struct cand *clist; /* merely a free storage pot for candidates */
114 int clistlen; /* the length of clist */
115 struct line *sfile[2]; /* shortened by pruning common prefix/suffix */
116 struct context_vec *context_vec_start;
117 struct context_vec *context_vec_end;
118 struct context_vec *context_vec_ptr;
119 struct stat stb1, stb2;
120};
121#define G (*ptr_to_globals)
122#define dl (G.dl )
123#define dl_count (G.dl_count )
124#define context (G.context )
125#define max_context (G.max_context )
126#define status (G.status )
127#define start (G.start )
128#define label1 (G.label1 )
129#define label2 (G.label2 )
130#define file (G.file )
131#define J (G.J )
132#define class (G.class )
133#define klist (G.klist )
134#define member (G.member )
135#define clen (G.clen )
136#define len (G.len )
137#define pref (G.pref )
138#define suff (G.suff )
139#define slen (G.slen )
140#define anychange (G.anychange )
141#define ixnew (G.ixnew )
142#define ixold (G.ixold )
143#define clist (G.clist )
144#define clistlen (G.clistlen )
145#define sfile (G.sfile )
146#define context_vec_start (G.context_vec_start )
147#define context_vec_end (G.context_vec_end )
148#define context_vec_ptr (G.context_vec_ptr )
149#define stb1 (G.stb1 )
150#define stb2 (G.stb2 )
151#define INIT_G() do { \
152 PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
153 context = 3; \
154 max_context = 64; \
155} while (0)
156
157
158static void print_only(const char *path, size_t dirlen, const char *entry)
159{
160 if (dirlen > 1)
161 dirlen--;
162 printf("Only in %.*s: %s\n", (int) dirlen, path, entry);
163}
164
165static void print_status(int val, char *path1, char *path2, char *entry)
166{
167 const char * const _entry = entry ? entry : "";
168 char * const _path1 = entry ? concat_path_file(path1, _entry) : path1;
169 char * const _path2 = entry ? concat_path_file(path2, _entry) : path2;
170
171 switch (val) {
172 case D_ONLY:
173 print_only(path1, strlen(path1), entry);
174 break;
175 case D_COMMON:
176 printf("Common subdirectories: %s and %s\n", _path1, _path2);
177 break;
178 case D_BINARY:
179 printf("Binary files %s and %s differ\n", _path1, _path2);
180 break;
181 case D_DIFFER:
182 if (option_mask32 & FLAG_q)
183 printf("Files %s and %s differ\n", _path1, _path2);
184 break;
185 case D_SAME:
186 if (option_mask32 & FLAG_s)
187 printf("Files %s and %s are identical\n", _path1, _path2);
188 break;
189 case D_MISMATCH1:
190 printf("File %s is a %s while file %s is a %s\n",
191 _path1, "directory", _path2, "regular file");
192 break;
193 case D_MISMATCH2:
194 printf("File %s is a %s while file %s is a %s\n",
195 _path1, "regular file", _path2, "directory");
196 break;
197 case D_SKIPPED1:
198 printf("File %s is not a regular file or directory and was skipped\n",
199 _path1);
200 break;
201 case D_SKIPPED2:
202 printf("File %s is not a regular file or directory and was skipped\n",
203 _path2);
204 break;
205 }
206 if (entry) {
207 free(_path1);
208 free(_path2);
209 }
210}
211static void fiddle_sum(int *sum, int t)
212{
213 *sum = (int)(*sum * 127 + t);
214}
215/*
216 * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
217 */
218static int readhash(FILE * f)
219{
220 int i, t, space;
221 int sum;
222
223 sum = 1;
224 space = 0;
225 if (!(option_mask32 & (FLAG_b | FLAG_w))) {
226 for (i = 0; (t = getc(f)) != '\n'; i++) {
227 if (t == EOF) {
228 if (i == 0)
229 return 0;
230 break;
231 }
232 fiddle_sum(&sum, t);
233 }
234 } else {
235 for (i = 0;;) {
236 switch (t = getc(f)) {
237 case '\t':
238 case '\r':
239 case '\v':
240 case '\f':
241 case ' ':
242 space++;
243 continue;
244 default:
245 if (space && !(option_mask32 & FLAG_w)) {
246 i++;
247 space = 0;
248 }
249 fiddle_sum(&sum, t);
250 i++;
251 continue;
252 case EOF:
253 if (i == 0)
254 return 0;
255 /* FALLTHROUGH */
256 case '\n':
257 break;
258 }
259 break;
260 }
261 }
262 /*
263 * There is a remote possibility that we end up with a zero sum.
264 * Zero is used as an EOF marker, so return 1 instead.
265 */
266 return (sum == 0 ? 1 : sum);
267}
268
269
270/*
271 * Check to see if the given files differ.
272 * Returns 0 if they are the same, 1 if different, and -1 on error.
273 */
274static int files_differ(FILE * f1, FILE * f2, int flags)
275{
276 size_t i, j;
277
278 if ((flags & (D_EMPTY1 | D_EMPTY2)) || stb1.st_size != stb2.st_size
279 || (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)
280 ) {
281 return 1;
282 }
283 while (1) {
284 i = fread(bb_common_bufsiz1, 1, BUFSIZ/2, f1);
285 j = fread(bb_common_bufsiz1 + BUFSIZ/2, 1, BUFSIZ/2, f2);
286 if (i != j)
287 return 1;
288 if (i == 0)
289 return (ferror(f1) || ferror(f2));
290 if (memcmp(bb_common_bufsiz1,
291 bb_common_bufsiz1 + BUFSIZ/2, i) != 0)
292 return 1;
293 }
294}
295
296
297static void prepare(int i, FILE * fd, off_t filesize)
298{
299 struct line *p;
300 int h;
301 size_t j, sz;
302
303 rewind(fd);
304
305 sz = (filesize <= FSIZE_MAX ? filesize : FSIZE_MAX) / 25;
306 if (sz < 100)
307 sz = 100;
308
309 p = xmalloc((sz + 3) * sizeof(struct line));
310 j = 0;
311 while ((h = readhash(fd))) {
312 if (j == sz) {
313 sz = sz * 3 / 2;
314 p = xrealloc(p, (sz + 3) * sizeof(struct line));
315 }
316 p[++j].value = h;
317 }
318 len[i] = j;
319 file[i] = p;
320}
321
322
323static void prune(void)
324{
325 int i, j;
326
327 for (pref = 0; pref < len[0] && pref < len[1] &&
328 file[0][pref + 1].value == file[1][pref + 1].value; pref++)
329 ;
330 for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
331 file[0][len[0] - suff].value == file[1][len[1] - suff].value;
332 suff++)
333 ;
334 for (j = 0; j < 2; j++) {
335 sfile[j] = file[j] + pref;
336 slen[j] = len[j] - pref - suff;
337 for (i = 0; i <= slen[j]; i++)
338 sfile[j][i].serial = i;
339 }
340}
341
342
343static void equiv(struct line *a, int n, struct line *b, int m, int *c)
344{
345 int i, j;
346
347 i = j = 1;
348 while (i <= n && j <= m) {
349 if (a[i].value < b[j].value)
350 a[i++].value = 0;
351 else if (a[i].value == b[j].value)
352 a[i++].value = j;
353 else
354 j++;
355 }
356 while (i <= n)
357 a[i++].value = 0;
358 b[m + 1].value = 0;
359 j = 0;
360 while (++j <= m) {
361 c[j] = -b[j].serial;
362 while (b[j + 1].value == b[j].value) {
363 j++;
364 c[j] = b[j].serial;
365 }
366 }
367 c[j] = -1;
368}
369
370
371static int isqrt(int n)
372{
373 int y, x;
374
375 if (n == 0)
376 return 0;
377 x = 1;
378 do {
379 y = x;
380 x = n / x;
381 x += y;
382 x /= 2;
383 } while ((x - y) > 1 || (x - y) < -1);
384
385 return x;
386}
387
388
389static int newcand(int x, int y, int pred)
390{
391 struct cand *q;
392
393 if (clen == clistlen) {
394 clistlen = clistlen * 11 / 10;
395 clist = xrealloc(clist, clistlen * sizeof(struct cand));
396 }
397 q = clist + clen;
398 q->x = x;
399 q->y = y;
400 q->pred = pred;
401 return clen++;
402}
403
404
405static int search(int *c, int k, int y)
406{
407 int i, j, l, t;
408
409 if (clist[c[k]].y < y) /* quick look for typical case */
410 return k + 1;
411 i = 0;
412 j = k + 1;
413 while (1) {
414 l = i + j;
415 if ((l >>= 1) <= i)
416 break;
417 t = clist[c[l]].y;
418 if (t > y)
419 j = l;
420 else if (t < y)
421 i = l;
422 else
423 return l;
424 }
425 return l + 1;
426}
427
428
429static int stone(int *a, int n, int *b, int *c)
430{
431 int i, k, y, j, l;
432 int oldc, tc, oldl;
433 unsigned int numtries;
434
435#if ENABLE_FEATURE_DIFF_MINIMAL
436 const unsigned int bound =
437 (option_mask32 & FLAG_d) ? UINT_MAX : MAX(256, isqrt(n));
438#else
439 const unsigned int bound = MAX(256, isqrt(n));
440#endif
441 k = 0;
442 c[0] = newcand(0, 0, 0);
443 for (i = 1; i <= n; i++) {
444 j = a[i];
445 if (j == 0)
446 continue;
447 y = -b[j];
448 oldl = 0;
449 oldc = c[0];
450 numtries = 0;
451 do {
452 if (y <= clist[oldc].y)
453 continue;
454 l = search(c, k, y);
455 if (l != oldl + 1)
456 oldc = c[l - 1];
457 if (l <= k) {
458 if (clist[c[l]].y <= y)
459 continue;
460 tc = c[l];
461 c[l] = newcand(i, y, oldc);
462 oldc = tc;
463 oldl = l;
464 numtries++;
465 } else {
466 c[l] = newcand(i, y, oldc);
467 k++;
468 break;
469 }
470 } while ((y = b[++j]) > 0 && numtries < bound);
471 }
472 return k;
473}
474
475
476static void unravel(int p)
477{
478 struct cand *q;
479 int i;
480
481 for (i = 0; i <= len[0]; i++)
482 J[i] = i <= pref ? i : i > len[0] - suff ? i + len[1] - len[0] : 0;
483 for (q = clist + p; q->y != 0; q = clist + q->pred)
484 J[q->x + pref] = q->y + pref;
485}
486
487
488static void unsort(struct line *f, int l, int *b)
489{
490 int *a, i;
491
492 a = xmalloc((l + 1) * sizeof(int));
493 for (i = 1; i <= l; i++)
494 a[f[i].serial] = f[i].value;
495 for (i = 1; i <= l; i++)
496 b[i] = a[i];
497 free(a);
498}
499
500
501static int skipline(FILE * f)
502{
503 int i, c;
504
505 for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
506 continue;
507 return i;
508}
509
510
511/*
512 * Check does double duty:
513 * 1. ferret out any fortuitous correspondences due
514 * to confounding by hashing (which result in "jackpot")
515 * 2. collect random access indexes to the two files
516 */
517static void check(FILE * f1, FILE * f2)
518{
519 int i, j, jackpot, c, d;
520 long ctold, ctnew;
521
522 rewind(f1);
523 rewind(f2);
524 j = 1;
525 ixold[0] = ixnew[0] = 0;
526 jackpot = 0;
527 ctold = ctnew = 0;
528 for (i = 1; i <= len[0]; i++) {
529 if (J[i] == 0) {
530 ixold[i] = ctold += skipline(f1);
531 continue;
532 }
533 while (j < J[i]) {
534 ixnew[j] = ctnew += skipline(f2);
535 j++;
536 }
537 if ((option_mask32 & FLAG_b) || (option_mask32 & FLAG_w)
538 || (option_mask32 & FLAG_i)) {
539 while (1) {
540 c = getc(f1);
541 d = getc(f2);
542 /*
543 * GNU diff ignores a missing newline
544 * in one file if bflag || wflag.
545 */
546 if (((option_mask32 & FLAG_b) || (option_mask32 & FLAG_w)) &&
547 ((c == EOF && d == '\n') || (c == '\n' && d == EOF))) {
548 break;
549 }
550 ctold++;
551 ctnew++;
552 if ((option_mask32 & FLAG_b) && isspace(c) && isspace(d)) {
553 do {
554 if (c == '\n')
555 break;
556 ctold++;
557 } while (isspace(c = getc(f1)));
558 do {
559 if (d == '\n')
560 break;
561 ctnew++;
562 } while (isspace(d = getc(f2)));
563 } else if (option_mask32 & FLAG_w) {
564 while (isspace(c) && c != '\n') {
565 c = getc(f1);
566 ctold++;
567 }
568 while (isspace(d) && d != '\n') {
569 d = getc(f2);
570 ctnew++;
571 }
572 }
573 if (c != d) {
574 jackpot++;
575 J[i] = 0;
576 if (c != '\n' && c != EOF)
577 ctold += skipline(f1);
578 if (d != '\n' && c != EOF)
579 ctnew += skipline(f2);
580 break;
581 }
582 if (c == '\n' || c == EOF)
583 break;
584 }
585 } else {
586 while (1) {
587 ctold++;
588 ctnew++;
589 if ((c = getc(f1)) != (d = getc(f2))) {
590 J[i] = 0;
591 if (c != '\n' && c != EOF)
592 ctold += skipline(f1);
593 if (d != '\n' && c != EOF)
594 ctnew += skipline(f2);
595 break;
596 }
597 if (c == '\n' || c == EOF)
598 break;
599 }
600 }
601 ixold[i] = ctold;
602 ixnew[j] = ctnew;
603 j++;
604 }
605 for (; j <= len[1]; j++)
606 ixnew[j] = ctnew += skipline(f2);
607}
608
609
610/* shellsort CACM #201 */
611static void sort(struct line *a, int n)
612{
613 struct line *ai, *aim, w;
614 int j, m = 0, k;
615
616 if (n == 0)
617 return;
618 for (j = 1; j <= n; j *= 2)
619 m = 2 * j - 1;
620 for (m /= 2; m != 0; m /= 2) {
621 k = n - m;
622 for (j = 1; j <= k; j++) {
623 for (ai = &a[j]; ai > a; ai -= m) {
624 aim = &ai[m];
625 if (aim < ai)
626 break; /* wraparound */
627 if (aim->value > ai[0].value ||
628 (aim->value == ai[0].value && aim->serial > ai[0].serial))
629 break;
630 w.value = ai[0].value;
631 ai[0].value = aim->value;
632 aim->value = w.value;
633 w.serial = ai[0].serial;
634 ai[0].serial = aim->serial;
635 aim->serial = w.serial;
636 }
637 }
638 }
639}
640
641
642static void uni_range(int a, int b)
643{
644 if (a < b)
645 printf("%d,%d", a, b - a + 1);
646 else if (a == b)
647 printf("%d", b);
648 else
649 printf("%d,0", b);
650}
651
652
653static void fetch(long *f, int a, int b, FILE * lb, int ch)
654{
655 int i, j, c, lastc, col, nc;
656
657 if (a > b)
658 return;
659 for (i = a; i <= b; i++) {
660 fseek(lb, f[i - 1], SEEK_SET);
661 nc = f[i] - f[i - 1];
662 if (ch != '\0') {
663 putchar(ch);
664 if (option_mask32 & FLAG_T)
665 putchar('\t');
666 }
667 col = 0;
668 for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
669 if ((c = getc(lb)) == EOF) {
670 printf("\n\\ No newline at end of file\n");
671 return;
672 }
673 if (c == '\t' && (option_mask32 & FLAG_t)) {
674 do {
675 putchar(' ');
676 } while (++col & 7);
677 } else {
678 putchar(c);
679 col++;
680 }
681 }
682 }
683}
684
685
686static int asciifile(FILE * f)
687{
688#if ENABLE_FEATURE_DIFF_BINARY
689 int i, cnt;
690#endif
691
692 if ((option_mask32 & FLAG_a) || f == NULL)
693 return 1;
694
695#if ENABLE_FEATURE_DIFF_BINARY
696 rewind(f);
697 cnt = fread(bb_common_bufsiz1, 1, BUFSIZ, f);
698 for (i = 0; i < cnt; i++) {
699 if (!isprint(bb_common_bufsiz1[i])
700 && !isspace(bb_common_bufsiz1[i])) {
701 return 0;
702 }
703 }
704#endif
705 return 1;
706}
707
708
709/* dump accumulated "unified" diff changes */
710static void dump_unified_vec(FILE * f1, FILE * f2)
711{
712 struct context_vec *cvp = context_vec_start;
713 int lowa, upb, lowc, upd;
714 int a, b, c, d;
715 char ch;
716
717 if (context_vec_start > context_vec_ptr)
718 return;
719
720 b = d = 0; /* gcc */
721 lowa = MAX(1, cvp->a - context);
722 upb = MIN(len[0], context_vec_ptr->b + context);
723 lowc = MAX(1, cvp->c - context);
724 upd = MIN(len[1], context_vec_ptr->d + context);
725
726 printf("@@ -");
727 uni_range(lowa, upb);
728 printf(" +");
729 uni_range(lowc, upd);
730 printf(" @@\n");
731
732 /*
733 * Output changes in "unified" diff format--the old and new lines
734 * are printed together.
735 */
736 for (; cvp <= context_vec_ptr; cvp++) {
737 a = cvp->a;
738 b = cvp->b;
739 c = cvp->c;
740 d = cvp->d;
741
742 /*
743 * c: both new and old changes
744 * d: only changes in the old file
745 * a: only changes in the new file
746 */
747 if (a <= b && c <= d)
748 ch = 'c';
749 else
750 ch = (a <= b) ? 'd' : 'a';
751#if 0
752 switch (ch) {
753 case 'c':
754 fetch(ixold, lowa, a - 1, f1, ' ');
755 fetch(ixold, a, b, f1, '-');
756 fetch(ixnew, c, d, f2, '+');
757 break;
758 case 'd':
759 fetch(ixold, lowa, a - 1, f1, ' ');
760 fetch(ixold, a, b, f1, '-');
761 break;
762 case 'a':
763 fetch(ixnew, lowc, c - 1, f2, ' ');
764 fetch(ixnew, c, d, f2, '+');
765 break;
766 }
767#else
768 if (ch == 'c' || ch == 'd') {
769 fetch(ixold, lowa, a - 1, f1, ' ');
770 fetch(ixold, a, b, f1, '-');
771 }
772 if (ch == 'a')
773 fetch(ixnew, lowc, c - 1, f2, ' ');
774 if (ch == 'c' || ch == 'a')
775 fetch(ixnew, c, d, f2, '+');
776#endif
777 lowa = b + 1;
778 lowc = d + 1;
779 }
780 fetch(ixnew, d + 1, upd, f2, ' ');
781
782 context_vec_ptr = context_vec_start - 1;
783}
784
785
786static void print_header(const char *file1, const char *file2)
787{
788 if (label1)
789 printf("--- %s\n", label1);
790 else
791 printf("--- %s\t%s", file1, ctime(&stb1.st_mtime));
792 if (label2)
793 printf("+++ %s\n", label2);
794 else
795 printf("+++ %s\t%s", file2, ctime(&stb2.st_mtime));
796}
797
798
799/*
800 * Indicate that there is a difference between lines a and b of the from file
801 * to get to lines c to d of the to file. If a is greater than b then there
802 * are no lines in the from file involved and this means that there were
803 * lines appended (beginning at b). If c is greater than d then there are
804 * lines missing from the to file.
805 */
806static void change(char *file1, FILE * f1, char *file2, FILE * f2, int a,
807 int b, int c, int d)
808{
809 if ((a > b && c > d) || (option_mask32 & FLAG_q)) {
810 anychange = 1;
811 return;
812 }
813
814 /*
815 * Allocate change records as needed.
816 */
817 if (context_vec_ptr == context_vec_end - 1) {
818 ptrdiff_t offset = context_vec_ptr - context_vec_start;
819
820 max_context <<= 1;
821 context_vec_start = xrealloc(context_vec_start,
822 max_context * sizeof(struct context_vec));
823 context_vec_end = context_vec_start + max_context;
824 context_vec_ptr = context_vec_start + offset;
825 }
826 if (anychange == 0) {
827 /*
828 * Print the context/unidiff header first time through.
829 */
830 print_header(file1, file2);
831 } else if (a > context_vec_ptr->b + (2 * context) + 1 &&
832 c > context_vec_ptr->d + (2 * context) + 1) {
833 /*
834 * If this change is more than 'context' lines from the
835 * previous change, dump the record and reset it.
836 */
837 dump_unified_vec(f1, f2);
838 }
839 context_vec_ptr++;
840 context_vec_ptr->a = a;
841 context_vec_ptr->b = b;
842 context_vec_ptr->c = c;
843 context_vec_ptr->d = d;
844 anychange = 1;
845}
846
847
848static void output(char *file1, FILE * f1, char *file2, FILE * f2)
849{
850 /* Note that j0 and j1 can't be used as they are defined in math.h.
851 * This also allows the rather amusing variable 'j00'... */
852 int m, i0, i1, j00, j01;
853
854 rewind(f1);
855 rewind(f2);
856 m = len[0];
857 J[0] = 0;
858 J[m + 1] = len[1] + 1;
859 for (i0 = 1; i0 <= m; i0 = i1 + 1) {
860 while (i0 <= m && J[i0] == J[i0 - 1] + 1)
861 i0++;
862 j00 = J[i0 - 1] + 1;
863 i1 = i0 - 1;
864 while (i1 < m && J[i1 + 1] == 0)
865 i1++;
866 j01 = J[i1 + 1] - 1;
867 J[i1] = j01;
868 change(file1, f1, file2, f2, i0, i1, j00, j01);
869 }
870 if (m == 0) {
871 change(file1, f1, file2, f2, 1, 0, 1, len[1]);
872 }
873 if (anychange != 0 && !(option_mask32 & FLAG_q)) {
874 dump_unified_vec(f1, f2);
875 }
876}
877
878/*
879 * The following code uses an algorithm due to Harold Stone,
880 * which finds a pair of longest identical subsequences in
881 * the two files.
882 *
883 * The major goal is to generate the match vector J.
884 * J[i] is the index of the line in file1 corresponding
885 * to line i file0. J[i] = 0 if there is no
886 * such line in file1.
887 *
888 * Lines are hashed so as to work in core. All potential
889 * matches are located by sorting the lines of each file
890 * on the hash (called ``value''). In particular, this
891 * collects the equivalence classes in file1 together.
892 * Subroutine equiv replaces the value of each line in
893 * file0 by the index of the first element of its
894 * matching equivalence in (the reordered) file1.
895 * To save space equiv squeezes file1 into a single
896 * array member in which the equivalence classes
897 * are simply concatenated, except that their first
898 * members are flagged by changing sign.
899 *
900 * Next the indices that point into member are unsorted into
901 * array class according to the original order of file0.
902 *
903 * The cleverness lies in routine stone. This marches
904 * through the lines of file0, developing a vector klist
905 * of "k-candidates". At step i a k-candidate is a matched
906 * pair of lines x,y (x in file0 y in file1) such that
907 * there is a common subsequence of length k
908 * between the first i lines of file0 and the first y
909 * lines of file1, but there is no such subsequence for
910 * any smaller y. x is the earliest possible mate to y
911 * that occurs in such a subsequence.
912 *
913 * Whenever any of the members of the equivalence class of
914 * lines in file1 matable to a line in file0 has serial number
915 * less than the y of some k-candidate, that k-candidate
916 * with the smallest such y is replaced. The new
917 * k-candidate is chained (via pred) to the current
918 * k-1 candidate so that the actual subsequence can
919 * be recovered. When a member has serial number greater
920 * that the y of all k-candidates, the klist is extended.
921 * At the end, the longest subsequence is pulled out
922 * and placed in the array J by unravel
923 *
924 * With J in hand, the matches there recorded are
925 * checked against reality to assure that no spurious
926 * matches have crept in due to hashing. If they have,
927 * they are broken, and "jackpot" is recorded--a harmless
928 * matter except that a true match for a spuriously
929 * mated line may now be unnecessarily reported as a change.
930 *
931 * Much of the complexity of the program comes simply
932 * from trying to minimize core utilization and
933 * maximize the range of doable problems by dynamically
934 * allocating what is needed and reusing what is not.
935 * The core requirements for problems larger than somewhat
936 * are (in words) 2*length(file0) + length(file1) +
937 * 3*(number of k-candidates installed), typically about
938 * 6n words for files of length n.
939 */
940static unsigned diffreg(char * ofile1, char * ofile2, int flags)
941{
942 char *file1 = ofile1;
943 char *file2 = ofile2;
944 FILE *f1 = stdin, *f2 = stdin;
945 unsigned rval;
946 int i;
947
948 anychange = 0;
949 context_vec_ptr = context_vec_start - 1;
950
951 if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
952 return (S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2);
953
954 rval = D_SAME;
955
956 if (LONE_DASH(file1) && LONE_DASH(file2))
957 goto closem;
958
959 if (flags & D_EMPTY1)
960 f1 = xfopen(bb_dev_null, "r");
961 else if (NOT_LONE_DASH(file1))
962 f1 = xfopen(file1, "r");
963 if (flags & D_EMPTY2)
964 f2 = xfopen(bb_dev_null, "r");
965 else if (NOT_LONE_DASH(file2))
966 f2 = xfopen(file2, "r");
967
968/* We can't diff non-seekable stream - we use rewind(), fseek().
969 * This can be fixed (volunteers?).
970 * Meanwhile we should check it here by stat'ing input fds,
971 * but I am lazy and check that in main() instead.
972 * Check in main won't catch "diffing fifos buried in subdirectories"
973 * failure scenario - not very likely in real life... */
974
975 i = files_differ(f1, f2, flags);
976 if (i == 0)
977 goto closem;
978 else if (i != 1) { /* 1 == ok */
979 /* error */
980 status |= 2;
981 goto closem;
982 }
983
984 if (!asciifile(f1) || !asciifile(f2)) {
985 rval = D_BINARY;
986 status |= 1;
987 goto closem;
988 }
989
990 prepare(0, f1, stb1.st_size);
991 prepare(1, f2, stb2.st_size);
992 prune();
993 sort(sfile[0], slen[0]);
994 sort(sfile[1], slen[1]);
995
996 member = (int *) file[1];
997 equiv(sfile[0], slen[0], sfile[1], slen[1], member);
998 member = xrealloc(member, (slen[1] + 2) * sizeof(int));
999
1000 class = (int *) file[0];
1001 unsort(sfile[0], slen[0], class);
1002 class = xrealloc(class, (slen[0] + 2) * sizeof(int));
1003
1004 klist = xmalloc((slen[0] + 2) * sizeof(int));
1005 clen = 0;
1006 clistlen = 100;
1007 clist = xmalloc(clistlen * sizeof(struct cand));
1008 i = stone(class, slen[0], member, klist);
1009 free(member);
1010 free(class);
1011
1012 J = xrealloc(J, (len[0] + 2) * sizeof(int));
1013 unravel(klist[i]);
1014 free(clist);
1015 free(klist);
1016
1017 ixold = xrealloc(ixold, (len[0] + 2) * sizeof(long));
1018 ixnew = xrealloc(ixnew, (len[1] + 2) * sizeof(long));
1019 check(f1, f2);
1020 output(file1, f1, file2, f2);
1021
1022 closem:
1023 if (anychange) {
1024 status |= 1;
1025 if (rval == D_SAME)
1026 rval = D_DIFFER;
1027 }
1028 fclose_if_not_stdin(f1);
1029 fclose_if_not_stdin(f2);
1030 if (file1 != ofile1)
1031 free(file1);
1032 if (file2 != ofile2)
1033 free(file2);
1034 return rval;
1035}
1036
1037
1038#if ENABLE_FEATURE_DIFF_DIR
1039static void do_diff(char *dir1, char *path1, char *dir2, char *path2)
1040{
1041 int flags = D_HEADER;
1042 int val;
1043 char *fullpath1 = NULL; /* if -N */
1044 char *fullpath2 = NULL;
1045
1046 if (path1)
1047 fullpath1 = concat_path_file(dir1, path1);
1048 if (path2)
1049 fullpath2 = concat_path_file(dir2, path2);
1050
1051 if (!fullpath1 || stat(fullpath1, &stb1) != 0) {
1052 flags |= D_EMPTY1;
1053 memset(&stb1, 0, sizeof(stb1));
1054 if (path2) {
1055 free(fullpath1);
1056 fullpath1 = concat_path_file(dir1, path2);
1057 }
1058 }
1059 if (!fullpath2 || stat(fullpath2, &stb2) != 0) {
1060 flags |= D_EMPTY2;
1061 memset(&stb2, 0, sizeof(stb2));
1062 stb2.st_mode = stb1.st_mode;
1063 if (path1) {
1064 free(fullpath2);
1065 fullpath2 = concat_path_file(dir2, path1);
1066 }
1067 }
1068
1069 if (stb1.st_mode == 0)
1070 stb1.st_mode = stb2.st_mode;
1071
1072 if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
1073 printf("Common subdirectories: %s and %s\n", fullpath1, fullpath2);
1074 goto ret;
1075 }
1076
1077 if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
1078 val = D_SKIPPED1;
1079 else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
1080 val = D_SKIPPED2;
1081 else
1082 val = diffreg(fullpath1, fullpath2, flags);
1083
1084 print_status(val, fullpath1, fullpath2, NULL);
1085 ret:
1086 free(fullpath1);
1087 free(fullpath2);
1088}
1089#endif
1090
1091
1092#if ENABLE_FEATURE_DIFF_DIR
1093static int dir_strcmp(const void *p1, const void *p2)
1094{
1095 return strcmp(*(char *const *) p1, *(char *const *) p2);
1096}
1097
1098
1099/* This function adds a filename to dl, the directory listing. */
1100static int add_to_dirlist(const char *filename,
1101 struct stat ATTRIBUTE_UNUSED * sb, void *userdata,
1102 int depth ATTRIBUTE_UNUSED)
1103{
1104 /* +2: with space for eventual trailing NULL */
1105 dl = xrealloc(dl, (dl_count+2) * sizeof(dl[0]));
1106 dl[dl_count] = xstrdup(filename + (int)(ptrdiff_t)userdata);
1107 dl_count++;
1108 return TRUE;
1109}
1110
1111
1112/* This returns a sorted directory listing. */
1113static char **get_dir(char *path)
1114{
1115 dl_count = 0;
1116 dl = xzalloc(sizeof(dl[0]));
1117
1118 /* If -r has been set, then the recursive_action function will be
1119 * used. Unfortunately, this outputs the root directory along with
1120 * the recursed paths, so use void *userdata to specify the string
1121 * length of the root directory - '(void*)(strlen(path)+)'.
1122 * add_to_dirlist then removes root dir prefix. */
1123
1124 if (option_mask32 & FLAG_r) {
1125 recursive_action(path, ACTION_RECURSE|ACTION_FOLLOWLINKS,
1126 add_to_dirlist, NULL,
1127 (void*)(strlen(path)+1), 0);
1128 } else {
1129 DIR *dp;
1130 struct dirent *ep;
1131
1132 dp = warn_opendir(path);
1133 while ((ep = readdir(dp))) {
1134 if (!strcmp(ep->d_name, "..") || LONE_CHAR(ep->d_name, '.'))
1135 continue;
1136 add_to_dirlist(ep->d_name, NULL, (void*)(int)0, 0);
1137 }
1138 closedir(dp);
1139 }
1140
1141 /* Sort dl alphabetically. */
1142 qsort(dl, dl_count, sizeof(char *), dir_strcmp);
1143
1144 dl[dl_count] = NULL;
1145 return dl;
1146}
1147
1148
1149static void diffdir(char *p1, char *p2)
1150{
1151 char **dirlist1, **dirlist2;
1152 char *dp1, *dp2;
1153 int pos;
1154
1155 /* Check for trailing slashes. */
1156 dp1 = last_char_is(p1, '/');
1157 if (dp1 != NULL)
1158 *dp1 = '\0';
1159 dp2 = last_char_is(p2, '/');
1160 if (dp2 != NULL)
1161 *dp2 = '\0';
1162
1163 /* Get directory listings for p1 and p2. */
1164
1165 dirlist1 = get_dir(p1);
1166 dirlist2 = get_dir(p2);
1167
1168 /* If -S was set, find the starting point. */
1169 if (start) {
1170 while (*dirlist1 != NULL && strcmp(*dirlist1, start) < 0)
1171 dirlist1++;
1172 while (*dirlist2 != NULL && strcmp(*dirlist2, start) < 0)
1173 dirlist2++;
1174 if ((*dirlist1 == NULL) || (*dirlist2 == NULL))
1175 bb_error_msg(bb_msg_invalid_arg, "NULL", "-S");
1176 }
1177
1178 /* Now that both dirlist1 and dirlist2 contain sorted directory
1179 * listings, we can start to go through dirlist1. If both listings
1180 * contain the same file, then do a normal diff. Otherwise, behaviour
1181 * is determined by whether the -N flag is set. */
1182 while (*dirlist1 != NULL || *dirlist2 != NULL) {
1183 dp1 = *dirlist1;
1184 dp2 = *dirlist2;
1185 pos = dp1 == NULL ? 1 : dp2 == NULL ? -1 : strcmp(dp1, dp2);
1186 if (pos == 0) {
1187 do_diff(p1, dp1, p2, dp2);
1188 dirlist1++;
1189 dirlist2++;
1190 } else if (pos < 0) {
1191 if (option_mask32 & FLAG_N)
1192 do_diff(p1, dp1, p2, NULL);
1193 else
1194 print_only(p1, strlen(p1) + 1, dp1);
1195 dirlist1++;
1196 } else {
1197 if (option_mask32 & FLAG_N)
1198 do_diff(p1, NULL, p2, dp2);
1199 else
1200 print_only(p2, strlen(p2) + 1, dp2);
1201 dirlist2++;
1202 }
1203 }
1204}
1205#endif
1206
1207
1208int diff_main(int argc, char **argv);
1209int diff_main(int argc, char **argv)
1210{
1211 bool gotstdin = 0;
1212 char *U_opt;
1213 char *f1, *f2;
1214 llist_t *L_arg = NULL;
1215
1216 INIT_G();
1217
1218 /* exactly 2 params; collect multiple -L <label> */
1219 opt_complementary = "=2:L::";
1220 getopt32(argc, argv, "abdiL:NqrsS:tTU:wu"
1221 "p" /* ignored (for compatibility) */,
1222 &L_arg, &start, &U_opt);
1223 /*argc -= optind;*/
1224 argv += optind;
1225 while (L_arg) {
1226 if (label1 && label2)
1227 bb_show_usage();
1228 if (!label1)
1229 label1 = L_arg->data;
1230 else { /* then label2 is NULL */
1231 label2 = label1;
1232 label1 = L_arg->data;
1233 }
1234 /* we leak L_arg here... */
1235 L_arg = L_arg->link;
1236 }
1237 if (option_mask32 & FLAG_U)
1238 context = xatoi_u(U_opt);
1239
1240 /*
1241 * Do sanity checks, fill in stb1 and stb2 and call the appropriate
1242 * driver routine. Both drivers use the contents of stb1 and stb2.
1243 */
1244
1245 f1 = argv[0];
1246 f2 = argv[1];
1247 if (LONE_DASH(f1)) {
1248 fstat(STDIN_FILENO, &stb1);
1249 gotstdin = 1;
1250 } else
1251 xstat(f1, &stb1);
1252 if (LONE_DASH(f2)) {
1253 fstat(STDIN_FILENO, &stb2);
1254 gotstdin = 1;
1255 } else
1256 xstat(f2, &stb2);
1257 if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
1258 bb_error_msg_and_die("can't compare - to a directory");
1259 if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
1260#if ENABLE_FEATURE_DIFF_DIR
1261 diffdir(f1, f2);
1262#else
1263 bb_error_msg_and_die("directory comparison not supported");
1264#endif
1265 } else {
1266 if (S_ISDIR(stb1.st_mode)) {
1267 f1 = concat_path_file(f1, f2);
1268 xstat(f1, &stb1);
1269 }
1270 if (S_ISDIR(stb2.st_mode)) {
1271 f2 = concat_path_file(f2, f1);
1272 xstat(f2, &stb2);
1273 }
1274/* XXX: FIXME: */
1275/* We can't diff e.g. stdin supplied by a pipe - we use rewind(), fseek().
1276 * This can be fixed (volunteers?) */
1277 if (!S_ISREG(stb1.st_mode) || !S_ISREG(stb2.st_mode))
1278 bb_error_msg_and_die("can't diff non-seekable stream");
1279 print_status(diffreg(f1, f2, 0), f1, f2, NULL);
1280 }
1281 return status;
1282}
diff --git a/coreutils/readlink.c b/coreutils/readlink.c
new file mode 100644
index 000000000..d454cbf19
--- /dev/null
+++ b/coreutils/readlink.c
@@ -0,0 +1,50 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini readlink implementation for busybox
4 *
5 * Copyright (C) 2000,2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 *
7 * Licensed under GPL v2 or later, see file LICENSE in this tarball for details.
8 */
9
10#include <getopt.h>
11
12#include "libbb.h"
13
14int readlink_main(int argc, char **argv);
15int readlink_main(int argc, char **argv)
16{
17 char *buf;
18 char *fname;
19
20 USE_FEATURE_READLINK_FOLLOW(
21 unsigned opt;
22 /* We need exactly one non-option argument. */
23 opt_complementary = "=1";
24 opt = getopt32(argc, argv, "f");
25 fname = argv[optind];
26 )
27 SKIP_FEATURE_READLINK_FOLLOW(
28 const unsigned opt = 0;
29 if (argc != 2) bb_show_usage();
30 fname = argv[1];
31 )
32
33 /* compat: coreutils readlink reports errors silently via exit code */
34 logmode = LOGMODE_NONE;
35
36 if (opt) {
37 buf = realpath(fname, bb_common_bufsiz1);
38 } else {
39 buf = xmalloc_readlink_or_warn(fname);
40 }
41
42 if (!buf)
43 return EXIT_FAILURE;
44 puts(buf);
45
46 if (ENABLE_FEATURE_CLEAN_UP && !opt)
47 free(buf);
48
49 fflush_stdout_and_exit(EXIT_SUCCESS);
50}
diff --git a/coreutils/watch.c b/coreutils/watch.c
deleted file mode 100644
index 2ad0564cd..000000000
--- a/coreutils/watch.c
+++ /dev/null
@@ -1,80 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini watch implementation for busybox
4 *
5 * Copyright (C) 2001 by Michael Habermann <mhabermann@gmx.de>
6 * Copyrigjt (C) Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
7 *
8 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
9 */
10
11/* BB_AUDIT SUSv3 N/A */
12/* BB_AUDIT GNU defects -- only option -n is supported. */
13
14#include "libbb.h"
15
16// procps 2.0.18:
17// watch [-d] [-n seconds]
18// [--differences[=cumulative]] [--interval=seconds] command
19//
20// procps-3.2.3:
21// watch [-dt] [-n seconds]
22// [--differences[=cumulative]] [--interval=seconds] [--no-title] command
23//
24// (procps 3.x and procps 2.x are forks, not newer/older versions of the same)
25
26int watch_main(int argc, char **argv);
27int watch_main(int argc, char **argv)
28{
29 unsigned opt;
30 unsigned period = 2;
31 unsigned cmdlen = 1; // 1 for terminal NUL
32 char *header = NULL;
33 char *cmd;
34 char *tmp;
35 char **p;
36
37 opt_complementary = "-1"; // at least one param please
38 opt = getopt32(argc, argv, "+dtn:", &tmp);
39 //if (opt & 0x1) // -d (ignore)
40 //if (opt & 0x2) // -t
41 if (opt & 0x4) period = xatou(tmp);
42 argv += optind;
43
44 p = argv;
45 while (*p)
46 cmdlen += strlen(*p++) + 1;
47 tmp = cmd = xmalloc(cmdlen);
48 while (*argv) {
49 tmp += sprintf(tmp, " %s", *argv);
50 argv++;
51 }
52 cmd++; // skip initial space
53
54 while (1) {
55 printf("\033[H\033[J");
56 if (!(opt & 0x2)) { // no -t
57 int width, len;
58 char *thyme;
59 time_t t;
60
61 get_terminal_width_height(STDOUT_FILENO, &width, 0);
62 header = xrealloc(header, width--);
63 // '%-*s' pads header with spaces to the full width
64 snprintf(header, width, "Every %ds: %-*s", period, width, cmd);
65 time(&t);
66 thyme = ctime(&t);
67 len = strlen(thyme);
68 if (len < width)
69 strcpy(header + width - len, thyme);
70 puts(header);
71 }
72 fflush(stdout);
73 // TODO: 'real' watch pipes cmd's output to itself
74 // and does not allow it to overflow the screen
75 // (taking into account linewrap!)
76 system(cmd);
77 sleep(period);
78 }
79 return 0; // gcc thinks we can reach this :)
80}