Added an option ignore_dotfiles.
[dus.git] / dus.c
1
2 /*
3  *  dus is a simple utility to display the files and directories
4  *  according to their total disk occupancy.
5  *
6  *  Copyright (c) 2010 Francois Fleuret
7  *  Written by Francois Fleuret <francois@fleuret.org>
8  *
9  *  This file is part of dus.
10  *
11  *  dus is free software: you can redistribute it and/or modify it
12  *  under the terms of the GNU General Public License version 3 as
13  *  published by the Free Software Foundation.
14  *
15  *  dus is distributed in the hope that it will be useful, but WITHOUT
16  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17  *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
18  *  License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with dus.  If not, see <http://www.gnu.org/licenses/>.
22  *
23  */
24
25 #define _BSD_SOURCE
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <dirent.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <sys/ioctl.h>
36 #include <locale.h>
37
38 #define BUFFER_SIZE 4096
39
40 typedef int64_t size_sum_t;
41
42 int ignore_dotfiles = 1;
43
44 /********************************************************************/
45
46 int ignore_entry(const char *name) {
47   return
48     strcmp(name, ".") == 0 ||
49     strcmp(name, "..") == 0 ||
50     (ignore_dotfiles && name[0] == '.');
51 }
52
53 size_sum_t file_or_dir_size(const char *name) {
54   DIR *dir;
55   struct dirent *dir_e;
56   struct stat dummy;
57   size_sum_t result;
58   char subname[BUFFER_SIZE];
59
60   result = 0;
61
62   if(lstat(name, &dummy) != 0) {
63     printf("Can not stat %s: %s\n", name, strerror(errno));
64     exit (1);
65   }
66
67   if(S_ISLNK(dummy.st_mode)) {
68     return 0;
69   }
70
71   dir = opendir(name);
72
73   if(dir) {
74     while((dir_e = readdir(dir))) {
75       if(!ignore_entry(dir_e->d_name)) {
76         snprintf(subname, BUFFER_SIZE, "%s/%s", name, dir_e->d_name);
77         result += file_or_dir_size(subname);
78       }
79     }
80     closedir(dir);
81   } else {
82     if(S_ISREG(dummy.st_mode)) {
83       /* printf("%d %s\n", dummy.st_size, name); */
84       result += dummy.st_size;
85     }
86   }
87
88   return result;
89 }
90
91 /**********************************************************************/
92
93 struct file_with_size {
94   char *filename;
95   size_sum_t size;
96   struct file_with_size *next;
97 };
98
99 struct file_with_size *create(char *name, struct file_with_size *current) {
100   struct file_with_size *result;
101   result = malloc(sizeof(struct file_with_size));
102   result->filename = strdup(name);
103   result->size = file_or_dir_size(name);
104   result->next = current;
105   return result;
106 }
107
108 void destroy(struct file_with_size *node) {
109   struct file_with_size *next;
110   while(node) {
111     next = node->next;
112     free(node->filename);
113     free(node);
114     node = next;
115   }
116 }
117
118 /**********************************************************************/
119
120 int compare_files(const void *x1, const void *x2) {
121   const struct file_with_size **f1, **f2;
122
123   f1 = (const struct file_with_size **) x1;
124   f2 = (const struct file_with_size **) x2;
125
126   if((*f1)->size < (*f2)->size) {
127     return -1;
128   } else if((*f1)->size > (*f2)->size) {
129     return 1;
130   } else {
131     return 0;
132   }
133 }
134
135 void print_sorted(struct file_with_size *root, int height) {
136   struct file_with_size *node;
137   struct file_with_size **nodes;
138   int nb, n, first;
139
140   nb = 0;
141   for(node = root; node; node = node->next) {
142     nb++;
143   }
144
145   nodes = malloc(nb * sizeof(struct file_with_size *));
146
147   n = 0;
148   for(node = root; node; node = node->next) {
149     nodes[n++] = node;
150   }
151
152   qsort(nodes, nb, sizeof(struct file_with_size *), compare_files);
153
154   /*
155   for(n = 0; n < nb; n++) {
156     printf("%lld %s\n",
157            nodes[n]->size,
158            nodes[n]->filename);
159   }
160   */
161
162   first = 0;
163   if(height > 0 && height < nb) {
164     first = nb - height;
165   }
166   for(n = first; n < nb; n++) {
167     if(nodes[n]->size < 1024) {
168       printf("% 7d %s\n",
169              ((int) nodes[n]->size),
170              nodes[n]->filename);
171     } else if(nodes[n]->size < 1024 * 1024) {
172       printf("% 6.1fK %s\n",
173              ((double) (nodes[n]->size))/(1024.0),
174              nodes[n]->filename);
175     } else if(nodes[n]->size < 1024 * 1024 * 1024) {
176       printf("% 6.1fM %s\n",
177              ((double) (nodes[n]->size))/(1024.0 * 1024),
178              nodes[n]->filename);
179     } else {
180       printf("% 6.1fG %s\n",
181              ((double) (nodes[n]->size))/(1024.0 * 1024.0 * 1024.0),
182              nodes[n]->filename);
183     }
184   }
185
186   free(nodes);
187 }
188
189 /**********************************************************************/
190
191 int main(int argc, char **argv) {
192   int k;
193   struct file_with_size *root;
194
195   root = 0;
196
197   setlocale (LC_ALL, "");
198
199   if(argc > 1) {
200     for(k = 1; k < argc; k++) {
201       root = create(argv[k], root);
202     }
203   } else {
204     DIR *dir;
205     struct dirent *dir_e;
206     dir = opendir(".");
207     if(dir) {
208       while((dir_e = readdir(dir))) {
209         if(!ignore_entry(dir_e->d_name)) {
210           root = create(dir_e->d_name, root);
211         }
212       }
213       closedir(dir);
214     }
215   }
216
217   if(isatty(STDOUT_FILENO)) {
218     struct winsize win;
219     if(ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win)) {
220       printf("Can not get the tty size: %s\n", strerror(errno));
221       exit (1);
222     }
223     print_sorted(root, win.ws_row - 2);
224   } else {
225     print_sorted(root, -1);
226   }
227
228   destroy(root);
229
230   exit(0);
231 }