/*
 *  diskinfo -- ディスクのジオメトリ情報を取得
 *
 *  revision history
 *	0.0: Sep.  6, 2002 by Dai ISHIJIMA (とりあえずバージョン)
 *	0.1: Oct. 21, 2002
 *	0.2: Apr. 28, 2004
 *	0.3: May   1, 2004
 *	0.4: May  30, 2004
 *	0.5: Jun. 19, 2004
 *
 *  参考文献:
 *	fdisk(8) のソース (/usr/src/sbin/i386/fdisk/fdisk.c)
 */

#include <sys/disklabel.h>
#include <fcntl.h>
#include <unistd.h>


#define SHOW_CYLS 1
#define SHOW_HEAD 2
#define SHOW_SECS 4
#define SHOW_BYTES 8
#define SHOW_BYTES_WITH_PREFIX 16
#define SHOW_BLOCKS 32
#define SHOW_BYTES_WITH_COMMA 64

#define shift --argc; ++argv


#define DFLT_DISK "/dev/rad0"
#define EOS '\0'


#ifndef BUFSIZ
#define BUFSIZ 512
#endif


/* 数値を文字列に変換 */
char *num2str(long long d, char *s)
{
    int p;
    int q;
    int ch;

    p = 0;
    do {
	s[p] = '0' + (d % 10);
	++p;
	d /= 10;
    } while (d > 0);
    s[p] = EOS;
    for (q = 0; q < p / 2; q++) {
	ch = s[q];
	s[q] = s[p - q - 1];
	s[p - q - 1] = ch;
    }
    return(s);
}


/* 数値を文字列に変換 */
char *num2commastr(long long d, char *s)
{
    int p;
    int q;
    int n;
    int ch;

    p = 0;
    n = 0;
    do {
	if ((n > 0) && ((n % 3) == 0)) {
	    s[p] = ',';
	    ++p;
	}
	s[p] = '0' + (d % 10);
	++p;
	++n;
	d /= 10;
    } while (d > 0);
    s[p] = EOS;
    for (q = 0; q < p / 2; q++) {
	ch = s[q];
	s[q] = s[p - q - 1];
	s[p - q - 1] = ch;
    }
    return(s);
}


/* 一文字出力 */
void putch(int fd, unsigned char ch)
{
    write(fd, &ch, 1);
}


/* 文字列出力 */
void myputs(int fd, char *s)
{
    while (*s) {
	putch(fd, *s);
	++s;
    }
}


int main(int argc, char *argv[])
{
    char *disk;
    char s[BUFSIZ];
    struct disklabel disklabel;
    int show;
    int fd;
    unsigned long long blocks;	/* 64bit? over 2G bytes...*/
    /* unsigned long: 4,294,967,296?, signed long: 2,147,483,648? */
    
    show = 0;
    shift;
    while ((argc > 0) && (argv[0][0] == '-')) {
	if (argv[0][1] == 'c') {
	    show |= SHOW_CYLS;
	}
	else if (argv[0][1] == 'h') {
	    show |= SHOW_HEAD;
	}
	else if (argv[0][1] == 's') {
	    show |= SHOW_SECS;
	}
	else if (argv[0][1] == 'b') {
	    show |= SHOW_BYTES;
	}
	else if (argv[0][1] == 'B') {
	    show |= SHOW_BYTES_WITH_COMMA;;
	}
	else if (argv[0][1] == 'p') {
	    show |= SHOW_BYTES_WITH_PREFIX;
	}
	else if (argv[0][1] == 'l') {
	    show |= SHOW_BLOCKS;
	}
	else {
	    myputs(STDERR_FILENO,
		   "Usage: diskinfo [-c|-h|-s|-b|-p|-l] [device]\n");
	    exit(1);
	}
	shift;
    }
    if (show == 0) {
	show = SHOW_CYLS | SHOW_HEAD | SHOW_SECS;
    }
    if (argc > 0) {
	disk = *argv;
    }
    else {
	disk = DFLT_DISK;
    }
    if ((fd = open(disk, O_RDONLY)) < 0) {
	myputs(STDERR_FILENO, "diskinfo: unable to open disk ");
	myputs(STDERR_FILENO, disk);
	myputs(STDERR_FILENO, "\n");
	exit(1);
    }
    if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) {
	myputs(STDERR_FILENO, "diskinfo: can't get disk parameters\n");
	exit(1);
    }
    blocks = disklabel.d_ncylinders
	   * disklabel.d_ntracks
	   * disklabel.d_nsectors;
    if (show & SHOW_BLOCKS) {
	num2str(blocks, s);
	myputs(STDOUT_FILENO, s);
	myputs(STDOUT_FILENO, "\n");
    }
    if (show & SHOW_BYTES) {
	num2str(blocks * 512, s);
	myputs(STDOUT_FILENO, s);
	myputs(STDOUT_FILENO, "\n");
    }
    if (show & SHOW_BYTES_WITH_COMMA) {
	num2commastr(blocks * 512, s);
	myputs(STDOUT_FILENO, s);
	myputs(STDOUT_FILENO, "\n");
    }
    if (show & SHOW_BYTES_WITH_PREFIX) {
	blocks /= 2;
	if ((0 < blocks) && (blocks < 1024)) {
	    num2str(blocks, s);
	    myputs(STDOUT_FILENO, s);
	    myputs(STDOUT_FILENO, "k");
	}
	blocks = blocks / 1024;
	if ((0 < blocks) && (blocks < 1024)) {
	    num2str(blocks, s);
	    myputs(STDOUT_FILENO, s);
	    myputs(STDOUT_FILENO, "M");
	}
	blocks = blocks / 1024;
	if ((0 < blocks) && (blocks < 1024)) {
	    num2str(blocks, s);
	    myputs(STDOUT_FILENO, s);
	    myputs(STDOUT_FILENO, "G");
	}
	blocks = blocks / 1024;
	if (0 < blocks) {
	    num2str(blocks, s);
	    myputs(STDOUT_FILENO, s);
	    myputs(STDOUT_FILENO, "T");
	}
	myputs(STDOUT_FILENO, "\n");
    }
    if (show & SHOW_CYLS) {
	num2str(disklabel.d_ncylinders, s);
	myputs(STDOUT_FILENO, s);
	myputs(STDOUT_FILENO, "\n");
    }
    if (show & SHOW_HEAD) {
	num2str(disklabel.d_ntracks, s);
	myputs(STDOUT_FILENO, s);
	myputs(STDOUT_FILENO, "\n");
    }
    if (show & SHOW_SECS) {
	num2str(disklabel.d_nsectors, s);
	myputs(STDOUT_FILENO, s);
	myputs(STDOUT_FILENO, "\n");
    }
    exit(0);
}


/* Local Variables: */
/* compile-command:"cc -Wall -O -s -static -o diskinfo diskinfo.c" */
/* End: */
