(usage): Include one- or two-line synopsis in --help output.
[platform/upstream/coreutils.git] / src / du.c
1 /* du -- summarize disk usage
2    Copyright (C) 1988, 1989, 1990, 1991, 1995 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
17
18 /* Differences from the Unix du:
19    * Doesn't simply ignore the names of regular files given as arguments
20      when -a is given.
21    * Additional options:
22    -l           Count the size of all files, even if they have appeared
23                 already in another hard link.
24    -x           Do not cross file-system boundaries during the recursion.
25    -c           Write a grand total of all of the arguments after all
26                 arguments have been processed.  This can be used to find
27                 out the disk usage of a directory, with some files excluded.
28    -k           Print sizes in kilobytes instead of 512 byte blocks
29                 (the default required by POSIX).
30    -b           Print sizes in bytes.
31    -S           Count the size of each directory separately, not including
32                 the sizes of subdirectories.
33    -D           Dereference only symbolic links given on the command line.
34    -L           Dereference all symbolic links.
35
36    By tege@sics.se, Torbjorn Granlund,
37    and djm@ai.mit.edu, David MacKenzie.  */
38
39 #ifdef _AIX
40  #pragma alloca
41 #endif
42
43 #include <config.h>
44 #include <stdio.h>
45 #include <getopt.h>
46 #include <sys/types.h>
47
48 #include "system.h"
49 #include "version.h"
50 #include "error.h"
51
52 /* Initial number of entries in each hash table entry's table of inodes.  */
53 #define INITIAL_HASH_MODULE 100
54
55 /* Initial number of entries in the inode hash table.  */
56 #define INITIAL_ENTRY_TAB_SIZE 70
57
58 /* Initial size to allocate for `path'.  */
59 #define INITIAL_PATH_SIZE 100
60
61 /* Hash structure for inode and device numbers.  The separate entry
62    structure makes it easier to rehash "in place".  */
63
64 struct entry
65 {
66   ino_t ino;
67   dev_t dev;
68   struct entry *coll_link;
69 };
70
71 /* Structure for a hash table for inode numbers. */
72
73 struct htab
74 {
75   unsigned modulus;             /* Size of the `hash' pointer vector.  */
76   struct entry *entry_tab;      /* Pointer to dynamically growing vector.  */
77   unsigned entry_tab_size;      /* Size of current `entry_tab' allocation.  */
78   unsigned first_free_entry;    /* Index in `entry_tab'.  */
79   struct entry *hash[1];        /* Vector of pointers in `entry_tab'.  */
80 };
81
82 struct saved_cwd
83   {
84     int desc;
85     char *name;
86   };
87
88
89 /* Structure for dynamically resizable strings. */
90
91 typedef struct
92 {
93   unsigned alloc;               /* Size of allocation for the text.  */
94   unsigned length;              /* Length of the text currently.  */
95   char *text;                   /* Pointer to the text.  */
96 } *string, stringstruct;
97
98 char *savedir ();
99 char *xgetcwd ();
100 char *xmalloc ();
101 char *xrealloc ();
102
103 static int hash_insert ();
104 static int hash_insert2 ();
105 static long count_entry ();
106 static void du_files ();
107 static void hash_init ();
108 static void hash_reset ();
109 static void str_concatc ();
110 static void str_copyc ();
111 static void str_init ();
112 static void str_trunc ();
113
114 /* Name under which this program was invoked.  */
115 char *program_name;
116
117 /* If nonzero, display only a total for each argument. */
118 static int opt_summarize_only = 0;
119
120 /* If nonzero, display counts for all files, not just directories. */
121 static int opt_all = 0;
122
123 /* If nonzero, count each hard link of files with multiple links. */
124 static int opt_count_all = 0;
125
126 /* If nonzero, do not cross file-system boundaries. */
127 static int opt_one_file_system = 0;
128
129 /* If nonzero, print a grand total at the end. */
130 static int opt_combined_arguments = 0;
131
132 /* If nonzero, do not add sizes of subdirectories. */
133 static int opt_separate_dirs = 0;
134
135 /* If nonzero, dereference symlinks that are command line arguments. */
136 static int opt_dereference_arguments = 0;
137
138 enum output_size
139 {
140   size_blocks,                  /* 512-byte blocks. */
141   size_kilobytes,               /* 1K blocks. */
142   size_bytes                    /* 1-byte blocks. */
143 };
144
145 /* The units to count in. */
146 static enum output_size output_size;
147
148 /* Accumulated path for file or directory being processed.  */
149 static string path;
150
151 /* Pointer to hash structure, used by the hash routines.  */
152 static struct htab *htab;
153
154 /* Globally used stat buffer.  */
155 static struct stat stat_buf;
156
157 /* A pointer to either lstat or stat, depending on whether
158    dereferencing of all symbolic links is to be done. */
159 static int (*xstat) ();
160
161 /* The exit status to use if we don't get any fatal errors. */
162 static int exit_status;
163
164 /* If non-zero, display usage information and exit.  */
165 static int show_help;
166
167 /* If non-zero, print the version on standard output and exit.  */
168 static int show_version;
169
170 /* Grand total size of all args. */
171 static long tot_size = 0L;
172
173 static struct option const long_options[] =
174 {
175   {"all", no_argument, &opt_all, 1},
176   {"bytes", no_argument, NULL, 'b'},
177   {"count-links", no_argument, &opt_count_all, 1},
178   {"dereference", no_argument, NULL, 'L'},
179   {"dereference-args", no_argument, &opt_dereference_arguments, 1},
180   {"kilobytes", no_argument, NULL, 'k'},
181   {"one-file-system", no_argument, &opt_one_file_system, 1},
182   {"separate-dirs", no_argument, &opt_separate_dirs, 1},
183   {"summarize", no_argument, &opt_summarize_only, 1},
184   {"total", no_argument, &opt_combined_arguments, 1},
185   {"help", no_argument, &show_help, 1},
186   {"version", no_argument, &show_version, 1},
187   {NULL, 0, NULL, 0}
188 };
189
190 static void
191 save_cwd (cwd)
192      struct saved_cwd *cwd;
193 {
194   static int have_working_fchdir = 1;
195
196   if (have_working_fchdir)
197     {
198 #ifdef HAVE_FCHDIR
199       cwd->desc = open (".", O_RDONLY);
200       if (cwd->desc < 0)
201         error (1, errno, "cannot open current directory");
202
203       /* On SunOS 4, fchdir returns EINVAL if accounting is enabled,
204          so we have to fall back to chdir.  */
205       if (fchdir (cwd->desc))
206         {
207           if (errno == EINVAL)
208             {
209               close (cwd->desc);
210               cwd->desc = -1;
211               have_working_fchdir = 0;
212             }
213           else
214             {
215               error (1, errno, "current directory");
216             }
217         }
218 #else
219 #define fchdir(x) (abort (), 0)
220       have_working_fchdir = 0;
221 #endif
222     }
223
224   if (!have_working_fchdir)
225     {
226       cwd->desc = -1;
227       cwd->name = xgetcwd ();
228       if (cwd->name == NULL)
229         error (1, errno, "cannot get current directory");
230     }
231   else
232     {
233       cwd->name = NULL;
234     }
235 }
236
237 static void
238 restore_cwd (cwd, dest, current)
239      const struct saved_cwd *cwd;
240      const char *dest;
241      const char *current;
242 {
243   if (cwd->desc >= 0)
244     {
245       if (fchdir (cwd->desc))
246         error (1, errno, "cannot return to %s%s%s", dest,
247                (current ? " from " : ""),
248                (current ? current : ""));
249     }
250   else if (chdir (cwd->name) < 0)
251     {
252       error (1, errno, "%s", cwd->name);
253     }
254 }
255
256 static void
257 usage (status, reason)
258      int status;
259      char *reason;
260 {
261   if (reason != NULL)
262     fprintf (status == 0 ? stdout : stderr, "%s: %s\n",
263              program_name, reason);
264
265   if (status != 0)
266     fprintf (stderr, "Try `%s --help' for more information.\n",
267              program_name);
268   else
269     {
270       printf ("Usage: %s [OPTION]... [PATH]...\n", program_name);
271       printf ("\
272 \n\
273   -a, --all                write counts for all files, not just directories\n\
274   -b, --bytes              print size in bytes\n\
275   -c, --total              produce a grand total\n\
276   -k, --kilobytes          use 1024 blocks, not 512 despite POSIXLY_CORRECT\n\
277   -l, --count-links        count sizes many times if hard linked\n\
278   -s, --summarize          display only a total for each argument\n\
279   -x, --one-file-system    skip directories on different filesystems\n\
280   -D, --dereference-args   dereference PATHs when symbolic link\n\
281   -L, --dereference        dereference all symbolic links\n\
282   -S, --separate-dirs      do not include size of subdirectories\n\
283       --help               display this help and exit\n\
284       --version            output version information and exit\n");
285     }
286   exit (status);
287 }
288 \f
289 void
290 main (argc, argv)
291      int argc;
292      char *argv[];
293 {
294   int c;
295   char *cwd_only[2];
296
297   cwd_only[0] = ".";
298   cwd_only[1] = NULL;
299
300   program_name = argv[0];
301   xstat = lstat;
302   output_size = getenv ("POSIXLY_CORRECT") ? size_blocks : size_kilobytes;
303
304   while ((c = getopt_long (argc, argv, "abcklsxDLS", long_options, (int *) 0))
305          != EOF)
306     {
307       switch (c)
308         {
309         case 0:                 /* Long option. */
310           break;
311
312         case 'a':
313           opt_all = 1;
314           break;
315
316         case 'b':
317           output_size = size_bytes;
318           break;
319
320         case 'c':
321           opt_combined_arguments = 1;
322           break;
323
324         case 'k':
325           output_size = size_kilobytes;
326           break;
327
328         case 'l':
329           opt_count_all = 1;
330           break;
331
332         case 's':
333           opt_summarize_only = 1;
334           break;
335
336         case 'x':
337           opt_one_file_system = 1;
338           break;
339
340         case 'D':
341           opt_dereference_arguments = 1;
342           break;
343
344         case 'L':
345           xstat = stat;
346           break;
347
348         case 'S':
349           opt_separate_dirs = 1;
350           break;
351
352         default:
353           usage (2, (char *) 0);
354         }
355     }
356
357   if (show_version)
358     {
359       printf ("%s\n", version_string);
360       exit (0);
361     }
362
363   if (show_help)
364     usage (0, NULL);
365
366   if (opt_all && opt_summarize_only)
367     usage (2, "cannot both summarize and show all entries");
368
369   /* Initialize the hash structure for inode numbers.  */
370   hash_init (INITIAL_HASH_MODULE, INITIAL_ENTRY_TAB_SIZE);
371
372   str_init (&path, INITIAL_PATH_SIZE);
373
374   du_files (optind == argc ? cwd_only : argv + optind);
375
376   exit (exit_status);
377 }
378 \f
379 /* Recursively print the sizes of the directories (and, if selected, files)
380    named in FILES, the last entry of which is NULL.  */
381
382 static void
383 du_files (files)
384      char **files;
385 {
386   struct saved_cwd cwd;
387   ino_t initial_ino;            /* Initial directory's inode. */
388   dev_t initial_dev;            /* Initial directory's device. */
389   int i;                        /* Index in FILES. */
390
391   save_cwd (&cwd);
392
393   /* Remember the inode and device number of the current directory.  */
394   if (stat (".", &stat_buf))
395     error (1, errno, "current directory");
396   initial_ino = stat_buf.st_ino;
397   initial_dev = stat_buf.st_dev;
398
399   for (i = 0; files[i]; i++)
400     {
401       char *arg;
402       int s;
403
404       arg = files[i];
405
406       /* Delete final slash in the argument, unless the slash is alone.  */
407       s = strlen (arg) - 1;
408       if (s != 0)
409         {
410           if (arg[s] == '/')
411             arg[s] = 0;
412
413           str_copyc (path, arg);
414         }
415       else if (arg[0] == '/')
416         str_trunc (path, 0);    /* Null path for root directory.  */
417       else
418         str_copyc (path, arg);
419
420       if (!opt_combined_arguments)
421         hash_reset ();
422
423       count_entry (arg, 1, 0);
424
425       /* chdir if `count_entry' has changed the working directory.  */
426       if (stat (".", &stat_buf))
427         error (1, errno, ".");
428       if (stat_buf.st_ino != initial_ino || stat_buf.st_dev != initial_dev)
429         {
430           restore_cwd (&cwd, "starting directory", NULL);
431         }
432     }
433
434   if (opt_combined_arguments)
435     {
436       printf ("%ld\ttotal\n", output_size == size_bytes ? tot_size
437               : convert_blocks (tot_size, output_size == size_kilobytes));
438       fflush (stdout);
439     }
440
441   if (cwd.name != NULL)
442     free (cwd.name);
443 }
444 \f
445 /* Print (if appropriate) and return the size
446    (in units determined by `output_size') of file or directory ENT.
447    TOP is one for external calls, zero for recursive calls.
448    LAST_DEV is the device that the parent directory of ENT is on.  */
449
450 static long
451 count_entry (ent, top, last_dev)
452      char *ent;
453      int top;
454      dev_t last_dev;
455 {
456   long size;
457
458   if (((top && opt_dereference_arguments)
459        ? stat (ent, &stat_buf)
460        : (*xstat) (ent, &stat_buf)) < 0)
461     {
462       error (0, errno, "%s", path->text);
463       exit_status = 1;
464       return 0;
465     }
466
467   if (!opt_count_all
468       && stat_buf.st_nlink > 1
469       && hash_insert (stat_buf.st_ino, stat_buf.st_dev))
470     return 0;                   /* Have counted this already.  */
471
472   if (output_size == size_bytes)
473     size = stat_buf.st_size;
474   else
475     size = ST_NBLOCKS (stat_buf);
476
477   tot_size += size;
478
479   if (S_ISDIR (stat_buf.st_mode))
480     {
481       unsigned pathlen;
482       dev_t dir_dev;
483       char *name_space;
484       char *namep;
485       struct saved_cwd cwd;
486       int through_symlink;
487       struct stat e_buf;
488
489       dir_dev = stat_buf.st_dev;
490
491       if (opt_one_file_system && !top && last_dev != dir_dev)
492         return 0;               /* Don't enter a new file system.  */
493
494 #ifndef S_ISDIR
495 # define S_ISDIR(s) 0
496 #endif
497       /* If we're dereferencing symlinks and we're about to chdir through
498          a symlink, remember the current directory so we can return to it
499          later.  In other cases, chdir ("..") works fine.  */
500       through_symlink = (xstat == stat
501                          && lstat (ent, &e_buf) == 0
502                          && S_ISLNK (e_buf.st_mode));
503       if (through_symlink)
504         save_cwd (&cwd);
505
506       if (chdir (ent) < 0)
507         {
508           error (0, errno, "cannot change to directory %s", path->text);
509           exit_status = 1;
510           return 0;
511         }
512
513       errno = 0;
514       name_space = savedir (".", stat_buf.st_size);
515       if (name_space == NULL)
516         {
517           if (errno)
518             {
519               error (0, errno, "%s", path->text);
520               if (through_symlink)
521                 {
522                   restore_cwd (&cwd, "..", path->text);
523                   if (cwd.name != NULL)
524                     free (cwd.name);
525                 }
526               else if (chdir ("..") < 0)
527                   error (1, errno, "cannot change to `..' from directory %s",
528                          path->text);
529               exit_status = 1;
530               return 0;
531             }
532           else
533             error (1, 0, "virtual memory exhausted");
534         }
535
536       /* Remember the current path.  */
537
538       str_concatc (path, "/");
539       pathlen = path->length;
540
541       namep = name_space;
542       while (*namep != 0)
543         {
544           str_concatc (path, namep);
545
546           size += count_entry (namep, 0, dir_dev);
547
548           str_trunc (path, pathlen);
549           namep += strlen (namep) + 1;
550         }
551       free (name_space);
552       if (through_symlink)
553         {
554           restore_cwd (&cwd, "..", path->text);
555           if (cwd.name != NULL)
556             free (cwd.name);
557         }
558       else if (chdir ("..") < 0)
559         error (1, errno, "cannot change to `..' from directory %s", path->text);
560
561       str_trunc (path, pathlen - 1); /* Remove the "/" we added.  */
562       if (!opt_summarize_only || top)
563         {
564           printf ("%ld\t%s\n", output_size == size_bytes ? size
565                   : convert_blocks (size, output_size == size_kilobytes),
566                   path->length > 0 ? path->text : "/");
567           fflush (stdout);
568         }
569       return opt_separate_dirs ? 0 : size;
570     }
571   else if (opt_all || top)
572     {
573       /* FIXME: make this an option.  */
574       int print_only_dir_size = 0;
575       if (!print_only_dir_size)
576         {
577           printf ("%ld\t%s\n", output_size == size_bytes ? size
578                   : convert_blocks (size, output_size == size_kilobytes),
579                   path->text);
580           fflush (stdout);
581         }
582     }
583
584   return size;
585 }
586 \f
587 /* Allocate space for the hash structures, and set the global
588    variable `htab' to point to it.  The initial hash module is specified in
589    MODULUS, and the number of entries are specified in ENTRY_TAB_SIZE.  (The
590    hash structure will be rebuilt when ENTRY_TAB_SIZE entries have been
591    inserted, and MODULUS and ENTRY_TAB_SIZE in the global `htab' will be
592    doubled.)  */
593
594 static void
595 hash_init (modulus, entry_tab_size)
596      unsigned modulus;
597      unsigned entry_tab_size;
598 {
599   struct htab *htab_r;
600
601   htab_r = (struct htab *)
602     xmalloc (sizeof (struct htab) + sizeof (struct entry *) * modulus);
603
604   htab_r->entry_tab = (struct entry *)
605     xmalloc (sizeof (struct entry) * entry_tab_size);
606
607   htab_r->modulus = modulus;
608   htab_r->entry_tab_size = entry_tab_size;
609   htab = htab_r;
610
611   hash_reset ();
612 }
613
614 /* Reset the hash structure in the global variable `htab' to
615    contain no entries.  */
616
617 static void
618 hash_reset ()
619 {
620   int i;
621   struct entry **p;
622
623   htab->first_free_entry = 0;
624
625   p = htab->hash;
626   for (i = htab->modulus; i > 0; i--)
627     *p++ = NULL;
628 }
629
630 /* Insert an item (inode INO and device DEV) in the hash
631    structure in the global variable `htab', if an entry with the same data
632    was not found already.  Return zero if the item was inserted and non-zero
633    if it wasn't.  */
634
635 static int
636 hash_insert (ino, dev)
637      ino_t ino;
638      dev_t dev;
639 {
640   struct htab *htab_r = htab;   /* Initially a copy of the global `htab'.  */
641
642   if (htab_r->first_free_entry >= htab_r->entry_tab_size)
643     {
644       int i;
645       struct entry *ep;
646       unsigned modulus;
647       unsigned entry_tab_size;
648
649       /* Increase the number of hash entries, and re-hash the data.
650          The method of shrimping and increasing is made to compactify
651          the heap.  If twice as much data would be allocated
652          straightforwardly, we would never re-use a byte of memory.  */
653
654       /* Let `htab' shrimp.  Keep only the header, not the pointer vector.  */
655
656       htab_r = (struct htab *)
657         xrealloc ((char *) htab_r, sizeof (struct htab));
658
659       modulus = 2 * htab_r->modulus;
660       entry_tab_size = 2 * htab_r->entry_tab_size;
661
662       /* Increase the number of possible entries.  */
663
664       htab_r->entry_tab = (struct entry *)
665         xrealloc ((char *) htab_r->entry_tab,
666                  sizeof (struct entry) * entry_tab_size);
667
668       /* Increase the size of htab again.  */
669
670       htab_r = (struct htab *)
671         xrealloc ((char *) htab_r,
672                  sizeof (struct htab) + sizeof (struct entry *) * modulus);
673
674       htab_r->modulus = modulus;
675       htab_r->entry_tab_size = entry_tab_size;
676       htab = htab_r;
677
678       i = htab_r->first_free_entry;
679
680       /* Make the increased hash table empty.  The entries are still
681          available in htab->entry_tab.  */
682
683       hash_reset ();
684
685       /* Go through the entries and install them in the pointer vector
686          htab->hash.  The items are actually inserted in htab->entry_tab at
687          the position where they already are.  The htab->coll_link need
688          however be updated.  Could be made a little more efficient.  */
689
690       for (ep = htab_r->entry_tab; i > 0; i--)
691         {
692           hash_insert2 (htab_r, ep->ino, ep->dev);
693           ep++;
694         }
695     }
696
697   return hash_insert2 (htab_r, ino, dev);
698 }
699
700 /* Insert INO and DEV in the hash structure HTAB, if not
701    already present.  Return zero if inserted and non-zero if it
702    already existed.  */
703
704 static int
705 hash_insert2 (htab, ino, dev)
706      struct htab *htab;
707      ino_t ino;
708      dev_t dev;
709 {
710   struct entry **hp, *ep2, *ep;
711   hp = &htab->hash[ino % htab->modulus];
712   ep2 = *hp;
713
714   /* Collision?  */
715
716   if (ep2 != NULL)
717     {
718       ep = ep2;
719
720       /* Search for an entry with the same data.  */
721
722       do
723         {
724           if (ep->ino == ino && ep->dev == dev)
725             return 1;           /* Found an entry with the same data.  */
726           ep = ep->coll_link;
727         }
728       while (ep != NULL);
729
730       /* Did not find it.  */
731
732     }
733
734   ep = *hp = &htab->entry_tab[htab->first_free_entry++];
735   ep->ino = ino;
736   ep->dev = dev;
737   ep->coll_link = ep2;          /* `ep2' is NULL if no collision.  */
738
739   return 0;
740 }
741 \f
742 /* Initialize the struct string S1 for holding SIZE characters.  */
743
744 static void
745 str_init (s1, size)
746      string *s1;
747      unsigned size;
748 {
749   string s;
750
751   s = (string) xmalloc (sizeof (stringstruct));
752   s->text = xmalloc (size + 1);
753
754   s->alloc = size;
755   *s1 = s;
756 }
757
758 static void
759 ensure_space (s, size)
760      string s;
761      unsigned size;
762 {
763   if (s->alloc < size)
764     {
765       s->text = xrealloc (s->text, size + 1);
766       s->alloc = size;
767     }
768 }
769
770 /* Assign the null-terminated C-string CSTR to S1.  */
771
772 static void
773 str_copyc (s1, cstr)
774      string s1;
775      char *cstr;
776 {
777   unsigned l = strlen (cstr);
778   ensure_space (s1, l);
779   strcpy (s1->text, cstr);
780   s1->length = l;
781 }
782
783 static void
784 str_concatc (s1, cstr)
785      string s1;
786      char *cstr;
787 {
788   unsigned l1 = s1->length;
789   unsigned l2 = strlen (cstr);
790   unsigned l = l1 + l2;
791
792   ensure_space (s1, l);
793   strcpy (s1->text + l1, cstr);
794   s1->length = l;
795 }
796
797 /* Truncate the string S1 to have length LENGTH.  */
798
799 static void
800 str_trunc (s1, length)
801      string s1;
802      unsigned length;
803 {
804   if (s1->length > length)
805     {
806       s1->text[length] = 0;
807       s1->length = length;
808     }
809 }