[comp.os.minix] Dosdir/read/write for hard disks

nckachel@ndsuvax.UUCP (Tim Kachel) (07/16/88)

Below is a new version of dosdir... that will work with hard disks.  I have
tested it on floppies and 16bit FAT hard disk, not 12.  I do not, however,
expect any problems with the 12 bit Hard disk. -- Have fun.

Tim Kachel
Bitnet:	nckachel@plains.nodak.edu
UUCP:	{uunet, ihnp4!umn-cs}!ndsuvax!nckachel


-------------- cut-here ---------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	dosdir.c
# This archive created: Thu Jul 14 13:10:36 1988
export PATH; PATH=/bin:$PATH
if test -f 'README'
then
    echo shar: will not over-write existing file "'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
X
X	Here is a new dosdir[read, write] for hard disks and floppies.
XI have tested this program on my hard disk( 16 bit FAT) and again on 1.2
Xand 360K floppies and all works well.  The only problems anyone should (or
Xmight) run into would be with a larger partition that requires a larger FAT
XSIZE.  When increasing this size be careful of your memory allocations, since
Xyou can very readily run out of stack space. (I have not tested for an upper
Xlimit since it also depends on the depth of your directories.)
X
X	I named my device for my hard disk (the total disk) as hd_0 to
Xprevent any accidents on my part.
X
X	A few other notes, there are error traps to catch a partition that
Xis too large,  Debug statements are left in and just commented out to assist
Xin understanding harddisks and problem installations.  I also named the
Xprograms as dir, dosread, doswrite to remain consistent with DOS and reduce
Xtyping.
SHAR_EOF
fi # end of overwriting check
if test -f 'dosdir.c'
then
    echo shar: will not over-write existing file "'dosdir.c'"
