don't pass argc in getopt32, it's superfluous
[platform/upstream/busybox.git] / coreutils / du.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini du implementation for busybox
4  *
5  * Copyright (C) 1999,2000,2001 by Lineo, inc. and John Beppu
6  * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7  * Copyright (C) 2002  Edward Betts <edward@debian.org>
8  *
9  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
10  */
11
12 /* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */
13 /* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */
14
15 /* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
16  *
17  * Mostly rewritten for SUSv3 compliance and to fix bugs/defects.
18  * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options.
19  *    The -d option allows setting of max depth (similar to gnu --max-depth).
20  * 2) Fixed incorrect size calculations for links and directories, especially
21  *    when errors occurred.  Calculates sizes should now match gnu du output.
22  * 3) Added error checking of output.
23  * 4) Fixed busybox bug #1284 involving long overflow with human_readable.
24  */
25
26 #include "libbb.h"
27
28 #if ENABLE_FEATURE_HUMAN_READABLE
29 # if ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
30 static unsigned long disp_hr = 1024;
31 # else
32 static unsigned long disp_hr = 512;
33 # endif
34 #elif ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
35 static unsigned disp_k = 1;
36 #else
37 static unsigned disp_k; /* bss inits to 0 */
38 #endif
39
40 static int max_print_depth = INT_MAX;
41 static nlink_t count_hardlinks = 1;
42
43 static int status;
44 static int print_files;
45 static int slink_depth;
46 static int du_depth;
47 static int one_file_system;
48 static dev_t dir_dev;
49
50
51 static void print(long size, const char *const filename)
52 {
53         /* TODO - May not want to defer error checking here. */
54 #if ENABLE_FEATURE_HUMAN_READABLE
55         printf("%s\t%s\n", make_human_readable_str(size, 512, disp_hr),
56                         filename);
57 #else
58         if (disp_k) {
59                 size++;
60                 size >>= 1;
61         }
62         printf("%ld\t%s\n", size, filename);
63 #endif
64 }
65
66 /* tiny recursive du */
67 static long du(const char *const filename)
68 {
69         struct stat statbuf;
70         long sum;
71
72         if (lstat(filename, &statbuf) != 0) {
73                 bb_perror_msg("%s", filename);
74                 status = EXIT_FAILURE;
75                 return 0;
76         }
77
78         if (one_file_system) {
79                 if (du_depth == 0) {
80                         dir_dev = statbuf.st_dev;
81                 } else if (dir_dev != statbuf.st_dev) {
82                         return 0;
83                 }
84         }
85
86         sum = statbuf.st_blocks;
87
88         if (S_ISLNK(statbuf.st_mode)) {
89                 if (slink_depth > du_depth) {   /* -H or -L */
90                         if (stat(filename, &statbuf) != 0) {
91                                 bb_perror_msg("%s", filename);
92                                 status = EXIT_FAILURE;
93                                 return 0;
94                         }
95                         sum = statbuf.st_blocks;
96                         if (slink_depth == 1) {
97                                 slink_depth = INT_MAX;  /* Convert -H to -L. */
98                         }
99                 }
100         }
101
102         if (statbuf.st_nlink > count_hardlinks) {
103                 /* Add files/directories with links only once */
104                 if (is_in_ino_dev_hashtable(&statbuf)) {
105                         return 0;
106                 }
107                 add_to_ino_dev_hashtable(&statbuf, NULL);
108         }
109
110         if (S_ISDIR(statbuf.st_mode)) {
111                 DIR *dir;
112                 struct dirent *entry;
113                 char *newfile;
114
115                 dir = warn_opendir(filename);
116                 if (!dir) {
117                         status = EXIT_FAILURE;
118                         return sum;
119                 }
120
121                 newfile = last_char_is(filename, '/');
122                 if (newfile)
123                         *newfile = '\0';
124
125                 while ((entry = readdir(dir))) {
126                         char *name = entry->d_name;
127
128                         newfile = concat_subpath_file(filename, name);
129                         if (newfile == NULL)
130                                 continue;
131                         ++du_depth;
132                         sum += du(newfile);
133                         --du_depth;
134                         free(newfile);
135                 }
136                 closedir(dir);
137         } else if (du_depth > print_files) {
138                 return sum;
139         }
140         if (du_depth <= max_print_depth) {
141                 print(sum, filename);
142         }
143         return sum;
144 }
145
146 int du_main(int argc, char **argv);
147 int du_main(int argc, char **argv)
148 {
149         long total;
150         int slink_depth_save;
151         int print_final_total;
152         char *smax_print_depth;
153         unsigned opt;
154
155 #if ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
156         if (getenv("POSIXLY_CORRECT")) {        /* TODO - a new libbb function? */
157 #if ENABLE_FEATURE_HUMAN_READABLE
158                 disp_hr = 512;
159 #else
160                 disp_k = 0;
161 #endif
162         }
163 #endif
164
165         /* Note: SUSv3 specifies that -a and -s options cannot be used together
166          * in strictly conforming applications.  However, it also says that some
167          * du implementations may produce output when -a and -s are used together.
168          * gnu du exits with an error code in this case.  We choose to simply
169          * ignore -a.  This is consistent with -s being equivalent to -d 0.
170          */
171 #if ENABLE_FEATURE_HUMAN_READABLE
172         opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s";
173         opt = getopt32(argv, "aHkLsx" "d:" "lc" "hm", &smax_print_depth);
174         if (opt & (1 << 9)) {
175                 /* -h opt */
176                 disp_hr = 0;
177         }
178         if (opt & (1 << 10)) {
179                 /* -m opt */
180                 disp_hr = 1024*1024;
181         }
182         if (opt & (1 << 2)) {
183                 /* -k opt */
184                 disp_hr = 1024;
185         }
186 #else
187         opt_complementary = "H-L:L-H:s-d:d-s";
188         opt = getopt32(argv, "aHkLsx" "d:" "lc", &smax_print_depth);
189 #if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
190         if (opt & (1 << 2)) {
191                 /* -k opt */
192                 disp_k = 1;
193         }
194 #endif
195 #endif
196         if (opt & (1 << 0)) {
197                 /* -a opt */
198                 print_files = INT_MAX;
199         }
200         if (opt & (1 << 1)) {
201                 /* -H opt */
202                 slink_depth = 1;
203         }
204         if (opt & (1 << 3)) {
205                 /* -L opt */
206                 slink_depth = INT_MAX;
207         }
208         if (opt & (1 << 4)) {
209                 /* -s opt */
210                 max_print_depth = 0;
211         }
212         one_file_system = opt & (1 << 5); /* -x opt */
213         if (opt & (1 << 6)) {
214                 /* -d opt */
215                 max_print_depth = xatoi_u(smax_print_depth);
216         }
217         if (opt & (1 << 7)) {
218                 /* -l opt */
219                 count_hardlinks = MAXINT(nlink_t);
220         }
221         print_final_total = opt & (1 << 8); /* -c opt */
222
223         /* go through remaining args (if any) */
224         argv += optind;
225         if (optind >= argc) {
226                 *--argv = (char*)".";
227                 if (slink_depth == 1) {
228                         slink_depth = 0;
229                 }
230         }
231
232         slink_depth_save = slink_depth;
233         total = 0;
234         do {
235                 total += du(*argv);
236                 slink_depth = slink_depth_save;
237         } while (*++argv);
238         if (ENABLE_FEATURE_CLEAN_UP)
239                 reset_ino_dev_hashtable();
240         if (print_final_total) {
241                 print(total, "total");
242         }
243
244         fflush_stdout_and_exit(status);
245 }