Hi misc@,

while trying to use indent(1) on my lsblk.c, I get the following two errors:> Error@150: Stmt nesting error.
Error@702: Missing braces at end of file.

It also doesn't handle unicode and breaks horribly at some points.

I have attached both the origin and the indented version
as Thunderbird doesn't seem to wanna deal with so much text.

For now, I'm just gonna do it myself according to style(9).

Thanks,
Benjamin Stürz
/*
 * Copyright (c) 2023 Benjamin StГјrz <be...@stuerz.xyz>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#define _XOPEN_SOURCE 700
#define _BSD_SOURCE 1
#define DKTYPENAMES
#define WSDEBUG 0
#include <stddef.h>
#include <dev/biovar.h>
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/disklabel.h>
#include <sys/sysctl.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/dkio.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <util.h>
#include <err.h>

static int 
diskcount(void)
{
	const int 	mib  [2] = {CTL_HW, HW_DISKCOUNT};
	int 		diskcount;
	size_t 		len = sizeof diskcount;

	if (sysctl(mib, 2, &diskcount, &len, NULL, 0) == -1)
		err(1, "sysctl(hw.diskcount)");

	return diskcount;
}

static char    *
disknames(void)
{
	const int 	num = diskcount();
	const int 	mib  [2] = {CTL_HW, HW_DISKNAMES};
	size_t 		len = 32 * num;
	char           *buffer = malloc(len);

	if (sysctl(mib, 2, buffer, &len, NULL, 0) == -1)
		err(1, "sysctl(hw.disknames)");

	return buffer;
}

static char    *
stripdisk(char *n)
{
	const char 	sufx[] = " disk";
	const size_t 	ln = strnlen(n, 16);
	const size_t 	ls = sizeof sufx - 1;

	if (memcmp(n + ln - ls, sufx, ls) == 0) {
		n[ln - ls] = '\0';
	}
	return n;
}

static void 
print_size(uint64_t sz)
{
	const struct unit {
		char 		sym;
		uint64_t 	factor;
	} 		units       [] = {
		{
			'P', 1ull << 50
		},
		{
			'T', 1ull << 40
		},
		{
			'G', 1ull << 30
		},
		{
			'M', 1ull << 20
		},
		{
			'K', 1ull << 10
		},
		{
			'0', 0
		},
	};

	char 		sym = 'B';
	uint64_t 	factor = 1;

	for (const struct unit * u = &units[0]; u->factor; ++u) {
		if (sz >= (u->factor * 9 / 10)) {
			sym = u->sym;
			factor = u->factor;
			break;
		}
	}

	const unsigned 	scaled10 = sz * 10 / factor;
	const unsigned 	scaled = sz / factor;
	if (scaled10 >= 1000) {
		printf("%u", scaled);
	} else if (scaled10 >= 100) {
		printf(" %u", scaled);
	} else {
		printf("%u.%u", scaled, scaled10 % 10);
	}

	putchar(sym);
	putchar(' ');
}

enum {
	FIELD_NAME = 0x01,
	FIELD_DUID = 0x02,
	FIELD_SIZE = 0x04,
	FIELD_USED = 0x08,
	FIELD_FREE = 0x10,
	FIELD_TYPE = 0x20,
	FIELD_COMMENT = 0x40,

	FIELD_DEFAULT = FIELD_NAME | FIELD_SIZE | FIELD_TYPE | FIELD_COMMENT,
};

enum {
	OPT_NOHEADER = 0x01,
	OPT_NOUNICODE = 0x02,
	OPT_NOBIO = 0x04,
};

struct my_diskinfo;

struct my_partinfo {
	char 		letter;
	uint64_t 	size;
	uint64_t 	fssize;
	uint64_t 	free;
	const char     *fstype;
	const char     *mount;
	const struct my_diskinfo *sub;
	              //If this is part of a RAID
			const char   *raidstatus;
	              //Only available if (sub != NULL)
		};

	struct my_diskinfo {
		char 		type     [16];
		char 		label    [16];
		char 		name     [8];
		uint64_t 	size;
		uint64_t 	used;
		u_char 		duid   [8];
		uint8_t 	num_parts;
		struct my_partinfo parts[MAXPARTITIONS];
		const char     *raidstatus;
		              //If this is a RAID device
	};

	struct padding {
		int 		name;
		/* duid = 8 */
		/* size = 4 */
		/* used = 4 */
		/* free = 4 */
		int 		type;
		int 		comment;
	};

	static void 	print_header(int fields, const struct padding * p) {
		if (fields & FIELD_NAME)
			printf("%-*s ", p->name, "NAME");

		if (fields & FIELD_DUID)
			printf("%-18s ", "DUID");

		if (fields & FIELD_SIZE)
			printf("%-4s ", "SIZE");

		if (fields & FIELD_USED)
			printf("%-4s ", "USED");

		if (fields & FIELD_FREE)
			printf("%-4s ", "FREE");

		if (fields & FIELD_TYPE)
			printf("%-*s ", p->type, "TYPE");

		if (fields & FIELD_COMMENT)
			printf("%-*s ", p->comment, "COMMENT");

#if WSDEBUG
		putchar('X');
#endif

		putchar('\n');
	}

	static void 	print_duid(const u_char * duid) {
		for (size_t i = 0; i < 8; ++i) {
			printf("%02x", duid[i]);
		}
	}

	static void 	print_disk(const struct my_diskinfo *, int, int, const char *, const struct padding *);
	static void 	print_part(
			  		const 	struct my_diskinfo * disk,
			  		const 	struct my_partinfo * part,
						int 		fields ,
						int 		options,
						bool 		last  ,
						const 	struct padding * p
	) {
		if (fields & FIELD_NAME) {
			const char     *prefix = (options & OPT_NOUNICODE) ? "  " : (last ? "в””в”175" : "в”њв”175");
					printf        (
					 		"%s%s%c%-*s ",
					 		prefix       ,
					 		disk        ->name,
					 		part        ->letter,
					 		p           ->name - 2 - (int) strlen(disk->name) - 1,
					 		""
			);
		}
		if (fields & FIELD_DUID) {
			print_duid(disk->duid);
			printf(".%c ", part->letter);
		}
		if              (fields & FIELD_SIZE)
					print_size    (part->size);

		if (fields & FIELD_USED) {
			if (part->fssize) {
				print_size(part->fssize - part->free);
			} else {
				printf(" N/A ");
			}
		}
		if (fields & FIELD_FREE) {
			if (part->fssize) {
				print_size(part->free);
			} else {
				printf(" N/A ");
			}
		}
		if (fields & FIELD_TYPE)
			printf("%-*s ", p->type, part->fstype);

		if (fields & FIELD_COMMENT)
			printf("%-*s ", p->comment, part->mount ? part->mount : "");

#if WSDEBUG
		putchar('X');
#endif

		putchar('\n');

		if (part->sub) {
			print_disk(part->sub, fields, options, part->raidstatus, p);
		}
	}

	static void 	print_disk(
			  		const 	struct my_diskinfo * disk,
						int 		fields ,
						int 		options,
						const 	char  *raidstatus,
						const 	struct padding * p
	) {
		if (fields & FIELD_NAME) {
			const char     *prefix = raidstatus ? (options & OPT_NOUNICODE ? "    " : "в”‚ в””в”175") : "";

					printf        (
					 		"%s%-*s "    ,
					 		prefix       ,
					 		p           ->name - (raidstatus ? 4 : 0),
					 		disk        ->name
			);
		}
		if (fields & FIELD_DUID) {
			print_duid(disk->duid);
			printf("   ");
		}
		if              (fields & FIELD_SIZE)
					print_size    (disk->size);

		if (fields & FIELD_USED)
			print_size(disk->used);

		if (fields & FIELD_FREE)
			print_size(disk->size - disk->used);

		if (fields & FIELD_TYPE)
			printf("%-*.16s ", p->type, disk->type);

		if (fields & FIELD_COMMENT) {
			if (raidstatus) {
				printf("%-*s ", p->comment, raidstatus);
			} else if (disk->raidstatus) {
				printf(
				       "%.16s (%s)%*s ",
				       disk->label,
				       disk->raidstatus,
				       p->comment - (int) strnlen(disk->label, sizeof disk->label) - (int) strlen(disk->raidstatus) - 3,
				       ""
					);
			} else {
				printf("%-*.16s ", p->comment, disk->label);
			}
		}
#if WSDEBUG
		putchar('X');
#endif

		putchar('\n');

		if (!raidstatus) {
			for (uint8_t i = 0; i < disk->num_parts; ++i)
				print_part(disk, &disk->parts[i], fields, options, i == (disk->num_parts - 1), p);
		}
	}

	static const struct statfs *find_mount(const char *dev) {
		static struct statfs *mounts = NULL;
		static int 	n_mounts;

		if              (!mounts) {
			n_mounts = getmntinfo(&mounts, MNT_NOWAIT);
			if (n_mounts == 0)
				err(1, "getmntinfo()");
		}
		for             (int i = 0; i < n_mounts; ++i) {
			if (!strcmp(dev, mounts[i].f_mntfromname))
				return &mounts[i];
		}

		return NULL;
	}

	static int 	read_disk(const char *name, struct my_diskinfo * disk) {
		struct disklabel label;
		char           *ppath, *letter;

				memset        (disk, 0, sizeof *disk);

		{
			//Read disklabel.
			size_t len;
			int 		fd;

					fd = 		opendev(name, O_RDONLY, OPENDEV_PART | OPENDEV_BLCK, &ppath);
			if              (fd < 0) {
				warn("read_disk(): opendev(%s)", name);
				return -1;
			}
			if              (ioctl(fd, DIOCGDINFO, &label) < 0) {
				warn("read_disk(): ioctl(%s, DIOCGDINFO)", name);
				close(fd);
				return -1;
			}
			close(fd);

			len = strlen(ppath);
			letter = ppath + len - 1;
		}

		strlcpy(disk->name, name, sizeof disk->name);
		disk->size = DL_GETDSIZE(&label) * label.d_secsize;
		memcpy(disk->type, label.d_typename, sizeof disk->type);
		stripdisk(disk->type);
		memcpy(disk->label, label.d_packname, sizeof disk->label);
		memcpy(disk->duid, label.d_uid, sizeof disk->duid);
		disk->num_parts = 0;

		for (uint16_t i = 0; i < label.d_npartitions; ++i) {
			const struct partition *p = &label.d_partitions[i];
			const struct statfs *mnt;
			struct my_partinfo part;

			memset(&part, 0, sizeof part);

			part.size = DL_GETPSIZE(p) * label.d_secsize;

			if (!part.size)
				continue;

			part.letter = 'a' + i;
			*letter = part.letter;
			mnt = find_mount(ppath);

			if (mnt) {
				const uint64_t 	bs = mnt->f_bsize;
				part.mount = mnt->f_mntonname;
				part.fssize = mnt->f_blocks * bs;
				part.free = mnt->f_bfree * bs;
			}
			part.fstype = fstypenames[p->p_fstype];
			if (i != 2)
				disk->used += part.size;
			disk->parts[disk->num_parts++] = part;
		}

		return 0;
	}

	static const char *bd_statusstr(int status) {
		switch (status) {
			case BIOC_SDONLINE:return BIOC_SDONLINE_S;
			case BIOC_SDOFFLINE:return BIOC_SDOFFLINE_S;
			case BIOC_SDFAILED:return BIOC_SDFAILED_S;
			case BIOC_SDREBUILD:return BIOC_SDREBUILD_S;
			case BIOC_SDHOTSPARE:return BIOC_SDHOTSPARE_S;
			case BIOC_SDUNUSED:return BIOC_SDUNUSED_S;
			case BIOC_SDSCRUB:return BIOC_SDSCRUB_S;
			case BIOC_SDINVALID:return BIOC_SDINVALID_S;
			default:return "Unknown";
		}
	}

	static const char *bv_statusstr(int status) {
		switch (status) {
			case BIOC_SVONLINE:return BIOC_SVONLINE_S;
			case BIOC_SVOFFLINE:return BIOC_SVOFFLINE_S;
			case BIOC_SVDEGRADED:return BIOC_SVDEGRADED_S;
			case BIOC_SVBUILDING:return BIOC_SVBUILDING_S;
			case BIOC_SVSCRUB:return BIOC_SVSCRUB_S;
			case BIOC_SVREBUILD:return BIOC_SVREBUILD_S;
			case BIOC_SVINVALID:return BIOC_SVINVALID_S;
			default:return "Unknown";
		}
	}

	static void 	read_raid(
			       		struct 	my_diskinfo * disk,
			       		struct 	my_diskinfo * disks,
			       		size_t 	num_disks
	) {
		struct bioc_inq bi;
		int 		fd;

				fd = 		opendev(disk->name, O_RDONLY, OPENDEV_PART | OPENDEV_BLCK, NULL);
		if              (fd < 0) {
			warn("read_raid(): opendev(%s)", disk->name);
			return;
		}
				memset        (&bi, 0, sizeof bi);

		if (ioctl(fd, BIOCINQ, &bi) == -1)
			goto ret;

		for (int i = 0; i < bi.bi_novol; ++i) {
			struct bioc_vol bv;

			memset(&bv, 0, sizeof bv);
			memcpy(&bv.bv_bio, &bi.bi_bio, sizeof bi.bi_bio);
			bv.bv_volid = i;

			if (ioctl(fd, BIOCVOL, &bv) == -1) {
				warn("read_raid(%s): BIOCVOL(%d)", disk->name, i);
				continue;
			}
			if (strcmp(disk->name, bv.bv_dev) != 0)
				continue;

			disk->raidstatus = bv_statusstr(bv.bv_status);

			for (int j = 0; j < bv.bv_nodisk; ++j) {
				struct bioc_disk bd;
				size_t 		len_vendor;
				char 		letter;

				memset(&bd, 0, sizeof bd);
				memcpy(&bd.bd_bio, &bi.bi_bio, sizeof bi.bi_bio);
				bd.bd_volid = i;
				bd.bd_diskid = j;

				if (ioctl(fd, BIOCDISK, &bd) == -1) {
					warn("read_raid(%s): BIOCDISK(%d, %d)", disk->name, i, j);
					continue;
				}
				len_vendor = strlen(bd.bd_vendor);
				if (len_vendor < 4 || len_vendor > 8) {
					warnx("read_raid(%s): unexpected vendor string: %.32s", disk->name, bd.bd_vendor);
					continue;
				}
				letter = bd.bd_vendor[len_vendor - 1];

				for (size_t k = 0; k < num_disks; ++k) {
					if (!memcmp(bd.bd_vendor, disks[k].name, 3)) {
						for (size_t l = 0; l < disks[k].num_parts; ++l) {
							if (letter == disks[k].parts[l].letter) {
								disks[k].parts[l].sub = disk;
								disks[k].parts[l].raidstatus = bd_statusstr(bd.bd_status);
								goto found;
							}
						}
					}
				}
		found:		;
			}
		}
ret:
		close(fd);
	}

	static int 	usage(void) {
		fputs("Usage: lsblk [-abinUuV] [disk...]\n", stderr);
		return 1;
	}

	static void 	pad_update(int *pad, int newval) {
		if (newval > *pad)
			*pad = newval;
	}

	static void 	pad_disk(struct padding * p, const struct my_diskinfo * disk) {
		size_t 		len_disk;
		int 		comment;

				len_disk = 	strnlen(disk->name, sizeof disk->name);
				comment = 	strnlen(disk->label, sizeof disk->label);
		if              (disk->raidstatus)
					comment += 	strlen(disk->raidstatus) + 3;

				pad_update    (&p->name, len_disk);
				pad_update    (&p->type, strnlen(disk->type, sizeof disk->type));
				pad_update    (&p->comment, comment);

		for             (int i = 0; i < disk->num_parts; ++i) {
			const struct my_partinfo *part = &disk->parts[i];

					pad_update    (&p->name, len_disk + 3);
					pad_update    (&p->type, strlen(part->fstype));

			if              (part->sub) {
				pad_update(&p->name, strnlen(part->sub->name, sizeof part->sub->name) + 4);
				pad_update(&p->comment, strlen(part->raidstatus));
			} else if       (part->mount) {
				pad_update(&p->comment, strlen(part->mount));
			}
		}
	}


	static int 	compare_disk(const void *p1, const void *p2) {
		const struct my_diskinfo *d1 = p1;
		const struct my_diskinfo *d2 = p2;

				return 	strcmp(d1->name, d2->name);
	}

	int 		main      (int argc, char *argv[]) {
		int 		option;
		int 		fields = FIELD_DEFAULT;
		int 		options = 0;
		int 		ret = 0;

		if              (unveil("/dev", "r") == -1)
					err           (1, "unveil(/dev)");

		if              (unveil(NULL, NULL) == -1)
					err           (1, "unveil()");

		while           ((option = getopt(argc, argv, ":abinUuV")) != -1) {
			switch (option) {
				case 'a':
				fields = -1;
				break;
				case 'b':
				options |= OPT_NOBIO;
				break;
				case 'i':
				options |= OPT_NOUNICODE;
				break;
				case 'n':
				options |= OPT_NOHEADER;
				break;
				case 'U':
				fields |= FIELD_DUID;
				break;
				case 'u':
				fields |= FIELD_USED | FIELD_FREE;
				break;
				case 'V':
				puts("lsblk-" VERSION);
				return 0;
				default:
				return usage();
			}
		}

				argv += 	optind;
		argc -= optind;

		char           *names = argc == 0 ? disknames() : NULL;

		if (pledge("stdio rpath disklabel", NULL) == -1)
			err(1, "pledge()");

		size_t 		cap_disks;
		if (argc == 0) {
			const char     *s = names;
			cap_disks = 1;
			while ((s = strchr(s, ',')) != NULL)
				++cap_disks, ++s;
		} else {
			cap_disks = argc;
		}

		struct my_diskinfo *disks = calloc(cap_disks, sizeof(struct my_diskinfo));
		size_t 		num_disks = 0;

		if (argc == 0) {
			for (char *name; (name = strsep(&names, ",")) != NULL;) {
				char           *colon = strchr(name, ':');
				struct my_diskinfo disk;

				if (colon)
					*colon = '\0';
				if (read_disk(name, &disk) == 0) {
					disks[num_disks++] = disk;
				} else {
					ret = 1;
				}

				if (colon)
					*colon = ':';
			}
		} else {
			for (int i = 0; i < argc; ++i) {
				char           *name = basename(argv[i]);
				char           *last = name + strlen(name) - 1;
				struct my_diskinfo disk;

				if (isalpha(*last)) {
					warnx("%s: specifying a partition is not supported, using the drive.", name);
					*last = '\0';
				}
				if (read_disk(name, &disk) == 0) {
					disks[num_disks++] = disk;
				} else {
					ret = 1;
				}
			}
		}

		free(names);

		if (num_disks == 0)
			return ret;

		mergesort(disks, num_disks, sizeof *disks, compare_disk);

		if (!(options & OPT_NOBIO)) {
			for (size_t i = 0; i < num_disks; ++i) {
				read_raid(&disks[i], disks, num_disks);
			}
		}
		struct padding 	p = {
			.name = strlen("NAME"),
			.type = strlen("TYPE"),
			.comment = strlen("COMMENT"),
		};

		for (size_t i = 0; i < num_disks; ++i)
			pad_disk(&p, &disks[i]);

		if (!(options & OPT_NOHEADER))
			print_header(fields, &p);

		for (size_t i = 0; i < num_disks; ++i) {
			print_disk(&disks[i], fields, options, NULL, &p);
		}

		return ret;
	}
