Thursday, March 19, 2009

sizeof

Even though this tool isn't really done, I've decided to release what I've built so far.

This program finds the size of a directory or file's contents based on the arguments given, then scales it down into human-readable output. It's a great tool for tracking the largest files on disk, and is far more reliable than du, despite being slower.

sizeof.c

#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <signal.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#define MAX_FLEN PATH_MAX
const char * failname = NULL;

void seg() {
perror(failname);
fprintf(stderr, "%s: Unable to recover; exiting.\n", failname);
exit(255);
}

off_t get_size(const char * dirname, ino_t dinod) {
struct stat info;
DIR * dirinfo;
struct dirent * file;
off_t total = 0;
char tmp[MAX_FLEN], * tmp_target;
memset(tmp, '\0', MAX_FLEN);
size_t tmp_target_len;
strncpy(tmp, dirname, MAX_FLEN - 1);
strcat(tmp, "/");
tmp_target = tmp + (strlen(tmp));
tmp_target_len = MAX_FLEN - strlen(tmp_target) - 2;
dirinfo = opendir(dirname);
failname = dirname;
if(dirinfo != NULL) {
while((file = readdir(dirinfo)) != NULL) {
if(file->d_name[0] != '.') {
strncpy(tmp_target, file->d_name, tmp_target_len);
failname = tmp;
if(!stat(tmp, &info)) {
if(S_ISDIR(info.st_mode)) {
if(info.st_ino != dinod) // Prevent symlinks to self
total += get_size(tmp, info.st_ino);
else
fprintf(stderr, "%s: Directory symlink to self.\n", tmp);
}
else if(S_ISREG(info.st_mode))
total += info.st_size;
}
else
perror(file->d_name);
}
}
closedir(dirinfo);
}
else
perror(dirname);
return total;
}
void scale_down(double * total, const char ** suffix) {
if(*total >= (1 << 30)) {
*total /= (1 << 30);
*suffix = "gigabytes";
}
else if(*total >= (1 << 20)) {
*total /= (1 << 20);
*suffix = "megabytes";
}
else if(*total >= (1 << 10)) {
*total /= (1 << 10);
*suffix = "kilobytes";
}
}


int main(int argc, const char ** argv) {
double total;
const char * suffix;
int i;
struct stat info;
signal(11, &seg);
if(argc > 2) {
for(i = 1; i < argc; i++) {
if(!stat(argv[i], &info)) {
suffix = "bytes";
if(S_ISDIR(info.st_mode))
total = get_size(argv[i], 0);
else if(S_ISREG(info.st_mode))
total = info.st_size;
scale_down(&total, &suffix);
printf("%-40s: %.2lf %s\n", argv[i], total, suffix);
}
else
perror(argv[i]);
}
return 0;
}
else if(argc == 2) {
if(!stat(argv[1], &info)) {
suffix = "bytes";
if(S_ISDIR(info.st_mode))
total = get_size(argv[1], 0);
else if(S_ISREG(info.st_mode))
total = info.st_size;
scale_down(&total, &suffix);
printf("%.2lf %s\n", total, suffix);
return 0;
}
else {
perror(argv[2]);
return 1;
}
}
else {
fprintf(stderr, "Usage: %s directory\n", *argv);
return 1;
}
}
% ./sizeof *
Makefile : 291.00 bytes
sizeof : 19.33 kilobytes
sizeof.c : 2.51 kilobytes
sizeof.c.html : 23.34 kilobytes
sizeof.o : 13.17 kilobytes
sizeof.s : 8.08 kilobytes
sizeof_un.s : 7.80 kilobytes

No comments: