1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1989-2011 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <glenn.s.fowler@gmail.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * Glenn Fowler
23 * AT&T Research
24 *
25 * df -- free disk block report
26 */
27
28 static const char usage[] =
29 "[-?\n@(#)$Id: df (AT&T Research) 2010-08-11 $\n]"
30 USAGE_LICENSE
31 "[+NAME?df - summarize disk free space]"
32 "[+DESCRIPTION?\bdf\b displays the available disk space for the filesystem"
33 " of each file argument. If no \afile\a arguments are given then all"
34 " mounted filesystems are displayed.]"
35
36 "[b:blockbytes?Measure disk usage in 512 byte blocks. This is the default"
37 " if \bgetconf CONFORMANCE\b is \bstandard\b.]"
38 "[D:define?Define \akey\a with optional \avalue\a. \avalue\a will be expanded"
39 " when \b%(\b\akey\a\b)\b is specified in \b--format\b. \akey\a may"
40 " override internal \b--format\b identifiers.]:[key[=value]]]"
41 "[e:decimal-scale|thousands?Scale disk usage to powers of 1000.]"
42 "[f:format?Append to the listing format string. The \bls\b(1), \bpax\b(1) and"
43 " \bps\b(1) commands also have \b--format\b options in this same style."
44 " \aformat\a follows \bprintf\b(3) conventions, except that \bsfio\b(3)"
45 " inline ids are used instead of arguments:"
46 " %[#-+]][\awidth\a[.\aprecis\a[.\abase\a]]]]]](\aid\a[:\asubformat\a]])\achar\a."
47 " If \b#\b is specified then the internal width and precision are used."
48 " If \abase\a is non-zero and \b--posix\b is not on then the field values"
49 " are wrapped when they exceed the field width. If \achar\a is \bs\b then"
50 " the string form of the item is listed, otherwise the corresponding"
51 " numeric form is listed. If \achar\a is \bq\b then the string form of"
52 " the item is $'...' quoted if it contains space or non-printing"
53 " characters. If \awidth\a is omitted then the default width"
54 " is assumed. \asubformat\a overrides the default formatting for \aid\a."
55 " Supported \aid\as and \asubformat\as are:]:[format]{\fformats\f}"
56 "[g:gigabytes?Measure disk usage in 1024M byte blocks.]"
57 "[h!:header|heading?Display a heading line.]"
58 "[i:inodes?Display inode usage instead of block usage. There is at least one"
59 " inode for each active file and directory on a filesystem.]"
60 "[k:kilobytes?Measure disk usage in 1024 byte blocks.]"
61 "[K:scale|binary-scale|human-readable?Scale disk usage to powers of 1024."
62 " This is the default if \bgetconf CONFORMANCE\b is not \bstandard\b.]"
63 "[l:local?List information on local filesystems only, i.e., network"
64 " mounts are omitted.]"
65 "[m:megabytes?Measure disk usage in 1024K byte blocks.]"
66 "[n:native-block?Measure disk usage in the native filesystem block size."
67 " This size may vary between filesystems; it is displayed by the"
68 " \bsize\b format identifier.]"
69 "[O:options?Display the \bmount\b(1) options.]"
70 "[P:portable?Display each filesystem on one line. By default output is"
71 " folded for readability. Also implies \b--blockbytes\b.]"
72 "[s:sync?Call \bsync\b(2) before querying the filesystems.]"
73 "[F|t:type?Display all filesystems of type \atype\a. Unknown types are"
74 " listed as \blocal\b. Typical (but not supported on all systems) values"
75 " are:]:[type]{"
76 " [+ufs?default UNIX file system]"
77 " [+ext2?default linux file system]"
78 " [+xfs?sgi XFS]"
79 " [+nfs?network file system version 2]"
80 " [+nfs3?network file system version 3]"
81 " [+afs3?Andrew file system]"
82 " [+proc?process file system]"
83 " [+fat?DOS FAT file system]"
84 " [+ntfs?nt file system]"
85 " [+reg?windows/nt registry file system]"
86 " [+lofs?loopback file system for submounts]"
87 "}"
88 "[v:verbose?Report all filesystem query errors.]"
89 "[q|T?Ignored by this implementation.]"
90
91 "\n"
92 "\n[ file ... ]\n"
93 "\n"
94 "[+EXAMPLES?The default \b--format\b is"
95 " \"%#..1(filesystem)s %#(type)s %#(blocks)s %#(used)s %#(available)s %#(capacity)s %(mounted)s\"]"
96 "[+SEE ALSO?\bgetconf\b(1), \bmount\b(1), \bls\b(1), \bpax\b(1), \bps\b(1),"
97 " \bmount\b(2)]"
98 ;
99
100 #include <ast.h>
101 #include <error.h>
102 #include <cdt.h>
103 #include <ls.h>
104 #include <mnt.h>
105 #include <sig.h>
106 #include <sfdisc.h>
107
108 typedef struct /* df entry */
109 {
110 Mnt_t* mnt; /* mnt info */
111 struct statvfs vfs; /* statvfs() info */
112 unsigned long avail;
113 unsigned long tavail;
114 unsigned long total;
115 unsigned long ttotal;
116 unsigned long used;
117 unsigned long tused;
118 unsigned long itotal;
119 unsigned long iavail;
120 unsigned long iused;
121 int percent;
122 int fraction;
123 int ipercent;
124 } Df_t;
125
126 typedef struct /* sfkeyprintf() keys */
127 {
128 char* name; /* key name */
129 char* heading; /* key heading */
130 char* description; /* key description */
131 short index; /* key index */
132 short width; /* default width */
133 short abbrev; /* abbreviation width */
134 short disable; /* macro being expanded */
135 char* macro; /* macro definition */
136 Dtlink_t hashed; /* hash link */
137 } Key_t;
138
139 #define KEY_environ (-1)
140
141 #define KEY_available 1
142 #define KEY_blocks 2
143 #define KEY_capacity 3
144 #define KEY_filesystem 4
145 #define KEY_iavailable 5
146 #define KEY_icapacity 6
147 #define KEY_inodes 7
148 #define KEY_iused 8
149 #define KEY_mounted 9
150 #define KEY_native 10
151 #define KEY_options 11
152 #define KEY_type 12
153 #define KEY_used 13
154
155 static Key_t keys[] =
156 {
157
158 {
159 0
160 },
161 {
162 "available",
163 "Available",
164 "Unused block count.",
165 KEY_available,
166 0,
167 5
168 },
169 {
170 "blocks",
171 0,
172 "Total block count.",
173 KEY_blocks,
174 0,
175 0
176 },
177 {
178 "capacity",
179 "Capacity",
180 "Percent of total blocks used.",
181 KEY_capacity,
182 4,
183 3
184 },
185 {
186 "filesystem",
187 "Filesystem",
188 "Filesystem special device name.",
189 KEY_filesystem,
190 -19,
191 0
192 },
193 {
194 "iavailable",
195 "Iavailable",
196 "Unused inode count.",
197 KEY_iavailable,
198 7,
199 6
200 },
201 {
202 "icapacity",
203 "Icapacity",
204 "Percent of total inodes used.",
205 KEY_icapacity,
206 4,
207 4
208 },
209 {
210 "inodes",
211 "Inodes",
212 "Total inode count.",
213 KEY_inodes,
214 7,
215 3
216 },
217 {
218 "iused",
219 "Iused",
220 "Used inode count.",
221 KEY_iused,
222 7,
223 3
224 },
225 {
226 "mounted",
227 "Mounted on",
228 "Mounted on path.",
229 KEY_mounted,
230 -19,
231 5
232 },
233 {
234 "size",
235 "Size",
236 "Native block size.",
237 KEY_native,
238 4,
239 3
240 },
241 {
242 "options",
243 "Options",
244 "\bmount\b(1) options.",
245 KEY_options,
246 -29,
247 3
248 },
249 {
250 "type",
251 "Type",
252 "Filesystem type.",
253 KEY_type,
254 10,
255 3
256 },
257 {
258 "used",
259 "Used",
260 "Used block count.",
261 KEY_used,
262 0,
263 3
264 },
265
266 };
267
268 static const char fmt_def[] = "%#..1(filesystem)s %#(type)s %#(blocks)s %#(used)s %#(available)s %#(capacity)s %(mounted)s";
269 static const char fmt_ino[] = "%#..1(filesystem)s %#(type)s %#(blocks)s %#(available)s %#(capacity)s %#(inodes)s %#(iavailable)s %#(icapacity)s %(mounted)s";
270 static const char fmt_opt[] = "%#..1(filesystem)s %#(type)s %#(options)s %(mounted)s";
271 static const char fmt_std[] = "%#..1(filesystem)s %#(blocks)s %#(used)s %#(available)s %8(capacity)s %(mounted)s";
272
273 /*
274 * man page and header comments notwithstanding
275 * some systems insist on reporting block counts in 512 units
276 * let me know how you probe for this
277 */
278
279 #if _SCO_COFF || _SCO_ELF
280 #define F_FRSIZE(v) (512)
281 #else
282 #if _mem_f_frsize_statvfs
283 #define F_FRSIZE(v) ((v)->f_frsize?(v)->f_frsize:(v)->f_bsize?(v)->f_bsize:1024)
284 #else
285 #define F_FRSIZE(v) ((v)->f_bsize?(v)->f_bsize:1024)
286 #endif
287 #endif
288
289 #if _mem_f_basetype_statvfs
290 #define F_BASETYPE(v,p) ((v)->f_basetype)
291 #else
292 static char*
basetype(const char * path)293 basetype(const char* path)
294 {
295 struct stat st;
296
297 return stat(path, &st) ? "ufs" : fmtfs(&st);
298 }
299 #define F_BASETYPE(v,p) (basetype(p))
300 #endif
301
302 #define REALITY(n) ((((long)(n))<0)?0:(n))
303 #define UNKNOWN "local"
304
305 #if _lib_sync
306 extern void sync(void);
307 #endif
308
309 static struct
310 {
311 int block; /* block unit */
312 int local; /* local mounts only */
313 int posix; /* posix format */
314 int scale; /* metric scale power */
315 int sync; /* sync() first */
316 int timeout; /* status() timed out */
317 int verbose; /* verbose message level {-1,1} */
318 char* type; /* type pattern */
319 Sfio_t* mac; /* temporary macro stream */
320 Sfio_t* tmp; /* really temporary stream */
321 Dt_t* keys; /* format key table */
322 char buf[1024]; /* format item buffer */
323 } state;
324
325 /*
326 * optget() info discipline function
327 */
328
329 static int
optinfo(Opt_t * op,Sfio_t * sp,const char * s,Optdisc_t * dp)330 optinfo(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp)
331 {
332 register int i;
333
334 if (streq(s, "formats"))
335 for (i = 1; i < elementsof(keys); i++)
336 {
337 sfprintf(sp, "[+%s?%s The title string ", keys[i].name, keys[i].description);
338 if (keys[i].heading)
339 sfprintf(sp, "is \b%s\b ", keys[i].heading, keys[i].width);
340 sfprintf(sp, "and the default width ");
341 if (keys[i].width)
342 sfprintf(sp, "is %d.]", keys[i].width);
343 else
344 sfprintf(sp, "%s determined by the \b-b\b, \b-g\b, \b-k\b, \b-m\b and \b-n\b options.]", keys[i].heading ? "is" : "are");
345 }
346 return 0;
347 }
348
349 /*
350 * append format string to fmt
351 */
352
353 static void
append(Sfio_t * fmt,const char * format)354 append(Sfio_t* fmt, const char* format)
355 {
356 if (sfstrtell(fmt))
357 sfputc(fmt, ' ');
358 sfputr(fmt, format, -1);
359 }
360
361 /*
362 * scale <m,w,p> into op
363 */
364
365 static char*
scale(int m,unsigned long w,unsigned long p)366 scale(int m, unsigned long w, unsigned long p)
367 {
368 Sfulong_t n;
369
370 if (state.scale)
371 {
372 n = w;
373 n *= state.block;
374 return fmtscale(n, state.scale);
375 }
376 if (state.block < 1024 * 1024)
377 sfsprintf(state.buf, sizeof(state.buf), "%lu", w);
378 else if (!m || !w && !p || w > 9)
379 sfsprintf(state.buf, sizeof(state.buf), "%lu", w);
380 else
381 sfsprintf(state.buf, sizeof(state.buf), "%lu.%lu", w, p);
382 return state.buf;
383 }
384
385 /*
386 * sfkeyprintf() lookup
387 * handle==0 for heading
388 */
389
390 static int
key(void * handle,register Sffmt_t * fp,const char * arg,char ** ps,Sflong_t * pn)391 key(void* handle, register Sffmt_t* fp, const char* arg, char** ps, Sflong_t* pn)
392 {
393 register Df_t* df = (Df_t*)handle;
394 register char* s = 0;
395 register Sflong_t n = 0;
396 register Key_t* kp;
397 char* t;
398
399 if (!fp->t_str)
400 return 0;
401 if (!(kp = (Key_t*)dtmatch(state.keys, fp->t_str)))
402 {
403 if (*fp->t_str != '$')
404 {
405 error(3, "%s: unknown format key", fp->t_str);
406 return 0;
407 }
408 if (!(kp = newof(0, Key_t, 1, strlen(fp->t_str) + 1)))
409 error(3, "out of space [key]");
410 kp->name = strcpy((char*)(kp + 1), fp->t_str);
411 kp->macro = getenv(fp->t_str + 1);
412 kp->index = KEY_environ;
413 kp->disable = 1;
414 dtinsert(state.keys, kp);
415 }
416 if (kp->macro && !kp->disable)
417 {
418 kp->disable = 1;
419 sfkeyprintf(state.mac, handle, kp->macro, key, NiL);
420 if (!(*ps = sfstruse(state.mac)))
421 error(ERROR_SYSTEM|3, "out of space");
422 kp->disable = 0;
423 }
424 else if (!df)
425 {
426 if (fp->base >= 0)
427 fp->base = -1;
428 s = kp->heading;
429 if (fp->flags & SFFMT_ALTER)
430 {
431 if (!kp->width)
432 kp->width = keys[KEY_blocks].width;
433 if ((fp->width = kp->width) < 0)
434 {
435 fp->width = -fp->width;
436 fp->flags |= SFFMT_LEFT;
437 }
438 fp->precis = fp->width;
439 }
440 if (fp->width > 0 && fp->width < strlen(kp->heading))
441 {
442 if (fp->width < kp->abbrev)
443 {
444 fp->width = kp->abbrev;
445 if (fp->precis >= 0 && fp->precis < fp->width)
446 fp->precis = fp->width;
447 }
448 if (t = newof(0, char, kp->abbrev, 1))
449 s = kp->heading = (char*)memcpy(t, kp->heading, kp->abbrev);
450 }
451 kp->width = fp->width;
452 if (fp->flags & SFFMT_LEFT)
453 kp->width = -kp->width;
454 fp->fmt = 's';
455 *ps = s;
456 }
457 else
458 {
459 if ((fp->flags & SFFMT_ALTER) && (fp->width = kp->width) < 0)
460 {
461 fp->width = -fp->width;
462 fp->flags |= SFFMT_LEFT;
463 }
464 switch (kp->index)
465 {
466 case KEY_available:
467 s = (df->total || df->ttotal) ? scale(df->fraction, df->avail, df->tavail) : "-";
468 break;
469 case KEY_blocks:
470 s = (df->total || df->ttotal) ? scale(df->fraction, df->total, df->ttotal) : "-";
471 break;
472 case KEY_capacity:
473 s = (df->total || df->ttotal) ? (sfsprintf(state.buf, sizeof(state.buf), "%3d%%", df->percent), state.buf) : "-";
474 break;
475 case KEY_environ:
476 if (!(s = kp->macro))
477 return 0;
478 break;
479 case KEY_filesystem:
480 if (!(s = df->mnt->fs))
481 s = "";
482 break;
483 case KEY_iavailable:
484 if (df->total || df->ttotal)
485 n = df->iavail;
486 else
487 s = "-";
488 break;
489 case KEY_icapacity:
490 s = (df->total || df->ttotal) ? (sfsprintf(state.buf, sizeof(state.buf), "%3d%%", df->ipercent), state.buf) : "-";
491 break;
492 case KEY_inodes:
493 if (df->total || df->ttotal)
494 n = df->itotal;
495 else
496 s = "-";
497 break;
498 case KEY_iused:
499 if (df->total || df->ttotal)
500 n = df->iused;
501 else
502 s = "-";
503 break;
504 case KEY_mounted:
505 if (!(s = df->mnt->dir))
506 s = "";
507 break;
508 case KEY_native:
509 if ((n = F_FRSIZE(&df->vfs)) >= 1024)
510 sfsprintf(state.buf, sizeof(state.buf), "%I*3dk", sizeof(n), n / 1024);
511 else
512 sfsprintf(state.buf, sizeof(state.buf), "%I*4d", sizeof(n), n);
513 s = state.buf;
514 break;
515 case KEY_options:
516 if (!(s = df->mnt->options))
517 s = "";
518 break;
519 case KEY_type:
520 if (!(s = df->mnt->type))
521 s = "";
522 break;
523 case KEY_used:
524 s = (df->total || df->ttotal) ? scale(df->fraction, df->used, df->tused) : "-";
525 break;
526 default:
527 return 0;
528 }
529 if (s)
530 {
531 if (fp->base >= 0)
532 {
533 fp->base = -1;
534 if (!state.posix && strlen(s) >= fp->width)
535 {
536 sfprintf(state.tmp, "%s%-*.*s", s, fp->width + 1, fp->width + 1, "\n");
537 if (!(s = sfstruse(state.tmp)))
538 error(ERROR_SYSTEM|3, "out of space");
539 }
540 }
541 *ps = s;
542 }
543 else
544 *pn = n;
545 }
546 return 1;
547 }
548
549 /*
550 * catch statvfs() timeout
551 */
552
553 static void
timeout(int sig)554 timeout(int sig)
555 {
556 state.timeout = 1;
557 }
558
559 /*
560 * statvfs() with timeout
561 */
562
563 static int
status(const char * path,struct statvfs * vfs)564 status(const char* path, struct statvfs* vfs)
565 {
566 int r;
567
568 state.timeout = 0;
569 signal(SIGALRM, timeout);
570 alarm(4);
571 r = statvfs(path, vfs);
572 alarm(0);
573 signal(SIGALRM, SIG_DFL);
574 if (r)
575 {
576 if (state.timeout)
577 error(ERROR_SYSTEM|2, "%s: filesystem stat timed out", path);
578 else
579 error(ERROR_SYSTEM|2, "%s: cannot stat filesystem", path);
580 }
581 return r;
582 }
583
584 /*
585 * list one entry
586 */
587
588 static void
entry(Df_t * df,const char * format)589 entry(Df_t* df, const char* format)
590 {
591 unsigned long b;
592 int s;
593
594 if ((!state.type || strmatch(df->mnt->type, state.type)) && (!state.local || !(df->mnt->flags & MNT_REMOTE)))
595 {
596 if (REALITY(df->vfs.f_blocks) == 0)
597 {
598 df->total = df->avail = df->used = 0;
599 df->percent = 0;
600 }
601 else
602 {
603 /*
604 * NOTE: on some systems vfs.f_* are unsigned,
605 * and on others signed negative values
606 * denote error
607 */
608
609 df->total = df->vfs.f_blocks;
610 df->used = ((long)df->vfs.f_blocks <= (long)df->vfs.f_bfree) ? 0 : (df->vfs.f_blocks - df->vfs.f_bfree);
611 df->avail = ((long)df->vfs.f_bavail < 0) ? 0 : df->vfs.f_bavail;
612 df->percent = (df->ttotal = df->avail + df->used) ? (unsigned long)(((double)df->used / (double)df->ttotal + 0.005) * 100.0) : 0;
613 }
614 df->fraction = 0;
615 df->ttotal = df->tavail = df->tused = 0;
616 if (state.scale)
617 state.block = F_FRSIZE(&df->vfs);
618 else if (state.block)
619 {
620 b = F_FRSIZE(&df->vfs);
621 if (b > state.block)
622 {
623 s = b / state.block;
624 df->total *= s;
625 df->avail *= s;
626 df->used *= s;
627 }
628 else if (b < state.block)
629 {
630 s = state.block / b;
631 if (df->fraction = s / 10)
632 {
633 df->ttotal = (df->total / df->fraction) % 10;
634 df->tavail = (df->avail / df->fraction) % 10;
635 df->tused = (df->used / df->fraction) % 10;
636 }
637 df->total /= s;
638 df->avail /= s;
639 df->used /= s;
640 }
641 }
642 df->itotal = REALITY(df->vfs.f_files);
643 df->iavail = REALITY(df->vfs.f_ffree);
644 if (df->itotal < df->iavail)
645 df->iused = 0;
646 else
647 df->iused = df->itotal - df->iavail;
648 df->iavail = REALITY(df->vfs.f_favail);
649 df->ipercent = (s = df->iused + df->iavail) ? (unsigned long)(((double)df->iused / (double)s + 0.005) * 100.0) : 0;
650 sfkeyprintf(sfstdout, df, format, key, NiL);
651 }
652 }
653
654 int
main(int argc,register char ** argv)655 main(int argc, register char** argv)
656 {
657 register int n;
658 int rem;
659 int head;
660 int i;
661 int* match;
662 dev_t dirdev;
663 dev_t mntdev;
664 dev_t* dev;
665 void* mp;
666 Sfio_t* fmt;
667 Key_t* kp;
668 char* s;
669 char* format;
670 struct stat st;
671 struct statvfs vfs;
672 Dtdisc_t keydisc;
673 Optdisc_t optdisc;
674 Mnt_t mnt;
675 Df_t df;
676
677 error_info.id = "df";
678 state.block = -1;
679 state.posix = -1;
680 state.verbose = -1;
681 dev = 0;
682 head = 1;
683
684 /*
685 * set up the disciplines
686 */
687
688 optinit(&optdisc, optinfo);
689 memset(&keydisc, 0, sizeof(keydisc));
690 keydisc.key = offsetof(Key_t, name);
691 keydisc.size = -1;
692 keydisc.link = offsetof(Key_t, hashed);
693
694 /*
695 * initialize the tables and string streams
696 */
697
698 if (!(fmt = sfstropen()) || !(state.mac = sfstropen()) || !(state.tmp = sfstropen()))
699 error(3, "out of space [fmt]");
700 if (!(state.keys = dtopen(&keydisc, Dtset)))
701 error(3, "out of space [dict]");
702 for (n = 1; n < elementsof(keys); n++)
703 dtinsert(state.keys, keys + n);
704
705 /*
706 * grab the options
707 */
708
709 for (;;)
710 {
711 switch (optget(argv, usage))
712 {
713 case 'b':
714 state.block = 512;
715 continue;
716 case 'D':
717 if (s = strchr(opt_info.arg, '='))
718 *s++ = 0;
719 if (*opt_info.arg == 'n' && *(opt_info.arg + 1) == 'o')
720 {
721 opt_info.arg += 2;
722 s = 0;
723 }
724 if (!(kp = (Key_t*)dtmatch(state.keys, opt_info.arg)))
725 {
726 if (!s)
727 continue;
728 if (!(kp = newof(0, Key_t, 1, strlen(opt_info.arg) + 1)))
729 error(ERROR_SYSTEM|3, "out of space [macro]");
730 kp->name = strcpy((char*)(kp + 1), opt_info.arg);
731 dtinsert(state.keys, kp);
732 }
733 if (kp->macro = s)
734 stresc(s);
735 continue;
736 case 'e':
737 state.scale = 1000;
738 continue;
739 case 'f':
740 append(fmt, opt_info.arg);
741 continue;
742 case 'F':
743 case 't':
744 state.type = opt_info.arg;
745 continue;
746 case 'g':
747 state.block = 1024 * 1024 * 1024;
748 continue;
749 case 'h':
750 head = opt_info.num;
751 continue;
752 case 'i':
753 append(fmt, fmt_ino);
754 continue;
755 case 'k':
756 state.block = 1024;
757 continue;
758 case 'K':
759 state.scale = 1024;
760 continue;
761 case 'l':
762 state.local = 1;
763 continue;
764 case 'm':
765 state.block = 1024 * 1024;
766 continue;
767 case 'n':
768 state.block = 0;
769 continue;
770 case 'O':
771 append(fmt, fmt_opt);
772 continue;
773 case 'P':
774 state.posix = 1;
775 continue;
776 case 'q':
777 continue;
778 case 's':
779 state.sync = opt_info.num;
780 continue;
781 case 'T':
782 continue;
783 case 'v':
784 state.verbose = ERROR_SYSTEM|1;
785 continue;
786 case '?':
787 error(ERROR_USAGE|4, "%s", opt_info.arg);
788 break;
789 case ':':
790 error(2, "%s", opt_info.arg);
791 break;
792 }
793 break;
794 }
795 if (error_info.errors)
796 error(ERROR_USAGE|4, "%s", optusage(NiL));
797 argc -= opt_info.index;
798 argv += opt_info.index;
799 if (!sfstrtell(fmt))
800 append(fmt, state.posix > 0 ? fmt_std : fmt_def);
801 sfputc(fmt, '\n');
802 if (!(format = sfstruse(fmt)))
803 error(ERROR_SYSTEM|3, "out of space");
804 stresc(format);
805 if (state.posix < 0)
806 state.posix = !!conformance(0, 0);
807 if (state.block < 0)
808 {
809 if (state.posix)
810 state.block = 512;
811 else
812 {
813 state.block = 1024 * 1024;
814 if (!state.scale)
815 state.scale = 1024;
816 }
817 }
818 s = 0;
819 if (state.scale)
820 {
821 n = 5;
822 s = "Size";
823 }
824 else if (state.block <= 512)
825 {
826 n = 10;
827 if (!state.block)
828 s = "blocks";
829 }
830 else if (state.block <= 1024)
831 {
832 n = 8;
833 if (state.block == 1024)
834 s = "Kbytes";
835 }
836 else if (state.block <= 1024 * 1024)
837 {
838 n = 6;
839 if (state.block == 1024 * 1024)
840 s = "Mbytes";
841 }
842 else if (state.block <= 1024 * 1024 * 1024)
843 {
844 n = 6;
845 if (state.block == 1024 * 1024 * 1024)
846 s = "Gbytes";
847 }
848 if (!s)
849 {
850 sfprintf(state.mac, "%d-blocks", state.block);
851 if (!(s = strdup(sfstruse(state.mac))))
852 error(ERROR_SYSTEM|3, "out of space [heading]");
853 n = strlen(s);
854 }
855 keys[KEY_blocks].width = n;
856 keys[KEY_blocks].heading = s;
857 #if _lib_sync
858 if (state.sync)
859 sync();
860 #endif
861 if (!(mp = mntopen(NiL, "r")))
862 {
863 error(ERROR_SYSTEM|(argc > 0 ? 1 : 3), "cannot access mount table");
864 sfkeyprintf(head ? sfstdout : state.tmp, NiL, format, key, NiL);
865 sfstrseek(state.tmp, 0, SEEK_SET);
866 df.mnt = &mnt;
867 mnt.dir = UNKNOWN;
868 mnt.type = 0;
869 mnt.flags = 0;
870 while (mnt.fs = *argv++)
871 if (!status(mnt.fs, &df.vfs))
872 entry(&df, format);
873 }
874 else
875 {
876 sfkeyprintf(head ? sfstdout : state.tmp, NiL, format, key, NiL);
877 sfstrseek(state.tmp, 0, SEEK_SET);
878 if (argc)
879 {
880 rem = argc;
881 if (!(dev = newof(0, dev_t, argc, 0)))
882 error(ERROR_SYSTEM|3, "out of space [dev_t]");
883 for (n = 0; n < argc; n++)
884 if (stat(argv[n], &st))
885 {
886 error(ERROR_SYSTEM|2, "%s: cannot stat", argv[n]);
887 argv[n] = 0;
888 rem--;
889 }
890 else
891 dev[n] =
892 #if _mem_st_rdev_stat
893 S_ISBLK(st.st_mode) ? st.st_rdev :
894 #endif
895 st.st_dev;
896 }
897 else
898 rem = 0;
899 if (rem && (match = newof(0, int, n, 0)))
900 {
901 /*
902 * scan the mount table (up to 3x) for prefix matches
903 * to avoid the more expensive stat() and statvfs()
904 * calls in the final loop below
905 */
906
907 while (df.mnt = mntread(mp))
908
909 for (n = 0; n < argc; n++)
910 if (argv[n] && (i = strlen(df.mnt->dir)) > 1 && i > match[n] && strneq(argv[n], df.mnt->dir, i) && (argv[n][i] == '/' || !argv[n][i]))
911 match[n] = i;
912 mntclose(mp);
913 if (!(mp = mntopen(NiL, "r")))
914 error(ERROR_SYSTEM|3, "cannot reopen mount table");
915 while (rem && (df.mnt = mntread(mp)))
916 for (n = 0; n < argc; n++)
917 if (match[n] && (i = strlen(df.mnt->dir)) == match[n] && strneq(argv[n], df.mnt->dir, i) && (argv[n][i] == '/' || !argv[n][i]))
918 {
919 argv[n][match[n]] = 0;
920 if (!status(argv[n], &df.vfs))
921 {
922 entry(&df, format);
923 argv[n] = 0;
924 match[n] = 0;
925 if (!--rem)
926 break;
927 }
928 }
929 free(match);
930 if (rem)
931 {
932 mntclose(mp);
933 if (!(mp = mntopen(NiL, "r")))
934 error(ERROR_SYSTEM|3, "cannot reopen mount table");
935 }
936 }
937 while ((!argc || rem) && (df.mnt = mntread(mp)))
938 {
939 if (stat(df.mnt->dir, &st))
940 {
941 if (errno != ENOENT && errno != ENOTDIR)
942 error(state.verbose, "%s: cannot stat", df.mnt->dir);
943 continue;
944 }
945 dirdev = st.st_dev;
946 mntdev = (!rem || *df.mnt->fs != '/' || stat(df.mnt->fs, &st)) ? dirdev : st.st_dev;
947 if (rem)
948 {
949 for (n = 0; n < argc; n++)
950 if (argv[n] && (dev[n] == dirdev || dev[n] == mntdev))
951 {
952 argv[n] = 0;
953 rem--;
954 break;
955 }
956 if (n >= argc)
957 continue;
958 }
959 if (!status(df.mnt->dir, &df.vfs) || !status(df.mnt->fs, &df.vfs))
960 entry(&df, format);
961 if (rem)
962 {
963 while (++n < argc)
964 if (dev[n] == dirdev || dev[n] == mntdev)
965 {
966 argv[n] = 0;
967 rem--;
968 }
969 if (rem <= 0)
970 break;
971 }
972 }
973 mntclose(mp);
974 if (argc > 0)
975 {
976 df.mnt = &mnt;
977 memset(&mnt, 0, sizeof(mnt));
978 for (n = 0; n < argc; n++)
979 if ((mnt.dir = argv[n]) && !status(mnt.dir, &vfs))
980 entry(&df, format);
981 }
982 }
983 return error_info.errors != 0;
984 }
985