ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) (09/17/86)
[ The line eating bug is dead, but these are still fun, anyway. ] Am I the first one to write something like this? Hmmm.... Will someone (read: Neil Katin) at Amiga please tell me if I did this right? The DOS docs provided only enough information to allow me to scratch my head a lot. Enjoy! Schwab #################### Apply particle beam 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: # README # Makefile # things.h # fm.c # file.c # misc.c # includes.c # This archive created: Tue Sep 16 21:57:30 1986 # By: Leo 'Bols Ewhac' Schwab () export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'README' then echo shar: "will not over-write existing file 'README'" else cat << \SHAR_EOF > 'README' This little toy is designed to allow you to examine the sector allocation on your disks. Instead of using the DOS to find files, it uses the trackdisk.device and examines the sectors directly, traversing the filesystem "by hand." This is written for the Manx compiler; conversion to Lettuce may or may not be trivial. To make this beastie, first download all the files, then do this: 1> make Simple, isn't it? SYNOPSIS fm [device [file]] DESCRIPTION fm is a utility that allows you to examine the sector allocation of files on your disk. Unallocated sectors are the background color; file control blocks are rendered in color 1; data blocks are color 2; and sectors allocated by other things show up as color 3. Files to be examined are entered in the filename gadget. All filenames must be a full path from the root. When entered, the program will then attempt to find the file and display its allocation on the screen. If an error occurs, the program will inform you. Errors may or may not be fatal. However, the program does clean up after itself rather well. To change devices, enter a new device name in the device gadget. The program will then load the bitmap off the new device and display it. If you wish to examine a different disk in the same device, you *must* hit the device gadget and then press RETURN. Failing to do this causes (non- destructive) problems, since the bitmap sector number is cached. To re-display the current device/file, hit the refresh gadget. This is useful if another task is running concurrently and writing to the disk. Hitting refresh allows you to see the changes made to the disk. -------- Enjoy! _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ ________ ___ \ /___--__ Leo L. Schwab ___ ___ /\ ---##\ ihnp4!ptsfa!well!ewhac / X \_____ | __ _---)) ..or.. / /_\-- -----+==____\ // \ _ well ---\ ___ ( o---+------------------O/ \/ \ dual ----> !unicom!ewhac \ / ___ \_ (`o ) hplabs -/ ("AE-wack") ____ \___/ \_/ Recumbent Bikes: "Work FOR? I don't work FOR The _O_n_l_y Way To Fly! anybody! I'm just having fun." SHAR_EOF fi if test -f 'Makefile' then echo shar: "will not over-write existing file 'Makefile'" else cat << \SHAR_EOF > 'Makefile' # :ts=8 bk=0 # My first Makefile ever. Please excuse any verbosity. # fm: fm.o misc.o file.o ln fm.o misc.o file.o -lc -o fm fm.o: fm.c structs.b cc +istructs.b fm.c misc.o: misc.c structs.b cc +istructs.b misc.c file.o: file.c structs.b cc +istructs.b file.c structs.b: includes.c cc -a +hstructs.b includes.c delete includes.asm SHAR_EOF fi if test -f 'things.h' then echo shar: "will not over-write existing file 'things.h'" else cat << \SHAR_EOF > 'things.h' /* :ts=8 bk=0 * Lotsa defines and a few global externals. */ #define REV 0L #define BLOCKSIZE TD_SECTOR #define NUMBLOCKS (NUMCYLS * NUMHEADS * NUMSECS) #define ROOTBLOCK (NUMBLOCKS / 2) #define BITMAPINDEX 79 #define NUMLONGS (NUMBLOCKS / 32) #define XX 6L #define YY 5L #define XOFF 30L #define YOFF 25L #define BRKOVER (NUMSECS * YY + YOFF) #define SEP 6 #define LABEL_Y (2*NUMSECS*YY+SEP+YOFF+10) #define ever (;;) /* Offsets into interesting fields in disk blocks */ #define SIZE (BLOCKSIZE/4) #define NAME (SIZE-20) #define HASHCHAIN (SIZE-4) #define EXTENSION (SIZE-2) #define TYPE 0 #define HEADER_KEY 1 #define SECONDARY_TYPE (SIZE-1) #define DIR_HASHTAB 6 #define FH_BLOCKLIST (SIZE-51) #define FH_ENDLIST 6 /* Type definitions (coerced from the DOS using DISKED) */ #define T_SHORT 2 #define T_DATA 8 #define T_LIST 16 #define ST_ROOT 1 #define ST_DIR 2 #define ST_FILE -3 extern void *OpenLibrary(), *OpenWindow(), *AllocMem(), *Lock(), *Info(), *CreatePort(), *CreateExtIO(), *GetMsg(), *index(); extern long TextLength(), OpenDevice(), ReadPixel(); SHAR_EOF fi if test -f 'fm.c' then echo shar: "will not over-write existing file 'fm.c'" else cat << \SHAR_EOF > 'fm.c' /* :ts=8 bk=0 * File mapper. Uses trackdisk.device to grab sectors and traverse the * filesystem the hard way to find out what sectors a particular file * occupies. * * Crufted together by Leo Schwab while waiting for an open line on the WELL. * 8608.19 * Finally finished: 8609.16 * I could have done it quicker if I hadn't started working for Wendy's. * * Note: This program is only valid for 3.5" floppy drives. */ #include <exec/types.h> #include <exec/memory.h> #include <intuition/intuition.h> #include <libraries/dos.h> #include <devices/trackdisk.h> #include "things.h" extern struct NewWindow windef; extern struct IntuiText errmsg, ok; extern struct Gadget gad[]; extern char filename[], devname[]; struct Window *win; struct RastPort *rp; struct InfoData *id; struct IOExtTD *diskreq; struct MsgPort *diskport; ULONG diskchangecount, *diskbuffer, bitmap[SIZE]; int bmsect; void *IntuitionBase, *GfxBase, *lok; main (ac, av) char *av[]; { struct IntuiMessage *msg; struct Gadget *ptr; int class; if (ac) { ac--; av++; if (ac) { strcpy (devname, *av); ac--; av++; } else strcpy (devname, "df0:"); if (ac) strcpy (filename, *av); else strcpy (filename, ":"); } openstuff (); drawgrid (); setdev (); /* Process IntuiEvents */ for ever { WaitPort (win -> UserPort); msg = GetMsg (win -> UserPort); class = msg -> Class; ptr = (struct Gadget *) msg -> IAddress; ReplyMsg (msg); if (class == CLOSEWINDOW) break; else if (class == GADGETUP) { if (ptr == gad) { /* New filename */ drawbitmap (); findfile (filename); } else if (ptr == &gad[1]) /* New device */ setdev (); else if (ptr == &gad[2]) { /* Refresh screen */ getbitmap (); drawbitmap (); findfile (filename); } } } closestuff (); } setdev () { int unit; /* * This rather lengthy series of DOS calls is needed to turn DOS * device names into unit numbers that OpenDevice() can deal with. */ if (!(lok = Lock (devname, ACCESS_READ))) die ("Can't obtain lock for specified device."); if (!(id = AllocMem ((long) sizeof (*id), MEMF_CLEAR))) die ("Can't get InfoData memory."); if (!Info (lok, id)) die ("Call to Info() failed."); if (id -> id_DiskType == ID_NO_DISK_PRESENT) die ("No disk in drive."); unit = id -> id_UnitNumber; FreeMem (id, (long) sizeof (*id)); id = NULL; UnLock (lok); lok = NULL; opendisk (unit); getbitmap (); drawbitmap (); } getbitmap () { register int i; MotorOn (); GetSector ((long) ROOTBLOCK); bmsect = diskbuffer [BITMAPINDEX]; GetSector ((long) bmsect); MotorOff (); for (i=0; i<SIZE; i++) bitmap [i] = diskbuffer [i]; } drawbitmap () { register int i, n, l, p; int free = NUMBLOCKS-2; long k, x, y; char buf[80]; SetDrMd (rp, JAM1); SetAPen (rp, 3L); /* Show sectors 0 and 1 (always allocated) */ RectFill (rp, XOFF+1, YOFF+1, XX+XOFF-1, YY+YY+YOFF-1); l = 2; for (i=1; i<=NUMLONGS; i++) { k = bitmap[i]; for (n=0; n<32; n++) { /* Bits progress from low to high order */ if (i<NUMLONGS || n<30) { /* Ignore last two */ /* Perform icky conversion */ x = (l / 22) * XX + XOFF; y = (l % 22) * YY + YOFF; if (y >= BRKOVER) y += SEP; /* * The following incantation basically means, * don't draw sectors that don't need to be * drawn. */ p = ReadPixel (rp, x+1, y+1); if (~k & 1) { free--; if (p != 3) { SetAPen (rp, 3L); RectFill (rp, x+1, y+1, x+XX-1, y+YY-1); } } else if (p) { SetAPen (rp, 0L); RectFill (rp, x+1, y+1, x+XX-1, y+YY-1); } k >>= 1; } l++; /* Increment sector number */ } } /* Do labels */ SetAPen (rp, 1L); SetBPen (rp, 0L); SetDrMd (rp, JAM2); sprintf (buf, "Bitmap on sector %-4d", bmsect); Move (rp, XOFF, LABEL_Y); Text (rp, buf, (long) strlen (buf)); sprintf (buf, "Sectors free: %-4d", free); Move (rp, 250L, LABEL_Y); Text (rp, buf, (long) strlen (buf)); sprintf (buf, "Allocated: %-4d", (int) NUMBLOCKS-free); Move (rp, 450L, LABEL_Y); Text (rp, buf, (long) strlen (buf)); Move (rp, NUMCYLS*XX+XOFF+10, NUMSECS*YY/2+YOFF+2); Text (rp, "Surface 0", 9L); Move (rp, NUMCYLS*XX+XOFF+10, NUMSECS*YY/2+BRKOVER+SEP+2); Text (rp, "Surface 1", 9L); } drawgrid () /* Draw grid and labels so we can see */ { long x, y; SetDrMd (rp, JAM1); SetAPen (rp, 1L); for (x=XOFF; x<=80*XX+XOFF; x += XX) { Move (rp, x, YOFF); Draw (rp, x, YOFF+11*YY); Move (rp, x, BRKOVER+SEP); Draw (rp, x, BRKOVER+SEP+11*YY); } for (y=0; y<=11*YY; y += YY) { Move (rp, XOFF, y+YOFF); Draw (rp, XOFF+80*XX, y+YOFF); Move (rp, XOFF, y+SEP+BRKOVER); Draw (rp, XOFF+80*XX, y+SEP+BRKOVER); } /* Draw map markings */ Move (rp, XOFF+XX/2, YOFF); Draw (rp, XOFF+XX/2, YOFF-3); Move (rp, XOFF+80*XX-XX/2, YOFF); Draw (rp, XOFF+80*XX-XX/2, YOFF-3); Move (rp, XOFF, YOFF+YY/2); Draw (rp, XOFF-3, YOFF+YY/2); Move (rp, XOFF, YOFF+YY*10+YY/2); Draw (rp, XOFF-3, YOFF+YY*10+YY/2); Move (rp, XOFF-1, YOFF-4); Text (rp, "0", 1L); Move (rp, XOFF+79*XX-1, YOFF-4); Text (rp, "79", 2L); Move (rp, XOFF-12, YOFF+6); Text (rp, "0", 1L); Move (rp, XOFF-20, YOFF+11*YY); Text (rp, "10", 2L); } marksector (n, color) long n; int color; { register int x, y; x = (n / 22) * XX + XOFF; y = (n % 22) * YY + YOFF; if (y >= BRKOVER) y += SEP; SetAPen (rp, (long) color); RectFill (rp, x+1L, y+1L, x+XX-1L, y+YY-1L); } openstuff () { if (!(IntuitionBase = OpenLibrary ("intuition.library", REV))) { /* * If we can't open Intuition, then we can't use * AutoRequest () */ printf ("Intuition failed; you'll have to use logic.\n"); closestuff (); exit (100); } if (!(GfxBase = OpenLibrary ("graphics.library", REV))) { printf ("Art shop closed.\n"); closestuff (); exit (100); } if (!(win = OpenWindow (&windef))) { printf ("Window painted shut.\n"); closestuff (); exit (100); } rp = win -> RPort; if (!(diskport = CreatePort (NULL, NULL))) die ("No port."); if (!(diskreq = CreateExtIO (diskport, (long) sizeof (*diskreq)))) die ("Can't make IO block."); if (!(diskbuffer = AllocMem (BLOCKSIZE, MEMF_CLEAR | MEMF_CHIP))) die ("Can't allocate disk buffer."); } opendisk (unit) int unit; { long err; char *buf[80]; /* We may be changing units, so close it if it's open */ if (diskreq -> iotd_Req.io_Device) { CloseDevice (diskreq); diskreq -> iotd_Req.io_Device = NULL; } if (err = OpenDevice (TD_NAME, (long) unit, diskreq, NULL)) { sprintf (buf, "Can't get at disk; err = %ld.", err); die (buf); } diskreq -> iotd_Req.io_Command = TD_CHANGENUM; DoIO (diskreq); diskchangecount = diskreq -> iotd_Req.io_Actual; } closestuff () { if (lok) UnLock (lok); if (diskreq) { /* * Apparently, if OpenDevice() fails, it fills in the * io_Device field with -1. This pretty much blows all * my previous code out of the water, which assumed it got * filled in with 0. Sigh. Why don't they tell us these * things? */ if ((long) diskreq -> iotd_Req.io_Device != -1L) CloseDevice (diskreq); DeleteExtIO (diskreq, (long) sizeof (*diskreq)); } if (diskbuffer) FreeMem (diskbuffer, (long) BLOCKSIZE); if (id) FreeMem (id, (long) sizeof (*id)); if (diskport) DeletePort (diskport); if (win) CloseWindow (win); if (GfxBase) CloseLibrary (GfxBase); if (IntuitionBase) CloseLibrary (IntuitionBase); } notice (str) /* For non-fatal errors */ UBYTE *str; { MotorOff (); errmsg.IText = str; AutoRequest (win, &errmsg, NULL, &ok, NULL, NULL, TextLength (rp, str, (long) strlen (str)) + 40, 46L); } die (str) /* For fatal errors */ UBYTE *str; { errmsg.IText = str; AutoRequest (win, &errmsg, NULL, &ok, NULL, NULL, TextLength (rp, str, (long) strlen (str)) + 40, 46L); closestuff (); exit (100); } /* Guess.... debug (str) char *str; { printf (str); getchar (); } */ SHAR_EOF fi if test -f 'file.c' then echo shar: "will not over-write existing file 'file.c'" else cat << \SHAR_EOF > 'file.c' /* :ts=8 bk=0 * Filesystem routines (or, How To Find A File The Hard Way) */ #include <exec/types.h> #include <devices/trackdisk.h> #include "things.h" extern struct IOExtTD *diskreq; extern ULONG diskchangecount, *diskbuffer; findfile (name) /* Traverse the filesystem the hard way */ char *name; { register int err; register char *b, *e; char str[80]; strcpy (str, name); if (b = index (str, ':')) /* Ignore device names */ b++; else b = str; while (*b == '/') /* Ignore leading slashes */ b++; if (!*b) { /* Don't do anything if string is empty */ notice ("Badly formed filename."); return (0); } e = b; MotorOn (); GetSector (ROOTBLOCK); /* All paths are relative to the root */ while (e) { if (!*b) { notice ("Badly formed filename."); return (0); } if (e = index (b, '/')) *e = '\0'; if ((err = traverse (b, hash (b))) < 0) { switch (err) { case -1: notice ("Badly formed path."); break; case -2: notice ("File not found."); break; } return (0); } if (e) { skip: b = e+1; while (*b == '/') b++; } } drawfile (); MotorOff (); return (1); } traverse (str, hash) /* Indirect through the disk blocks */ char *str; int hash; { register int block; register char *file = (char *) &diskbuffer [NAME]; if (diskbuffer [TYPE] == T_SHORT && diskbuffer [SECONDARY_TYPE] == ST_FILE) /* Can't indirect out of a file */ return (-1); strupper (str); block = diskbuffer [hash]; for ever { if (!block) return (-2); /* No such file */ GetSector ((long) block); /* Force null termination in case BCPL doesn't do it */ file [*file + 1] = '\0'; strupper (file+1); /* Force to all upper case */ if (!strcmp (str, file+1)) /* Is this it? */ break; /* This ain't it; traverse hash chain */ block = diskbuffer [HASHCHAIN]; } return (1); } drawfile () /* Draw file's allocated sectors */ { register int i; /* * File control blocks are marked with pen 1, data blocks with pen 2. */ marksector (diskbuffer [HEADER_KEY], 1); if (diskbuffer [TYPE] == T_SHORT && diskbuffer [SECONDARY_TYPE] == ST_FILE) for ever { for (i=FH_BLOCKLIST; i>=FH_ENDLIST; i--) if (diskbuffer [i]) marksector (diskbuffer [i], 2); if (diskbuffer [EXTENSION]) { marksector (diskbuffer [EXTENSION], 1); GetSector (diskbuffer [EXTENSION]); } else break; } } strupper (str) register char *str; { while (*str) { *str = toupper (*str); str++; } } /* * The following routines were stolen from an RKM example by Bob Peck and * hacked up a bit. */ GetSector (sector) long sector; { LONG offset = sector * BLOCKSIZE; diskreq -> iotd_Req.io_Length = BLOCKSIZE; diskreq -> iotd_Req.io_Data = (APTR) diskbuffer; /* show where to put the data when read */ diskreq -> iotd_Req.io_Command = ETD_READ; /* check that disk not changed before reading */ diskreq -> iotd_Count = diskchangecount; /* convert from cylinder, head, sector to byte-offset value to get * right one (as dos and everyone else sees it)...*/ /* driver reads one CYLINDER at a time (head does not move for * 22 sequential sector reads, or better-put, head doesnt move for * 2 sequential full track reads.) */ diskreq -> iotd_Req.io_Offset = offset; DoIO (diskreq); return (0); } MotorOn() { /* TURN ON DISK MOTOR ... old motor state is returned in io_Actual */ diskreq -> iotd_Req.io_Length = 1; /* this says motor is to be turned on */ diskreq -> iotd_Req.io_Command = TD_MOTOR; /* do something with the motor */ DoIO (diskreq); return (0); } MotorOff() { diskreq -> iotd_Req.io_Length = 0; /* says that motor is to be turned on */ diskreq -> iotd_Req.io_Command = TD_MOTOR; /* do something with the motor */ DoIO (diskreq); return (0); } /* * The following code segment was thrown at the USENET by Neil Katin. * Thanks, Neil. */ hash (str) unsigned char *str; { int res; unsigned char *sp; unsigned c; res = strlen (str); for (sp = str; *sp; sp++) { c = *sp; if (c >= 'a' && c <= 'z') c = c - 'a' + 'A'; res = ((res * 13 + c) & 0x7ff); } return (res % 72 + DIR_HASHTAB); } SHAR_EOF fi if test -f 'misc.c' then echo shar: "will not over-write existing file 'misc.c'" else cat << \SHAR_EOF > 'misc.c' /* :ts=8 bk=0 * Static structure definitions (mostly for Intuition). */ #include <exec/types.h> #include <intuition/intuition.h> #include <devices/trackdisk.h> #include "things.h" /* * IntuiText structures for AutoRequestors */ struct IntuiText ok = { AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, AUTOLEFTEDGE, AUTOTOPEDGE, AUTOITEXTFONT, (UBYTE *) "OK", AUTONEXTTEXT }; struct IntuiText errmsg = { AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, AUTOLEFTEDGE, AUTOTOPEDGE, AUTOITEXTFONT, NULL, /* Gets filled in later */ AUTONEXTTEXT }; /* * Gadgets 'n stuff. */ SHORT filebox[] = { /* Coordinates for box surrounding gadget */ -2, 9, -2, -2, 401, -2, 401, 9, -1, 9, -1, -2, 400, -2, 400, 9 }; SHORT devbox[] = { -2, 9, -2, -2, 41, -2, 41, 9, -1, 9, -1, -2, 40, -2, 40, 9 }; SHORT redrawbox[] = { -2, 10, -2, -1, 65, -1, 65, 10, -1, 10, -1, -1, 64, -1, 64, 10 }; struct Border bord[] = { /* Borders for gadgets */ { 0, 0, 1, 0, JAM1, 8, filebox, NULL }, { 0, 0, 1, 0, JAM1, 8, devbox, NULL }, { 0, 0, 1, 0, JAM1, 8, redrawbox, NULL } }; struct IntuiText rdtext = { /* Text for redraw gadget */ 1, 0, JAM1, 4, 1, NULL, "Refresh", NULL }; struct IntuiText fnametxt = { /* Text for filename gadget */ 1, 0, JAM1, 0, -10, NULL, "Filename", NULL }; struct IntuiText devnamtxt = { /* Text for device name gadget */ 1, 0, JAM1, 0, -10, NULL, "Device", NULL }; /* * Filename gadget special info. */ char filename[80], ufilename[80]; struct StringInfo filenamegad = { filename, ufilename, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; char devname[10], udevname[10]; struct StringInfo devnamegad = { devname, udevname, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* * List of gadgets for window. */ struct Gadget gad[] = { { /* Filename */ &gad[1], XOFF, LABEL_Y+15, 50*8, 10, GADGHCOMP, RELVERIFY, STRGADGET, &bord[0], NULL, &fnametxt, 0, &filenamegad, 0, 0 }, { /* Device */ &gad[2], XOFF+XOFF+50*8+20, LABEL_Y+15, 5*8, 10, GADGHCOMP, RELVERIFY, STRGADGET, &bord[1], NULL, &devnamtxt, 0, &devnamegad, 1, 0 }, { /* Refresh */ NULL, XOFF+NUMCYLS*XX+20, BRKOVER, 64, 10, GADGHCOMP, RELVERIFY, BOOLGADGET, &bord[2], NULL, &rdtext, 0, 0, 2, 0 } }; /* * Window definition */ struct NewWindow windef = { 0, 0, 640, 180, -1, -1, CLOSEWINDOW | GADGETUP, WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE | SMART_REFRESH | ACTIVATE, &gad[0], NULL, (UBYTE *) "File Allocation Map", NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN }; SHAR_EOF fi if test -f 'includes.c' then echo shar: "will not over-write existing file 'includes.c'" else cat << \SHAR_EOF > 'includes.c' #include <exec/types.h> #include <exec/memory.h> #include <intuition/intuition.h> #include <libraries/dos.h> #include <devices/trackdisk.h> #include "things.h" SHAR_EOF fi exit 0 # End of shell archive