sbase

suckless unix tools
git clone git://git.2f30.org/sbase
Log | Files | Refs | README | LICENSE

commit 09fcbfc338f7401019565ef286d3cc508a59f4e9
parent 84ba9cb87e5fdb5448e5ba7683f61e88f6736430
Author: sin <sin@2f30.org>
Date:   Wed, 16 Oct 2013 16:58:52 +0100

Add primitive du(1)

Defaults to a 512-byte blocksize.

Diffstat:
MMakefile | 1+
Adu.1 | 18++++++++++++++++++
Adu.c | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 139 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -39,6 +39,7 @@ SRC = \ cut.c \ date.c \ dirname.c \ + du.c \ echo.c \ env.c \ expand.c \ diff --git a/du.1 b/du.1 @@ -0,0 +1,18 @@ +.TH DU 1 sbase\-VERSION +.SH NAME +du \- display disk usage statistics +.SH SYNOPSIS +.B du +.RB [ \-a ] +.RI [ file ...] +.SH DESCRIPTION +.B du +displays the file system block usage for each +.I file +argument and for each directory in the file hierarchy rooted in directory argument. +If no file is specified, the block usage of the hierarchy rooted in the current +directory is displayed. +.SH OPTIONS +.TP +.BI \-a +Display an entry for each file in the file hierarchy. diff --git a/du.c b/du.c @@ -0,0 +1,120 @@ +/* See LICENSE file for copyright and license details. */ +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <limits.h> +#include "util.h" + +static long blksize = 512; + +static bool aflag = false; + +static long du(const char *); + +void +usage(void) +{ + eprintf("usage: %s [-a] [file...]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + char *bsize; + + ARGBEGIN { + case 'a': + aflag = true; + break; + default: + usage(); + } ARGEND; + + bsize = getenv("BLOCKSIZE"); + if (bsize) + blksize = estrtol(bsize, 0); + + if (argc < 1) { + du("."); + } else { + for (; argc > 0; argc--, argv++) + du(argv[0]); + } + return EXIT_SUCCESS; +} + +static void +print(long n, char *path) +{ + printf("%lu\t%s\n", n, path); + free(path); +} + +static char * +push(const char *path) +{ + char *cwd; + + cwd = agetcwd(); + if (chdir(path) < 0) + eprintf("chdir: %s:", path); + return cwd; +} + +static void +pop(char *path) +{ + if (chdir(path) < 0) + eprintf("chdir: %s:", path); + free(path); +} + +static long +du(const char *path) +{ + DIR *dp; + char *cwd; + struct dirent *dent; + struct stat st; + long n = 0, m; + + if (lstat(path, &st) < 0) + eprintf("stat: %s:", path); + n = 512 * st.st_blocks / blksize; + + if (S_ISDIR(st.st_mode)) { + dp = opendir(path); + if (!dp) { + fprintf(stderr, "opendir: %s: %s\n", path, + strerror(errno)); + } else { + cwd = push(path); + while ((dent = readdir(dp))) { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + if (lstat(dent->d_name, &st) < 0) + eprintf("stat: %s:", dent->d_name); + if (S_ISDIR(st.st_mode)) { + n += du(dent->d_name); + } else { + m = 512 * st.st_blocks / blksize; + n += m; + if (aflag) + print(m, realpath(dent->d_name, NULL)); + } + } + pop(cwd); + closedir(dp); + } + } + + print(n, realpath(path, NULL)); + return n; +}