         Q L   H A C K E R ' S   J O U R N A L
      ===========================================
           Supporting  All  QL  Programmers
      ===========================================
         #2                        February 1991
      
    The QL Hacker's Journal (QHJ) is published by Tim
Swenson as a service to the QL Community.  The QHJ is
freely distributable.  Past issues are available on disk,
via e-mail, or via the Anon-FTP server, garbo.uwasa.fi. 
The QHJ is always on the look out for article submissions.

        QL Hacker's Journal
     c/o Tim Swenson
     5615 Botkins Rd 
     Huber Heights, OH 45424 USA
     (513) 233-2178
     swensontc@mail.serve.com
     http://www.serve.com/swensont/


     Table of Contents

Editor's Forum ................................. 1
Find_c ......................................... 1
Diskinfo ....................................... 5
C Beautifier ................................... 16


Editor's Forum
    By Timothy Swenson

    I'm a bit surprized to have this issue out so soon after
the last  issue.  But,  the source  code from  Herb Schaaf's
article and the  C Beautifier  made for  quite a  few pages.
Since it was enough to fill an issue, here it is.

    Some of you may have noticed that last month's issue had
the  wrong year on it.  I guess I was so used to seeing 1990
that  it did not dawn on me until a friend pointed it out to
me.  Oh, well, such is life.

    As far as future  programs  I'm  working  on,  the  last
couple of days  I have  been working  on QDOS  functions for
Lattice C.  Lattice C is very lacking in QDOS functions like
those found in  SuperBasic  and  Small-C.   Using  the  QDOS
traps, all  QDOS functions should  be able to  be written in
Lattice  C.  Herb has done a few  that I have seen.  Using a
book  on QDOS calls for all  traps, I have written functions
for  the easiest QDOS functions.  I'm currently stuck trying
to get the functions to work.  I've tried a number of things
and  nothing seems to work.  I'll let it rest a few days and
then try it again.

    If  any of you  have written functions  similar to this,
let  me know.  I'm interested in seeing how you were able to
implement  them.  I want the functions to have the same name
as those found in Small-C.  This way programs can be written
for both compilers with minimal changes to the code.

    That's about all I  have to  say for  now.  Hope  to see
articles from more of you.


FIND2_C
    By Timothy Swenson

    I have written a  C  program  that  will  search  for  a
particular string in  a  file.   My  first  version  of  the
program  worked only on  text file.  The  second version was
designed to work on Quill files, or any binary file.

    The  reason for these two programs, is that I was trying
to help a friend index some Quill files he has.  Not wanting
to write a long  complex program  in a  language I  was just
learning, I  decided to  write a  short little  program that
will search  a binary file  for every occurance  of a string
and report where it found the string.


                           page 1

    The listings  below is  the result  of my  efforts. Both
programs  start off by asking for the file to search through
and the string to  search for.   If the  file is  not opened
(the  file does not exist) the  program aborts.  If the file
is opened, the  programs  then  proceeds  to  read  in  each
character of the file, one at a time.

    The  search algorithm is quick but weak.  There are some
cases  that it will miss.  Essentially it has a pointer that
points  to the character  in the string  to compare against.
This  is initialy set to one so it starts comparing with the
first  character of the  string.  If the  input character is
equal  to the first character, the pointer is incremented so
it will  compare with  the second  character.  This  goes on
until the pointer is greater than the string.  At this point
the string comparison has been successfull.  If at any point
a single character comparison fails, the pointer is set back
to the first character.

    This algorithm will miss  strings  like  abc  in  ababc.
This can be fixed, but I'll leave it up to the reader on how
to do this.   The Knuth-Morris-Pratt  algorithm is  the best
for string search,  but it is  not simple to  implement in a
new language (sometimes not even in an old language).

    The second  program prints  out the  byte offset  of the
beginning  of the string.  This means  that the start of the
string was found at  the Nth  byte of  the file.   Since the
program  works on binary  files (such as  Quill) the program
could  not return the line number  of the string.  The first
version of the program does print out the line number of the
matching line.

    I have also modified the  second  program  so  that  its
results are sent to a "log" file, so that a permanent record
can be kept of the searches.  

Program Version 1:

/*  Find_c
    This program searches any file for a certain string
    that the user has input.  Every time the program
    finds the string in the file, it prints to the
    screen the offset of where the string started
    The program is case insensitive.  All characters
    are converted to upper case.
    The algorithm does have some problems.  It will miss
    the string abc in ababc.
*/

#include <stdio_h>

main() {
   char  c,
         string[20],
         file[30];

   int   count,
         file_count,
         str_len,
         fd;



                           page 2
/* abcdefghijk test string*/

   count = 0;
   file_count = 0;

   printf("Enter File Name : \n");
   gets(file);

   printf("Enter Search String : \n");
   gets(string);

   fd = fopen(file,"r");
   if (fd == NULL)  {
          printf("Did not open file");
         abort(1);
   }

   str_len = strlen(string);

   while (( c = getc(fd)) != EOF) {
      ++file_count;
      /*  make sure a character is upper case */
      if (isascii(c)) c=toupper(c);
      if ( c == toupper(string[count]))
            ++count;
      else
            count = 0;

      if (count == str_len)
            printf("String found at  %d\n",
file_count-str_len+1);

   }
}

Program Version 2:

/*  Find2_c
    This program searches any file for a certain string
    that the user has input.  Every time the program
    finds the string in the file, it prints to a
    file the keyword and offsets of where the string
    started.
    The program is case insensitive.  All characters
    are converted to upper case.
    The algorithm does have some problems.  It will miss
    the string abc in ababc.
    The program expects a file called find2_log to exist
    on flp1_.  
*/

#include <stdio_h>

