Google
 

Trailing-Edge - PDP-10 Archives - DEC_CMS-20_V1.0_SRC - cms/sources/ioserv.bli
There are no other files named ioserv.bli in the archive.
module ioserv (	! Input and output service functions
		ident = '1',
		%if
		    %bliss(bliss32)
		%then
		    language(bliss32),
		    addressing_mode(external=long_relative,
				    nonexternal=long_relative)
		%else
		    language(bliss36)
		%fi
		) =
begin

!
!			  COPYRIGHT (C) 1982 BY
!	      DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
!
! THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
! ONLY  IN  ACCORDANCE  WITH  THE  TERMS  OF  SUCH  LICENSE AND WITH THE
! INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE OR  ANY  OTHER
! COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
! OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
! TRANSFERRED.
!
! THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE  WITHOUT  NOTICE
! AND  SHOULD  NOT  BE  CONSTRUED  AS  A COMMITMENT BY DIGITAL EQUIPMENT
! CORPORATION.
!
! DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR  RELIABILITY  OF  ITS
! SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.
!

!++
! Facility:  CMS Library Processor
!
! Abstract:
!
!	This module provides miscellaneous input and output service functions.
!
! Environment:
!
! Author:  Earl Van Horn	Creation Date:  March 5, 1980
!
!--
!
! Table of Contents:
!
forward routine
    filter,		! Detect noise characters.
    find_next_words,	! Breaks a string into terminal lines.
    writes,		! Write a multi-line string to any stream.
    twidth: novalue,	! Return width of terminal, default value or from SWIDTH
    swidth,		! Set term width used by other routines in module
    yes ;		! Get a yes or no answer.

!
! Include Files:
!

library 'XPORT:' ;

%if %bliss(bliss32) %then
	LIBRARY 'sys$library:starlet';
%else
	REQUIRE 'jsys:';
%fi

require 'BLISSX:' ;
require 'condit:';

!
! Macros:
!

!
! Equated Symbols:
!
literal
    k_max_terminal_width = 150 ;	! Buffer size for terminal lines.

!
! Own Storage:
!

own
    terminal_width : initial(80) ;	! Number of chars. in terminal line.

!
! External References:
!

external literal
	s_enteryn,		!enter YES or NO
	s_meansno,		!means NO
	s_speccherr;		!char was not a graphic,space, or horiz tab

external routine
    ask,			! Ask the user a question and get the answer.
    bug : novalue,		! Report a bug.
    ers,			! Report a user mistake.
    fresad : novalue,		! Free string allocated by ASK.
    maksad,			! Allocate a string and descriptor.
    sysmsg ;			! Output to the terminal.
global routine filter(a_string) =

!++
! Functional Description:
!
!	This routine scans the supplied string for a character that
!	is neither a graphic, nor a space, nor a horizontal tab.
!	If there are any, it informs the user and returns FALSE.
!	An empty string is considered good.
!
! Formal Parameters:
!
!	a_string:	Address of a descriptor for the string to be scanned.
!
! Implicit Inputs:
!
!	None
!
! Implicit Outputs:
!
!	None
!
! Routine Value:
! Completion Codes:
!
!	TRUE if the user made no mistakes, otherwise FALSE.
!
! Side Effects:
!
!	None
!
!--

    begin	! FILTER
    bind
	string = .a_string : desc_block ;

    local
	p_this ;		! Character pointer to current character.

    p_this = .string[desc_ptr] ;
    incr i from 1 to .string[desc_len] do
	begin	! Test one character and advance to the next.

	selectone ch$rchar(.p_this) of
	    set		! Test one character

	    [9, 32 to 127]:
		;	! OK.

	    [otherwise]:
		begin	! Illegal
		ers(s_speccherr,lit(
		 'A character was not a graphic, space, or horizontal tab')) ;
		return false ;
		end ;	! Illegal

	    tes ;	! Test one character.

	p_this = ch$plus(.p_this, 1) ;
	end ;	! Test one character and advance to the next.

    true
    end ;	! FILTER
global routine find_next_words(a_raw_string, max_len, a_next_segment) =

