LBSOS KRNLI/O ERRORFILE 'SOS.KERNEL' NOT FOUNDINVALID KERNEL FILExةw,@  4  J  ȱ⩤i8#) ) 8Le0 WAP /// SIG MENU.MAKER PROGRAM (v. 6.2) =".D1"210: Coldstart (320: Warmstart &*X=11000: TEXT SLOW-DOWN LOOP ,X.1 CHANGE DISK SUBROUTINE23œ202:2200<RFa$=" YOU MAY SELECT YOUR DISK BY  z06&'MLMUTILPB1!BPUFFIN3B4!B4-READ.ME.FIRSTO O"SEG.T0505III.BLM.020698Bu' &AC3CMD.B2!B +AC3CMD.FILEB2!t %SEG.T j05Ÿ/ %EZDIFZ+B1!B$MAC3py8B!B*MENU.MAKER m#im#iЛ#Lȱ  6L憦  Lsmm l y` @8(Je稽 ʈVOLUME NAME (/DISKNAME) OR DEVICE NAME (.Dx)"P12);::"80C";a$;:Zb$="CHANGING DISKS"$d=23:=0::"80C";b$;::12).n=12:=20:"MAKE A NEW MENU FOR DISK: ";N$xN$)<2110=N$ :210 I=1L(A$(I),A$))200B$ ";"ANY KEY RETURNS TO THE MENU.">G$:::320H: Error Routine 202:U=11:"79C";"BAD PATH ERROR (NO DISK IN DISK DRIVE OR DESIRED FILE NOT FOUND.)"X=11000:X:::210Z a$="{,|,~,}; selects; back 1 level;1600 &:WW=1:0 :SEG=1;".D1/S EG.F" SEG=1".D1/SEG.G"diskname$=3802  CATCH PASCAL TEXT FILES 202 :F*=08:"78C";"SORRY BUT MENU.MAKER CAN'T READ PASCAL TEXT FILES."04=10:"78C"NOVEMBER":1750M$="DECEMBER":1750826);"-";M$;" ";Ѡ,2));", ";"19";Р,2);" ";/П,2))=>13П,2))-12;џ,6);:1780$П,2))=0"12";џ,6);:ٟ;$П,2))=>12" PM-":" AM-" 1830WW=1530 =26:=21 0,1670,1680,1690,1700,1710,1720,1730,1740^M$="JANUARY":1750hM$="FEBRUARY":1750rM$="MARCH":1750|M$="APRIL":1750M$="MAY":1750M$="JUNE":1750M$="JULY":1750M$="AUGUST":1750M$="SEPTEMBER":1750M$="OCTOBER":1750M$=")2070H540R\A$="RUNNING "+B$(I),16,B)f"79C";A$;:=0pB$(I),16,B) z::SEG=1".D1/SEG.T"t=+B$(I),16,B) yCT=CT+1~240:=24:=0:"@ ..... "DATE.TIME.LINE" ....JM=Ҡ,4,2))BTM1630,1640,1650,166=+IBOTM/2-.5):I=IBOTM:I/2=I/2)I=I-1 œ2120B=B$(I),16)," ")-1 B$(I),"BASIC 0")850B$(I),"TEXT 0")890 B$(I),"CAT 0")1140*B$(I),"FONT 0")18504B$(I),"FOTO 0")1930>B$(I),"PASTXT 0I);v:520: 500THPOS=4:I/2=I/2)I=I-1I=IBOTM THPOS=44:I/2<>I/2)I=I+1I2=-1:I=I-2:IBOTM<30THPOS=44I=IBOTM/2)*2:=+IBOTM/2)-1:0"PRINT.ALL": OA+P 3HA=(81+UCA)A=(81+LCA):::: OA+Q Quits 3IA=(83+LCA)A=(83+UCA)"PRINT.SHOW": OA+S 2JA=(68+LCA)A=(68+UCA)/Screen.Savers/HELLON=THPOS:B$(I);XA<8A>11540bA-7640,660,690,720l:=THPOS:B$(00 =Q:WW=0A=:A=21A=9&oldprefix$=40A=31410: Control C "aborts" program to Basic(:A=13770: Return Selects a file *DA=27:50: Escape to change disks/FA=324000: back out one directory level 3GA=(80+UCA)A=(80+LCA)SIC 0":150A$="TEXT 0":150A$="CAT 0":150A$="FONT 0":150A$="FOTO 0":150A$(L),"BLOCKS")510*=27:=19:"FREE MEMORY AVAILABLE: ";=7:=20:"80C";A$(L);$:=5:THPOS=4:I=1:IBOTM=J-1:620Q=:=26:=21:16; +Q Quits."r12);::"80C";a$;:+w#9,"DISKNAME.DAT":#9;DISKNAME$:#9|d$=DISKNAME$$=23:=0::"80C";d$;::12)201M=3:=14:"This /// SIG Disk is \^ 19";Р,2)", Washington Apple `, Ltd."=4:B$(1)="":B$(2)=""A$="BA16,B) THEN 240 #1, d$="":=10:"80C";d$ ž#1300I=0"I=I+1:#1;A$(I):290,#1 6L=I-1@j=1:same=0 J:SEG=0 Tœ2030^CT<1CT=1:CT>13000Zha$="{,|,~,}; selects; to new disk; J/2)=4:=+1:ۙ=44B$(J);:J=J+1I:1,180,22:2,280,21:2,2380,23:8A$(1000),B$(1000),C%(511),C$(20),name$(20):=10:=0UCA=128:LCA=UCA+32CT=15 IF PREFIX$= PREFIX$+MID$(B$(I), ?@ABCDEFGHIJKLMNOPQRSTUVWX  ALLAN M. BLOOM, PhD CDP 2303 San Marcos Street, Blacksburg, Virginia 24060 Office: (703) 231-7921 Home: (703) 951-2025 INSIDE APPLE /// MAIL LIST MANAGER 79C";"PRESS ANY KEY TO HALT LISTING"::202 1020#2,B$(I),16,B)ž#242:::1160Z=1#2;A$:"78A";A$Z=Z+1:Z>1842:::Z=1980*:=23:=0::"79C";"CONTINUE...?":1C$:C$<>"Y"C$<>"y"C$<>"N"C$<>"n"10 MENU.MAKER TEXT MODULESEG=0"MENU.MAKER"890&*X=11000: TEXT SLOW-DOWN LOOP ,X.1,180,22:2,280,21:2,2380,23:z:A$="LISTING "+B$(I),16,B)$=01:=0::"80C";A$;::12)>=23:=0::" MLM File Structure Apple's "Mail List Manager" Version 1.0 is a diskette-oriented data base. An MLM file takes up an entire standard diskette with its two files: MLMGR.INDX and MLMGR.DATA. The INDX file consists of 31 blocks (of 512 bytes each) of data. The first block is a "header" record, consisting of file-related information. The second block onward is the actual "index" to the data records in the DATA file, with 32 sixteen-byte index entries per blreas SEEK calls the first record of a file "0."To read the whole file, go MLMUTIL September 6, 1983 Page 2 Bloom Mail List Manager through the cycle fl have to cope). Having gotten (GETted?) an index entry, you know to which of the DATA file records it refers. SEEK that record number minus one and GET it. The "minus one" crops up because the index entry pointers range from "1" to "960," whegins with closing it after having read the header block, then re-opening it to read the subsequent 16-byte records. A "seek" to record number 32 then sets you up to "get" the first index entry. (This is all PASCAL nomenclature. You Business BASIC types wiltforward. The INDX file header record must be read as a single 512K block, and it should probably be kept in memory (if for no other reason than to know how many records the file contains). The least convoluted way of accessing the rest of the INDX file be the first "n + m" records of the DATA file. Without the INDX file pointers, there is no way to tell which "n" DATA records are active. Figure 1 diagrams the situation. Accessing the three record types of an MLM file is a bit less than straighile simply no longer points to it. The DATA space on the disk is noted as available for future additions. If a mailing list file has had "m" deletions posted to it, without adding any new records, the "n" active records are distributed throughd by the most recent MLM sort request. Each entry points to a DATA file record, from 1 to 960. Only those DATA records pointed to by an INDX entry are active. When a mailing list record is "deleted," MLM does not actually delete the DATA record. The INDX fnot be accessed meaningfully by themselves. They must be taken in the context of the INDX file entries. A datum in the INDX header record notes the number of active records ("n"). Only the first "n" INDX entries count. Those entries are physically sequenceose differences. Always device-copy an MLM diskette to keep everything properly scrunched. Or write it a single block at a time. Accessing MLM Data Records The individual records of the DATA file caniskette is not seated properly in that drive." Try it. You won't like it. A catalog listing of the copied diskette will show that the "Size" of each file is one block larger than on the original (32 and 241 blocks, respectively). MLM gets indigestion on thcopy facilities of the "System Utilities" program or of PASCAL's "Filer" will copy the MLM files, but MLM will not recognize the result as valid. MLM will balk at a disk that has been file-by-file copied, with the message "Error -- a good mailing record docks on a diskette. SOS overhead takes up the remaining blocks, and a catalog listing of an MLM diskette will show zero available blocks. The MLM diskette is so crammed full, in fact, that it cannot be copied on a file-by-file basis. The file Mail List Manager The DATA file actually holds the MLM data records, with four 128-byte entries per block. The 960 maximum entries take up 240 blocks. Both files, then, consume 271 of a possible 280 block. The more mathematically oriented will note that 30 blocks of 32 entries each totals the 960 that is the maximum number of records that an MLM file can hold. MLMUTIL September 6, 1983 Page 1 Bloom or the number of times specified by the INDX header's record count. There is no "end of file" indication. The sequence of DATA records will be as advertised in the MLM "The file is sorted by " message. If you're not fussy about sequence, you can speed up reading the DATA records rather considerably. The method is not intuitively obvious, but it isn't all that arcane, either. Set up an array of 960 booleans, each initialized to FALSE. Read in all index entries ("record count") sequentially.The lowest value found the last time the file was sorted on its secondary sort field. See the "Sort Field" section. 061-062 DUNNO 2A1 Integer constant, the number -1, $'FFFF'. 063-064 DUNNO 2A2 ven to this particular mailing list. 049-054 S2 HIGH VAL The highest value found the last time the file was sorted on its secondary sort field. See the "Sort Field" section. 055-060 S2 LOW VAL ber 0. 015-016 INT 960 Integer constant, the number 960. Indicates the maximum number of records permitted in the file. 017-048 LIST NAME A length-31 string containing the name that was gi the length of each index entry is 16 bytes. 011-012 REC COUNT Integer denoting the number of active records in the file. Very important quantity. 013-014 DUNNO 1B Integer constant, the numt doesn't seem to matter if you call it "Fred ," so I don't know what purpose it serves. 009-010 DUNNO 1A Integer constant, the number 16. This may or may not indicate that Name Datum Description ------- ----------- ---------------------------------------------------- 001-002 DUNNO 1 It seems to be an integer of no redeeming value. 003-008 MLM ID A length-5 string containing "MLMgr." I modest encouragement, let's look at the index header record, datum by datum. MLMUTIL September 6, 1983 Page 3 Bloom Mail List Manager Bytes Datach of the data names include "dunno," and I for sure dunno what they are. If anyone finds out, I'd appreciate being told. After six months of working with the record description, though, I haven't been burned by not knowing what the dunno's mean. With thatX Header Record The INDX file's header is defined as a packed record type named "indx hdr" in the beginning of the MLMUTIL listing. It holds by far the most unique data items of the three record types. And some of the most inexplicable. A bun It might be best to take a few minutes now and look over the source code for the unit for general familiarization. As I said, we'll be continually going back and forth between it and the following text. The INDr half dozen private ones that could be useful in a broader context than MLM. Indeed, they were extracted from a library unit of PASCAL utilities that I've found to be generally useful. They were placed here to make MLMUTIL a stand-alone unit. trinsic unit named MLMUTIL whose listing is attached. It contains the record descriptions for all three types of MLM records, plus a half dozen public procedures that handle the more onerous data encoding and decoding. Amid the public procedures are anothe "records used" and process the record. When "records used" equals "record count," you're done. The MLMUTIL Intrinsic Unit The following sections revolve about and continually reference the PASCAL in Set the array item corresponding to each entry's DATA record pointer TRUE. Then read the DATA file sequentially, keeping track of record number and records used. If the array entry corresponding to record number is FALSE, bypass the record, else increment Integer constant, the number +1, $'0001'. 065-070 S1 HIGH VAL The highest value found the last time the file was sorted on its primary sort field. See the "sort field" section. 071-076 S1 LOW VAL The lowest value found the last time the file was sorted on its primary sort field. See the "Sort Field" section. 077-078 DUNNO 2B1 Integer constant, the number 0. 079-080 DUNNO 2B2 I NAME NUM Integer indicating which of the up to twelve defined DATA NAME's (see above) is the primary sort field. 301-302 S2 ZIP SORT2 Integer indicating whether the file's secondary sort field is "Z Page 5 Bloom Mail List Manager Bytes Data Name Datum Description ------- ----------- ---------------------------------------------------- 299-300 S1 the primary sort field is on. 297-298 S2 NAME NUM Integer indicating which of the up to twelve defined DATA NAME's (see above) is the secondary sort field. MLMUTIL September 6, 1983 within its label or comment line. 293-294 S2 LINE NUM Integer indicating the label or comment line that the secondary sort field is on. 295-296 S1 LINE NUM Integer indicating the label or comment line that isn't used. 289-290 S2 LINE ENTRY Integer indicating the secondary sort field's position within its label or comment line. 291-292 S1 LINE ENTRY Integer indicating the primary sort field's position used. 112 LINE NUMBER Integer showing which label or comment line this field is on. If LINE NUMBER exceeds LINE COUNT (above), the line is a comment. Zero if this field byte one = chr(0)), and the field is filled with "=" symbols. 110 LINE ENTRY Integer indicating this field's position within its label or comment line. Zero if this field isn't twelve fields is 12 bytes long. 108 DATA NAME A length-11 string containing the name given the label or comment field. If this field isn't used, string length is zero (ximum characters per line," default 30. 096 LINE COUNT "lines printed on physical label," default 4. 097-288 DATA GROUP Array (1..12) of label and comment field data from the "Record Format" MLM screen. Each of thearacters," 1-99, default 30. 092 LBL LINES "Height of Physical Labels in Lines," 1-99, default 5. 093-096 Logical label data from the "Record Format" MLM screen: 094 LINE LENGTH "ma LBL ACROSS "Number of Labels Across Page," 1-4, default 2. 086 LBL SPACING "Characters Between Labels," 0-99, default 4. 088 LBL SKIP "Lines Between Labels," 0-99, default 1. 090 LBL WIDTH "Width of Physical Labels in Chme Datum Description ------- ----------- ---------------------------------------------------- 081-092 Print format fields from the "Print/Display" MLM screen: 082 LBL MARGIN "Left Print Margin," 0-99, default 0. 084 y field, it is -2. Zero implies an unsorted file. MLMUTIL September 6, 1983 Page 4 Bloom Mail List Manager Bytes Data Nanteger indicating the file's sort status. If the file was most recently sorted on the primary sort field, the number becomes -1. If the file was most recently sorted on the secondarIP CODE." If so, the value is 1, else it is 0. 303-304 S1 NAME SORT1 Integer indicating whether the file's primary sort field is "NAME." If so, the value is 1, else it is zero. 305-512 WRK Array (0..12) defined the same as the 16-byte index entries (see below). Apparently used by MLM as work space of some sort. ------- ------------ ------------------------------- 2 right parenthesis 3 dash 4-13 the digits "0" through "9" 14-39 the letters "A" through "Z" The nine characters of a sort string are split into groups of three, onracters in a sort string is limited to the forty mentioned in order to save space by encoding. They are encoded as ASCII 0 through 39 as follows: 0 space 1 left parenthesis 6 Bloom Mail List Manager Procedure LNAME, further, uses another general-use procedure (BLANK1) to strip leading, trailing, and double blanks from a string. The number of permissible chaivate procedure LNAME in SORTSTRG performs this function. It is of more general use than creating MLM sort strings, and you may wish to add it to your PASCAL programming toolkit. MLMUTIL September 6, 1983 Page IL procedure SORTSTRG creates an MLM sort string from an arbitrary character string. If the sort field represents the label/comment entry called NAME, SORTSTRG does its damnedest to find a "last name" in the string and returns it as the sort string. The preeded to upper case, and non-permissible characters are replaced with blanks. Then, finally, a sort field is constructed from that "sort string." A label/comment field value of "R & D, Inc." would become "R D INC" as a sort string. The MLMUT left and right parentheses, hyphens, and spaces. The value that MLM displays for a sort field is created by moving the sort field's value into a nine-character string, truncated or padded to the right with blanks as needed. Then letters are converted as nata structure itself, some background is in order. MLM sorts no more than the first nine characters of a label or comment field (or no more than the first nine characters of "last name" if NAME is a sort field). A sort field contains only letters, numbers,e for actual sorting. As they say in the textbooks, actually sorting the file based on its sort field values is intuitively obvious and left as an exercise for the student. In other words, I haven't the foggiest notion. Before going into the dather ingenuously defined as a three-element array of integer. A dump of a sort field indeed shows three repeating fields, each an integer initialized to $'8000', -32768. That's the only way I've found to encode/decode the field, but it may be inappropriat Sort Field MLM's "Sort Field" came up several times in the index header's record description, and it will arise again in the actual index entries. Now seems as good a time as any to describe it. The "sort field" type in the MLMUTIL unit is rey, and perhaps some of the integer "constants," are needed to handle Free Format records. I have no experience with -- and no interest in -- such things, and they will never be mentioned again herein. The MLMld "x". One might also ask why the WRK array is in the record at all. A lot of great questions can come to mind. The questions of whether "NAME" is the primary sort field and "ZIP CODE" is the secondary sort field are not necessarily flaky. Th--------------------- Some of the "index header" record's entries seem just plain inexplicable. The Sx LINE NUM and Sx LINE ENTRY fields are redundant. Sx NAME NUM points to DATA GROUP "x", which contains the line number and entry-within-line for sort fiee group for each integer in the sort field. For example, the sort string "LINDQUIST" would be converted and grouped as follows: Group 1 Group 2 Group 3 ------- ------- ------- L I N D Q U I S T 25 22 27 17 30 34 22 32 33 A hex dump of the corresponding sort field shows that Group 1 is represented by the integer $'1FCB', or decimal 8,139. OK, readers, you've field is $'11FCB', or 73,765. From there it's easy to go the the encoding algorithm backwards. Subtracting $'8000' (32,768) from 73,765 yields 40,907. Back to the powers-of-forty bit to translate that into sort string characters: 40, the five "digit" hex number whose high "digit" was lopped to yield the MLM sort field. Therefore, if the sort field value is less than $'8000' (32,768), the high "digit" is $'1', else it is $'0'. Since 8,139 is less than 32,768, the real value of the sortft a five "digit" hex number, so the real value of the sort field is either $'11FCB' or $'01FCB'. The difference is 64K (65,536). Back to the ubiquitous $'8000' (decimal 32,768). Remember that $'8000' was added to the calculated $'9FCB' to get ---------------------------------- Welcome back. Did you get it? Something of a bear, yes? For those still stuck, the procedure involves first converting the $'1FCB' to decimal, the result being 8,139. Now remember that encoding le Mail List Manager chance to redeem yourselves here. Can you puzzle out the de-coding procedure? ---------------------------------- Short (?) Break for Puzzle Solving vert that encoding algorithm to decode an MLM sort field into its equivalent sort string. Those of you who gave up on encoding have a MLMUTIL September 6, 1983 Page 7 Bloom lgorithm, taking a nine-character sort string and returning its three-integer sort value. SORTVAL uses the private procedure X TO Y to raise an integer "X" to the integer "Yth" power, perhaps another addition to your PASCAL tool kit. You can also inre, the last three hex "digits" of the sort value. Adding $'9FCB' and $'8000' yields $'11FCB'. Ignoring the high "digit" yields the sort field value. Try it on the other two groups of sort string "LINDQUIST." The MLMUTIL procedure SORTVAL implements this a 27 ------ Sum = 40,907 = $'9FCB' Aha! We've seen that $'FCB' befo("LIN") as the first integer in an MLM sort field: "L" 25 x 40 ** 2 = 40,000 "I" 22 x 40 ** 1 = 880 "N" 27 x 40 ** 0 = ve no interest or ability in the math realm, worry not. MLMUTIL's procedures SORTFLD and SORTVAL handle the encoding and decoding of MLM sort fields for you. The solution is keyed to the numbers 40 and $'8000'. Powers of 40, actually. Let's encode Group 1 Short (?) Break for Puzzle Solving ---------------------------------- Well, did you get it? Give up? Never start? The first group can skip to the next section. The second group should read on. If you' the integer $'1FCB'? Fortunately, it is unique. You can check your answer by applying the same algorithm to the other two groups. "DQU" translates as $'EF12', and "IST" is $'0EA1'. ---------------------------------- as many clues as you need at this point. Each three-character group, with 40 possible values per character, fits into a 16-byte integer field. You might wish to exercise your mind a bit with the following puzzle. What mechanism converts the string "LIN" to907 / 40 ** 2 = 25 remainder 907 25 = "L" 907 / 40 ** 1 = 22 remainder 27 22 = "I" 27 / 40 ** 0 = 0 remainder 27 27 = "N" As an exercise, you might decode the remaining integers of the sort field, $'EF12' and $'0EA1', the rest of the "LINDQUIST" sort string. Procedure SORTFLD in the MLMUTIL library unit implements this algorithm, yielding the nine-character string represented by a three-integer MLM sort field. MLMUTIL value is actually $'095A'. OK, problem solvers, try to find out how "4532" becomes $'095A'. -------------------------------- Shorter Break for Puzzle Solving ----------------------------D Q U I S T Soundex Letter Values 4 0 5 3 2 0 0 2 3 Strip Zeroes 4 5 3 2 2 3 Take First Four Digits 4 5 3 2 A hex dump of the MLM index entry's SOUNDEX field for "LINDQUIST" shows that the Mail List Manager Let's return to our old friend, sort string "LINDQUIST." Soundex encoding of the nine characters yields 4532, as follows: Original String L I N storage contains four nibbles (each of which can hold a number up to decimal 15), there would be no need to further encode. The implementer of MLM obviously disagrees. MLMUTIL September 6, 1983 Page 9 Bloom e have to worry about here is how a four-digit Soundex code is implemented in the MLM record. A reasonable person might say that since a Soundex code is composed of four digits, since none of the digits is greater than "6", and since a word of The MLM Soundex Field Those of you who survived the section on "Sort Fields" deserve a break, and you'll get a bit of one here. Appendix A of the MLM manual gives an excellent explanation of Soundex encoding. All wur data elements in an index entry record, only SOUNDEX has yet to be introduced. The MLMUTIL procedure named SOUNDEX creates a Soundex-encoded integer from an MLM sort string. Those not interested in exactly how that is done may skip the next section. REC COUNT active entries. Searching for a record is a snap, scanning the index entries for a matching sort field value (or a "similar name" match on the primary sort field), and only then actually seeking and decoding the DATA file record. Of the fotries to avoid having to deal with DATA file records as much as possible. Keeping all 960 index entries in memory takes little room (15,360 bytes) and speeds processing quite a bit. Sorting an MLM file involves no more than re-ordering the file's INDX HDR.LM file. 011-016 SORT 2 MLM sort field, the encoded value of the DATA file record's secondary sort field. ------- ----------- ---------------------------------------------------- Mail List Manager uses the index en file record's primary sort field. 009-010 SOUNDEX Integer, the Soundex-encoded value of the DATA file record's primary sort field. Used in "similar name" searches of an M----------------------------------------------- 001-002 REC NUM Integer, range 1 to 960, indicating which DATA file record this index entry points to. 003-008 SORT 1 MLM sort field, the encoded value of the DATAy to the MLM data record file. Each is a sixteen-byte packed record of the type called "indx entry" in the MLMUTIL unit. The record's data elements are as follows: Bytes Data Name Datum Description ------- ----------- ----- September 6, 1983 Page 8 Bloom Mail List Manager The MLM Index Entry Record As I said earlier, the MLM index entries hold the ke---- See, I told you that this would be easier than the "Sort Field" problems. A few of you still didn't get it? You should have looked at the bit representation of $'095A': | | | Storage Word | |----------------------------- -----------------------------| | length of each of those lines. Used to delineate DATA NAME into line-by-line sub-strings. An undefined line has a length of zero, as does a defined line with no datae little diamond in the MLM record format and data entry screens. 107-112 LINE LENGTH Array (1..6) of a sub-range integer (0..63), one for each possible MLM label or comment line, giving the (byte 1) must be held constant at 105. There are no delimiters between line entries. Entries within a line are separated by the delimiter CHR(143), which displays as thscription ------- ----------- ---------------------------------------------------- 001-106 DATA VAL A length-105 string containing all the values of the MLM record's label and comment lines. String length s is the DATA file's record, described as type "data rec" in the MLMUTIL library unit. This is the 128-byte record that actually holds all the mailing record information, and it is structured as follows: Bytes Data Name Datum DeFor those of you who haven't tried it, the "ORD" function is necessary to assign an integer to a sub-range integer (or one sub-range to another); The MLM DATA File Record The last of the three types of MLM recordollowing PASCAL code segment: VARIANT.INTEGER := 0; FOR I := 1 T0 4 DO BEGIN VARIANT.OCTAL [I] := ORD (SOUNDEX.DIGIT [I]); END; MLM.SOUNDEX := VARIANT.INTEGER; Mail List Manager in memory as an integer and as a packed array of five occurrences of an octal number (0..7). Initialize the integer to zero, then assign the Soundex digits to their respective octal ones, as in the fhe SOUNDEX procedure implements MLM's encoding of a Soundex field via the relatively simple expedient of the free union variant, defining the same word MLMUTIL September 6, 1983 Page 10 Bloom it makes a perfectly good way to store four digits, none higher than 6. Without creative thinking like that, where would the fun be in programming? The chair will entertain a motion to slap the paddy of any programmer who pulls a "cute" like that. Ever. T | 4 | 5 | 3 | 2 | | | | | | | | Now doesn't that make eminent sense? All you have to do is look at SOUNDEX as five octal numbers (with a dribble left over) and | | | | | | | Regroup | 0 |0 0 0 |1 0 0 |1 0 1 |0 1 1 |0 1 0 | |----|-----------|--------|------------|-----------|---------| OCTAL | | 0 -------------|--------------| ASCII | 0 0 0 0 | 1 0 0 1 || 0 1 0 1 | 1 0 1 0 | | | || | | High Byte || Low Byte | |-------------- --------------||-------------- --------------| HEX | 0 | 9 || 5 | A | |--------------|--------------||- or delimiters. 113-122 PHONE Array (1..20) of nibbles (value range 0..15) representing the standard MLM field "Phone." Initialized to $'D'. 123-128 LABEL CODE Array (1..6) of CHAR, one for each of the up to six "Label Codes" entered for a record. Initialized to spaces. Uncoded. Contains whatever was keyed into the field. ------- ----------- -------------------racter phone string. Concluding Remarks We've gone through a whole lot of information, of varying degrees of complexity, in this article just to give a skeleton outline of the file and data structures within Apof this phone field would show the first fourteen "digits" as above, with "digits" 15 through 20 containing $'D'. Procedure PHONEVAL reverses the processing in PHONEFLD, taking a ten-byte MLM phone field and translating it into its corresponding twenty-cha00 00 HEX B 3 1 2 C D 4 9 3 A 0 8 0 0 MLMUTIL September 6, 1983 Page 12 Bloom Mail List Manager A hex dump ield from the telephone number "(312) 493-0800." Position 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Number ( 3 1 2 ) 4 9 3 - 0 8 0 0 Encoded 11 03 01 02 12 13 04 09 03 10 00 08 4 "5" - 05 "6" - 06 "7" - 07 "8" - 08 "9" - 09 "-" - 10 "(" - 11 ")" - 12 " " - 13 "X" - 14 "x" - 15 It is a straightforward process. As an example, let's create an MLM phone fains a permissible character, it is transferred to its corresponding position in the phone string. The elements of PHONE are then assigned based on the following conversions: "0" - 00 "1" - 01 "2" - 02 "3" - 03 "4" - 0it snugly into a nibble's integer sub-range of 0..15. MLMUTIL's procedure PHONEFLD returns an MLM phone field from any string. After initializing phone string to 20 spaces, the routine scans up to 20 bytes of the input string. If the byte contField The only encoded field in a data record is PHONE. It is the easiest field to encode and decode. The MLM manual (page 72) states that the only permissible phone field characters are "1234567890Xx-()" and space, a total of sixteen values that fes," the first 13 characters of DATA VAL. The second line is the next 15 characters, "Whosit CA^12345." Everything to the left of the CHR(143) delimiter is "Address," and everything to the right is "Zip Code." The MLM Phone a record's DATA VAL string like "John B. JonesWhosit CA^12345" can be parsed into field values in two steps. The corresponding LINE LENGTH array would have its first two entries non-zero (13 and 15, respectively). So the record's first line is "John B. JonM ------------ --------- ---------- -------- 1 Name 1 1 2 Address 1 2 3 Zip Code 2 2 A dat ] | | [Address^Zip Code ] | | It would have three INDX HDR.DATA GROUP entries defined as follows: Record Field DATA NAME LINE ENTRY LINE NUsed to parse out the individual label and comment fields of a mailing record record. As an example, let's look at a two-line, three-field mailing record formatted as follows: [Name--------------------------------- MLMUTIL September 6, 1983 Page 11 Bloom Mail List Manager The LINE LENGTH array and the DATA VAL string must be uple Computer, Inc's "Mail List Manager" program product. The MLMUTIL intrinsic unit has subroutines to take much of the heartburn out of data encoding and decoding. Why have we bothered? Because knowledge is power. The information imparted here leaves you no longer at the mercy of "Mail List Manager." You can do whatever you want with your data base, not just what MLM allows you to do. And just what might that "whatever" be? Let me give you a few examples. Vanilla MLM requires that all mailing 0,10 | of three elements each. Required groups are for TABLE, | "" | VECTORS, TUPLES and DATA. Each header group contains | TUPLES | three lines. Optional groups may also appear. | 0,8 | | "" tains general information about | TABLE | the spread sheet. The header section of a typical file | 0,1 | is re-created to the left. | "" | | VECTORS | A DIF header always has at least four required groups | ase. If a spread sheet cell contains a formula, the corresponding DIF file entry shows the results of the calculation. A DIF file has three major elements: a header, a body, and a terminator. | | A DIF file's header coniles and shows you how to function in spite of them. First, however, let's see what exactly this DIF thing is. WHAT'S THE DIF? A DIF file contains only data -- numbers and character strings -- from a spread sheet or data b bugs. Unless you are a staunch believer in the Tooth Fairy, you are doubtless as certain as I that EZ bugs will not get fixed. Fortunately, there are ways around these two bugs. This article describes the insect ridden way that EZ handles standard DIF f system to "hang" when trying to create a DIF file for export to another data base or spread sheet. EZ will occasionally give you zeros for numeric cells when importing a DIF file created by another program. Neither condition is desirable. Both are program spread sheet data between, say, Lotus 1-2-3 and an EZ spread sheet or data base. That is not necessarily the case. The EZ-DIF connection is not all that it could be. EZ has two problems with respect to DIF files. EZ will occasionally cause your Apple ///tween disparate data base and spread sheet systems. Most spread sheet and data base programs advertise that they will handle DIF files. /// EZ Pieces (EZ) claims it will import and export DIF files. Via the standard DIF format, you could expect to transfer INTRODUCTION DIF (Data Interchange Format) was invented by the VisiCalc folks back in the dark ages to allow data transfer beY[\]^_`abcdefghijklmnesearch, Virginia Tech, Blacksburg, VA 24061 2303 San Marcos Street, Blacksburg, Virginia 24060 Office: (703) 231-7921 Home: (703) 951-2025 CIS: 76656,1514 BitNet: IRBLOOM @ VTVM1 EZ-DIF /// EZ Pieces and DIF Files ALLAN M. BLOOM, PhD CDP Institutional Rlist information be entered by keyboard. Rather a bore if you already have a machine-readable mailing list, and an affront to the electronic age. I've taken two widely differently formatted mailing list files and automatically converted them to MLM files. | The VECTORS group corresponds to the number of columns | DATA | in the spread sheet (10 in this case), and the TUPLES | 0,0 | group corresponds to the rows (eight in this example). | "" | DIF does not refer to them as ROWS and COLUMNS because | | some programs let you reverse the definitions. SuperCalc produces a file for data interchange that has a similar header format, but it lacks the VECTORS and TUPLES groups. The spread sheet's actual da to "print" (via the "Openapple P" command) your EZ spread sheet file as an ASCII file or as a DIF file. The symptom is chillingly simple. After EZ asks you for the pathname of the output DIF or ASCII file, it asks again. This continues as long as you ente September 8, 1986 Page 2 Bloom 3EZP and DIF SYSTEM FAILURE 006 The dread SYSTEM FAILURE 006 message can occur whenever you tryre of what is contained therein. Now that we all know what a DIF file is, let's see how EZ makes good on its promise to read and write files in DIF format. If you've not yet had the pleasure, I suggest you'll be unpleasantly surprised. EZDIF and destination computers and operating systems don't mean beans. A Lotus 1-2-3 DIF file (MS-DOS) can be shipped by phone lines to an Apple /// or to any "fill in the blank" computer. You can also "look at" a DIF file with any handy text editor to make su another two-line group specifying End Of Data. The first line is "-1,0" and the second line is "EOD." A useful DIF feature is its format. It is an ASCII text file. ASCII text files may generally be freely sent over telephone lines by modem, so the source" The "6.84" and "2.00" numeric entries could have been written in "exponential" (or scientific) notation as "6.84000E+00" and "1.999999E+00." Lotus 1-2-3 is a popular spread sheet that uses scientific notation. At the end of a DIF file is the terminator,t part of the first line of the vector group. The actual value follows on the same line. In the example to the left, the last five items of the tuple are numeric. The second line of the vector group for a numeric entry is always "V. lations should be performed. The "1,0" line specifies | V | this distinction between numbers and numeric labels. | 0, 322 | | V | A true numeric value is denoted by a zero ("0") as the | | firs | | V | The first five values in this example are string data. | 0, 397 | The numeric fields ("003754" and "020402") in this ex- | V | ample are really character strings on which no calcu- | 0, 835 |tring, The | "020402" | first line of its corresponding vector group is "0,1." | 0, 6.84 | The second line is the actual cell value. Surrounding | V | the character string with quotation marks is optional. | 0, 2.00 S header group tells how many | "Agri" | vector groups that each tuple contains. | 1,0 | | "Agronomy" | DIF allows "string" and "numeric" data values. If the | 1,0 | value of a spread sheet cell is a character s 3EZP and DIF | 1,0 | Beginning Of Table. The BOT group is followed by two- | "003754" | line vector groups, one for each column of the parent | 1,0 | spread sheet. The VECTOR tells how many tuples the DIF file's body contains. | 1,0 | | "F84" | Each tuple begins with a "BOT" two-line group, meaning EZDIF September 8, 1986 Page 1 Bloom ta always follow the DIF header's DATA group in the DIF body. | | A DIF file's body is organized by tuples, one for each | -1,0 | row of the parent spread sheet. The TUPLES header group | BOT | r a valid but different pathname. If you press RETURN or ESCAPE in response to the second or subsequent pathname queries, your system hangs up. If you re-enter the same pathname, EZ asks if it OK to over-write the existing file. If you answer "y" for yes, you are told the existing file cannot be erased. If you answer "n" for no, guess what? Your system hangs up. You must re-boot. This sad condition only occurs if your spread sheet has any "null" non-numeric cells. These are non-numeric entries that contain TO MAC 'N BACK Milt Johnson 05/30/1987 Do you have to deal with different computer types and wish that you could get information back and forth among them 3EZP and DIF Only then are you safe in transferring the file to EZ. CONCLUSION EZ cannot generally import or export a DIF file without help. If you have no real need to exchange EZ a DIF file created by either VisiCalc version. If you don't know how a particular program handles numbers, import its DIF file into VisiCalc first. EZDIF September 8, 1986 Page 3 Bloom nted. The simplest way is via VisiCalc. Bare bones Apple /// VisiCalc is blessedly cheap these days. Advanced VisiCalc isn't all that expensive either. Both can import a Lotus 1-2-3 standard DIF file perfectly. EZ can import a VisiCalc file directly or viahundred integers perfectly. The one "real" number will be replaced by a zero. That may not be appropriate. That may be fatal. Can you trust yourself to find that one anomaly in a large spread sheet or data base? The "real number" bug, too, can be circumveshow integer values in algebraic notation, but any number with an imbedded decimal point (a "real" number) is shown in scientific notation. If your spread sheet has a column with several hundred integers and one "real" number, EZ will import those several value with zero in your EZ spread sheet or data base. That is naughty! Let's use Lotus 1-2-3 as an example. 1-2-3 creates a standard DIF file, as befits a product from the firm that bought the DIF Clearing House that sets DIF standards. 1-2-3 DIF files eptable under the published DIF standards. EZ only knows about algebraic notation. EZ, however, does not admit to that particular brand of myopia. It will not crash when it sees a numeric value expressed in scientific form. It will simply replace each suchard allows two formats for numeric values. Let's call them "algebraic" and "scientific" formats. The value 38.35 may be stored in a DIF file as "38.35" in algebraic notation. It may be stored as "3.834999E+01" in scientific notation. Both notations are acc of EZ will import any standard DIF file without a whimper. You need not worry about system crashes or trashed disk directories. You do, however, have to worry about some non-zero numeric values being imported as zeroes. Just some, mind you. The DIF standa diskette containing nothing you're fond of. IMPORTING ZEROES While EZ can mess up your hardware while trying to create a DIF file, importing a DIF file can just mess up your head. Both the spread sheet and data base partsthe directory of the volume to which your DIF or ASCII file is directed. A damaged hard disk directory is much like a collision at sea. It can ruin your whole day. You should always "print" your spread sheet to an ASCII or DIF file on diskette. Preferably ally with every cell defined as numeric. You can always change your mind and put "label" data in a previously defined "numeric" cell. If you miss an empty non-numeric cell, a SYSTEM ERROR 006 will probably be the least of your worries. You may also trash nothing. Numeric entries containing nothing are apparently OK. The fix is as simple as it is mind-numbing. Place at least one blank (via the space bar) in each non-numeric cell that doesn't contain anything. Either that, or set up your spread sheet origin? This is not a difficult task if we are talking about "ASCII text files". There are a few potholes in the road, however, and that is the purpose of this article. The focus will be on transferring files between an Apple /// and Macintosh. The concepts oqrstuvyz{|}~what I want. The solutions have not always been ideal. I am certain that I haven't found all the answers. I think I have, however, made contributions to the problem. I hope my insights help those of you who have been stymied by the vaga required. One approach is to connect each computer to a modem and transmit information over telephone MAC3 Page 2 lines. If both machines are close together (eg. a few feet apart) w Some experimentation with software and file formats may be required on both the sending and receiving end. HARDWARE REQUIRED In order to communicate from one computer to another (we will assume that we have two computers) a method of connecting them isng it to a disk file. The major point here is that what is being transmitted to another machine may differ from word processing program to word processing program. Careful reading of your word processing program's manual may help, but probably not much. programs will generate files that contain another character in place of the carriage return, and may also contain a line feed character and a special character to represent tabs. This is particularly true if the ASCII text file has been created by printies (as they are called on the Macintosh) in either format. Microsoft's Word, on the other hand, defines a "text only" file as one with a carriage return at the end of each paragraph. Although most ASCII text files will follow one of the above rules, somegrams place a carriage return only at the end of each paragraph. AppleWriter /// and Word Juggler (when using its *filename format for saving files) are examples of this latter approach. On the Macintosh side, MacWrite will read and write "text only" filJust where the carriage returns occur is one of the variables. Some programs generate a carriage return at the end of each line. The Pascal Text Editor and Word Juggler (in its normal output mode) are examples on the Apple ///. Other word processing prono internal format such as underlining, bold faced text or tab stops to define multiple spaces on a line. More technically, it contains no CONTROL characters (those with ASCII values between 0 and 31 inclusive) except a carriage return (ASCII value 13). ers together through a serial port on each machine. ASCII TEXT FILES The term "ASCII text file" is often used as if it were a defined standard. While this is generally true, there are variations. In its most common usage, an ASCII text file is one with intosh for publishing in this newsletter. The software used for the communication was Apple's MicroCourier on the Apple /// and Hayes' Smartcom II on the Macintosh. A custom serial cable and standard modem eliminator cable were used to connect the comput it up and 5) some generalizations on the concepts described for other situations. Before we get into specifics, it is worth noting that this article was created on an Apple /// using AppleWriter /// and transferred to MacWrite and Microsoft Word on a Macapply for most computer to computer communications. The major points to be discussed are: 1) what is an ASCII text file and how are they created, 2) what hardware is required, 3) what software is required to make the transfer, 4) transmitting and cleaningries of Access /// command files. More, I hope to hear from those of you who have divined other pieces of the "command file" puzzle. If you've found something else about making Access /// command files, please don't be shy, and please don't be proprietary. This is yet another example of the Apple /// community being left to its own devices. Let's show that those devices are at least adequate for this problem. proprietary. This is yet another example of the Apple /// community beake" signal which tell each of the machines when they are connected and ready to transmit and receive data (Apple /// end pins 4, 5, 6, 8 & 20). The advantage of separate cables is that the second one described can be used for connecting a Macintosh to a wires accomplish several tasks. First, they connect the two machines together by their signal and chassis grounds (Apple /// end pins 1 & 7). Second, they provide paths to receive and transmit data (Apple /// end pins 2 & 3). Third, they provide "handsh 7 Data set ready (handshake) 20 6 Data terminal ready Note that not all lines from the modem eliminator female end are connected via the serial cable to the Macintosh. This is not a problem, because it works! All these ------------- 1 1 Shield ground 2 5 Transmit data 3 9 Receive data 7 3 Signal ground 8 cable connecting the modem eliminator to the Macintosh has the following setup: Modem eliminator end Macintosh end DB-25 male DB-9 male Pin number Pin & signal --------------------ames are given primarily for reference to manuals, if desired. Understanding the terminology is not necessary to accomplish the task of connecting the machines. MAC3 Page 3 The serialClear to send 20 Data terminal ready 6 Data set ready Note that pin 2 on one end connects to pin 3 on the other end, pins 4 and 5 on one end connect to pin 8 on the other end, and pin 6 on one end connects to pin 20 on the other end. The nsend 8 Data carrier detect 6 Data set ready 20 Data terminal ready 7 Signal ground 7 Signal ground 8 Data carrier detect 4 Request to send (and) 5 -- ---------------- 1 Shield ground 1 Shield ground 2 Transmit data 3 Receive data 3 Receive data 2 Transmit data 4 Request to send (and) 5 Clear to The standard Apple modem eliminator cable has the following connections: Apple /// end Serial cable end DB-25 male DB-25 female pin & signal pin number -----------atch in type (eg. DB type connectors), size (9 pin or 25 pin) and gender (male or female). If a Macintosh Plus is being used, it will be necessary to get a converting cable to go from the Plus' 8 pin round connector to the DB-9 connector described below. one were being used) and the other is called a "modem eliminator" or "null modem" cable. The basic connection is viewed as follows: Apple /// <--> Modem Eliminator Cable <--> Serial Cable <--> Macintosh The <--> represents a connection. These must me may connect them together with a cable through their serial ports and transfer data without the use of modems. Actually, two cables are required, although these may be consolidated into one. One cable would normally connect the computer to a modem (if ing left to its own devices. Let's show that those devices are at least adequate for this problem. modem with a DB-25 connector, such as a Hayes modem. If you are wiring a custom cable, you may want to do it as one cable, combining modem eliminator and serial cable into one. The connections are as follows, with the Apple /// end (male DB-25) given first and the Macintosh end (male DB-9) given second: pin 1 to 1, 2 to 9, 3 to 5, 6 to 6, 7 to 3 and 20 to 7. This cable should also work with an Imagewriter printer connected to the Macintosh. The Apple /// end of cable (DB-25 male) connects to the RS-232 red. For example, the title on this article was left at the left margin on the Apple /// and centered on the receiving end. The table of pin connections for the cables needed cleanup on the receiving end. A major cause of cleanup (whether going from App receiving machine and sending another file. It is suggested that you experiment with small files first. It is best to create a few samples so you can see the results on the other end. Once the files have been transmitted, some cleanup is normally requieceiving machine first, then end the transmission on the other machine (most programs will do this for you automatically when the file being transmitted comes to an end). This process may be repeated multiple times by opening another receiving file on theer off on both computers. connect them together and start the telecommunications programs. Get the receiving machine set up for receiving before you begin transmitting on the sending machine. When the transfer is complete, close (save) the file on the rthe proper combination of software variables on each machine. This is not at all unusual when transmitting data between two machines with different software. TRANSMITTING AND CLEANUP Before connecting the machines together with the cables, turn the powgrams have the ability to send a line feed after each carriage return in the file that is being sent. Turn this option off (no LF after CR). As you can probably guess, reading the manuals will be helpful, but some experimentation will be required to get s what is happening. Some programs add a carriage return at the end of each line of text (normally 80 characters); others do not. For those that insert the carriage return at the end of each line, this is often an option that can be turned off. Most proare packages you must tell the program that it is to send MAC3 Page 4 (originate) a "call", or answer (receive) the "call". In others, starting the sending or receiving process definehardware handshake (to my knowledge) is MicroCourier. Set both machines at half duplex (sometimes called local echo) so that you can see what is happening on the screen. One machine must be set up to send the file and the other to receive. In some softw what is know as a software protocol, and most communication programs have it available. One exception is MacTransfer for the Macintosh. This program uses a hardware handshake (no software protocol). The only program on the Apple /// that can handle a ol to let the receiving machine tell the sending machine when it has to wait so that the receiving machine can take time to write information to disk. The receiving machine must then let the sending machine know when it can resume sending data. This isally provide similar functions. The software must be configured for the same speed (baud rate) such as 300 or 1200, number of bits per byte (use 8, if you can) and parity (use none). Most communications programs use what is known as the XON / XOFF protoc the machines are cabled together without a modem, it is necessary to have communications software available to send and receive data and save them on a disk file of the receiving machine. A variety of software is available on each machine, but they generport on the back of the machine (Port C). The Macintosh end (DB-9 male) connects to the Modem port on the Macintosh. If you have a Macintosh Plus, you will need to get a DB-9 to Mini-8 adaptor from you Apple Dealer. COMMUNICATIONS SOFTWARE Even thoughle /// to Macintosh or Macintosh to Apple ///) is the way characters are handled on each end. All characters on the Apple /// screen have a fixed width. A "1" occupies the same amount of space as an "M" or an "X". On the Macintosh, most characters (called fonts) are proportional. This means that you can get more 1's on a line than M's or X's. Even if your printer uses proportional fonts, they may not be in the same proportions as the font to be used on the Macintosh. Tables are the biggest problem in t WAP /// SIG PUBLIC DOMAIN LIBRARY 12022 Parklawn Drive Rockville, MD. 20852 (301)-984-0300 PDS NAME: Best of Bloom: Disk 02 DISK ID#: 3BLM-02 BOOTABLE: floppy diskettes before trying a hard disk. Happy transmitting to your Mac and Back, or whatever machine it may be! rite beverage (unless that is an expensive scotch!) or other remuneration. Not much can go wrong in this process that will damage your computer equipment or computer files. Exercise caution to begin with (make sure the cable is correct) and save files tol need to know information about each machine, but this is normally available in user manuals. In addition to providing them with the cable and connectors (or paying them for same), it is always nice to provide the volunteer with a six pack of their favomong IBM, Radio Shack, Texas Instruments and other computers. The most difficult part for most people will be preparing the cable. Get help from your user group. Almost every group has at least one person that can wire a cable for any purpose. They wil to have a couple of word processors available on the receiving end. The same is true for the communications software. Time to get help from that friend with the large software library or a user group! The same concepts apply to transmitting text files ach machine this will be an advantage. Quirks in one program may not exist in another. You are probably tied to one word processor on your source machine. It may not be easy to convert these files to another word processor. Therefore, it will be helpful of each line. A primary example is a computer program, where word wrap is normally not desirable. GENERALIZING As you can see, some experimentation will be necessary. If you have access to various word processors and telecommunications programs on eafile read by MacWrite in this manner can be saved and then processed by Microsoft Word, if desired. MAC3 Page 5 On the other hand, you may want to have the carriage returns at the end on the Apple /// puts a carriage return at the end of each line, use MacWrite on the Macintosh and tell the program that a carriage return represents a line break. MacWrite will properly handle these and eliminate them so that word wrap will function. A or delete words the line automatically adjust in length). Some word processors provide automatic features (or a find and replace function to delete these carriage returns. In others, you may have to do it manually (one at a time). If your word processorrocessor adds carriage returns to the end of each line, and the receiving word processor can not automatically delete them, then you may not be able to take full advantage of the word processor on the receiving end (primarily word wrap, so that if you add his regard. You may also wish to add emphasis such as bold letters or underlining. Since these are not allowed in an ASCII text file, you will have to eliminate them before transmitting and then add them back to the file on the other end. If your word p Yes It's One More Time for Dr. Bloom, as he weighs in here with a PD disk full of reviews and articles of interest about the Apple ///. -: One Side One: -: BBTEXT :Business Basic Random Text Files in Pascal. -: PUFFIN :Converting Apple II Pascal o handle any data communications process that you can describe. The Access /// command file promised to be the greatest thing since sliced bread and peanut butter. It would allow you to handle any communications service. Just fill in the bla recording files. Version 3.2 also came with the ability to use "command" files. A command file tells Access /// how to configure itself, how to log on to a communications service (CompuServe, your favorite mainframe, whatever), how tno Black Hole and presented us Version 3.2 (interpreter) of Access ///. Version 3.2 offered some neat features -- fast invocation due to its being an interpreter, screen printing via openapple-P to the current "recording file," massively bufferedwx 76656,1514 BitNet: IRBLOOM @ VTVM1 INTRODUCTION A couple years ago, Ray Wilfinger announced himself as the Cuperti ACCESS /// COMMAND FILES ALLAN M. BLOOM, PhD CDP 2303 San Marcos Street, Blacksburg, Virginia 24060 Office: (703) 231-7921 Home: (703) 951-2025 CIS: NOTE That the files in this subdirectory: PUFFIN.TEXT PUFFIN3.TEXT TRANSFER.TEXT Are all Pascal Text files and will need Pascal to read or use them (they are source code files.) 3CMD. -: -: EZDIF :3EZ Pieces and DIF Files. -: MAC3 :Transfering files between the /// and the Macintosh. -: MLMUTIL :Inside Apple /// Mail List Manager. -: PUFFIN3 :Use with Puffin on Side One. -: PUFFIN3 :Use with Puffin on SidPrograms (see appropriate files -: on Side Two). -: UNDERLINE :Underlining in Apple Writer. -: -: On Side Two: -: AC3CMD :Access /// Command Files (Access /// Interp version). -: AC3CMD.FILE :Representative command file as discussed in ACnks of any "script" (computer says this, and you say that) once, name that script, and file it. Invoking that script would let you ever more log in to your favorite communications service without your ever again having to remember your ID or password, or having to remember the steps to go through, or having to otherwise engage your brain. The "command file" is perhaps the least understood, and the least used, of Version 3.2's features. Anyone who has tried to implement anever, expects to deal only with perfection. Access /// can deal with two types of command files -- those that have never been used before and those that haven't changed a whit since they were last used. The Access /// documentation hints at thAccess /// command files. A command file is a program, a set of instructions to the Access /// interpreter. There may be someone who can write a non-trivial program perfectly the first time. I've never met that person. Access ///, how SACRIFICE A VIRGIN There you go again. Thinking impure thoughts. Stop that. I am not talking about a twelve year old West Virginian who can outrun her brothers (Just kidding, Wild 'n Wonderful). I am talking about after entering Access ///'s terminal mode. If you always use one communication service, you can name the file ACCESS.CMD (in Access ///'s directory) to automatically log in to that service whenever you invoke the Access /// interpreter. trail -- what the service says to you and what you type in response. The Access /// command file will automate that process. If you do it right, you can invoke an Access /// command file (openapple-C followed by the command file's pathname)ut. Invoke the Access /// interpreter. Set your recording file to PRINTER. Log on to your favorite communications service manually. During the log-in process, press openapple-P for each separate screen you see. That will give you a printed auditer programs, is a literal beast. If you tell it how to respond to a stimulus, you had best be certain that you get the right stimulus at the right time and that you spell the stimulus correctly. It's easy to get your desired script laid od rate or the parity or whatever. Most problems occur when you attempt to invoke conditional processing -- only do these particular things under these particular circumstances. First off, the Access /// interpreter, like all computactly how the program handles your parameters, but he or she isn't talking these days. The most predictable results are from straight commands. The Access /// interpreter behaves as expected when your command file tells it to set the bau THE BASICS An Access /// command file is a program, but only in a sense. A command file is really a set of parameters used by a program that is already written -- the Access /// interpreter. There may be someone who knows excond, they might get other members of the /// community who have other partial answers to come forth. With enough partial answers, perhaps we will unlock all the secrets of the Access /// command file. Let's start with what I think I know. es since receiving the product in late 1984. I haven't completely failed yet. I've gleaned some insights. I don't pretend to have all the answers. I have two hopes for the following partial answers. First, they might help someone who is stymied. Sedocumentation and the examples are there. Somehow they don't seem to lead to real command files that can be used by real people. Many bright people have stubbed their toes on Access /// command files. I've been working with Access /// command fil Access /// command file has probably lost his or her faith in a deterministic universe. Version 3.2's documentation deals almost exclusively with command files, and the distribution disk contains several example command files. The is quirk -- saying that a command file may not be write protected. It may not be write protected because Access /// puts "hooks" in the file. You can't see them, but they are there. If you make a change to a working command file, the previously set hooks point somewhere else. Access /// gets horribly confused if you change a command file that it has previously hooked. My experience is that Access /// puts hooks not only in the command file, but also in its disk dir the new unused file as the working command file. It didn't work. I couldn't make it work. That's why God invented backups. I erased the new command file and copied my old "unused" command file into Access ///'s directory. It works, but I c digits lower (961-7328 instead of 961-7333). No sweat, right? I just brought my unused command file into Apple Writer and changed all occurrences of "7333" to "7328." I erased the working command file from my Access /// directory, then saved ccess /// command file can be one of those things more often than not. Let me give you two examples. First, my university mainframe added five lines to its dial-up communication service. To take advantage of that improvement, I needed to call five on? Tell me. Tell our colleagues in the Apple /// community. BLACK MAGIC I haven't been in the computing racket this long without having developed a healthy respect for black magic. Some things just don't behave. An Arks. In other circumstances, however, I cannot successfully tell Access /// (via a command file entry) to recognize a "match string" that is not on the first line of a multi-line stimulus. OK, you computer jocks, do you know what is going as many more than two lines, and that the only thing I can count on is the colon. My command file tells Access /// to send my terminal type (VT100) when it sees the colon and to stop looking for the "match string" when it sees a colon. It woc service. The first line identifies the YALE ASCII TERMINAL EMULATION service. The second line asks for my particular terminal type and ends with a colon (:). I know full well that the message is most often garbled, that it is often displayedw the last line. My experience is that your command file should key to a string in the first line of your host's "stimulus" message. I am not on firm ground here. My university's mainframe has a two-line introduction when I request a specifi goes to the "no match" processing. Your host computer service can send more than one line of text. It may welcome you and give you a bunch of alternatives or give you a lead-in message. Your (and the Access /// command file's) response must follo The documentation makes it look simple. You tell Access /// a string to look for, and you tell Access /// when to stop looking. If Access /// sees the "match string" before reaching the "stop looking" character, it does one thing, else itast the first command file hurdle, knowing that you may not re-use a command file, you are still not home free. Your command file must offer a stimulus -- one that Access /// recognizes -- before Access /// will send your desired response.ever ever change a working Access /// command file. I'd re-type it if I had no other choice. As I said, Access /// chokes on the slightest modification to a previously used command file. FIRST COME, FIRST SERVED Once pccess ///. I make all changes to that file, named WHATEVER.UNUSED. When I'm ready to try it as an Access /// command file, I first erase any existing WHATEVER command file, then save the UNUSED file to Access ///'s directory as WHATEVER. I nectory entry. You must present Access /// with a completely virgin command file, one that has never been either used or pointed to. My solution to that problem is having each command file start out life as an Apple Writer file independent of Aan't access the five new telephone numbers. I don't worry about it. The second bit of madness came when I joined CompuServe. When you call a CompuServe access number, you must send a Control-C (ASCII 3) when your modem tells you that you have made connection. The Access /// command file structure allows transmission of that code. I tried every way I know to tell Access /// to send a Control-C (slowly, wait a bit before sending, send it twice) with no luck. IPUFFIN3vB4'  '+PUFFIN.TEXT8B5~h,PUFFIN3.TEXT+TB5ت.-TRANSFER.TEXT$B4lNOTEö ö Host Computer During Log-On @DL You May Now Attempt to Manually Log-On. @!! @!! ----- Exit Command Text File --- @LBSTOPD" found. Send ID. @LBLOG2 76656,1514 @!! ----- Compuserve sends "Password:" @MC @MS1: @ML1LOG3 @MU1: @MT @JPERRS @!! ----- ":" found. Send Password. @LBLOG3 lodge*digest @JPSTOP @LBERRS @DI'007 @WS1 @DI'007 @DL @DL @DL Incorrect Response From Access /// sometimes misses it. @MC @MS1@ @ML1LG1B @MU1@ @MT @JPERRS @!! ----- "@" found. Send connect string. @LBLG1B C 202202 @!! ----- Telenet sends "202 202 CONNECTED" Compuserve sends "User ID:" @MC @MS1: @ML1LOG2 @MU1: @MT @JPERRS @!! ----- "User IRIER" then idle the modem and bug out ATH0 @JPERRS @!! ----- CONNECT detected. Send two CRs. @LBLOG1 @!! ----- Telenet sends TELENET and "Terminal=" @MC @MS1T @ML1LG1A @MU1T @MT @JPERRS @!! ----- "T" found. Send D1. @LBLG1A D1 @!! ----- Telenet sends "@"@!! CompuServe "Telenet" Command File for Hayes Smartmodem 1200 @!! @!! ----- Set Up Mode Menu --------- @V1 @LF @C7 @QT @VN @FD @AT @XR3 @PR3 @FT @CD1 @LD5 @!! ----- Send modem the dial command ATD552-9181 @MC @MS1C @ML1LOG1 @MU1N @MT @!! ----- If "NO CARYou will probably have no better success. Magic does at times prevail. Go with it. Fighting it leads to various unpleasant forms of madness. CONCLUSION One way or the other, I've gotten Access /// command files to do thumb about never re-using a working command file and mostly keying your stimulus strings to the first line of a multiple line transmission from the host computer. A couple work less beautifully, even when my "rules" are followed religiously. net does not involve any control characters. My command file for CompuServe, going through Telenet as an intermediary, works like a charm. I have a half dozen working Access /// command files. A couple work beautifully, given my rules of ver solved the problem, but I got around it. The nearest CompuServe access number is a toll call away. A few months later, Telenet (from which one may access CompuServe) came in with a local phone number. Logging onto CompuServe via Tele finally just left the "transmit a Control-C" code in the command file and -- when the script got to that point -- pressed the key combination myself. I felt a little silly injecting a manual key combination into an "automatic" log-in procedure. I neacknum:=sb[listoffset+(i-1)*contleng]; &list[i].sectnum:=sb[listoffset+(i-1)*contleng+1]; %END; %get_node:=TRUE; $END; !END; ! !FUNCTION eonode(VAR linkindex:linkrange;VAR tslink:link):BOOLEAN; !VAR "emptysector:BOOLEAN; !BEGIN "emptysector:=TRUnge; !BEGIN "IF NOT (readtrksec(dosdir[0].dunitnum,location,sb,ioerror)) THEN get_node:=FALSE #ELSE WITH listdata DO $BEGIN %continuation.tracknum:=sb[contoffset]; %continuation.sectnum:=sb[contoffset+1]; %FOR i:=1 TO maxlink DO %BEGIN &list[i].tr!CONST "contoffset= 1; (* beginning of continuation link *) "contleng = 2; (* length of continuation info *) "listoffset= 12; (* beginning of list of track sector links *) "listleng =244; (* length of list data *) !VAR "sb:sectbuffer; "i:linkraf,name); "ioerror:=ioresult; "(*$I+*) "openfile:=ioerror=0; !END;  !FUNCTION eolist(next:link):BOOLEAN; !BEGIN "WITH next DO #eolist:=((tracknum=0) AND (sectnum=0)); !END; ! !FUNCTION get_node(location:link;VAR listdata:tslist):BOOLEAN; ); !BEGIN "writeln; "writeln('IO ERROR ',ioerror); "writeln('EXITING TRANSFER'); "(*$I-*) #close(pasfile,purge); "(*$I+*) "exit(transfer); !END; ! !FUNCTION openfile(name:pid;VAR f:ffile;VAR ioerror:INTEGER):BOOLEAN; !BEGIN "(*$I-*) "rewrite(!pasfile :ffile; !primpage, !sparepage :pagebuffer; !pagepntr, !sparepntr :pagerange; !relblock :INTEGER; !filetype :pasfilekinds;  fotoflag :BOOLEAN; (* flag for shifts of size 'binaryoffset' *)  !PROCEDURE abortxfer(ioerror:INTEGERPROCEDURE transfer;  TYPE !ffile=FILE;  VAR !dosname :did; !pasname :pid; !suffix :sid; !dirindex :dirrange; !linkindex :linkrange; !nextlink, !nextnode :link; !nextsector :sectbuffer; !currentnode:tslist; !ioerror :INTEGER; $1 $2 O^uE; "WHILE ((linkindex pagesize) THEN primfs[nextnull+1],s[nextnull],lengindex-nextnull-1); %lengindex:=lengindex-1; %nextnull:=scan(lengindex,=null,s); $END; " #moveleft(s,sparepage[sparepntr+1],lengindex); #sparepntr:=sparepntr+lengindex; #endofspare:=FALSE; #primfull:=FALSE; #lagindex:=%IF NOT (s[lengindex] IN [space..tilde,asciicr]) THEN s[lengindex]:=0; $END; # #(* squeeze out the middle null characters *) #null:=chr(0); #lengindex:=sectsize; #nextnull:=scan(lengindex,=null,s); #WHILE (nextnull0) THEN (* note: pagepntr to stop '); )read(keyboard,ch); )IF ch=chr(esc) THEN exit(displaydir) *ELSE BEGIN gotoxy(0,2);write(chr(cleos)); END; (END; &END; ! write(dosdir[0].dnumentries,' files on disk, ',cumsectors,' sectors in use'); $END; !END; $ directory is empty!') #ELSE BEGIN %displayheader; %FOR count:=1 TO dosdir[0].dnumentries DO &BEGIN 'displayentry(dosdir[count]); 'cumsectors:=cumsectors+dosdir[count].sectorcount; 'IF (count MOD maxlines)=0 THEN (BEGIN )write('Type to continTSL link':10); !END; ! !PROCEDURE displaydir; !CONST "cleos=11; ! esc=27; "maxlines=21; !VAR "cumsectors:INTEGER; "count:dirrange; ( !BEGIN ! page(output); "gotoxy(0,1); "cumsectors:=0; "IF dosdir[0].dnumentries=0 THEN writeln('The working $write(sectorcount:9); $writeln(filetsl.tracknum:6,'-',filetsl.sectnum:3); #END; !END; ! !PROCEDURE displayheader; !BEGIN "write('File Name'); "write('Type':((didleng-length('file name'))+7)); "write('Locked':8); "write('Sectors':9); "writeln('rite(name,' ':(didleng-length(name)+1)); $CASE dfkind OF %dftext:write('text':6); %dfinteger:write('int':6); %applesoft:write('soft':6); %binary:write('bnry':6); %unknown:write('unkn':6); %END; $IF locked THEN write('yes':8) %ELSE write('no':8); command(VAR ch:CHAR); !BEGIN "read(keyboard,ch); "WHILE NOT(ch IN ['C','c','D','d','T','t','Q','q']) DO #BEGIN $write(chr(7)); $read(keyboard,ch); #END; ! writeln; !END; ! !PROCEDURE displayentry(de:dosdirentry); !BEGIN "WITH de DO #BEGIN $w'.FOTO';filetype:=fotofile; END; #'D','d':BEGIN suffix:='';filetype:=untyped; END; "END; !END; ! !PROCEDURE printmenu; !CONST "cleoln=29; !BEGIN "gotoxy(0,0); "write(chr(cleoln),'C)atalog, D)isplay, T)ransfer, Q)uit?'); !END; ! !PROCEDURE read"writeln; "write('>> '); ! read(keyboard,ch); "WHILE NOT (ch IN ['t','f','d','T','F','D']) DO " BEGIN write(chr(7));read(keyboard,ch); END; ! writeln(ch); ! CASE ch OF #'T','t':BEGIN suffix:='.TEXT';filetype:=textfile; END; #'F','f':BEGIN suffix:=IN $capitalize(name); $getdosid:=TRUE; ! END; !END; " !PROCEDURE getfiletype(VAR suffix:sid;VAR filetype:pasfilekinds); !BEGIN "writeln; "writeln('Transfer to a:'); "writeln; "writeln('T)ext file, F)oto file, or D)ata (binary) file?'); ! !FUNCTION getdosid(VAR name:did):BOOLEAN; !BEGIN "writeln; "writeln('Enter the name of the DOS file to transfer,'); "writeln('or enter to exit:'); "writeln; "write('>>'); "readln(name); "IF (length(name)=0) THEN getdosid:=FALSE #ELSE BEGeln; "writeln('Enter the name of the Pascal destination file,'); "writeln('or enter to exit:'); "writeln; "write('>>'); "readln(name); "IF (length(name)=0) THEN getpasid:=FALSE #ELSE BEGIN $capitalize(name); $getpasid:=TRUE; ! END; !END;  shiftcase=32; !VAR "index:0..maxbyte; !BEGIN "FOR index:=1 TO length(line) DO #IF line[index] IN [chr(ordsmla)..chr(ordsmlz)] # THEN line[index]:=chr(ord(line[index])-shiftcase); !END; ! !FUNCTION getpasid(VAR name:pid):BOOLEAN; !BEGIN "writteln; #write('>> '); #un:=stoi; #IF NOT (un IN [0,4,5,9..12]) THEN writeln(chr(7)); "UNTIL un IN [0,4,5,9..12]; "unitnum:=un; "get_unit_num:=(un<>0); !END;  !PROCEDURE capitalize(VAR line:STRING); !CONST "ordsmla=97; "ordsmlz=122; *) "mark =maxbyte; (* directory entries which have been deleted are 'marked' 5in (relative) byte zero *) ! maxindex = 7; (* maximum of 7 directory entries in a sector *) ! "space= 32; (* ASCII space *) "tilde=126; (* ASCII tilde *) !TYPE "indexrange=0..maxindex; ! entrybuffer=PACKED ARRAY[1..entrylength] OF byterange; ! !VAR "sectorindex:indexrange; ! entrybase:byterange; "dir_link:link; "dir_sector:sectbuffer; "nextentry:entrybuffer; "entrycount:dirrange; " !FUNCTION eodir(dirli#'t','T':transfer; #END; !UNTIL ch IN ['Q','q'];  END. N !WITH dosdir[0] DO "BEGIN dfkind:=volinfo; dnumentries:=0; dunitnum:=0; END; !page(output); !gotoxy(0,5); !writeln('Welcome to PUFFIN!'); !REPEAT "printmenu; "readcommand(ch); "CASE ch OF #'c','C':catalog; #'d','D':displaydir; k DO $BEGIN %tracknum:=dir_sector[nextlink]; %sectnum:=dir_sector[nextlink+1]; $END; "END; !WITH dosdir[0] DO "BEGIN #dnumentries:=entrycount; #dunitnum:=unitnum; "END; !displaydir;  END; (* catalog *)   (*$IDPTH2.1:TRANSFER.TEXT*)   BEGIx:=0; %WHILE NOT eodirsector(sectorindex,dir_sector,entrybase) DO %BEGIN &moveleft(dir_sector[entrybase],nextentry,entrylength); &entrycount:=entrycount+1; &filldirentry(dosdir[entrycount],nextentry); %END; $END; (*IF...THEN...ELSE *) #WITH dir_lin#sectnum:=firstdirsect; "END; !entrycount:=0; !WHILE NOT eodir(dir_link) DO "BEGIN #IF NOT readtrksec(unitnum,dir_link,dir_sector,ioerror) $THEN BEGIN writeln('ioerror ',ioerror,' reading directory'); /exit(catalog); )END $ELSE BEGIN %sectorinde)*entrylength; $nofile:=(dirsector[entrybase] IN [0,mark]); #END; "eodirsector:=nofile; !END; !  BEGIN (* catalog *) !page(output); !IF NOT getunitnum(unitnum) THEN exit(catalog); !WITH dir_link DO "BEGIN #tracknum:=dirtrack; ) & !FUNCTION eodirsector(VAR index:indexrange; 7VAR dirsector:sectbuffer;VAR entrybase:byterange):BOOLEAN; !VAR "nofile:BOOLEAN; !BEGIN "nofile:=TRUE; "WHILE (nofile AND (index' ',eb[nameoffset+didleng-1]); $(* non_blank=0 if and only if no trailing blanks *) ocked:=FALSE; $FOR j:=0 TO (didleng-1) DO %BEGIN &(* set the high bit low to get true ASCII *) &eb[nameoffset+j]:=eb[nameoffset+j] MOD 128; &(* eliminate any weird characters *) ndoffset]; $IF NOT ((kind MOD lockbit) IN [0,1,2,4]) THEN dfkind:=unknown %ELSE CASE (kind MOD lockbit) OF +0:dfkind:=dftext; +1:dfkind:=dfinteger; +2:dfkind:=applesoft; +4:dfkind:=binary; +END; $IF ((kind DIV lockbit)=1) THEN locked:=TRUE %ELSE le file *) "lockbit =128; (* locked files have the high bit of the file type byte set *) !VAR "j,kind:byterange; ! nonblank:0..didleng; !BEGIN "WITH de DO #BEGIN $filetsl.tracknum:=eb[linkoffset]; $filetsl.sectnum:=eb[linkoffset+1]; $kind:=eb[ki5track-sector list *) "kindoffset = 3; (* relative byte 2 designates the file type of the entry *) "nameoffset = 4; (* relative byte 3 is the beginning of the file name *) "countoffset=34; (* relative byte 33 is the sector count (MOD sectsize) for 5thnk:link):BOOLEAN; !BEGIN "WITH dirlink DO #eodir:=(sectnum=0) AND (tracknum=0); !END;  !PROCEDURE fill_dir_entry(VAR de:dosdirentry;VAR eb:entrybuffer); !CONST "linkoffset = 1; (* relative byte zero for an entry gives the location of its !maxdir =105; (* maximum number of entries in a DOS diskette directory *)  maxlink =122; (* maximum number of entries in a track sector list *) !didleng =30; (* maximum length of a DOS file name *)  pidleng =23; (* maximum length of a Pascal fi }  CONST !{For Apple ][: !cleoln=29; !cleos=11; End For Apple ][} {For Apple ///: } !cleoln=31; !cleos =29; {End For Apple ///} maxunit =12; (* maximum number for a pascal unit *) { }  { Removed options for textfile and fotofile. They cause a major system crash.}  { Strip the high bit of each byte to turn it into something readable. }  { }  { Modified DISPLAYDIR procedure to begin with a clear screen and to pause }  { after displaying the last line of the catalog listing. } ewport)} { } { For convenience, the included file TRANSFER.TEXT is imbedded in this file. }  { The /// does not have the same memory limitations as the ][. }  { ands: } { } { CONST cleoln 29 (clear to end of viewport) --> 31 (clear to end of line) } { cleos 11 (vertical tab) --> 29 (clear to end vi {$list}  (*$V-*) (*$S+*)  PROGRAM puffin3; { From "Call A.P.P.L.E. In Depth, Number 2," All About Pascal } { } { Conversion for Apple /// involves two screen comm '^xLle name *)  sectsize =256; (* size of a DOS sector *) !blocksize=512;  pagesize =1024; (* size of a pascal text page *) !maxbyte =255;   dirtrack =17; (* track number where a DOS directory resides *) !firstdirsect=15; (* first sector of a DOS directory *)   TYPE !byterange =0..maxbyte; !sectrange =0..sectsize; !dirrange =0..maxdir; !linkrange =0..maxlink;  unitrange =0..maxunit; !blockrange=0..blocksize; !pagerange =0..pagesize; ! !sectb; "searchdir:=found; !END; ! !FUNCTION stoi:INTEGER; !VAR "ch:CHAR; ! x:INTEGER; !BEGIN "x:=5; "read(ch); "if ch IN ['0'..'9'] then x := 0; "WHILE ch IN ['0'..'9'] DO #BEGIN $x:=10*x+(ord(ch)-ord('0')); " read(ch); #END; "writeln; "stoi:target:did;VAR index:dirrange):BOOLEAN; !VAR "found:BOOLEAN; !BEGIN "found:=FALSE; "index:=dosdir[0].dnumentries; ! WHILE NOT (found OR (index=0)) DO #BEGIN $found:=target=dosdir[index].name; $index:=index-1; #END; ! IF found THEN index:=index+1"IF NOT (ioerror=0) THEN writetrksec:=FALSE #ELSE BEGIN $moveleft(sb,block[offset+1],sizeof(sectbuffer)); $(*$I-*) $unitwrite(unitnum,block,sizeof(block)); $(*$I+*) $ioerror:=ioresult; $writetrksec:=ioerror=0; #END; !END;  !FUNCTION searchdir( &ELSE offset:=0; %(* now compte blocknum off set from track 0 *) %blocknum:=(blocknum DIV 2)+8*tracknum; #END; (* WITH trksec DO *) "(*$I-*) "unitread(unitnum,block,sizeof(block),blocknum); "(*$I+*) "ioerror:=ioresult; ckbuffer; !BEGIN "(* see comments for 'readtrksec' *) "WITH trksec DO #BEGIN $(* compute half-block corresponding to desired sector *) %IF (sectnum IN [0,15]) THEN blocknum:=sectnum &ELSE blocknum:=15-sectnum; %IF (odd(blocknum)) THEN offset:=256left(block[offset+1],sb,sizeof(sectbuffer)); %readtrksec:=TRUE; #END; (* IF...THEN...ELSE *) !END; !  FUNCTION writetrksec(unitnum:unitrange; 5trksec:link;VAR sb:sectbuffer;VAR ioerror:INTEGER):BOOLEAN;  VAR "blocknum,offset:INTEGER; ! block:blo%blocknum:=(blocknum DIV 2)+8*tracknum; #END; (* WITH trksec DO *) "(*$I-*) "unitread(unitnum,block,sizeof(block),blocknum); "(*$I+*) "ioerror:=ioresult; "IF NOT (ioerror=0) THEN readtrksec:=FALSE #ELSE BEGIN $(* write into sector buffer *) %moveEGIN $(* compute half-block corresponding to desired sector *) %IF (sectnum IN [0,15]) THEN blocknum:=sectnum &ELSE blocknum:=15-sectnum; %IF (odd(blocknum)) THEN offset:=256 &ELSE offset:=0; %(* now compte blocknum off set from track 0 *) rksec:link;VAR sb:sectbuffer;VAR ioerror:INTEGER):BOOLEAN; !(* reads sector number 'trksec.sectnum' from tracknumber 'trksec.tracknum' $on disk drive nunber 'unitnum' *) !VAR ! block:blockbuffer; "blocknum,offset:INTEGER; !BEGIN "WITH trksec DO #BD; +  dosdirectory=ARRAY[dirrange] OF dosdirentry;  VAR !dosdir:dosdirectory; (* current working DOS directory *)  unitnum:unitrange; !ioerror:INTEGER; wk_integer: integer; {09/26/83 AMB} !ch:CHAR;  !FUNCTION readtrksec(unitnum:unitrange; 5t.dftext, .dfinteger, .applesoft, .binary: /(file_tsl:link; (* location of file's track-sector list*) 0locked:BOOLEAN; (* designates whether file is locked *) 0name:did; 0sectorcount:byterange); (* number of diskette sectors allocated *) -ENer,applesoft,binary); ! !(* Pascal format for the information contained in a DOS directory entry *) !dosdirentry=PACKED RECORD CASE dfkind:dosfilekinds OF .volinfo: (* this is volume info *) /(dunitnum:unitrange; 0dnumentries:dirrange); .unknown, tnum:byterange; 'END; !tslist=(* track sector list *) (RECORD )continuation:link; )list:PACKED ARRAY[1..maxlink] OF link; (END; ( !did=STRING[didleng]; !pid=STRING[pidleng]; ! !dosfilekinds= (* DOS file types *) .(volinfo,unknown,dftext,dfinteguffer =PACKED ARRAY[byterange] OF byterange; !blockbuffer=PACKED ARRAY[1..blocksize] OF byterange;  pagebuffer =PACKED ARRAY[1..pagesize] OF byterange; ! !link=PACKED RECORD (* used to designate track/sector combinations *) 'tracknum:byterange; 'sec=x; !END; ! !FUNCTION get_unit_num(VAR unitnum:unitrange):BOOLEAN; !VAR "un:INTEGER; !BEGIN "REPEAT #writeln; #writeln('Place the DOS diskette in the built-in drive and press RETURN'); #writeln('Enter 0 to escape.'); #writeln; #write('>> '); #un:=stoi; #IF NOT (un IN [0,5]) THEN writeln(chr(7)); {09/26/83 AMB Only 0,5 accepted} "UNTIL un IN [0,5]; "unitnum:=un; "get_unit_num:=(un<>0); !END;  !PROCEDURE capitalize(VAR line:STRING); !CONST "ordsmla=97; "ordsmlz=122;  shiftcase=32;x; ! entrybuffer=PACKED ARRAY[1..entrylength] OF byterange; ! !VAR "sectorindex:indexrange; ! entrybase:byterange; "dir_link:link; "dir_sector:sectbuffer; "nextentry:entrybuffer; "entrycount:dirrange; " !FUNCTION eodir(dirlink:link):BOOLEAN; !Byte; (* directory entries which have been deleted are 'marked' 5in (relative) byte zero *) ! maxindex = 7; (* maximum of 7 directory entries in a sector *) ! "space= 32; (* ASCII space *) "tilde=126; (* ASCII tilde *) !TYPE "indexrange=0..maxinde!CONST "nextlink = 1; (* relative byte 1 of directory sector is link to 5next directory sector *) " "zerobase =11; (* first byte of file info in a directory sector *) "entrylength=35; (* DOS directory entries occupy 35 bytes *) "mark =maxbD; ! writeln (dosdir[0].dnumentries,' files on disk, ',cumsectors, )' sectors in use'); %END; "gotoxy (0, 33); "write('Press any key for the PUFFIN menu.'); "read(keyboard,ch); "page(output); "write (chr(cleos)); END; $ !PROCEDURE catalog; tors:=cumsectors+dosdir[count].sectorcount; 'IF (count MOD maxlines)=0 THEN (BEGIN )write('Type to continue, to stop '); )read(keyboard,ch); )IF ch=chr(esc) THEN exit(displaydir) *ELSE BEGIN gotoxy(0,2);write(chr(cleos)); END; (END; &EN83 AMB} %writeln('The working directory is empty!'); %writeln; %writeln('Naughty. Must catalog a disk first.'); %end "ELSE BEGIN %displayheader; %gotoxy(0,2); %FOR count:=1 TO dosdir[0].dnumentries DO &BEGIN 'displayentry(dosdir[count]); 'cumsec"writeln('TSL link':10); !END; ! !PROCEDURE displaydir; !CONST "esc=27; "maxlines=21; !VAR "cumsectors:INTEGER; "count:dirrange; ( !BEGIN ! page(output); "write (chr(cleos)); "cumsectors:=0; "IF dosdir[0].dnumentries=0 THEN begin; {09/26/':8); $write(sectorcount:9); $writeln(filetsl.tracknum:6,'-',filetsl.sectnum:3); #END; !END; ! !PROCEDURE displayheader; !BEGIN "write('File Name'); "write('Type':((didleng-length('file name'))+7)); "write('Locked':8); "write('Sectors':9); GIN $write(name,' ':(didleng-length(name)+1)); $CASE dfkind OF %dftext:write('text':6); %dfinteger:write('int':6); %applesoft:write('soft':6); %binary:write('bnry':6); %unknown:write('unkn':6); %END; $IF locked THEN write('yes':8) %ELSE write('noRE readcommand(VAR ch:CHAR); !BEGIN "read(keyboard,ch); "WHILE NOT(ch IN ['C','c','D','d','T','t','Q','q']) DO #BEGIN $write(chr(7)); $read(keyboard,ch); #END; ! writeln; !END; ! !PROCEDURE displayentry(de:dosdirentry); !BEGIN "WITH de DO #BE"readln(name); "IF (length(name)=0) THEN getdosid:=FALSE #ELSE BEGIN $capitalize(name); $getdosid:=TRUE; ! END; !END; " !PROCEDURE printmenu; !BEGIN "gotoxy(0,0); "write(chr(cleoln),'C)atalog, D)isplay, T)ransfer, Q)uit?'); !END; ! !PROCEDUe := wk_name; &capitalize(name); &getpasid:=TRUE; &END; !END; ! !FUNCTION getdosid(VAR name:did):BOOLEAN; !BEGIN "writeln; "writeln('Enter the name of the DOS file to transfer,'); "writeln('or enter to exit:'); "writeln; "write('>>'); pid; !BEGIN "writeln; "writeln('Enter the name of the output file, default = .D2/', +name); "writeln; "write('>>'); "readln(wk_name); "IF (length(wk_name)=0) then begin; % getpasid:=true; &name := concat ('.D2/', name); &end "ELSE BEGIN &nam !VAR "index:0..maxbyte; !BEGIN "FOR index:=1 TO length(line) DO #IF line[index] IN [chr(ordsmla)..chr(ordsmlz)] # THEN line[index]:=chr(ord(line[index])-shiftcase); !END; ! !FUNCTION getpasid(VAR name:pid):BOOLEAN; {09/26/83 AMB} !var wk_name: EGIN "WITH dirlink DO #eodir:=(sectnum=0) AND (tracknum=0); !END;  !PROCEDURE fill_dir_entry(VAR de:dosdirentry;VAR eb:entrybuffer); !CONST "linkoffset = 1; (* relative byte zero for an entry gives the location of its 5track-sector list *) "kindoffset = 3; (* relative byte 2 designates the file type of the entry *) "nameoffset = 4; (* relative byte 3 is the beginning of the file name *) "countoffset=34; (* relative byte 33 is the sector count (MOD sectsize) for 5the file *) "lockbit =um=0)); !END; ! !FUNCTION get_node(location:link;VAR listdata:tslist):BOOLEAN; !CONST "contoffset= 1; (* beginning of continuation link *) "contleng = 2; (* length of continuation info *) "listoffset= 12; (* beginning of list of track sector linkopenfile(name:pid;VAR f:ffile;VAR ioerror:INTEGER):BOOLEAN; !BEGIN "(*$I-*) "rewrite(f,name); "ioerror:=ioresult; "(*$I+*) "openfile:=ioerror=0; !END;  !FUNCTION eolist(next:link):BOOLEAN; !BEGIN "WITH next DO #eolist:=((tracknum=0) AND (sectn!sparepntr :pagerange; !relblock :INTEGER; ! !PROCEDURE abortxfer(ioerror:INTEGER); !BEGIN "writeln; "writeln('IO ERROR ',ioerror); "writeln('EXITING TRANSFER'); "(*$I-*) #close(pasfile,purge); "(*$I+*) "exit(transfer); !END; ! !FUNCTION :did; !pasname :pid; !dirindex :dirrange; !linkindex :linkrange; !nextlink, !nextnode :link; !nextsector :sectbuffer; !currentnode:tslist; !ioerror :INTEGER; !pasfile :ffile; !primpage, !sparepage :pagebuffer; !pagepntr, "END; !WITH dosdir[0] DO "BEGIN #dnumentries:=entrycount; #dunitnum:=unitnum; "END; !displaydir;  END; (* catalog *)   {For Apple ][ (*$IDPTH2.1:TRANSFER.TEXT*) For Apple ][}  PROCEDURE transfer;  TYPE !ffile=FILE;  VAR !dosname r_sector[entrybase],nextentry,entrylength); &entrycount:=entrycount+1; &filldirentry(dosdir[entrycount],nextentry); %END; $END; (*IF...THEN...ELSE *) #WITH dir_link DO $BEGIN %tracknum:=dir_sector[nextlink]; %sectnum:=dir_sector[nextlink+1]; $END;#IF NOT readtrksec(unitnum,dir_link,dir_sector,ioerror) $THEN BEGIN writeln('ioerror ',ioerror,' reading directory'); /exit(catalog); )END $ELSE BEGIN %sectorindex:=0; %WHILE NOT eodirsector(sectorindex,dir_sector,entrybase) DO %BEGIN &moveleft(dinofile; !END; !  BEGIN (* catalog *) !page(output); !IF NOT get_unit_num(unitnum) THEN exit(catalog); !WITH dir_link DO "BEGIN #tracknum:=dirtrack; #sectnum:=firstdirsect; "END; !entrycount:=0; !WHILE NOT eodir(dir_link) DO "BEGIN trybase:byterange):BOOLEAN; !VAR "nofile:BOOLEAN; !BEGIN "nofile:=TRUE; "WHILE (nofile AND (index' ',eb[nameoffset+didleng-1]); $(* non_blank=0 if and only if no trailing blanks *) $(* initialize the length of 'name' *) $(*$R-*) $name[0]:=chr(didleng-nonblank);0 TO (didleng-1) DO %BEGIN &(* set the high bit low to get true ASCII *) &eb[nameoffset+j]:=eb[nameoffset+j] MOD 128; &(* eliminate any weird characters *) &IF NOT (eb[nameoffset+j] IN [space..tilde]) THEN eb[nameoffset+j]:=space; %END; ind MOD lockbit) IN [0,1,2,4]) THEN dfkind:=unknown %ELSE CASE (kind MOD lockbit) OF +0:dfkind:=dftext; +1:dfkind:=dfinteger; +2:dfkind:=applesoft; +4:dfkind:=binary; +END; $IF ((kind DIV lockbit)=1) THEN locked:=TRUE %ELSE locked:=FALSE; $FOR j:=128; (* locked files have the high bit of the file type byte set *) !VAR "j,kind:byterange; ! nonblank:0..didleng; !BEGIN "WITH de DO #BEGIN $filetsl.tracknum:=eb[linkoffset]; $filetsl.sectnum:=eb[linkoffset+1]; $kind:=eb[kindoffset]; $IF NOT ((ks *) "listleng =244; (* length of list data *) !VAR "sb:sectbuffer; "i:linkrange; !BEGIN "IF NOT (readtrksec(dosdir[0].dunitnum,location,sb,ioerror)) THEN get_node:=FALSE #ELSE WITH listdata DO $BEGIN %continuation.tracknum:=sb[contoffset]; %continuation.sectnum:=sb[contoffset+1]; %FOR i:=1 TO maxlink DO %BEGIN &list[i].tracknum:=sb[listoffset+(i-1)*contleng]; &list[i].sectnum:=sb[listoffset+(i-1)*contleng+1]; %END; %get_node:=TRUE; $END; !END; ! !FUNCTION eonode(VAR linkindex:linkll it needs to be written *) "IF (pagepntr>0) THEN (* note: pagepntr 0 do begin %pasname [wk_integer] := '.'; %wk_integer := pos (' ', pasname); "end; "wk_integer := pos ('..', pasname); "while wk_integer > 0 do begin %delete (pasname, wk_integer, 1); %wk_integer := pos ('.."displayheader; "displayentry(dosdir[dirindex]); "nextnode:=dosdir[dirindex].file_tsl; " "{09/26/83 AMB Set up a default "pasname"} "if length (dosname) > 19 %then pasname := copy (dosname, 1, 19) %else pasname := dosname; "{endif} "wk_integer : "page(output); "IF NOT (getdosid(dosname)) THEN exit(transfer); "WHILE NOT (searchdir(dosname,dirindex)) DO #BEGIN $writeln; $writeln(dosname,' not in current dosdir'); $IF NOT (getdosid(dosname)) THEN exit(transfer);  END;  writeln; ndex]:=s[lengindex] MOD hibit; %if (s[lengindex] = 0) and (not end_of_file) then end_of_file := true; %if end_of_file then s [lengindex] := 0; #END; #moveleft (s, p [pagepntr+1], sectsize); #pagepntr := pagepntr + sectsize; # "end;{stuff} ! "BEGINndex:pagerange; #cr,null:CHAR; #primfull,endofspare:BOOLEAN; #end_of_file: boolean; "BEGIN #end_of_file := false; #(* zero the high bits to get true ASCII *) #FOR lengindex:=0 TO maxbyte DO $BEGIN %(* zero the high bit of s[lengindex] *) %s[lengi!PROCEDURE stuff(VAR s:sectbuffer;VAR p:pagebuffer; VAR pagepntr:pagerange); "CONST #hibit=128; #asciicr=13; (* ASCII carriage return *) #space=32; (* ASCII space *) #tilde=126; (* ASCII tilde *) "VAR #lengindex,nextnull:sectrange; #leadindex,lagie by blocksize upon entry *) "(*$I-*) "pbpntr:=pbpntr-(blockcount*blocksize)+1; "IF (blockwrite(pasfile,pb[pbpntr],blockcount,relblock)=blockcount) #THEN relblock:=relblock+blockcount "(*$I+*) #ELSE abortxfer(ioerror); !END; ! #END; "IF NOT emptysector THEN tslink:=currentnode.list[linkindex]; "eonode:=emptysector; !END; ! !PROCEDURE writeblocks(VAR pb:pagebuffer;pbpntr:pagerange; ?blockcount:INTEGER;VAR relblock:INTEGER); !BEGIN "(******* note: pbpntr should be divisiblrange;VAR tslink:link):BOOLEAN; !VAR "emptysector:BOOLEAN; !BEGIN "emptysector:=TRUE; "WHILE ((linkindex"Y"C$<>"y"C$<>"N"C$<>"n"1170*C$="N"C$="n"ferenced. 3. The DATA file contains 960 records of 128 bytes each. In the above sample, seven data records contain information. Records 002, 003, and 005 are inactive. There is no way to know this without information from INDX. AMB/aw3: MLMUTIL enccords contain meaningful information. The above sample MLM file has been sorted by "Name," so the four index records are sequenced to point to the active data in alphabetic order. Data records 002 (CLARK), 003 (SMITH), and 006 (SMART) are not reata items is a count of the number of active data records in the file. 2. Following the header in the INDX file are 960 "index" records of 16 bytes each. In the above sample file, only four data records are active, so only the first four index re | | Index Record 004 Points to 001 | | Data Record 007 Name = DUMB | | | | | 1. The INDX file contains one 512-byte "header" record. Among its many do 005 | | Data Record 006 Name = SMART | | | | | | | | | | | | | | | | Index Record 003 Points t | | | | | | Index Record 002 Points to 007 | | Data Record 005 Name = JONES | | | | | | | Index Record 001 Points to 004 | | Data Record 004 Name = ABLE | | | | | | | | | | Data Record 003 Name = SMITH | | | | | EAD PASCAL TEXT FILES."04=10:"78C";"ANY KEY RETURNS TO THE MENU."!>G$:::".D1/MENU.MAKER",320