main() {
   char  c,
         string[20],
         file[30];

   int   count,
         file_count,
         str_len,
         fd,
         fd2;


                           page 3

/* abcdefghijk test string */

   count = 0;
   file_count = 0;

   printf("Enter File Name : \n");
   gets(file);

   printf("Enter Search String : \n");
   gets(string);

   fd = fopen(file,"r");
   if (fd == NULL)  {
          printf("Did not open file");
         abort(1);
   }

   fd2 = fopen("flp1_find2_log","a");

   fputs(string,fd2);
   fprintf(fd2,"\n   ");
   fputs(file,fd2);

   str_len = strlen(string);

   while (( c = getc(fd)) != EOF) {
      ++file_count;
      /*  make sure a character is upper case */
      if (isascii(c)) c=toupper(c);
      if ( c == toupper(string[count]))
            ++count;
      else
            count = 0;

      if (count == str_len) {
            printf(" String found at %d\n",
file_count-str_len+1);
            fprintf(fd2,", %d",file_count-str_len+1);
      }
   }
   fprintf(fd2,"\n");
   fclose(fd2);

}


Diskinfo
    by H. L. Schaaf
            
    This program started out as an attempt to examine a disk
with corrupted  files.  The  problem may  have been  with my
disk drive.  Just a  suggestion to  others; try  reading the
disk in another drive before you give up on it.  At any rate
don't write anything to the disk!

    Before  I knew  it I  had contracted  Babbage's ailment*
(which  often happens to programmers) and just couldn't stop
improving  (messing  with) the program.  It is in SuperBasic
and  uses Toolkit 2.  With it I  can read sectors  on a disk
that  will crash the machine  when you  try to do DIR in the
usual way.   But now it shows  the Disk  Header information,
finds which  blocks  might  have  files  starting  in  them,


                           page 4
displays the   mapping of the blocks,  gets File Header info
from the  files, if it can, (as  well as from the directory)
so you might even recover some 'erased' files.

    It will read blocks in the 'logical' sequence or you can
choose any block for reading.  It will transfer a contiguous
group of blocks into ram1_, and do a HEX/ASCII dump for me.

    In line with Babbage's ailment,  I'd  like  to  have  it
handle formats  other than the 720K(1440 sector) standard QL
style,  do a little more   "expert' analysis and statistical
summarization, and ultimately  see  if  we can  "fix-up" bad
disks  by appropriate selective surgical writing to just the
right bits, bytes, sectors, blocks, tracks, files, etc. 

    Right  now 'diskinfo12' is  a little under  20K in size,
and as you can tell, I consider it still under development. 

    *Babbage's  ailment is not knowing when to stop or leave
well  enough alone.  If it ain't broke you keep messing with
it until it ain't fixed.

100 REMark read disk header maps, locate files, read
    directory data
110 REMark diskinfo12
120 TK2_EXT
130 r$="" :REMark a recovery string
140 ALTKEY 'r','run',''
150 ALTKEY 'R','RUN',''
160 REDEF_TV :CSIZE 0,0
170 dateOK
180 PRINT "rest is available" : PAUSE 30
190 gfh = 0 :REMark a flag for get_file_hdrs
200 explain
205 prompt
210 OPEN #3,'flp1_*d2d'
220 h$=""
230 track = 0
240 side = 0
250 FOR sectr = 1,4,7
260 GET #3\sectr+(side*256)+(track*65536), a$
270 h$=h$&a$
280 NEXT sectr
290 CLOSE #3
300 OPEN_NEW #3, ram1_hdrstrg
310 PRINT #3, h$
320 CLOSE #3
330 CLS#0
340 look_at
350 PRINT #0,,"any key for location of files, etc."
360 PAUSE
370 CLS#0
380 locafile
390 PRINT #0,,"any key for file allocation table"
400 PAUSE
410 CLS#0
420 fatable
430 PRINT #0,,"any key for reading blocks(or directory
    data)"
440 PAUSE
450 block_read
460 PRINT #0,,"For another look try ALT-R"
470 STOP


                           page 5
480 :
490 REMark - - - - - end of main program - - - - - - - - - -
    - - - - -
500 :
510 REMark  = = = = = = = = = = = = = = = = = = = = = = = =
    = = = = = = =
520 :
530 REMark - - - - - beginning of procedures and functions -
    - - - - -
540 :
550 REMark -- file allocation table display  --  -- --   F A
    T A B L E  --
560 DEFine PROCedure fatable
570 CLS
580 clean = 0 : erased = 0 : bad = 0 :col = 0 : nextcol = 25
590 start = 97
600 FOR i = start TO LEN(h$) STEP 3
610 high = 0 : low = 0
620 b$=""
630 PRINT TO col;"B#";(i-start)/3;"=";
640 FOR j = 0 TO 2
650 b$=b$&HEX$(CODE(h$(i+j)),8)
660 END FOR j
670 b2$=b$(1 TO 2)
680 IF b2$="FE" :bad = bad + 1
690 IF (b2$="FD" AND b$(3 TO )<>"FFFF"): erased = erased + 1
700 IF (b2$="FD" AND b$(3 TO )="FFFF") : clean = clean + 1
710 high = HEX(b$(1 TO 3)) : low = HEX(b$(4 TO 6))
720 PRINT b$! high;".";low,
730 col = col + nextcol
740 IF ((col+15)>(3*nextcol)) : col = 0
760 END FOR i
770 PRINT clean;" clean blocks",erased;" blocks erased
    ";bad;" blocks unusable"
780 END DEFine fatable
790 :
800 REMark -- look at disk header information  --  --  -- L
    O O K _ A T --
810 :
820 DEFine PROCedure look_at
830 CLS
840 PRINT h$(1 TO 14),"random # =
    ";256*CODE(h$(16))+CODE(h$(15)),
850 PRINT "number of updates = ";
860 PRINT
    CODE(h$(20))+256*(CODE(h$(19))+256*(CODE(h$(18))+256*(C-
    ODE(h$(17)))))
870 PRINT"Sectors->","free","good","total","#/track
    #/cylinder #/block"
880 PRINT,,CODE(h$(22))+256*CODE(h$(21)),
890 PRINT CODE(h$(24))+256*CODE(h$(23)),
900 PRINT CODE(h$(26))+256*CODE(h$(25)),"  ";
910 PRINT CODE(h$(28))+256*CODE(h$(27)),"     ";
920 PRINT CODE(h$(30))+256*CODE(h$(29)),,
930 PRINT CODE(h$(34))+256*CODE(h$(33))\
940 PRINT\CODE(h$(32))+256*CODE(h$(31));" tracks";
950 PRINT" with a sector offset per track of ";CODE(h$(40))
    +256*CODE(h$(39))
