PACKET dos fat and dir DEFINES (* Copyright (C) 1985, 86 *) (* Frank Klapper *) open disk, (* 30.05.86 *) close disk, format disk, disk changed, open fetch, next fetch cluster no, open save, next save cluster no, close save, erase table entrys, (*COND TEST dump fat, ENDCOND*) dir all, dir list, dir contains: LET fat row size = 16384, (* 32 KB *) max fat blocks = 25, first fat entry no = 2, last entry of fat chain = 4088, dir entrys per block = 16, max dir entrys = 1600, (* 100 KB *) archive byte = " "; LET FAT = BOUND STRUCT (ALIGN dummy, ROW 256 INT block row, ROW fat row size INT fat row); LET LOCATION = STRUCT (INT msdos block no, block entry no), FILEENTRY = STRUCT (TEXT date and time, REAL size, INT first cluster, LOCATION location), DIRENTRY = INT, FILELIST = STRUCT (THESAURUS thes, ROW max dir entrys FILEENTRY entry, INT no of entrys), DIRLIST = STRUCT (THESAURUS thes, ROW max dir entrys DIRENTRY entry, INT no of entrys), FREELIST = STRUCT (ROW max dir entrys LOCATION stack, INT stacktop, LOCATION begin of free area, end of dir, INT dir chain root), DIR = BOUND STRUCT (FILELIST filelist, DIRLIST dirlist, FREELIST freelist, TEXT disklabel, path); INITFLAG VAR this packet := FALSE; DATASPACE VAR fat space, dir ds, block ds; BOOL VAR dataspaces open; FAT VAR fat struct; ROW max fat blocks BOOL VAR write access; INT VAR first possible available fat entry; DIR VAR dir; CLUSTER VAR block; INT VAR akt file cluster no, first file cluster no; BOOL VAR no cluster saved; TEXT VAR save name; INT VAR count; TEXT VAR convert buffer := " ", name, dir entry; .fat: fat struct.fat row. PROC open disk (TEXT CONST subdir path): disable stop; enable open disk (subdir path); IF is error THEN close action FI END PROC open disk; PROC enable open disk (TEXT CONST subdir path): enable stop; init dataspaces; open fat; open dir. open fat: reset disk attributes; read first fat block; set disk attributes (fat byte (0)); read other fat blocks; define write access table (FALSE); first possible available fat entry := first fat entry no. read first fat block: read fat block (0, FALSE). read other fat blocks: INT VAR block no; FOR block no FROM 1 UPTO number of fat sectors - 1 REP read fat block (block no, FALSE) PER. open dir: init dir struct (subdir path, -1); load main dir blocks; load subdirs if necessary. load main dir blocks: BOOL VAR last block; store end of dir (loc (end of main dir, dir entrys per block - 1)); FOR block no FROM begin of dir UPTO end of main dir REP load dir block (block no, last block); UNTIL last block PER. end of main dir: begin of dir + number of dir sectors - 1. load subdirs if necessary: TEXT VAR path := subdir path; WHILE path <> "" REP load next subdir if possible PER. load next subdir if possible: INT VAR cluster no; get next subdir name; get first cluster no of subdir table; clear dir entrys (cluster no); WHILE cluster no >= 0 REP load subdir entrys of cluster; cluster no := next fetch cluster no UNTIL last block PER. get next subdir name: TEXT VAR subdir name; IF (path SUB 1) <> "\" THEN error stop ("ungültige Pfadbezeichnung") FI; INT VAR backslash pos := pos (path, "\", "\", 2); IF backslash pos = 0 THEN subdir name := subtext (path, 2); path := "" ELSE subdir name := subtext (path, 2, backslash pos - 1); path := subtext (path, backslash pos) FI; subdir name := adapted name (subdir name, TRUE). get first cluster no of subdir table: IF dir thes CONTAINS subdir name THEN open fetch subdir (subdir name, cluster no); ELSE error stop ("Subdirectory existiert nicht") FI. load subdir entrys of cluster: store end of dir (loc (last block no of cluster, dir entrys per block - 1)); FOR block no FROM 0 UPTO (sectors per cluster - 1) REP load dir block (first block no of cluster (cluster no) + block no, last block) UNTIL last block PER. last block no of cluster: first block no of cluster (cluster no) + sectors per cluster - 1. END PROC enable open disk; PROC init dataspaces: enable stop; IF NOT initialized (this packet) THEN dataspaces open := FALSE FI; IF NOT dataspaces open THEN disable stop; dataspaces open := TRUE; fat space := nilspace; dir ds := nilspace; block ds := nilspace; fat struct := fat space; dir := dir ds; block := block ds FI. END PROC init dataspaces; PROC init dir struct (TEXT CONST path string, INT CONST root): clear dir entrys (root); dir.path := path string; dir.disk label := "". END PROC init dir struct; PROC clear dir entrys (INT CONST root): init file list; init dir list; init free list (root). init file list: dir.file list.thes := empty thesaurus; dir.file list.no of entrys := 0. init dir list: dir.dir list.thes := empty thesaurus; dir.dir list.no of entrys := 0. END PROC clear dir entrys; PROC close disk: enable stop; IF NOT initialized (this packet) THEN dataspaces open := FALSE FI; IF dataspaces open THEN forget (dir ds); forget (block ds); forget (fat space); dataspaces open := FALSE FI. END PROC close disk; (*COND FLOPPY PROC format disk: enable stop; init dataspaces; format fat; format dir. format fat: write first four fat bytes; write other fat bytes; define write access table (TRUE); copy fat to disk. write first four fat bytes: fat [1] := word (first fat byte, 255); fat [2] := word (255, 0). write other fat bytes: FOR count FROM 3 UPTO fat length REP fat [count] := 0 PER. fat length: INT VAR len := number of fat entrys + number of fat entrys DIV 2 + number of fat entrys MOD 2; len DIV 2 + len MOD 2. format dir: init dir struct ("", -1); store begin of free area (loc (begin of dir, 0)); store end of dir (loc (end of dir, dir entrys per block - 1)); FOR count FROM 0 UPTO dir entrys per block - 1 REP write text 32 (block, ""0"" + 31 * ""246"", count) PER; disable stop; FOR count FROM begin of dir UPTO end of dir REP write disk block (block ds, count); PER. end of dir: begin of dir + number of dir sectors - 1. END PROC format disk; ENDCOND*) (*COND HDU*) PROC disk clear: error stop ("nicht implementiert") END PROC disk clear; PROC format disk: error stop ("nicht implementiert") END PROC format disk; (*ENDCOND*) INT PROC word (INT CONST low byte, high byte): convert buffer := code (low byte) + code (high byte); convert buffer ISUB 1. END PROC word; BOOL PROC disk changed: (*COND FLOPPY disable stop; NOT first fat block ok COR is error (* must be COR *) ENDCOND*) (*COND HDU*) FALSE (*ENDCOND*) END PROC disk changed; BOOL PROC first fat block ok: enable stop; read fat block (0, TRUE); FOR count FROM 1 UPTO 256 REP compare word PER; TRUE. compare word: IF fat struct.fat row [count] <> fat struct.block row [count] THEN LEAVE first fat block ok WITH FALSE FI. END PROC first fat block ok; PROC open fetch (TEXT CONST name, REAL VAR size, INT VAR first cluster no): enable stop; first cluster no := dir.file list.entry [link index].first cluster; size := dir.file list.entry [link index].size; IF first cluster no >= 4088 THEN first cluster no := -1 FI; akt file cluster no := first cluster no. link index: link (file thes, name). END PROC open fetch; PROC open fetch subdir (TEXT CONST subdir name, INT VAR first cluster no): first cluster no := dir.dir list.entry [link index]; IF first cluster no >= 4088 THEN first cluster no := -1 FI; akt file cluster no := first cluster no. link index: link (dir thes, subdir name). END PROC open fetch subdir; INT PROC next fetch cluster no: enable stop; akt file cluster no := fat entry (akt file cluster no); IF akt file cluster no < 4088 (*ff8h *) THEN akt file cluster no ELSE -1 FI. END PROC next fetch cluster no; PROC open save (TEXT CONST file name): enable stop; save name := file name; IF dir full THEN error stop ("Directory voll") FI; IF dir thes CONTAINS file name THEN error stop ("Subdirectory mit gleichem Namen existiert bereits") FI; IF file thes CONTAINS file name THEN error stop ("Datei mit gleichem Namen existiert bereits") FI; no cluster saved := TRUE. END PROC open save; INT PROC next save cluster no: enable stop; IF no cluster saved THEN akt file cluster no := available fat entry; first file cluster no := akt file cluster no; no cluster saved := FALSE ELSE INT VAR old cluster no := akt file cluster no; akt file cluster no := available fat entry; write fat entry (old cluster no, akt file cluster no) FI; write fat entry (akt file cluster no, last entry of fat chain); akt file cluster no. END PROC next save cluster no; PROC close save (REAL CONST size): enable stop; IF no cluster saved THEN insert dir entry (save name, 4088, 0.0) ELSE copy fat to disk; insert dir entry (save name, first file cluster no, size) FI. END PROC close save; PROC erase table entrys (TEXT CONST name): enable stop; INT VAR first file cluster := first cluster; delete dir entry (name); erase fat chain (first file cluster); copy fat to disk. first cluster: dir.file list.entry [link index].first cluster. link index: link (file thes, name). END PROC erase table entrys; INT PROC fat entry (INT CONST entry no): fix bytes; construct value. fix bytes: INT VAR first byte no := entry no + entry no DIV 2. construct value: IF entry no MOD 2 = 0 THEN (right byte MOD 16) * 256 + left byte ELSE right byte * 16 + left byte DIV 16 FI. left byte: fat byte (first byte no). right byte: fat byte (first byte no + 1). END PROC fat entry; INT PROC available fat entry: FOR count FROM first possible available fat entry UPTO number of fat entrys - 1 REP IF is available entry (count) THEN first possible available fat entry := count; LEAVE available fat entry WITH count FI; PER; close action; error stop ("MS-DOS Datentraeger voll"); maxint. END PROC available fat entry; BOOL PROC is available entry (INT CONST entry no): is zero entry. is zero entry: IF entry no MOD 2 = 0 THEN (left byte = 0) CAND ((right byte MOD 16) = 0) ELSE (right byte = 0) CAND ((left byte DIV 16) = 0) FI. left byte: fat byte (first byte no). right byte: fat byte (first byte no + 1). first byte no: entry no + entry no DIV 2. END PROC is available entry; PROC erase fat chain (INT CONST first entry): INT VAR akt entry no := first entry, entry := fat entry (akt entry no); WHILE akt entry no not last chain entry no REP erase akt entry; akt entry no := entry; entry := fat entry (akt entry no) PER; erase akt entry. akt entry no not last chain entry no: (entry < last entry of fat chain) AND (entry > 1). erase akt entry: write fat entry (akt entry no, 0). END PROC erase fat chain; PROC write fat entry (INT CONST entry no, value): fix bytes; remark write access (fat block of first byte); remark write access (fat block of second byte); write value; update first possible available entry. fix bytes: INT VAR first byte no := entry no + entry no DIV 2. fat block of first byte: first byte no DIV 512. fat block of second byte: second byte no DIV 512. write value: IF even entry no THEN write fat byte (first byte no, value MOD 256); write fat byte (second byte no, (right byte DIV 16) * 16 + value DIV 256) ELSE write fat byte (first byte no, (left byte MOD 16) + 16 * (value MOD 16)); write fat byte (second byte no, value DIV 16) FI. even entry no: entry no MOD 2 = 0. second byte no: first byte no + 1. left byte: fat byte (first byte no). right byte: fat byte (second byte no). update first possible available entry: IF value = 0 THEN first possible available fat entry := min (first possible available fat entry, entry no) FI. END PROC write fat entry; INT PROC fat byte (INT CONST no): replace (convert buffer, 1, word); IF even byte no THEN code (convert buffer SUB 1) ELSE code (convert buffer SUB 2) FI. even byte no: no MOD 2 = 0. word: fat [no DIV 2 + 1]. END PROC fat byte; PROC write fat byte (INT CONST byte no, new value): read old word; change byte; write new word. read old word: replace (convert buffer, 1, word). write new word: word := convert buffer ISUB 1. word: fat [byte no DIV 2 + 1]. change byte: replace (convert buffer, byte pos, code (new value)). byte pos: byte no MOD 2 + 1. END PROC write fat byte; PROC copy fat to disk: INT VAR block no; FOR block no FROM 0 UPTO number of fat sectors - 1 REP IF was write access (block no) THEN write fat block (block no) FI PER. END PROC copy fat to disk; PROC write fat block (INT CONST fat block no): INT VAR fat copy no; INT VAR return code; disable stop; FOR fat copy no FROM 0 UPTO number of fat copies -1 REP write disk block (fat space, ds page no, block no, return code); IF return code > 0 THEN close action FI PER; remark no write access (block no); enable stop. ds page no: first non dummy ds page + fat block no + 1. block no: begin of fat (fat copy no) + fat block no. END PROC write fat block; PROC read fat block (INT CONST fat block, BOOL CONST test block): INT VAR fat copy no; disable stop; FOR fat copy no FROM 0 UPTO number of fat copies - 1 REP clear error; read disk block (fat space, ds page no, fat block no) UNTIL NOT is error PER; IF is error THEN close action FI; enable stop. ds page no: IF test block THEN first non dummy ds page ELSE fat block + first non dummy ds page + 1 FI. fat block no: begin of fat (fat copy no) + fat block. END PROC read fat block; PROC define write access table (BOOL CONST status): FOR count FROM 1 UPTO number of fat sectors REP write access [count] := status PER. END PROC define write access table; PROC remark write access (INT CONST fat block no): write access [fat block no + 1] := TRUE END PROC remark write access; PROC remark no write access (INT CONST fat block no): write access [fat block no + 1] := FALSE END PROC remark no write access; BOOL PROC was write access (INT CONST fat block no): write access [fat block no + 1] END PROC was write access; (*COND TEST PROC dump fat: IF NOT exists ("fat dump") THEN open file FI; DATASPACE VAR ds := nilspace; FILE VAR in := sequential file (input, "fat dump"), out := sequential file (output, ds); INT VAR i; TEXT VAR line; FOR i FROM 0 UPTO number of fat entrys - 1 REP dump fat entry PER; forget ("fat dump", quiet); copy (ds, "fat dump"); forget (ds). open file: in := sequential file (output, "fat dump"); FOR i FROM 0 UPTO number of fat entrys - 1 REP putline (in, text (i, 4) + ": ") PER. dump fat entry: cout (i); getline (in, line); putline (out, line + " " + text (fat entry (i), 4)). END PROC dump fat; ENDCOND*) PROC load dir block (INT CONST block no, BOOL VAR last block): last block := FALSE; INT VAR return code; read disk block (block ds, first non dummy ds page, block no, return code); IF return code > 0 THEN close action; io error (return code) FI; INT VAR entry no, thes index; FOR entry no FROM 0 UPTO dir entrys per block - 1 REP dir entry := text 32 (block, entry no); process entry PER. process entry: SELECT pos (""0"."229"", dir entry SUB 1) OF CASE 1: end of dir search CASE 2: main dir entry CASE 3: free entry OTHERWISE file entry END SELECT. end of dir search: last block := TRUE; store begin of free area (loc (block no, entry no)); LEAVE load dir block. main dir entry: (* no operation *). free entry: store in free list (loc (block no, entry no)). file entry: SELECT code (dir entry SUB 12) OF CASE 8: volume label CASE 16: sub dir entry OTHERWISE dos file entry END SELECT. volume label: dir.disk label := text (dir entry, 1, 11). sub dir entry: dir.dir list.no of entrys INCR 1; insert (dir thes, name, thes index); dir list entry := first cluster no. dos file entry: IF dir.file list.no of entrys >= max dir entrys THEN error stop ("Directorytabelle voll") FI; dir.file list.no of entrys INCR 1; insert (file thes, name, thes index); file list entry.first cluster := first cluster no; file list entry.date and time := dos date + " " + dos time; file list entry.size := dos storage; file list entry.location.msdos block no := block no; file list entry.location.block entry no := entry no. name: IF name post <> "" THEN name pre + "." + name post ELSE name pre FI. name pre: compress (subtext (dir entry, 1, 8)). name post: compress (subtext (dir entry, 9, 11)). file list entry: dir.file list.entry [thes index]. dir list entry: dir.dir list.entry [thes index]. first cluster no: code (dir entry SUB 27) + 256 * code (dir entry SUB 28). dos storage: real (code (dir entry SUB 29)) + real (code (dir entry SUB 30)) * 256.0 + real (code (dir entry SUB 31)) * 65536.0 + real (code (dir entry SUB 32)) * 16777216.0. dos date: day + "." + month + "." + year. day: IF code (dir entry SUB 25) MOD 32 < 10 THEN "0" + text (code (dir entry SUB 25) MOD 32) ELSE text (code (dir entry SUB 25) MOD 32) FI. month: INT VAR dummy := code (dir entry SUB 25) DIV 32 + 8 * (code (dir entry SUB 26) MOD 2); IF dummy < 10 THEN "0" + text (dummy) ELSE text (dummy) FI. year: text (80 + code (dir entry SUB 26) DIV 2, 2). dos time: hour + ":" + minute. hour: dummy := code (dir entry SUB 24) DIV 8; IF dummy < 10 THEN "0" + text (dummy) ELSE text (dummy) FI. minute: dummy := code (dir entry SUB 23) DIV 32 + 8 * (code (dir entry SUB 24) MOD 8); IF dummy < 10 THEN "0" + text (dummy) ELSE text (dummy) FI. END PROC load dir block; PROC insert dir entry (TEXT CONST name, INT CONST start cluster, REAL CONST used storage): (* name must be a dos name *) LOCATION VAR ins pos := free location; TEXT VAR akt date := date (clock (1)), akt time := time of day (clock (1)); write disk entry; write dir struct entry. write disk entry: INT VAR return code; read disk block (block ds, first non dummy ds page, ins pos.msdos block no, return code); IF return code > 0 THEN close action; io error (return code) FI; prepare name; dir entry := name pre + name post + archive byte + (10 * ""0"") + dos time + dos date + starting cluster + storage; write text 32 (block, dir entry, ins pos.block entry no); write disk block (block ds, first non dummy ds page,ins pos.msdos block no, return code); IF return code > 0 THEN close action; io error (return code) FI. prepare name: TEXT VAR name pre, name post; IF point pos > 0 THEN name pre := subtext (name, 1, point pos - 1); name post := subtext (name, point pos + 1); name pre CAT (8 - LENGTH name pre) * " "; name post CAT (3 - LENGTH name post) * " " ELSE name pre := name + (8 - LENGTH name) * " "; name post := " " FI. point pos: pos (name, "."). dos time: code ((minute MOD 8) * 32) + code (8 * hour + minute DIV 8). hour: int (subtext (akt time, 1, 2)). minute: int (subtext (akt time, 4, 5)). dos date: code (32 * (month MOD 8) + day) + code ((year - 80) * 2 + month DIV 8). day: int (subtext (akt date, 1, 2)). month: int (subtext (akt date, 4, 5)). year: int (subtext (akt date, 7, 8)). starting cluster: code (start cluster MOD 256) + code (start cluster DIV 256). storage: code (int (round (256.0 * frac (used storage / 256.0), 0))) + code (int (round (frac (floor (used storage / 256.0) / 256.0) * 256.0, 0))) + code (int (floor (used storage / 65536.0))) + code (0). (* maximal 16384 K *********************************) write dir struct entry: INT VAR thes link; insert (file thes, name, thes link); file list entry.location := ins pos; file list entry.first cluster := start cluster; file list entry.date and time := akt date + " " + akt time; file list entry.size := used storage. file list entry: dir.filelist.entry [thes link]. END PROC insert dir entry; PROC delete dir entry (TEXT CONST name): LOCATION VAR del pos; get del pos; erase dir struct entry; erase disk entry; store in free list (del pos). get del pos: del pos := dir.filelist.entry [link index].location. link index: link (file thes, name). erase dir struct entry: INT VAR i; delete (file thes, name, i). erase disk entry: INT VAR return code; read disk block (block ds, first non dummy ds page, del pos.msdos block no, return code); IF return code > 0 THEN close action; io error (return code) FI; dir entry := text 32 (block, del pos.block entry no); replace (dir entry, 1, ""229""); write text 32 (block, dir entry, del pos.block entry no); write disk block (block ds, first non dummy ds page, del pos.msdos block no, return code); IF return code > 0 THEN close action; io error (return code) FI. END PROC delete dir entry; . file thes: dir.filelist.thes. dir thes: dir.dir list.thes. (*********************** dir information ******************************) THESAURUS PROC dir all: file thes. END PROC dir all; BOOL PROC dir contains (TEXT CONST name): file thes CONTAINS name END PROC dir contains; PROC dir list (DATASPACE VAR ds): enable stop; open list file; list files; list dirs; write list head. open list file: forget (ds); ds := nilspace; FILE VAR list file := sequential file (output, ds); putline (list file, ""). list files: INT VAR number := 0; get (file thes, name, number); WHILE number > 0 REP generate file list line; get (file thes, name, number) PER. generate file list line: write (list file, centered name); write (list file, " "); write (list file, text (act file entry.size, 11, 0)); write (list file, " Bytes belegt "); write (list file, act file entry.date and time); (*COND TEST write (list file, " +++ "); write (list file, text (act file entry.first cluster)); ENDCOND*) line (list file). list dirs: number := 0; get (dir thes, name, number); WHILE number > 0 REP generate dir list line; get (dir thes, name, number) PER. generate dir list line: write (list file, centered name); write (list file, "