| /* df.c - report free disk space. |
| * |
| * Copyright 2006 Rob Landley <rob@landley.net> |
| * |
| * See http://opengroup.org/onlinepubs/9699919799/utilities/df.html |
| |
| USE_DF(NEWTOY(df, "HPkhit*a[-HPh]", TOYFLAG_SBIN)) |
| |
| config DF |
| bool "df" |
| default y |
| help |
| usage: df [-HPkhi] [-t type] [FILE...] |
| |
| The "disk free" command shows total/used/available disk space for |
| each filesystem listed on the command line, or all currently mounted |
| filesystems. |
| |
| -a Show all (including /proc and friends) |
| -P The SUSv3 "Pedantic" option |
| -k Sets units back to 1024 bytes (the default without -P) |
| -h Human readable (K=1024) |
| -H Human readable (k=1000) |
| -i Show inodes instead of blocks |
| -t type Display only filesystems of this type |
| |
| Pedantic provides a slightly less useful output format dictated by POSIX, |
| and sets the units to 512 bytes instead of the default 1024 bytes. |
| */ |
| |
| #define FOR_df |
| #include "toys.h" |
| |
| GLOBALS( |
| struct arg_list *t; |
| |
| int units, width[6]; |
| ) |
| |
| static void measure_columns(char *s[]) |
| { |
| int i; |
| |
| for (i = 0; i<5; i++) TT.width[i] = maxof(TT.width[i], strlen(s[i])); |
| } |
| |
| static void print_columns(char **dsuapm) |
| { |
| int i; |
| |
| for (i = 0; i<6; i++) printf(!i ? "%-*s" : " %*s", TT.width[i], dsuapm[i]); |
| xputc('\n'); |
| } |
| |
| static void print_header() |
| { |
| char *dsuapm[] = {"Filesystem", "Size", "Used", "Avail", "Use%","Mounted on"}; |
| |
| // The filesystem column is always at least this wide. |
| TT.width[0] = maxof(TT.width[0], 14+(FLAG(H)||FLAG(h))); |
| |
| if (FLAG(i)) memcpy(dsuapm+1, (char *[]){"Inodes", "IUsed", "IFree", "IUse%"}, |
| sizeof(char *)*4); |
| else { |
| if (!(FLAG(H)||FLAG(h))) { |
| dsuapm[1] = TT.units == 512 ? "512-blocks" : |
| FLAG(P) ? "1024-blocks" : "1K-blocks"; |
| dsuapm[3] = "Available"; |
| if (FLAG(P)) dsuapm[4] = "Capacity"; |
| } |
| } |
| |
| measure_columns(dsuapm); |
| TT.width[5] = -1; |
| print_columns(dsuapm); |
| } |
| |
| static void show_mt(struct mtab_list *mt, int measuring) |
| { |
| unsigned long long suap[4], block = 1, ll; |
| char *dsuapm[6]; // device, size, used, avail, percent, mount |
| int i; |
| |
| // If we don't have -a, skip overmounted and synthetic filesystems. |
| if (!mt || (!FLAG(a) && (!mt->stat.st_dev || !mt->statvfs.f_blocks))) return; |
| |
| // If we have -t, skip other filesystem types |
| if (TT.t) { |
| struct arg_list *al; |
| |
| for (al = TT.t; al; al = al->next) if (!strcmp(mt->type, al->arg)) break; |
| |
| if (!al) return; |
| } |
| |
| // Prepare filesystem display fields |
| *dsuapm = *mt->device == '/' ? xabspath(mt->device, 0) : 0; |
| if (!*dsuapm) *dsuapm = mt->device; |
| if (!mt->stat.st_dev) for (i = 1; i<5; i++) dsuapm[i] = "-"; |
| else { |
| if (FLAG(i)) { |
| suap[0] = mt->statvfs.f_files; |
| suap[1] = mt->statvfs.f_files - mt->statvfs.f_ffree; |
| suap[2] = getuid() ? mt->statvfs.f_favail : mt->statvfs.f_ffree; |
| } else { |
| block = maxof(mt->statvfs.f_frsize, 1); |
| suap[0] = mt->statvfs.f_blocks; |
| suap[1] = mt->statvfs.f_blocks - mt->statvfs.f_bfree; |
| suap[2] = getuid() ? mt->statvfs.f_bavail : mt->statvfs.f_bfree; |
| } |
| |
| // Scale and convert to strings |
| dsuapm[1] = toybuf; |
| for (i = 0; i<3; i++) { |
| suap[i] = (block*suap[i])/TT.units; |
| |
| if (FLAG(H)||FLAG(h)) |
| human_readable(dsuapm[i+1], suap[i], FLAG(H) ? HR_1000 : 0); |
| else sprintf(dsuapm[i+1], "%llu", suap[i]); |
| dsuapm[i+2] = strchr(dsuapm[i+1], 0)+1; |
| } |
| |
| // percent |
| if ((suap[3] = ll = suap[1]+suap[2])) { |
| suap[3] = (block = suap[1]*100)/ll; |
| if (block != suap[3]*ll) suap[3]++; |
| } |
| sprintf(dsuapm[4], "%llu%%", suap[3]); |
| } |
| dsuapm[5] = mt->dir; |
| |
| if (measuring) measure_columns(dsuapm); |
| else print_columns(dsuapm); |
| |
| if (*dsuapm != mt->device) free(*dsuapm); |
| } |
| |
| void df_main(void) |
| { |
| struct mtab_list *mt, *mtstart, *mtend, *mt2, *mt3; |
| int measuring; |
| char **next; |
| |
| // Units are 512 bytes if you select "pedantic" without "kilobytes". |
| if (FLAG(H)||FLAG(h)||FLAG(i)) TT.units = 1; |
| else TT.units = FLAG(P) && !FLAG(k) ? 512 : 1024; |
| |
| if (!(mtstart = xgetmountlist(0))) return; |
| mtend = dlist_terminate(mtstart); |
| |
| // If we have a list of filesystems on the command line, loop through them. |
| if (*toys.optargs) { |
| // Measure the names then output the table. |
| for (measuring = 1;;) { |
| for (next = toys.optargs; *next; next++) { |
| struct stat st; |
| |
| // Stat it (complain if we can't). |
| if (stat(*next, &st)) { |
| if (!measuring) perror_msg("'%s'", *next); |
| } else { |
| // Find and display this filesystem. Use _last_ hit in case of |
| // overmounts (which is first hit in the reversed list). |
| for (mt = mtend, mt2 = 0; mt; mt = mt->prev) { |
| if (!mt2 && st.st_dev == mt->stat.st_dev) mt2 = mt; |
| if (st.st_rdev && (st.st_rdev == mt->stat.st_dev)) break; |
| } |
| show_mt(mt ? : mt2, measuring); |
| } |
| } |
| if (!measuring--) break; |
| print_header(); |
| } |
| } else { |
| // Loop through mount list to filter out overmounts. |
| for (mt = mtend; mt; mt = mt->prev) { |
| for (mt3 = mt, mt2 = mt->prev; mt2; mt2 = mt2->prev) { |
| if (mt->stat.st_dev == mt2->stat.st_dev) { |
| // For --bind mounts, show earliest mount |
| if (!strcmp(mt->device, mt2->device)) { |
| mt3->stat.st_dev = 0; |
| mt3 = mt2; |
| } else mt2->stat.st_dev = 0; |
| } |
| } |
| } |
| |
| // Measure the names then output the table (in filesystem creation order). |
| for (measuring = 1;;) { |
| for (mt = mtstart; mt; mt = mt->next) show_mt(mt, measuring); |
| if (!measuring--) break; |
| print_header(); |
| } |
| } |
| |
| if (CFG_TOYBOX_FREE) llist_traverse(mtstart, free); |
| } |