/*
 * Copyright (c) 2023 Benjamin Stürz <be...@stuerz.xyz>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#define _XOPEN_SOURCE 700
#define _BSD_SOURCE 1
#define DKTYPENAMES
#define WSDEBUG 0
#include <stddef.h>
#include <dev/biovar.h>
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/disklabel.h>
#include <sys/sysctl.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/dkio.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <util.h>
#include <err.h>

static int diskcount (void)
{
    const int mib[2] = { CTL_HW, HW_DISKCOUNT };
    int diskcount;
    size_t len = sizeof diskcount;

    if (sysctl (mib, 2, &diskcount, &len, NULL, 0) == -1)
        err (1, "sysctl(hw.diskcount)");

    return diskcount;
}

static char *disknames (void)
{
    const int num = diskcount ();
    const int mib[2] = { CTL_HW, HW_DISKNAMES };
    size_t len = 32 * num;
    char *buffer = malloc (len);

    if (sysctl (mib, 2, buffer, &len, NULL, 0) == -1)
        err (1, "sysctl(hw.disknames)");

    return buffer;
}

static char *stripdisk (char *n)
{
    const char sufx[] = " disk";
    const size_t ln = strnlen (n, 16);
    const size_t ls = sizeof sufx - 1;

    if (memcmp (n + ln - ls, sufx, ls) == 0) {
        n[ln - ls] = '\0';
    }

    return n;
}

static void print_size (uint64_t sz)
{
    const struct unit {
        char sym;
        uint64_t factor;
    } units[] = {
        { 'P', 1ull << 50 },
        { 'T', 1ull << 40 },
        { 'G', 1ull << 30 },
        { 'M', 1ull << 20 },
        { 'K', 1ull << 10 },
        { '0', 0 },
    };

    char sym = 'B';
    uint64_t factor = 1;

    for (const struct unit *u = &units[0]; u->factor; ++u) {
        if (sz >= (u->factor * 9 / 10)) {
            sym = u->sym;
            factor = u->factor;
            break;
        }
    }

    const unsigned scaled10 = sz * 10 / factor;
    const unsigned scaled = sz / factor;
    if (scaled10 >= 1000) {
        printf ("%u", scaled);
    } else if (scaled10 >= 100) {
        printf (" %u", scaled);
    } else {
        printf ("%u.%u", scaled, scaled10 % 10);
    }

    putchar (sym);
    putchar (' ');
}

enum {
    FIELD_NAME      = 0x01,
    FIELD_DUID      = 0x02,
    FIELD_SIZE      = 0x04,
    FIELD_USED      = 0x08,
    FIELD_FREE      = 0x10,
    FIELD_TYPE      = 0x20,
    FIELD_COMMENT   = 0x40,

    FIELD_DEFAULT   = FIELD_NAME | FIELD_SIZE | FIELD_TYPE | FIELD_COMMENT,
};

enum {
    OPT_NOHEADER    = 0x01,
    OPT_NOUNICODE   = 0x02,
    OPT_NOBIO       = 0x04,
};

struct my_diskinfo;

struct my_partinfo {
    char letter;
    uint64_t size;
    uint64_t fssize;
    uint64_t free;
    const char *fstype;
    const char *mount;
    const struct my_diskinfo *sub; // If this is part of a RAID
    const char *raidstatus; // Only available if (sub != NULL)
};

struct my_diskinfo {
    char type[16];
    char label[16];
    char name[8];
    uint64_t size;
    uint64_t used;
    u_char duid[8];
    uint8_t num_parts;
    struct my_partinfo parts[MAXPARTITIONS];
    const char *raidstatus; // If this is a RAID device
};

struct padding {
    int name;
    /* duid = 8 */
    /* size = 4 */
    /* used = 4 */
    /* free = 4 */
    int type;
    int comment;
};

