7cb888de86efc692c0c84697900d602a5840f6d9
[oweals/busybox.git] / du.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini du implementation for busybox
4  *
5  *
6  * Copyright (C) 1999,2000,2001 by Lineo, inc.
7  * Written by John Beppu <beppu@lineo.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22  *
23  */
24
25 #include <sys/types.h>
26 #include <fcntl.h>
27 #include <dirent.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <getopt.h>
31 #include <string.h>
32 #include <errno.h>
33 #include "busybox.h"
34 #define BB_DECLARE_EXTERN
35 #define bb_need_name_too_long
36 #include "messages.c"
37
38
39 #ifdef BB_FEATURE_HUMAN_READABLE
40 static unsigned long disp_hr = KILOBYTE;
41 #endif
42
43 typedef void (Display) (long, char *);
44
45 static int du_depth = 0;
46 static int count_hardlinks = 0;
47
48 static Display *print;
49
50 static void print_normal(long size, char *filename)
51 {
52         unsigned long base;
53 #ifdef BB_FEATURE_HUMAN_READABLE
54         switch (disp_hr) {
55                 case MEGABYTE:
56                         base = KILOBYTE;
57                         break;
58                 case KILOBYTE:
59                         base = 1;
60                         break;
61                 default:
62                         base = 0;
63         }
64         printf("%s\t%s\n", make_human_readable_str(size, base), filename);
65 #else
66         printf("%ld\t%s\n", size, filename);
67 #endif
68 }
69
70 static void print_summary(long size, char *filename)
71 {
72         if (du_depth == 1) {
73                 printf("summary\n");
74                 print_normal(size, filename);
75         }
76 }
77
78 /* tiny recursive du */
79 static long du(char *filename)
80 {
81         struct stat statbuf;
82         long sum;
83         int len;
84
85         if ((lstat(filename, &statbuf)) != 0) {
86                 perror_msg_and_die("%s", filename);
87         }
88
89         du_depth++;
90         sum = (statbuf.st_blocks >> 1);
91
92         /* Don't add in stuff pointed to by symbolic links */
93         if (S_ISLNK(statbuf.st_mode)) {
94                 sum = 0L;
95                 if (du_depth == 1)
96                         print(sum, filename);
97         }
98         if (S_ISDIR(statbuf.st_mode)) {
99                 DIR *dir;
100                 struct dirent *entry;
101
102                 dir = opendir(filename);
103                 if (!dir) {
104                         du_depth--;
105                         return 0;
106                 }
107
108                 len = strlen(filename);
109                 if (filename[len - 1] == '/')
110                         filename[--len] = '\0';
111
112                 while ((entry = readdir(dir))) {
113                         char newfile[BUFSIZ + 1];
114                         char *name = entry->d_name;
115
116                         if ((strcmp(name, "..") == 0)
117                                 || (strcmp(name, ".") == 0)) {
118                                 continue;
119                         }
120
121                         if (len + strlen(name) + 1 > BUFSIZ) {
122                                 error_msg(name_too_long);
123                                 du_depth--;
124                                 return 0;
125                         }
126                         sprintf(newfile, "%s/%s", filename, name);
127
128                         sum += du(newfile);
129                 }
130                 closedir(dir);
131                 print(sum, filename);
132         }
133         else if (statbuf.st_nlink > 1 && !count_hardlinks) {
134                 /* Add files with hard links only once */
135                 if (is_in_ino_dev_hashtable(&statbuf, NULL)) {
136                         sum = 0L;
137                         if (du_depth == 1)
138                                 print(sum, filename);
139                 }
140                 else {
141                         add_to_ino_dev_hashtable(&statbuf, NULL);
142                 }
143         }
144         du_depth--;
145         return sum;
146 }
147
148 int du_main(int argc, char **argv)
149 {
150         int status = EXIT_SUCCESS;
151         int i;
152         int c;
153
154         /* default behaviour */
155         print = print_normal;
156
157         /* parse argv[] */
158         while ((c = getopt(argc, argv, "sl"
159 #ifdef BB_FEATURE_HUMAN_READABLE
160 "hm"
161 #endif
162 "k")) != EOF) {
163                         switch (c) {
164                         case 's':
165                                         print = print_summary;
166                                         break;
167                         case 'l':
168                                         count_hardlinks = 1;
169                                         break;
170 #ifdef BB_FEATURE_HUMAN_READABLE
171                         case 'h': disp_hr = 0;        break;
172                         case 'm': disp_hr = MEGABYTE; break;
173 #endif
174                         case 'k': break;
175                         default:
176                                         show_usage();
177                         }
178         }
179
180         /* go through remaining args (if any) */
181         if (optind >= argc) {
182                 if (du(".") == 0)
183                         status = EXIT_FAILURE;
184         } else {
185                 long sum;
186
187                 for (i=optind; i < argc; i++) {
188                         if ((sum = du(argv[i])) == 0)
189                                 status = EXIT_FAILURE;
190                         if(is_directory(argv[i], FALSE, NULL)==FALSE) {
191                                 print_normal(sum, argv[i]);
192                         }
193                         reset_ino_dev_hashtable();
194                 }
195         }
196
197         return status;
198 }
199
200 /* $Id: du.c,v 1.43 2001/03/09 14:36:42 andersen Exp $ */
201 /*
202 Local Variables:
203 c-file-style: "linux"
204 c-basic-offset: 4
205 tab-width: 4
206 End:
207 */