🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: net.micro.atari16
Article #2419

(none)

#2419
From: Eric Terrell
Date: Wed, 15 Oct 1986 01:09
975 lines
23030 bytes
program spell;

const
  {$i gemconst}

  { length of largest possible word }
  max_word_length    =             30;

  { number of entries in the hash table }
  table_size         =            1000;

  small_length       =               4;
  medium_length      =               8;
  large_length       =              10;
  largest_length     = max_word_length;
  name_of_dictionary =     'words.txt';
  temp_dict_name     =     'words.xxx';

  { Color of text }
  color              =           $1180;

type
  {$i gemtype}

  word_string      = string[max_word_length];
  string_index     = 1..max_word_length;
  hash_index       = 1..table_size;

  length_type      = (small, medium, large, largest);

  small_string     = string[small_length];
  medium_string    = string[medium_length];
  large_string     = string[large_length];
  largest_string   = string[largest_length];

  { pointer to a small, medium, large, or largest string }
  word_pointer     = record

                       case tag_field : length_type of

                         small   : (small_ptr   : ^small_string);
                         medium  : (medium_ptr  : ^medium_string);
                         large   : (large_ptr   : ^large_string);
                         largest : (largest_ptr : ^largest_string);

                     end;

  list             = ^cell;

  cell             = record

                         word : word_pointer;
                         next : list;

                       end;

  hash_table       = array[hash_index] of list;

  word_list        = record

                       words   : hash_table;
                       changed : boolean;

                     end;

  char_file        = packed file of char;

var
  { memory-resident dictionary }
  dictionary,

  {
  list of words that are spelled correctly but will not be inserted into
  a dictionary.
  }
  secondary_dict   : word_list;

  done,
  cancel,
  out_of_memory    : boolean;
  word             : word_string;
  in_file_name,
  out_file_name,
  in_file_type     : string;
  spell_file,
  spelled_file,
  new_spelled_file : char_file;
  next_char        : char;
  button           : integer;


{$i gemsubs}


procedure io_check(switch : boolean); external;
function io_result : integer; external;


function file_exists(filename : string) : boolean;

{
Return true if the file specified by filename exists on the default disk.
}

const
  { File operation was successfull }
  no_error = 0;

var
  input_file : char_file;

begin

  { Turn off i/o checking. }
  io_check(false);

  { Attempt to open the named file. }
  reset(input_file, filename);

  { If there was no error opening the file, it is assumed to exist. }
  file_exists := io_result = no_error;

  close(input_file);

  { Turn i/o checking back on. }
  io_check(true);

end;


procedure remove_white(var str : string);

{
Remove all whitespace characters from a character string.  Whitespace
characters are assumed to have character codes less than or equal to
the code for space (' ').
}

var
  new_string : string;
  index      : integer;

begin

  new_string := '';

  for index := 1 to length(str) do
    if str[index] > ' '
      then new_string := concat(new_string, str[index]);

  str := new_string;

end;


procedure read_char(var input_file : char_file; var ch : char);

{
Read a character from a file.
}

begin

  ch := input_file^;
  get(input_file);

end;


procedure write_char(var output_file : char_file; ch : char);

{
Write a character to a file.
}

begin

  output_file^ := ch;
  put(output_file);

end;


procedure write_word(var output_file : char_file; word : word_string);

{
Write a character string to a file.
}

var
  index : string_index;

begin

  for index := 1 to length(word) do
    write_char(output_file, word[index]);

end;


procedure get_word(var input_file, output_file : char_file;
             var word : word_string; var next_char : char);

{
Retrieve the next word from the input file.  When the file is completely read,
(word) will be the empty string.
}

var
  ch         : char;
  letters,
  apostrophe : set of char;

