POINTERS
An example of constructing a list of words and line numbers
The following program illustrates a buggy method of reading a small
file and generating a list of words and associated line numbers. It
does this using a linked list.
Its been ported from a C equivalent example in the C programming module. It fails on large text files (generates a heap overflow error). Proper handling of error situations is minimised so as to concentrate primarily on code execution.
Use it at your own peril.
program findwords( input, output ); { $M 32000, 65536 } const TRUE = 1; FALSE = 0; BS = 8; TAB = 9; LF = 10; VT = 11; FF = 12; CR = 13; { this holds the line numbers for each word. Its double linked for ease of freeing memory later on } type listptr = ^list; list = record line : integer; { line number of occurrence } nextline : listptr; { link to next line number } prevline : listptr { link to previous line number } end; { this holds the word with a link to a struct list holding line numbers. Double linking to simplify freeing of memory later on } wordptr = ^words; words = record word : string; { pointer to word } lines : listptr; { pointer to list of line numbers } nextword : wordptr; { pointer to next word in list } prevword : wordptr; { pointer to previous word in list} end; var head, tail : wordptr; { beginning and end of list } fin : file of char; { input file handle } filename : string; { name of input file } thisisfirstword : integer; { to handle start of list words=0 } { customised exit routine to provide orderly shutdown } procedure myexit( exitcode : integer ); var word_ptr, tempw : wordptr; line_ptr, templ : listptr; begin { close input file } close( fin ); { free any allocated memory } writeln('Deallocating memory:'); word_ptr := head; while word_ptr <> nil do begin tempw := word_ptr; { remember where we are } line_ptr := word_ptr^.lines; { go through line storage list } while line_ptr <> nil do begin templ := line_ptr; { remember where we are } line_ptr := line_ptr^.nextline; { point to next list } dispose( templ ) { free current list } end; word_ptr := word_ptr^.nextword; { point to next word node } dispose( tempw ) { free current word node } end; { return to OS } halt( exitcode ) end; { check to see if word already in list, 1=found, 0=not present } function checkforword( word : string ) : integer; var ptr : wordptr; begin ptr := head; { start at first word in list } while ptr <> nil do begin if ptr^.word = word then { found the word? } checkforword := TRUE; { yes, return found } ptr := ptr^.nextword { else cycle to next word in list } end; checkforword := FALSE { word has not been found in list } end; { enter word and occurrence into list } procedure makeword( word : string; line : integer ); var newword, word_ptr : wordptr; newline, line_ptr : listptr; begin if checkforword( word ) = FALSE then begin { insert word into list } newword := new( wordptr ); if newword = nil then begin writeln('Error allocating word node for new word: ', word ); myexit( 1 ) end; { add newnode to the list, update tail pointer } if thisisfirstword = TRUE then begin head := newword; tail := nil; thisisfirstword := FALSE; head^.prevword := nil end; newword^.nextword := nil; { node is signified as last in list } newword^.prevword := tail; { link back to previous node in list } tail^.nextword := newword; { tail updated to last node in list } tail := newword; { allocate storage for the word including end of string NULL } tail^.word := word; { allocate a line storage for the new word } newline := new( listptr ); if newline = nil then begin writeln('Error allocating line memory for new word: ', word); myexit( 3 ) end; newline^.line := line; newline^.nextline := nil; newline^.prevline := nil; tail^.lines := newline end else begin { word is in list, add on line number } newline := new( listptr ); if newline = nil then begin writeln('Error allocating line memory for existing word: ', word); myexit( 4 ) end; { cycle through list to get to the word } word_ptr := head; while word_ptr <> nil do begin if word_ptr^.word = word then break; word_ptr := word_ptr^.nextword; end; if word_ptr = nil then begin writeln('ERROR - SHOULD NOT OCCUR '); myexit( 5 ) end; { cycle through the line pointers } line_ptr := word_ptr^.lines; while line_ptr^.nextline <> nil do line_ptr := line_ptr^.nextline; { add next line entry } line_ptr^.nextline := newline; newline^.line := line; newline^.nextline := nil; newline^.prevline := line_ptr { create back link to previous line number } end end; { read in file and scan for words } procedure processfile; var ch : char; loop, in_word, linenumber : integer; buffer : string; begin in_word := 0; { not currently in a word } linenumber := 1; { start at line number 1 } loop := 0; { index character pointer for buffer[] } buffer := ''; read( fin, ch ); while not Eof( fin ) do begin case ch of chr(CR) : begin if in_word = 1 then begin in_word := 0; makeword( buffer, linenumber ); buffer := ''; end; linenumber := linenumber + 1 end; ' ', chr(LF), chr(TAB), chr(VT), chr(FF), ',' , '.' : begin if in_word = 1 then begin in_word := 0; makeword( buffer, linenumber ); buffer := ''; end end; else begin if in_word = 0 then begin in_word := 1; buffer := buffer + ch end else begin buffer := buffer + ch end end; end; { end of switch } read( fin, ch ) end { end of while } end; { print out all words found and the line numbers } procedure printlist; var word_ptr : wordptr; line_ptr : listptr; begin writeln('Word list follows:'); word_ptr := head; while word_ptr <> nil do begin write( word_ptr^.word, ': ' ); line_ptr := word_ptr^.lines; while line_ptr <> nil do begin write( line_ptr^.line, ' ' ); line_ptr := line_ptr^.nextline end; writeln; word_ptr := word_ptr^.nextword end end; procedure initvars; begin head := nil; tail := nil; thisisfirstword := TRUE end; begin writeln('Enter filename of text file: '); readln( filename ); assign( fin, filename ); reset( fin ); { if fin = nil then begin writeln('Unable to open ',filename,' for reading'); myexit( 1 ) end; } initvars; processfile; printlist; myexit(0) end.