$Search 'KRMWNDW', 'KRMRPT'$ $ucsd on$ module command; import windowlib, err_codes, krmrpt; export const text_string_size = 255; MAXKEYWORDS = 20; required = false; { arguments for parse, tell if arg is optional } optional = true; type breakset_type = set of char; arg_type = (p_char, p_integer, p_text, p_eol, p_boolean, p_password, p_keyword); text_string = string [text_string_size]; keyword_string_type = string [20]; keyword_entry = record ks : keyword_string_type; kv : integer; end; { record } keyword_table = array[1..MAXKEYWORDS] of keyword_entry; keyword_table_ptr = ^keyword_table; var parse_keyword_table : keyword_table_ptr; parse_result : integer; { result of last parse } parse_result_str : text_string; { These are the argument buffers. There is one buffer for each type of argument. } arg_char : char; arg_integer : integer; { holds integers } arg_keyword : keyword_string_type; { holds full keyword text of last parsed keyword } arg_text : text_string; { holds text, keywords, passwords } arg_boolean : boolean; procedure parse_init ( var prompt : string ); procedure parse( arg : arg_type ; opt : boolean ); $page$ implement var eol_parsed : boolean; { cleared by parse_init, set by parse } cur_bufpos : integer; { position of next char to be put in buffer } init_bufpos : integer; { position of first char of this token } parse_buffer : string [80]; function read_kbd_char : char; { Reads a char from the keyboard (non-echoing). If a carriage return is typed, returns a control M (#M). } var c : char; begin if eoln(keyboard) then begin readln(keyboard); c := #13; { carriage return } end else read(keyboard,c); read_kbd_char := c; end; { function read_kbd_char } $page$ { read_break Reads from the terminal until one of a specified set of characters is read. The break character that terminated the read is placed in breakchar. Inputs : buffer Buffer used to accumulate actual characters typed on keyboard, including prompt and break characters init_bufpos Initial position in buffer in which to store the next character read from the keyboard. Will be updated to point to next char. after current input. atom String in which to return the token read (without break characters) breakset Set of characters which, when typed, signal that the token has been completed and that it should now be parsed breakchar Receives the break character actually read echo If true, characters read will be echoed to the screen; if false, they will not be echoed. Returns : Result code, one of the following: success The field was successfully read back_past_field The user backed up past the beginning of this field abort_line The user aborted the line by typing CTRL-U null_string The user typed only a break character } function read_break( var buffer : string; init_bufpos : integer; var cur_bufpos : integer; var atom : string; breakset : breakset_type; var breakchar : char; echo : boolean ) : integer; var c : char; done : boolean; result : integer; bufpos : integer; begin result := success; done := false; bufpos := cur_bufpos; {setstrlen(atom,0);} repeat c := read_kbd_char; case c of #H,#127: begin { backspace or delete } if bufpos > init_bufpos then begin { delete the character } bufpos := bufpos-1; setstrlen( buffer, strlen(buffer)-1); setstrlen( atom, strlen(atom)-1); write_window_char(command_window,#127); end { delete the character } else begin { backing up past beginning of field } write(#7); { beep } result := back_past_field; done := true; end; { backing up past field } end; { backspace or delete } #U: begin { control-U } done := true; result := abort_line; end; { control-U } #R: begin { control-R } end; { control-R } otherwise begin { c is not an editing char } if c >= #32 then begin { if c is printable } setstrlen(buffer,strlen(buffer)+1); buffer[bufpos] := c; bufpos := bufpos + 1; if echo then write_window_char(command_window, c); end; { if c is printable } if not (c in breakset) then if c >= #32 then begin { c is printable } setstrlen(atom,strlen(atom)+1); atom[strlen(atom)] := c; end { c is printable } else begin { c is not printable } write(#7); { beep } end { c is not printable } else begin { c is a break char } breakchar := c; if strlen(atom) <> 0 then result := success else result := null_string; done := true; end; { c is a break char } end; { c is not an editing char } end; { case } until done; read_break := result; cur_bufpos := bufpos; end; { procedure read_break } $page$ function stoi( var s : string ; var i : integer ) : integer; { Converts string to integer. Inputs : s string containing decimal digits to convert i integer to receive the converted value if successful Returns : Status code, one of the following: success Integer converted successfully non_digit Non-digit character encountered overflow Integer overflow null_string Null string given as argument } var e, j, digit : integer; c : char; result : integer; begin result := success; e := 1; i := 0; j := strlen(s); if j = 0 then result := null_string; while (j <> 0) and (result = success) do begin c := s[j]; digit := ord(c) - ord('0'); if (digit < 0) or (digit > 9) then result := non_digit else begin i := i + e*digit; e := e * 10; j := j - 1; end; end; { while } stoi := result; end; { procedure stoi } { Function match returns true if the string test is a valid abbreviation for the string keyword. } function match (var word : string; var keyword : string) : boolean; var result : boolean; j : integer; c : char; begin result := true; if strlen(word) > strlen(keyword) then result := false else begin { could still be abbreviation } j := 1; while (j <= strlen(word)) and (result = true) do begin c := word[j]; { get character from test string } if c >= 'a' then c := chr( ord(c) - ord(' ') ); { uppercase it } if c <> keyword[j] then result := false; j := j+1; end; { while } end; { could still be abbreviation } match := result; end; { function match } $page$ function lookup_key( table : keyword_table; var word : string; var value : integer; var full_word : string ) : integer; { Searches the given keyword table for an entry that matches the given keyword. Inputs : table - keyword table, which is array of records of type keyword_entry. These records consist of the keyword string itself and the integer value assigned to the keyword. word - keyword string to search for. Outputs : value - If a match for the keyword is found, value receives the integer value assigned to the keyword, found in the keyword's record. full_word - if a match for the keyword is found, full_word receives the full keyword text. For example, if the word 'FO' matched the keyword 'FORMS' then full_word would receive 'FORMS'. Returns: Result code, one of success match found for keyword, value contains the keyword's assigned integer value. ambig_keyword given keyword matched more than one table entry no_keyword No table entry matched the given keyword. } var i : integer; { keyword position in table } result : integer; begin i := 1; { point to first keyword in table } result := no_keyword; while (result <> ambig_keyword) and (strlen(table[i].ks) <> 0) do begin if match(word, table[i].ks) then begin { this keyword matches } if result = success then result := ambig_keyword { already found match } else begin { this is first match yet } value := table[i].kv; full_word := table[i].ks; result := success; end; { this is first match yet } end; { this keyword matches } i := i + 1; end; { while } lookup_key := result; end; { procedure lookup_key } $page$ procedure parse_init ( var prompt : string ); begin clear_window(command_window); clear_window(help_window); write_window_string(command_window, prompt); clear_eol_window(command_window); parse_buffer := prompt; init_bufpos := strlen(prompt) + 1; cur_bufpos := init_bufpos; eol_parsed := false; end; { procedure parse_init } $page$ { This procedure, parse, reads an argument of the given type from the command input device (usually the console) and leaves it in the buffer corresponding to that type (there is a buffer for each type of argument). If the argument is optional, as indicated by the second parameter (named optional) being true, then the argument may or may not be given by the user. If it is not, the corresponding buffer will remain unchanged. This allows default values to be set by the set_p_xxx procedures. The value in the buffer may be read by the get_p_xxx functions. Error code will be left in parse_result. A string with an parse error message and the atom causing the error will be left in parse_result_str. } procedure parse( arg : arg_type ; opt : boolean ); label 200,1000; var breakchar : char; read_result : integer; atom, report, title, kwd : string [80]; echo : boolean; added_keyword, kwd_match : boolean; breakset : breakset_type; rpos, i : integer; bk : keyword_table_ptr; { boolean TRUE/FALSE keyword table } procedure do_tab( var s : string ); var pos : integer; begin pos := strlen(s); repeat pos := pos + 1; setstrlen(s,pos); s[pos] := ' '; until pos mod 8 = 0; end; { procedure do_tab } begin parse_result := success; { assume success for now } atom := ''; cur_bufpos := init_bufpos; if arg = p_eol then begin { parsing for EOL } if not eol_parsed then parse_result := not_confirmed; goto 1000; end else { not parsing for EOL } if eol_parsed then begin if not opt then parse_result := parse_after_eol; goto 1000; end; if arg = p_password then echo := false else echo := true; 200: if arg in [p_text, p_integer, p_boolean, p_password, p_keyword] then begin { arg needs a string } if arg = p_text then breakset := ['?', #M] else breakset := ['?', ' ', ',', #M]; read_result := read_break ( parse_buffer, init_bufpos, cur_bufpos, atom, breakset, breakchar, echo ); case read_result of success: begin if breakchar = #M then eol_parsed := true; end; back_past_field: begin parse_result := back_past_field; goto 1000; end; abort_line: begin parse_result := abort_line; goto 1000; end; null_string: begin if breakchar <> '?' then begin parse_result := null_string; goto 1000; end; end; end; { case } end; { arg needs a string } case arg of p_char : begin arg_char := read_kbd_char; end; { p_char } p_integer : begin parse_result := stoi( atom, arg_integer ); end; { p_integer } p_text : begin arg_text := atom; end; { p_text } p_boolean : begin new(bk); bk^[1].ks := 'FALSE'; bk^[1].kv := 0; bk^[2].ks := 'TRUE'; bk^[2].kv := 1; bk^[3].ks := ''; bk^[3].kv := 0; parse_result := lookup_key( bk^, atom, arg_integer, arg_keyword ); arg_boolean := (arg_integer = 1); end; { p_boolean } p_password : begin arg_text := atom; end; { p_password } { Parse a keyword. See if the given string matches any of the entries in parse_keyword_table. } p_keyword : begin if breakchar = '?' then begin { help character typed } clear_window( help_window ); i := 1; setstrlen(report,0); added_keyword := false; repeat kwd := parse_keyword_table^[i].ks; if (strlen(atom) = 0) then kwd_match := true else kwd_match := match(atom,kwd); if (strlen(kwd) <> 0) and kwd_match then begin { add keyword to output string } do_tab(report); rpos := strlen(report)+1; strwrite(report,rpos,rpos,kwd); if strlen(kwd) >=7 then do_tab(report); if not added_keyword { if haven't printed title yet } then begin { print title } title := 'Keyword, one of the following:'; writeln_window_string(help_window, title); added_keyword := true; end; { print title } end; { add keyword to output string } if (strlen(report) > 64) or (strlen(kwd) = 0) then begin { print the accumulated keyword list } writeln_window_string(help_window,report); setstrlen(report,0); rpos := 1; end; { print the accumulated keyword list } i := i+1; until strlen(kwd) = 0; if not added_keyword { if no keywords in list } then begin { print no match msg } title := 'Keyword (no defined keywords match this input)'; writeln_window_string(help_window,title); end; { print no match msg } { remove the break character from the input buffer } setstrlen(parse_buffer, strlen(parse_buffer)-1); cur_bufpos := cur_bufpos - 1; clear_window(command_window); write_window_string(command_window,parse_buffer); goto 200; end { help character typed } else begin { parse the keyword } parse_result := lookup_key( parse_keyword_table^, atom, arg_integer, arg_keyword ); arg_text := atom; end; { parse the keyword } end; { p_keyword } end; { case } 1000: init_bufpos := cur_bufpos; if not (parse_result in [success, abort_line, back_past_field, null_string]) then begin { set up parse error string } setstrlen(parse_result_str,0); strwrite(parse_result_str,1,rpos,'parsing "',atom,'"' ); end; { set up parse error string } end; { procedure parse } end. { module command }