!++
! Functional Description:
!
!	This routine identifies successive segments of a given raw string of
!	arbitrary length.  Each segment contains as many whole words as will
!	fit in a given maximum length, where words are separated by blanks.
!	Leading and trailing blanks are omitted from each segment.  A segment
!	ends at the end of a word, unless the first word in the segment is
!	longer than the maximum length.
!
!	Each segment is identified by storing into a descriptor provided by
!	the caller.  On the first call for a given raw string, the descriptor
!	to be used for the first segment should have a zero length.
!
!	Successive calls with the same raw string identify successive
!	segments, provided the descriptor to be used for the next segment
!	identifies the previous segment.
!
!	The routine returns TRUE when it identifies the last segment of
!	the raw string, and otherwise returns FALSE.
!
!	In this version of the routine, tabs are treated the same as any
!	non-space character.
!
! Formal Parameters:
!
!	a_raw_string :	Address of a descriptor of a string to be segmented.
!	max_len:	Maximum number of characters allowed in a segment.
!	a_next_segment:	Address of a descriptor having several functions.
!			When the routine is called the first time for a given
!			raw string, the descriptor must have a zero length.
!			When the routine returns, the descriptor denotes the
!			next segment identified.  When the routine is called
!			to identify additional segments, the descriptor must
!			contain the same information as that output by the
!			previous call.
!
! Implicit Inputs:
!
!	None
!
! Implicit Outputs:
!
!	None
!
! Routine Value:
! Completion Codes:
!
!	TRUE if the last segment is identified, else FALSE.
!
! Side Effects:
!
!	None
!
!--

    begin	! FIND_NEXT_WORDS
    bind
	raw_string = .a_raw_string : desc_block,
	next_segment = .a_next_segment : desc_block ;
    local
	num_blanks,		! Number of leading blanks in the new segment.
	remaining_len ;		! Number of chars. remaining in the raw string,
				! starting with the first of this segment.

    ! Set the tentative pointer to the new segment.
    if .next_segment[desc_len] eql 0
    then
	next_segment[desc_ptr] = .raw_string[desc_ptr]
    else
	next_segment[desc_ptr] = ch$plus(.next_segment[desc_ptr],
					 .next_segment[desc_len]) ;

    ! Compute and check the number of characters remaining.
    remaining_len = .raw_string[desc_len] -
		      ch$diff(.next_segment[desc_ptr], .raw_string[desc_ptr]) ;
    if .remaining_len lss 0
    then
	bug(lit('Bad argument to FIND_NEXT_WORDS')) ;

    ! Skip leading blanks in the new segment.
    num_blanks = 0 ;
    while .num_blanks lss .remaining_len do
	begin   ! Look for non-blank.
	if ch$rchar(ch$plus(.next_segment[desc_ptr], .num_blanks)) neq %c' '
	then
	    exitloop ;
	num_blanks = .num_blanks + 1 ;
	end ;	! Look for non-blank.
    if .num_blanks gtr 0
    then
	begin	! Found leading blanks.
	next_segment[desc_ptr] = ch$plus(.next_segment[desc_ptr], .num_blanks);
	remaining_len = .remaining_len - .num_blanks ;
	end ;	! Found leading blanks.

    ! Check for a call after the final segment.
    if .raw_string[desc_len] gtr 0 and .remaining_len eql 0
    then
	bug(lit('FIND_NEXT_WORDS was called too often')) ;

    ! Set the tentative length of the new segment.
    next_segment[desc_len] = .remaining_len ;

    ! See if more than one segment remains.
    if .next_segment[desc_len] gtr .max_len
    then
	begin	! Break before closest space
	local
	    p_break ;	! Char. pointer to space that will follow the segment.

	p_break = ch$plus(.next_segment[desc_ptr], .max_len) ;
	while ch$rchar(.p_break) neq %c' ' do
	    begin	! Back up for space
	    if ch$diff(.p_break, .next_segment[desc_ptr]) gtr 0
	    then
		p_break = ch$plus(.p_break, -1)
	    else
		begin	! No space found
		next_segment[desc_len] = .max_len ;	! Tough
		return false ;
		end ;	! No space found
	    end ;	! Back up for space
	next_segment[desc_len] =
		ch$diff(.p_break, .next_segment[desc_ptr]) ;
	end ;	! Break before closest space

    ! Omit trailing blanks.
    while .next_segment[desc_len] gtr 0 do
	begin	! Back up for non-space
	if ch$rchar(
		ch$plus(.next_segment[desc_ptr], .next_segment[desc_len] - 1))
		neq %c' '
	then
	    exitloop
	else
	    next_segment[desc_len] = .next_segment[desc_len] - 1 ;
	end ;	! Back up for non-space

    ! Finally, determine if this is the last segment.
	begin	! Look for more non-blank characters.
	local
	    extra_len ;		! Num. chars. still in raw string

	! The following statement is parenthesized like A = (B - C) - D
	! to avoid a compiler bug.
	extra_len = (.raw_string[desc_len]
		- ch$diff(.next_segment[desc_ptr], .raw_string[desc_ptr]))
		- .next_segment[desc_len] ;
	if .extra_len lss 0
	then
	    bug(lit('FIND_NEXT_WORDS found too much')) ;

	! Compute the value for the routine to return.
	if .extra_len eql 0
	then
	    true
	else
	    ch$fail(ch$find_not_ch(.extra_len,
		    ch$plus(.next_segment[desc_ptr], .next_segment[desc_len]),
		    %c' '))
	end	! Look for more non-blank characters
    end ;	! FIND_NEXT_WORDS