960 dirlen = 512*(CODE(h$(36)))+256*CODE(h$(35))
970 dirlen = dirlen +(CODE(h$(38))+256*CODE(h$(37)))-64
980 PRINT "Directory takes ";CODE(h$(36))+256*CODE(h$(35));
990 PRINT " sectors + ";CODE(h$(38))+256*CODE(h$(37));
1000 PRINT " bytes which implies ";dirlen/64;" files"


                           page 6
1010 PRINT \"logical to physical table"
1020 FOR i = 41 TO 58
1030 PRINT CODE(h$(i)),
1040 END FOR i
1050 PRINT \ "physical to logical table"
1060 FOR i = 59 TO 76
1070 PRINT CODE(h$(i)),
1080 END FOR i
1090 PRINT\ "Spare 20 bytes available for creative uses ! !"
1100 FOR i = 77 TO 96
1110 PRINT CODE (h$(i)),
1120 END FOR i
1130 END DEFine look_at
1140 :
1150 REMark -- locate start of files   --  --  --  -  L O C
    A F I L E  --
1160 :
1170 DEFine PROCedure locafile
1180 CLS
1190 f$="#" : REMark found files
1200 e$="~" : REMark erased files ?
1210 start = 97
1220 col = 0 : nextcol = 25
1230 fls = 0 : ers = 0
1240 FOR i = start TO LEN(h$) STEP 3
1250 high = 0 : low = 0
1260 b$=""
1270 FOR j = 0 TO 2
1280 b$=b$&HEX$(CODE(h$(i+j)),8)
1290 END FOR j
1300 high = HEX(b$(1 TO 3)) : low = HEX(b$(4 TO 6))
1310 blck$ =(i-start)/3
1320 IF high = 3968 : PRINT "mapping sector at block ";blck$
1330 IF high = 0 : PRINT "directory block ";blck$
1340 IF b$="FDFFFF": PRINT TO col;"unused block @ ";blck$,
    :col=col+nextcol
1350 IF (b$(1 TO 2) ="FD" AND b$(3 TO) <> "FFFF") : PRINT TO
    col; "erased block @ ";blck$,:col =col + nextcol
1360 IF b$(1 TO 2) ="FE" : PRINT TO col; "nogood block @
    ";blck$,:col = col + nextcol
1370 IF ((low = 0) AND ( high>0 ) AND (high <480)) : PRINT
    TO col;"file# ";high;" at block ";blck$, :fls = fls +1
    :fls$=fls:f$=f$&fls$&"@"&blck$&"#" : col = col + nextcol
1380 IF ((low = 0) AND (high >3968)) : PRINT \ TO
    nextcol/2;" ? ? ERASED  FILE STARTING @ block ";blck$\ :
    col = 0 :ers = ers + 1: ers$ = ers: e$ = e$&"#?"&ers$&"
    @"&blck$&"~"
1390 IF col >2*nextcol : col = 0
1400 END FOR i
1420 PRINT\ "Found the starting place for ";fls;" files"
1430 PRINT "Found possible starting place for ";ers;" erased
    files"
1440 PRINT #0, "OK that's the end of locating the start of
    ";fls;" files"
1450 END DEFine locafile
1460 :
1470 REMark --  read blocks in sequence  -- --  --  B L O C
    K _ R E A D --
1480 DEFine PROCedure block_read
1490 REDEF_TV : CSIZE 0,0 : MODE 4
1495 CLS
1500 CLS#0
1510 DIM sectab(3,3):RESTORE 


                           page 7
1520 DATA 0,3,6,1,4,7,2,5,8  :REMark usual sector jumps
1530 FOR i = 1 TO 3
1540    FOR j = 1 TO 3
1550       READ sectab(i,j)
1560    END FOR j
1570 END FOR i
1580 blck = -1
1590 blck$ = "0"
1600 REPeat query
1605 IF blck = 479 : blck = 478
1610 PRINT"Enter a number (0 to 479) for a specific
    block"\"'D' = read Directory headers / 'G' = Get file
    headers "\"'F' = locate Files / 'R' = attempt Recovery
    of blocks  "\"'L' = Look at disk data / 'M' =  see
    Mapping of blocks / 'Q' = Quit"
1620 IF LEN(r$)<>0 :PRINT "'H' = HEX / ASCII  Dump of
    recovered blocks"
1630 INPUT "(ENTER for Block #";blck+1;"  '-' for previous
    Block,",blck$
1640 IF blck$ == "q" :EXIT query
1650 IF blck$ == 'd' : PAPER 4: INK 0:read_dir: prompt :
    block_read
1660 IF blck$ == 'g' : PAPER 0: INK 4:get_file_hdrs : prompt
    : block_read
1670 IF blck$ == 'r' :PAPER 2: INK 0:block_recover :prompt :
    block_read
1680 IF blck$ == 'l' :PAPER 0:INK 2:look_at : prompt :
    block_read
1690 IF blck$ == 'f' :PAPER 0:INK 4:locafile: prompt :
    block_read
1700 IF blck$ == 'm' :PAPER 0:INK 6:fatable : prompt :
    block_read
1710 IF blck$ == 'h' :PAPER 4:INK 0:recread : prompt :
    block_read
1720 IF ((blck$ = "-") AND (blck >0)) :blck = blck - 1
    :blck$ = blck
1730 IF ((blck$ = "-") AND (blck = 0)):PRINT; "this is the
    zeroth block can't go back any more!":blck$ = "0" : blck
    = 0
1735 IF ((blck$ = "") AND (blck<479)) : blck = blck + 1 :
    blck$ = blck
1740 IF ((blck$ = "") AND (blck=479)) :PRINT;" this is the
    final block can't go any further": blck$ = "479" : blck
    = 479