static void print_header (int fields, const struct padding *p)
{
    if (fields & FIELD_NAME)
        printf ("%-*s ", p->name, "NAME");

    if (fields & FIELD_DUID)
        printf ("%-18s ", "DUID");

    if (fields & FIELD_SIZE)
        printf ("%-4s ", "SIZE");

    if (fields & FIELD_USED)
        printf ("%-4s ", "USED");

    if (fields & FIELD_FREE)
        printf ("%-4s ", "FREE");

    if (fields & FIELD_TYPE)
        printf ("%-*s ", p->type, "TYPE");

    if (fields & FIELD_COMMENT)
        printf ("%-*s ", p->comment, "COMMENT");

#if WSDEBUG
    putchar ('X');
#endif

    putchar ('\n');
}

static void print_duid (const u_char *duid)
{
    for (size_t i = 0; i < 8; ++i) {
        printf ("%02x", duid[i]);
    }
}

static void print_disk (const struct my_diskinfo *, int, int, const char *, 
const struct padding *);
static void print_part (
    const struct my_diskinfo *disk,
    const struct my_partinfo *part,
    int fields,
    int options,
    bool last,
    const struct padding *p
) {
    if (fields & FIELD_NAME) {
        const char *prefix = (options & OPT_NOUNICODE) ? "  " : (last ? "└─" : 
"├─");
        printf (
            "%s%s%c%-*s ",
            prefix,
            disk->name,
            part->letter,
            p->name - 2 - (int)strlen (disk->name) - 1,
            ""
        );
    }

    if (fields & FIELD_DUID) {
        print_duid (disk->duid);
        printf (".%c ", part->letter);
    }

    if (fields & FIELD_SIZE)
        print_size (part->size);

    if (fields & FIELD_USED) {
        if (part->fssize) {
            print_size (part->fssize - part->free);
        } else {
            printf (" N/A ");
        }
    }

    if (fields & FIELD_FREE) {
        if (part->fssize) {
            print_size (part->free);
        } else {
            printf (" N/A ");
        }
    }

    if (fields & FIELD_TYPE)
        printf ("%-*s ", p->type, part->fstype);

    if (fields & FIELD_COMMENT)
        printf ("%-*s ", p->comment, part->mount ? part->mount : "");

#if WSDEBUG
    putchar ('X');
#endif

    putchar ('\n');

    if (part->sub) {
        print_disk (part->sub, fields, options, part->raidstatus, p);
    }
}