global routine writes(a_iob, a_string) =

!++
! Functional Description:
!
!	This routine writes a string to the output stream denoted by the
!	IOB supplied.  If the string will not fit on a terminal line,
!	it is broken into several lines, at spaces if possible, and the
!	extra lines are indented one more space than the number of leading
!	spaces in the first.
!
!	Excessive indentation is handled in a reasonable way:
!	If the number of leading spaces in the first line is more than
!	half a screen width, the number of leading spaces is assumed to
!	be the given number modulo half a screen width.
!
!	The string descriptor address supplied as argument is returned as
!	the value of the routine.
!
! Formal Parameters:
!
!	a_iob :		Address of the iob to use for writing
!	a_string:	Address of a descriptor for the string to write
!
! Implicit Inputs:
!
!	The own variable TERMINAL_WIDTH declared in this module.
!
! Implicit Outputs:
!
!	None
!
! Routine Value:
! Completion Codes:
!
!	The string descriptor address passed in as argument
!
! Side Effects:
!
!	Information is written into the output stream denoted by the IOB.
!
!--

    begin	! WRITES
    bind
	iob = .a_iob,
	string = .a_string: desc_block ;

    local
	first_line,			! Means this line is the first.
	indentation,			! Number of leading blanks in a line.
	last_line,			! Means this line is the last.
	line : vector[ch$allocation(k_max_terminal_width)], ! One output line.
	seg : desc_block ;		! Denotes the portion of the string
					! to be output next.

    ! Determine the indentation of this message.
    indentation = 0 ;
    while .indentation lss .string[desc_len] do
	begin	! Look for non-blank.
	if ch$rchar(ch$plus(.string[desc_ptr], .indentation)) neq %c' '
	then
	    exitloop ;
	indentation = .indentation + 1 ;
	end ;	! Look for non-blank.

    ! Handle excessive indentation in a reasonable way.
    if .indentation gtr .terminal_width/2
    then
	indentation = .indentation mod (.terminal_width/2) ;

    ! Tell FIND_NEXT_WORDS to start at the beginning of the string.
    $str_desc_init(descriptor = seg, string = (0, k_null)) ;

    ! Output the lines.
    first_line = true ;
    do
	begin	! Write one line
	last_line = find_next_words(string,
				.terminal_width - .indentation, seg) ;
	ch$fill(%c' ', .indentation, ch$ptr(line)) ;
	ch$move(len_comma_ptr(seg), ch$ptr(line, .indentation)) ;
	$step_put(iob = iob,
		string = (.indentation + .seg[desc_len], ch$ptr(line))) ;
	if .first_line and not .last_line
	then
	    begin	! Prepare for more lines.
	    first_line = false ;
	    indentation = .indentation + 1 ;
	    end ;	! Prepare for more lines.
	end	! Write one line
    until .last_line ;

    string
    end ;	! WRITES
GLOBAL ROUTINE twidth (a_width):novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine returns the width of the terminal when called
!
! FORMAL PARAMETERS:
!
!	a_width		Address of place to store the width of the terminal.
!
! IMPLICIT INPUTS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! ROUTINE VALUE and
! COMPLETION CODES:
!
!	None
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN
    
    .a_width = .terminal_width ;
    
    END;			! end of routine %(/*routine-name*/)%
