df.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /* df.c - report free disk space.
  2. *
  3. * Copyright 2006 Rob Landley <rob@landley.net>
  4. *
  5. * See http://opengroup.org/onlinepubs/9699919799/utilities/df.html
  6. USE_DF(NEWTOY(df, "HPkhit*a[-HPh]", TOYFLAG_SBIN))
  7. config DF
  8. bool "df"
  9. default y
  10. help
  11. usage: df [-HPkhi] [-t type] [FILE...]
  12. The "disk free" command shows total/used/available disk space for
  13. each filesystem listed on the command line, or all currently mounted
  14. filesystems.
  15. -a Show all (including /proc and friends)
  16. -P The SUSv3 "Pedantic" option
  17. -k Sets units back to 1024 bytes (the default without -P)
  18. -h Human readable (K=1024)
  19. -H Human readable (k=1000)
  20. -i Show inodes instead of blocks
  21. -t type Display only filesystems of this type
  22. Pedantic provides a slightly less useful output format dictated by POSIX,
  23. and sets the units to 512 bytes instead of the default 1024 bytes.
  24. */
  25. #define FOR_df
  26. #include "toys.h"
  27. GLOBALS(
  28. struct arg_list *t;
  29. int units, width[6];
  30. )
  31. static void measure_columns(char *s[])
  32. {
  33. int i;
  34. for (i = 0; i<5; i++) TT.width[i] = maxof(TT.width[i], strlen(s[i]));
  35. }
  36. static void print_columns(char **dsuapm)
  37. {
  38. int i;
  39. for (i = 0; i<6; i++) printf(!i ? "%-*s" : " %*s", TT.width[i], dsuapm[i]);
  40. xputc('\n');
  41. }
  42. static void print_header()
  43. {
  44. char *dsuapm[] = {"Filesystem", "Size", "Used", "Avail", "Use%","Mounted on"};
  45. // The filesystem column is always at least this wide.
  46. TT.width[0] = maxof(TT.width[0], 14+(FLAG(H)||FLAG(h)));
  47. if (FLAG(i)) memcpy(dsuapm+1, (char *[]){"Inodes", "IUsed", "IFree", "IUse%"},
  48. sizeof(char *)*4);
  49. else {
  50. if (!(FLAG(H)||FLAG(h))) {
  51. dsuapm[1] = TT.units == 512 ? "512-blocks" :
  52. FLAG(P) ? "1024-blocks" : "1K-blocks";
  53. dsuapm[3] = "Available";
  54. if (FLAG(P)) dsuapm[4] = "Capacity";
  55. }
  56. }
  57. measure_columns(dsuapm);
  58. TT.width[5] = -1;
  59. print_columns(dsuapm);
  60. }
  61. static void show_mt(struct mtab_list *mt, int measuring)
  62. {
  63. unsigned long long suap[4], block = 1, ll;
  64. char *dsuapm[6]; // device, size, used, avail, percent, mount
  65. int i;
  66. // If we don't have -a, skip overmounted and synthetic filesystems.
  67. if (!mt || (!FLAG(a) && (!mt->stat.st_dev || !mt->statvfs.f_blocks))) return;
  68. // If we have -t, skip other filesystem types
  69. if (TT.t) {
  70. struct arg_list *al;
  71. for (al = TT.t; al; al = al->next) if (!strcmp(mt->type, al->arg)) break;
  72. if (!al) return;
  73. }
  74. // Prepare filesystem display fields
  75. *dsuapm = *mt->device == '/' ? xabspath(mt->device, 0) : 0;
  76. if (!*dsuapm) *dsuapm = mt->device;
  77. if (!mt->stat.st_dev) for (i = 1; i<5; i++) dsuapm[i] = "-";
  78. else {
  79. if (FLAG(i)) {
  80. suap[0] = mt->statvfs.f_files;
  81. suap[1] = mt->statvfs.f_files - mt->statvfs.f_ffree;
  82. suap[2] = geteuid() ? mt->statvfs.f_favail : mt->statvfs.f_ffree;
  83. } else {
  84. block = maxof(mt->statvfs.f_frsize, 1);
  85. suap[0] = mt->statvfs.f_blocks;
  86. suap[1] = mt->statvfs.f_blocks - mt->statvfs.f_bfree;
  87. suap[2] = geteuid() ? mt->statvfs.f_bavail : mt->statvfs.f_bfree;
  88. }
  89. // Scale and convert to strings
  90. dsuapm[1] = toybuf;
  91. for (i = 0; i<3; i++) {
  92. suap[i] = (block*suap[i])/TT.units;
  93. if (FLAG(H)||FLAG(h))
  94. human_readable(dsuapm[i+1], suap[i], FLAG(H) ? HR_1000 : 0);
  95. else sprintf(dsuapm[i+1], "%llu", suap[i]);
  96. dsuapm[i+2] = strchr(dsuapm[i+1], 0)+1;
  97. }
  98. // percent
  99. if ((suap[3] = ll = suap[1]+suap[2])) {
  100. suap[3] = (block = suap[1]*100)/ll;
  101. if (block != suap[3]*ll) suap[3]++;
  102. }
  103. sprintf(dsuapm[4], "%llu%%", suap[3]);
  104. }
  105. dsuapm[5] = mt->dir;
  106. if (measuring) measure_columns(dsuapm);
  107. else print_columns(dsuapm);
  108. if (*dsuapm != mt->device) free(*dsuapm);
  109. }
  110. void df_main(void)
  111. {
  112. struct mtab_list *mt, *mtstart, *mtend, *mt2, *mt3;
  113. int measuring;
  114. char **next;
  115. // Units are 512 bytes if you select "pedantic" without "kilobytes".
  116. if (FLAG(H)||FLAG(h)||FLAG(i)) TT.units = 1;
  117. else TT.units = FLAG(P) && !FLAG(k) ? 512 : 1024;
  118. if (!(mtstart = xgetmountlist(0))) return;
  119. mtend = dlist_terminate(mtstart);
  120. // If we have a list of filesystems on the command line, loop through them.
  121. if (*toys.optargs) {
  122. // Measure the names then output the table.
  123. for (measuring = 1;;) {
  124. for (next = toys.optargs; *next; next++) {
  125. struct stat st;
  126. // Stat it (complain if we can't).
  127. if (stat(*next, &st)) {
  128. if (!measuring) perror_msg("'%s'", *next);
  129. } else {
  130. // Find and display this filesystem. Use _last_ hit in case of
  131. // overmounts (which is first hit in the reversed list).
  132. for (mt = mtend, mt2 = 0; mt; mt = mt->prev) {
  133. if (!mt2 && st.st_dev == mt->stat.st_dev) mt2 = mt;
  134. if (st.st_rdev && (st.st_rdev == mt->stat.st_dev)) break;
  135. }
  136. show_mt(mt ? : mt2, measuring);
  137. }
  138. }
  139. if (!measuring--) break;
  140. print_header();
  141. }
  142. } else {
  143. // Loop through mount list to filter out overmounts.
  144. for (mt = mtend; mt; mt = mt->prev) {
  145. for (mt3 = mt, mt2 = mt->prev; mt2; mt2 = mt2->prev) {
  146. if (mt->stat.st_dev == mt2->stat.st_dev) {
  147. // For --bind mounts, show earliest mount
  148. if (!strcmp(mt->device, mt2->device)) {
  149. mt3->stat.st_dev = 0;
  150. mt3 = mt2;
  151. } else mt2->stat.st_dev = 0;
  152. }
  153. }
  154. }
  155. // Measure the names then output the table (in filesystem creation order).
  156. for (measuring = 1;;) {
  157. for (mt = mtstart; mt; mt = mt->next) show_mt(mt, measuring);
  158. if (!measuring--) break;
  159. print_header();
  160. }
  161. }
  162. if (CFG_TOYBOX_FREE) llist_traverse(mtstart, free);
  163. }