1760 blck = blck$
1770 :
1780 REMark sometimes we have read errors, etc.
1790 WHEN ERRor 
1800 PRINT\"There's been a hitch somewhere so let's try
    something else .."\
1810 RETRY 1600 :REMark repeat query
1820 END WHEN 
1830 :
1840 block_find
1850 block_get
1860 FOR sec = 0 TO 2
1870    PRINT\"BLOCK ";blck;" part ";sec+1,,"side
    ";side,"track ";track,            "sector
    ";(sctrs(sec+1))\
1880    PRINT b$((1+(sec * 512)) TO ((sec + 1)* 512))
1890    prompt
1900 END FOR sec
1910 PRINT "that was block ";blck


                           page 8
1920 IF blck = 0 :PRINT " which is the disk header and
    mapping block"\,"ENTER 'L' to look at disk info/ ENTER
    'F' to find file locations"\,"ENTER 'M' for mapping
    data"
1930 IF blck = 1: PRINT " probably the start of the
    directory block(s) "\,"ENTER 'D' for directory header
    translations"\,"ENTER 'G' to get file headers"
1940 IF gfh = 1 : PRINT,"ENTER 'R' to recover (rescue)
    blocks"
1950 IF blck = 479 : PRINT " That's the last block on a 720K
    disk"
1960 END REPeat query
1970 CLS
1980 PRINT #0, "Try ALT-R to run another"
1990 STOP
2000 END DEFine block_read
2010 :
2020 REMark --  read block into b$   --  --  --  --  B L O C
    K _ G E T  --
2030 DEFine PROCedure block_get
2040 OPEN #3,'flp1_*d2d'
2050 b$=""
2060 FOR i = 1 TO 3
2070    GET #3\sctrs(i)+(side*256)+(track*65536), a$
2080    b$=b$&a$
2090    END FOR i
2100 CLOSE #3
2110 END DEFine block_get
2120 :
2130 REMark -- find side,track,sectors of block --  B L O C
    K _ F I N D  --
2140 DEFine PROCedure block_find
2150 PRINT "BLOCK# ";blck,,
2160 side = 2*((blck/2) - INT(blck/2))
2170 PRINT "side ";side,
2180 track = INT(blck/6)
2190 PRINT "track ";track,
2200 begin = ((INT(blck/2)) MOD 3) + 1
2210 PRINT "sectors ";
2220 DIM sctrs(3)
2230 FOR i = 1 TO 3
2240    sctrs(i) = ((sectab(begin,i)+5*track) MOD 9) + 1
2250    PRINT "  ";sctrs(i);
2260 END FOR i
2270 END DEFine block_find
2280 :
2290 REMark -- read directory headers  --  --  --  --  R E A
    D _ D I R  --
2300 DEFine PROCedure read_dir
2310 CLS
2320 file_no = 1 :blck = 0 : mark = 0
2330 REPeat reeding
2340    blck = blck +1
2350 PRINT\
2360    block_find
2370    PRINT\
2380    block_get
2390  dirstart = 1
2400 IF blck = 1 : dirstart = 65
2410 END IF 
2420     FOR jj = dirstart TO LEN(b$) STEP 64
2430 dirstr$ = b$(jj TO jj + 63)
2440 mark = mark + 64


                           page 9
2450 IF (mark > dirlen) : EXIT reeding
2460 PRINT\ "File # ";file_no;
2470 read_dirfilehdr
2480 file_no = file_no + 1
2490 END FOR jj
2500 END REPeat reeding
2510 PRINT "That's the last file in the directory"
2520 END DEFine read_dir
2530 :
2540 REMark - read directory file header  R E A D _ D I R F
    I L E H D R --
2550 DEFine PROCedure read_dirfilehdr
2560 PRINT " Length is ";
2570 hx$=""
2580 FOR kk = 1 TO 4
2590   hx$ = hx$ & HEX$ (CODE(dirstr$(kk)),8)
2600 END FOR kk
2610 PRINT HEX(hx$),"(";(HEX(hx$)-64);" plus this 64 byte
    header)"
2620 PRINT "Access code is "; CODE(dirstr$(5)),
2630 type = CODE(dirstr$(6))
2640 PRINT "Type ";type;" ";
2650 SELect ON type
2660   ON type = 0 : PRINT "(Data)"
2670   ON type = 1 : PRINT "(Task)"
2680   ON type = 2 : PRINT "(Link)"
2690   = REMAINDER : PRINT "(????)"
2700 END SELect 
2710 PRINT "file information (if task file) = ";
2720 hx$=""
2730 FOR kk = 7 TO 10
2740   hx$ = hx$ & HEX$ (CODE(dirstr$(kk)),8)
2750 END FOR kk
2760 PRINT hx$,HEX(hx$),dirstr$(7 TO 10)
2770 PRINT "4 bytes not usually used ? ! --->",
2780 hx$=""
2790 FOR kk = 11 TO 14
2800   hx$ = hx$ & HEX$ (CODE(dirstr$(kk)),8)
2810 END FOR kk
2820 PRINT hx$,HEX(hx$)
2830 finaln = 256*CODE(dirstr$(15))+CODE(dirstr$(16))
2840 PRINT "length of file name is ";finaln;" bytes",
2850 IF finaln > 36 : finaln = 36
2860 PRINT "FILENAME IS ";
2870 FOR kk = 17 TO (16+finaln)
2880   PRINT dirstr$(kk);
2890 END FOR kk
2900 PRINT\
2910 DIM time(3)
2920 FOR kk = 0 TO 2
2930   hx$ = ""
2940   FOR tt = 1 TO 4
2950     hx$ = hx$ & HEX$(CODE(dirstr$(tt+(52+kk*4))),8)
2960   END FOR tt
2970   time(kk) = HEX(hx$)
2980   PRINT time(kk),DATE$(time(kk)),
2990   SELect ON kk
3000     = 0 :  PRINT " last save "
3010     = 1 :  PRINT " last read ( 4 bytes for other uses ?
    ! )"
3020     = 2 :  PRINT " last copy ( 4 bytes for other uses ?
    ! )"
3030   END SELect 


                          page 10
3040 END FOR kk
3050 END DEFine read_dirfilehdr
3060 :
3070 REMark -- Explain what program does --  --  --  --  E X
    P L A I N --
3080 DEFine PROCedure explain
3090 CLS
3100 PRINT"This started out as a simple file recovery
    exercise"\"but like Topsy 'it just growed'"