else
sed 's/^X//' << \SHAR_EOF > 'dosdir.c'
X/*
X * dir - list MS-DOS directories.
X * doswrite - write stdin to DOS-file
X * dosread - read DOS-file to stdout
X *
X * Author: Michiel Huisjes.
X * 
X * Usage: dos... [-lra] drive [file/dir]
X *	  l: Give long listing.
X *	  r: List recursively.
X *	  a: Set ASCII bit.
X *
X *	Modified by Tim Kachel 4-88
X *		drive can be 0,1, a, b, c, d, e, f
X *		program will automatically configure to different hard disks
X *		  and the partitions for such (could change drive name for
X *		  a send hard disk if you have one)
X *			(has been tested on a 16 bit FAT AT drive)
X *			(High density AT diskettes and regular 360K)
X *		compile with cc -O -i
X *		Changed the name from dosdir to dir (link dosread/doswrite)
X *		hard disk is named /dev/hd_0 to avoid accidents
X *		  To test FAT sizes on your hard disk first try dir -lr c
X *			(or what ever your dos partition is)  if this works
X *			properly then all the rest should be okay.
X *		If there are any problems there is debugging information
X *		  in fdinit() -- please let me know of any problems
X */
X
X#include "stat.h"
X
X#define DRIVE0		"/dev/at0"
X#define DRIVE1		"/dev/at1"
X#define FDRIVE		"/dev/hd_0"
X
X#define DDDD	0xFD
X#define DDHD	0xF9
X#define DDFD	0xF8
X
X#define	MAX_CLUSTER_SIZE	4096
X#define MAX_FAT_SIZE		23552	/* 46 sectoren */
X#define HMASK		0xFF00
X#define LMASK		0x00FF
X
X#define MAX_ROOT_ENTRIES	512	/* 32 sectoren */
X#define FAT_START		512L	/* After bootsector */
X#define clus_add(cl_no)		((long) (((long) cl_no - 2L) \
X					* (long) cluster_size \
X					+ (long) data_start \
X				       ))
Xstruct dir_entry {
X	unsigned char d_name[8];
X	unsigned char d_ext[3];
X	unsigned char d_attribute;
X	unsigned char d_reserved[10];
X	unsigned short d_time;
X	unsigned short d_date;
X	unsigned short d_cluster;
X	unsigned long d_size;
X};
X
Xtypedef struct dir_entry DIRECTORY;
X
X#define NOT_USED	0x00
X#define ERASED		0xE5
X#define DIR		0x2E
X#define DIR_SIZE	(sizeof (struct dir_entry))
X#define SUB_DIR		0x10
X#define NIL_DIR		((DIRECTORY *) 0)
X
X#define LAST_CLUSTER	0x0FFF
X#define MASK		0xFF8		/* FF8 - FFF are last cluster */
X#define FREE		0x000
X#define BAD		0xFF0		/* Includes reserved */
X
X#define LAST_16		0xFFFF
X#define MASK_16		0xFFF8
X#define FREE_16		0x0000
X#define BAD_16		0xFFF0		/* Includes reserved */
X
Xtypedef char BOOL;
X
X#define TRUE	1
X#define FALSE	0
X#define NIL_PTR	((char *) 0)
X
X#define DOS_TIME	315532800L     /* 1970 - 1980 */
X
X#define READ			0
X#define WRITE			1
X#define disk_read(s, a, b)	disk_io(READ, s, a, b)
X#define disk_write(s, a, b)	disk_io(WRITE, s, a, b)
X
X#define FIND	3
X#define LABEL	4
X#define ENTRY	5
X#define find_entry(d, e, p)	directory(d, e, FIND, p)
X#define list_dir(d, e, f)	(void) directory(d, e, f, NIL_PTR)
X#define label()			directory(root, root_entries, LABEL, NIL_PTR)
X#define new_entry(d, e)		directory(d, e, ENTRY, NIL_PTR)
X
X#define is_dir(d)		((d)->d_attribute & SUB_DIR)
X
X#define EOF			0400
X#define EOF_MARK		'\032'
X#define STD_OUT			1
X#define flush()			print(STD_OUT, NIL_PTR, 0)
X
Xshort disk;
Xunion tbl
X{
X	unsigned char  twelve[4096];
X	unsigned short sixteen[MAX_FAT_SIZE / 2 ];
X} fat;
X
XDIRECTORY root[MAX_ROOT_ENTRIES], save_entry, *directory(), *read_cluster();
Xchar null[MAX_CLUSTER_SIZE], *device, path[128];
Xshort total_clusters, cluster_size, fat_size, root_entries, sub_entries;
X
XBOOL Rflag, Lflag, Aflag, dos_read, dos_write, dos_dir, Tfat = TRUE;
X
Xunsigned short free_cluster(), next_cluster();
Xchar *make_name(), *num_out(), *slash(), *brk();
Xlong mark, data_start, lseek(), time(), f_start;
X
Xleave(nr)
Xshort nr;
X{
X	(void) umount(device);
X	exit(nr);
X}
X
Xusage(prog_name)
Xregister char *prog_name;
X{
X	print_string(TRUE, "Usage: %s [%s\n", prog_name,
X		     dos_dir ? "-lr] drive [dir]" : "-a] drive file");
X	exit(1);
X}
X
Xmain(argc, argv)
Xint argc;
Xregister char *argv[];
X{
X	register char *arg_ptr = slash(argv[0]);
X	DIRECTORY *entry;
X	short index = 1;
X	char dev_nr;
X	unsigned char fat_type, fat_check;
X	BOOL fdisk = FALSE;
X	int i;
X
X	if (!strcmp(arg_ptr, "dir"))
X		dos_dir = TRUE;
X	else if (!strcmp(arg_ptr, "dosread"))
X		dos_read = TRUE;
X	else if (!strcmp(arg_ptr, "doswrite"))
X		dos_write = TRUE;
X	else {
X		print_string(TRUE, "Program should be named dosread, doswrite or dir.\n");
X		exit(1);
X	}
X
X	if (argc == 1)
X		usage(argv[0]);
X
X	if (argv[1][0] == '-') {
X		for (arg_ptr = &argv[1][1]; *arg_ptr; arg_ptr++) {
X			if (*arg_ptr == 'l' && dos_dir)
X				Lflag = TRUE;
X			else if (*arg_ptr == 'r' && dos_dir)
X				Rflag = TRUE;
X			else if (*arg_ptr == 'a' && !dos_dir)
X				Aflag = TRUE;
X			else
X				usage(argv[0]);
X		}
X		index++;
X	}
X
X	if (index == argc)
X		usage(argv[0]);
X
X	switch (dev_nr = *argv[index++])
X	{
X		case '0':
X		case 'a':	device = DRIVE0; break;
X		case '1':
X		case 'b':	device = DRIVE1; break;
X		case 'c':
X		case 'd':
X		case 'e':
X		case 'f':	fdisk = TRUE; device = FDRIVE; break;
X		default :	usage(argv[0]);
X	}
X
X	if ((disk = open(device, 2)) < 0) {
X		print_string(TRUE, "Cannot open %s\n", device);
X		exit(1);
X	}
X
X	if (fdisk) {		/* fixed disk */
X		fdinit(dev_nr);
X		disk_read(f_start, &fat_type, sizeof(fat_type));
X		if (fat_type != DDFD) {
X			print_string(TRUE, "Fixed disk is not DOS\n");
X			leave(1);
X		}
X	}
X	else {		/* use standard start for floppies */
X		f_start = FAT_START;
X		disk_read(f_start, &fat_type, sizeof(fat_type));
X		if (fat_type == DDDD) {		/* Double-sided double-density 9 s/t */
X			total_clusters = 355;	/* 720 - 7 - 2 - 2 - 1 */
X			cluster_size = 1024;	/* 2 sectors per cluster */
X			fat_size = 1024;	/* 2 sectors */
X			data_start = 6144L;	/* Starts on sector #12 */
X			root_entries = 112;	
X			sub_entries = 32;	/* 1024 / 32 */
X		}
X		else if (fat_type == DDHD) {	/* Double-sided high-density 15 s/t */
X			total_clusters = 2372;	/* 2400 - 14 - 7 - 7 - 1 */
X			cluster_size = 512;	/* 1 sector per cluster */
X			fat_size = 3584;	/* 7 sectors */
X			data_start = 14848L;	/* Starts on sector #29 */
X			root_entries = 224;	
X			sub_entries = 16;	/* 512 / 32 */
X		}
X		else {
X        		print_string(TRUE, "Diskette is not DOS 2.0 360K or 1.2M\n");
X			leave(1);
X		}
X	}
X
X	disk_read(f_start + (long) fat_size, &fat_check, sizeof(fat_check));
X	if (fat_check != fat_type) {
X		print_string(TRUE, "Disk type in FAT copy differs from disk type in FAT original.\n");
X		leave(1);
X	}
X
X	if (Tfat)	/* twelve bit FAT entries */
X		disk_read(f_start, fat.twelve, fat_size);
X	else		/* sixteen bit */
X		disk_read(f_start, fat.sixteen, fat_size);
X/*******
X	for (i=0; i<= 30; i++){
X		printf("%x\t%c", fat.sixteen[i], (i % 10) ? ' ':'\n');
X	}
X	leave(1);
X/*******/
X	disk_read(f_start + 2L * (long) fat_size, root, DIR_SIZE * root_entries);
X/*******
X	for (i=0; i<2; i++){
X		printf("%s d_name\n", root[i].d_name);
X		printf("%s d_ext\n",  root[i].d_ext);
X		printf("%d d_attr\n",  root[i].d_attribute);
X		printf("%s d_reserved\n",  root[i].d_reserved);
X		printf("%d d_time\n",  root[i].d_time);
X		printf("%d d_date\n",  root[i].d_date);
X		printf("%d d_cluster\n",  root[i].d_cluster);
X		printf("%D d_size\n",  root[i].d_size);
X	}
X
X
X/*********/
X	if (dos_dir) {
X		entry = label();
X		print_string(FALSE, "Volume in drive %c ", dev_nr);
X		if (entry == NIL_DIR)
X			print(STD_OUT, "has no label.\n\n", 0);
X		else
X			print_string(FALSE, "is %S\n\n", entry->d_name);
X	}
X
X	if (argv[index] == NIL_PTR) {
X		if (!dos_dir)
X			usage(argv[0]);
X		print(STD_OUT, "Root directory:\n", 0);
X		list_dir(root, root_entries, FALSE);
X		free_blocks();
X		flush();
X		leave(0);
X	}
X
X	for (arg_ptr = argv[index]; *arg_ptr; arg_ptr++)
X		if (*arg_ptr == '\\')
X			*arg_ptr = '/';
X		else if (*arg_ptr >= 'a' && *arg_ptr <= 'z')
X			*arg_ptr += ('A' - 'a');
X	if (*--arg_ptr == '/')
X		*arg_ptr = '\0';       /* skip trailing '/' */
X
X	add_path(argv[index], FALSE);
X	add_path("/", FALSE);
X
X	if (dos_dir)
X		print_string(FALSE, "Directory %s:\n", path);
X
X	entry = find_entry(root, root_entries, argv[index]);
X
X	if (dos_dir) {
X		list_dir(entry, sub_entries, FALSE);
X		free_blocks();
X	}
X	else if (dos_read)
X		extract(entry);
X	else {
X		if (entry != NIL_DIR) {
X			flush();
X			if (is_dir(entry))
X				print_string(TRUE, "%s is a directory.\n", path);
X			else
X				print_string(TRUE, "%s already exists.\n", argv[index]);
X			leave(1);
X		}
X
X		add_path(NIL_PTR, TRUE);
X
X		if (*path)
X			make_file(find_entry(root, root_entries, path),
X				  sub_entries, slash(argv[index]));
X		else
X			make_file(root, root_entries, argv[index]);
X	}
X
X	(void) close(disk);
X	flush();
X	leave(0);
X}
X
Xfdinit(part_nr)		/* Fixed Disk Initializations */
Xchar part_nr;
X{
X
X#define	SECSIZE        512        /* sector size        */
X#define TABLEOFFSET    0x1be      /* offset in boot sector*/
X
X	/*
X	 * Description of entry in partition table
X	 */
X	struct part_entry {
X	    char    bootind;    /* boot indicator 0/0x80    */
X	    char    start_head;    /* head value for first sector    */
X	    char    start_sec;    /* sector value for first sector*/
X	    char    start_cyl;    /* track value for first sector    */
X	    char    sysind;        /* system indicator 00=?? 01=DOS*/
X	    char    last_head;    /* head value for last sector    */
X	    char    last_sec;    /* sector value for last sector    */
X	    char    last_cyl;    /* track value for last sector    */
X	    long    lowsec;        /* logical first sector        */
X	    long    size;        /* size of partion in sectors    */
X	} *pe;
X
X	char    secbuf[SECSIZE];
X
X	/*
X	 *	Description of the boot block
X	 */
X	struct {
X		unsigned char jump[3];
X		unsigned char oem[8];
X		unsigned char bytes_sector[2];
X		unsigned char cluster_size;
X		unsigned char res_sectors[2];
X		unsigned char num_fats;
X		unsigned char root_entries[2];
X		unsigned char logical_sectors[2];
X		unsigned char media_type;
X		unsigned char fat_sectors[2];
X		unsigned char track_sectors[2];
X		unsigned char num_heads[2];
X		unsigned char hidden_sectors[2];
X	} boot;
X
X	short block_size, reserved;	long boot_loc;
X
X	disk_read(0L, secbuf, SECSIZE);	/* get boot sector */
X		/* offset into boot sector for the partition table */
X	pe = (struct part_entry *)&secbuf[TABLEOFFSET];
X		/* get the proper partition */
X	switch(part_nr) {
X		case 'f': pe++;
X		case 'e': pe++;
X		case 'd': pe++;
X		case 'c': boot_loc = pe->lowsec * 512L; break;
X        	default:  printf("Error: unknown partition\n"); leave();
X	}
X		/* now read the boot block for the partition needed */
X	disk_read(boot_loc, &boot, sizeof(boot));
X
X	/* this section can be used to print drive information */
X/**************
X	printf("OEM = %s\n", boot.oem);
X	printf("Bytes/sector = %d\n",
X		(boot.bytes_sector[1] << 8 & HMASK) + (boot.bytes_sector[0] & LMASK)); 
X	printf("Sectors/cluster = %d\n", boot.cluster_size);
X	printf("Number of Reserved Clusters = %d\n",
X		(boot.res_sectors[1] << 8 & HMASK) + (boot.res_sectors[0] & LMASK));
X	printf("Number of FAT's = %d\n", boot.num_fats);
X	printf("Number of root-directory entries = %d\n",
X		(boot.root_entries[1] << 8 & HMASK) + (boot.root_entries[0] & LMASK));
X	printf("Total sectors in logical volume = %D\n",
X		(long) (boot.logical_sectors[1] << 8 & HMASK) + (boot.logical_sectors[0] & LMASK));
X	printf("Media Descriptor = %x\n", boot.media_type);
X	printf("Number of sectors/FAT = %d\n",
X		(boot.fat_sectors[1] << 8 & HMASK) + (boot.fat_sectors[0] & LMASK));
X	printf("Sectors/track = %d\n",
X		(boot.track_sectors[1] << 8 & HMASK) + (boot.track_sectors[0] & LMASK));
X	printf("Number of Heads = %d\n",
X		(boot.num_heads[1] << 8 & HMASK) + (boot.num_heads[0] & LMASK));
X	printf("Number of hidden sectors = %d\n",
X		(boot.hidden_sectors[1]  << 8 & HMASK) + (boot.hidden_sectors[0] & LMASK));
X	leave(1);
X/**************/
X	if (boot.media_type != DDFD) {
X		printf("DISK is not DOS Format.\n");
X		leave(1);
X	}
X	if (boot.num_fats != 2) {
X		printf("Disk does not have two FAT Tables!\n");
X		leave(1);
X	}
X	block_size = (boot.bytes_sector[1] << 8 & HMASK) +
X			 (boot.bytes_sector[0] & LMASK);
X	if ((cluster_size = block_size * boot.cluster_size)
X					> MAX_CLUSTER_SIZE) {
X		printf("Cluster size is larger than MAX_CLUSTER_SIZE.\n");
X		leave(1);
X	}
X	reserved =  ((boot.res_sectors[1] << 8 & HMASK) +
X			(boot.res_sectors[0] & LMASK));
X	f_start = boot_loc + (long) block_size * (long) reserved;
X	root_entries = (boot.root_entries[1] << 8 & HMASK) +
X			(boot.root_entries[0] & LMASK);
X	fat_size = (boot.fat_sectors[1] << 8 & HMASK) +
X			 (boot.fat_sectors[0] & LMASK);
X		/* (sectors - rootdir - fats - reserved) / blocks/cluster */
X	total_clusters = (int) ((long) ((boot.logical_sectors[1] << 8 & HMASK) +
X				   (boot.logical_sectors[0] & LMASK)) - 
X		  (root_entries * 32 / block_size) -
X		  (fat_size * 2) - reserved) / boot.cluster_size;
X	if (total_clusters > 4096)
X		Tfat = FALSE;		/* sixteen bit fat entries */
X	if ( (fat_size *= block_size) > MAX_FAT_SIZE) {
X		printf("Disk FAT is larger than MAX_FAT_SIZE.\n");
X		leave(1);
X	}
X	sub_entries = cluster_size / 32;
X	data_start = f_start + (long) (fat_size * 2L) +
X			(long) (root_entries * 32L);
X/**********
X	printf("f_start = %D\n", f_start);
X	printf("total_clusters = %d\n", total_clusters);
X	printf("cluster_size = %d\n", cluster_size);
X	printf("fat_size = %d\n", fat_size);
X	printf("data_start = %D\n", data_start);
X	printf("root_entries = %d\n", root_entries);
X	printf("sub_entries = %d\n", sub_entries);
X	printf("Tfat = %d\n", Tfat);
X	leave(1);
X/*********/
X}
X
XDIRECTORY *directory(dir, entries, function, pathname)
XDIRECTORY *dir;
Xshort entries;
XBOOL function;
Xregister char *pathname;
X{
X	register DIRECTORY *dir_ptr = dir;
X	DIRECTORY *mem = NIL_DIR;
X	unsigned short cl_no = dir->d_cluster;
X	unsigned short type, last;
X	char file_name[14];
X	char *name;
X	int i = 0;
X
X	if (function == FIND) {
X        while (*pathname != '/' && *pathname && i < 12)
X			file_name[i++] = *pathname++;
X		while (*pathname != '/' && *pathname)
X			pathname++;
X		file_name[i] = '\0';
X	}
X
X	do {
X		if (entries != root_entries) {
X			mem = dir_ptr = read_cluster(cl_no);
X			last = cl_no;
X			cl_no = next_cluster(cl_no);
X		}
X
X		for (i = 0; i < entries; i++, dir_ptr++) {
X			type = dir_ptr->d_name[0] & 0x0FF;
X			if (function == ENTRY) {
X				if (type == NOT_USED || type == ERASED) {
X					mark = lseek(disk, 0L, 1) -
X						(long) cluster_size +
X						(long) i * (long) DIR_SIZE;
X					if (!mem)
X						mark += (long) cluster_size - (long) (root_entries * sizeof (DIRECTORY));
X					return dir_ptr;
X				}
X				continue;
X			}
X			if (type == NOT_USED)
X				break;
X			if (dir_ptr->d_attribute & 0x08) {
X				if (function == LABEL)
X					return dir_ptr;
X				continue;
X			}
X			if (type == DIR || type == ERASED || function == LABEL)
X				continue;
X			type = is_dir(dir_ptr);
X			name = make_name(dir_ptr, (function == FIND) ?
X					 FALSE : type);
X			if (function == FIND) {
X				if (strcmp(file_name, name) != 0)
X					continue;
X				if (!type) {
X					if (dos_dir || *pathname) {
X						flush();
X						print_string(TRUE, "Not a directory: %s\n", file_name);
X						leave(1);
X					}
X				}
X				else if (*pathname == '\0' && dos_read) {
X					flush();
X					print_string(TRUE, "%s is a directory.\n", path);
X					leave(1);
X				}
X				if (*pathname) {
X					dir_ptr = find_entry(dir_ptr,
X						   sub_entries, pathname + 1);
X				}
X				if (mem) {
X					if (dir_ptr) {
X						bcopy(dir_ptr, &save_entry, DIR_SIZE);
X						dir_ptr = &save_entry;
X					}
X					(void) brk(mem);
X				}
X				return dir_ptr;
X			}
X			else {
X				if (function == FALSE)
X					show(dir_ptr, name);
X				else if (type) {	/* Recursive */
X					print_string(FALSE, "Directory %s%s:\n", path, name);
X					add_path(name, FALSE);
X					list_dir(dir_ptr, sub_entries, FALSE);
X					add_path(NIL_PTR, FALSE);
X				}
X			}
X		}
X		if (mem)
X			(void) brk(mem);
X	} while ((Tfat && cl_no != LAST_CLUSTER && mem) ||
X		 (!Tfat && cl_no != LAST_16 && mem));
X
X	switch (function) {
X		case FIND:
X			if (dos_write && *pathname == '\0')
X				return NIL_DIR;
X			flush();
X			print_string(TRUE, "Cannot find `%s'.\n", file_name);
X			leave(1);
X		case LABEL:
X			return NIL_DIR;
X		case ENTRY:
X			if (!mem) {
X				flush();
X				print_string(TRUE, "No entries left in root directory.\n");
X				leave(1);
X			}
X
X			cl_no = free_cluster(TRUE);
X			link_fat(last, cl_no);
X			if (Tfat)
X				link_fat(cl_no, LAST_CLUSTER);
X			else
X				link_fat(cl_no, LAST_16);
X			disk_write(clus_add(cl_no), null, cluster_size);
X
X			return new_entry(dir, entries);
X		case FALSE:
X			if (Rflag) {
X				print(STD_OUT, "\n", 0);
X				list_dir(dir, entries, TRUE);
X			}
X	}
X}
X
Xextract(entry)
Xregister DIRECTORY *entry;
X{
X	register unsigned short cl_no = entry->d_cluster;
X	char buffer[MAX_CLUSTER_SIZE];
X	short rest;
X
X	if (entry->d_size == 0)	       /* Empty file */
X		return;
X
X	do {
X		disk_read(clus_add(cl_no), buffer, cluster_size);
X		rest = (entry->d_size > (long) cluster_size) ? cluster_size : (short) entry->d_size;
X		print(STD_OUT, buffer, rest);
X		entry->d_size -= (long) rest;
X		cl_no = next_cluster(cl_no);
X		if ((Tfat && cl_no == BAD) || (!Tfat && cl_no == BAD_16)){
X			flush();
X			print_string(TRUE, "Reserved cluster value encountered.\n");
X			leave(1);
X		}
X	} while ((Tfat && entry->d_size && cl_no != LAST_CLUSTER) ||
X		 (!Tfat && entry->d_size && cl_no != LAST_16));
X
X	if ((Tfat && cl_no != LAST_CLUSTER) || (!Tfat && cl_no != LAST_16))
X		print_string(TRUE, "Too many clusters allocated for file.\n");
X	else if (entry->d_size != 0)
X		print_string(TRUE, "Premature EOF: %L bytes left.\n",
X			     entry->d_size);
X}
X
Xprint(fd, buffer, bytes)
Xshort fd;
Xregister char *buffer;
Xregister short bytes;
X{
X	static short index;
X	static BOOL lf_pending = FALSE;
X	static char output[MAX_CLUSTER_SIZE + 1];
X
X	if (buffer == NIL_PTR) {
X		if (dos_read && Aflag && lf_pending) {
X			output[index++] = '\r';
X			lf_pending = FALSE;
X		}
X		if (write(fd, output, index) != index)
X			bad();
X		index = 0;
X		return;
X	}
X
X	if (bytes == 0)
X		bytes = strlen(buffer);
X
X	while (bytes--) {
X		if (index >= MAX_CLUSTER_SIZE) {
X			if (write(fd, output, index) != index)
X				bad ();
X			index = 0;
X		}
X		if (dos_read && Aflag) {
X			if (*buffer == '\r') {
X				if (lf_pending)
X					output[index++] = *buffer++;
X				else {
X					lf_pending = TRUE;
X					buffer++;
X				}
X			}
X			else if (*buffer == '\n') {
X				output[index++] = *buffer++;
X				lf_pending = FALSE;
X			}
X			else if (lf_pending) {
X				output[index++] = '\r';
X				output[index++] = *buffer++;
X			}
X			else if ((output[index++] = *buffer++) == EOF_MARK) {
X				if (lf_pending) {
X					output[index - 1] = '\r';
X					index++;
X					lf_pending = FALSE;
X				}
X				index--;
X				return;
X			}
X		}
X		else
X			output[index++] = *buffer++;
X	}
X}
X
Xmake_file(dir_ptr, entries, name)
XDIRECTORY *dir_ptr;
Xint entries;
Xchar *name;
X{
X	register DIRECTORY *entry = new_entry(dir_ptr, entries);
X	register char *ptr;
X	char buffer[MAX_CLUSTER_SIZE];
X	unsigned short cl_no, next;
X	short i, r;
X	long size = 0L;
X
X    bcopy("           ",&entry->d_name[0],11);    /* clear entry */
X    for (i = 0, ptr = name; i < 8 && *ptr != '.' && *ptr; i++)
X        entry->d_name[i] = *ptr++;
X    while (*ptr != '.' && *ptr)
X        ptr++;
X    if (*ptr == '.')
X        ptr++;
X    for (i=0;i < 3 && *ptr; i++)
X        entry->d_ext[i] = *ptr++;
X
X	for (i = 0; i < 10; i++)
X		entry->d_reserved[i] = '\0';
X	entry->d_attribute = '\0';
X
X	entry->d_cluster = 0;
X
X	while ((r = fill(buffer)) > 0) {
X		if ((next = free_cluster(FALSE)) > total_clusters) {
X			print_string(TRUE, "Disk full. File truncated.\n");
X			break;
X		}
X
X		disk_write(clus_add(next), buffer, r);
X
X		if (entry->d_cluster == 0)
X			cl_no = entry->d_cluster = next;
X		else {
X			link_fat(cl_no, next);
X			cl_no = next;
X		}
X
X		size += r;
X	}
X
X	if (entry->d_cluster != 0) {
X		if (Tfat)
X			link_fat(cl_no, LAST_CLUSTER);
X		else
X			link_fat(cl_no, LAST_16);
X	}
X
X	entry->d_size = Aflag ? (size - 1) : size;	/* Strip added ^Z */
X	fill_date(entry);
X	disk_write(mark, entry, DIR_SIZE);
X	if (Tfat) {
X		disk_write(f_start, fat.twelve, fat_size);
X		disk_write(f_start + (long) fat_size, fat.twelve, fat_size);
X	} else {
X		disk_write(f_start, fat.sixteen, fat_size);
X		disk_write(f_start + (long) fat_size, fat.sixteen, fat_size);
X	}
X}
X
X
X#define SEC_MIN	60L
X#define SEC_HOUR	(60L * SEC_MIN)
X#define SEC_DAY	(24L * SEC_HOUR)
X#define SEC_YEAR	(365L * SEC_DAY)
X#define SEC_LYEAR	(366L * SEC_DAY)
X
Xshort mon_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
X
Xfill_date(entry)
XDIRECTORY *entry;
X{
X	register long cur_time = time((long *) 0) - DOS_TIME;
X	unsigned short year = 0, month = 1, day, hour, minutes, seconds;
X	int i;
X	long tmp;
X
X	if (cur_time < 0)	       /* Date not set on booting ... */
X		cur_time = 0;
X	for (;;) {
X		tmp = (year % 4 == 0) ? SEC_LYEAR : SEC_YEAR;
X		if (cur_time < tmp)
X			break;
X		cur_time -= tmp;
X		year++;
X	}
X
X	day = (unsigned short) (cur_time / SEC_DAY);
X	cur_time -= (long) day *SEC_DAY;
X
X	hour = (unsigned short) (cur_time / SEC_HOUR);
X	cur_time -= (long) hour *SEC_HOUR;
X
X	minutes = (unsigned short) (cur_time / SEC_MIN);
X	cur_time -= (long) minutes *SEC_MIN;
X
X	seconds = (unsigned short) cur_time;
X
X	mon_len[1] = (year % 4 == 0) ? 29 : 28;
X	i = 0;
X	while (day >= mon_len[i]) {
X		month++;
X        day -= mon_len[i++];
X	}
X	day++;
X
X	entry->d_date = (year << 9) | (month << 5) | day;
X	entry->d_time = (hour << 11) | (minutes << 5) | seconds;
X}
X
Xchar *make_name(dir_ptr, dir_fl)
Xregister DIRECTORY *dir_ptr;
Xshort dir_fl;
X{
X	static char name_buf[14];
X	register char *ptr = name_buf;
X	short i;
X
X	for (i = 0; i < 8; i++)
X		*ptr++ = dir_ptr->d_name[i];
X
X	while (*--ptr == ' ');
X
X	ptr++;
X	if (dir_ptr->d_ext[0] != ' ') {
X		*ptr++ = '.';
X		for (i = 0; i < 3; i++)
X			*ptr++ = dir_ptr->d_ext[i];
X		while (*--ptr == ' ');
X		ptr++;
X	}
X	if (dir_fl)
X		*ptr++ = '/';
X	*ptr = '\0';
X
X	return name_buf;
X}
X
Xfill(buffer)
Xregister char *buffer;
X{
X	static BOOL eof_mark = FALSE;
X	char *last = &buffer[cluster_size];
X	char *begin = buffer;
X	register short c;
X
X	if (eof_mark)
X		return 0;
X
X	while (buffer < last) {
X		if ((c = get_char()) == EOF) {
X			eof_mark = TRUE;
X			if (Aflag)
X				*buffer++ = EOF_MARK;
X			break;
X		}
X		*buffer++ = c;
X	}
X
X    return (int) (buffer - begin);
X}
X
Xget_char()
X{
X	static short read_chars, index;
X	static char input[MAX_CLUSTER_SIZE];
X	static BOOL new_line = FALSE;
X
X	if (new_line == TRUE) {
X		new_line = FALSE;
X		return '\n';
X	}
X
X	if (index == read_chars) {
X		if ((read_chars = read(0, input, cluster_size)) == 0)
X			return EOF;
X		index = 0;
X	}
X
X	if (Aflag && input[index] == '\n') {
X		new_line = TRUE;
X		index++;
X		return '\r';
X	}
X
X	return input[index++];
X}
X
X#define HOUR	0xF800		       /* Upper 5 bits */
X#define MIN	0x07E0		       /* Middle 6 bits */
X#define YEAR	0xFE00		       /* Upper 7 bits */
X#define MONTH	0x01E0		       /* Mid 4 bits */
X#define DAY	0x01F		       /* Lowest 5 bits */
X
Xchar *month[] = {
X		 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X		 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
X};
X
Xmodes(mode)
Xregister unsigned char mode;
X{
X	print_string(FALSE, "\t%c%c%c%c%c", (mode & SUB_DIR) ? 'd' : '-',
X		     (mode & 02) ? 'h' : '-', (mode & 04) ? 's' : '-',
X		     (mode & 01) ? '-' : 'w', (mode & 0x20) ? 'a' : '-');
X}
X
Xshow(dir_ptr, name)
XDIRECTORY *dir_ptr;
Xchar *name;
X{
X	register unsigned short e_date = dir_ptr->d_date;
X	register unsigned short e_time = dir_ptr->d_time;
X	unsigned short next;
X	char bname[20];
X	short i = 0;
X
X	while (*name && *name != '/')
X		bname[i++] = *name++;
X	bname[i] = '\0';
X	if (!Lflag) {
X		print_string(FALSE, "%s\n", bname);
X		return;
X	}
X	modes(dir_ptr->d_attribute);
X	print_string(FALSE, "\t%s%s", bname, strlen(bname) < 8 ? "\t\t" : "\t");
X	i = 1;
X	if (is_dir(dir_ptr)) {
X		next = dir_ptr->d_cluster;
X		while (((next = next_cluster(next)) != LAST_CLUSTER && Tfat) ||
X			(!Tfat && next != LAST_16))
X			i++;
X		print_string(FALSE, "%L", (long) i * (long) cluster_size);
X	}
X	else
X		print_string(FALSE, "%L", dir_ptr->d_size);
X	print_string(FALSE, "\t%N:%N %P %s %d\n", ((e_time & HOUR) >> 11),
X		     ((e_time & MIN) >> 5), (e_date & DAY),
X	   month[((e_date & MONTH) >> 5) - 1], ((e_date & YEAR) >> 9) + 1980);
X}
X
Xfree_blocks()
X{
X	register unsigned short cl_no;
X	register short free = 0;
X	short bad = 0;
X	if (Tfat)
X		for (cl_no = 2; cl_no <= total_clusters; cl_no++) {
X			switch (next_cluster(cl_no)) {
X				case FREE:
X					free++;
X					break;
X				case BAD:
X					bad++;
X			}
X		}
X	else
X		for (cl_no = 2; cl_no <= total_clusters; cl_no++) {
X			switch (next_cluster(cl_no)) {
X				case FREE_16:
X					free++;
X					break;
X				case BAD_16:
X					bad++;
X			}
X		}
X
X	print_string(FALSE, "Free space: %L bytes.\n", (long) free * (long) cluster_size);
X	if (bad)
X		print_string(FALSE, "Bad sectors: %L bytes.\n", (long) bad * (long) cluster_size);
X}
X
Xchar *num_out(number)
Xregister long number;
X{
X	static char num_buf[13];
X	char temp[13];
X	register short i = 0;
X	short j;
X
X	if (number == 0)
X		temp[i++] = '0';
X
X	while (number) {
X		temp[i++] = (char) (number % 10L + '0');
X		number /= 10L;
X	}
X
X	for (j = 0; j < 11; j++)
X		num_buf[j] = temp[i - j - 1];
X
X	num_buf[i] = '\0';
X	return num_buf;
X}
X
X/* VARARGS */
Xprint_string(err_fl, fmt, args)
XBOOL err_fl;
Xchar *fmt;
Xint args;
X{
X	char buf[200];
X	register char *buf_ptr = buf;
X	char *scan_ptr;
X	register int *arg_ptr = &args;
X	short i;
X
X	while (*fmt) {
X		if (*fmt == '%') {
X			fmt++;
X			if (*fmt == 'c') {
X				*buf_ptr++ = (char) *arg_ptr++;
X				fmt++;
X				continue;
X			}
X			if (*fmt == 'S') {
X				scan_ptr = (char *) *arg_ptr;
X				for (i = 0; i < 11; i++)
X					*buf_ptr++ = *scan_ptr++;
X				fmt++;
X				continue;
X			}
X			if (*fmt == 's')
X				scan_ptr = (char *) *arg_ptr;
X			else if (*fmt == 'L') {
X				scan_ptr = num_out(*((long *) arg_ptr));
X				arg_ptr++;
X			}
X			else {
X				scan_ptr = num_out((long) *arg_ptr);
X				if (*fmt == 'P' && *arg_ptr < 10)
X					*buf_ptr++ = ' ';
X				else if (*fmt == 'N' && *arg_ptr < 10)
X					*buf_ptr++ = '0';
X			}
X			while (*buf_ptr++ = *scan_ptr++);
X			buf_ptr--;
X			arg_ptr++;
X			fmt++;
X		}
X		else
X			*buf_ptr++ = *fmt++;
X	}
X
X	*buf_ptr = '\0';
X
X	if (err_fl) {
X		flush();
X        write(2, buf, (int) (buf_ptr - buf));
X	}
X	else
X		print(STD_OUT, buf, 0);
Xflush();
X}
X
XDIRECTORY *read_cluster(cluster)
Xregister unsigned short cluster;
X{
X	register DIRECTORY *sub_dir;
X	extern char *sbrk();
X
X	if ((sub_dir = (DIRECTORY *) sbrk(cluster_size)) < 0) {
X		print_string(TRUE, "Cannot set break!\n");
X		leave(1);
X	}
X	disk_read(clus_add(cluster), sub_dir, cluster_size);
X
X	return sub_dir;
X}
X
Xunsigned short free_cluster(leave_fl)
XBOOL leave_fl;
X{
X	static unsigned short cl_index = 2;
X
X	if (Tfat)
X		while (cl_index <= total_clusters && next_cluster(cl_index) != FREE)
X			cl_index++;
X	else		/* Sixteen bit */
X		while (cl_index <= total_clusters && next_cluster(cl_index) != FREE_16)
X			cl_index++;
X
X	if (leave_fl && cl_index > total_clusters) {
X		flush();
X		print_string(TRUE, "Disk full. File not added.\n");
X		leave(1);
X	}
X
X	return cl_index++;
X}
X
X/* ****************FIX FOR SIXTEEN BIT ***************** */
Xlink_fat(cl_1, cl_2)
Xunsigned short cl_1;
Xregister unsigned short cl_2;
X{
X	if (Tfat) {
X		register unsigned char *fat_index = &fat.twelve[(cl_1 >> 1) * 3 + 1];
X		if (cl_1 & 0x01) {
X			*(fat_index + 1) = cl_2 >> 4;
X			*fat_index = (*fat_index & 0x0F) | ((cl_2 & 0x0F) << 4);
X		}
X		else {
X			*(fat_index - 1) = cl_2 & 0x0FF;
X			*fat_index = (*fat_index & 0xF0) | (cl_2 >> 8);
X		}
X	}
X	else {
X		fat.sixteen[cl_1] = cl_2;
X	}
X}
X
X
Xunsigned short next_cluster(cl_no)
Xregister unsigned short cl_no;
X{
X	if (Tfat) {
X		register unsigned char *fat_index = &fat.twelve[(cl_no >> 1) * 3 + 1];
X
X		if (cl_no & 0x01)
X			cl_no = (*(fat_index + 1) << 4) | (*fat_index >> 4);
X		else
X			cl_no = ((*fat_index & 0x0F) << 8) | *(fat_index - 1);
X
X		if ((cl_no & MASK) == MASK)
X			cl_no = LAST_CLUSTER;
X		else if ((cl_no & BAD) == BAD)
X			cl_no = BAD;
X	}
X	else {
X		/*cl_no = fat.sixteen[cl_no << 1];*/
X		cl_no = fat.sixteen[cl_no];
X		if ((cl_no & MASK_16) == MASK_16)
X			cl_no = LAST_16;
X		else if ((cl_no & BAD_16) == BAD_16)
X			cl_no = BAD_16;
X	}
X
X	return cl_no;
X}
X
Xchar *slash(str)
Xregister char *str;
X{
X	register char *result = str;
X
X	while (*str)
X		if (*str++ == '/')
X			result = str;
X
X	return result;
X}
X
Xadd_path(file, slash_fl)
Xregister char *file;
XBOOL slash_fl;
X{
X	register char *ptr = path;
X
X	while (*ptr)
X		ptr++;
X
X	if (file == NIL_PTR) {
X		ptr--;
X		do {
X			ptr--;
X		} while (*ptr != '/' && ptr != path);
X		if (ptr != path && !slash_fl)
X			ptr++;
X		*ptr = '\0';
X	}
X	else
X		while (*ptr++ = *file++);
X}
X
Xbcopy(src, dest, bytes)
Xregister char *src, *dest;
Xshort bytes;
X{
X	while (bytes--)
X		*dest++ = *src++;
X}
X
Xdisk_io(op, seek, address, bytes)
Xregister BOOL op;
Xunsigned long seek;
XDIRECTORY *address;
Xregister unsigned bytes;
X{
X	unsigned int r;
X
X	if (lseek(disk, seek, 0) < 0L) {
X		flush();
X		print_string(TRUE, "Bad lseek\n");
X		leave(1);
X	}
X
X	if (op == READ)
X		r = read(disk, address, bytes);
X	else
X		r = write(disk, address, bytes);
X
X	if (r != bytes)
X		bad();
X}
X
Xbad()
X{
X	flush();
X	perror("I/O error");
X	leave(1);
X}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0