Use an int to hold the result of fgetc (bug noted by David Kimdon).
[oweals/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  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */
23
24 #include <sys/types.h>
25 #include <fcntl.h>
26 #include <dirent.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <getopt.h>
30 #include <string.h>
31 #include <errno.h>
32 #include "busybox.h"
33
34
35 #ifdef CONFIG_FEATURE_HUMAN_READABLE
36 static unsigned long disp_hr = KILOBYTE;
37 #endif
38
39 typedef void (Display) (long, char *);
40
41 static int du_depth = 0;
42 static int count_hardlinks = 0;
43
44 static Display *print;
45
46 static void print_normal(long size, char *filename)
47 {
48 #ifdef CONFIG_FEATURE_HUMAN_READABLE
49         printf("%s\t%s\n", make_human_readable_str(size<<10, 1, disp_hr), filename);
50 #else
51         printf("%ld\t%s\n", size, filename);
52 #endif
53 }
54
55 static void print_summary(long size, char *filename)
56 {
57         if (du_depth == 1) {
58                 print_normal(size, filename);
59         }
60 }
61
62 #define HASH_SIZE       311             /* Should be prime */
63 #define hash_inode(i)   ((i) % HASH_SIZE)
64
65 typedef struct ino_dev_hash_bucket_struct {
66   struct ino_dev_hash_bucket_struct *next;
67   ino_t ino;
68   dev_t dev;
69   char name[1];
70 } ino_dev_hashtable_bucket_t;
71
72 static ino_dev_hashtable_bucket_t *ino_dev_hashtable[HASH_SIZE];
73
74 /*
75  * Return 1 if statbuf->st_ino && statbuf->st_dev are recorded in
76  * `ino_dev_hashtable', else return 0
77  *
78  * If NAME is a non-NULL pointer to a character pointer, and there is
79  * a match, then set *NAME to the value of the name slot in that
80  * bucket.
81  */
82 static int is_in_ino_dev_hashtable(const struct stat *statbuf, char **name)
83 {
84         ino_dev_hashtable_bucket_t *bucket;
85
86         bucket = ino_dev_hashtable[hash_inode(statbuf->st_ino)];
87         while (bucket != NULL) {
88           if ((bucket->ino == statbuf->st_ino) &&
89                   (bucket->dev == statbuf->st_dev))
90           {
91                 if (name) *name = bucket->name;
92                 return 1;
93           }
94           bucket = bucket->next;
95         }
96         return 0;
97 }
98
99 /* Add statbuf to statbuf hash table */
100 static void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name)
101 {
102         int i;
103         size_t s;
104         ino_dev_hashtable_bucket_t *bucket;
105
106         i = hash_inode(statbuf->st_ino);
107         s = name ? strlen(name) : 0;
108         bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + s);
109         bucket->ino = statbuf->st_ino;
110         bucket->dev = statbuf->st_dev;
111         if (name)
112                 strcpy(bucket->name, name);
113         else
114                 bucket->name[0] = '\0';
115         bucket->next = ino_dev_hashtable[i];
116         ino_dev_hashtable[i] = bucket;
117 }
118
119 /* Clear statbuf hash table */
120 static void reset_ino_dev_hashtable(void)
121 {
122         int i;
123         ino_dev_hashtable_bucket_t *bucket;
124
125         for (i = 0; i < HASH_SIZE; i++) {
126                 while (ino_dev_hashtable[i] != NULL) {
127                         bucket = ino_dev_hashtable[i]->next;
128                         free(ino_dev_hashtable[i]);
129                         ino_dev_hashtable[i] = bucket;
130                 }
131         }
132 }
133
134 /* tiny recursive du */
135 static long du(char *filename)
136 {
137         struct stat statbuf;
138         long sum;
139
140         if ((lstat(filename, &statbuf)) != 0) {
141                 perror_msg("%s", filename);
142                 return 0;
143         }
144
145         du_depth++;
146         sum = (statbuf.st_blocks >> 1);
147
148         /* Don't add in stuff pointed to by symbolic links */
149         if (S_ISLNK(statbuf.st_mode)) {
150                 sum = 0L;
151                 if (du_depth == 1) {
152                 }
153         }
154         if (S_ISDIR(statbuf.st_mode)) {
155                 DIR *dir;
156                 struct dirent *entry;
157                 char *newfile;
158
159                 dir = opendir(filename);
160                 if (!dir) {
161                         du_depth--;
162                         return 0;
163                 }
164
165                 newfile = last_char_is(filename, '/');
166                 if (newfile)
167                         *newfile = '\0';
168
169                 while ((entry = readdir(dir))) {
170                         char *name = entry->d_name;
171
172                         if ((strcmp(name, "..") == 0)
173                                 || (strcmp(name, ".") == 0)) {
174                                 continue;
175                         }
176                         newfile = concat_path_file(filename, name);
177                         sum += du(newfile);
178                         free(newfile);
179                 }
180                 closedir(dir);
181                 print(sum, filename);
182         }
183         else if (statbuf.st_nlink > 1 && !count_hardlinks) {
184                 /* Add files with hard links only once */
185                 if (is_in_ino_dev_hashtable(&statbuf, NULL)) {
186                         sum = 0L;
187                         if (du_depth == 1)
188                                 print(sum, filename);
189                 }
190                 else {
191                         add_to_ino_dev_hashtable(&statbuf, NULL);
192                 }
193         }
194         du_depth--;
195         return sum;
196 }
197
198 int du_main(int argc, char **argv)
199 {
200         int status = EXIT_SUCCESS;
201         int i;
202         int c;
203
204         /* default behaviour */
205         print = print_normal;
206
207         /* parse argv[] */
208         while ((c = getopt(argc, argv, "sl"
209 #ifdef CONFIG_FEATURE_HUMAN_READABLE
210 "hm"
211 #endif
212 "k")) != EOF) {
213                         switch (c) {
214                         case 's':
215                                         print = print_summary;
216                                         break;
217                         case 'l':
218                                         count_hardlinks = 1;
219                                         break;
220 #ifdef CONFIG_FEATURE_HUMAN_READABLE
221                         case 'h': disp_hr = 0;        break;
222                         case 'm': disp_hr = MEGABYTE; break;
223 #endif
224                         case 'k': break;
225                         default:
226                                         show_usage();
227                         }
228         }
229
230         /* go through remaining args (if any) */
231         if (optind >= argc) {
232                 if (du(".") == 0)
233                         status = EXIT_FAILURE;
234         } else {
235                 long sum;
236
237                 for (i=optind; i < argc; i++) {
238                         sum = du(argv[i]);
239                         if(is_directory(argv[i], FALSE, NULL)==FALSE) {
240                                 print_normal(sum, argv[i]);
241                         }
242                         reset_ino_dev_hashtable();
243                 }
244         }
245
246         return status;
247 }
248
249 /* $Id: du.c,v 1.51 2001/10/24 04:59:27 andersen Exp $ */
250 /*
251 Local Variables:
252 c-file-style: "linux"
253 c-basic-offset: 4
254 tab-width: 4
255 End:
256 */