/*  diskinfo.c
    Copyright (C) 2000 Jason R. Fink

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>

#define AUTHOR          "Jason R Fink"
#define MAXLEN		1024
#define NAME            "diskinfo"
#define VERSION		"version 1.8"

/* proc file handles */
static char *pfh[]=
{
	"/proc/ide/hda/model",
	"/proc/ide/hda/driver",
	"/proc/ide/hda/capacity",
	"/proc/ide/hda/cache",
	"/proc/ide/hda/geometry",
	"/proc/ide/hdb/model",
	"/proc/ide/hdb/driver",
	"/proc/ide/hdb/capacity",
	"/proc/ide/hdb/cache",
	"/proc/ide/hdb/geometry",
	"/proc/ide/hdc/model",
	"/proc/ide/hdc/driver",
	"/proc/ide/hdc/capacity",
	"/proc/ide/hdc/cache",
	"/proc/ide/hdc/geometry",
	"/proc/ide/hdd/model",
	"/proc/ide/hdd/driver",
	"/proc/ide/hdd/capacity",
	"/proc/ide/hdd/cache",
	"/proc/ide/hdd/geometry",
	"/proc/ide/hde/model",
	"/proc/ide/hde/driver",
	"/proc/ide/hde/capacity",
	"/proc/ide/hde/cache",
	"/proc/ide/hde/geometry",
	"/proc/ide/hdf/model",
	"/proc/ide/hdf/driver",
	"/proc/ide/hdf/capacity",
	"/proc/ide/hdf/cache",
	"/proc/ide/hdf/geometry",
	"/proc/ide/hdg/model",
	"/proc/ide/hdg/driver",
	"/proc/ide/hdg/capacity",
	"/proc/ide/hdg/cache",
	"/proc/ide/hdg/geometry",
	"/proc/ide/hdh/model",
	"/proc/ide/hdh/driver",
	"/proc/ide/hdh/capacity",
	"/proc/ide/hdh/cache",
	"/proc/ide/hdh/geometry",
};

typedef struct disk_struct * ptr_type;

/* Single IDE Disk Data Structure */
struct disk_struct
{
	char vendor         [MAXLEN];
	char model          [MAXLEN];
	char xtra           [MAXLEN];
	char ide_driver     [MAXLEN];
	char driver_version [MAXLEN];
	char capacity       [MAXLEN];
	char cache          [MAXLEN];
	char physical       [MAXLEN];
	char logical        [MAXLEN];
}; 

/* prototypes */
void usage(void);
void get_info(unsigned int sp, ptr_type p);
void model(char *model, ptr_type m);
void driver(char *driver, ptr_type d);
void capacity(char *capacity, ptr_type c);
void cache(char *cache, ptr_type e);
void geometry(char *geometry, ptr_type g);
void printdata(char *dev, ptr_type p);
void clean(void);

/* Initialize all Possible Pointers */
ptr_type hda_ptr, hdb_ptr, hdc_ptr, hdd_ptr, 
         hde_ptr, hdf_ptr, hdg_ptr, hdh_ptr;

/* Prints a usage message */
void usage()
{
	printf("%s %s by %s\n"
	"usage: %s <option>\n"
	"usage: %s <disk driver>\n" 
	"options:\n"
	"          -h mini-helper\n"
	"          -v version\n"
	"drivers:  /dev/hda /dev/hdb /dev/hdc /dev/hdd\n"
	"          /dev/hde /dev/hdf /dev/hdg /dev/hdh\n",
	NAME, VERSION, AUTHOR, NAME, NAME);
}

/* gets the appropiate information per the arguments */
void get_info(unsigned int sp, ptr_type p)
{
	model     (pfh[sp], p);
	driver    (pfh[(sp + 1)], p);
	capacity  (pfh[(sp + 2)], p);
	cache     (pfh[(sp + 3)], p);
	geometry  (pfh[(sp + 4)], p);
}

/* Reads the contents of the model file, puts them in some
   string variables then prints them out */
void model(char *model, ptr_type m)
{
	FILE *fp;
	static char ch[MAXLEN];
	static char vendor_line[MAXLEN];
	static char model_line[MAXLEN];
	static char xtra_line[MAXLEN];

	if ((fp = fopen(model, "r")) == NULL)
		return;

	while (fgets(ch, MAXLEN, fp) != NULL) {
		sscanf(ch, "%s %s %s", vendor_line, model_line, xtra_line);
	}

	strcpy(m->vendor, vendor_line);
	strcpy(m->model, model_line);
	strcpy(m->xtra, xtra_line);

	fclose(fp);
}

/* Reads the contents of the driver file, puts them in some
   string variables then prints them out */
void driver(char *driver, ptr_type d)
{
	FILE *drfp;
	static char drch[MAXLEN];
	static char ide_driver_line[MAXLEN];
	static char driver_number_line[MAXLEN];

	if ((drfp = fopen(driver, "r")) == NULL) 
		return;

	while (fgets(drch, MAXLEN, drfp) != NULL) {
		sscanf(drch, "%s %*s %s", ide_driver_line, 
			driver_number_line);
	}

	strcpy(d->ide_driver, ide_driver_line);
	strcpy(d->driver_version, driver_number_line);

	fclose(drfp);
}

/* Reads the contents of the capacity file, puts them in some
   string variables then prints them out */
void capacity(char *capacity, ptr_type c)
{
	FILE *cpfp;
	static char cpch[MAXLEN];
	static char capacity_line[MAXLEN];

	if ((cpfp = fopen(capacity, "r")) == NULL)
		return;

	while (fgets(cpch, MAXLEN, cpfp) != NULL) {
		sscanf(cpch, "%s", capacity_line);
	}

	strcpy(c->capacity, capacity_line);

	fclose(cpfp);
}