begin

  word := '';

  letters := ['a'..'z', 'A'..'Z'];
  apostrophe := [''''];

  if not eof(input_file)
    then begin

      { Get the initial letter of the word. }
      repeat

        read_char(input_file, ch);

        { Send all letters not in the word to the output file. }
        if not (ch in letters)
          then write_char(output_file, ch);

      until (ch in letters) or eof(input_file);

      { If the initial letter of the word was found, get the other letters. }
      if ch in letters
        then begin

          word := ch;

          repeat

            read_char(input_file, ch);

            if ch in letters + apostrophe
              then word := concat(word, ch)
              else { Save character following word to write to output file. }
                next_char := ch;

          until (not (ch in letters + apostrophe)) or eof(input_file) or
                (length(word) = max_word_length);

        end;

    end;

end;


function min(a, b : integer) : integer;

{
Return the smaller of a and b.
}

begin

  if a < b
    then min := a
    else min := b;

end;


function capital(ch : char) : char;

{
Return the capital of a lowercase letter.  Return the original
letter otherwise.
}

begin

  if ch in ['a'..'z']
    then capital := chr(ord(ch) - (ord('a') - ord('A')))
    else capital := ch;

end;


function hash(word : word_string) : hash_index;

{
Return an index into the hash table based on a key (word).
}

var
  index       : string_index;
  number      : long_integer;
  power_of_10 : integer;

begin

  { Convert the key to a number. }
  number := 0;
  power_of_10 := 1;

  for index := min(4, length(word)) downto 1 do begin

    number := number + power_of_10 * ord(capital(word[index]));
    power_of_10 := power_of_10 * 10;

  end;

  { Compute a valid hash index. }
  hash := int(number mod table_size) + 1;

end;


procedure get_word_from_dictionary(var word : word_string;
                                        word_cell : list);

{
Retrieve a word from a cell of the hash table.
}

begin

  case word_cell^.word.tag_field of

    small   : word := word_cell^.word.small_ptr^;
    medium  : word := word_cell^.word.medium_ptr^;
    large   : word := word_cell^.word.large_ptr^;
    largest : word := word_cell^.word.largest_ptr^;

  end;

end;


procedure word_capitalize(var word : word_string);

{
Make all alphabetic characters of a word upper-case.
}

var
  index : string_index;

begin

  for index := 1 to length(word) do
    word[index] := capital(word[index]);

end;


function words_equal(word_1, word_2 : word_string) : boolean;

{
Return true if two words are equal (alphabetic case of characters is
ignored.
}

begin

  if length(word_1) <> length(word_2)
    then words_equal := false
    else begin

      word_capitalize(word_1);
      word_capitalize(word_2);

      words_equal := word_1 = word_2;

    end;

end;


function word_in_dictionary(word : word_string; dictionary : word_list) :
                                                                 boolean;

{
Return true when a word is found in the dictionary.
}

var
  index          : hash_index;
  retrieved_word : word_string;
  word_found     : boolean;

begin

  index := hash(word);

  { Single letters will be considered valid words. }
  word_found := length(word) = 1;

  { Search for the word in the hash table. }
  while (dictionary.words[index] <> nil) and (not word_found) do begin

    get_word_from_dictionary(retrieved_word, dictionary.words[index]);
    word_found := words_equal(word, retrieved_word);

    dictionary.words[index] := dictionary.words[index]^.next;

  end;

  word_in_dictionary := word_found;

end;


procedure insert_word(word : word_string; var dictionary : word_list;
                                        var out_of_memory : boolean);

{
Insert a word into the dictionary if it is not already there.
}

const
  min_free_memory = 1024;

var
  index     : hash_index;
  word_cell : word_pointer;
  new_cell  : list;


  procedure get_word_cell(word : word_string; var word_cell : word_pointer);

  {
  Return a pointer to a word.
  }

  begin

    {
    Depending on the length of the word, allocate the proper amount of
    memory.
    }

    if length(word) in [1..small_length]
      then begin

        word_cell.tag_field := small;
        new(word_cell.small_ptr);
        word_cell.small_ptr^ := word;

      end;

    if length(word) in [small_length + 1 .. medium_length]
      then begin

        word_cell.tag_field := medium;
        new(word_cell.medium_ptr);
        word_cell.medium_ptr^ := word;

      end;

    if length(word) in [medium_length + 1 .. large_length]
      then begin

        word_cell.tag_field := large;
        new(word_cell.large_ptr);
        word_cell.large_ptr^ := word;

      end;

    if length(word) in [large_length + 1 .. largest_length]
      then begin

        word_cell.tag_field := largest;
        new(word_cell.largest_ptr);
        word_cell.largest_ptr^ := word;

      end;

  end;


begin

  { Find out if memory is getting low. }
  out_of_memory := memavail < min_free_memory;

  if (not out_of_memory) and (not word_in_dictionary(word, dictionary)) and
     (length(word) in [1..largest_length])
    then begin

      { Get a pointer to a cell containing the word. }
      get_word_cell(word, word_cell);

      { Insert the word into the hash table. }
      new(new_cell);
      new_cell^.word := word_cell;

      index := hash(word);

      new_cell^.next := dictionary.words[index];
      dictionary.words[index] := new_cell;

    end;

end;


procedure clear_dictionary(var dictionary : word_list);

{
Initialize the dictionary to empty.
}

var
  index : hash_index;

begin

  for index := 1 to table_size do
    dictionary.words[index] := nil;

  dictionary.changed := false;

end;


procedure load_dictionary(var dictionary : word_list;
                        var out_of_memory : boolean);

{
Load the dictionary with words from a disk file.
}

var
  word_file : text;
  word      : word_string;

begin

  { Make mouse a "busy bee". }
  set_mouse(m_bee);

  { Load the dictionary into memory. }

  { Make the dictionary empty. }
  clear_dictionary(dictionary);

  { If the dictionary disk file does not exist, create a new (empty) one. }
  if not file_exists(name_of_dictionary)
    then begin

      rewrite(word_file, name_of_dictionary);
      close(word_file);

    end;

  { Prepare to read words from the disk file. }
  reset(word_file, name_of_dictionary);

  out_of_memory := false;

  { Insert each word in the disk file into the dictionary. }

  while (not eof(word_file)) and (not out_of_memory) do begin

    readln(word_file, word);
    insert_word(word, dictionary, out_of_memory);

  end;

  { Make the mouse an arrow again. }
  set_mouse(m_arrow);

end;


procedure update_dictionary(dictionary : word_list);

{
Save all dictionary words in a disk file if the dictionary has changed.
}

var
  index         : hash_index;
  word          : word_string;
  word_file,
  new_word_file : text;
  button        : integer;

begin

  if dictionary.changed
    then begin

      { Find out if user wants to update the dictionary stored on disk. }
      button := do_alert('[0][Update Dictionary?][YES|NO]', 1);

      if button = 1
        then begin

          { Make the mouse a "busy bee." }
          set_mouse(m_bee);

          { Save dictionary with a temporary filename. }
          rewrite(word_file, temp_dict_name);

          { Save all words stored in each list of the hash table. }
          for index := 1 to table_size do begin

            { Save all words stored in current list of the hash table. }
            while dictionary.words[index] <> nil do begin

              get_word_from_dictionary(word, dictionary.words[index]);
              writeln(word_file, word);

              { Advance to next cell of current list. }
              dictionary.words[index] := dictionary.words[index]^.next;

            end;

          end;

          { Replace old dictionary with new dictionary. }
          rewrite(new_word_file, name_of_dictionary);
          rename(word_file, new_word_file);

          { Return mouse to arrow shape. }
          set_mouse(m_arrow);

        end;

    end;

end;


procedure process_word(var word : word_string; var dictionary,
     secondary_dict : word_list; var out_of_memory : boolean);

{
Process a misspelled word: either

i.   place word in dictionary
ii.  change spelling of word
iii. don't change word
}

var
  insert_wd : boolean;


  procedure get_option(var word : word_string; var insert_wd : boolean);

  {
  Allow user to correct the spelling of a possibly misspelled word and insert
  it into the dictionary.
  }

  const
    not_in_dict  = 'is not in the dictionary.';
    question     = 'Add this word to dictionary?';

    { Width (in characters) of dialog box }
    box_width    = 40;

  var
    word_line,
    line_2,
    line_3,
    button_pushed,
    no_button,
    yes_button     : integer;
    option_box     : dialog_ptr;
    word_string    : str255;

  begin

    { Get a dialog box. }
    option_box := new_dialog(5, 0, 0, box_width, 7);

    { Place possibly misspelled word in the dialog box. }
    word_line := add_ditem(option_box, g_ftext, editable, 1, 1, 30, 1, 0,
                                                                  color);

    line_2 := add_ditem(option_box, g_text, none, 1, 2, length(not_in_dict),
                                                               1, 0, color);

    line_3 := add_ditem(option_box, g_text, none, 1, 3, length(question),
                                                            1, 0, color);

    { Place YES and NO buttons in the dialog box. }
    yes_button := add_ditem(option_box, g_button, exit_btn | selectable, 10, 5,
                                                   length('YES'), 1, 0, color);

    no_button := add_ditem(option_box, g_button, exit_btn | selectable,
                                     28, 5, length('NO'), 1, 0, color);

    { Prepare to allow user to correct the spelling of the word. }
    set_dedit(option_box, word_line, '______________________________',
                                     'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
                                          word, system_font, te_left);

    set_dtext(option_box, line_2, not_in_dict, system_font, te_center);
    set_dtext(option_box, line_3, question, system_font, te_center);

    { Prepare to have the YES or NO buttons pressed. }
    set_dtext(option_box, yes_button, 'YES', system_font, te_center);
    set_dtext(option_box, no_button, 'NO', system_font, te_center);

    center_dialog(option_box);

    { Get user input from dialog box. }
    button_pushed := do_dialog(option_box, word_line);

    { Retrieve the possibly edited word. }
    get_dedit(option_box, word_line, word_string);
    word := word_string;

    insert_wd := button_pushed = yes_button;

    end_dialog(option_box);
    delete_dialog(option_box);

  end;


begin

  {
  Allow user to correct spelling of word, and have him decide if it
  will be inserted into the dictionary.
  }

  get_option(word, insert_wd);

  {
  If the word is to be inserted into the dictionary, do so.  Otherwise
  insert the word into the secondary dictionary so that the user will
  not be asked about the word a second time.
  }
  if insert_wd
    then begin

      insert_word(word, dictionary, out_of_memory);
      dictionary.changed := true;

    end
    else insert_word(word, secondary_dict, out_of_memory);

end;


procedure get_input_filename(var in_file_name, in_file_type : string;
                                               var cancel : boolean);

{
Get the name the file to be corrected.  If cancel is true after this
procedure is called, the program is exited.
}

const
  fl_type_length = 3;

var
  button   : integer;
  path,
  filename : string;

begin

  { Default path for filename selection }
  path := 'A:\*.DOC';

  {
  Place file selection box on screen and allow user to choose a file or
  to press the cancel button.  Repeat until user enters a valid filename
  or presses the cancel button.
  }

  repeat

    cancel := not get_in_file(path, filename);

    {
    If the requested file does not exist, have the user choose an existing
    file or quit the program.
    }

    if (not cancel) and (not file_exists(filename))
      then begin

        button := do_alert('[2][Not found-Try again?][YES|NO]', 1);
        cancel := button = 2;

      end;

  until cancel or file_exists(filename);

  {
  If the filename of an existing filename has been given, extract
  the name and type of the file.
  }

  if not cancel
    then begin

      if pos('.', filename) <> 0
        then begin

          in_file_type := copy(filename, pos('.', filename) + 1,
                         length(filename) - pos('.', filename));
          in_file_name := copy(filename, 1, pos('.', filename) - 1);

        end
        else begin

          in_file_type := '';
          in_file_name := filename;

        end;

      remove_white(in_file_name);
      remove_white(in_file_type);

    end;

end;


procedure introduce_program;

{
Introduce the program with a dialog box.
}

const
  { Width (in characters) of dialog box }
  box_width = 64;

  { Strings that will be inserted into dialog box. }
  str_1     = 'Spell 1.3 - A Spelling Correction Program';
  str_2     = 'Written by Eric Bergman-Terrell';
  str_3     = 'of Cadenza Software, Ltd.';
  str_4     = '1704 Imperial Ridge, Las Cruces, NM  88001, USA';
  str_5     = 'Portions of this product are copyright (c) 1986, OSS and CCD';
  str_6     = 'Used by Permission of OSS';
  str_7     = 'This software has been placed in the public domain.';
  start_str = 'BEGIN';

var
  intro_box     : dialog_ptr;
  line_1,
  line_2,
  line_3,
  line_4,
  line_5,
  line_6,
  line_7,
  start_button,
  button_pushed : integer;
  start_item    : tree_index;

begin

  { Set up the mouse the be an arrow. }
  init_mouse;
  set_mouse(m_arrow);

  { Get a dialog box. }
  intro_box := new_dialog(8, 0, 0, box_width, 16);

  { Insert strings into dialog box. }
  line_1 := add_ditem(intro_box, g_text, none, 1, 1, box_width, 1, 0, color);
  line_2 := add_ditem(intro_box, g_text, none, 1, 3, box_width, 1, 0, color);
  line_3 := add_ditem(intro_box, g_text, none, 1, 4, box_width, 1, 0, color);
  line_4 := add_ditem(intro_box, g_text, none, 1, 5, box_width, 1, 0, color);
  line_5 := add_ditem(intro_box, g_text, none, 1, 7, box_width, 1, 0, color);
  line_6 := add_ditem(intro_box, g_text, none, 1, 8, box_width, 1, 0, color);
  line_7 := add_ditem(intro_box, g_text, none, 1, 11, box_width, 1, 0, color);
  start_button := add_ditem(intro_box, g_button,
                            exit_btn | selectable | default,
                            30, 14, length(start_str), 1, 0, color);

  { Adjust the strings in the dialog box. }
  set_dtext(intro_box, line_1, str_1, system_font, te_center);
  set_dtext(intro_box, line_2, str_2, system_font, te_center);
  set_dtext(intro_box, line_3, str_3, system_font, te_center);
  set_dtext(intro_box, line_4, str_4, system_font, te_center);
  set_dtext(intro_box, line_5, str_5, system_font, te_center);
  set_dtext(intro_box, line_6, str_6, system_font, te_center);
  set_dtext(intro_box, line_7, str_7, system_font, te_center);
  set_dtext(intro_box, start_button, start_str, system_font, te_center);

  center_dialog(intro_box);

  { Introduce the program. }
  button_pushed := do_dialog(intro_box, start_item);

  end_dialog(intro_box);
  delete_dialog(intro_box);

end;


begin

  if init_gem >= 0
    then begin

      introduce_program;

      load_dictionary(dictionary, out_of_memory);

      {
      Initialize list of correctly spelled words that will not be
      inserted into a dictionary.
      }
      clear_dictionary(secondary_dict);

      if not out_of_memory
        then begin

          get_input_filename(in_file_name, in_file_type, cancel);

          if not cancel
            then begin

              out_file_name := concat(in_file_name, '.XXX');

              reset(spell_file, concat(in_file_name, '.', in_file_type));
              rewrite(spelled_file, out_file_name);

              repeat

                get_word(spell_file, spelled_file, word, next_char);

                done := length(word) = 0;

                if not done
                  then begin

                    if (not word_in_dictionary(word, dictionary)) and
                       (not word_in_dictionary(word, secondary_dict))
                      then process_word(word, dictionary, secondary_dict,
                                                          out_of_memory);

                    write_word(spelled_file, word);
                    write_char(spelled_file, next_char);

                end;

              until done or out_of_memory;

              close(spell_file);

              { Replace old input file with file that has been corrected. }
              rewrite(new_spelled_file, concat(in_file_name, '.',
                                                  in_file_type));
              rename(spelled_file, new_spelled_file);

              { Update dictionary if it has changed. }
              update_dictionary(dictionary);

            end;

        end;

      if out_of_memory
        then button := do_alert('[1][Out of Memory][CANCEL]', 1);

    end;

end.
Technical Details
Message-ID: <8610150109.AA11838@ohio-state.ARPA>
Path: arc1-ca.pugleaf.net!utzoo!watmath!clyde!caip!think!nike!ucbcad!ucbvax!OHIO-STATE.ARPA!terrell

Show Full Headers
Complete Article Headers
Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: arc1-ca.pugleaf.net!utzoo!watmath!clyde!caip!think!nike!ucbcad!ucbvax!OHIO-STATE.ARPA!terrell
From: terrell@OHIO-STATE.ARPA (Eric Terrell)
Newsgroups: net.micro.atari16
Subject: (none)
Message-ID: <8610150109.AA11838@ohio-state.ARPA>
Date: Tue, 14-Oct-86 21:09:55 EDT
Article-I.D.: ohio-sta.8610150109.AA11838
Posted: Tue Oct 14 21:09:55 1986
Date-Received: Mon, 20-Oct-86 22:24:14 EDT
Sender: daemon@ucbvax.BERKELEY.EDU
Organization: The ARPA Internet
Lines: 975