static void print_disk (
    const struct my_diskinfo *disk,
    int fields,
    int options,
    const char *raidstatus,
    const struct padding *p
) {
    if (fields & FIELD_NAME) {
        const char *prefix = raidstatus ? (options & OPT_NOUNICODE ? "    " : 
"│ └─") : "";

        printf (
            "%s%-*s ",
            prefix,
            p->name - (raidstatus ? 4 : 0),
            disk->name
        );
    }

    if (fields & FIELD_DUID) {
        print_duid (disk->duid);
        printf ("   ");
    }

    if (fields & FIELD_SIZE)
        print_size (disk->size);

    if (fields & FIELD_USED)
        print_size (disk->used);

    if (fields & FIELD_FREE)
        print_size (disk->size - disk->used);

    if (fields & FIELD_TYPE)
        printf ("%-*.16s ", p->type, disk->type);

    if (fields & FIELD_COMMENT) {
        if (raidstatus) {
            printf ("%-*s ", p->comment, raidstatus);
        } else if (disk->raidstatus) {
            printf (
                "%.16s (%s)%*s ",
                disk->label,
                disk->raidstatus,
                p->comment - (int)strnlen (disk->label, sizeof disk->label) - 
(int)strlen (disk->raidstatus) - 3,
                ""
            );
        } else {
            printf ("%-*.16s ", p->comment, disk->label);
        }
    }

#if WSDEBUG
    putchar ('X');
#endif

    putchar ('\n');

    if (!raidstatus) {
        for (uint8_t i = 0; i < disk->num_parts; ++i)
            print_part (disk, &disk->parts[i], fields, options, i == 
(disk->num_parts - 1), p);
    }
}

