[comp.os.minix] dosread, tos

evans@ditsyda.oz (Bruce Evans) (09/18/89)

>I am trying to update my minix 1.2 to 1.4a, because I want to use 
>1.44M floppies on a Olivetti M280. I encountered two problems:
>
>1) clustersize problem
>Dosread and doswrite have a constant called maxclustersize, and the 
>value is 2K. My hard disk has a clustersize of 4K, so I tried to 
>change this constant, but I got a lot of errors I didn't understand.

This is a reposting of the dosread posted by Bing Bang in
"<5923@galbp.LBP.HARRIS.COM>	 dosread for 16 bit FAT and hard disks".

It allows a clustersize of 4K in particular.

I fixed some sign extension bugs and replaced the hard-coded constants for
floppies by values read from the boot block.

The 1.3 dosread still has a bug with total_clusters being short and not
unsigned short. This stops it working on partitions >= 16M. Fixed here.

I have run this on

1) standard 360K floppies
2) nonstandard 800K floppies (short test only, required temporary driver mod)
3) standard 1200K floppies
4) 12M partition, 12-bit FAT, 4K cluster size
5) 20M partition, 16-bit FAT, 2K cluster size

It should work with 1.44M floppies.

It appears to lack most bugs affecting tos, so might work on ST's.

The interface to the hard disk is different. The program does not know
anything about partition tables. You have to create /dev/dosC for drive C,
etc. On my system, this is a link to /dev/hd1.

There is a gotcha. This partition normally starts at sector 17, and the
Minix wini drivers gratuitously round up partitions to an even sector,
losing the first sector. This is easily fixed by deleting some code from
the drivers (see e.g. my protected mode postings). However, if your Minix
partitions happened to start at an odd boundary, they would become
inaccessible. This may not be much of a problem with partitions other
than hd1; perhaps Minix fdisk avoided creating them. (?) Many people have
been confused by running mkfs on hd1 and trying for one too many blocks.

A related but unsolvable problem is that if there are an odd number of
sectors in a DOS partition, DOS may use the last one, and this version
of dosread will not be able to read it.

The sign extension bugs were mainly related to ANSI C. ANSI "broke" the
promotion of an unsigned char so it becomes an int instead of an unsigned.
So

	(long) ((unsigned char) 0xFF) << 8)

gives 0xFFFFFF00 in ANSI C (or Turbo C) but 0x0000FF00 in old C.