3105 PRINT "It looks for flp1_, ram1_, TK2, and 720K (1440
    sector) disks"
3110 PRINT"Just keep following the prompts and enjoy "
3120 PRINT "It is in SuperBasic so feel free to modify to
    your taste."
3130 PRINT "After asking for the date the program looks at
    Block 0"\"In the first 96 bytes are data about the disk
    itself"\"The remaining 1440 bytes keep track of where
    the files are"
3140 PRINT "The disk info is shown on one screen,
    while"\"directory data, and files allocation tables take
    160 lines each."
3150 PRINT "Directory data starts in Block 1 "
3160 PRINT "Erased files are not in the directory, but
    corrupted ones may be."
3170 PRINT "File headers can be found for erased files"
3180 PRINT "File headers data is usually just the name of
    the file"
3190 PRINT"Hopefully this gives you something to work with."
3200 PRINT"Recovery of contiguous blocks is possible and a
    "\"simple hex-offset / HEX-dump / ASCII-dump is
    available"
3210 END DEFine explain
3220 :
3230 REMark -- a little prompt in #0   --  --  --  --  P R O
    M P T  --  --
3240 DEFine PROCedure prompt
3250 PRINT #0, "Any key to continue"
3260 PAUSE
3270 CLS#0
3280 END DEFine prompt
3290 :
3300 REMark -- get file headers   --  --  -- G E T _ F I L E
    _ H D R S  --
3310 DEFine PROCedure get_file_hdrs
3320 PRINT\ "Getting headers from files"
3330 DIM fh(fls):DIM fh$(fls,64)
3340 IF ers <> 0 THEN DIM eh(ers):DIM eh$(ers,64)
3350 tmp$ = f$
3360 REPeat stripfls
3370 IF LEN(tmp$) = 1 THEN EXIT stripfls
3380 front = "#" INSTR tmp$ :back = "@" INSTR tmp$
3390 fl_no = tmp$(front+1 TO back-1) : tmp$ = tmp$(back+1
    TO)
3400 fh(fl_no) = tmp$( 1 TO ("#" INSTR tmp$)-1)
3410 tmp$ = tmp$(("#" INSTR tmp$) TO )
3420 PRINT\ "File #";fl_no;" @ block ";fh(fl_no)\
3430 blck = fh(fl_no)
3440 block_find
3450 block_get
3460 fh$(fl_no) = b$(1 TO 64)
3470 PRINT \fh$(fl_no)
3480 dirstr$ = fh$(fl_no)
3490 read_file_head


                          page 11
3500 END REPeat stripfls
3510 tmp$ = e$
3520 fl_no = 0
3530 REPeat stripers
3540 IF LEN(tmp$) = 1 THEN EXIT stripers
3550 front = "?" INSTR tmp$ :back = "@" INSTR tmp$
3560 fl_no = tmp$(front+1 TO back-1) : tmp$ = tmp$(back+1
    TO)
3570 eh(fl_no) = tmp$(1 TO ("~" INSTR tmp$)-1):tmp$ =
    tmp$(("~" INSTR tmp$) TO)
3580 PRINT\"erased ? file #";fl_no;" @ block ";eh(fl_no)\
3590 blck = eh(fl_no)
3600 block_find
3610 block_get
3620 eh$(fl_no)=b$(1 TO 64)
3630 PRINT eh$(fl_no)
3640 dirstr$ = eh$(fl_no)
3650 read_file_head
3660 END REPeat stripers
3670 gfh = 1
3680 END DEFine get_file_hdrs
3690 :
3700 REMark - recover blocks in sequence - B L O C K  _ R E
    C O V E R  --
3710 DEFine PROCedure block_recover
3720 REMark assumes use of Toolkit 2 (TK2_ext)
3730 REMark assumes get_file_hdrs has been used
3740 IF (gfh <> 1) :get_file_hdrs
3760 CLS
3770 PRINT\,, "Summary of files found: "
3780 FOR ii = 1 TO fls
3790 PRINT "file # ";ii;" @ block ";fh(ii);" ";
3800 IF blck$<> 'r' THEN PRINT fh$(ii); :END IF 
3810 dirstr$ = fh$(ii)
3820 read_file_head
3830 END FOR ii
3840 IF ers <> 0 THEN PRINT "Potential files, but erased "
    :END IF 
3850 ii = 1
3860 REPeat efiles
3870 IF ers = 0 OR ers < ii THEN EXIT efiles
3880 PRINT "Possible erased file # ";ii;" at block
    ";eh(ii);" ";
3890 IF blck$<> 'r' THEN PRINT eh$(ii); :END IF 
3900 dirstr$ = eh$(ii)
3910 read_file_head
3920 ii =ii + 1
3930 END REPeat efiles
3940 PRINT\"Note the numbers of the first and last blocks
    for recovery"\,"They will be gathered into
    RAM1_rstrg"\,,"With luck you may be able to salvage
    something."\"ENTER 'B' to return to block_read - - any
    other key for recovery process."
3950 ans$ = INKEY$(-1)
3960 IF ans$ == "b" THEN block_read
3970 CLS
3980 DIM sectab(3,3):RESTORE 
3990 DATA 0,3,6,1,4,7,2,5,8  :REMark usual sector jumps
4000 FOR i = 1 TO 3
4010    FOR j = 1 TO 3
4020       READ sectab(i,j)
4030    END FOR j
4040 END FOR i


                          page 12
4050 r$=""
4060 INPUT "Starting from block number ? ";from_block
4070 INPUT "Ending with block number ?"\"(can be same as
    starting block to get just one block ";to_block
4080 IF to_block < from_block THEN to_block = from_block
4090 FOR blck = from_block TO to_block
4100 PRINT
4110 block_find
4120 block_get
4130 r$=r$&b$
4140 END FOR blck
4150 OPEN_NEW #3,ram1_rstrg
4160 PRINT #3, r$
4170 CLOSE #3
4180 PRINT \"Blocks from ";from_block;" to ";to_block;" have
    been saved as RAM1_rstrg"\
4190 PRINT\ "the recovery string is r$ and may be examined
    as a hex/ascii dump"