static const struct statfs *find_mount (const char *dev)
{
    static struct statfs *mounts = NULL;
    static int n_mounts;

    if (!mounts) {
        n_mounts = getmntinfo (&mounts, MNT_NOWAIT);
        if (n_mounts == 0)
            err (1, "getmntinfo()");
    }

    for (int i = 0; i < n_mounts; ++i) {
        if (!strcmp (dev, mounts[i].f_mntfromname))
            return &mounts[i];
    }

    return NULL;
}

static int read_disk (const char *name, struct my_diskinfo *disk)
{
    struct disklabel label;
    char *ppath, *letter;

        memset (disk, 0, sizeof *disk);

    {   // Read disklabel.
        size_t len;
        int fd;

        fd = opendev (name, O_RDONLY, OPENDEV_PART | OPENDEV_BLCK, &ppath);
        if (fd < 0) {
            warn ("read_disk(): opendev(%s)", name);
                        return -1;
                }

        if (ioctl (fd, DIOCGDINFO, &label) < 0) {
            warn ("read_disk(): ioctl(%s, DIOCGDINFO)", name);
                        close (fd);
                        return -1;
                }
        close (fd);

        len = strlen (ppath);
        letter = ppath + len - 1;
    }

    strlcpy (disk->name, name, sizeof disk->name);
    disk->size = DL_GETDSIZE (&label) * label.d_secsize;
    memcpy (disk->type, label.d_typename, sizeof disk->type);
    stripdisk (disk->type);
    memcpy (disk->label, label.d_packname, sizeof disk->label);
    memcpy (disk->duid, label.d_uid, sizeof disk->duid);
    disk->num_parts = 0;

    for (uint16_t i = 0; i < label.d_npartitions; ++i) {
        const struct partition *p = &label.d_partitions[i];
        const struct statfs *mnt;
        struct my_partinfo part;

                memset (&part, 0, sizeof part);

        part.size = DL_GETPSIZE (p) * label.d_secsize;

        if (!part.size)
            continue;

        part.letter = 'a' + i;
        *letter = part.letter;
        mnt = find_mount (ppath);

        if (mnt) {
            const uint64_t bs = mnt->f_bsize;
            part.mount = mnt->f_mntonname;
            part.fssize = mnt->f_blocks * bs;
            part.free = mnt->f_bfree * bs;
        }

        part.fstype = fstypenames[p->p_fstype];
        if (i != 2)
            disk->used += part.size;
        disk->parts[disk->num_parts++] = part;
    }

    return 0;
}