#! /bin/sh
# Contents:  dosread.c
# Wrapped by sys@besplex on Mon Sep 18 18:00:51 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'dosread.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dosread.c'\"
else
echo shar: Extracting \"'dosread.c'\" \(24531 characters\)
sed "s/^X//" >'dosread.c' <<'END_OF_FILE'
X/*
X * dosdir - 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
X#ifdef debug			/* usually avoid stdio, what a nuisance */
X#include <stdio.h>
X#undef EOF			/* one-off stdio redefines it different */
X#endif
X#include <sys/stat.h>
X
X#define DRIVE		"/dev/dosX"
X#define DRIVE_NR	8
X
X#define MAX_CLUSTER_SIZE	4096
X#define MAX_ROOT_ENTRIES	512
X#define FAT_START		512L	/* After bootsector */
X#define ROOTADDR		(FAT_START + 2L * (long) fat_size)
X#define clus_add(cl_no)		((long) (((long) cl_no - 2L) \
X					* (long) cluster_size \
X					+ 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	0xFFFF
X#define MASK		0xFF8
X#define MASK16		0xFFF8
X#define FREE		0x000
X#define BAD		0xFF0
X#define BAD16		0xFFF0
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 get_fat(f, b)		buf_read(FAT_START + f, b, 1)
X
X#define put_fat(f, b)		{ disk_io(WRITE, FAT_START + f, b, 1); \
X				disk_io(WRITE, FAT_START + f + fat_size, b, 1);}
X
X#define put_fat16(f, b)		{ disk_io(WRITE, FAT_START + f, b, 2); \
X				disk_io(WRITE, FAT_START + f + fat_size, b, 2);}
X
X#define get_fat16(f, b)		buf_read(FAT_START + f, b, 2)
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;
Xunsigned char fat_info;
XDIRECTORY root[MAX_ROOT_ENTRIES];
XDIRECTORY save_entry;
Xchar null[MAX_CLUSTER_SIZE], device[] = DRIVE, path[128];
Xlong data_start, mark;
Xunsigned short total_clusters, cluster_size, fat_size,
X      root_entries, sub_entries;
X
XBOOL Rflag, Lflag, Aflag, dos_read, dos_write, dos_dir, fat_16 = 0;
X
Xchar disk_written = 1, buf_buf[1025];
Xlong buf_addr = 0;
X
XDIRECTORY *directory(), *read_cluster();
Xunsigned short free_cluster(), next_cluster();
Xchar *make_name(), *num_out(), *slash(), *brk();
Xlong lseek(), time();
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
Xunsigned c2u2( ucarray )
Xunsigned char *ucarray;
X{
X	return ucarray[0] + (ucarray[1] << 8);	/* parens vital */
X}
X
Xdetermine()
X{
X	struct dosboot {
X		unsigned char	cjump[2];	/* unsigneds avoid bugs */
X		unsigned char	nop;
X		unsigned char	name[8];
X		unsigned char	cbytepers[2];	/* don't use shorts, etc */
X		unsigned char	secpclus;	/* to avoid struct member */
X		unsigned char	creservsec[2];	/* alignment and byte */
X		unsigned char	fats;		/* order bugs */
X		unsigned char	cdirents[2];
X		unsigned char	ctotsec[2];
X		unsigned char	media;
X		unsigned char	csecpfat[2];
X		unsigned char	csecptrack[2];
X		unsigned char	cheads[2];
X		unsigned char	chiddensec[2];
X		/* char    fill[482]; */
X	}  boot;
X	unsigned short boot_magic;		/* last of boot block */
X	unsigned bytepers, reservsec, dirents, totsec;
X	unsigned secpfat, secptrack, heads, hiddensec;
X	
X	int errcount = 0;
X
X	/* read Bios-Parameterblock */
X	disk_read(0L, &boot, sizeof boot);
X	disk_read(0x1FEL, &boot_magic, sizeof boot_magic);
X
X	/* convert some arrays */
X	bytepers = c2u2(boot.cbytepers);
X	reservsec = c2u2(boot.creservsec);
X	dirents = c2u2(boot.cdirents);
X	totsec = c2u2(boot.ctotsec);
X	secpfat = c2u2(boot.csecpfat);
X	secptrack = c2u2(boot.csecptrack);
X	heads = c2u2(boot.cheads);
X	hiddensec = c2u2(boot.chiddensec);
X
X	/* calculate everything before checking for debugging print */
X 	total_clusters = totsec / (boot.secpclus == 0 ? 1 : boot.secpclus);
X	cluster_size = bytepers * boot.secpclus;
X	fat_size = secpfat * bytepers;
X	data_start = (long)bytepers + (long)boot.fats * (long)fat_size
X		   + (long)dirents * 32L;
X	root_entries = dirents;	
X	sub_entries = boot.secpclus * bytepers / 32;
X	if (total_clusters > 4096)
X		fat_16 = 1;
X
X#ifdef debug
X	/* This used to help find foolish sign extensions and op precedences.
X	 * It remains useful for looking at nonstandard formats.
X	 */
X	fprintf(stderr, "OEM = %8.8s\n", boot.name);
X	fprintf(stderr, "Bytes/sector = %u\n", bytepers);
X	fprintf(stderr, "Sectors/cluster = %u\n", boot.secpclus);
X	fprintf(stderr, "Number of Reserved Clusters = %u\n", reservsec);
X	fprintf(stderr, "Number of FAT's = %u\n", boot.fats);
X	fprintf(stderr, "Number of root-directory entries = %u\n", dirents);
X	fprintf(stderr, "Total sectors in logical volume = %u\n", totsec);
X	fprintf(stderr, "Media descriptor = 0x%02x\n", boot.media);
X	fprintf(stderr, "Number of sectors/FAT = %u\n", secpfat);
X	fprintf(stderr, "Sectors/track = %u\n", secptrack);
X	fprintf(stderr, "Number of heads = %u\n", heads);
X	fprintf(stderr, "Number of hidden sectors = %u\n", hiddensec);
X	fprintf(stderr, "Bootblock magic number = 0x%04x\n", boot_magic);
X#endif
X
X	/* safety checking */
X	if (boot_magic != 0xAA55) {
X		print_string(TRUE, "magic != 0xAA55\n");
X		++errcount;
X	}
X	/* check sectors per track instead of inadequate media byte */
X	if (secptrack < 15 &&		/* assume > 15 hard disk & wini OK */
X#ifdef SECT10				/* BIOS modified for 10 sec/track */
X	    secptrack != 10 &&
X#endif
X#ifdef SECT8				/* BIOS modified for 8 sec/track */
X	    secptrack != 8 &&
X#endif
X	    secptrack != 9) {
X		print_string(TRUE, "sectors per track not supported\n");
X		++errcount;
X	}
X	if (boot.secpclus == 0) {
X		print_string(TRUE, "sectors per cluster == 0\n");
X		++errcount;
X	}
X	if (boot.fats != 2 && dos_write) {
X		print_string (TRUE, "fats != 2\n");
X		++errcount;
X	}
X	if (reservsec != 1) {
X		print_string (TRUE, "reserved != 1\n");
X		++errcount;
X	}
X	if (cluster_size > MAX_CLUSTER_SIZE) {
X		print_string (TRUE, "cluster size too big\n");
X		++errcount;
X	}
X	if (errcount != 0) {
X		print_string (TRUE, "Can't handle disk\n");
X		leave (2);
X	}
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 = '0';
X	unsigned char fat_check;
X
X	if (!strcmp(arg_ptr, "dosdir"))
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 dosdir.\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	if ((dev_nr = (0x5f & *argv[index++])) < 'A' || dev_nr > 'Z')
X		usage(argv[0]);
X
X	device[DRIVE_NR] = dev_nr;
X
X	if ((disk = open(device, dos_write ? 2 : 0)) < 0) {
X		print_string(TRUE, "Cannot open %s\n", device);
X		exit(1);
X	}
X
X	disk_read(FAT_START, &fat_info, 1);
X	determine();
X	disk_read(FAT_START + (long) fat_size, &fat_check, sizeof(fat_check));
X
X	if (fat_check != fat_info) {
X		print_string(TRUE, "Disk type in FAT copy differs from disk type in FAT original.\n");
X		leave(1);
X	}
X
X	disk_read(ROOTADDR, root, DIR_SIZE * root_entries);
X
X	if (dos_dir && Lflag) {
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		if (Lflag)
X			print(STD_OUT, "Root directory:\n", 0);
X		list_dir(root, root_entries, FALSE);
X		if (Lflag)
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 && Lflag)
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		if (Lflag)
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
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					if (!mem)
X						mark = ROOTADDR + (long) i * (long) DIR_SIZE;
X					else
X						mark = clus_add(last) + (long) i * (long) DIR_SIZE;
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 (cl_no != LAST_CLUSTER && 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			link_fat(cl_no, LAST_CLUSTER);
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 (cl_no == (fat_16? BAD16: BAD)) {
X			flush();
X			print_string(TRUE, "Reserved cluster value encountered.\n");
X			leave(1);
X		}
X	} while (entry->d_size && cl_no != LAST_CLUSTER);
X
X	if (cl_no != LAST_CLUSTER)
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, "Diskette 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		link_fat(cl_no, LAST_CLUSTER);
X
X	entry->d_size = Aflag ? (size - 1) : size;	/* Strip added ^Z */
X	fill_date(entry);
X	disk_write(mark, entry, DIR_SIZE);
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)
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	long free = 0;
X	long bad = 0;
X
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 BAD16:
X				bad++;
X				break;
X			case BAD:
X				if(!fat_16)
X					bad++;
X		}
X	}
X
X	print_string(FALSE, "Free space: %L bytes.\n", free * (long) cluster_size);
X	if (bad)
X		print_string(FALSE, "Bad sectors: %L bytes.\n", 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);
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	while (cl_index <= total_clusters && next_cluster(cl_index) != FREE)
X		cl_index++;
X
X	if (leave_fl && cl_index > total_clusters) {
X		flush();
X		print_string(TRUE, "Diskette full. File not added.\n");
X		leave(1);
X	}
X
X	return cl_index++;
X}
X
Xlink_fat(cl_1, cl_2)
Xunsigned short cl_1;
Xregister unsigned short cl_2;
X{
X	register unsigned fat_index;
X	unsigned char pad;
X	unsigned pad16;
X
X	if(fat_16) {
X		pad16 = cl_2;
X		put_fat16((long)(cl_1 << 1), &pad16);
X		return;
X	}
X	fat_index = (cl_1 >> 1) * 3 + 1;
X	if (cl_1 & 0x01) {
X		pad = cl_2 >> 4;
X		put_fat((long)(fat_index + 1), &pad);
X		get_fat((long)fat_index, &pad);
X		pad = (pad & 0x0F) | ((cl_2 & 0x0F) << 4);
X		put_fat((long)fat_index, &pad);
X	}
X	else {
X		pad = cl_2;
X		put_fat((long)(fat_index - 1), &pad);
X		get_fat((long)fat_index, &pad);
X		pad = (pad & 0xF0) | (0xf & (cl_2 >> 8));
X		put_fat((long)fat_index, &pad);
X	}
X}
X
Xunsigned short next_cluster(cl_no)
Xregister unsigned short cl_no;
X{
X	register unsigned fat_index;
X	unsigned char pad;
X	unsigned pad16;
X	unsigned mask = MASK16;
X	unsigned bad = BAD16;
X
X	if(!fat_16) {
X		fat_index = (cl_no >> 1) * 3 + 1;
X		get_fat((long)fat_index, &pad);
X		if (cl_no & 0x01) {
X			pad16 = 0x0f & (pad >> 4);
X			get_fat((long)(fat_index + 1), &pad);
X			cl_no = (((short)pad) << 4) | pad16;
X		}
X		else {
X			pad16 = (0x0f & pad) << 8;
X			get_fat((long)(fat_index - 1), &pad);
X			cl_no = (short)pad | pad16;
X		}
X	mask = MASK;
X	bad = BAD;
X	}
X	else {
X		get_fat16((long)(cl_no << 1), &pad16);
X		cl_no = pad16;
X	}
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	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		if(ptr != path)
X			ptr--;
X		if(ptr != path) 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		disk_written = 1;
X		r = write(disk, address, bytes);
X	}
X
X	if (r != bytes)
X		bad();
X}
X
Xbad()
X{
X	flush();
X	perror("I/O error");
X	leave(1);
X}
X
Xbuf_read(seek, b, c)
Xlong seek;
Xregister char *b;
Xint c;
X{
X	if(disk_written || (seek & (~0x3ffL)) != buf_addr) {
X		disk_written = 0;
X		disk_io(READ, buf_addr = seek & (~0x3ffL), buf_buf, 1025);
X	}
X
X	seek &= 0x3ffL;
X	*b++ = buf_buf[seek++];
X	if(c == 2)
X		*b = buf_buf[seek];
X}
END_OF_FILE
if test 24531 -ne `wc -c <'dosread.c'`; then
    echo shar: \"'dosread.c'\" unpacked with wrong size!
fi
# end of 'dosread.c'
fi
echo shar: End of shell archive.
exit 0
-- 
Bruce Evans		evans@ditsyda.oz.au