4200 END DEFine block_recover
4210 :
4220 REMark  DATEOK and REST
4230 :
4240 REMark  --  --  --  --  --  --  --  --  --  --  --  --
    R E S T  --
4250 :
4260 REMark a screen blanker you can invoke when you're
    called away etc.
4270 :
4280 DEFine PROCedure rest
4290 OPEN#4,scr
4300 WINDOW#4,512,256,0,0
4310 PAPER#4,0:INK#4,4:CLS#4
4320 doze=DATE
4330 PAUSE
4340 wake=DATE
4350 CLOSE#4
4360 WMON
4370 PRINT DATE$(doze),"rested"\DATE$(wake),"awoken"
4380 d$=DATE$(wake-doze)
4390 AT 3,12:PRINT d$(13 TO),"napped"
4400 END DEFine rest
4410 :
4420 REMark -- asks if datestamp is set ? --  --  --  --  D
    A T E O K  --
4430 DEFine PROCedure dateOK
4440 CLS
4450 CSIZE 2,1
4460 PRINT DATE$,,DAY$
4470 PRINT\"Is   D A T E   O.K. ?"
4480 PAUSE 20
4490 IF INKEY$(-1)=="n" THEN chngdate
4500 CSIZE 0,0
4510 END DEFine dateOK
4520 :
4530 REMark -- to set date using ENTER --  --  --  -- C H N
    G D A T E  --
4540 DEFine PROCedure chngdate
4550 CSIZE 1,0
4560 PRINT\ "enter numeric values for SDATE"
4570 PRINT\"(ENTER will place commas )"
4580 CSIZE 0,0
4590 PRINT"year month  day    hour  min    sec"
4600 DIM d(6)


                          page 13
4610 FOR i=1 TO 6
4620 AT 13,(((i-1)*13)/2):
4630 INPUT d(i);","
4640 END FOR i
4650 SDATE d(1),d(2),d(3),d(4),d(5),d(6)
4660 dateOK
4670 END DEFine chngdate
4680 :
4690 REMark last line as of Nov. 10th 1990  6am ...
4700 :
4710 REMark -- read file headers --  -- R E A D _ F I L E _
    H E A D  -- --
4720 DEFine PROCedure read_file_head
4730 ok = 0
4740 hx$=""
4750 FOR kk = 1 TO 4
4760   hx$ = hx$ & HEX$ (CODE(dirstr$(kk)),8)
4770 END FOR kk
4780 IF HEX(hx$) = 64 AND blck$ <> 'r' : PRINT " looks OK
    once ";
4790 IF HEX(hx$) = 64 : ok = ok + 1
4800 ELSE PRINT " bytes 1-4 = ";dirstr$(1 TO 4);
4810 END IF 
4820 IF (CODE(dirstr$(5)) + CODE(dirstr$(6)) = 0) AND blck$
    <> 'r' THEN PRINT " looks OK twice";
4830 IF (CODE(dirstr$(5)) + CODE(dirstr$(6)) = 0):ok = ok +
    1
4840 ELSE PRINT " bytes 5-6 = ";dirstr$(5 TO 6);
4850 END IF 
4860 hx$=""
4870 FOR kk = 7 TO 10
4880   hx$ = hx$ & HEX$ (CODE(dirstr$(kk)),8)
4890 END FOR kk
4900 IF (HEX(hx$) = 0) AND blck$<>'r' :PRINT " looks OK
    thrice ";
4910 IF (HEX(hx$) = 0) :ok = ok + 1
4920 ELSE PRINT " bytes 7-10 = ";dirstr$(7 TO 10);
4930 END IF 
4940 hx$=""
4950 FOR kk = 11 TO 14
4960   hx$ = hx$ & HEX$ (CODE(dirstr$(kk)),8)
4970 END FOR kk
4980 IF (HEX(hx$) = 0) AND blck$<>'r' THEN PRINT " looks OK
    for 4 ";
4990 IF (HEX(hx$) = 0) THEN ok = ok + 1
5000 ELSE PRINT " bytes 11-14 = ";dirstr$(11-14);
5010 END IF 
5020 finaln = 256*CODE(dirstr$(15))+CODE(dirstr$(16))
5030 REMark PRINT "length of file name is ";finaln;" bytes",
5040 IF finaln > 36 THEN PRINT\ "Filename too long ";
    :finaln = 36
5050 END IF 
5060 IF blck$ <> 'r' THEN PRINT\ "FILENAME IS ";
5070 FOR kk = 17 TO (16+finaln)
5080   PRINT dirstr$(kk);
5090 END FOR kk
5100   hx$ = ""
5110   FOR kk = 52 TO 64
5120     hx$ = hx$ & HEX$(CODE(dirstr$(kk)),8)
5130   END FOR kk
5140 IF (HEX(hx$) = 0) AND blck$<>'r' : PRINT," Looks OK at
    end"
5150 IF (HEX(hx$) = 0) :ok = ok + 1


                          page 14
5160 ELSE PRINT," Strange ending ";hx$
5170 END IF 
5180 IF ok = 5 AND blck$ = 'r' :PRINT ,,"Looks OK "
5190 END DEFine read_file_head
5200 :
5210 REMark -- redefine tv --  --  --  --  --  --  --  R E D
    E F _ T V  --
5220 DEFine PROCedure REDEF_TV
5230 WINDOW #0,448,40,32,216
5240 PAPER #0,0: INK #0,7: BORDER #0
5250 OPEN #1,con_448x200a32x16_128
5260 PAPER #1,2: INK #1,7: BORDER #1
5270 OPEN #2,con_448x200a32x16_128
5280 PAPER #2,1: INK #2,7: BORDER #2
5290 MODE 4
5300 END DEFine REDEF_TV
5310 :
5320 :
5330 REMark --   recread  --  --  --  --  --  --  --  --  R
    E C R E A D --
5340 REMark to take r$ and read it in debugd style
5350 DEFine PROCedure recread
5360 CLS
5370 PRINT #0\"Reading from block ";from_block;" to
    ";to_block;" ";HEX$(LEN(r$),16)