static const char *bd_statusstr (int status) {
    switch (status) {
    case BIOC_SDONLINE:     return BIOC_SDONLINE_S;
    case BIOC_SDOFFLINE:    return BIOC_SDOFFLINE_S;
    case BIOC_SDFAILED:     return BIOC_SDFAILED_S;
    case BIOC_SDREBUILD:    return BIOC_SDREBUILD_S;
    case BIOC_SDHOTSPARE:   return BIOC_SDHOTSPARE_S;
    case BIOC_SDUNUSED:     return BIOC_SDUNUSED_S;
    case BIOC_SDSCRUB:      return BIOC_SDSCRUB_S;
    case BIOC_SDINVALID:    return BIOC_SDINVALID_S;
    default:                return "Unknown";
    }
}

static const char *bv_statusstr (int status) {
    switch (status) {
    case BIOC_SVONLINE:     return BIOC_SVONLINE_S;
    case BIOC_SVOFFLINE:    return BIOC_SVOFFLINE_S;
    case BIOC_SVDEGRADED:   return BIOC_SVDEGRADED_S;
    case BIOC_SVBUILDING:   return BIOC_SVBUILDING_S;
    case BIOC_SVSCRUB:      return BIOC_SVSCRUB_S;
    case BIOC_SVREBUILD:    return BIOC_SVREBUILD_S;
    case BIOC_SVINVALID:    return BIOC_SVINVALID_S;
    default:                return "Unknown";
    }
}

