aboutsummaryrefslogtreecommitdiff
path: root/src/df.c
blob: 9c0b5eb9ee40561a7987cfe3c319beb8163a66d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 500

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/statvfs.h>
#include <math.h>
#include <limits.h>

static unsigned long g_output_block_size = 512;

typedef struct
{
	char name[PATH_MAX + 1];
	char mount[PATH_MAX + 1];
} t_fs_info;

static t_fs_info g_fs_info_dummy = { .name = "", .mount = "" };

static t_fs_info *g_fs_infos      = NULL;
static size_t     g_fs_infos_size = 0;

void put_file_fs_info(char *file_relpath, bool allow_empty)
{
	char file_path[PATH_MAX + 1] = {'\0'};
	realpath(file_relpath, file_path);

	struct statvfs statvfsbuf;
	if (statvfs(file_path, &statvfsbuf) == -1)
		exit(1);

	long block_size     = statvfsbuf.f_bsize;
	unsigned long total = statvfsbuf.f_blocks * statvfsbuf.f_frsize / g_output_block_size;
	long used           = total - statvfsbuf.f_bfree  * block_size / g_output_block_size;
	long available      = statvfsbuf.f_bavail * block_size / g_output_block_size;

	bool empty_fs = used + available == 0;
	if (empty_fs && !allow_empty)
		return;

	t_fs_info *fs_info = &g_fs_info_dummy;
	for (size_t i = 0; i < g_fs_infos_size; i++)
	{
		char *mount = g_fs_infos[i].mount;
		size_t mount_len = strlen(mount);
		if (strncmp(file_path, mount, mount_len) == 0 && mount_len > strlen(fs_info->mount))
			fs_info = &g_fs_infos[i];
	}

	printf("%-15s", fs_info->name);
	printf("%11ld",  total);
	printf("%9ld",   used);
	printf("%10ld",  available);
	if (!empty_fs)
		printf("%8.0f%%", ceil(((double)used / (double)(used + available)) * 100));
	else
		printf("%9c", '-');
	printf(" %s", fs_info->mount);
	fputc('\n', stdout);
}

int main(int argc, char **argv)
{
	int option;

	while ((option = getopt(argc, argv, "kPt")) != -1)
	{
		switch (option)
		{
			case 'k':
				g_output_block_size = 1024;
				break;
			case 'P':
			case 't':
				break;
		}
	}

	FILE *mounts_file = fopen("/etc/mtab", "r");
	if (mounts_file == NULL)
		exit(1);
	while (!feof(mounts_file))
	{
		g_fs_infos_size++;
		g_fs_infos = realloc(g_fs_infos, sizeof(t_fs_info) * g_fs_infos_size);
		if (g_fs_infos == NULL)
		{
			fclose(mounts_file);
			exit(1);
		}
		t_fs_info *last = &g_fs_infos[g_fs_infos_size - 1];
		fscanf(mounts_file, "%s %s", &last->name, &last->mount);
		int c;
		do
			c = fgetc(mounts_file);
		while (c != '\n' && c != EOF);
	}
	fclose(mounts_file);
	g_fs_infos_size--;

	printf("Filesystem     %4lu-blocks     Used Available Capacity Mounted on\n", g_output_block_size);

	if (optind == argc)
	{
		for (size_t i = 0; i < g_fs_infos_size; i++)
			put_file_fs_info(g_fs_infos[i].mount, false);
		return 0;
	}
	for (; optind < argc; optind++)
		put_file_fs_info(argv[optind], true);
	free(g_fs_infos);
	return 0;
}