5380 REMark PRINT r$,LEN(r$)\
5390 FOR i = 1 TO LEN(r$) STEP 16
5400 PRINT HEX$(i,16);"  ";
5410 FOR j = 0 TO 15
5420 rcode = CODE(r$(i+j))
5430 PRINT HEX$(rcode,8);" ";
5440 END FOR j
5450 PRINT;"   ";
5460 FOR j = 0 TO 15
5470 IF CODE(r$(i+j))<> 10 THEN PRINT r$(i+j);
5480 IF CODE(r$(i+j))= 10 THEN PRINT "";
5490 END IF 
5500 END FOR j
5510 PRINT\
5520 END FOR i
5530 END DEFine recread
    

C Beautifier
    By Tim Swenson

    The C User's Group has disk full of C programs that have
been written for portability.  One of these programs is a C
beautifier (cb_c).  This is a program that takes any C code
and outputs a file with the C code in "proper" formating.
It does things like indenting, putting single statements on
a line, etc.  Below (listing 1) is a listing of a C program
that is not written nicely.  Listing 2 is the same program
after it has gone throught the C beautifier.

    To get the program to compile under Lattice C, I only
had to make some changes to the Include statements, since
Lattice C expects the drive specifier.  The program compiled
fine after those minor changes.  The only thing I have
noticed that might need to be changed, is that the program
uses Tabs instead of X number of spaces.  This is fine on
other computers, but the QL does not display tabs as so many
blanks on the screen.  There is a function called ptabs()


                          page 15
that can be changed to output X number of spaces, instead of
tabs.

Listing 1:

#include <stdio_h>