static void read_raid (
    struct my_diskinfo *disk,
    struct my_diskinfo *disks,
    size_t num_disks
) {
    struct bioc_inq bi;
    int fd;

    fd = opendev (disk->name, O_RDONLY, OPENDEV_PART | OPENDEV_BLCK, NULL);
    if (fd < 0) {
        warn ("read_raid(): opendev(%s)", disk->name);
        return;
    }

        memset (&bi, 0, sizeof bi);

    if (ioctl (fd, BIOCINQ, &bi) == -1)
        goto ret;

    for (int i = 0; i < bi.bi_novol; ++i) {
        struct bioc_vol bv;

                memset (&bv, 0, sizeof bv);
        memcpy (&bv.bv_bio, &bi.bi_bio, sizeof bi.bi_bio);
        bv.bv_volid = i;

        if (ioctl (fd, BIOCVOL, &bv) == -1) {
            warn ("read_raid(%s): BIOCVOL(%d)", disk->name, i);
            continue;
        }

        if (strcmp (disk->name, bv.bv_dev) != 0)
            continue;

        disk->raidstatus = bv_statusstr (bv.bv_status);

        for (int j = 0; j < bv.bv_nodisk; ++j) {
            struct bioc_disk bd;
            size_t len_vendor;
            char letter;

                        memset (&bd, 0, sizeof bd);
            memcpy (&bd.bd_bio, &bi.bi_bio, sizeof bi.bi_bio);
            bd.bd_volid = i;
            bd.bd_diskid = j;

            if (ioctl (fd, BIOCDISK, &bd) == -1) {
                warn ("read_raid(%s): BIOCDISK(%d, %d)", disk->name, i, j);
                continue;
            }

            len_vendor = strlen (bd.bd_vendor);
            if (len_vendor < 4 || len_vendor > 8) {
                warnx ("read_raid(%s): unexpected vendor string: %.32s", 
disk->name, bd.bd_vendor);
                continue;
            }
            letter = bd.bd_vendor[len_vendor - 1];

            for (size_t k = 0; k < num_disks; ++k) {
                if (!memcmp (bd.bd_vendor, disks[k].name, 3)) {
                    for (size_t l = 0; l < disks[k].num_parts; ++l) {
                        if (letter == disks[k].parts[l].letter) {
                            disks[k].parts[l].sub = disk;
                            disks[k].parts[l].raidstatus = 
bd_statusstr(bd.bd_status);
                            goto found;
                        }
                    }
                }
            }
        found:;
        }
    }
ret:
    close (fd);
}

