[comp.os.vms] Bug in VAXTPU searching?

srwhmdr@windy.dsir.govt.nz (Malcolm Robbins) (07/21/88)

I've either found a bug in VAXTPU when it does a search for an alphabetic
character at the beginning of a line or, I lack some understanding about
pattern matching.

For example if I construct a TPU procedure to do a search for the following
pattern I find it occaisionally matches an empty line.

	LINE_BEGIN & ANY ('ABCDEFGHIJKLMNOPQRSTUVWXYZ')

In fact, given that a mismatch occurs, the problem can be restricted to
searching for

	LINE_BEGIN & ANY ('G')

say, (assuming that G is what is being matched by accident) and the same
thing occurs.

Furthermore if I cut the area affected and then repaste it in exactly the same
position, so no difference is visible, then the problem disappears!!

If, instead, I remove the ANY and search only for:

	LINE_BEGIN & ANY ('G')

VAXTPU behaves correctly (but this is not what I want to do of course).
Therefore the function ANY appears to be causing the problem.


What I'd like to know is:

  o  Is this problem caused by a lack of understanding on my part or is this
     a VAXTPU bug? Perhaps the mode of the search (Seek versus incremental)
     is causing the unexpected, but "valid" behaviour.

  o  How can I do what I intended, which is to search for an alphabetic
     character at the start of a line, in a reliable way?


For those of you who are raised to the challenge here's a sample TPU procedure
that shows up the problem. Simply compile it and execute it from the top of
a (largish) file with a number of empty lines in it. Hopefully at least one
empty line will match in the unexpected way.

Any replies would be greatly appreciated. In fact to show my appreciation
I'll post a "detab" procedure for removing TABs in a file to this newsgroup
shortly.

------------------------------ Cut Here ------------------------------
PROCEDURE any_bug

LOCAL   pat,
        rng;

pat := LINE_BEGIN &
       ANY ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');

LOOP
!
!   Ensure we move off current position for multiple calls to any_bug
!
    MOVE_HORIZONTAL (1);
    rng := SEARCH (pat, FORWARD, EXACT);
    EXITIF rng = 0;
!
!   If the search is successful move to it and see if an empty line has
!   been matched. This should not occur but does.
!
    IF rng <> 0 THEN
	POSITION (rng);
        this_char := CURRENT_CHARACTER;
    	IF this_char = "" THEN
            MESSAGE ('Here is an invalid match!');
	    RETURN;
    	ENDIF;
    ENDIF;
ENDLOOP;

MESSAGE ('No match found');
ENDPROCEDURE
------------------------------ Cut Here ------------------------------


+-----------------------------------------------------------------------------+
| Malcolm Robbins,                                                            |
| Department of Scientific and Industrial Research                            |
| New Zealand.                                                                |
|                                                                             |
| Internet: srwhmdr@wnv.dsir.govt.nz			      		      |
| Pacnet:   PSI%(05301)4600000060::SRWHMDR				      |
+-----------------------------------------------------------------------------+

srwhmdr@windy.dsir.govt.nz (Malcolm Robbins) (07/21/88)

In article <1711@windy.dsir.govt.nz>, srwhmdr@windy.dsir.govt.nz (Malcolm Robbins) writes:

> 
> What I'd like to know is:
> 
>   o  How can I do what I intended, which is to search for an alphabetic
>      character at the start of a line, in a reliable way?
> 
> Any replies would be greatly appreciated. In fact to show my appreciation
> I'll post a "detab" procedure for removing TABs in a file to this newsgroup
> shortly.

OK, here's the detab procedure, but you can have it only if you've considered
the problem I've been having :-).

Simply extract the SHAR file, @ it to extract DETAB.TPU. To merge it with EVE
load it, and enter EXTEND TPU. Now DETAB is an EVE command. Note that it works
no matter what the tab settings are. e.g. It works for 

	Command:  SET TABS EVERY 13
	Command:  SET TABS AT 10 50 55 60

etc.

...................... Cut between dotted lines and save. .....................
$!.............................................................................
$! VAX/VMS archive file created by VMS_SHARE V06.00 26-May-1988.
$!
$! VMS_SHARE was written by James Gray (Gray:OSBUSouth@Xerox.COM) from
$! VMS_SHAR by Michael Bednarek (U3369429@ucsvc.dn.mu.oz.au).
$!
$! To unpack, simply save, concatinate all parts into one file and
$! execute (@) that file.
$!
$! This archive was created by user SRWHMDR
$! on  6-APR-1866 20:07:23.78.
$!
$! It contains the following 1 file:
$!        DETAB.TPU
$!
$!==============================================================================
$ SET SYMBOL/SCOPE=( NOLOCAL, NOGLOBAL )
$ VERSION = F$GETSYI( "VERSION" )
$ IF VERSION .GES "V4.4" THEN GOTO VERSION_OK
$ WRITE SYS$OUTPUT "You are running VMS ''VERSION'; ", -
    "VMS_SHARE V06.00 26-May-1988 requires VMS V4.4 or higher."