main() {

int x; int y;  int z;

   for (x = 1; x <= 10; ++x) { printf("test"; }

   for (y = 1; y <= 10; ++y) 
     {
        printf("test"); }

   for (z = 1; z <= 10; ++z) {
        printf("test");
   }

}

Listing 2:

#include <stdio_h>

main() {

    int x;
    int y;
    int z;

    for (x = 1; x <= 10; ++x) {
         printf("test");
    }

    for (y = 1; y <= 10; ++y) 
    {
         printf("test");
    }

    for (z = 1; z <= 10; ++z) {
         printf("test");
    }

}


USAGE:

     CRUN "CB inputfile { outputfile }"

     
HELP:

     Typing CB with no file name displays a short help
reminder.

/*
   HEADER:      CUG236;
   TITLE:      C Source Formatter;
   DATE:      04/04/1987;


                          page 16
   DESCRIPTION:   "Formats a C source program with proper
indents for each statement.";
   VERSION:   2.1;
   KEYWORDS:   Pretty Printer;
   FILENAME:   CB.C;
   SEE-ALSO:   CB.DOC;
   COMPILERS:   vanilla;
   AUTHORS:   W. C. Colley III, J. W. Kindschi Jr.;
*/

/*
   Modified for Portable C
   by: William C. Colley, III (4 APR 1987)

   Modified for Lattice C Ver 1.01
   by: John W. Kindschi Jr. (10-30-83)

   Swiped from CPIG'S UNIX system and modified to
   run under BDS C by William C. Colley, III


To use the program type the following command line:

   A>cb input.fil [output.fil]

   Where input.fil is the file to be pretty printed and
[output.fil] is the destination file. If no output file is
specified, then the output goes to standard output.
*/

#include <flp1_stdio_h>

/*
 * Portability Note:  The AZTEC C compilers handle the
binary/text file dichotomy differently from most other
compilers.  Uncomment the following pair of #defines if you
are running AZTEC C:
 */

/*
#define getc(f)      agetc(f)
#define putc(c,f)   aputc(c,f)
*/

char cc, lchar, pchar, string[200];
char *wif[] =   {   "if",   NULL      };
char *welse[] = {   "else", NULL      };
char *wfor[] =   {   "for",  NULL      };
char *wds[] =   {   "case", "default",   NULL   };

int clevel, ct, iflev, ind[10], level, paren, sifflg[10],
siflev[10];
int sind[20][10], slevel[10], spflg[20][10], stabs[20][10];
int aflg, bflg, eflg, ifflg = -1, pflg[10], qflg, sflg = 1;
int c, j, lastchar, peek = -1, tabs;
FILE *f1, *f2 = stdout;

void comment(), gotelse(), ptabs(), put_str();

int main(argc,argv)
int argc;
char *argv[];
{


                          page 17
    int getchr(), get_nl(), get_str(), lookup();

    /* Initialize everything here */

    if (argc < 2 || argc > 3) {
   fprintf(stderr,"Usage:  CB input.fil { output.fil }\n");
return !0;
    }
    if (!(f1 = fopen(*++argv,"r"))) {
   fprintf(stderr,"ERROR:  Cannot find file %s\n",*argv);
return !0;
    }
    if (argc == 3 && !(f2 = fopen(*++argv,"w"))) {
   fprintf(stderr,"ERROR:  Cannot create file %s\n",*argv);
return !0;
    }

    /* End of Initialization */

    while ((c = getchr()) != EOF) {
   switch (c) {
       default:   string[j++] = c;
         if (c != ',') lchar = c;
         break;

       case ' ':
       case '\t':   if (lookup(welse) == 1) {
             gotelse();
             if (sflg == 0 || j > 0) string[j++] = c;
             put_str();   sflg = 0;  break;
         }
         if (sflg == 0 || j > 0) string[j++] = c;
         break;

       case '\n':   if (eflg = lookup(welse) == 1)
gotelse();
         put_str();  fprintf(f2,"\n");  sflg = 1;
         if (eflg == 1) {  pflg[level]++;  tabs++; }
         else if(pchar == lchar) aflg = 1;
         break;

       case '{':   if (lookup(welse) == 1) gotelse();
         siflev[clevel] = iflev;    sifflg[clevel] = ifflg;
         iflev = ifflg = 0;  clevel++;
         if (sflg == 1 && pflg[level] != 0) {
             pflg[level]--;  tabs--;
         }
         string[j++] = c;  put_str();  get_nl();
put_str();
         fprintf(f2,"\n");  tabs++;  sflg = 1;
         if (pflg[level] > 0) {
             ind[level] = 1;  level++;  slevel[level] =
clevel;
         }
         break;

       case '}':   clevel--;
         if ((iflev = siflev[clevel]-1) < 0) iflev = 0;
         ifflg = sifflg[clevel];    put_str();  tabs--;
ptabs();
         if ((peek = getchr()) == ';') {
             fprintf(f2,"%c;",c);  peek = -1;
         }


                          page 18
         else fprintf(f2,"%c",c);
         get_nl();  put_str();  fprintf(f2,"\n");  sflg = 1;
         if (clevel < slevel[level] && level > 0) level--;
         if (ind[level] != 0) {
             tabs -= pflg[level];  pflg[level] = ind[level]
= 0;
         }
         break;

       case '"':
       case '\'':   string[j++] = c;
         while ((cc = getchr()) != c) {
             string[j++] = cc;
             if (cc == '\\') string[j++] = getchr();
             if (cc == '\n') { put_str();  sflg = 1; }
         }
         string[j++] = cc;
         if (get_nl() == 1) { lchar = cc;  peek = '\n'; }
         break;

       case ';':   string[j++] = c;  put_str();
         if (pflg[level] > 0 && ind[level] == 0) {
             tabs -= pflg[level];  pflg[level] = 0;
         }
         get_nl();  put_str();  fprintf(f2,"\n");  sflg = 1;
         if(iflev > 0 && ifflg == 1){ iflev--;  ifflg = 0; }
         else iflev = 0;
         break;

       case '\\':   string[j++] = c;  string[j++] =
getchr();  break;

       case '?':   qflg = 1;  string[j++] = c;  break;

       case ':':   string[j++] = c;
         if (qflg == 1) { qflg = 0;  break; }
         if (!lookup(wds)) { sflg = 0;  put_str(); }
         else { tabs--;   put_str();  tabs++; }
         if ((peek = getchr()) == ';') {
             fprintf(f2,";");  peek = -1;
         }
         get_nl();  put_str();  fprintf(f2,"\n");  sflg = 1;
         break;

       case '/':   string[j++] = c;
         if ((peek = getchr()) != '*') break;
         string[j++] = peek;  peek = -1;    comment();
break;

       case ')':   paren--;  string[j++] = c;  put_str();
         if (get_nl() == 1) {
             peek = '\n';
             if (paren != 0) aflg = 1;
             else if (tabs > 0) {
            pflg[level]++;   tabs++;    ind[level] = 0;
             }
         }
         break;

       case '#':   string[j++] = c;
         while ((cc = getchr()) != '\n') string[j++] = cc;
         string[j++] = cc;  sflg = 0;  put_str();  sflg = 1;
         break;


                          page 19

       case '(':   string[j++] = c;  paren++;
         if (lookup(wfor) == 1) {
             while ((c = get_str()) != ';');
             ct = 0;
cont:             while ((c = get_str()) != ')')
            if(c == '(') ct++;
             if (ct != 0) { ct--;  goto cont; }
             paren--;  put_str();
             if (get_nl() == 1) {
            peek = '\n';  pflg[level]++;
            tabs++;    ind[level] = 0;
             }
             break;
         }
         if (lookup(wif) == 1) {
             put_str();
             stabs[clevel][iflev] = tabs;
             spflg[clevel][iflev] = pflg[level];
             sind[clevel][iflev] = ind[level];
             iflev++;  ifflg = 1;
         }
   }
    }
    if (f2 != stdout && (ferror(f2) || fclose(f2))) {
   fprintf(stderr,"ERROR:  Disk full\n");   return !0;
    }
    fclose(f1);    return 0;
}

void ptabs()
{
    int i;

    for (i=0; i < tabs; i++) fprintf(f2,"\t");
}

int getchr()
{
    if (peek < 0 && lastchar != ' ' && lastchar != '\t')
pchar = lastchar;
    lastchar = (peek < 0) ? getc(f1) : peek;  peek = -1;
    return lastchar;
}

void put_str()
{
    if (j > 0) {
   if (sflg != 0) {
       ptabs();  sflg = 0;
       if (aflg == 1) {
      aflg = 0;
      if (tabs > 0) fprintf(f2,"    ");
       }
   }
   string[j] = '\0';  fprintf(f2,"%s",string);  j = 0;
    }
    else if (sflg != 0) { sflg = 0;  aflg = 0; }
}

int lookup(tab)
char *tab[];
{


                          page 20
    char r;
    int i,kk,k,l;

    if (j < 1) return 0;
    for (kk = 0; string[kk] == ' '; ++kk);
    for (i = 0; tab[i] != 0; i++) {
   l = 0;
   for (k=kk; (r = tab[i][l++]) == string[k] && r != '\0';
++k);
   if (r == '\0' && (string[k] < 'a' || string[k] > 'z'))
return 1;
    }
    return 0;
}

int get_str()
{
    char ch;

beg:
    if ((ch = string[j++] = getchr()) == '\\') {
   string[j++] = getchr();    goto beg;
    }
    if (ch == '\'' || ch == '"') {
   while ((cc = string[j++] = getchr()) != ch)
       if (cc == '\\') string[j++] = getchr();
   goto beg;
    }
    if (ch == '\n') { put_str();  aflg = 1;  goto beg; }
    else return ch;
}

void gotelse()
{
    tabs = stabs[clevel][iflev];  pflg[level] =
spflg[clevel][iflev];
    ind[level] = sind[clevel][iflev];  ifflg = 1;
}

int get_nl()
{
    while ((peek = getchr()) == '\t' || peek == ' ') {
   string[j++] = peek;  peek = -1;
    }
    if ((peek = getchr()) == '/') {
   peek = -1;
   if ((peek = getchr()) == '*') {
       string[j++] = '/';   string[j++] = '*';
       peek = -1;   comment();
   }
   else string[j++] = '/';
    }
    if ((peek = getchr()) == '\n') { peek = -1;    return 1;
}
    return 0;
}

void comment()
{
rep:
    while ((c = string[j++] = getchr()) != '*')
   if (c == '\n') { put_str();  sflg = 1; }
gotstar:


                          page 21
    if ((c = string[j++] = getchr()) != '/') {
   if (c == '*') goto gotstar;
   goto rep;
    }
}




























































                          page 22