static int usage (void)
{
    fputs ("Usage: lsblk [-abinUuV] [disk...]\n", stderr);
    return 1;
}

static void pad_update (int *pad, int newval)
{
    if (newval > *pad)
        *pad = newval;
}

static void pad_disk (struct padding *p, const struct my_diskinfo *disk)
{
    size_t len_disk;
    int comment;

    len_disk = strnlen (disk->name, sizeof disk->name);
    comment = strnlen (disk->label, sizeof disk->label);
    if (disk->raidstatus)
        comment += strlen (disk->raidstatus) + 3;

    pad_update (&p->name, len_disk);
    pad_update (&p->type, strnlen (disk->type, sizeof disk->type));
    pad_update (&p->comment, comment);

    for (int i = 0; i < disk->num_parts; ++i) {
        const struct my_partinfo *part = &disk->parts[i];

        pad_update (&p->name, len_disk + 3);
        pad_update (&p->type, strlen (part->fstype));

        if (part->sub) {
            pad_update (&p->name, strnlen (part->sub->name, sizeof 
part->sub->name) + 4);
            pad_update (&p->comment, strlen (part->raidstatus));
        } else if (part->mount) {
            pad_update (&p->comment, strlen (part->mount));
        }
    }
}


static int compare_disk (const void *p1, const void *p2)
{
    const struct my_diskinfo *d1 = p1;
    const struct my_diskinfo *d2 = p2;

    return strcmp (d1->name, d2->name);
}

int main (int argc, char *argv[])
{
    int option;
    int fields = FIELD_DEFAULT;
    int options = 0;
        int ret = 0;

    if (unveil ("/dev", "r") == -1)
        err (1, "unveil(/dev)");

    if (unveil (NULL, NULL) == -1)
        err (1, "unveil()");

    while ((option = getopt (argc, argv, ":abinUuV")) != -1) {
        switch (option) {
        case 'a':
            fields = -1;
            break;
        case 'b':
            options |= OPT_NOBIO;
            break;
        case 'i':
            options |= OPT_NOUNICODE;
            break;
        case 'n':
            options |= OPT_NOHEADER;
            break;
        case 'U':
            fields |= FIELD_DUID;
            break;
        case 'u':
            fields |= FIELD_USED | FIELD_FREE;
            break;
        case 'V':
            puts ("lsblk-" VERSION);
            return 0;
        default:
            return usage ();
        }
    }

    argv += optind;
    argc -= optind;

    char *names = argc == 0 ? disknames () : NULL;

    if (pledge ("stdio rpath disklabel", NULL) == -1)
        err (1, "pledge()");

    size_t cap_disks;
    if (argc == 0) {
        const char *s = names;
        cap_disks = 1;
        while ((s = strchr (s, ',')) != NULL)
            ++cap_disks, ++s;
    } else {
        cap_disks = argc;
    }

    struct my_diskinfo *disks = calloc (cap_disks, sizeof (struct my_diskinfo));
    size_t num_disks = 0;

    if (argc == 0) {
        for (char *name; (name = strsep (&names, ",")) != NULL; ) {
            char *colon = strchr (name, ':');
                        struct my_diskinfo disk;

            if (colon)
                *colon = '\0';
                        if (read_disk (name, &disk) == 0) {
                                disks[num_disks++] = disk;
                        } else {
                                ret = 1;
                        }

            if (colon)
                *colon = ':';
        }
    } else {
        for (int i = 0; i < argc; ++i) {
            char *name = basename (argv[i]);
            char *last = name + strlen (name) - 1;
                        struct my_diskinfo disk;

            if (isalpha (*last)) {
                warnx ("%s: specifying a partition is not supported, using the 
drive.", name);
                *last = '\0';
            }
                        if (read_disk (name, &disk) == 0) {
                                disks[num_disks++] = disk;
                        } else {
                                ret = 1;
                        }
        }
    }

    free (names);

        if (num_disks == 0)
                return ret;

    mergesort (disks, num_disks, sizeof *disks, compare_disk);

    if (!(options & OPT_NOBIO)) {
        for (size_t i = 0; i < num_disks; ++i) {
            read_raid (&disks[i], disks, num_disks);
        }
    }

    struct padding p = {
        .name = strlen ("NAME"),
        .type = strlen ("TYPE"),
        .comment = strlen ("COMMENT"),
    };

    for (size_t i = 0; i < num_disks; ++i)
        pad_disk (&p, &disks[i]);

    if (!(options & OPT_NOHEADER))
        print_header (fields, &p);

    for (size_t i = 0; i < num_disks; ++i) {
        print_disk (&disks[i], fields, options, NULL, &p);
    }

    return ret;
}

Reply via email to