/* Reads the contents of the cache file, puts them in some
   string variables then prints them out */
void cache(char *cache, ptr_type e)
{
	FILE *cafp;
	static char cach[MAXLEN];
	static char cache_line[MAXLEN];

	if ((cafp = fopen(cache, "r")) == NULL)
		return;

	while (fgets(cach, MAXLEN, cafp) != NULL) {
		sscanf(cach, "%s", cache_line);
	}

	strcpy(e->cache, cache_line);

	fclose(cafp);
}

/* Reads the contents of the geometry file, puts them in some
   string variables then prints them out */
void geometry(char *geometry, ptr_type g)
{
	FILE *gmfp;
	static char gmch[MAXLEN];
	static char physical_line[MAXLEN];
	static char logical_line[MAXLEN];

	if ((gmfp = fopen(geometry, "r")) == NULL)
		return;

	while (fgets(gmch, MAXLEN, gmfp) != NULL) {
		if(!strncmp(gmch, "physical", 8)) { 
			sscanf(gmch, "%*s %s", physical_line);
		} else if(!strncmp(gmch, "logical", 7)) {
			sscanf(gmch, "%*s %s", logical_line);
		}
	}

	strcpy(g->physical, physical_line);
	strcpy(g->logical, logical_line);

	fclose(gmfp);
}

/* Prints out the information from a data structure as specified by 'p' */
void printdata(char *dev, ptr_type p)
{
	int len; /* Variable to check for Data */

	printf("IDE Disk Information for /dev/%s\n", dev);

	printf("Model Information: %s %s %s\n", p->vendor, p->model, p->xtra);
	printf("Disk Driver Type : %s\t\tDriver Version     : %s\n",
			p->ide_driver, p->driver_version);

	printf("Capacity in Bytes: %s\t\t", p->capacity);

	/* Some disks have no cache */
	len = strlen(p->cache); 
	if(len > 0)
		printf("Cache Size in Bytes: %s", p->cache);

	/* CDROM geometry may not be available */
	len = strlen(p->physical); 
	if(len > 0)
		printf("\nPhysical Geometry: %s\t\tLogical Geometry   : %s",
				p->physical, p->logical);

	printf("\n");
}

/* Clean up memory */
void clean()
{
	free(hda_ptr);
	free(hdb_ptr);
	free(hdc_ptr);
	free(hdd_ptr);
	free(hde_ptr);
	free(hdf_ptr);
	free(hdg_ptr);
	free(hdh_ptr);

	hda_ptr = NULL;
	hdb_ptr = NULL;
	hdc_ptr = NULL;
	hdd_ptr = NULL;
	hde_ptr = NULL;
	hdf_ptr = NULL;
	hdg_ptr = NULL;
	hdh_ptr = NULL;
}

/* Main */
int main(int argc, char **argv)
{
	register unsigned int opt = 1;		/* Tiny Options counter */

	/* Catch no args specified */
	if (argc == 1) {
		fprintf(stderr, "no arument specified\n");
		usage();
		exit(1);
	}

	/* main loop */
	while (opt < argc) { 
		if (argc > 1) {
			if (!strcmp(argv[opt], "/dev/hda")) {
				++opt;
				hda_ptr = malloc(sizeof(struct disk_struct));
				get_info(0, hda_ptr);
				printdata("hda", hda_ptr);
			} else if (!strcmp(argv[opt], "/dev/hdb")) {
				++opt;
				hdb_ptr = malloc(sizeof(struct disk_struct));
				get_info(5, hdb_ptr);
				printdata("hdb", hdb_ptr);
			} else if (!strcmp(argv[opt], "/dev/hdc")) {
				++opt;
				hdc_ptr = malloc(sizeof(struct disk_struct));
				get_info(10, hdc_ptr);
				printdata("hdc", hdc_ptr);
			} else if (!strcmp(argv[opt], "/dev/hdd")) {
				++opt;
				hdd_ptr = malloc(sizeof(struct disk_struct));
				get_info(15, hdd_ptr);
				printdata("hdd", hdd_ptr);
			} else if (!strcmp(argv[opt], "/dev/hde")) {
				++opt;
				hde_ptr = malloc(sizeof(struct disk_struct));
				get_info(20, hde_ptr);
				printdata("hde", hde_ptr);
			} else if (!strcmp(argv[opt], "/dev/hdf")) {
				++opt;
				hdf_ptr = malloc(sizeof(struct disk_struct));
				get_info(25, hdf_ptr);
				printdata("hdf", hdf_ptr);
			} else if (!strcmp(argv[opt], "/dev/hdg")) {
				++opt;
				hdg_ptr = malloc(sizeof(struct disk_struct));
				get_info(30, hdg_ptr);
				printdata("hdg", hdg_ptr);
			} else if (!strcmp(argv[opt], "/dev/hdh")) {
				++opt;
				hdh_ptr = malloc(sizeof(struct disk_struct));
				get_info(35, hdh_ptr);
				printdata("hdh", hdh_ptr);
			} else if (!strcmp(argv[opt], "-v")) {
				printf("%s %s by %s\n",
					NAME, VERSION, AUTHOR);
				exit(0);
			} else if (!strcmp(argv[opt], "-h")) {
				usage();
				exit(0);
			} else {
				++opt;
				fprintf(stderr, "invalid option or argument\n");
				usage(); 
				exit(2);
			}
		}
	}
	clean();
	return 0;
}