GLOBAL ROUTINE swidth (width) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine sets the width of the terminal.
!
! FORMAL PARAMETERS:
!
!	width		Value to be set
!
! IMPLICIT INPUTS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! ROUTINE VALUE and
! COMPLETION CODES:
!
!	true  - width in-range and accepted
!	false - width out-of-range and ignored
!
! SIDE EFFECTS:
!
!	Affects operation of WRITES
!
!--

    BEGIN
    
    if (.width lss 40) or (.width gtr k_max_terminal_width)
    then
	return false;

    terminal_width = .width;
    return true;
    END;			! end of routine swidth
global routine yes(a_prompt) =

!++
! Functional Description:
!
!	YES issues the prompt supplied followed by a question mark, and
!	insists on a yes or no answer.  Upper and lower case are equivalent,
!	and the answer may be abbreviated.   An answer with no printing
!	graphics is interpreted as an answer of no, and the user is so
!	informed.  If the response is not valid, the user is prompted again.
!
!	If no prompt is supplied, a default prompt is issued.
!
! Formal Parameters:
!
!	a_prompt :	! Address of a descriptor for the prompt.  If the
!			! address is K_NULL or the descriptor contains a zero
!			! length, a default prompt is issued.
!
! Implicit Inputs:
!
!	None
!
! Implicit Outputs:
!
!	None
!
! Routine Value:  TRUE if the answer was yes, otherwise FALSE
! Completion Codes:
!
!	None
!
! Side Effects:
!
!	The terminal input and normal output streams are read and written.
!
!--

    begin	! YES
    bind
	prompt = .a_prompt : desc_block ;

    local
	r_query : ref desc_block,	! Prompt followed by question mark.
	r_response : ref desc_block,	! Address of descriptor of response.
	answer,				! +1 means yes.
					!  0 means undetermined.
					! -1 means no.
	c,				! Character being examined.
	p,				! Pointer to character being examined.
	p_guts,				! Pointer to first letter.
	guts_len ;			! Number of characters between first and last
					! letters, inclusive.

    ! Put a question mark on the prompt, even if it is empty.
    if prompt eql k_null
    then
	r_query = lit('?')
    else
	begin	! Append a question mark.
	r_query = maksad(.prompt[desc_len] + 12) ;
	ch$copy(len_comma_ptr(prompt), len_comma_ptr('?  [Y/N] (N)'), 0,
		len_comma_ptr(.r_query)) ;
	end ;	! Append a question mark.

    answer = 0 ;			! Initialize
    while .answer eql 0 do
	begin	! ask and test

	r_response = ask(.r_query) ;

	p_guts = k_null ;			! Initialize.
	guts_len = 0 ;			! Initialize.
	p = .r_response[desc_ptr] ;	! Initialize.

	! Launder the input.
	incr i from 1 to .r_response[desc_len] do
	    begin	! Examine one character
	    c = ch$rchar(.p) ;

	    if .c geq %c'A' and .c leq %c'Z'
	    then
		begin	! Convert to lower case
		c = .c + %c'a' - %c'A' ;
		ch$wchar(.c, .p) ;
		end ;	! Convert to lower case

	    if .c neq 9 and .c neq 32	! Neither space nor tab.
	    then
		begin	! Note first and last letters
		if .p_guts eql k_null
		then
		    p_guts = .p ;
		guts_len = .guts_len + 1 ;
		end ;	! Note first and last letters

	    p = ch$plus(.p, 1) ;
	    end ;	! Examine one character

	! See if we have an answer.
	if .guts_len eql 0
	then
	    begin	! Default.
	    sysmsg(s_meansno,lit('Your response means NO'),0) ;
	    answer = -1 ;
	    end		! Default.
	else if not
	    ch$fail(ch$find_sub(len_comma_ptr(lit('no')), .guts_len, .p_guts))
	then
	    answer = -1
	else if not
	    ch$fail(ch$find_sub(len_comma_ptr(lit('yes')), .guts_len, .p_guts))
	then
	    answer = +1
	else
	    sysmsg(s_enteryn,lit('Type YES or NO'),0) ;

	! Get rid of the block allocated by ASK.
	fresad(.r_response) ;

	end ;	! Ask and test

    ! Free the query block if it was allocated.
    if prompt neq k_null
    then
	fresad(.r_query) ;

    .answer eql +1
    end ;	! YES
end				! Module IOSERV
eludom