$ EXIT 44 
$VERSION_OK:
$ GOTO START
$
$UNPACK_FILE:
$ WRITE SYS$OUTPUT "Creating ''FILE_IS'"
$ DEFINE/USER_MODE SYS$OUTPUT NL:
$ EDIT/TPU/COMMAND=SYS$INPUT/NODISPLAY/OUTPUT='FILE_IS'/NOSECTION -
    VMS_SHARE_DUMMY.DUMMY
b_part := CREATE_BUFFER( "{Part}", GET_INFO( COMMAND_LINE, "file_name" ) )
; s_file_spec := GET_INFO( COMMAND_LINE, "output_file" ); SET( OUTPUT_FILE
, b_part, s_file_spec ); b_errors := CREATE_BUFFER( "{Errors}" ); i_errors 
:= 0; pat_beg_1 := ANCHOR & "-+-+-+ Beginning"; pat_beg_2 := LINE_BEGIN 
& "+-+-+-+ Beginning"; pat_end := ANCHOR & "+-+-+-+-+ End"; POSITION
( BEGINNING_OF( b_part ) ); i_append_line := 0; LOOP EXITIF MARK( NONE 
) = END_OF( b_part ); s_x := ERASE_CHARACTER( 1 ); IF s_x = "+" THEN r_skip 
:= SEARCH( pat_beg_1, FORWARD, EXACT ); IF r_skip <> 0 THEN s_x := ""
; MOVE_HORIZONTAL( -CURRENT_OFFSET ); ERASE_LINE; ENDIF; ENDIF
; IF s_x = "-" THEN r_skip := SEARCH( pat_end, FORWARD, EXACT ); IF r_skip <
> 0 THEN s_x := ""; MOVE_HORIZONTAL( -CURRENT_OFFSET ); m_skip := MARK( NONE )
; r_skip := SEARCH( pat_beg_2, FORWARD, EXACT ); IF r_skip <> 0 THEN POSITION
( END_OF( r_skip ) ); MOVE_HORIZONTAL( -CURRENT_OFFSET ); MOVE_VERTICAL( 1 )
; MOVE_HORIZONTAL( -1 ); ELSE POSITION( END_OF( b_part ) ); ENDIF; ERASE
( CREATE_RANGE( m_skip, MARK( NONE ), NONE ) ); ENDIF; ENDIF
; IF s_x = "V" THEN s_x := ""; IF i_append_line <> 0 THEN APPEND_LINE
; MOVE_HORIZONTAL( -CURRENT_OFFSET ); ENDIF; i_append_line := 1; MOVE_VERTICAL
( 1 ); ENDIF; IF s_x = "X" THEN s_x := ""; IF i_append_line <
> 0 THEN APPEND_LINE; MOVE_HORIZONTAL( -CURRENT_OFFSET ); ENDIF
; i_append_line := 0; MOVE_VERTICAL( 1 ); ENDIF; IF s_x <> "" THEN i_errors 
:= i_errors + 1; s_text := CURRENT_LINE; POSITION( b_errors ); COPY_TEXT
( "The following line could not be unpacked properly:" ); SPLIT_LINE
; COPY_TEXT( s_x ); COPY_TEXT( s_text ); POSITION( b_part ); MOVE_VERTICAL( 1 
); ENDIF; ENDLOOP; POSITION( BEGINNING_OF( b_part ) ); LOOP r_x := SEARCH( "`"
, FORWARD, EXACT ); EXITIF r_x = 0; POSITION( r_x ); ERASE_CHARACTER( 1 )
; IF CURRENT_CHARACTER = "`" THEN MOVE_HORIZONTAL( 1 ); ELSE COPY_TEXT( ASCII
( INT( ERASE_CHARACTER( 3 ) ) ) ); ENDIF; ENDLOOP; IF i_errors = 0 THEN SET
( NO_WRITE, b_errors, ON ); ELSE POSITION( BEGINNING_OF( b_errors ) )
; COPY_TEXT( FAO( "The following !UL errors were detected while unpacking !AS"
, i_errors, s_file_spec ) ); SPLIT_LINE; SET( OUTPUT_FILE, b_errors
, "SYS$COMMAND" ); ENDIF; EXIT; 
$ DELETE VMS_SHARE_DUMMY.DUMMY;*
$ CHECKSUM 'FILE_IS
$ WRITE SYS$OUTPUT " CHECKSUM ", -
  F$ELEMENT( CHECKSUM_IS .EQ. CHECKSUM$CHECKSUM, ",", "failed!,passed." )
