1//	df - for Haiku
2//
3//	authors, in order of contribution:
4//	jonas.sundstrom@kirilla.com
5//	axeld@pinc-software.de
6//
7
8
9#include <Volume.h>
10#include <Directory.h>
11#include <Path.h>
12
13#include <fs_info.h>
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <ctype.h>
19#include <errno.h>
20
21
22void
23PrintFlag(uint32 deviceFlags, uint32 testFlag, char yes, char no)
24{
25	printf("%c", deviceFlags & testFlag ? yes : no);
26}
27
28
29void
30PrintMountPoint(dev_t device, bool verbose)
31{
32	char mount[B_PATH_NAME_LENGTH];
33	mount[0] = '\0';
34
35	BVolume	volume(device);
36	BDirectory root;
37	if (volume.GetRootDirectory(&root) == B_OK) {
38		BPath path(&root, NULL);
39		if (path.InitCheck() == B_OK)
40			strlcpy(mount, path.Path(), sizeof(mount));
41		else
42			strlcpy(mount, "?", sizeof(mount));
43	}
44
45	if (verbose)
46		printf("   Mounted at: %s\n", mount);
47	else {
48		printf("%-17s ", mount);
49		if (strlen(mount) > 17)
50			printf("\n%17s ", "");
51	}
52}
53
54
55void
56PrintType(const char *fileSystem)
57{
58	char type[16];
59	strlcpy(type, fileSystem, sizeof(type));
60
61	printf("%-9s", type);
62}
63
64
65const char *
66ByteString(int64 numBlocks, int64 blockSize)
67{
68	double blocks = 1. * numBlocks * blockSize;
69	static char string[64];
70
71	if (blocks < 1024)
72		sprintf(string, "%" B_PRId64, numBlocks * blockSize);
73	else {
74		const char *units[] = {"KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
75							   "ZiB", "YiB", NULL};
76		int32 i = -1;
77
78		do {
79			blocks /= 1024.0;
80			i++;
81		} while (blocks >= 1024 && units[i + 1]);
82
83		sprintf(string, "%.1f %s", blocks, units[i]);
84	}
85
86	return string;
87}
88
89
90void
91PrintBlocks(int64 blocks, int64 blockSize, bool showBlocks)
92{
93	char temp[1024];
94
95	if (showBlocks)
96		sprintf(temp, "%" B_PRId64, blocks * (blockSize / 1024));
97	else
98		strcpy(temp, ByteString(blocks, blockSize));
99
100	printf("%10s", temp);
101}
102
103
104void
105PrintVerbose(dev_t device)
106{
107	fs_info info;
108	if (fs_stat_dev(device, &info) != B_OK) {
109		fprintf(stderr, "Could not stat fs: %s\n", strerror(errno));
110		return;
111	}
112
113	printf("   Device No.: %" B_PRIdDEV "\n", info.dev);
114	PrintMountPoint(info.dev, true);
115	printf("  Volume Name: \"%s\"\n", info.volume_name);
116	printf("  File System: %s\n", info.fsh_name);
117	printf("       Device: %s\n", info.device_name);
118
119	printf("        Flags: ");
120	PrintFlag(info.flags, B_FS_HAS_QUERY, 'Q', '-');
121	PrintFlag(info.flags, B_FS_HAS_ATTR, 'A', '-');
122	PrintFlag(info.flags, B_FS_HAS_MIME, 'M', '-');
123	PrintFlag(info.flags, B_FS_IS_SHARED, 'S', '-');
124	PrintFlag(info.flags, B_FS_IS_PERSISTENT, 'P', '-');
125	PrintFlag(info.flags, B_FS_IS_REMOVABLE, 'R', '-');
126	PrintFlag(info.flags, B_FS_IS_READONLY, '-', 'W');
127
128	printf("\n     I/O Size: %10s (%" B_PRIdOFF " byte)\n",
129		ByteString(info.io_size, 1), info.io_size);
130	printf("   Block Size: %10s (%" B_PRIdOFF " byte)\n",
131		ByteString(info.block_size, 1), info.block_size);
132	printf(" Total Blocks: %10s (%" B_PRIdOFF " blocks)\n",
133		ByteString(info.total_blocks, info.block_size), info.total_blocks);
134	printf("  Free Blocks: %10s (%" B_PRIdOFF " blocks)\n",
135		ByteString(info.free_blocks, info.block_size), info.free_blocks);
136	printf("  Total Nodes: %" B_PRIdOFF "\n", info.total_nodes);
137	printf("   Free Nodes: %" B_PRIdOFF "\n", info.free_nodes);
138	printf("   Root Inode: %" B_PRIdINO "\n", info.root);
139}
140
141
142void
143PrintCompact(dev_t device, bool showBlocks, bool all)
144{
145	fs_info info;
146	if (fs_stat_dev(device, &info) != B_OK)
147		return;
148
149	if (!all && (info.flags & B_FS_IS_PERSISTENT) == 0)
150		return;
151
152	PrintType(info.fsh_name);
153	PrintBlocks(info.total_blocks, info.block_size, showBlocks);
154	PrintBlocks(info.free_blocks, info.block_size, showBlocks);
155
156	printf(" ");
157	PrintFlag(info.flags, B_FS_HAS_QUERY, 'Q', '-');
158	PrintFlag(info.flags, B_FS_HAS_ATTR, 'A', '-');
159	PrintFlag(info.flags, B_FS_HAS_MIME, 'M', '-');
160	PrintFlag(info.flags, B_FS_IS_SHARED, 'S', '-');
161	PrintFlag(info.flags, B_FS_IS_PERSISTENT, 'P', '-');
162	PrintFlag(info.flags, B_FS_IS_REMOVABLE, 'R', '-');
163	PrintFlag(info.flags, B_FS_IS_READONLY, '-', 'W');
164
165	printf(" %24s ", info.device_name);
166	PrintMountPoint(info.dev, false);
167	printf("\n");
168}
169
170
171void
172ShowUsage(const char *programName)
173{
174	printf("usage: %s [--help | --blocks, -b | -all, -a] [<path-to-device>]\n"
175		"  -a, --all\tinclude all file systems, also those not visible from Tracker\n"
176		"  -b, --blocks\tshow device size in blocks of 1024 bytes\n"
177		"If <path-to-device> is used, detailed info for that device only will be listed.\n"
178		"Flags:\n"
179		"   Q: has query\n"
180		"   A: has attribute\n"
181		"   M: has mime\n"
182		"   S: is shared\n"
183		"   P: is persistent (visible in Tracker)\n"
184		"   R: is removable\n"
185		"   W: is writable\n", programName);
186	exit(0);
187}
188
189
190int
191main(int argc, char **argv)
192{
193	char *programName = argv[0];
194	if (strrchr(programName, '/'))
195		programName = strrchr(programName, '/') + 1;
196
197	bool showBlocks = false;
198	bool all = false;
199	dev_t device = -1;
200
201	while (*++argv) {
202		char *arg = *argv;
203		if (*arg == '-') {
204			while (*++arg && isalpha(*arg)) {
205				switch (arg[0]) {
206					case 'a':
207						all = true;
208						break;
209					case 'b':
210						showBlocks = true;
211						break;
212					case 'h':
213						// human readable units in Unix df
214						break;
215					default:
216						ShowUsage(programName);
217				}
218			}
219			if (arg[0] == '-') {
220				arg++;
221				if (!strcmp(arg, "all"))
222					all = true;
223				else if (!strcmp(arg, "blocks"))
224					showBlocks = true;
225				else
226					ShowUsage(programName);
227			}
228		} else
229			break;
230	}
231
232	// Do we already have a device? Then let's print out detailed info about that
233
234	if (argv[0] != NULL) {
235		PrintVerbose(dev_for_path(argv[0]));
236		return 0;
237	}
238
239	// If not, then just iterate over all devices and give a compact summary
240
241	printf(" Type      Total     Free      Flags   Device                   Mounted on\n"
242		   "--------- --------- --------- ------- ------------------------ -----------------\n");
243
244	int32 cookie = 0;
245	while ((device = next_dev(&cookie)) >= B_OK) {
246		PrintCompact(device, showBlocks, all);
247	}
248
249	return 0;
250}
251