$ RETURN
$
$START:
$ FILE_IS = "DETAB.TPU"
$ CHECKSUM_IS = 1750754036
$ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY
X!
X! This procedure removes tabs and writes a message
X!
Xprocedure eve_detab
X
XLOCAL   num_tabs;
X
XMESSAGE ("Detabbing buffer...");
Xnum_tabs := xeve$detab;
XIF num_tabs = 0 THEN
X    MESSAGE ('No TABs in buffer')
XELSE
X    MESSAGE (FAO ('Converted !UL tab!%S', num_tabs))
XENDIF;
XENDPROCEDURE
X
X!
X! Boolean function which returns TRUE if there are tabs in the buffer.
X! This is extracted from the detab routine since it is called by the
X! rectangular block cut/paste procedures and the sort procedure.
X!
XPROCEDURE xeve$tabs_present
X
XLOCAL   my_tab;                 ! Range containing the tab character if found
X
X! disable messages
X!
XON_ERROR
XENDON_ERROR;
X
Xmy_tab := SEARCH (ASCII(9), FORWARD);
XIF my_tab = 0 THEN
X    my_tab := SEARCH (ASCII(9), REVERSE);
XENDIF;
X
XIF my_tab = 0 THEN
X    RETURN 0;
XELSE
X    RETURN 1;
XENDIF;
XENDPROCEDURE
X
X!
X! This procedure replaces TABs with spaces and returns a count of the
X! number of TABs replaced
X!
XPROCEDURE xeve$detab
X
XLOCAL   dest_column,            ! Column to insert spaces to
X        this_position,          ! Save current position
X        count,                  ! A count of the number of tabs converted
X        timer_string,
X        my_tab;                 ! Range containing the tab character if found
X
XON_ERROR
XENDON_ERROR;
X
XIF NOT xeve$tabs_present THEN
X    RETURN 0;
XENDIF;
X
Xtimer_string := GET_INFO (SYSTEM, 'timed_message');
XSET (TIMER, ON, "Detabing...");
Xthis_position := MARK (NONE);
XPOSITION (BEGINNING_OF (CURRENT_BUFFER));
Xcount := 0;
Xtabs := GET_INFO (CURRENT_BUFFER, "tab_stops");
X
XLOOP
X    my_tab := SEARCH (ASCII (9), FORWARD);
X    EXITIF my_tab = 0;
X    count := count + 1;
X    POSITION (my_tab);
X    ERASE (my_tab);
X!
X!   If the tabs stops are set every n characters then tabs is an integer
X!   and expanding is easy. If tabs are set at differing positions the problem
X!   is a little more complicated
X!
X    IF GET_INFO (tabs, "type") = INTEGER THEN
X        dest_column := (CURRENT_OFFSET / tabs + 1) * tabs + 1;
X    ELSE
X        dest_column := xeve$get_offset (tabs, CURRENT_OFFSET);
X    ENDIF;
X    eve$to_column (dest_column)
XENDLOOP;
X
XPOSITION (this_position);
XSET (TIMER, ON, timer_string);
XRETURN count;
XENDPROCEDURE
X
X!
X! This function returns the column position of the next tab stop from
X! the current position. i.e. "tabs" contains the tabs stop string and
X! offset contains the current position.
X! This function is used within xeve$detab so it can replace tabs with
X! the right number of spaces.
X!
XPROCEDURE xeve$get_offset (tabs, offset)
X
XLOCAL   end_pos,
X        tab_string,
X        local_tabs,
X        num;
X
Xlocal_tabs := tabs;
XLOOP
X    end_pos := INDEX (local_tabs, ' ');
X    IF end_pos = 0 THEN
X        end_pos := LENGTH (local_tabs) + 1
X    ENDIF;
X    tab_string := SUBSTR (local_tabs, 1, end_pos - 1);
X    num := INT (tab_string);
X    EXITIF num > offset;
X    local_tabs := SUBSTR (local_tabs, end_pos + 1, LENGTH (local_tabs));
X    IF local_tabs = '' THEN
X        RETURN offset + 2
X    ENDIF;
XENDLOOP;
X
XRETURN num;
XENDPROCEDURE
X
X
XDEFINE_KEY ("eve_detab", KEY_NAME ("D", SHIFT_KEY));
$ GOSUB UNPACK_FILE
$ EXIT

+-----------------------------------------------------------------------------+
| Malcolm Robbins,                                                            |
| Department of Scientific and Industrial Research                            |
| New Zealand.                                                                |
|                                                                             |
| Internet: srwhmdr@wnv.dsir.govt.nz			      		      |
| Pacnet:   PSI%(05301)4600000060::SRWHMDR				      |
+-----------------------------------------------------------------------------+