Multics > Library > Source
25 Jan 1977

bound_info_rtns_.s.archive

This Multics source file was rescued from the messed-up source archive at MIT.

This is an exmaple of an archive of Multics source programs, a small part of the Multics Standard Service System commands. These programs were compiled and their individual object files bound into a single object segment. The individual components and their descriptions are:

check_info_segs (cis)

This command and active function checks to see if any system information segments have changed since the user last looked. The command prints out the names of changed segments; the active function returns their pathnames. Control arguments allow the command invocation to specify another command to be called on each changed segment. Each user has a personal "value segment" used to store various bits of information, and check_info_segs stores the date and time last looked in the value segment.

Original implementation by Tom Van Vleck. It occurs to me, about 30 years after I wrote this command, that it's only useful at a site where info seg updates happen more or less continuously. Customers at Multics sites who got yearly releases would see nothing from this command for a year, and then a huge list of changes once. So, like the who command, this command assumes something about the online community that uses it.

help

The help command prints out system information files. It is similar to the Unix "man" command.

Multics system information segments ("info segments") for standard commands are created by processing the system manual source and extracting the help information: thus, the manuals and the help have a single source, but there is more information in the manual than in the help segments, and the help segments have indexing and structural items added so that help can display summaries, skip sections, and provide better interaction.

help is a wrapper for the help_ subroutine below. The wrapper/guts structure is used in many commands. The wrapper handles the business of being a command, error printing, and so on. The guts does the internals of the processing and may be used by the wrapper and by other subsystems.

Original implementation by Tom Van Vleck.

help_

This subroutine is the guts of the help command.

list_help (lh)

This command and active function lists system information segments that are relevant to a particular topic. (The Unix "man" command has a similar control argument.)

list_ref_names (lrn)

This command lists the reference names by which a segment is initiated.

print_motd (pmotd)

This command prints those lines in the "message of the day" system information segment, motd.info, which have the user has not seen. This command also stores its information in the user's value segment.

resource_usage (ru)

This command prints a table that shows the user's resource limits and usage against these limits.

Original implementation by Tom Van Vleck and Janice Phillipps.

ring0_get_

This subroutine reads information from the supervisor. Only specific values are available to normal user programs.

system_info_

This subroutine reads information about the system.

Original implementation by Tom Van Vleck.

user_info_

This subroutine returns information about the particular logged-in user process.

Original implementation by Tom Van Vleck.

where (wh)

This command and active function uses the system search rules to find a sgment, and prints out its file system pathname.

who, how_many_users (hmu)

This command prints a list of logged in users. The source also implements a related command, "how_many_users (hmu)" that prints out the total number of users currently logged in. These commands merely format and display data placed in a public data segment by the answering service.

See also the writeup of the Who Command, containing a copy of the info seg and a sample of the output.

This command is descended from the WHO command on CTSS. Some versions of Unix have a similar command of the same name.

Original Multics implementation by Tom Van Vleck.

Back to Multics Source index.

\014



            check_info_segs.pl1             02/04/82  1425.6rew 02/04/82  1420.7      179946



/* ******************************************************
   *                                                    *
   *                                                    *
   * Copyright (c) 1972 by Massachusetts Institute of   *
   * Technology and Honeywell Information Systems, Inc. *
   *                                                    *
   *                                                    *
   ****************************************************** */

/* Check directories for new info segments.

   This command remarks about any file in a directory in the "info_segments"
   search list or in user-supplied directories with the dtem greater than the
   last_time_looked.  The last_time_looked is kept in the user's default
   value segment.

   The active function returns the selected info seg names separated by spaces

   Rewritten 24-Oct-78 by Monte Davidoff.
   Modified February 1979 by Michael R. Jordan for unsigned changes to star_structures.incl.pl1. */
/* No_s bug obtaining dtcm's fixed 12/12/79 S. Herbst */
/* Implement [cis], -absolute_pathname, and fix bugs 06/11/80 S. Herbst */
/* Implement -time_checked Sept 1980  Marshall Presser */
/* Implement discarding of duplicates when same segment identified twice 81/02/11 Paul Benjamin */
/* Modified: 14 January 1982 by G. Palter to convert to using the default value segment */

/* format: style4,delnl,insnl,ifthenstmt,ifthen */

check_info_segs:
cis:
     procedure () options (variable);

dcl  arg_count fixed binary;
dcl  arg_length fixed binary (21);
dcl  arg_ptr pointer;
dcl  argx fixed binary;
dcl  call_str_length fixed binary (21);
dcl  call_str_ptr pointer;
dcl  change_sw bit (1);
dcl  code fixed binary (35);
dcl  complain entry variable options (variable);
dcl  dir_name char (168);
dcl  duplicate bit (1);
dcl  entryname char (32);
dcl  last_time_looked fixed binary (71);
dcl  return_len fixed binary;
dcl  return_ptr pointer;
dcl  uid_list_count fixed binary;
dcl  uid_list_index fixed binary;
dcl  uid_list_ptr ptr;
dcl  1 sw,
       2 absp bit (1),
       2 af bit (1),
       2 brief bit (1),
       2 call bit (1),
       2 long bit (1),
       2 pathname bit (1),
       2 update bit (1),
       2 check_time bit (1);
dcl  time_checked char (24);
dcl  update_time fixed binary (71);

dcl  arg_string char (arg_length) based (arg_ptr);
dcl  return_arg char (return_len) varying based (return_ptr);
dcl  uid_list (uid_list_count) bit (36) based (uid_list_ptr);

dcl  (addr, binary, clock, currentsize, divide, empty, hbound, index, length, null, rtrim) builtin;

dcl  (cleanup, program_interrupt) condition;

dcl  DEFAULT_VALUE_SEGMENT pointer static options (constant) initial (null ());
dcl  PERMANENT_VALUE bit (36) aligned static options (constant) initial ("01"b);
dcl  CIS_VALUE_NAME character (17) static options (constant) initial ("check_info_segs._");

dcl  command char (32) internal static options (constant) initial ("check_info_segs");

dcl  error_table_$badopt fixed binary (35) external static;
dcl  error_table_$no_dir fixed binary (35) external static;
dcl  error_table_$no_s_permission fixed binary (35) external static;
dcl  error_table_$noentry fixed binary (35) external static;
dcl  error_table_$nomatch fixed binary (35) external static;
dcl  error_table_$not_act_fnc fixed binary (35) external static;
dcl  error_table_$oldnamerr fixed binary (35) external static;

dcl  active_fnc_err_ entry () options (variable);
dcl  active_fnc_err_$suppress_name entry () options (variable);
dcl  com_err_ entry () options (variable);
dcl  com_err_$suppress_name entry () options (variable);
dcl  convert_date_to_binary_ entry (char (*), fixed binary (71), fixed binary (35));
dcl  cu_$af_return_arg entry (fixed bin, ptr, fixed bin, fixed bin (35));
dcl  cu_$arg_ptr entry (fixed binary, pointer, fixed binary (21), fixed binary (35));
dcl  cu_$cp entry (pointer, fixed binary (21), fixed binary (35));
dcl  date_time_ entry (fixed binary (71), char (*));
dcl  expand_pathname_ entry (char (*), char (*), char (*), fixed binary (35));
dcl  get_system_free_area_ entry () returns (pointer);
dcl  get_temp_segment_ entry (char (*), ptr, fixed bin (35));
dcl  hcs_$get_link_target entry (char (*), char (*), char (*), char (*), fixed bin (35));
dcl  hcs_$initiate entry (char (*), char (*), char (*), fixed bin (1), fixed bin (2), ptr, fixed bin (35));
dcl  hcs_$star_dir_list_
    entry (char (*), char (*), fixed binary (3), pointer, fixed binary, fixed binary, pointer, pointer,
    fixed binary (35));
dcl  hcs_$status_ entry (char (*), char (*), fixed bin (1), ptr, ptr, fixed bin (35));
dcl  hcs_$status_long entry (char (*), char (*), fixed bin (1), ptr, ptr, fixed bin (35));
dcl  hcs_$terminate_noname entry (ptr, fixed bin (35));
dcl  ioa_ entry () options (variable);
dcl  release_temp_segment_ entry (char (*), ptr, fixed bin (35));
dcl  requote_string_ entry (char (*)) returns (char (*));
dcl  search_paths_$get entry (char (*), bit (36), char (*), pointer, pointer, fixed binary, pointer, fixed binary (35));
dcl  user_info_ entry (char (*));
dcl  user_info_$homedir entry (char (*));
dcl  value_$get_data entry (ptr, bit (36) aligned, char (*), ptr, ptr, fixed bin (18), fixed bin (35));
dcl  value_$get_path entry (char (*), fixed bin (35));
dcl  value_$set_data
    entry (ptr, bit (36) aligned, char (*), ptr, fixed bin (18), ptr, ptr, fixed bin (18), fixed bin (35));
dcl  value_$set_path entry (char (*), bit (1), fixed bin (35));

/*\014*/

    call cu_$af_return_arg (arg_count, return_ptr, return_len, code);
    if code = error_table_$not_act_fnc then do;
         sw.af = "0"b;
         complain = com_err_;
    end;
    else do;
         sw.af = "1"b;
         complain = active_fnc_err_;
         return_arg = "";
    end;

    sl_info_p = null ();
    star_entry_ptr = null ();
    star_names_ptr = null ();
    uid_list_ptr = null ();

    on cleanup call cleanup_;

    last_time_looked = 0;           /* none yet supplied */
    sw.absp = "0"b;
    sw.brief = "0"b;
    sw.call = "0"b;
    sw.long = "0"b;
    sw.pathname = "0"b;
    sw.check_time = "0"b;
    sw.update = "1"b;
    change_sw = "0"b;
    call_str_length = 0;


    do argx = 1 to arg_count;

         call cu_$arg_ptr (argx, arg_ptr, arg_length, code);
         if code ^= 0 then do;
        call complain (code, command, "Fetching argument #^d.", argx);
        return;
         end;

         if arg_string = "-absolute_pathname" | arg_string = "-absp" then sw.absp = "1"b;

         else if arg_string = "-brief" | arg_string = "-bf" then
        if sw.af then
             go to BAD_OPT;
        else sw.brief = "1"b;

         else if arg_string = "-call" then do;
        if sw.af then go to BAD_OPT;
        sw.call = "1"b;
        argx = argx + 1;
        call cu_$arg_ptr (argx, call_str_ptr, call_str_length, code);
        if code ^= 0 then do;
             call complain (code, command, "Missing command line after -call.");
             return;
        end;
         end;

         else if arg_string = "-date" | arg_string = "-dt" then do;
        sw.update = "0"b;
        argx = argx + 1;
        call cu_$arg_ptr (argx, arg_ptr, arg_length, code);
        if code ^= 0 then do;
             call complain (code, command, "Missing date after -date.");
             return;
        end;

        call convert_date_to_binary_ (arg_string, last_time_looked, code);
        if code ^= 0 then do;
             call complain (code, command, "^a", arg_string);
             return;
        end;
         end;

         else if arg_string = "-long" | arg_string = "-lg" then
        if sw.af then
             go to BAD_OPT;
        else sw.long = "1"b;

         else if arg_string = "-no_update" | arg_string = "-nud" then sw.update = "0"b;

         else if arg_string = "-time_checked" | arg_string = "-tmck" then sw.check_time = "1"b;

         else if arg_string = "-pathname" | arg_string = "-pn" then do;
        sw.pathname = "1"b;
        argx = argx + 1;
        call cu_$arg_ptr (argx, arg_ptr, arg_length, code);
        if code ^= 0 then do;
             call complain (code, command, "Missing star pathname after -pathname.");
             return;
        end;

        call expand_pathname_ (arg_string, dir_name, entryname, code);
        if code ^= 0 then do;
             call complain (code, command, "^a", arg_string);
             return;
        end;
         end;

         else if is_control_arg (arg_string) then do;
BAD_OPT:
        call complain (error_table_$badopt, command, "^a", arg_string);
        return;
         end;

         else do;
        if sw.af then
             call active_fnc_err_$suppress_name (0, command, "Usage:  [^a {-control_args}]", command);
        else call com_err_$suppress_name (0, command, "Usage:  ^a {-control_args}", command);
        return;
         end;
    end;

    if sw.af & sw.check_time then
         if arg_count > 1 then do;
        call complain (0, command, "The -time_checked control argument is incompatible with any others.");
        return;
         end;

/*\014*/

    if last_time_looked = 0 then            /* if user didn't supply a date/time on the command line */
         call get_time (last_time_looked);

    if sw.check_time then do;
         call date_time_ (last_time_looked, time_checked);
         if sw.af then
        if last_time_looked = 0 then do;
             call complain (0, command,
            "There is no initial date in the user profile on when info segments were last checked.");
             return;
        end;
        else do;
             return_arg = requote_string_ (time_checked);
             return;
        end;

         else do;
        if last_time_looked = 0 then do;
             call complain (0, command,
            "There is no initial date in the user profile on when info segments were last checked.");
             return;
        end;
        else call ioa_ ("Info segments were last checked on ^a", time_checked);
        if arg_count = 1 then return;
         end;
    end;

    update_time = clock ();         /* avoids missing segments if -call is used */

    if sw.update & last_time_looked = 0 then do;
         if ^sw.af then
        call ioa_ ("^a: ^a", command,
             "Initializing date stored in default value segment on which info segments were last checked.");
         call put_time (update_time);
         return;
    end;

    call get_temp_segment_ (command, uid_list_ptr, code);
    if code ^= 0 then do;
         call complain (code, command);
         call cleanup_;
         return;
    end;
    uid_list_count = 0;

    if sw.pathname then do;
         do argx = 1 to arg_count;

        call cu_$arg_ptr (argx, arg_ptr, arg_length, code);
        if code = 0 then
             if arg_string = "-pathname" | arg_string = "-pn" then do;
            argx = argx + 1;
            call cu_$arg_ptr (argx, arg_ptr, arg_length, code);
            call expand_pathname_ (arg_string, dir_name, entryname, code);
            call check_directory (dir_name, entryname);
             end;
             else if arg_string = "-call" | arg_string = "-date" | arg_string = "-dt" then argx = argx + 1;
         end;
    end;
    else do;
         call search_paths_$get ("info_segments", sl_control_default, "", null (), get_system_free_area_ (),
        sl_info_version_1, sl_info_p, code);
         if code ^= 0 then do;
        call complain (code, command, "info_segments");
        call cleanup_;
        return;
         end;

         do argx = 1 to sl_info.num_paths;
        call check_directory (sl_info.paths (argx).pathname, "**.info");
         end;
    end;

    if ^change_sw & ^sw.brief & ^sw.af then call ioa_ ("No changed info segments.");

    if sw.update then call put_time (update_time);

RETURN_FROM_CHECK_INFO_SEGS:
    call cleanup_;

    return;

/*\014*/

/* Check a directory for changed info segments */

check_directory:
     procedure (dir_name, star_name);

dcl  dir_name char (*);             /* (Input) directory to search */
dcl  star_name char (*);                /* (Input) star name of segments to check */

dcl  1 branch like status_branch.short aligned;
dcl  target_dn char (168);
dcl  target_en char (32);
dcl  command_line char (call_str_length + 169) aligned;
dcl  entryx fixed binary;

dcl  NO_CHASE fixed binary (1) internal static options (constant) initial (0);

    on program_interrupt goto done_checking_dir;

    star_select_sw = star_ALL_ENTRIES;
    call hcs_$star_dir_list_ (dir_name, star_name, star_select_sw, get_system_free_area_ (), star_branch_count,
         star_link_count, star_list_branch_ptr, star_list_names_ptr, code);

    if code ^= 0 & code ^= error_table_$nomatch & code ^= error_table_$no_dir & ^sw.brief then
         call complain (code, command, "^a^[>^]^a", dir_name, dir_name ^= ">", star_name);
                        /* in particular, >doc>iml_info may be empty or non-existent */

    else do entryx = 1 to hbound (star_links, 1);
         if star_links (entryx).type = star_SEGMENT then
        call check_segment (dir_name, star_list_names (star_dir_list_branch (entryx).nindex), dir_name,
             star_list_names (star_dir_list_branch (entryx).nindex), star_dir_list_branch (entryx).dtem);

         else if star_links (entryx).type = star_LINK then do;
        call hcs_$get_link_target (dir_name, star_list_names (star_links (entryx).nindex), target_dn,
             target_en, code);
        if code = 0 then do;        /* target exists */

             call hcs_$status_ (target_dn, target_en, NO_CHASE, addr (branch), null (), code);
             if code ^= 0 & code ^= error_table_$noentry & code ^= error_table_$no_s_permission then
            call complain (code, command, "Link target ^a^[>^]^a", target_dn, target_dn ^= ">",
                 target_en);

             else if branch.type = Segment then
            call check_segment (target_dn, target_en, dir_name,
                 star_list_names (star_links (entryx).nindex), branch.dtcm);
        end;
         end;
    end;

done_checking_dir:
    if star_list_names_ptr ^= null () then do;
         free star_list_names;
         star_list_names_ptr = null ();
    end;
    if star_list_branch_ptr ^= null () then do;
         free star_links;
         star_list_branch_ptr = null ();
    end;

    return;

/*\014*/

/* Check if a segment has been modified */

check_segment:
    procedure (dir_name, entryname, print_dn, print_en, dtm);

dcl  dir_name char (*);             /* (Input) directory containing the segment */
dcl  entryname char (*);                /* (Input) entryname of the segment */
dcl  print_dn char (*);             /* (Input) directory name of link if link, or seg */
dcl  print_en char (*);             /* (Input) entryname of link if link, or seg */
dcl  dtm bit (36);                  /* (Input) date-time modified */

dcl  name char (168);               /* name as printed */
dcl  pathname char (168);               /* absolute pathname */
dcl  date_time char (16);
dcl  modified_time fixed binary (71);

dcl  call_str char (call_str_length) based (call_str_ptr);

dcl  1 branch like status_branch aligned;
dcl  NO_CHASE fixed bin (1) int static options (constant) init (0);

         modified_time = binary (dtm || (16)"0"b, 71);
         if modified_time >= last_time_looked then do;

        call hcs_$status_long (dir_name, entryname, NO_CHASE, addr (branch), null (), code);

        modified_time = binary (dtcm || (16)"0"b, 71);
                        /* make sure by checking dtcm */
        if modified_time >= last_time_looked then do;

             duplicate = "0"b;

             do uid_list_index = 1 to uid_list_count;
            if uid_list (uid_list_index) = branch.uid then do;
                 duplicate = "1"b;
                 uid_list_index = uid_list_count;
            end;
             end;
             if duplicate = "0"b then do;
            uid_list_count = uid_list_count + 1;
            uid_list (uid_list_count) = branch.uid;
            change_sw = "1"b;       /* something has actually changed */

            if print_dn = ">" then
                 pathname = ">";
            else pathname = rtrim (print_dn) || ">";
            pathname = rtrim (pathname) || print_en;

            if sw.absp then
                 name = pathname;   /* return absolute pathnames */
            else name = print_en;

            if sw.af then do;
                 if return_arg ^= "" then return_arg = return_arg || " ";
                 return_arg = return_arg || requote_string_ (rtrim (name));
            end;
            else if sw.long then do;
                 call date_time_ (modified_time, date_time);
                 call ioa_ ("^a ^a", date_time, name);
            end;
            else if ^sw.brief then call ioa_ ("^a", name);
            if sw.call then do;
                 command_line = call_str || " " || pathname;
                 call cu_$cp (addr (command_line), length (rtrim (command_line)), code);
            end;
             end;
        end;
         end;

         return;

    end check_segment;

     end check_directory;

/*\014*/

/* Check if an argument is a control arg */

is_control_arg:
     procedure (arg) returns (bit (1));

dcl  arg char (*);                  /* (Input) command argument */

    if arg = "" then
         return ("0"b);
    else return (index (arg, "-") = 1);

     end is_control_arg;

/*\014*/

cleanup_:
     procedure ();

    if sl_info_p ^= null () then do;
         free sl_info;
         sl_info_p = null ();
    end;

    if star_names_ptr ^= null () then do;
         free star_list_names;
         star_names_ptr = null ();
    end;

    if star_entry_ptr ^= null () then do;
         free star_links;
         star_entry_ptr = null ();
    end;

    if uid_list_ptr ^= null () then call release_temp_segment_ (command, uid_list_ptr, code);

    return;

     end cleanup_;

/*\014*/

/* Fetch the date/time info segments were last check from the value segment: if the time isn't present in the value
   segment, check the abbrev profile for an old style date/time and copy it to the value segment */

get_time:
     procedure (p_date_time);

dcl  p_date_time fixed binary (71) parameter;

dcl  small_area area (256);
dcl  based_date_time fixed binary (71) based (date_time_ptr);
dcl  date_time_ptr pointer;

    call value_$get_data (DEFAULT_VALUE_SEGMENT, PERMANENT_VALUE, CIS_VALUE_NAME, addr (small_area), date_time_ptr,
         (0), code);

    if (code = error_table_$oldnamerr) | (code = error_table_$noentry) then do;
         call get_date_time_from_profile ();
         call value_$get_data (DEFAULT_VALUE_SEGMENT, PERMANENT_VALUE, CIS_VALUE_NAME, addr (small_area),
        date_time_ptr, (0), code);
    end;

    if code ^= 0 then               /* couldn't find a date/time anywhere */
         p_date_time = 0;
    else p_date_time = based_date_time;

    return;



/* Internal to get_time: check for an abbrev style profile and, if present, copy the date/time from it */

get_date_time_from_profile:
    procedure ();

dcl  home_dir character (168);
dcl  person_id character (24);
dcl  profile_ename character (32);

dcl  1 old_profile aligned based (profile_ptr),     /* abbrev profile */
       2 version fixed binary,
       2 pad (3) bit (36),
       2 check_info_time fixed binary (71);
dcl  profile_ptr pointer;

         call user_info_$homedir (home_dir);
         call user_info_ (person_id);
         profile_ename = rtrim (person_id) || ".profile";

         profile_ptr = null ();

         on cleanup
        begin;              /* just in case (even with such a small window) */
             if profile_ptr ^= null () then call hcs_$terminate_noname (profile_ptr, (0));
             profile_ptr = null ();
        end;

         call hcs_$initiate (home_dir, profile_ename, "", 0b, 00b, profile_ptr, (0));

         if profile_ptr ^= null () then do;     /* there is a profile */
        if old_profile.version = 1 then /* only new style profile has the cis date/time */
             call put_time (old_profile.check_info_time);
        call hcs_$terminate_noname (profile_ptr, (0));
         end;

         return;

    end get_date_time_from_profile;

     end get_time;

/*\014*/

/* Put the updated date/time into the user's value segment */

put_time:
     procedure (p_date_time);

dcl  p_date_time fixed binary (71) parameter;

    call value_$set_data (DEFAULT_VALUE_SEGMENT, PERMANENT_VALUE, CIS_VALUE_NAME, addr (p_date_time),
         currentsize (p_date_time), null (), (null ()), (0), code);

    if code = error_table_$noentry then do;     /* value segment not present: try to create it */
         call create_default_value_segment ();
         call value_$set_data (DEFAULT_VALUE_SEGMENT, PERMANENT_VALUE, CIS_VALUE_NAME, addr (p_date_time),
        currentsize (p_date_time), null (), (null ()), (0), code);
    end;

    if code ^= 0 then call com_err_ (code, command, "Attempting to update date/time in default value segment.");

    return;



/* Internal to put_time: create the default value segment (if possible) */

create_default_value_segment:
    procedure ();

dcl  value_segment_path character (168);

         call value_$set_path ("", "1"b, code);

         if code = 0 then do;           /* created it */
        call value_$get_path (value_segment_path, (0));
        call com_err_ (0, command, "Created ^a.", value_segment_path);
         end;

         return;

    end create_default_value_segment;

     end put_time;

/*\014*/

%include sl_info;

%include sl_control_s;
%page;
%include star_structures;
%page;
%include status_structures;

     end check_info_segs;
\014



            help.pl1                        03/27/81  1446.0rew 03/27/81  1444.9      128583



/* ******************************************************
   *                                                    *
   *                                                    *
   * Copyright (c) 1972 by Massachusetts Institute of   *
   * Technology and Honeywell Information Systems, Inc. *
   *                                                    *
   *                                                    *
   ****************************************************** */


    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* Name:    help                                */
    /*                                  */
    /* This is the command interface to the Multics help facility.  It does the following.  */
    /*                                  */
    /* 1) call help_$init to obtain a help_args structure in which arguments and control    */
    /*    arguments can be stored.                      */
    /* 2) process caller-supplied arguments, filling in the help_args structure.        */
    /* 3) call help_ with the help_args structure to actually find and print the info segs. */
    /* 4) call help_$term to release the help_args structure.               */
    /*                                  */
    /* help searches for info segments (having a suffix of info) in the directories given in    */
    /* the search paths of the info_segments (info_segs or infos) search list, which    */
    /* is maintained by the Multics search facility.                    */
    /*                                  */
    /* Status                                   */
    /*                                  */
    /* 0) Created:   November, 1969   by T. H. VanVleck             */
    /* 1) Modified:  February, 1975   by T. H. VanVleck - complete rewrite      */
    /* 2) Modified:  September,1976   by Steve Herbst - accept -pathname ctl_arg        */
    /* 3) Modified:  June, 1977     by Paul Green - diagnose zero-length info segs  */
    /* 4) Modified:  October, 1978    by Gary Dixon - complete rewrite; split into help */
    /*              command and separate help_ subroutine.      */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
\014
help: procedure;

     dcl
         (Iarg_end_ca, Iarg_end_scn, Iarg_start_ca, Iarg_start_scn, Iarg_start_srh)
                fixed bin,
         (Larg, Lop)        fixed bin,
    Nargs           fixed bin,
         (Parg, Pop)        ptr,
    Serror          bit(1) aligned,
         (cleanup, conversion, size)    condition,
    code            fixed bin(35),
    error_type      fixed bin,
         (i, j)         fixed bin;

     dcl
    arg         char(Larg) based(Parg),
    op          char(Lop)  based(Pop);

     dcl (bin, convert, dim, maxlength, null, substr)
                builtin;

     dcl
    com_err_            entry options(variable),
    cu_$arg_count       entry returns(fixed bin),
    cu_$arg_ptr     entry (fixed bin, ptr, fixed bin, fixed bin(35));

     dcl
         (FALSE         init ("0"b),
    TRUE            init ("1"b)) bit(1) aligned int static options(constant),
    ctl_abbrev (10)     char(6) varying int static options(constant) init (
                     "-scn",    /* 1 */
                     "-srh",    /* 2 */
                     "-bf", /* 3 */
                     "-ca", /* 4 */
                     "-ep", /* 5 */
                     "-he", /* 6 */
                     "-bfhe",   /* 7 */
                     "-pn", /* 8 */
                     "-a",  /* 9 */
                     "-title"), /*10 */
    ctl_word (12)       char(13) varying int static options(constant) init (
                     "-section",        /* 1 */
                     "-search",     /* 2 */
                     "-brief",      /* 3 */
                     "-control_arg",    /* 4 */
                     "-entry_point",    /* 5 */
                     "-header",     /* 6 */
                     "-brief_header",   /* 7 */
                     "-pathname",       /* 8 */
                     "-all",        /* 9 */
                     "-titles",     /*10 */
                     "-maxlines",       /*11 */
                     "-minlines"),      /*12 */
\014
    ctl_obsolete (2)        char(3) varying int static options(constant) init (
                     "-sc", /* 1 */
                     "-sh"),    /* 2 */
         (error_table_$bad_arg,
    error_table_$badopt,
    error_table_$bigarg,
    error_table_$inconsistent,
    error_table_$noarg,
          error_table_$noentry,
    error_table_$unimplemented_version)
                fixed bin(35) ext static;
\014
%include help_args_;
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


    Phelp_args = null;
    on cleanup call janitor();          /* Cleanup help arg segment if help aborted.    */
    call help_$init ("help", "info_segments", "", Vhelp_args_1, Phelp_args, code);
    if Phelp_args = null then           /* get help input arguments.            */
         go to ARG_STRUC_ERR;
    if help_args.version ^= Vhelp_args_1 then do;   /* check version of structure for validity. */
         code = error_table_$unimplemented_version;
         go to ARG_STRUC_ERR;
         end;
    Nargs = cu_$arg_count();            /* get count of input arguments.        */

    Serror = FALSE;             /* Remember if error encountered in args.   */
    Iarg_start_srh = Nargs+1;           /* -search not encountered so far.      */
    Iarg_start_ca  = Nargs+1;           /* Same for -control_arg.           */
    Iarg_start_scn = Nargs+1;           /* Same for -section            */
    Iarg_end_ca    = 0;
    Iarg_end_scn   = 0;
    help_args.Sctl.he_pn = TRUE;            /* Output long heading by default.      */
    help_args.Sctl.he_counts = TRUE;
    do i = 1 to Nargs;              /* Process args.                */
         call cu_$arg_ptr (i, Parg, Larg, 0);
         if  Larg>=1  &  substr(arg,1,1) = "-"  then do;
        do j = 1 to dim(ctl_abbrev,1) while (arg ^= ctl_abbrev(j));
             end;
        if j > dim(ctl_abbrev,1) then do;
             do j = 1 to dim(ctl_word,1) while (arg ^= ctl_word(j));
            end;
             if j > dim(ctl_word,1) then do;
            do j = 1 to dim(ctl_obsolete,1) while (arg ^= ctl_obsolete(j));
                 end;
            if j > dim(ctl_obsolete,1) then do;
                 Serror = TRUE;
                 call com_err_ (error_table_$badopt, "help", arg);
                 go to NEXT_ARG;
                 end;
            end;
             end;
        go to DO_ARG(j);
\014
DO_ARG(1):          if i = Nargs then go to NO_OPERAND;
        call cu_$arg_ptr (i+1, Pop, Lop, code);
        if  Lop>=1  then
             if substr(op,1,1) = "-" then go to NO_OPERAND;
        help_args.Sctl.scn = TRUE;
        i = i+1;                /* -section:  next arg guaranteed part of         */
        Iarg_start_scn = i;         /*   section name.                                */
        Iarg_end_scn = i;
        do i = i+1 to Nargs;        /* Remaining args not starting with - are part    */
                        /*   of section name too.                         */
             call cu_$arg_ptr (i, Pop, Lop, 0);
             if  Lop >= 1  then
            if substr(op,1,1) = "-"  then do;
                 i = i - 1;
                 go to NEXT_ARG;
                 end;
             Iarg_end_scn = i;
             end;
        go to NEXT_ARG;
\014
DO_ARG(2):          if i = Nargs then go to NO_OPERAND;
        help_args.Sctl.srh = TRUE;      /* -search:  All remaining args are search        */
                        /*   strings.                                     */
        Iarg_start_srh = i + 1;     /* Remember where search args begin.              */
        i = Nargs;
        go to NEXT_ARG;

DO_ARG(3):          help_args.Sctl.bf = TRUE;       /* -brief                                         */
        go to NEXT_ARG;

DO_ARG(4):          if i = Nargs then go to NO_OPERAND;
        i = i + 1;          /* -control_arg:  args not starting with - are    */
                        /*   control argument names.                      */
        Iarg_start_ca = i;          /* Remember where ca names start.                 */
        Iarg_end_ca = i;            /* Remember where last ca name is.      */
        help_args.Sctl.ca = TRUE;       /* -ca                  */
        do i = i+1 to Nargs;
             call cu_$arg_ptr (i, Pop, Lop, 0);
             if  Lop>=1  then
            if substr(op,1,1) = "-"  then do;
                 i = i - 1;
                 go to NEXT_ARG;
                 end;
             Iarg_end_ca = i;
             end;
        go to NEXT_ARG;
\014
DO_ARG(5):  help_args.Sctl.ep = TRUE;       /* -entry_point             */
        go to NEXT_ARG;

DO_ARG(6):  help_args.Sctl.he_only = TRUE;  /* -header (print only heading)     */
        go to NEXT_ARG;

DO_ARG(7):  help_args.Sctl.he_pn = FALSE;       /* -brief_header (output brief headings)    */
        go to NEXT_ARG;

DO_ARG(8):  if i = Nargs then go to NO_OPERAND; /* -pathname:  following arg is a pathname, */
        i = i + 1;          /*   no matter what it looks like.      */
        call cu_$arg_ptr (i, Pop, Lop, 0);
        j = 1;
        if maxlength(help_args.path(j).value) < Lop then do;
             call com_err_ (error_table_$bigarg, "help", " ^a ^a",
            arg, op);
             Serror = TRUE;
             end;
        else do;
             help_args.Npaths, j = help_args.Npaths + 1;
             help_args.path(j).S = "0"b;
             help_args.path(j).S.pn_ctl_arg = TRUE;
             help_args.path(j).value = op;
             help_args.path(j).info_name = "";
             end;
        go to NEXT_ARG;
\014
DO_ARG(9):  help_args.Sctl.all = TRUE;      /* -all                 */
        go to NEXT_ARG;

DO_ARG(10): help_args.Sctl.title = TRUE;        /* -title                   */
        go to NEXT_ARG;

DO_ARG(11): if i = Nargs then go to NO_OPERAND; /* -maxlines N              */
        i = i + 1;
        call cu_$arg_ptr (i, Pop, Lop, 0);
        on conversion, size go to BAD_OPERAND;
        help_args.min_Lpgh = convert (help_args.min_Lpgh, op);
        revert conversion, size;
        if  help_args.min_Lpgh < 1  |  help_args.min_Lpgh > 50  then
             go to BAD_OPERAND;
        go to NEXT_ARG;

DO_ARG(12): if i = Nargs then go to NO_OPERAND; /* -minlines N              */
        i = i + 1;
        call cu_$arg_ptr (i, Pop, Lop, 0);
        on conversion, size go to BAD_OPERAND;
        help_args.min_Lpgh = convert (help_args.min_Lpgh, op);
        revert conversion, size;
        if  help_args.min_Lpgh < 1  |  help_args.min_Lpgh > 50  then
             go to BAD_OPERAND;
        go to NEXT_ARG;

NO_OPERAND: Serror = TRUE;          /* No operand given with -scn, -srh, -ca, -pn   */
        call com_err_ (error_table_$noarg, "help", "No operand given following ^a.", arg);
        go to NEXT_ARG;

BAD_OPERAND:    Serror = TRUE;          /* Bad numeric operand with -minlines.  */
        call com_err_ (error_table_$bad_arg, "help",
             " ^a^/Operand of ^a must be integer from 1 to 50.", op, arg);

        end;
         else do;
        j = 1;
        if maxlength(help_args.path(j).value) < Larg then do;
             call com_err_ (error_table_$bigarg, "help", " ^a",
            arg);
             Serror = TRUE;
             end;
        else do;
             help_args.Npaths, j = help_args.Npaths + 1;
             help_args.path(j).S = "0"b;
             help_args.path(j).value = arg;
             help_args.path(j).info_name = "";
             end;
        end;
NEXT_ARG:      end;
\014
    if help_args.Sctl.bf then           /* Complain if other ctl_args given with -brief */
         if help_args.Sctl.title |
            help_args.Sctl.all  then do;
        Serror = TRUE;
        call com_err_ (error_table_$inconsistent, "help",
             "^/-brief may not be given with: ^[ -title^]^[ -all^].",
             help_args.Sctl.title, help_args.Sctl.all);
        end;
    if help_args.Sctl.ca then           /* Complain if other ctl_args given with -ca    */
         if help_args.Sctl.title |
            help_args.Sctl.all  then do;
        Serror = TRUE;
        call com_err_ (error_table_$inconsistent, "help",
             "^/-control_arg may not be given with: ^[ -title^]^[ -all^].",
             help_args.Sctl.title, help_args.Sctl.all);
        end;
    if help_args.Sctl.he_only then
         if help_args.Sctl.title |
                    help_args.Sctl.bf |
            help_args.Sctl.all |
            help_args.Sctl.ca   then do;
        Serror = TRUE;
        call com_err_ (error_table_$inconsistent, "help", "
-header may not be given with: ^[ -brief^]^[ -title^]^[ -control_arg^]^[ -all^].",
             help_args.Sctl.bf, help_args.Sctl.title,
             help_args.Sctl.ca, help_args.Sctl.all);
        end;
    if help_args.Npaths = 0 then do;        /* Supply default pathname of help_system.gi.info.  */
         help_args.Npaths = 1;
         help_args.path(1).value = ">doc>info>help_system.gi.info";
                        /* Give info for installed help command.    */
         help_args.path(1).info_name = "";
         help_args.path(1).S = "0"b;
         end;
\014
    do i = Iarg_start_ca to Iarg_end_ca;        /* Add control arg names to arg structure.  */
         call cu_$arg_ptr (i, Parg, Larg, 0);
         j = 1;
         if maxlength (help_args.ca(j)) < Larg then do;
        Serror = TRUE;
        call com_err_ (error_table_$bigarg, "help", " -ca ^a
Maximum length is ^d characters.", arg, maxlength(help_args.ca(j)));
        end;
         else do;
        help_args.Ncas, j = help_args.Ncas + 1;
        help_args.ca(j)   = arg;
        end;
         end;
    do i = Iarg_start_scn to Iarg_end_scn;      /* Add -section substrings to arg structure.    */
         call cu_$arg_ptr (i, Parg, Larg, 0);
         j = 1;
         if maxlength (help_args.scn(j)) < Larg then do;
        Serror = TRUE;
        call com_err_ (error_table_$bigarg, "help", " -scn ^a
Maximum length is ^d characters.", arg, maxlength(help_args.scn(j)));
        end;
         else do;
        help_args.Nscns, j = help_args.Nscns + 1;
        help_args.scn(j)   = arg;
        end;
         end;
    do i = Iarg_start_srh to Nargs;     /* Add -search args to control structure.   */
         call cu_$arg_ptr (i, Parg, Larg, 0);
         j = 1;
         if maxlength (help_args.srh(j)) < Larg then do;
        Serror = TRUE;
        call com_err_ (error_table_$bigarg, "help", " -srh ^a
Maximum length is ^d characters.", arg, maxlength(help_args.srh(j)));
        end;
         else do;
        help_args.Nsrhs, j = help_args.Nsrhs + 1;
        help_args.srh(j)   = arg;
        end;
         end;
    if Serror then do;
         call janitor();
         return;
         end;
\014
    call help_ ("help", Phelp_args, "info", error_type, code);
    go to ERROR (error_type);

ARG_STRUC_ERR:
ERROR(1):                       /* bad help_args version.           */
ERROR(2):                       /* No pathnames given in help_args.     */
    call com_err_ (code, "help", "^/While processing the argument structure used by help_.");
    call janitor();
    return;

ERROR(3):                       /* Error encountered in processing one or more  */
                        /* of the pathnames given in help_args.     */
    do i = 1 to help_args.Npaths;
         if help_args.path(i).code ^= 0 then
        call com_err_ (help_args.path(i).code, "help", " ^[-pn ^]^a",
             help_args.path(i).S.pn_ctl_arg, help_args.path(i).value);
         end;
    call janitor();
    return;

ERROR(5):                       /* If a nonzero error code is returned, it means    */
                        /* than -section and -search failed to find any */
                        /* matching info segs to print.  This error must    */
                        /* be reported to the user.         */
    if code ^= 0 then
         call com_err_ (error_table_$noentry, "help", "
Looking for infos matching info_name^[s^]^[^; and -search criteria^; and -section criteria^;, plus -section and -search criteria^].",
        (help_args.Npaths > 1), (1 + 2*bin(help_args.Sctl.scn,1) + bin(help_args.Sctl.srh,1)));

ERROR(4):                       /* No fatal errors encountered.  Most nonfatal  */
                        /*   errors have been reported by help_.    */
    call janitor();
    return;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


janitor: procedure;

    if Phelp_args ^= null then
         call help_$term ("help", Phelp_args, code);

    end janitor;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


    end help;
\014



            help_.pl1                       11/19/82  1015.7rew 11/19/82  0956.4     1507932



/* ***********************************************************
   *                                                         *
   * Copyright, (C) Honeywell Information Systems Inc., 1982 *
   *                                                         *
   * Copyright (c) 1972 by Massachusetts Institute of        *
   * Technology and Honeywell Information Systems, Inc.      *
   *                                                         *
   *********************************************************** */


    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* Name:  help_                             */
    /*                                  */
    /* This subroutine implements the help command.  It performs the following functions.   */
    /*                                  */
    /* 1) Finds info segments.                          */
    /* 2) Selects particular infos within multi-info segments.              */
    /* 3) Sorts the list of infos to be processed.                  */
    /* 4) Processes each info, implementing all help control arguments and query responses. */
    /*                                  */
    /* The subroutine may also be used to implement a help-style information facility in    */
    /* other subsystems.  Information segments (with an info suffix or another suffix) are  */
    /* selected and printed, based upon information given primarily in a help_args structure,   */
    /* which is declared in help_args_.incl.pl1.                    */
    /*                                  */
    /* Usage                                    */
    /*                                  */
    /* The help_ subroutine must be invoked by a sequence of calls.         */
    /*                                  */
    /* 1) call help_$init to get temp segment containing help_args structure and stores the     */
    /*    current info_segments search rules in the structure.              */
    /* 2) call help_ one or more times to select and print info segments.           */
    /* 3) call help_$term to release the temp segment.              */
    /*                                  */
    /* Entry:  help_$check_info_segs                        */
    /*                                  */
    /* This subroutine generates the list of info segments to be processed by the       */
    /* check_info_segs command.  It finds info segments modified since a given date, sorts  */
    /* the list and returns it for check_info_segs to process.              */
    /*                                  */
    /* Usage                                    */
    /*                                  */
    /* 1) call help_$init to get temp segment containing help_args and the output list. */
    /* 2) call help_$check_info_segs to build and sort the list of segments to be processed.    */
    /* 3) call help_$term to release the temp segment.              */
    /*                                  */
\014
    /* Status                                   */
    /*                                  */
    /* 0) Created:   November, 1969   by T. H. VanVleck             */
    /* 1) Modified:  February, 1975   by T. H. VanVleck - complete rewrite      */
    /* 2) Modified:  September,1976   by Steve Herbst - accept -pathname ctl_arg        */
    /* 3) Modified:  June, 1977     by Paul Green - diagnose zero-length info segs  */
    /* 4) Modified:  October, 1978    by Gary Dixon - complete rewrite; split into help */
    /*              command and separate help_ subroutine.      */
    /*              Add support for check_info_segs.        */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */

\014
help_: procedure (procedure_name, Phelp_args, suffix, progress, Acode);


     dcl                        /*  Parameters          */
    procedure_name      char(*),        /* Caller of help_ and help_$init.      */
                        /*   1) Owns temp segment help_args are stored in.*/
                        /*   2) Name used in error messages.        */
/*  Phelp_args      ptr,        /* ptr to argument struc at base of temp seg.   */
                        /* This is really declared in include seg.  */
    suffix          char(*),        /* Suffix on segs to be processed. Normally "info"*/
                        /* but may be some other suffix or "" to omit   */
                        /* suffix processing.           */
    progress            fixed bin,  /* =1: bad help_args version            */
                        /* =2: no pathnames given.          */
                        /* =3: evaluating pathnames.            */
                        /* =4: finding help segs.           */
                        /* =5: -section/-search & printing help segs.   */
    Acode           fixed bin(35),  /* Return code.             */
    APPDinfo_seg        ptr;        /* Ptr to output structures returned by     */
                        /* help_$check_info_segs            */

     dcl    Loutput_line        fixed bin,  /* Length of terminal user's output line.   */
    Ninfos_printed      fixed bin,  /* Number of infos for which something has printed*/
    Nlast_info_cross_ref    fixed bin,  /* Last info with Scross_ref on.        */
    Nlast_info_no_brief_data    fixed bin,  /* Last info not containing Syntax section, */
                        /* which get_brief_data encountered.        */
    PI_LABEL            label variable,
    PDeps           ptr,
    PDinfo          ptr,
    Pinit_assoc_mem     ptr,
    Pnext_free_space        ptr,        /* ptr to next free word location in temp   */
                        /*   seg containing help_args.      */
    Pquery_answers      ptr,        /* ptr to formatted list of help responses. */
    Ptemp           ptr,
    Sprint_inhibit      bit(1) aligned, /* on if printing stopped by program_interrupt. */
    cleanup         condition,
    code            fixed bin(35),
    fcn         fixed bin,  /* Function to be performed by this invocation. */
       (HELP            init(0),        /*   help_              */
        CIS         init(1))        /*   check_info_segs            */
                     fixed bin int static options(constant),
         (i, j)         fixed bin,
    offset          fixed bin(35),
    program_interrupt       condition;
\014
%include help_cis_args_;
\014
     dcl    1 Dinfo         aligned based(PDinfo),
      2 N           fixed bin,
      2 seg (0 refer (Dinfo.N)) like Dinfo_seg; /* Information about each log. info to be printed.*/

     dcl    1 init_assoc_mem        aligned based(Pinit_assoc_mem),
                        /* Associative memory in which initiated segments   */
      2 N           fixed bin,  /* are stored.              */
      2 seg (50),               /* Allow up to 50 initiated segments at once.   */
        3 dir           char(168) unal,
        3 ent           char(32) unal,
        3 uid           bit(36),
        3 pad           fixed bin,
        3 P         ptr;

     dcl    1 LIST          aligned based,  /* structure used to format list of things to be    */
      2 header,             /* output in columns.           */
        3 N         fixed bin,  /*   number of list elements.           */
        3 Nreal     fixed bin,  /*   number of filled list elements.        */
        3 Npghs     fixed bin,  /*   number of filled paragraphs of formatted out.*/
        3 Nrows     fixed bin,  /*   number of rows in formatted output.    */
        3 Ncols     fixed bin,  /*   number of columns in formatted output. */
        3 ML (6)        fixed bin,  /*   length of longest element in each column.  */
        3 title     char(80) varying,   /*   title of output list.          */
        3 Iunit     fixed bin,  /*   unit no of pgh containing list elements.   */
      2 group (0 refer (LIST.N)),
        3 arg           char(88) varying,   /*   the argument.              */
        3 Snot_found        fixed bin;  /*   = 1 if no match found for the argument.    */

     dcl    1 query_answers     aligned based(Pquery_answers),
      2 header      like LIST.header,
      2 group (0 refer (query_answers.N))
                like LIST.group;

     dcl    responses (21)      char(36) var int static options(constant) init(
                     " yes, y", /* List of allowed responses to questions asked */
                     " rest {-scn},",
                     "    r {-scn}",/*   by help_.              */
                     " no, n",
                     " quit, q",
                     " top, t",
                     " header, he",
                     " title {-top}",
                     " section {STRs} {-top},",
                     "    scn  {STRs} {-top}",
                     " search  {STRs} {-top},",
                     "    srh  {STRs} {-top}",
                     " skip {-scn} {-seen} {-rest} {-ep},",
                     "    s {-scn} {-seen} {-rest} {-ep}",
                     " brief, bf",
                     " control_arg STRs, ca STRs",
                     " entry_point {EP_NAME},",
                     "    ep {EP_NAME}",
                     " ?",
                     " .",
                     " ..");

     dcl    bit36           bit(36) aligned based,
    bit72           bit(72) aligned based;

     dcl (addr, addrel, binary, currentsize, dim, dimension, divide, empty, hbound, index, lbound, length,
    ltrim, max, maxlength, min, mod, null, ptr, rel, reverse, rtrim, search, string, substr, sum,
    translate, verify)
                builtin;


     dcl
    com_err_            entry options(variable),
    command_query_      entry options(variable),
    convert_date_to_binary_     entry (char(*), fixed bin(71), fixed bin(35)),
    get_line_length_$switch entry (ptr, fixed bin(35)) returns(fixed bin),
    get_temp_segment_       entry (char(*), ptr, fixed bin(35)),
    hcs_$get_uid_seg        entry (ptr, bit(36) aligned, fixed bin(35)),
    hcs_$initiate       entry (char(*), char(*), char(*), fixed bin(1), fixed bin(2), ptr,
                     fixed bin(35)),
    hcs_$terminate_noname   entry (ptr, fixed bin(35)),
    hcs_$truncate_seg       entry (ptr, fixed bin, fixed bin(35)),
         (ioa_, ioa_$nnl, ioa_$rsnnl)   entry options(variable),
    iox_$control        entry (ptr, char(*), ptr, fixed bin(35)),
    iox_$put_chars      entry (ptr, ptr, fixed bin(21), fixed bin(35)),
    ipc_$block      entry (ptr, ptr, fixed bin(35)),
    match_star_name_        entry (char(*), char(*), fixed bin(35)),
    release_temp_segment_   entry (char(*), ptr, fixed bin(35)),
    search_paths_$get       entry (char(*), bit(36), char(*), ptr, ptr,
                       fixed bin, ptr, fixed bin(35)),
         (sort_items_$bit,
    sort_items_$char)       entry (ptr, fixed bin);

     dcl
    BS_underscore       char(2) aligned int static options(constant) init ("_"),
    FALSE           bit(1) aligned int static options(constant) init ("0"b),
    HELP_LINE_SIZE      fixed bin int static options(constant) init (79),
    HT_SP           char(2) init("   ") int static options(constant),
                        /* Horizontal-tab followed by space.        */
    MAX_HELP_LINE_SIZE      fixed bin int static options(constant) init(136),
    NL          char(1) int static options(constant) init ("
"),
    OLD_HELP_PGH_CHAR       char(1) aligned int static options(constant) init (""),   /* \006  */
    SPACES          char(100) aligned int static options(constant) init((100)" "),
    TRUE            bit(1) int static options(constant) init("1"b),
         (error_table_$badsyntax,
    error_table_$inconsistent,
    error_table_$incorrect_access,
    error_table_$moderr,
    error_table_$no_s_permission,
    error_table_$noarg,
    error_table_$noentry,
    error_table_$nomatch,
    error_table_$unimplemented_version,
    error_table_$zero_length_seg)
                fixed bin(35) ext static,
    iox_$user_output        ptr ext static,
    underscore_BS       char(2) aligned int static options(constant) init ("_");
\014
%include help_args_;

\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */



    fcn = HELP;             /* Perform a help function.         */
    go to COMMON;


check_info_segs: entry (procedure_name, Phelp_args, suffix, progress, Acode, APPDinfo_seg);

    fcn = CIS;              /* Perform a check_info_segs function.      */

COMMON: progress = 1;
    if help_args.version ^= Vhelp_args_1 then do;   /* Validate structure version.      */
         Acode = error_table_$unimplemented_version;
         return;
         end;
    progress = 2;
    if help_args.Npaths ^> 0 then do;        /* Make sure info file names were given.    */
         Acode = error_table_$noarg;
         return;
         end;
    Acode = 0;
    progress = 3;
    do i = 1 to help_args.Npaths;           /* validate input paths.            */
         call evaluate_path (help_args.path(i), suffix);
         if Acode = 0 then
        Acode = help_args.path.code(i);
         end;
    if Acode ^= 0 then return;

    progress = 4;
    Loutput_line = min (MAX_HELP_LINE_SIZE, get_line_length_$switch (iox_$user_output, code));
    if code ^= 0 then  Loutput_line = HELP_LINE_SIZE; /* Get user's terminal line size.     */
    Pquery_answers = set_space_used (Phelp_args, currentsize(help_args));
                        /* Get space for format list of help responses. */
    query_answers.N = 2 * hbound(responses,1);  /* Copy allowed responses into the list.    */
    query_answers.Nreal = query_answers.N;
    query_answers.Nrows = 0;            /* This indicates that list isn't formatted yet.    */
    query_answers.title = "List of Responses";
    do i = lbound(responses,1) to hbound(responses,1);
         query_answers.group(i).arg = responses(i);
         end;
    do i = i to query_answers.N;            /* Struc must be twice size of response array   */
         query_answers.group(i).arg = "";       /*   to allow for extension during formatting.  */
         end;                   /*   Set added elements to null strings.    */

    Pinit_assoc_mem, Pnext_free_space = set_space_used (Pquery_answers, currentsize(query_answers));
                        /* Get space in temp seg for associative memory */
                        /* used to reduce calls to hcs_$initiate.   */
    init_assoc_mem.N = 0;
    on cleanup call janitor();          /* Establish cleanup on unit.           */
\014
    PDinfo, Pnext_free_space = set_space_used (Pnext_free_space, currentsize(init_assoc_mem));
    Dinfo.N = 0;                /* Obtain space for list of info segs to be read.   */
    do i = 1 to help_args.Npaths;           /* Convert input paths to list of info segs.    */
         if help_args.path(i).S.less_greater then
        call get_info_seg_list (procedure_name, suffix, fcn,
                    help_args.path(i).dir(*), help_args.path(i), PDinfo);
         else call get_info_seg_list (procedure_name, suffix, fcn,
                    help_args.search_dirs(*), help_args.path(i), PDinfo);
         end;
    if Dinfo.N <= 0 then do;         /* Stop if no matching segs found.      */
         Acode = error_table_$nomatch;      /*   get_info_seg_list has already complained.  */
         call janitor();
         return;
         end;
\014
    progress = 5;               /* Infos selected by starname.  Any other errors    */
                        /* reported via Acode describe info selection by    */
                        /* -search and -seciton criteria.       */
    PPDinfo_seg, Pnext_free_space = set_space_used (Pnext_free_space, currentsize(Dinfo));
    PDinfo_seg.version = VPDinfo_seg_1;
    PDinfo_seg.N = Dinfo.N;
    do i = 1 to Dinfo.N;            /* Sort listed infos thrice:            */
         PDinfo_seg.P(i) = addr(Dinfo.seg(i).uid);  /*   1st:  sort on  Dinfo.seg.uid/.I combination    */
         end;                   /*   to eliminate duplicate infos.  */
                        /*   2nd:  sort on Dinfo.seg.ent to identify    */
                        /*   versions of info seg in different dirs.*/
    if Dinfo.N > 1 then do;          /*   3rd:  sort on Dinfo.seg.Scross_ref/dir/.ent    */
         call sort_items_$bit (addr(PDinfo_seg.N),72);/*     combination to alphabetize output. */
         offset = binary (rel (addr (Dinfo.seg(1).ent))) -
            binary (rel (addr (Dinfo.seg(1).uid)));
                        /* Compute negative offset to adjust ptrs to    */
                        /* Dinfo.seg.uid to point back to Dinfo.seg.ent.    */
         do i = 1 to Dinfo.N while (PDinfo_seg.P(i)->bit72 = "0"b);
        PDinfo_seg.P(i) = addrel(PDinfo_seg.P(i), offset);
        end;                /* Allow duplicate .uid/.I combos for infos */
                        /* in which errors were encountered.  These errors*/
                        /* must be reported.  get_info_seg_list has set */
                        /* .uid/.I combo to "0"b in these cases.    */
         j = i - 1;
         if i > Dinfo.N-1 then           /* if all info segs are in error, skip the  */
        go to SKIP_ELIMINATION;             /* elimination of duplicates.           */
         go to CHECK(fcn);

CHECK(0):        do i = i to Dinfo.N - 1;           /* Eliminate duplicate .uid/.I combos.      */
        if PDinfo_seg.P(i)->bit72 ^= PDinfo_seg.P(i+1)->bit72 then do;
             j = j + 1;         /*   (Only retain unique .uid/.I combos.)   */
             PDinfo_seg.P(j) = addrel(PDinfo_seg.P(i), offset);
             end;
        else PDinfo_seg.P(i+1) = PDinfo_seg.P(i);
                        /*   (Retain info found earliest in search rules).*/
        end;
         go to END_CHECK;

CHECK(1):        do i = i to Dinfo.N - 1;           /* Eliminate duplicate .uid combos.     */
        if PDinfo_seg.P(i)->bit36 ^= PDinfo_seg.P(i+1)->bit36 then do;
             j = j + 1;         /*   (Only retain unique .uid combos.)      */
             PDinfo_seg.P(j) = addrel(PDinfo_seg.P(i), offset);
             end;
        else PDinfo_seg.P(i+1) = PDinfo_seg.P(i);
                        /*   (Retain info found earliest in search rules).*/
        end;

END_CHECK:     j = j + 1;               /*   (Always retain the last entry in the list.)    */
         PDinfo_seg.P(j) = addrel(PDinfo_seg.P(i), offset);
         PDinfo_seg.N = j;
         end;
    else PDinfo_seg.P(1) = addr(Dinfo.seg(1).ent);
\014
SKIP_ELIMINATION:
    if PDinfo_seg.N > 1 then do;         /* Sort alphabetically by ent to identify info  */
         call sort_items_$char(addr(PDinfo_seg.N),32);/* segments appearing in more than one search dir.*/
         offset = binary (rel (addr (Dinfo.seg(1).Scross_ref))) -
            binary (rel (addr (Dinfo.seg(1).ent)));
                        /* Compute negative offset to adjust ptrs from  */
                        /* Dinfo.seg.ent to point to Dinfo.seg.Scross_ref.*/
         PDinfo_seg.P(1) = addrel(PDinfo_seg.P(1), offset);
         do i = 1 to Dinfo.N - 1;           /* Check for entry of same name in different dirs.*/
        PDinfo_seg.P(i+1) = addrel(PDinfo_seg.P(i+1), offset);
        if  PDinfo_seg.P(i) -> Dinfo_seg.ent  = PDinfo_seg.P(i+1) -> Dinfo_seg.ent &
            PDinfo_seg.P(i) -> Dinfo_seg.uid ^= PDinfo_seg.P(i+1) -> Dinfo_seg.uid &
            PDinfo_seg.P(i) -> Dinfo_seg.uid ^= "0"b  &
                      "0"b ^= PDinfo_seg.P(i+1) -> Dinfo_seg.uid  then do;
             if  binary(rel(PDinfo_seg.P(i)),18) < binary(rel(PDinfo_seg.P(i+1)),18)  then do;
            Ptemp = PDinfo_seg.P(i);    /* Mark all but entry found earliest in search  */
            PDinfo_seg.P(i) = PDinfo_seg.P(i+1);
            PDinfo_seg.P(i+1) = Ptemp;  /* rules with a cross reference flag.       */
            end;
             PDinfo_seg.P(i) -> Dinfo_seg.Scross_ref = TRUE;
             end;
        end;
         end;
    else PDinfo_seg.P(1) = addr(Dinfo.seg(1).Scross_ref);
    if PDinfo_seg.N > 1 then         /* Sort alphabetically by Scross_ref/dir/ent combo*/
         call sort_items_$char (addr(PDinfo_seg.N), 201  /* = 1 + 168 + 32 */);
    if fcn = CIS then do;
         call term_assoc_mem();
         APPDinfo_seg = PPDinfo_seg;
         return;
         end;


    PDeps, Pnext_free_space = set_space_used (Pnext_free_space, currentsize(PDinfo_seg));
                        /* Get space for entry point info descriptors.  */
    Nlast_info_no_brief_data = -1;      /* No info processed yet.           */
    Nlast_info_cross_ref = -1;
    PI_LABEL = PROCESS;             /* Establish pi handler.            */
    on program_interrupt begin;
         Sprint_inhibit = TRUE;
         go to PI_LABEL;
         end;
PROCESS:    Ninfos_printed = 0;
    do i = 1 to PDinfo_seg.N;           /* Process each listed info in alphabetical order.*/
         call process_info_seg (procedure_name, suffix, i, Ninfos_printed, PDinfo_seg.N, 
        Nlast_info_no_brief_data, Nlast_info_cross_ref, PDinfo_seg.P(i) -> Dinfo_seg, PDeps);
NEXT_INFO:     end;
    if Ninfos_printed = 0 then          /* -section and -search didn't find any match.  */
         Acode = error_table_$nomatch;
QUIT:   call janitor();             /* Cleanup and return.  Simple huh!     */
    return;                 /* But wait 'til you see what's below.      */

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


evaluate_path: procedure (info_path, suffix);

     dcl    1 info_path         aligned like help_args.path,
    suffix          char(*);

     dcl    i           fixed bin;

     dcl    check_star_name_$entry entry (char(*), fixed bin(35)),
    expand_pathname_$add_suffix entry (char(*), char(*), char(*), char(*), fixed bin(35));

    info_path.dir(1) = "";          /* Initialize to unset so caller can depend on  */
    info_path.ent = "";             /* these values.                */
    info_path.ep = "";
    info_path.S.less_greater = (search (info_path.value, "<>") > 0);
                        /* see if user gave more than just an entryname.    */
    i = index(reverse(info_path.value), "$");   /* see if user gave a subr entry point name.    */
    if info_path.S.less_greater then        /*   Must allow $ in entry names forming dir    */
                        /*   part of pathname.          */
         if search(reverse(info_path.value), "<>") < i then
        i = 0;
    if i > 0 then                /* save entry point name given by user in his   */
         info_path.ep = substr (info_path.value, length(info_path.value)-i+2);
    else info_path.ep = "";         /*   pathname argument.         */
    call expand_pathname_$add_suffix (substr (info_path.value, 1, length(info_path.value)-i), suffix,
         info_path.dir(1), info_path.ent, info_path.code);
    if info_path.code ^= 0 then         /* separate pathname into dir/ent parts, add info   */
         return;                /*   suffix.                */
    if info_path.S.pn_ctl_arg then      /* if -pn given, assume relative pathname follows   */
         info_path.S.less_greater = TRUE;       /*   (Note we've already expanded path on this  */
                        /*    assumption.)              */
    if info_path.info_name = "" then do;
         info_path.S.separate_info_name = FALSE;    /* info_name usually = entryname w/o suffix.    */
         if suffix = "" then
        info_path.info_name = info_path.ent;
         else info_path.info_name =
             substr(info_path.ent, 1, 32 - length(suffix) -
             index(reverse(info_path.ent), reverse(suffix)||"."));
         end;
    else info_path.S.separate_info_name = TRUE;
    call check_star_name_$entry (info_path.ent, info_path.code);
    if info_path.code = 0 then do;      /* if no starname given, -ep ctl arg allowed.   */
         info_path.S.starname_ent = FALSE;
         if help_args.Sctl.ep &
            info_path.ep = "" then      /* Default ep name = entryname w/o suffix.  */
        if suffix = "" then
             info_path.ep = info_path.ent;
        else info_path.ep =
            substr(info_path.ent, 1, 32 - length(suffix) -
            index(reverse(info_path.ent), reverse(suffix)||"."));
         end;
    else if info_path.code = 1 |            /* forbid -ep if starname was given.        */
            info_path.code = 2 then do;
         info_path.code = 0;
         info_path.S.starname_ent = TRUE;
         if help_args.Sctl.ep | (info_path.ep ^= "") then
        info_path.code = error_table_$inconsistent;
         end;

    if info_path.code ^= 0 then return;
    if info_path.S.separate_info_name then do;  /* Check star-ness of user-supplied info_name.  */
         if info_path.S.info_name_not_starname then
        info_path.S.starname_info_name = FALSE;
         else do;
        call check_star_name_$entry (info_path.info_name, info_path.code);
        if info_path.code = 1 |
           info_path.code = 2 then  do;
             info_path.code = 0;
             info_path.S.starname_info_name = TRUE;
             end;
        else info_path.S.starname_info_name = FALSE;
        end;
         end;
    else info_path.S.starname_info_name = info_path.S.starname_ent;

    end evaluate_path;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


get_info_seg_list: procedure (procedure_name, suffix, fcn,
            dirs, info_path, PDinfo_) options (non_quick);
                        /* non_quick so that the large area won't stay around   */
                        /*   all the while help active and take up stack frame  */
                        /*   space.                 */

     dcl    procedure_name      char(*),
    suffix          char(*),
    fcn         fixed bin,
    dirs (*)            char(168) unaligned,
    1 info_path     aligned like help_args.path,
    PDinfo_         ptr;

     dcl    I           fixed bin,
    Lline           fixed bin,
    Lseg            fixed bin(21),
    Nbranches           fixed bin,
    Nentries            fixed bin,
    Nlinks          fixed bin,
    Nentry_names        fixed bin,
    Nstart          fixed bin,
    Pentry          ptr,
    Pentry_name     ptr,
    Pseg            ptr,
    Ptemp           ptr,
    area            area (25000) init(empty()),
    code            fixed bin(35),
         (i, j, k)          fixed bin,
    l           fixed bin(21),
    line            char(Lline) based(Pseg),
    linfo_name      char(32),       /* info name without the suffix.        */
    sinfo_name      char(32),       /* info name with the suffix.           */
    saved_date      fixed bin(71);

     dcl    1 Dinfo_            aligned based(PDinfo_),
      2 N           fixed bin,
      2 seg (0 refer (Dinfo_.N))    like Dinfo_seg;

     dcl    1 branch            aligned,        /* returned by hcs_$status_long     */
     (2 type            bit(2),
      2 pad1            bit(34),
      2 pad2 (2)        fixed bin(35),
      2 mode            bit(5),
      2 pad3            bit(31),
      2 pad4            fixed bin(35),
      2 dtem            bit(36),
      2 pad5            fixed bin(35),
      2 pad6            bit(12),
      2 bit_count       bit(24),
      2 pad7 (2)        fixed bin(35)) unal;
\014
     dcl    1 entry (Nentries)      aligned based (Pentry),
     (2 type            bit(2),     /* returned by hcs_$star_dir_list_      */
      2 nnames      fixed bin(15),
      2 nindex      fixed bin(17),
      2 dtem            bit(36),
      2 pad1            bit(36),
      2 mode            bit(5),
      2 raw_mode        bit(5),
      2 master_dir      bit(1),
      2 bit_count       fixed bin(24)) unal,
    entry_name (Nentry_names)   char(32) aligned based (Pentry_name);

     dcl    seg         char(Lseg) based(Pseg),
                        /* The info segment.  Pseg must be declared in  */
                        /* the external procedure so its on unit    */
                        /* (janitor) can terminate the segment.     */
    seg_char (Lseg)     char(1) based(Pseg);

     dcl    hcs_$star_dir_list_     entry (char(*), char(*), fixed bin(3), ptr,
                fixed bin, fixed bin, ptr, ptr, fixed bin(35)),
    hcs_$status_long entry (char(*), char(*), fixed bin(1), ptr, ptr, fixed bin(35));
     dcl (DIRECTORY         init ("10"b),
    LINK            init ("00"b),
    SEGMENT         init ("01"b)) bit(2) aligned int static options(constant);



    Nstart = Dinfo_.N;              /* Remember count of info segs found before we  */
                        /*   start.  Then we'll know if we find any.    */
    do i = lbound(dirs,1) to hbound(dirs,1);    /* Apply info path to each dir to be searched.  */
         call hcs_$star_dir_list_ (dirs(i), info_path.ent, 3, addr(area), Nbranches, Nlinks, Pentry, Pentry_name,
        code);
         if code = 0 then do;
        Nentries = Nbranches + Nlinks;
        Nentry_names = entry(Nentries).nnames + entry(Nentries).nindex - 1;
        do j = 1 to Nentries;       /* process entries found in this directory. */
             k, Dinfo_.N = Dinfo_.N + 1;
             Dinfo_.seg(k).Scross_ref = FALSE;
             Dinfo_.seg(k).dir      = dirs(i);
             Dinfo_.seg(k).ent      = entry_name(entry(j).nindex);
             Dinfo_.seg(k).info_name    = "";
             Dinfo_.seg(k).ep       = info_path.ep;
             Dinfo_.seg(k).segment_type = entry(j).type;

                        /* Process each entry according to its type.    */
             if entry(j).type = SEGMENT then do;
            Dinfo_.seg(k).L    = divide(entry(j).bit_count, 9, 24, 0);
            Dinfo_.seg(k).date = numeric_date (entry(j).dtem);
            Dinfo_.seg(k).mode = substr(entry(j).mode,2,3);
            Dinfo_.seg(k).code = 0; /* extract "rew" mode bits from "trewa".    */
            if  Dinfo_.seg(k).L = 0  then
                 Dinfo_.seg(k).code = error_table_$zero_length_seg;
            else if  entry(j).bit_count - 9*Dinfo_.seg(k).L > 0  then
                 Dinfo_.seg(k).code = error_table_$badsyntax;
            end;
             else if entry(j).type = LINK then do;
                        /* Links must be chased, and target examined.   */
            call hcs_$status_long (Dinfo_.seg(k).dir, Dinfo_.seg(k).ent,
                 1, addr(branch), null(), code);
            if  (code = 0)  |  (code = error_table_$no_s_permission)  then do;
                 if branch.type = SEGMENT then do;
                Dinfo_.seg(k).L    = divide( binary(branch.bit_count, 24), 9, 24, 0);
                Dinfo_.seg(k).date = numeric_date (branch.dtem);
                Dinfo_.seg(k).mode = substr(branch.mode,2,3);
                Dinfo_.seg(k).code = 0;
                if  Dinfo_.seg(k).L = 0  then
                     Dinfo_.seg(k).code = error_table_$zero_length_seg;
                else if  binary(branch.bit_count, 24) - 9*Dinfo_.seg(k).L > 0  then
                     Dinfo_.seg(k).code = error_table_$badsyntax;
                end;
                 else if branch.type = LINK then do;
                Dinfo_.seg(k).L    = 0;
                Dinfo_.seg(k).date = 0;
                Dinfo_.seg(k).mode = "0"b;
                Dinfo_.seg(k).code = error_table_$noentry;
                end;
                 else do;       /* Skip matching directories.           */
                Dinfo_.N = Dinfo_.N - 1;
                go to SKIP_ENTRY;   /*   Forget everything we've done for this entry.   */
                end;
                 end;
            else do;            /* Don't have access to the link target.    */
                 Dinfo_.seg(k).L    = 0;
                 Dinfo_.seg(k).date = 0;
                 Dinfo_.seg(k).mode = "0"b;
                 Dinfo_.seg(k).code = code;
                 end;
            end;
             else do;           /* Skip matching directories.           */
            Dinfo_.N = Dinfo_.N - 1;
            go to SKIP_ENTRY;
            end;
             if  Dinfo_.seg(k).code = 0  then
            if  (Dinfo_.seg(k).mode & "100"b)  then
                 if  help_args.min_date_time ^<  Dinfo_.seg(k).date  then
                Dinfo_.N = Dinfo_.N - 1;
                 else;
            else Dinfo_.seg(k).code = error_table_$moderr;
                        /* report error if user can't access info seg.  */
SKIP_ENTRY:      end;

        free entry in (area),       /* free found entry structures.     */
             entry_name in (area);
        end;


         else if code = error_table_$incorrect_access & ^info_path.S.starname_ent then do;
                        /* If user does not have "s" permission to dir, */
                        /* look for a specific help seg.        */
        call hcs_$status_long (dirs(i), info_path.ent, 1, addr(branch), null(), code);
        if  (code = error_table_$no_s_permission)  |  (code = 0)  then do;
             if branch.type ^= DIRECTORY then do;
            k, Dinfo_.N = Dinfo_.N + 1;
            Dinfo_.seg(k).Scross_ref = FALSE;
            Dinfo_.seg(k).dir  = dirs(i);
            Dinfo_.seg(k).ent  = info_path.ent;
            Dinfo_.seg(k).info_name = "";
            Dinfo_.seg(k).ep   = info_path.ep;
            Dinfo_.seg(k).segment_type = branch.type;
            if branch.type = SEGMENT then do;
                 Dinfo_.seg(k).L    = divide( binary(branch.bit_count, 24), 9, 24, 0);
                 Dinfo_.seg(k).date = numeric_date (branch.dtem);
                 Dinfo_.seg(k).mode = substr(branch.mode,2,3);
                 if Dinfo_.seg(k).mode & "100"b then
                Dinfo_.seg(k).code = 0;
                 else Dinfo_.seg(k).code = error_table_$moderr;
                 if  Dinfo_.seg(k).L = 0  then
                Dinfo_.seg(k).code = error_table_$zero_length_seg;
                 else if  binary(branch.bit_count, 24) - 9*Dinfo_.seg(k).L > 0  then
                Dinfo_.seg(k).code = error_table_$badsyntax;
                 else if  code = 0  then
                if  help_args.min_date_time ^<  Dinfo_.seg(k).date  then
                     Dinfo_.N = Dinfo_.N - 1;
                 end;
            else do;            /* Give error for link target being a link. */
                 Dinfo_.seg(k).L    = 0;
                 Dinfo_.seg(k).date = 0;
                 Dinfo_.seg(k).mode = "0"b;
                 Dinfo_.seg(k).code = error_table_$noentry;
                 end;
            end;
             end;
        else if code = error_table_$noentry then;
        else go to DIR_ERROR;
        end;
         else if code = error_table_$nomatch then;
         else do;               /* Fatal error looking in this dir.     */
DIR_ERROR:  call com_err_ (code, procedure_name,
             "^/While looking for info segments in ^a.", dirs(i));
        if dim(dirs,1) = 1 then return; /* Avoid getting nomatch error in addition to   */
        end;                /* this one when only 1 dir to look into.   */
         end;


    if  fcn = CIS  then do;
         do i = Nstart+1 to Dinfo_.N;
        if  Dinfo_.seg(i).code ^= 0  then do;
             Dinfo_.seg(i).uid = "0"b;
             Dinfo_.seg(i).I   = 0;
             end;
        end;
         return;
         end;
    else if  Dinfo_.N = Nstart  then do;
         if info_path.S.starname_ent then
        code = error_table_$nomatch;
         else code = error_table_$noentry;
         call com_err_ (code, procedure_name, 
        "^/Looking for:  ^[-pn ^]^a", info_path.S.pn_ctl_arg, info_path.value);
         end;
    else do i = Nstart+1 to Dinfo_.N;       /* Look for :Info: info dividers.       */
         if Dinfo_.seg(i).code = 0 then do;
        Dinfo_.seg(i).uid = "0"b;       /* We don't know seg's uid yet.     */
        call initiate (Dinfo_.seg(i).dir, Dinfo_.seg(i).ent, Dinfo_.seg(i).uid, Pseg, code);
        if Pseg ^= null then do;
             Lseg = Dinfo_.seg(i).L;
             Dinfo_.seg(i).I = 1;       /* Fill in substring index of 1st       */
                        /* char of physical info seg.           */
             I = verify(seg, "   
");
             if I > 1 then do;       /* Strip HT SP NL from start of info seg.   */
            Pseg = addr(seg_char(I));
            Lseg = Lseg - (I-1);
            end;
             if Lseg > 8 then        /* See if info seg begins with :Info:       */
                        /*   (8 = length(":Info:C:"), C is any char.    */
            if substr(seg,1,6) = ":Info:" then do;
                 Pseg = addr(seg_char(7));
                 Lseg = Lseg - 6;
                 k = i;
                 Dinfo_.seg(k).info_name = info_path.info_name;
                        /* save info_name used to find infos for use in */
                        /* error messages  (without suffix).        */
                 saved_date = Dinfo_.seg(k).date;
                        /* save date assoc with phys info seg in case   */
                        /* some log. infos don't have date in their header*/
                 end;
            else Lseg, k = 0;
             else Lseg, k = 0;
             do while (Lseg > 0);        /* It does contain :Info:.  Look for info(s)    */
            Lline = index(seg, NL); /* which match user-supplied entryname.     */
            if Lline = 0 then  Lline = Lseg;
            linfo_name = find_info_name(line, I);
            do while (I > 0);
                 if info_path.S.starname_info_name then do;
                call match_star_name_ (linfo_name, info_path.info_name, code);
                if code ^= 0 then go to NO_MATCH;
                end;
                 else if linfo_name ^= info_path.info_name then
                go to NO_MATCH;

                 if ^info_path.S.separate_info_name then do;
                        /* POTENTIAL BUG:  Use of assoc. memory for     */
                        /* initiated segs may subvert test to see if    */
                        /* info_name really a name on phys. info seg.   */
                        /* Subsequent attempt to reinitiate may succeed     */
                        /* by uid found in assoc mem, rather than by name   */
                        /* being found on phys. info seg.       */
                if suffix ^= "" then
                     sinfo_name = rtrim(linfo_name) || "." || suffix;
                else sinfo_name = linfo_name;

                        /* Test now to see if log info_name is on seg.  */
                if info_path.S.starname_ent then do;
                     call hcs_$initiate (Dinfo_.seg(k).dir, sinfo_name, "", 0, 0, 
                             Ptemp, code);
                     if Ptemp = null then  go to NO_MATCH;
                     end;
                Dinfo_.seg(k).ent = sinfo_name;
                end;
                        /* This info matches.  Include it in output list.   */
                 j = Lline - index(reverse(line),":") + 2;
                 Dinfo_.seg(k).I = rel_char(addr(seg_char(j))) + 1;
                        /* get index of first char of this info.    */
                        /* 1 is added to the char offset returned by    */
                        /* rel_char to get a char index.        */
                 l = index(seg,"


:Info:");                       /* get info length by finding next info.    */
                 if l > 0 then
                Dinfo_.seg(k).L = l - (j-1);
                 else Dinfo_.seg(k).L = Lseg - (j-1);
                 Pseg = addr(seg_char(j));
                 Lseg = Lseg - (j-1);
                 Lline = Lline - (j-1);
                 j = verify(seg, "   
");
                 if j > 1 then do;   /* Remove leading HT SP NL from log info.   */
                Pseg = addr(seg_char(j));
                Lseg = Lseg - (j-1);
                Lline = index(seg, NL);
                if Lline = 0 then Lline = Lseg;
                end;
                 if Lseg >= Lline+1 then /* Store date assoc with log info.      */
                if seg_char(Lline+1) = NL then do;
                            /* Date comes from 1st field of heading line of */
                        /* log info, which must be followed by blank line.*/
                     Lline = Lline - 1;
                     j = search (line, "     ");
                     if j = 0 then
                    j = Lline;
                     else do;
                    call convert_date_to_binary_ (substr(line,1,j), Dinfo_.seg(k).date, code);
                    if code ^= 0 then
                         Dinfo_.seg(k).date = saved_date;
                    end;
                     end;
                else Dinfo_.seg(k).date = saved_date;
                 else Dinfo_.seg(k).date = saved_date;
                 I = 0;     /* Stop processing this :Info: line (this info).    */
                 if  ^(info_path.S.starname_info_name  |  info_path.S.separate_info_name)  then
                Lseg = 0;       /* If not a starname or separate info_name,     */
                        /* we've found one & only matching log. info    */
                 if help_args.min_date_time ^< Dinfo_.seg(k).date then
                go to MATCH;    /* Info modified before min date; skip it   */
                 Dinfo_.seg(k).info_name = linfo_name;
                        /* Save info_name for use in headings.      */
                 k, Dinfo_.N = Dinfo_.N + 1;
                 Dinfo_.seg(k) = Dinfo_.seg(i);
                 go to MATCH;

NO_MATCH:                Pseg = addr(seg_char(I+1));
                 Lseg = Lseg - I;   /* Look for another name on this info, since    */
                 Lline = Lline - I; /* previous names on it don't match user wants. */
                 linfo_name = find_info_name (line, I);
MATCH:               end;

            I = index(seg, "


:Info:");
            if I = 0 then Lseg = 0;
            else do;
                 Pseg = addr(seg_char(I+9));
                 Lseg = Lseg - (I+8);
                 end;
            end;
             if k = 0 then;         /* No :Info: in phys info seg.      */
             else if k = i then     /* No matching info in phys info seg.       */
            if info_path.S.starname_info_name then
                 Dinfo_.seg(i).code = error_table_$nomatch;
            else Dinfo_.seg(i).code = error_table_$noentry;
             else Dinfo_.N = Dinfo_.N - 1;  /* Matching info found.  We always get one more */
                        /* Dinfo_.seg than we can use.      */
             end;
        else Dinfo_.seg(i).code = code; /* Failed to initiate physical info seg.    */
        end;
         if Dinfo_.seg(i).code ^= 0 then do;
        Dinfo_.seg(i).uid = "0"b;       /* If error occurred during processing, mark    */
        Dinfo_.seg(i).I   = 0;      /* info to cause error message to be printed.   */
        end;
         end;
\014
find_info_name: proc (Aline, Iline) returns(char(32));
          
     dcl  Aline         char(*),        /* unprocessed part of :Info: line (incl NL).   */
          Iline     fixed bin,  /* amount processed while finding this info name. */
    info_name           char(32) varying;   /* the info_name which was found.       */

     dcl (Icolon, Inon_space, Iquote, Iquote_quote)
                    fixed bin,
    Lline           fixed bin,
    Pline           ptr;

     dcl (QUOTE         char(1) init(""""),
    QUOTE_QUOTE     char(2) init("""""")) int static options(constant);

     dcl    line            char(Lline) based(Pline),
    line_char (Lline)       char(1) based(Pline);
          
    Pline = addr(Aline);
    Lline = length(Aline);
    Inon_space = verify (line, HT_SP);      /* Remove leading white space from info name.   */
    if Inon_space > 1 then do;
         Pline = addr(line_char(Inon_space));
         Lline = Lline - (Inon_space-1);
         end;
    else if Inon_space = 0 then do;     /* Remainder of line is empty.      */
ERROR:       Iline = length(Aline);
         return("");
         end;
    
    if line_char(1) = QUOTE then do;        /* Look for quoted info name.           */
         Pline = addr(line_char(length(QUOTE)+1));  /*   Skip the opening quote.            */
         Lline = Lline - length(QUOTE);
         Iquote = index (line, QUOTE);      /*   Search for trailing quote.     */
         if  Iquote=0  |  Iquote+2>Lline  then   /*   Trailing quote is missing.     */
        go to ERROR;
         Iquote_quote = index (line, QUOTE_QUOTE);  /*   Check for doubled quotes.      */
         if Iquote ^= Iquote_quote then     /*   There are none.            */
        info_name = substr (line, 1, Iquote-1);
         else do;               /*   Doubled quotes must be undoubled in info name*/
        info_name = "";
        do while (Iquote = Iquote_quote);
             info_name = info_name || substr (line, 1, Iquote);
             Pline = addr(line_char(Iquote + length(QUOTE_QUOTE)));
             Lline = Lline - (Iquote + length(QUOTE_QUOTE) - 1);
             Iquote = index (line, QUOTE);
             if  Iquote=0  |  Iquote+2>Lline  then go to ERROR;
             Iquote_quote = index (line, QUOTE_QUOTE);
             end;
        info_name = info_name || substr (line, 1, Iquote-1);
        end;
         Pline = addr(line_char(Iquote + length(QUOTE)));
         Lline = Lline - (Iquote + length(QUOTE) - 1);
         Inon_space = verify (line, HT_SP);     /* Remove trailing white space.     */
         if Inon_space > 1 then do;
        Pline = addr(line_char(Inon_space));
        Lline = Lline - (Inon_space-1);
        end;
         else if Inon_space = 0 then go to ERROR;   /* No trailing colon.  Skip last name.      */
         if line_char(1) = ":" then     /* info name found in correct format.       */
        Iline = length(Aline) - (Lline - 1);
         else go to ERROR;          /* No trailing colon.  That's bad;      */
         end;
    else do;                    /* Info name is not quoted.         */
         Icolon = index (line, ":");
         if Icolon = 0 then go to ERROR;        /*   No trailing colon.         */
         info_name = rtrim (substr (line, 1, Icolon-1));
         Iline = length(Aline) - (Lline - Icolon);
         end;
    return (info_name);
    
    end find_info_name;
     









    end get_info_seg_list;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


initiate: proc (dir, ent, uid, Pseg, code);     /* Provide an associative memory for info segs  */
                        /* to reduce amt. of double initiating each seg.    */

     dcl    dir         char(168) unal,
    ent         char(32) unal,
    uid         bit(36) aligned,
    Pseg            ptr,
    code            fixed bin(35);

     dcl    i           fixed bin;
     dcl    Iempty          fixed bin;

    Iempty = 0;             /* No empty slots in assoc. mem so far.     */
    code = 0;
    Pseg = null;
    do i = 1 to init_assoc_mem.N while (Pseg = null);   /* Look for seg to be initiated in assoc. mem.  */
         if init_assoc_mem.seg(i).uid ^= "0"b then do;/*   Zero uid?  No, we must check the cell.   */
        if uid ^= "0"b then         /*   Can't check if we don't know segs uid. */
             if uid = init_assoc_mem.seg(i).uid then
            Pseg = init_assoc_mem.seg(i).P;
                        /*   Found seg in assoc mem. Got off cheap! */
             else;
        else if  dir = init_assoc_mem.seg(i).dir  &  ent = init_assoc_mem.seg(i).ent  then do;
                        /*   Check segs dir/ent with assoc mem.     */
             uid = init_assoc_mem.seg(i).uid;
             Pseg = init_assoc_mem.seg(i).P;
             end;
        end;
         else if Iempty = 0 then            /* Remember first empty cell in  case seg not   */
        Iempty = i;         /* found in assoc. mem.         */
         end;
    if Pseg ^= null then return;            /* See found in assoc. All done!        */

    call hcs_$initiate (dir, ent, "", 0, 0, Pseg, code);
    if Pseg = null then  return;            /* Have to initiate the segment.        */
    call hcs_$get_uid_seg (Pseg, uid, code);    /* Complain if error.  Otherwise, get seg's uid.    */
    do i = 1 to init_assoc_mem.N while (init_assoc_mem.seg(i).uid ^= uid);
         end;                   /* make sure uid doesn't appear in assoc memory */
    if i <= init_assoc_mem.N then return;        /* under another name.  If so, don't add again. */
    if Iempty = 0 then              /* If no empty cells, must make one.        */
         if init_assoc_mem.N < dimension (init_assoc_mem.seg, 1) then do;
        init_assoc_mem.N = init_assoc_mem.N + 1;
        Iempty = init_assoc_mem.N;      /* Add new cell to the table, if room.      */
        end;
         else do;               /* Must terminate cell member to make room for new*/
        Iempty = init_assoc_mem.N;      /* seg in assoc. mem.           */
        call hcs_$terminate_noname (init_assoc_mem.seg(Iempty).P, code);
        end;
    init_assoc_mem.seg(Iempty).dir = dir;
    init_assoc_mem.seg(Iempty).ent = ent;
    init_assoc_mem.seg(Iempty).uid = uid;
    init_assoc_mem.seg(Iempty).P = Pseg;
    return;
\014
terminate: entry (Pseg, code);

    do i = init_assoc_mem.N to 1 by -1 while (Pseg ^= init_assoc_mem.seg(i).P);
         end;                   /* Start looking at end of assoc. mem. since seg    */
    init_assoc_mem.seg(i).uid = "0"b;       /* is most likely to be there.      */
    if i = init_assoc_mem.N then
         init_assoc_mem.N = init_assoc_mem.N - 1;
    call hcs_$terminate_noname (Pseg, code);

    end initiate;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


janitor: procedure;                 /* terminate known info segs;  truncate temp seg.   */

    call term_assoc_mem();
    call hcs_$truncate_seg (Phelp_args, currentsize(help_args), 0);

    end janitor;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


numeric_date: procedure (bit_date) returns (fixed bin(71));
                        /* This procedure converts a file system date   */
                        /* to a numeric clock value.  A file system date    */
                        /* is the high-order 36 bits of a 52 bit clock  */
                        /* value.                   */

     dcl    bit_date        bit(36) unal,
    num_date        fixed bin(71);


    num_date = 0;
    substr(unspec(num_date),21,36) = bit_date;
    return (num_date);

    end numeric_date;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


rel_char: proc (P) returns(fixed bin(21));      /* This procedure converts a pointer value into */
                        /* a character offset from base of segment  */
                        /* pointed to.  We need a PL/I bif to do this.  */
     dcl    P           ptr;

     dcl    I           fixed bin(21),
    P1          ptr,
    i           fixed bin;

     dcl    char_offset (0:3)       char(1) based(P1);

    P1 = ptr(P, rel(P));
    I = 4 * binary(rel(P));
    do i = 0 to 3 while (addr(char_offset(i)) ^= P);
         end;
    I = I + i;
    return(I);

    end rel_char;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


set_space_used: procedure (Pcurrent_space, size_current_space) returns(ptr);
                        /* This procedure returns pointer to next free  */
                        /* word of storage in help_args temp segment.   */
     dcl    Pcurrent_space      ptr,        /* ptr to last space allocated in the seg.  */
    size_current_space      fixed bin(21),  /* amount of space used in structure last alloc.    */
    Pnext_space     ptr;        /* ptr to next free space.          */

    Pnext_space = addrel (Pcurrent_space, size_current_space + mod(size_current_space,2));
    return (Pnext_space);

    end set_space_used;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


term_assoc_mem: procedure;              /* terminate known info segs.           */

    do init_assoc_mem.N = init_assoc_mem.N to 1 by -1;
         if init_assoc_mem.seg(init_assoc_mem.N).uid ^= "0"b then
        call hcs_$terminate_noname (init_assoc_mem.seg(init_assoc_mem.N).P, 0);
         end;

    end term_assoc_mem;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


process_info_seg: procedure (procedure_name, suffix, Iinfo, Ninfos_printed, Ninfos, Nlast_info_no_brief_data,
            Nlast_info_cross_ref, Dinfo_seg_, PDeps);
                        /* This procedure does all the work of printing */
                        /* each info.               */
     dcl    procedure_name      char(*),
    suffix          char(*),
    Iinfo           fixed bin,  /* Number of the info being processed.      */
    Ninfos_printed      fixed bin,  /* Number of infos for which something has printed*/
    Ninfos          fixed bin(24),  /* Number of infos handled during this invocation   */
    Nlast_info_no_brief_data    fixed bin,  /* Last info processed not containing Syntax sect.*/
    Nlast_info_cross_ref    fixed bin,  /* Last info processed with Scross_ref on.  */
                        /* as diagnosed by get_brief_data.      */
    1 Dinfo_seg_        aligned like Dinfo_seg,
    PDeps           ptr;

     dcl    Iep         fixed bin,  /* subscript of current entry point or info */
                        /*   (logical info segment) being processed.    */
    Iunit           fixed bin,  /* subscript of current unit (paragraph).   */
    Iunit_end           fixed bin,
    Iunit_search        fixed bin,  /* searching begins with this unit.     */
    Iunit_syntax (10)       fixed bin,  /* indices of syntax units.         */
    Lcount          fixed bin,
    Linfo_name      fixed bin,
    Loutput         fixed bin,
    Lpath           fixed bin,
    Lpgh            fixed bin,
    Lseg            fixed bin(21),
         (Ncommon_units, Nconsecutive_bad_ops, Nuncommon_units, Nprint_units)
                fixed bin,
         (Nlines, Nlines_titles)    fixed bin,
         (Nlists_of_args, Nlists_of_bf_args)
                fixed bin,
    Nunit_syntax        fixed bin,  /* number of syntax units.          */
         (Plist, Plist_of_titles, Plist_of_cas)
                ptr,
    Pcommon_units       ptr,
    PDlinfo         ptr,
    Plist_base      ptr,
    Plists_of_args (18)     ptr,
    Poutput         ptr,
    Ppgh            ptr,
    Pseg            ptr,
    Sfound          bit(1) aligned,
    Sloop           bit(1) aligned,
         (Snl1,Snl2)        bit(1) aligned, /* Switches used to compute if NL should be output.*/
    ISnl3           fixed bin,
    Ssearch         bit(1) aligned, /* on if -section/-search searching to be done. */
    Sseen           bit(1) aligned, /* on if pgh already seen by user.      */
    answer          char(500) varying,
    ep_name         char(65) varying,
         (i, j)         fixed bin,
    match_result        fixed bin,
      (no_match     init(0),
       match            init(1),
       exact_match      init(2)) fixed bin int static options(constant),
    new_section     char(88) varying,   /* title of new section in which match pgh found    */
    op          fixed bin,
    query           char(200) varying,
    query_type      fixed bin,
      (normal       init(1),
       some_unseen      init(2),
       search_unseen        init(3),
       new_entry        init(4)) fixed bin int static options(constant),
    ref_name            char(32) varying;

     dcl    1 query_info        aligned int static options(constant),
      2 version     fixed bin init(2),
      2 yes_or_no_sw        bit(1) unal init("0"b),
      2 suppress_name_sw    bit(1) unal init("1"b),
      2 CODE            fixed bin(35) init(0),
      2 query_code      fixed bin(35) init(0);

     dcl    1 list_base     aligned based(Plist_base),
                        /* struc locating lists of things to be output. */
      2 N           fixed bin,  /*   number of output lists now allocated.  */
      2 Nmax            fixed bin,  /*   max number of list ptrs allocatable.   */
      2 Ispace_used_set     fixed bin,  /*   index of last list on which space used set.    */
      2 Plists (0 refer(list_base.Nmax))
                ptr;        /*   ptrs to allocated lists.           */

     dcl    1 list          aligned based(Plist),
      2 header      like LIST.header,
      2 group (0 refer (list.N))    like LIST.group;
                        /* struc containing lists of things to be output.   */
     dcl    1 Deps          aligned based (PDeps),
                        /* structure defining all entry points in log info*/
      2 Nlines      fixed bin,  /*   number of lines in log info.       */
      2 N           fixed bin,  /*   total number of entry points in log info.  */
      2 linfo (0: 0 refer (Deps.N)),        /*   description of each entry point.       */
        3 date      fixed bin(71),  /*     binary date assoc with entry point.  */
        3 Nep_names     fixed bin,  /*     number of entry point names.     */
        3 ep_name (20)      char(32) var,   /*     name of the entry point.     */
        3 PDlinfo       ptr,        /*     ptr to paragraph descriptors for this info.*/
        3 Pstart        ptr,        /*     first character of entry point info. */
        3 L         fixed bin,  /*     length (in chars) of entry point info.   */
        3 header        char(88) varying,   /*     its heading line.            */
        3 Nlines        fixed bin,  /*     number of lines in entry point info. */
        3 S,                    /*     switches:                */
         (4 seen_by_user,           /*       this entry point seen by the user. */
          4 old_format)     bit(1) unal,    /*       this entry point contains \006 chars.  */
          4 pad1        bit(34) unal;
\014
     dcl    1 Dlinfo            aligned based (PDlinfo),
                        /* structure defining all paragraphs (units) in */
                        /*   an entry point (misnamed linfo).       */
      2 Nunits      fixed bin,  /*   number of units in this ep.        */
      2 Nsections       fixed bin,  /*   number of units beginning a section.   */
      2 unit (0 refer (Dlinfo.Nunits)),     /*   unit (paragraph) descriptors.      */
        3 Pstart        ptr,        /*     ptr to first char of unit (excl. title). */
        3 title     char(80) varying,   /*     title of the unit.           */
        3 L         fixed bin(21),  /*     length of the unit (in chars).       */
        3 Nlines        fixed bin,  /*     number of lines in the unit.     */
        3 S         aligned,        /*     switches.                */
         (4 scn,                /*       unit begins a new section.     */
          4 seen_by_user,           /*       unit has been seen by user.        */
          4 ep_list,                /*       unit is an entry point list, to be     */
                        /*       generated by help_.            */
          4 arg_list)       bit(1) unal,    /*       unit is Arguments or Control args. */
          4 pad1        bit(14) unal,
        3 Icommon_unit      fixed bin(17) unal; /*     Index of common pgh in common_units. */

     dcl    1 common_units (Ncommon_units) aligned like Dlinfo.unit based(Pcommon_units);

     dcl    1 ca            aligned,        /* current control_arg STRs.            */
      2 header      like LIST.header,
      2 group (100)     like LIST.group,
    1 scn           aligned,        /* current section STRs.            */
      2 header      like LIST.header,
      2 group (100)     like LIST.group,
    1 srh           aligned,        /* current search STRs.         */
      2 header      like LIST.header,
      2 group (100)         like LIST.group;

     dcl    output          char(Loutput) based(Poutput);

     dcl    pgh         char(Lpgh) based(Ppgh);

     dcl    seg_char (Lseg)     char(1)    based(Pseg);

\014
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* 1) Report any errors encountered while finding physical info segment.        */
    /* 2) Initiate the physical info segment.                   */
    /* 3) Parse up the physical info segment into logical info segments (infos).        */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */

    PI_LABEL = NEXT_INFO;           /* Before any output starts, a pi skips to next */
                        /* info.                    */
    Sprint_inhibit = FALSE;         /* Printing is not inhibited yet.       */
    ca.N, scn.N, srh.N = 0;         /* No control_arg, search or section args done. */
    ref_name = "";              /* No entry point reference name set yet.   */
    if Dinfo_seg_.code ^= 0 then do;        /* Print any error encountered while finding seg.   */
INIT_ERROR:     call com_err_ (Dinfo_seg_.code, procedure_name,
        "^/While processing ^[link^;segment^;directory^] ^a^[>^]^a^[
Looking for an info matching ^a^].",
        binary (Dinfo_seg_.segment_type, 2) + 1,
        Dinfo_seg_.dir, Dinfo_seg_.dir ^= ">", Dinfo_seg_.ent,
        (Dinfo_seg_.info_name ^= ""), Dinfo_seg_.info_name);
         go to RETURN;
         end;
    call initiate (Dinfo_seg_.dir, Dinfo_seg_.ent, Dinfo_seg_.uid, Pseg, code);
    if Pseg = null then go to INIT_ERROR;       /* Initiate the info segment.           */
    Lseg = Dinfo_seg_.I;            /* Address first char of logical info.      */
    Pseg = addr(seg_char(Dinfo_seg_.I));
    Lseg = Dinfo_seg_.L;            /* Address all/only log info we are printing.   */
    if Lseg = 0 then do;
         code = error_table_$zero_length_seg;
         go to INIT_ERROR;
         end;
    call parse_info_into_entry_points (Pseg, Lseg, PDeps);
                        /* Parse up the log info into entry points. */



    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* Various kinds of output (arguments and control arguments, section titles,        */
    /* entry point names, etc) are output in columnar lists.  More than one list        */
    /* may exist at a time.  Initialize array of list pointers to keep track of them.   */
    /* The lists themselves are appended to the end of the segment containing       */
    /* the help_args structure, as are all of the variable size structures used in help_.   */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */

    Pnext_free_space = addrel(PDeps, currentsize(Deps));
                        /* reuse space for lists, etc each time that    */
                        /* process_info_seg is called.      */
    Plist_base = get_list_base (Pnext_free_space, currentsize(Deps), 30);
                        /* get space for gen'l purpose list of lists.   */
\014
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* 1) Get space for the descriptor of the paragraphs (units) in the common (or only) part   */
    /*     of the logical info.  Parse this common part into pgh units.         */
    /* 2) If there are other entry point descriptions in the log info, then     */
    /*    get space for their paragraph descriptors.                    */
    /*    Parse them up into pghs, and append to their descriptors the common units     */
    /*    (paragraphs included in all entry points) obtained from the common info       */
    /*    descriptors created in step 1 above.                  */
    /*    All entry point parts must be parsed now to get line count of entire info right.  */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */

    Ncommon_units = 0;              /* No common info has been found yet.       */
    PDlinfo = Pnext_free_space;         /* get space for paragraph descriptions of common   */
                        /* or only part of logical info.        */
    call parse_entry_point_into_units (Deps.linfo(0), Pcommon_units, Ncommon_units, PDlinfo);
    Pnext_free_space = set_space_used (PDlinfo, currentsize(Dlinfo));
    if Deps.N > 0 then do;           /* handle log. info w/ several entry point parts.   */
         do Nuncommon_units = 2 to Dlinfo.Nunits
        while (^Dlinfo.unit(Nuncommon_units).S.scn);
                        /* Find paragraphs in common part which are */
                        /* shared by (common to) all entry point parts. */
        end;
         Nuncommon_units = Nuncommon_units - 1;
         Ncommon_units = Dlinfo.Nunits - Nuncommon_units;
         if  (Ncommon_units = 0)  &  (Nuncommon_units = 1)  then
        if length(Dlinfo.unit(1).title) > length("Entry points in") then
        if substr(Dlinfo.unit(1).title,1,length("Entry points in ")) = "Entry points in " then do;
             Nuncommon_units = 0;
             Ncommon_units = 1;
             end;
         if Ncommon_units > 0 then do;
              Pcommon_units = addr (Dlinfo.unit(Nuncommon_units+1));
        end;
         else Pcommon_units = PDlinfo;
         do i = 1 to Ncommon_units;     /* Find section of common part containing   */
                        /*   help-generated list of entry points in info.   */
        if length(common_units(i).title) > 15 then   /* 15 = length("Entry points in "). */
        if substr(common_units(i).title,1,15) = "Entry points in " then do;
             common_units(i).S.ep_list = TRUE;
             j = i;
             do i = i to Ncommon_units; /* Remove any pghs following this special one   */
                        /*   from the common part of the info.      */
            Deps.linfo(0).Nlines = Deps.linfo(0).Nlines -
                 common_units(i).Nlines - 2;
            end;            /* Subtract line count of pghs following the    */
                        /* "Entry points in " section.      */
             Ncommon_units = j;     /* "Entry points in " is last pgh of info.  */
             Dlinfo.Nunits = Nuncommon_units + Ncommon_units;
             end;
        end;
\014
         if Ncommon_units > 0 then
         if common_units(Ncommon_units).S.ep_list then do;
        Plist = get_list (Plist_base);  /* Build entry point list pghs in temp seg. */
        list.title = common_units(Ncommon_units).title;
        if ref_name = "" then
             if suffix = "" then
            ref_name = rtrim(Dinfo_seg_.ent);
             else ref_name = substr(Dinfo_seg_.ent, 1, 32 - length(suffix) -
                      index(reverse(Dinfo_seg_.ent), reverse(suffix) || "."));
        call get_ep_list (ref_name, PDeps, Plist);
        call format_list (Plist, divide(list.N, 5, 17, 0) + 1, 0);
        Ncommon_units = Ncommon_units - 1;  /* Forget about empty entry point list pgh for now*/
        Poutput, Pnext_free_space = set_space_used (Plist, currentsize(list));
        do i = 1 to list.Npghs;     /* Create new entry point list pghs.        */
             call output_list (Plist, i, Poutput, Loutput, Nlines);
             j, Ncommon_units = Ncommon_units + 1;
             common_units(j).Pstart = Poutput;  /*   Add new pghs to end of common units.   */
             common_units(j).L = Loutput;
             common_units(j).Nlines = Nlines;
             Deps.linfo(0).Nlines = Deps.linfo(0).Nlines + Nlines + 2;
             common_units(j).S = "0"b;
             if i = 1 then do;      /*   Include section title for 1st pgh of ep list.*/
            common_units(j).title = list.title;
            common_units(j).S.scn = TRUE;
            end;
             else do;           /*   No section title for subsequent pghs.  */
            common_units(j).title = "";
            end;
             common_units(j).S.ep_list = TRUE;  /*   Remember how pghs got there (for debugging).   */
             Poutput, Pnext_free_space = set_space_used (Poutput, currentsize(output));
             end;               /*   Get space for next pgh.            */
        Dlinfo.Nunits = Nuncommon_units + Ncommon_units;
        list_base.N = list_base.N - 1;  /* Discard list containing entry point names.   */
        end;
         do i = 1 to Ncommon_units;     /* Mark all common units by number.     */
        common_units(i).Icommon_unit = i;   /* This will help avoid seeing common units in  */
        end;                /* every entry point info.          */
         PDlinfo = Pnext_free_space;
         do i = 1 to Deps.N;            /* Parse all other entry points to count lines. */
        call parse_entry_point_into_units (Deps.linfo(i), Pcommon_units, Ncommon_units, PDlinfo);
        PDlinfo, Pnext_free_space = set_space_used (PDlinfo, currentsize(Dlinfo));
        end;                /* Common pghs added to other entries when parsed.*/
         end;
    else do;
         Pcommon_units = PDlinfo;
         Ncommon_units = 0;
         end;
    Deps.Nlines = sum(Deps.linfo.Nlines);       /* Count lines in total info.           */
\014
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* Copy -section and -search control arguments.                 */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */

    if help_args.Sctl.scn then do;      /* Copy -section args to local storage.     */
         do i = 1 to min(help_args.Nscns, dim(scn.arg,1));
        scn.arg(i) = help_args.scn(i);
        end;
         scn.N  = i-1;
         end;
    if help_args.Sctl.srh then do;      /* Copy -search args to local storage.      */
         do i = 1 to min(help_args.Nsrhs, dim(srh.arg,1));
        srh.arg(i) = help_args.srh(i);
        end;
         srh.N = i-1;
         end;
\014
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* Find the correct logical info segment (info), if any was requested by user.      */
    /* If desired info was not found, then any searching required for the           */
    /* -section and -search control arguments cannot and will not be done, though the   */
    /* operands given with these control arguments are stored as the default values to be   */
    /* used with the section and search requests if first issued without operands.      */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */

    if  Dinfo_seg_.ep = ""  then do;        /* if no entry point requested,     */
         if  help_args.min_date_time ^= -1  then do;    /*   process 1st newer than given date/time */
        do Iep = 0 to Deps.N while (help_args.min_date_time ^< Deps.linfo(Iep).date);
             end;               /*   iff a nonzero date/time selector was given.    */
        if  Iep > Deps.N  then  Iep = 0;
        end;
         else if  help_args.Sctl.scn | help_args.Sctl.srh  then do;
        Ssearch = FALSE;            /*   process 1st entry containing matches for   */
        Iunit = 1;          /*   -section and/or -search  ctl_args.     */
        if       help_args.Sctl.scn  &  help_args.Sctl.srh  then do;
             do Iep = 0 to Deps.N while(^Ssearch);
            match_result = find_section (Deps.linfo(Iep).PDlinfo, scn, Iunit);
            if match_result ^= no_match then
                 Ssearch = find_pgh (Deps.linfo(Iep).PDlinfo, srh, Iunit, new_section);
            end;
             end;
        else if  help_args.Sctl.scn  then do;
             do Iep = 0 to Deps.N while(^Ssearch);
            match_result = find_section (Deps.linfo(Iep).PDlinfo, scn, Iunit);
            Ssearch = (match_result ^= no_match);
            end;
             end;
        else do;
             do Iep = 0 to Deps.N while(^Ssearch);
            Ssearch = find_pgh (Deps.linfo(Iep).PDlinfo, srh, Iunit, new_section);
            end;
             end;
        if ^Ssearch then return;
        Iep = Iep - 1;
        end;
         else  Iep = 0;             /* otherwise, process general description.  */
         Ssearch = TRUE;
         end;
\014
    else do;                    /* else search for requested entry point.   */
         Sfound = FALSE;
         do Iep = 1 to Deps.N while (^Sfound);
        do i = 1 to Deps.linfo(Iep).Nep_names while(^Sfound);
             if Dinfo_seg_.ep = Deps.linfo(Iep).ep_name(i) then
            Sfound = TRUE;
             end;
        end;
         if Sfound then do;
        Iep = Iep - 1;
        Ssearch  = TRUE;            /* Do -section/-search matching if user asked.  */
        end;
         else do;               /*   requested ep not found.            */
        if Dinfo_seg_.info_name = "" then
             Linfo_name = 0;
        else Linfo_name = length(rtrim(Dinfo_seg_.info_name)) + length(" ()");
        call com_err_ (error_table_$noentry, procedure_name, 
             "^/Looking for entry point ^a in info^[ ^a^/(^a^[>^]^a)^;^s^/^a^[>^]^a^]",
             Dinfo_seg_.ep, Linfo_name>0, Dinfo_seg_.info_name,
             Dinfo_seg_.dir, Dinfo_seg_.dir^=">", Dinfo_seg_.ent);
        Ssearch = FALSE;            /* Don't do -section/-search matching.      */
        Iep = 0;
        end;
         end;
    PDlinfo = Deps.linfo(Iep).PDlinfo;      /* Address entry point user wants first.    */
\014
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* When -header is given without other control arguments, generate a heading line   */
    /* containing full pathname of physical info segment, title line from logical       */
    /* info segment, line count of logical info segment, and count of logical info segments */
    /* (infos) in physical info seg (excluding common portion at the beginning).        */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */

    Ninfos_printed = Ninfos_printed + 1;        /* Beyond this point, something must get printed.   */
    if  Dinfo_seg_.Scross_ref then  do;     /* Just remark about existence of other versions    */
                        /* of an info.              */
         if Ninfos_printed = 1 then do;
        call ioa_ ("^a: No infos matching -section and -search control arguments were found.", procedure_name);
        call ioa_ ("However, several infos appear more than once in the search paths.");
        call ioa_ ("The following secondary info(s) match -section and -search control arguments.");
        end;
         else if Nlast_info_cross_ref ^= Iinfo-1 then do;
        call ioa_ ("^v/^a: Other versions of the info^[s^] above were found.  See also:", 
             help_args.Lspace_between_infos, procedure_name, Ninfos_printed>2);
        end;
         call ioa_ ("  ^a^[>^]^a", Dinfo_seg_.dir, Dinfo_seg_.dir^=">", Dinfo_seg_.ent);
         Nlast_info_cross_ref = Iinfo;
         go to RETURN;
         end;
    else if help_args.Sctl.he_only then do;     /* When -header is given without other ctl_args */
                        /*   output the header and return.      */
         call print_header_only();
         go to RETURN;
         end;


    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* The -brief control argument requests that the "Syntax" section (or "Usage" section of    */
    /* old format info segs) be output in full, along with a list of arguments and control  */
    /* arguments from the "Arguments" and "Control arguments" sections.         */
    /* 1) Find "Syntax" or "Usage" sections, and count lines in these sections.     */
    /* 2) Find "Arguments" and "Control arguments" sections, and build lists of arguments.  */
    /*    Count output lines in each list.                      */
    /* 3) Output a header line, optionally given full pathname of physical info seg (-header)   */
    /*    as well as number of lines in the brief output, total lines in the info, and  */
    /*    count of (other) infos in this physical info seg.             */
    /* 4) Output the "Syntax" or "Usage" section.                   */
    /* 5) Output the columnar lists of "Arguments" and "Control arguments".     */
    /* 6) Stop processing this physical info segment, and move on to the next specified */
    /*    by user (if any).                         */
    /*                                  */
    /* When -control_arg is given, output description of all args/ctl_args whose name lines */
    /* contain match for substring identifier(s) given as operands by the user.     */
    /* 1) Find "Argument" and "Control argument" name lines which contain one of the    */
    /*    substrings given by the user after -control_arg.              */
    /* 2) Store those argument description lines in a list.             */
    /* 3) Print the argument description lines in the list after an appropriate heading.    */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
\014
    if help_args.Sctl.bf |
       help_args.Sctl.ca then do;           /* Print argument descriptions when -ca given.  */
         Nlines = 1;                /* Count lines to be output.            */
                        /*   Add 1 line for heading line.       */
         if help_args.Sctl.bf then do;
        call get_brief_data (Deps.linfo(Iep).S.old_format, help_args.Sctl.he_pn, PDlinfo,
                Plist_base, Dinfo_seg_.dir, Dinfo_seg_.ent, Nlast_info_no_brief_data,
                Iinfo, Ninfos_printed, Iunit_syntax,
                Nunit_syntax, Nlists_of_bf_args, Nlines);
        if Nlines = 1 then go to RETURN;
        end;
         else do;
        Nunit_syntax = 0;
        Nlists_of_bf_args = 0;
        if ^brief_data_ok (Deps.linfo(Iep).S.old_format, help_args.Sctl.he_pn, PDlinfo,
                     Dinfo_seg_.dir, Dinfo_seg_.ent, Iinfo, Ninfos_printed,
                     Nlast_info_no_brief_data) then go to RETURN;
        end;

         if help_args.Sctl.ca then do;      /* Get control argument descriptions.       */
        Plist, Plist_of_cas = get_list (Plist_base);
        list.N = help_args.Ncas;        /*   Begin by copying user-supplied arg names.  */
        list.arg = help_args.ca;
        list.title = "-control_arg";        /*   Get one list for each section with ctl args.   */
        call get_arg_descriptions (Plist_of_cas, PDlinfo, Plist_base,
             Deps.linfo(Iep).S.old_format, Plists_of_args, Nlists_of_args);
        do i = 1 to Nlists_of_args;     /*   Count output lines in each list.  Lists are    */
             Plist = Plists_of_args(i); /*   separated by 2 1 line, with 1 line for */
             Nlines = Nlines + list.N + 2;  /*   title of section containing the args.  */
             end;
        end;

         if length(Deps.linfo(Iep).header) = 0 then Nlines = Nlines - 2;
                        /* No title?  Remove its line count.        */
         if  Ninfos > 1  then            /* Suppress heading if only 1 info being printed.   */
        call print_header();
         call print_brief_data (PDlinfo, Ninfos>1, Plist_base,
        Iunit_syntax, Nunit_syntax, Nlists_of_bf_args);

         if help_args.Sctl.ca then do;      /* Print ctl arg descriptions, section by sect. */
        do j = 1 to Nlists_of_args;
             Plist = Plists_of_args(j);
             call ioa_ ("^[^/^]^a:", (j>1  |  Ninfos>1  |
            (help_args.Sctl.bf & help_args.Sctl.ca)), list.title);
             do i = 1 to list.N;
            call ioa_ ("^a", list.arg(i));
            end;
             end;
        end;
         go to RETURN;              /*   Stop when -brief or -control_arg given.    */
         end;
\014
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* When -title is given, output a heading line and titles of paragraph sections.    */
    /* The heading line contains:                           */
    /* 1) The full pathname of the info segment (if -header was given).         */
    /* 2) The primary title line from the info selected by the user.            */
    /* 3) The count of section title lines to be output.                */
    /* 4) Count of total lines in logical info segment.             */
    /* 5) Count of (other) infos in this physical info segment.             */
    /* Output a list of section titles in columnar form.                */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */


    PI_LABEL = QUERY;               /* once printing starts, pi skips to next query.    */
    Iunit = 0;              /* No pghs printed so far.          */
    if  help_args.Sctl.title  &  
      ((Dlinfo.Nsections > 1)  |
       (Dlinfo.Nsections = 1  &  ^Dlinfo.unit(1).S.scn))
    then do;                    /* Print pgh titles when -title is given only if    */
                        /* more than one title will be printed.     */
         if length(Deps.linfo(Iep).header) > 0 then
        Nlines = 1;
         else Nlines = -1;
         Plist_of_titles = get_list (Plist_base);
         call get_title_list (PDlinfo, Plist_of_titles, 0);
         call format_list (Plist_of_titles, divide(Dlinfo.Nsections,7,17,0)+1, 1);
         Nlines = Nlines + Plist_of_titles->list.Nrows + 1;
         if help_args.Sctl.all then do;
        Nlines = Nlines + Deps.linfo(Iep).Nlines + 2;
        if length(Deps.linfo(Iep).header) > 0 then  Nlines = Nlines - 2;
        end;
         call print_header();
         call print_list(Plist_of_titles, Sprint_inhibit);
         Pnext_free_space = Plist_of_titles;
         list_base.N = list_base.N - 1;     /* Free list of titles.         */
         if help_args.Sctl.all then do;
        do Iunit = 1 to Dlinfo.Nunits;
             call print_pgh_2nl (Dlinfo.unit(Iunit), Sprint_inhibit);
             end;
        go to QUERY;
        end;
         end;
\014
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* When -title not given, begin printing paragraphs.  Normally start with first pgh.    */
    /* However, if -section  is given, search for section whose title contains      */
    /* user-specified substrings.  Print first pgh of this section if found.        */
    /* If -search is given, search for pgh containing user-specified substrings.        */
    /* Start with first matching pgh.  If both -section and -search are given, position to  */
    /* matching section before searching more matching pgh.             */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */


    else do;
         Iunit = 1;             /* Start searching in first unit.       */
         new_section = "";
         if ^help_args.Sctl.all  & 
            ^help_args.Sctl.title  & 
            (help_args.Sctl.scn |           /* Select pgh when  -section given.     */
             help_args.Sctl.srh)  then do;      /* Select pgh when -search given.       */
        if help_args.Sctl.scn then do;  /* Search all section titles for one containing */
             match_result = find_section (PDlinfo, scn, Iunit);
             if  match_result = no_match  then return;
             end;
        if help_args.Sctl.srh then do;  /* Search subsequent pghs for one containing    */
             Ssearch = find_pgh (PDlinfo, srh, Iunit, new_section);
             if ^Ssearch then return;
             end;
        end;
\014
         if help_args.Sctl.all then do;
        Nlines = Deps.linfo(Iep).Nlines;
        Nprint_units = Dlinfo.Nunits;
        end;
         else do;
              Nlines = Dlinfo.unit(Iunit).Nlines + 2;   /*   Add 2 for entry point heading.     */
        if length(Deps.linfo(Iep).header) = 0 then Nlines = Nlines - 2;
                        /*   However, if no heading, subtract the 2 lines.*/
              if  new_section^=""  & ^Dlinfo.unit(Iunit).S.scn  then
             Nlines = Nlines + 1;       /* Add 1 line for section title of matched pgh. */
              Nprint_units = 1;         /* Print one pgh.  If -section and -search were */
                        /* not given, print more pghs as well so long   */
                        /* as total lines fewer than help_args.max_Lpgh */
                        /* and additional pghs shorter than min_Lpgh.   */
              if ^(help_args.Sctl.scn | help_args.Sctl.srh) then
              do while (Iunit+Nprint_units <= Dlinfo.Nunits & 
           Dlinfo.unit(Iunit+Nprint_units).Nlines < help_args.min_Lpgh & 
           Dlinfo.unit(Iunit+Nprint_units).Nlines + 2 + Nlines <= help_args.max_Lpgh);
             Nlines = Dlinfo.unit(Iunit+Nprint_units).Nlines + 2 + Nlines;
             Nprint_units = Nprint_units + 1;
             end;
        end;
         call print_header();
         if  new_section^=""  &  ^Dlinfo.unit(Iunit).S.scn  then do;
        call ioa_ ("^/^a:", new_section);
        call print_pgh_nnl (Dlinfo.unit(Iunit), Sprint_inhibit);
        end;
         else if  ^help_args.Sctl.he_pn  & 
            ^help_args.Sctl.he_info_name  & 
            ^help_args.Sctl.he_counts  & 
            length(Deps.linfo(Iep).header) = 0  then
        call print_pgh_nnl (Dlinfo.unit(Iunit), Sprint_inhibit);
         else call print_pgh (Dlinfo.unit(Iunit), Sprint_inhibit);
         do Iunit = Iunit + 1  to  Iunit + Nprint_units - 1;
        call print_pgh_2nl (Dlinfo.unit(Iunit), Sprint_inhibit);
        end;
         Iunit = Iunit - 1;
         end;
\014
QUERY:  Nconsecutive_bad_ops = 0;           /* No errors in responses so far.       */
    Ssearch = FALSE;                /* no searching for matching section/pgh underway.*/
    Sloop = TRUE;               /* Loop through all paragraphs of info.     */
    Iunit = Iunit + 1;              /* Beginning with the next one.     */
    query_type = normal;            /* Print normal section/pgh messages for now.   */

    do while (Sloop);               /* Print remaining pghs under user control. */
         PI_LABEL = ASK;            /* Recompute query after most program_interrupt's.*/
         if Iunit > Dlinfo.Nunits then       /* Detect end_of_info and handle specially, but     */
        go to END_OF_INFO;          /* still remain within do group.        */
ASK:         if  query_type = normal  |
             query_type = some_unseen  |
             query_type = search_unseen  then do;
        Sseen = seen_pgh (Dlinfo.unit(Iunit));  /* Tell user in query if he's already seen pgh. */
        Nlines = Dlinfo.unit(Iunit).Nlines;
        Nprint_units = 1;           /* Normally print one pgh at a time.        */
        if  Dlinfo.unit(Iunit).S.scn  |  Iunit=1  then do;
                        /* However, if pgh begins a section and following   */
             if  Iunit = 1  &  ^Dlinfo.unit(1).S.scn then
                  query = "UNTITLED";
             else query = Dlinfo.unit(Iunit).title;
                        /* pghs are shorter than min_Lpgh, print them   */
             do i = Iunit+Nprint_units by 1 /* as well, until max_Lpgh lines are aggregated.    */
             while (i <= Dlinfo.Nunits  &    /*   When aggregating sections, include all */
             ^Ssearch  &        /*   section titles in the query.       */
              Sseen = seen_pgh (Dlinfo.unit(i))  & 
              Dlinfo.unit(i).Nlines < help_args.min_Lpgh  & 
              Dlinfo.unit(i).Nlines + 2 + Nlines <= help_args.max_Lpgh);
            Nlines = Nlines + Dlinfo.unit(i).Nlines + 2;
                        /*   Must skip 2 lines between pghs to keep line    */
                        /*   count of total info equal to count of all  */
                        /*   printed lines.             */
            if Dlinfo.unit(i).S.scn then  do;
                 query = query || "  &  ";
                 if length (query) + length(Dlinfo.unit(i).title) + 12 > Loutput_line then
                      query = query || NL;/* 12 = length ( "(nnn lines)." )     */
                 query = query || Dlinfo.unit(i).title;
                 end;
            Nprint_units = Nprint_units + 1;
            end;
             end;
        else do;                /* If pgh doesn't begin a section, we can only  */
             do i = Iunit+Nprint_units by 1 /* aggregate pghs in the current section.   */
             while (i <= Dlinfo.Nunits  &    /* Note that, here and above, if current pgh    */
                   ^Dlinfo.unit(i).S.scn  & /* has already been seen, then can only aggregate   */
                   ^Ssearch  &      /* following pgh if it has been seen as well.   */
                    Sseen = seen_pgh (Dlinfo.unit(i))  & 
                    Dlinfo.unit(i).Nlines < help_args.min_Lpgh  & 
                    Dlinfo.unit(i).Nlines + 2 + Nlines <= help_args.max_Lpgh);
                  Nlines = Nlines + Dlinfo.unit(i).Nlines + 2;
                  Nprint_units = Nprint_units + 1;
                  end;
              end;
        end;
\014
         else if  query_type = new_entry  then  do;
        Sseen = Deps.linfo(Iep).S.seen_by_user;
        Nlines = Dlinfo.unit(Iunit).Nlines;
        Nprint_units = 1;
        do while (Iunit+Nprint_units <= Dlinfo.Nunits & 
            Dlinfo.unit(Iunit+Nprint_units).Nlines < help_args.min_Lpgh & 
            Dlinfo.unit(Iunit+Nprint_units).Nlines + 2 + Nlines <= help_args.max_Lpgh);
             Nlines = Dlinfo.unit(Iunit+Nprint_units).Nlines + 2 + Nlines;
             Nprint_units = Nprint_units + 1;
             end;
        if Nlines+2 < Deps.linfo(Iep).Nlines then
             Lcount = length("Entry:(99 lines follow;  999 lines in entry point)  More help?");
        else Lcount = length("Entry:(999 lines in entry point)  More help?");
        Snl1 = (Lcount + 2 + length(Deps.linfo(Iep).header) + 3 > Loutput_line);
        end;
RE_ASK:      Ssearch = FALSE;           /* searching for matching section/pgh is done.  */
         PI_LABEL = ASK;            /* Routines branching here set PI_LABEL. Reset it.*/
         Sprint_inhibit = FALSE;
         if  query_type = normal  then
        call command_query_ (addr(query_info), answer, procedure_name,
             "^[^a (^d line^[s^]).^[^/^;  ^]^2s^;^4s^d more line^[s^].  ^]^[Review^;More help^]?",
             Dlinfo.unit(Iunit).S.scn,  query, Nlines, Nlines > 1, (length(query)+24 > Loutput_line),
             Nlines, Nlines > 1,
             Sseen);
         else if  query_type = some_unseen  then
        call command_query_ (addr(query_info), answer, procedure_name,
             "End of info.  Some paragraphs unseen.^/^[^a^;In:  ^a^] (^d line^[s^]).^[^/^;  ^]More help?",
             Dlinfo.unit(Iunit).S.scn, query,
             Nlines, Nlines > 1,
             length(query) + 30 > Loutput_line);
         else if  query_type = search_unseen  then
        call command_query_ (addr(query_info), answer, procedure_name,
             "^[^[^a^;In:  ^a^] (^d line^[s^])^;^2s^d more line^[s^]^].^[^/^;  ^]More help?",
             query ^= "", Dlinfo.unit(Iunit).S.scn, query,
             Nlines, Nlines > 1,
             length(query) + 30 > Loutput_line);
         else if  query_type = new_entry  then
        call command_query_ (addr(query_info), answer, procedure_name,
             "Entry:  ^a^[^/^;   ^](^[^d lines follow;  ^;^s^]^d lines in entry point).  ^[Review^;More help^]?",
             Deps.linfo(Iep).header,
             Snl1,
             (Nlines+2 < Deps.linfo(Iep).Nlines),
            Nlines, Deps.linfo(Iep).Nlines-2,
             Sseen);
PARSE:       call parse_answer (answer, op, ep_name, ca, scn, srh);
         if op = hbound(do,1) + 1 then do;      /* Count consecutive errors user makes in answer.   */
        Nconsecutive_bad_ops = Nconsecutive_bad_ops + 1;
        go to ERROR;
        end;
         else Nconsecutive_bad_ops = 0;
         go to do(op);              /* Process request at user's beck and call. */
\014
YES:
do(1):       Iunit_end = Iunit + Nprint_units - 1;  /* yes                  */
         PI_LABEL = YES_END;            /* go to pgh user said, even if he pi's.    */
         call print_pgh (Dlinfo.unit(Iunit), Sprint_inhibit);
         do Iunit = Iunit + 1  to  Iunit + Nprint_units - 1;
        call print_pgh_2nl (Dlinfo.unit(Iunit), Sprint_inhibit);
        end;
YES_END:         Iunit = Iunit_end;
         go to CONTINUE;

do(2):       go to RETURN;              /* no                   */

do(3):       go to QUIT;                /* quit                 */

do(4):       Iunit = 0;             /* top                  */
         if length(Deps.linfo(Iep).header) > 0 then call ioa_ ("^a", Deps.linfo(Iep).header);
         go to CONTINUE;

do(5):       Nlines_titles = Dlinfo.unit(Iunit).Nlines; /* rest                 */
         do Iunit_end = Iunit + 1 to Dlinfo.Nunits;
        Nlines_titles = Nlines_titles + Dlinfo.unit(Iunit_end).Nlines + 2;
        end;
         Iunit_end = Iunit_end - 1;
REST:        PI_LABEL = REST_END;
         call ioa_ ("(^d ^[lines follow^;line follows^])", Nlines_titles, Nlines_titles>1);
         call print_pgh (Dlinfo.unit(Iunit), Sprint_inhibit);
         do Iunit = Iunit + 1 to Iunit_end;
        call print_pgh_2nl (Dlinfo.unit(Iunit), Sprint_inhibit);
        end;
REST_END:        Iunit = Iunit_end;
         go to CONTINUE;

do(6):                      /* rest -scn                */
         Nlines_titles = Dlinfo.unit(Iunit).Nlines;
         do Iunit_end = Iunit + 1 to Dlinfo.Nunits while (^Dlinfo.unit(Iunit_end).S.scn);
        Nlines_titles = Nlines_titles + Dlinfo.unit(Iunit_end).Nlines + 2;
        end;
         Iunit_end = Iunit_end - 1;
         go to REST;

do(7):       go to CONTINUE;            /* skip                 */

do(8):                          /* skip -scn                */
         do Iunit = Iunit + 1 to Dlinfo.Nunits while (^Dlinfo.unit(Iunit).S.scn);
        end;
         Iunit = Iunit - 1;
         go to CONTINUE;

do(9):                      /* skip -ep             */
do(10):                     /* skip -rest               */
         do Iunit = 1 to Dlinfo.Nunits while (^seen_pgh(Dlinfo.unit(Iunit)));
        end;                /* Has user seen any pgh of this entry?     */
         if Iunit > Dlinfo.Nunits then       /* No.                  */
        Sseen = FALSE;
         else Sseen = TRUE;         /* If so, by skip -ep, he's saying he's seen all    */
         go to CHECK_OTHER_ENTRIES;     /* he wants to of this entry.           */
\014
do(11):      new_section  = "";         /* skip -seen               */
         Sfound = FALSE;
         do Iunit = Iunit+1 to Dlinfo.Nunits while(^Sfound);
        if Dlinfo.unit(Iunit).S.scn then
             new_section  = Dlinfo.unit(Iunit).title;
        Sfound = ^seen_pgh(Dlinfo.unit(Iunit));
        end;
         Iunit = Iunit - 1;
         if Sfound then do;
        query = new_section;
        query_type = search_unseen;
        go to ASK;
        end;
         else go to END_OF_INFO;

do(12):      Iunit_search = Iunit - 1;      /* title                    */
TITLE:       PI_LABEL = RE_ASK;
         Plist_of_titles = get_list (Plist_base);
         call get_title_list (PDlinfo, Plist_of_titles, Iunit_search);
         call format_list (Plist_of_titles, divide (Plist_of_titles->list.N, 7, 17, 0)+1, 1);
         Nlines_titles = Plist_of_titles->list.Nrows;
         call ioa_ ("(^d ^[lines follow^;line follows^])", Nlines_titles, Nlines_titles>1);
         call print_list (Plist_of_titles, Sprint_inhibit);
         Pnext_free_space = Plist_of_titles;
         list_base.N = list_base.N - 1;
         go to RE_ASK;

do(13):      Iunit_search = 0;          /* title -top               */
         go to TITLE;
\014
do(14):                     /* entry_point {ep_name}            */
         if ref_name = "" then
        if suffix = "" then
             ref_name = rtrim(Dinfo_seg_.ent);
        else ref_name = substr(Dinfo_seg_.ent, 1, 32 - length(suffix) -
                  index(reverse(Dinfo_seg_.ent), reverse(suffix) || "."));
         if ep_name = "" then           /* Look for main entry point (eg, ioa_$ioa_)    */
        ep_name = ref_name;
         else do;
        i = index(ep_name, "$");        /* Look for hcs_$initiate rather than initiate  */
        if i > 1 then do;            /* Validate given reference name.       */
             if substr(ep_name,1,i-1) ^= ref_name then do;
            call ioa_ ("Reference name  ^a  invalid.  Entry point names must be of the form:
    ^a$ENTRY_POINT_NAME
or just:    ENTRY_POINT_NAME", substr(ep_name,1,i-1), ref_name);
            Nconsecutive_bad_ops = Nconsecutive_bad_ops + 1;
            go to ERROR;
            end;
             end;
        if i > 0 then
             if i < length(ep_name) then
            ep_name = substr(ep_name,i+1);
             else ep_name = ref_name;
        end;
         Sfound = FALSE;            /* Find the requested entry point.      */
         do i = 1 to Deps.N while (^Sfound);
        do j = 1 to Deps.linfo(i).Nep_names while (^Sfound);
             if ep_name = Deps.linfo(i).ep_name(j) then
            Sfound = TRUE;
             end;
        end;
         if Sfound then do;
        Deps.linfo(Iep).S.seen_by_user = TRUE;  /* user has seen all he wants of this entry point.*/
        Iep = i - 1;
        PDlinfo = Deps.linfo(Iep).PDlinfo;
        Iunit = 1;
        query_type = new_entry;
        go to ASK;
        end;
         else do;
        call ioa_ ("Entry point  ^a$^a  not found.", ref_name, ep_name);
        go to RE_ASK;
        end;
\014
do(15):      Iunit_search = Iunit;          /* section              */
SECTION:         if scn.N = 0 then do;
        call ioa_$nnl ("No search strings given for section request.  ");
        Nconsecutive_bad_ops = Nconsecutive_bad_ops + 1;
        go to ERROR;
        end;
         match_result = find_section (PDlinfo, scn, Iunit_search);
         if  match_result = exact_match  then do;
        Iunit = Iunit_search;
        Nprint_units = 1;
        call ioa_ ("(^d ^[lines follow^;line follows^])",
             Dlinfo.unit(Iunit).Nlines, Dlinfo.unit(Iunit).Nlines > 1);
        go to YES;
        end;
         else if  match_result = match  then do;
        Iunit = Iunit_search;       /* When found, don't aggregate paragraphs.  */
        Ssearch  = TRUE;
        query_type = normal;
        go to ASK;
        end;
         else do;               /* Search failed?  Paragraphs can be aggregated */
        call ioa_ ("No matching section found.");
        go to RE_ASK;           /* based upon user's next response.     */
        end;

do(16):      Iunit_search = 1;          /* section -top             */
         go to SECTION;

do(17):      Iunit_search = Iunit;          /* search                   */
SEARCH:      if srh.N = 0 then do;
        call ioa_$nnl ("No search strings given for search request.  ");
        Nconsecutive_bad_ops = Nconsecutive_bad_ops + 1;
        go to ERROR;
        end;
         Ssearch = find_pgh (PDlinfo, srh, Iunit_search, new_section);
         if Ssearch then  do;           /* Found matching pgh?  Print it.       */
        Iunit = Iunit_search;
        Ssearch = FALSE;
        Nlines = Dlinfo.unit(Iunit).Nlines;
        if  new_section^= ""  &  ^Dlinfo.unit(Iunit).S.scn  then do;
             Nlines = Nlines + 1;
             call ioa_ ("(^d lines follow)^2/^a:", Nlines, new_section);
             call print_pgh_nnl (Dlinfo.unit(Iunit), Sprint_inhibit);
             end;
        else do;
             call ioa_ ("(^d ^[lines follow^;line follows^])", Nlines, Nlines>1);
             call print_pgh (Dlinfo.unit(Iunit), Sprint_inhibit);
             end;
        go to CONTINUE;
        end;
         else do;
        call ioa_ ("No matching paragraph found.");
        go to RE_ASK;
        end;

do(18):      Iunit_search  = 1;         /* search  -top             */
         go to SEARCH;
\014
do(19):      Nlines_titles = -1;            /* brief                    */
         call get_brief_data (Deps.linfo(Iep).S.old_format, help_args.Sctl.he_pn, PDlinfo,
                  Plist_base, Dinfo_seg_.dir, Dinfo_seg_.ent, Nlast_info_no_brief_data,
                 Iinfo, Ninfos_printed, Iunit_syntax,
                 Nunit_syntax, Nlists_of_bf_args, Nlines_titles);
         Nlists_of_bf_args = list_base.N;
         if Nlines_titles > 0 then do;
        call ioa_ ("(^d ^[lines follow^;line follows^])", Nlines_titles, Nlines_titles>1);
        call print_brief_data (PDlinfo, TRUE, Plist_base,
             Iunit_syntax, Nunit_syntax, Nlists_of_bf_args);
        end;
         go to RE_ASK;

do(20):      Nlines_titles = -1;            /* control_arg              */
         if ^brief_data_ok (Deps.linfo(Iep).S.old_format, help_args.Sctl.he_pn, PDlinfo,
                Dinfo_seg_.dir, Dinfo_seg_.ent, Iinfo, Ninfos_printed,
                Nlast_info_no_brief_data) then;
         else do;
        ca.title = "control_arg";
        call get_arg_descriptions (addr(ca), PDlinfo, Plist_base,
                       Deps.linfo(Iep).S.old_format, Plists_of_args, Nlists_of_args);
        if Nlists_of_args > 0 then  do;
             do i = 1 to Nlists_of_args;
                  Plist = Plists_of_args(i);
                  Nlines_titles = Nlines_titles + list.N + 2;
                  end;
             call ioa_ ("(^d ^[lines follow^;line follows^])", Nlines_titles, Nlines_titles>1);
             do j = 1 to Nlists_of_args;
                  Plist = Plists_of_args(j);
                  call ioa_ ("^/^a:", list.title);
                  do i = 1 to list.N;
                       call ioa_ ("^a", list.arg(i));
                       end;
                  end;
             Pnext_free_space = Plists_of_args(1);
             list_base.N  = 0;
             end;
        else call ioa_ ("No matching control arguments.");
        end;
         go to RE_ASK;

do(21):      call ioa_("^a", procedure_name);       /* . (= print name of caller)           */
         go to RE_ASK;

do(22):      if query_answers.Nrows = 0 then        /* ? (= list responses)         */
        call format_list(addr(query_answers), 5, 1);
         call print_list (addr(query_answers), Sprint_inhibit);
         go to RE_ASK;

do(23):      call print_header_only();      /* header                   */
         go to RE_ASK;
\014
ERROR:
         if Nconsecutive_bad_ops = 1 then do;   /* For first error, omit acceptable response    */
                        /* list, and just print mini query.     */
        call command_query_ (addr(query_info), answer, procedure_name,
             "^d ^[lines follow^;line follows^].  ^[Review^;More help^]?",  Nlines, Nlines>1, Sseen);
        go to PARSE;
        end;
         call ioa_("^/Type ? for a list of allowed responses.");
                            /* But if user errs more than once for given query*/
                        /* tell user how to print responses     */
         if Nconsecutive_bad_ops > 2 then go to RE_ASK;
                        /* If more than 2 consecutive errors, the user  */
                        /* may have forgotten original question.    */
                        /* Repeat it in  its entirety.      */
         else
        call command_query_ (addr(query_info), answer, procedure_name,
             "^d ^[lines follow^;line follows^].  ^[Review^;More help^]?",  Nlines, Nlines>1, Sseen);
         go to PARSE;
\014
END_OF_INFO:   Sseen = TRUE;                /* Examine all pghs looking for unseen pgh. */
         new_section = "UNTITLED";      /* Remember section titles as we examine them.  */
         do Iunit = 1 to Dlinfo.Nunits while (Sseen);
        if Dlinfo.unit(Iunit).S.scn then
             new_section = Dlinfo.unit(Iunit).title;
        Sseen = seen_pgh (Dlinfo.unit(Iunit));
        end;                /* Look for unseen paragraphs.      */
         if ^Sseen then  do;            /* Some were found?             */
        Iunit = Iunit - 1;          /* do group always increments 1 too many.   */
        query = new_section;
        query_type = some_unseen;
        go to ASK;
        end;
         Sseen = TRUE;              /* This entry point has been seen.      */

CHECK_OTHER_ENTRIES:
         if Deps.N = 0 then go to RETURN;       /* Only 1 part in log info?  We're done.    */
         else do;               /* Many entry points.           */
        Deps.linfo(Iep).S.seen_by_user = Sseen; /* Mark whether or not we've seen this entry.   */
        do i = Iep+1 to Deps.N while (Deps.linfo(i).S.seen_by_user  |
                    help_args.min_date_time ^< Deps.linfo(i).date);
             end;               /* Look for unseen entries.         */
        if i > Deps.N then do;       /* All entries seen?            */
             do i = 1 to Iep-1 while (Deps.linfo(i).S.seen_by_user  |
                    help_args.min_date_time ^< Deps.linfo(i).date);
            end;
             if  Deps.linfo(i).S.seen_by_user  |
                 help_args.min_date_time ^< Deps.linfo(i).date  then
            go to RETURN;
             end;
        Iep = i;                /* ith one is unseen.           */
        PDlinfo = Deps.linfo(Iep).PDlinfo;  /* access its paragraph descriptors.        */
        Iunit = 1;
        if help_args.Sctl.all then do;
             Lcount = length("Entry:(999 lines in entry point)");
             Snl1 = (Lcount + 2 + length(Deps.linfo(Iep).header) + 3 > Loutput_line);
             call ioa_ ("^v/Entry:  ^a^[^/^;   ^](^d lines in entry point)",
            help_args.Lspace_between_infos, Deps.linfo(Iep).header, Snl1, Deps.linfo(Iep).Nlines-2);
             Nprint_units = Dlinfo.Nunits;
             go to YES;
             end;
        query_type = new_entry;
        go to ASK;
        end;

CONTINUE:        Iunit = Iunit + 1;         /* Must implement looping ourselves because */
         Sloop = (Iunit <= Dlinfo.Nunits+1); /* Dlinfo.Nunits will change when we switch to a    */
         query_type = normal;           /* new entry point.  Loop would be:     */
         end;                   /*  do Iunit = Iunit+1 to Dlinfo.Nunits;    */

RETURN: PI_LABEL = NEXT_INFO;
    return;
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


find_pgh: procedure (PDlinfo_, Srh, Iunit, new_section) returns (bit(1) aligned);

     dcl    PDlinfo_            ptr;        /* ptr to descriptors for this log info seg.    */
     dcl    1 Dlinfo_           aligned based(PDlinfo_),
      2 Nunits      fixed bin,  /* number of units (pghs) in this log info seg. */
      2 Nsections       fixed bin,  /* number of units having section title.    */
      2 unit (0 refer (Dlinfo_.Nunits))
                like Dlinfo.unit;
     dcl    1 Srh           aligned,        /* Paragraph search args.           */
      2 header      like LIST.header,
      2 group (100)     like LIST.group;
     dcl    Iunit           fixed bin;  /* Pgh to start searching (Input)       */
     dcl    new_section     char(88) varying;   /* Title of new section  in which pgh occurs.   */
                        /* Pgh found. (Output)          */
     dcl    srh (Srh.N)     char(88) varying;   /* translated paragraph search args.        */
     dcl    Ssearch         bit(1) aligned;
     dcl (i, j)             fixed bin;
     dcl    PPgh            ptr,
    LPgh            fixed bin,
    Pgh         char(LPgh) based(PPgh);

    j = 0;                  /* Find length of longest pgh we will examine   */
    if Srh.N = 0 then return(FALSE);        /* If nothing to search for, forget it.     */
    do i = Iunit to Dlinfo_.Nunits;     /* so we can create temp storage into which */
         j = max(j, Dlinfo_.unit(i).L);     /* each pgh can be translated into lowercase.   */
         end;

BLOCK:  begin;
     dcl    pgh         char(j) varying;

    do i = 1 to Srh.N;              /* translate search args to lower case.     */
         srh(i) = translate(Srh.arg(i), "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
         end;
    Ssearch = FALSE;                /* Search until matching section title found    */
    new_section  = "";
    do Iunit = Iunit to Dlinfo_.Nunits while(^Ssearch);
         if Dlinfo_.unit(Iunit).S.scn then
        new_section = Dlinfo_.unit(Iunit).title;
         PPgh = Dlinfo_.unit(Iunit).Pstart;
         LPgh = Dlinfo_.unit(Iunit).L;
         pgh = translate (Pgh, "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
                        /* translate pgh to lower case.     */
         Ssearch = TRUE;            /* Assume title matches until proven otherwise. */
         do i = 1 to dimension(srh, 1) while(Ssearch);
        if index(pgh, srh(i)) = 0 then
             Ssearch = FALSE;
        end;
         end;
    if Ssearch then             /* Match found?             */
         Iunit = Iunit - 1;         /* do-group always increments one too many. */
    return (Ssearch);

    end BLOCK;

    end find_pgh;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


find_section: procedure (PDlinfo_, Scn, Iunit) returns (fixed bin);

     dcl    PDlinfo_            ptr;        /* ptr to descriptors for this log info seg.    */
     dcl    1 Dlinfo_           aligned based(PDlinfo_),
      2 Nunits      fixed bin,  /* number of units (pghs) in this log info seg. */
      2 Nsections       fixed bin,  /* number of units having section title.    */
      2 unit (0 refer (Dlinfo_.Nunits))
                like Dlinfo.unit;
     dcl    1 Scn           aligned,        /* Section title search args.           */
      2 header      like LIST.header,
      2 group (100)     like LIST.group;
     dcl    Iunit           fixed bin;  /* Pgh to start searching (Input)       */
                        /* Pgh found. (Output)          */
     dcl    scn (Scn.N)     char(88) varying;   /* translated section title search args.    */
     dcl    Ssearch         bit(1) aligned;
     dcl    i           fixed bin;
     dcl    result          fixed bin;
     dcl    temp            char(88) varying;
     dcl    title           char(88) varying;

    if Scn.N = 0 then return(no_match);     /* if nothing to search for, forget it.     */
    do i = 1 to Scn.N;              /* translate search args to lower case.     */
         scn(i) = translate(Scn.arg(i), "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
         end;
    Ssearch = FALSE;                /* Search until matching section title found    */
    do Iunit = Iunit to Dlinfo_.Nunits while(^Ssearch);
         if Dlinfo_.unit(Iunit).S.scn then do;
        title = translate (Dlinfo_.unit(Iunit).title, "abcdefghijklmnopqrstuvwxyz",
                              "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
                        /* translate title to lower case.       */
        Ssearch = TRUE;         /* Assume title matches until proven otherwise. */
        do i = 1 to dimension(scn, 1) while(Ssearch);
             if index(title, scn(i)) = 0 then
            Ssearch = FALSE;
             end;
        end;
         end;
    if Ssearch then do;             /* Match found?             */
         Iunit = Iunit - 1;         /* do-group always increments one too many. */
         temp = scn(1);
         do i = 2 to Scn.N;
        temp = temp || " ";
        temp = temp || scn(i);
        end;
         if temp = title then           /* check for exact match (except for letter case).*/
        result = exact_match;
         else result = match;
         end;
    else result = no_match;
    return (result);

    end find_section;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


format_list: procedure (Plist, Mcols, Mpghs);       /* This procedure formats a list of values. */

     dcl    Plist           ptr,        /* ptr to argument list to be printed.      */
    Mcols           fixed bin,  /* maximum number of columns to be used in format.*/
    Mpghs           fixed bin,  /* maximum pghs to be used. 0 means no limit.   */
    Sprint_inhibit      bit(1) aligned; /* on if printing suppressed by pi.     */


     dcl (Icol, Ipgh, Irow)     fixed bin,
    Pspaces         ptr,
    Lspaces         fixed bin,
    Pmatrix         ptr,
    Sdoes_not_fit       bit(1) aligned,
    i           fixed bin,
    line            char(MAX_HELP_LINE_SIZE) varying,
    spaces          char(Lspaces) based(Pspaces);

     dcl    1 list          aligned based(Plist),
      2 header      like LIST.header,
      2 group (0 refer (list.N)) like LIST.group;

     dcl    1 matrix (list.Npghs, list.Ncols, list.Nrows)
                aligned based(Pmatrix) like LIST.group;

     dcl    1 event_info        aligned,
      2 ev_chan     fixed bin(71),
      2 message     fixed bin(71),
      2 sender      bit(36),
      2 origin,
        3 dev_signal        bit(18) unal,
        3 ring      bit(18) unal,
      2 chan_index      fixed bin;

     dcl    1 wait_list     aligned int static,
      2 N           fixed bin,
      2 ev_chan (1)     fixed bin(71);

     dcl    1 write_status      aligned int static,
      2 ev_chan     fixed bin(71) init(0),
      2 output_pending      bit(1);
\014
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* Format the arguments in as many columns as possible to reduce the output lines.  */
    /* However, if the output fits in 2 or more rows, the number of rows is chosen so that  */
    /* all columns but the final one are full.                  */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */

    Pmatrix = addr(list.group);         /* overlay arg list with 3-D matrix.        */
    Sdoes_not_fit = TRUE;           /* Find proper number of columns to put output in.*/
    if Mcols < 1 then                /* Allow caller to limit number of columns. */
         list.Ncols = dimension(list.ML,1);
    else list.Ncols = min(Mcols, dimension(list.ML,1));
    do list.Ncols = list.Ncols to 1 by -1 while (Sdoes_not_fit);
         list.Nrows = divide (list.N + list.Ncols-1, list.Ncols, 17, 0);
                        /* Compute how many rows are required to display    */
                        /*   the args in list.Ncols columns.        */
         if Mpghs = 1 then          /* compute how many paragraphs are needed.  */
        list.Npghs = 1;
         else do;
        list.Npghs = divide (list.Nrows+help_args.max_Lpgh-2, help_args.max_Lpgh-1, 17, 0);
        list.Nrows = divide (list.Nrows+list.Npghs-1, list.Npghs, 17, 0);
        end;
                        /* Make sure that the output matrix is balanced,    */
                        /*   and that only the last column is not full. */
         if  (list.Npghs>1)  |  (list.Nrows*list.Ncols - list.N < list.Nrows) then do;
        list.Nreal = list.N;        /* Make life easier by creating enough empty args   */
                        /*   to fill out a square matrix.       */
        list.N = list.Ncols * list.Nrows * list.Npghs;
        do i = list.Nreal+1 to list.N;
             list.arg(i) = "";
             end;
        list.ML(*) = 0;         /* Find longest arg in each column.     */
        do Icol = 1 to list.Ncols;
             do Ipgh = 1 to list.Npghs;
            do Irow = 1 to list.Nrows;
                 list.ML(Icol) = max(list.ML(Icol), length(matrix.arg(Ipgh, Icol, Irow)));
                 end;
            end;
             end;
        if  sum(list.ML) + (list.Ncols-1)*3  > Loutput_line then
                        /* See if all rows will fit on a line.      */
                        /* Leave 2 spaces between columns.      */
             list.N = list.Nreal;
        else Sdoes_not_fit = FALSE;
        end;
         end;
    list.Ncols = list.Ncols + 1;            /* do-group decrements one too many.        */
    return;
\014
print_list_nnl: entry (Plist, Sprint_inhibit);

    if Sprint_inhibit then return;
    Pmatrix = addr(list.group);
    Pspaces = addr(SPACES);
    if length(list.title) > 0 then
         call ioa_ ("^a:", list.title);
    go to PRINT_LIST;


print_list: entry (Plist, Sprint_inhibit);      /* This entry point prints a list and its title.    */

    if Sprint_inhibit then return;
    Pmatrix = addr(list.group);         /* overlay arg list with 3-D matrix.        */
    Pspaces = addr(SPACES);
    if length(list.title) > 0 then
         call ioa_ ("^/^a:", list.title);
    else call iox_$put_chars(iox_$user_output, addr(NL), length(NL), 0);
PRINT_LIST:
    do Ipgh = 1 to list.Npghs;          /* Output paragraphs, one at a time.        */
         do Irow = 1 to list.Nrows;     /* Output rows, one at a time.      */
        line = "";
        do Icol = 1 to list.Ncols;
             line = line || matrix.arg (Ipgh, Icol, Irow);
             Lspaces = list.ML(Icol) - length(matrix.arg(Ipgh, Icol, Irow)) + 3;
             line = line || spaces;
             end;
        call ioa_ ("^a", line);
        end;
         end;
    return;
\014
output_list: entry (Plist, Apgh, Poutput, Loutput, Nlines);/* output 1 pgh of multipgh list into a string.  */

     dcl    Apgh            fixed bin,  /* Number of pgh to output.         */
    Poutput         ptr,        /* ptr to output string.            */
    Loutput         fixed bin,  /* length of output string.         */
    output          char(Loutput) based(Poutput),
    Nlines          fixed bin;  /* Lines in the output.         */

    Nlines = 0;
    Loutput = 0;
    Pmatrix = addr(list.group);
    Pspaces = addr(SPACES);
    Ipgh = Apgh;
    if Ipgh = 1 then                /* Output NL which would follow section title   */
         if length (list.title) > 0 then do; /* in a regular (non-made-up) section.      */
        call out (NL);
        Nlines = Nlines + 1;
        end;
    do Irow = 1 to list.Nrows;
         do Icol = 1 to list.Ncols;
        call outv (matrix.arg (Ipgh, Icol, Irow));
        Lspaces = list.ML(Icol) - length(matrix.arg(Ipgh, Icol, Irow)) + 3;
        call out(spaces);
        end;
         call out (NL);
         Nlines = Nlines + 1;
         end;
    return;
\014
out: proc (str);

     dcl    str         char(*);

    Loutput = Loutput + length(str);
    substr (output, Loutput-length(str)+1, length(str)) = str;

    end out;


outv: proc (str);

     dcl    str         char(*) var;

    Loutput = Loutput + length(str);
    substr (output, Loutput-length(str)+1, length(str)) = str;

    end outv;


    end format_list;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


get_arg_descriptions: procedure (Plist_cas, PDlinfo_, Plist_base,
                Sreally_old_format, Plists_of_args, Nlists_of_args);
                        /* This procedure builds a list of argument */
                        /* descriptions which match user-given arg names.   */

     dcl (Plist_cas, PDlinfo_, Plist_base)
                ptr,
    Sreally_old_format      bit(1),
    Plists_of_args (*)      ptr,
    Nlists_of_args      fixed bin;

     dcl    1 list_cas      aligned based(Plist_cas),
      2 header      like LIST.header,
      2 group (0 refer (list_cas.N))
                like LIST.group;
     dcl    1 Dlinfo_           aligned based (PDlinfo_),
                        /* structure defining all paragraphs (units) in */
                        /*   an entry point (logical info seg - linfo). */
      2 Nunits      fixed bin,  /*   number of units in this ep.        */
      2 Nsections       fixed bin,  /*   number of units beginning a section.   */
      2 unit (0 refer (Dlinfo_.Nunits)) like Dlinfo.unit;

     dcl    1 list_base     aligned based(Plist_base),
                        /* struc locating lists of things to be output. */
      2 N           fixed bin,  /*   number of output lists now allocated.  */
      2 Nmax            fixed bin,  /*   max number of list ptrs allocatable.   */
      2 Ispace_used_set     fixed bin,  /*   index of last list on which space used set.    */
      2 Plists (0 refer(list_base.Nmax))
                ptr;        /*   ptrs to allocated lists.           */
                        /*   unit (paragraph) descriptors.      */
     dcl    1 list_args     aligned based(Plist_args),
      2 header      like LIST.header,
      2 group (0 refer (list_args.N))
                like LIST.group;
     dcl    Plist_args      ptr,
    Ppgh            ptr,
    Lpgh            fixed bin,
    Largs           fixed bin,
    args            char(Largs) based(Ppgh),
    pgh         char(Lpgh) based (Ppgh),
    pgh_char (Lpgh)     char(1)    based(Ppgh);

     dcl    Iunit           fixed bin,
    DO_LINE         label local,
    Sconsecutive_arg_lines  bit(1) aligned,
    Sold_format     bit(1) aligned,
         (i, j, k, l)       fixed bin;
\014
    Nlists_of_args = 0;
    list_cas.Snot_found(*) = 1;
    do Iunit = 1 to Dlinfo_.Nunits;
         if Dlinfo_.unit(Iunit).S.arg_list then do;
        if Dlinfo_.unit(Iunit).S.scn then do;
             if Nlists_of_args > 0 then
            if list_args.N = 0 then
                 list_args.title = Dlinfo_.unit(Iunit).title;
            else go to NEXT_LIST;
             else do;
NEXT_LIST:      if Nlists_of_args >= dimension(Plists_of_args,1) then;
            else do;
                 Nlists_of_args = Nlists_of_args + 1;
                 Plist_args, Plists_of_args(Nlists_of_args) = get_list (Plist_base);
                 list_args.title = Dlinfo_.unit(Iunit).title;
                 end;
            end;
             end;
        Ppgh = Dlinfo_.unit(Iunit).Pstart;
        Lpgh = Dlinfo_.unit(Iunit).L;
        i = index(pgh, "
 ");                        /* Old format if some pgh lines don't begin  w/ SP*/
        if i = 0 then
             Sold_format = TRUE;
        else Sold_format = Sreally_old_format;
        DO_LINE = SKIP_LINE;
        do while (Lpgh > 0);     /* Search pgh for arguments.            */
             i = index(pgh, NL);        /* skip blank lines & lines starting with HT SP.    */
             if i > 0 then
            j = verify(substr(pgh,1,i), "    
");
             if  ((i > 0) & (j = 0))  |  (index ("    ", pgh_char(1)) > 0)  then do;
            Sconsecutive_arg_lines = FALSE;
            end;
             else if Sold_format then do;   /* Add arg to list iff it is a control_arg. */
            if pgh_char(1) ^= "-" then go to DO_LINE;
            if i = 0 then i = Lpgh; /* arg name line must begin with -      */
            Largs = index(substr(pgh,1,i), "  ");
            k = index(substr(pgh,1,i), "    ");
            if  (Largs^=0)  &  (k^=0)  then
                 Largs = min(Largs, k);
                        /* arg name ends when first double SP is found, */
            if Largs = 0 then do;   /*   or with first HT or SP char.       */
                 Largs = search(substr(pgh,1,i), "   ");
                 if Largs = 0 then do;  /* No SP/HT in line?  Forget it.        */
                DO_LINE = SKIP_LINE;
                go to SKIP_LINE;
                end;
                 k = index(substr(pgh,1,i), ",");
                 if k = 0 then;     /* Does line contain  a comma?      */
                 else if k = Largs-1 then
                Largs = Largs + search(substr(pgh,Largs+1, i-Largs),"    ");
                        /* Yes, look for "-long, -lg "      */
                 else do;       /* Yes, look for "-pathname path, -pn path "    */
                l = Largs + search(substr(pgh,Largs+1,i-Largs), "    ");
                if  l ^= Largs  &  k = l-1  then do;
                     k = l + search(substr(pgh,l+1,i-l),"    ");
                     if k ^= l then do;
                    l = k + search(substr(pgh,k+1,i-k),"     ");
                    if l ^= k then Largs  = l;
                    end;
                     end;
                end;
                 end;
            do k = 1 to list_cas.N while (index(args, list_cas.arg(k))=0);
                 end;
            if k <= list_cas.N then do;
                 list_cas.Snot_found(k) = 0;
                 DO_LINE = KEEP_LINE;
                 end;
            else DO_LINE = SKIP_LINE;
            end;
             else do;           /* arg name is entire line.         */
            if i = 0 then i = Lpgh;
            k = index(pgh, "  ");
            if k ^= 0 then if k < i then 
                 Largs = k-1;
            else Largs = i-1;
            if  substr(args, Largs, 1) = ","  &  i<Lpgh  then
                 Largs = Largs + index(substr(pgh,i+1, Lpgh-i), NL);
                        /* If arg name line ends with  , then assume it */
                        /* it is continued on next line.        */
            do k = 1 to list_cas.N while (index(args,list_cas.arg(k))=0);
                 end;
            if k <= list_cas.N then do;
                 list_cas.Snot_found(k) = 0;
                 DO_LINE = KEEP_LINE;
                 Largs = length(rtrim(args, "    
"));
                 end;
            else DO_LINE = SKIP_LINE;
            end;
             go to DO_LINE;

KEEP_LINE:       list_args.N = list_args.N + 1;
             if i = 0 then Largs = Lpgh;
             else Largs = i - 1;
             list_args.arg(list_args.N) = args;
SKIP_LINE:       if i = 0 then Lpgh = 0;
             else if i = Lpgh then Lpgh = 0;
             else do;
            Ppgh = addr(pgh_char(i+1));
            Lpgh = Lpgh - i;
            end;
END_LOOP:            end;
        end;
         end;
    if Nlists_of_args > 0 then           /* May have unused list.  If so, free it.   */
         if list_args.N = 0 then do;
        list_base.N = list_base.N - 1;
        Nlists_of_args = Nlists_of_args - 1;
        end;
    if sum(list_cas.Snot_found) > 0 then do; /* Any control arg names given by user unmatched?   */
         if Nlists_of_args = 0 then do;
        Nlists_of_args = Nlists_of_args + 1;
        Plist_args, Plists_of_args(Nlists_of_args) = get_list (Plist_base);
        list_args.title = "NO MATCH FOR " || list_cas.title || " STRINGS";
        end;
         else do;
        list_args.N = list_args.N + 1;
        list_args.arg(list_args.N) = "";
        list_args.N = list_args.N + 1;
        list_args.arg(list_args.N) = "NO MATCH FOR " || list_cas.title || " STRINGS:";
        end;
         do k = 1 to list_cas.N;
        if list_cas.Snot_found(k) > 0 then do;
             list_args.N = list_args.N + 1;
             list_args.arg(list_args.N) = "  ";
             list_args.arg(list_args.N) =
            list_args.arg(list_args.N) || list_cas.arg(k);
             end;
        end;
         end;

    end get_arg_descriptions;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


get_arg_list: procedure (unit, Plist, Sreally_old_format);  /* This procedure builds a list of arguments.   */

     dcl    1 unit          aligned like Dlinfo.unit,
    Plist           ptr,        /* ptr to space for arg list.           */
    Sreally_old_format      bit(1);     /* on if info segs contains \006 chars.     */

     dcl    1 list          aligned based(Plist),
      2 header      like LIST.header,
      2 group (0 refer (list.N)) like LIST.group;
     dcl    Ppgh            ptr,
    Lpgh            fixed bin,
    Sconsecutive_arg_lines  bit(1) aligned, /* on if info seg is in new format, and if  */
                        /* previous line of pgh was arg line ending with    */
                        /* a comma.             */
    Sold_format     bit(1) aligned, /* on if info seg is old or really old.     */
         (i, j, k, l)       fixed bin,
    pgh         char(Lpgh) based (Ppgh),
    pgh_char (Lpgh)     char(1)    based(Ppgh);

    Ppgh = unit.Pstart;             /* address the paragraph.           */
    Lpgh = unit.L;
    i = index (pgh, "
   ");  if i = 0 then               /* Check for old format info segs.      */
         Sold_format = TRUE;
    else Sold_format = Sreally_old_format;
    do while (Lpgh > 0);         /* search pgh for arguments.            */
         i = index(pgh, NL);            /* skip blank lines.            */
         if i > 0 then
        j = verify(substr(pgh,1,i), "    
");
         if  ((i > 0) & (j = 0))  |  (index ("    ", pgh_char(1)) > 0) then do;
                        /* skip line beginning with SP or HT.       */
        Sconsecutive_arg_lines = FALSE;
SKIP_LINE:  if i = 0 then Lpgh = 0;
        else if i = Lpgh then Lpgh = 0;
        else do;
             Ppgh = addr(pgh_char(i+1));
             Lpgh = Lpgh - i;
             end;
        end;
         else if Sold_format then do;       /* add arg to list if it is control arg.    */
        if pgh_char(1) ^= "-" then  go to SKIP_LINE;
        if i = 0 then i = Lpgh;     /* arg name line must begin with -      */
        j = index(substr(pgh,1,i), "  ");   /* arg name ends when first double SP is found, */
        k = index(substr(pgh,1,i), "    "); /*   or with first HT char.         */
        if  (j^=0)  &  (k^=0)  then
             j = min(j,k);
        if j = 0 then do;           /*   If no double SP, ends with first SP char.  */
             j = search(substr(pgh,1,i), "   ");
             if j = 0 then          /* If no SP or HT, forget it.           */
            go to SKIP_LINE;
             k = index(substr(pgh,1,i), ",");   /* Does comma immediately precede SP/HT?    */
             if k = 0 then;         /* No. We've found arg.         */
             else if k = j-1 then       /* Yes, look for  "-long, -lg "     */
            j = j + search(substr(pgh,j+1, i-j), "   ");
             else do;           /* Comma found in  line.            */
            l = j + search(substr(pgh,j+1,i-j), "    ");
            if  l ^= j  &  k = l-1  then do;
                 k = l + search(substr(pgh,l+1,i-l),"    ");
                 if k ^= l then do; /* Look for "-pathname path, -pn path ".    */
                l = k + search(substr(pgh,k+1,i-k),"     ");
                if l ^= k then j  = l;
                end;
                 end;
            end;
             end;
        list.N = list.N + 1;
        list.arg(list.N) = " ";
        list.arg(list.N) = list.arg(list.N) || substr(pgh,1,j-1);
        if length(list.arg(list.N)) > 18 then do;
             k =index(list.arg(list.N), ",");
             if k > 0 then do;
            list.N = list.N + 1;
            list.arg(list.N) = "    ";
            k = k + 1;
            k = (k-1) + verify(substr(list.arg(list.N-1),k), " ");
            list.arg(list.N) = list.arg(list.N) || substr(list.arg(list.N-1),k);
            list.arg(list.N-1) = rtrim(substr(list.arg(list.N-1),1,k-1));
            end;
             end;
        go to SKIP_LINE;
        end;

         else do;               /* add arg line to list.            */
        if i = 0 then i = Lpgh;
        k = index(pgh, "  ");
        if k ^= 0 then if k < i then i = k-1;
        j, list.N = list.N + 1;     /* arg is everything on the line.       */
        if Sconsecutive_arg_lines then
             list.arg(j) = "    ";
        else list.arg(j) = " ";
        list.arg(j) = list.arg(j) || rtrim(substr (pgh, 1, i), "     
");
        if substr(list.arg(j), length(list.arg(j)), 1) = "," then
             Sconsecutive_arg_lines = TRUE;
        else if length(list.arg(j)) > 18 then do;
             k =index(list.arg(j), ",");
             if k > 0 then do;
            j, list.N = list.N + 1;
            list.arg(j) = "    ";
            k = k + 1;
            k = (k-1) + verify(substr(list.arg(j-1),k), " ");
            list.arg(j) = list.arg(j) || substr(list.arg(j-1),k);
            list.arg(j-1) = rtrim(substr(list.arg(j-1),1,k-1));
            end;
             end;
        go to SKIP_LINE;
        end;
         end;

    end get_arg_list;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


get_brief_data: proc (Sold_format, Sheader, PDlinfo_, Plist_base, dir, ent, Nlast_info_no_brief_data,
          Iinfo, Ninfos_printed, Iunit_syntax, Nunit_syntax, Nlists_of_bf_args, Nlines);

     dcl    Sold_format     bit(1) unal,    /* on if log info contains \006 chars.      */
    Sheader         bit(1) unal,    /* on if -header required.          */
    PDlinfo_            ptr,        /* ptr to pgh descriptors of log info.      */
    Plist_base      ptr,        /* ptr to list of lists.            */
    dir         char(168) unal, /* dir part of phys info seg's path.        */
    ent         char(32) unal,  /* ent part of phys info seg's path.        */
    Nlast_info_no_brief_data    fixed bin,  /* Last info processed not containing Syntax sect.*/
    Iinfo           fixed bin,  /* number of the info seg being processed.  */
    Ninfos_printed      fixed bin,  /* number of infos for which something printed. */
    Iunit_syntax (10)       fixed bin,  /* indices of Syntax sections.      */
    Nunit_syntax        fixed bin,  /* count of Syntax sections.            */
    Nlists_of_bf_args       fixed bin,  /* count of sections containing args/ctl_args.  */
        Nlines          fixed bin;

     dcl    1 Dlinfo_           aligned based(PDlinfo_),
      2 Nunits      fixed bin,  /* number of units (pghs) in this log info seg. */
      2 Nsections       fixed bin,  /* number of units having section title.    */
      2 unit (0 refer (Dlinfo_.Nunits))
                like Dlinfo.unit;

     dcl    Iunit           fixed bin,
    Plist           ptr,
    Sfound          bit(1) aligned,
         (i, j)         fixed bin;

     dcl    1 list_base     aligned based(Plist_base),
      2 N           fixed bin,  /* number of lists in this list ptr structure.  */
      2 Nmax            fixed bin,  /* max possible number of lists in structure.   */
      2 Ispace_used_set     fixed bin,  /* index of last list on which space used was set.*/
      2 Plists (0 refer (list_base.Nmax))
                ptr;        /* pointers to list structures.     */

     dcl    1 list          aligned based(Plist),
      2 header      like LIST.header,
      2 group (0 refer (list.N))    like LIST.group;

    if Sold_format then                 /* Be sure "Syntax" section(s) exist.             */
         do Iunit = 1 to Dlinfo_.Nunits while (Dlinfo_.unit(Iunit).title ^= "Usage");
        end;
    else do Iunit = 1 to Dlinfo_.Nunits
        while (substr(Dlinfo_.unit(Iunit).title, 1,
                min(6, length(Dlinfo_.unit(Iunit).title))) ^= "Syntax");
         end;                                   /*   Search for the "Syntax" section.             */
    if Iunit > Dlinfo_.Nunits then do;           /*   Tell user if not found.                      */
         call ioa_ ("^[^v/^;^s^]No brief info available for ^[^a^[>^]^;^2s^]^a.",
        ((Ninfos_printed > 1)  &  help_args.Sctl.bf  &  (Nlast_info_no_brief_data ^= Iinfo-1)),
             help_args.Lspace_between_infos,
        Sheader,  dir, dir^=">", ent);
         Nlast_info_no_brief_data = Iinfo;
         return;
         end;
\014
    do i = 1 to dimension(Iunit_syntax,1) while (Iunit <= Dlinfo_.Nunits);
         Iunit_syntax(i) = Iunit;               /* Find & record location of syntax sections.     */
         Nunit_syntax = i;
         do Iunit = Iunit, Iunit + 1 to Dlinfo_.Nunits while (^Dlinfo_.unit(Iunit).S.scn);
        Nlines = Nlines + Dlinfo_.unit(Iunit).Nlines + 1;
        end;                            /* Count lines in each pgh of Syntax section.     */
                            /* Add 1 line for blank line preceding each pgh.  */
         if ^Sold_format then
        do Iunit = Iunit to Dlinfo_.Nunits
             while (substr(Dlinfo_.unit(Iunit).title, 1,
                    min(6, length(Dlinfo_.unit(Iunit).title))) ^= "Syntax");
             end;
         else Iunit = Dlinfo_.Nunits + 1;
         end;

    Iunit = 1;
    do while(Iunit <= Dlinfo_.Nunits);           /* Search for "Arguments" & "Control arguments"   */
                            /*   paragraphs to summarize these arguments.     */
         do Iunit = Iunit to Dlinfo_.Nunits while (^Dlinfo_.unit(Iunit).S.arg_list);
        end;                            /* These units were flagged by                    */
         if Iunit <= Dlinfo_.Nunits then do;     /*   parse_entry_point_into_units.                */
        Plist = get_list (Plist_base);
        if Plist = null() then
             Iunit = Dlinfo_.Nunits + 1;
        else do;
             list.title = Dlinfo_.unit(Iunit).title;
             list.Iunit = Iunit;
             if length(list.title) <= 17 then do;
            list.N = 1;
            list.arg(1) = list.title;
            list.arg(1) = list.arg(1) || ":";
            end;            /* Put title on same line as arg names, unless  */
                        /* title is too long.           */
             do Iunit = Iunit, Iunit + 1 to Dlinfo_.Nunits while (^Dlinfo_.unit(Iunit).S.scn);
            call get_arg_list (Dlinfo_.unit(Iunit), Plist, Sold_format);
            end;
             if  list.N > 0  &  list.title = "Arguments"  then do;
                        /* Suppress Arguments list if all arg names */
                        /* appear in Syntax section.            */
            if list.arg(1) = "Arguments:" then
                 i = 2;
            else i = 1;
            Sfound = TRUE;
            do i = i to list.N while(Sfound);
                 do j = 1 to Nunit_syntax while(Sfound);
                Ppgh = Dlinfo_.unit(Iunit_syntax(j)).Pstart;
                Lpgh = Dlinfo_.unit(Iunit_syntax(j)).L;
                if index (pgh, ltrim(rtrim(list.arg(i), ", "))) = 0 then
                     Sfound = FALSE;
                end;
                 end;
            if Sfound then list.N = 0;
            end;
             if list.N = 1 then
            if list.title = substr(list.arg(1),1,length(list.arg(1))-1) then
                 list.N = 0;
\014
             if list.N > 0 then do;
            if list.title = substr(list.arg(1),1,length(list.arg(1))-1) then
                 list.title = "";
            call format_list (Plist, 0, 1);
            if list.Nrows < 3 then do;
                 do i = 2 by 1 while (i <=list.N);
                if length(list.arg(i)) > 4 then
                if substr(list.arg(i),1,4) = "    " then do;
                     list.arg(i-1) = list.arg(i-1) || " ";
                     list.arg(i-1) = list.arg(i-1) || substr(list.arg(i),5);
                     do j = i+1 to list.N;
                    list.arg(j-1) = list.arg(j);
                    end;
                     list.N = list.N - 1;
                     end;
                end;
                 call format_list (Plist, 0, 1);
                 end;
            Nlines = Nlines + list.Nrows + 1;
            if length(list.title) > 0 then
                 Nlines = Nlines + 1;
            end;
             else list_base.N = list_base.N - 1;
             end;
        end;
         end;
         Nlists_of_bf_args = list_base.N;
    return;


print_brief_data: entry (PDlinfo_, Sheader, Plist_base, Iunit_syntax, Nunit_syntax, Nlists_of_bf_args);

         j = 1;             /* For -brief, print Syntax section and list of */
         do i = 1 to Nunit_syntax;      /* ctl args in order that their sections appear */
PRINT_NEXT_LIST:    if j <= Nlists_of_bf_args then do;   /* in the info.             */
             Plist = list_base.Plists(j);   /*   Print lists of args.           */
             if list.Iunit < Iunit_syntax(i) then do;
            call print_list (Plist, FALSE);
            j = j + 1;
            go to PRINT_NEXT_LIST;
            end;
             end;
        do Iunit = Iunit_syntax(i), Iunit+1 to Dlinfo_.Nunits while(^Dlinfo_.unit(Iunit).S.scn);
             if  j = 1  &  Iunit = Iunit_syntax(1)  &  Ninfos=1  &  ^Sheader  then
            call print_pgh_nnl (Dlinfo_.unit(Iunit), FALSE);
             else call print_pgh (Dlinfo_.unit(Iunit), FALSE);
             end;               /* Print syntax sections.           */
        end;
         do j = j to Nlists_of_bf_args;     /* Print remaining lists of ctl args.       */
        call print_list (list_base.Plists(j), FALSE);
        end;
    if Nlists_of_bf_args > 0 then do;
         Pnext_free_space = list_base.Plists(1);
         list_base.N = 0;
         end;
    return;
\014
brief_data_ok: entry (Sold_format, Sheader, PDlinfo_, dir, ent, Iinfo, Ninfos_printed, 
          Nlast_info_no_brief_data) returns(bit(1) aligned);

    if Sold_format then                 /* Be sure "Syntax" section(s) exist.             */
         do Iunit = 1 to Dlinfo_.Nunits while (Dlinfo_.unit(Iunit).title ^= "Usage");
        end;
    else do Iunit = 1 to Dlinfo_.Nunits
        while (substr(Dlinfo_.unit(Iunit).title, 1,
                min(6, length(Dlinfo_.unit(Iunit).title))) ^= "Syntax");
         end;                                   /*   Search for the "Syntax" section.             */
    if Iunit > Dlinfo_.Nunits then do;           /*   Tell user if not found.                      */
         call ioa_ ("^[^v/^;^s^]No control argument info available for ^[^a^[>^]^;^2s^]^a.",
        ((Ninfos_printed > 1)  &  help_args.Sctl.ca  &  (Nlast_info_no_brief_data ^= Iinfo-1)),
             help_args.Lspace_between_infos,
        Sheader,  dir, dir^=">", ent);
         Nlast_info_no_brief_data = Iinfo;
         return (FALSE);
         end;
    return(TRUE);

    end get_brief_data;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


get_ep_list: procedure (ref_name, PDeps_, Plist);   /* Create list of entry points in this phys. seg.   */

     dcl    ref_name            char(32) varying,
         (PDeps_, Plist)        ptr;

     dcl    1 Deps_         aligned based(PDeps_),
      2 Nlines      fixed bin,
      2 N           fixed bin,
      2 linfo (0: 0 refer (Deps_.N)) like Deps.linfo;
     dcl    1 list          aligned based(Plist),
      2 header      like LIST.header,
      2 group (0 refer (list.N))    like LIST.group;
     dcl (i, j, k)          fixed bin;

    do i = 1 to Deps.N;             /* Build list of all entry point info headers.  */
         k, list.N = list.N + 1;
         if length(Deps_.linfo(i).header) > 0 then do;/* If header already exists, use it.       */
        list.arg(k) = Deps_.linfo(i).header;
        if length(list.arg(k)) > 21 then do; /* Split a long heading into several lines. */
             j = 20 + index (substr(list.arg(k),21), " ");
             do while (j > 20);
            k, list.N = list.N + 1;
            list.arg(k) = "   ";
            j = j + verify(substr(list.arg(k),j), " ");
            list.arg(k) = list.arg(k) || substr(list.arg(k-1), j);
            list.arg(k-1) = rtrim(substr(list.arg(k-1),1,j-1));
            if length(list.arg(k)) > 21 then
                 j = 20 + index(substr(list.arg(k),21), " ");
            else j = 0;
            end;
             end;
        end;
         else do;               /* If doesn't exist, make one up.       */
        list.arg(k) = ref_name;
        list.arg(k) = list.arg(k) || "$";
        list.arg(k) = list.arg(k) || Deps_.linfo(i).ep_name(1);
        do j = 2 to Deps_.linfo(i).Nep_names;
             list.arg(k) = list.arg(k) || ",";
             k, list.N = list.N + 1;
             list.arg(k) = "   ";
             list.arg(k) = list.arg(k) || ref_name;
             list.arg(k) = list.arg(k) || "$";
             list.arg(k) = list.arg(k) || Deps_.linfo(i).ep_name(j);
             end;
        Deps_.linfo(i).header = list.arg(k);    /* Apply fruits of our labor by using header    */
                        /* in entry point info as well.     */
        end;
         end;

    end get_ep_list;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


get_list: procedure (Plist_base) returns(ptr);      /* This procedure allocates a new list in   */
                        /*   the help_args segment.         */

     dcl    Plist_base      ptr;

     dcl    1 list_base     aligned based(Plist_base),
      2 N           fixed bin,  /* number of lists in this list ptr structure.  */
      2 Nmax            fixed bin,  /* max possible number of lists in structure.   */
      2 Ispace_used_set     fixed bin,  /* index of last list on which space used was set.*/
      2 Plists (0 refer (list_base.Nmax))
                ptr,        /* pointers to list structures.     */
    Plist           ptr,
    1 list          aligned based(Plist),
      2 header      like LIST.header,
      2 group (0 refer (list.N))    like LIST.group;

    if list_base.N = dimension(list_base.Plists,1) then
         return(null);              /* list of lists full.  Oops!           */
    if list_base.Ispace_used_set > list_base.N then
         list_base.Ispace_used_set = 0;
    if list_base.Ispace_used_set < list_base.N-1 then
         return(null);              /* someone forgot to set space used for a list  */
                        /* other than the last in  list of lists.   */
    if list_base.Ispace_used_set = list_base.N-1 then do;
         Plist = list_base.Plists(list_base.N); /* set space used for last list.        */
         Pnext_free_space = set_space_used(Pnext_free_space, currentsize(list));
         list_base.Ispace_used_set = list_base.N;
         end;
    list_base.N = list_base.N + 1;      /* get new list.                */
    Plist = Pnext_free_space;
    list_base.Plists(list_base.N) = Plist;
    list.N = 0;
    list.Nreal  = 0;
    list.title = "";
    return(Plist);

    end get_list;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


get_list_base: procedure (Pnext_free_space, space_used, Nmax) returns (ptr);

     dcl    Pnext_free_space        ptr,        /* ptr to next free word of space in temp seg.  */
    space_used      fixed bin(21),  /* number of words used at that free word loc.  */
    Nmax            fixed bin,  /* number of lists to maintain in list of lists.    */
    Plist_base      ptr;        /* ptr to creates list of lists.        */

     dcl    1 list_base     aligned based(Plist_base),
      2 N           fixed bin,  /* number of lists in this list ptr structure.  */
      2 Nmax            fixed bin,  /* max possible number of lists in structure.   */
      2 Ispace_used_set     fixed bin,  /* index of last list on which space used was set.*/
      2 Plists (0 refer (list_base.Nmax))
                ptr;        /* pointers to list structures.     */

    if space_used ^= 0 then         /* set space used by previous allocation.   */
         Pnext_free_space = set_space_used (Pnext_free_space, space_used);
    Plist_base = Pnext_free_space;      /* get list of lists.           */
    list_base.N = 0;                /* No lists listed yet.         */
    list_base.Ispace_used_set = 0;
    list_base.Nmax = Nmax;
    if Nmax > 0 then             /* Size known?  Set space used.     */
         Pnext_free_space = set_space_used (Pnext_free_space, currentsize(list_base));
    return(Plist_base);

    end get_list_base;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


get_title_list: procedure (PDlinfo_, Plist, Iunit_start );  /* This entry builds a list of titles.      */

     dcl    PDlinfo_            ptr,
    Plist           ptr,
        Iunit_start     fixed bin;  /* Current unit number.  Get title of following */
                        /* units.                   */

     dcl    Iunit           fixed bin,
    Nlines          fixed bin,
    Nlines_pic      pic "zzzzz9",
         (i, j, k)          fixed bin;
     dcl    1 Dlinfo_           aligned based (PDlinfo_),
                        /* structure defining all paragraphs (units) in */
                        /*   an entry point (logical info seg - linfo). */
      2 Nunits      fixed bin,  /*   number of units in this ep.        */
      2 Nsections       fixed bin,  /*   number of units beginning a section.   */
      2 unit (0 refer (Dlinfo_.Nunits)) like Dlinfo.unit;
                        /*   unit (paragraph) descriptors.      */
     dcl    1 list          aligned based(Plist),
      2 header      like LIST.header,
      2 group (0 refer (list.N)) like LIST.group;

    do Iunit = Iunit_start+1 to Dlinfo_.Nunits;
         if  Dlinfo_.unit(Iunit).S.scn  |  Iunit = 1  then do;
        k, list.N = list.N + 1;
        if  Iunit = 1  &  ^Dlinfo_.unit(1).S.scn  then
             list.arg(k) = "UNTITLED";
        else list.arg(k) = Dlinfo_.unit(Iunit).title;
        if length(list.arg(k)) > 21 then do; /* Split a long section title into several lines.   */
             j = 20 + index (substr(list.arg(k),21), " ");
             do while (j > 20);
            k, list.N = list.N + 1;
            list.arg(k) = "   ";
            j = j + verify(substr(list.arg(k),j), " ");
            list.arg(k) = list.arg(k) || substr(list.arg(k-1), j);
            list.arg(k-1) = rtrim(substr(list.arg(k-1),1,j-1));
            if length(list.arg(k)) > 21 then
                 j = 20 + index(substr(list.arg(k),21), " ");
            else j = 0;
            end;
             end;
        Nlines = Dlinfo_.unit(Iunit).Nlines;    /* Count lines in  section.         */
        do i = Iunit+1 to Dlinfo_.Nunits while(^Dlinfo_.unit(i).S.scn);
             Nlines = Nlines + Dlinfo_.unit(i).Nlines + 2;
             end;
        Iunit = i - 1;
        Nlines_pic = Nlines;
        list.arg(k) = list.arg(k) || " (";
        list.arg(k) = list.arg(k) || ltrim(Nlines_pic);
        list.arg(k) = list.arg(k) || ")";
        end;
         end;
\014
    if list.N = 0 then do;
         list.N = 1;
         list.arg(1) = "NO MORE TITLES";
         end;

    end get_title_list;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


parse_answer: proc (answer, op, ep_name, ca, scn, srh);

     dcl    answer          char(500) varying,
    op          fixed bin,  /* Operation specified by the answer.       */
    ep_name         char(65) varying,   /* Name of entry point given  in ep request.    */
    1 ca            aligned,
      2 header      like LIST.header,
      2 group (100)     like LIST.group,
    1 scn           aligned,
      2 header      like LIST.header,
      2 group (100)     like LIST.group,
    1 srh           aligned,
      2 header      like LIST.header,
      2 group (100)     like LIST.group;


     dcl (i, j, k)          fixed bin,
    operation           char(12) varying,
    operand         char(89) varying;

    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* The tables below define the following combinations of answers and control args.  */
    /*                                  */
    /*    OP_CODE OPERATION   OP_CODE OPERATION   OP_CODE OPERATION         */
    /*   1  yes, y   7  skip, s 14  entry_point {STR}       */
    /*   2  no, n        8  skip -scn       ep {STR}            */
    /*   3  quit, q  9  skip -ep    15  section {STRs}          */
    /*   4  top, t  10  skip -rest      scn {STRs}          */
    /*   5  rest, r 11  skip -seen      sc {STRs}         --obsolete--  */
    /*   6  rest -scn   12  title       16  section {STRs} -top     */
    /*              titles      scn {STRs} -top         */
    /*          13  title -top      sc {STRs} -top    --obsolete--  */
    /*              titles -top                 */
    /*                                  */
    /*  17  search {STRs}           19  brief               */
    /*      srh {STRs}              bf              */
    /*      sh {STRs}   --obsolete--        20  control_arg STRs        */
    /*  18  search {STRs} -top          ca STRs         */
    /*      srh {STRs} -top         21  .               */
    /*      sh {STRs} -top   --obsolete--   22  ?               */
    /*                  23  header          */
    /*                      he              */
    /*                                  */
    /* -scn is the short name for -section.  Both are accepted.             */
    /* sc is obsolete short name for section.  It is still accepted, but -sc is not accept  */
    /*  as control arg in help requests.  It is accepted in command line, however.  */
    /* sh is obsolete short name for search.   It is still accepted.            */
    /* titles is in error, but is a ccommon error for the title request.  Accept it anyway. */
    /* -ep  is the short name for -entry_point.  Both are accepted.         */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
\014
     dcl    defined_ops (30)        char(11) varying int static options(constant) init (
                     "yes",  "y", "no",   "n",  /*  1 to  4 */
                     "quit", "q", "top",  "t",  /*  5 to  8 */
                     "rest", "r", "skip", "s",  /*  9 to 12 */
                     "title", "titles",     /* 13 &  14 */
                     "entry_point", "ep",       /* 15 &  16 */
                     "section", "scn", "sc",        /* 17 to 19 */
                     "search",  "srh", "sh",        /* 20 to 22 */
                     "brief",   "bf",       /* 23 &  24 */
                     "control_arg", "ca",       /* 25 &  26 */
                     ".", "?", "header", "he"), /* 27 to 30 */
    op_code (30)        fixed bin int static options(constant) init(
                      1,  1,  2,  2,  3,
                      3,  4,  4,  5,  5,
                      7,  7, 12, 12, 14,
                     14, 15, 15, 15, 17,
                     17, 17, 19, 19, 20,
                     20, 21, 22, 23, 23); 

    k = 0;                  /* No search  or section operands processed yet.    */
    ca.N = 0;                   /* Control_args must be given  with every ca req.   */
    ep_name = "";
    i = search (answer, "   ");     /* Find end of request name in answer.      */
    if i = 0 then i = length(answer)+1;
    operation = substr(answer,1,i-1);       /* Request name is our operation.       */
    if length(operation) > maxlength(defined_ops(1)) then do;
RESPONSE_UNKNOWN:
         call ioa_$nnl ("Response unknown:  ^a.  ", substr(answer,1,i-1));
ERROR:       op = hbound(parse_operand,1) + 1;      /* Error op code.               */
         return;
         end;
    else if length(operation) = 0 then go to ERROR; /* Just reask question for blank lines.     */
    do j = 1 to dimension(defined_ops,1) while (operation ^= defined_ops(j));
         end;                   /* See if operation defined.            */
    if j > dimension(defined_ops,1) then go to RESPONSE_UNKNOWN;
                        /* No?  Report the error.           */
\014
    op = op_code(j);
    if i >= length(answer) then          /* Remainder of answer is operands.     */
         answer = "";
    else answer = ltrim(substr(answer,i), "  ");    /* Trim leading SP HT from operands.        */
    do while(length(answer) > 0);            /* Process operands.            */
         i = search (answer, "  ");
         if i = 0 then i = length(answer)+1;
         operand = substr(answer,1,i-1);
         go to parse_operand(op);

parse_operand(5):                   /* rest                 */
         if  operand = "-section"  |  operand = "-scn"  then
        op = op + 1;
         else go to BAD_OPERAND;
         go to NEXT_OPERAND;

parse_operand(7):                   /* skip                 */
         if  operand = "-section"  |  operand = "-scn"  then
        op = op + 1;
         else if  operand = "-entry_point"  |  operand = "-ep"  then
        op = op + 2;
         else if  operand = "-rest"  |  operand = "-r"  then
        op = op + 3;
         else if  operand = "-seen"  then
        op = op + 4;
         else go to BAD_OPERAND;
         go to NEXT_OPERAND;

parse_operand(12):                  /* title                    */
         if  operand = "-top"  |  operand = "-t"  then
        op = op + 1;
         else go to BAD_OPERAND;
         go to NEXT_OPERAND;
\014
parse_operand(14):                  /* entry_point or ep            */
         if k > 0 then do;
        call ioa_ ("Only one entry point name can be given in  ^a  response.", operation);
        go to ERROR;
        end;
         if length(operand) > maxlength(ep_name) then do;
        call ioa_ ("Entry point name  ^a  is too long.", operand);
        go to ERROR;
        end;
         ep_name = operand;
         k = 1;
         go to NEXT_OPERAND;

parse_operand(15):                  /* section              */
         if  operand = "-top"  |  operand = "-t"  then
        op = op + 1;
         else do;
parse_operand(16):                  /* section -top             */
        if length(operand) > maxlength(scn.group(1).arg) then do;
             Lcount = 38;       /* 38 = length("Operand    of    response is too long.");   */
             Snl1 = (Lcount + i + length(operation) > Loutput_line);
             call ioa_$nnl ("Operand  ^a^[^/^]  of  ^a  response is too long.  ",
            substr(answer,1,i-1), Snl1, operation);
             go to ERROR;
             end;
        if k = dimension (scn.group, 1) then do;
             call ioa_$nnl ("More than ^d substrings given with  ^a  response.  ",
            dimension(scn.group,1), operation);
             go to ERROR;
             end;
        k, scn.N = k + 1;
        scn.arg(k) = operand;
        end;
         go to NEXT_OPERAND;

parse_operand(17):                  /* search                   */
         if  operand = "-top"  |  operand = "-t"  then
        op = op + 1;
         else do;
parse_operand(18):                  /* search -top              */
        if length(operand) > maxlength(srh.group(1).arg) then do;
             Lcount = 38;       /* 38 = length("Operand    of    response is too long.");   */
             Snl1 = (Lcount + i + length(operation) > Loutput_line);
             call ioa_$nnl ("Operand  ^a^[^/^]  of  ^a  response is too long.  ",
            substr(answer,1,i-1), Snl1, operation);
             go to ERROR;
             end;
        if k = dimension (srh.group, 1) then do;
             call ioa_$nnl ("More than ^d substrings given with  ^a  response.  ",
            dimension(srh.group,1), operation);
             go to ERROR;
             end;
        k, srh.N = k + 1;
        srh.arg(k) = operand;
        end;
         go to NEXT_OPERAND;
\014
parse_operand(20):                  /* control_arg STRs             */
         if length(operand) > maxlength(ca.group(1).arg) then  do;
             Lcount = 38;       /* 38 = length("Operand    of    response is too long.");   */
             Snl1 = (Lcount + i + length(operation) > Loutput_line);
             call ioa_$nnl ("Operand  ^a^[^/^]  of  ^a  response is too long.  ",
            substr(answer,1,i-1), Snl1, operation);
        go to ERROR;
        end;
         if k = dimension (ca.group, 1) then do;
        call ioa_$nnl ("More than  ^d  substrings given with  ^a  response.  ",
             dimension(ca.group, 1), operation);
        go to ERROR;
        end;
         k, ca.N = k + 1;
         ca.arg(k) = operand;
         go to NEXT_OPERAND;

parse_operand(1):                   /* yes                  */
parse_operand(2):                   /* no                   */
parse_operand(3):                   /* quit                 */
parse_operand(4):                   /* top                  */
parse_operand(19):                  /* brief                    */
parse_operand(21):                  /* . (= print "help")           */
parse_operand(22):                  /* ? (= list requests)          */
parse_operand(23):                  /* header                   */
         call ioa_$nnl ("^a  response does not allow operands.  ", operation);
         go to ERROR;

parse_operand(6):                   /* rest -scn                */
parse_operand(8):                   /* skip -scn                */
parse_operand(9):                   /* skip -ep             */
parse_operand(10):                  /* skip -rest               */
parse_operand(11):                  /* skip -seen               */
parse_operand(13):                  /* title -top               */
         call ioa_$nnl ("Invalid combination of operands for  ^a  response.  ", operation);
         go to ERROR;

BAD_OPERAND:   call ioa_$nnl ("Operand  ^a  invalid for  ^a  operation.  ", operand, operation);
         go to ERROR;

NEXT_OPERAND:  if i >= length(answer) then
        answer = "";
         else answer = ltrim (substr(answer,i), "    ");
         end;                   /* Strip leading HT SP from next operand.   */
    if op = 20 then             /* control_arg STRs             */
         if ca.N = 0 then do;
        call ioa_$nnl ("Substrings must be given with the  ^a  response.  ", operation);
        op = hbound(parse_operand,1) + 1;
        end;

    end parse_answer;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


parse_entry_point_into_units: procedure (linfo, Pcommon_units, Ncommon_units, PDlinfo_);
                        /* This procedure parses an entry point (logical    */
                        /*   info segment) into units (paragraphs). */
     dcl    1 linfo         aligned like Deps.linfo,
    Pcommon_units       ptr,
    Ncommon_units       fixed bin,
    PDlinfo_            ptr;        /* ptr to descriptors for this log info seg.    */

     dcl    1 Dlinfo_           aligned based(PDlinfo_),
      2 Nunits      fixed bin,  /* number of units (pghs) in this log info seg. */
      2 Nsections       fixed bin,  /* number of units having section title.    */
      2 unit (0 refer (Dlinfo_.Nunits))
                like Dlinfo.unit;

dcl 1 common_units (Ncommon_units)aligned based(Pcommon_units) like Dlinfo.unit;

     dcl    Iunit           fixed bin,
         (Lline1, Lline2, Lline3)   fixed bin,
         (Llseg, Lpgh)      fixed bin(21),
         (Pline1, Pline2, Pline3)   ptr,
         (Plseg, Ppgh)      ptr,
         (i, j, k)          fixed bin;

     dcl    line1           char(Lline1) based(Pline1),
    line2           char(Lline2) based(Pline2),
    line3           char(Lline3) based(Pline3),
    lseg            char(Llseg) based(Plseg),
    lseg_char   (Llseg)     char(1)     based(Plseg),
    pgh         char(Lpgh)  based(Ppgh),
    pgh_char (Lpgh)     char(1)     based(Ppgh);
\014
    Dlinfo_.Nunits = 0;
    Dlinfo_.Icommon_unit = 0;
    Dlinfo_.Nsections = 0;
    linfo.PDlinfo = PDlinfo_;
    Plseg = linfo.Pstart;
    Llseg = linfo.L;
    if linfo.S.old_format then
         go to OLD_FORMAT;          /* parse an old-format info segment.        */
    do while (Llseg > 0);            /* parse into units until log info seg exhausted.   */
         i = verify (lseg, "     
");                     /* check for pgh containing HT SP NL.       */
         if i = 0 then Llseg = 0;           /* stop if remaining part of log info seg   */
                        /*   consists of these chars.           */
         else do;
        i = verify (lseg, "  ");        /* strip blank lines from start of pgh.     */
        do while (i > 0);
             if lseg_char(i) = NL then do;
            Plseg = addr(lseg_char(i+1));
            Llseg = Llseg - i;
            i = verify (lseg, "  ");
            end;
             else i = 0;
             end;
        i = index(lseg, "


");                     /* find beginning of next pgh (<NL><NL><NL>). */
        Pline1, Ppgh = Plseg;       /* address this pgh.            */
        if i = 0 then do;           /* this is last pgh of log info seg.        */
             Lpgh = Llseg;
             Llseg = 0;
             end;
        else do;                /* next pgh found.              */
             Lpgh = i;          /* i is index relative to start of this     */
                        /*   pgh.  Save length of this pgh.     */
             Plseg = addr(lseg_char(i+1));  /* First NL of <NL><NL><NL> is in this pgh.   */
             Llseg = Llseg - i;
             end;
        j, Dlinfo_.Nunits = Dlinfo_.Nunits + 1; /* Fill in unit descriptor for this pgh.    */
        Dlinfo_.unit(j).title  = "";
        Dlinfo_.unit(j).Pstart = Ppgh;
        Dlinfo_.unit(j).L      = Lpgh;
        Dlinfo_.unit(j).Nlines = 0;
        Dlinfo_.unit(j).S      = FALSE;
        Dlinfo_.unit(j).Icommon_unit = 0;
\014

        Lline1 = index(pgh, NL);        /* See if pgh begins new section (has title).   */
        k = index (reverse(line1), ":");    /* Title ends with LAST : on 1st line of pgh.   */
        if k > 0 then do;
             k = Lline1 - (k-1);        /*   Get char index of last colon in  line. */
             Dlinfo_.unit(j).title = ltrim(rtrim(substr(line1,1,k-1), "  "), "   ");
             Dlinfo_.unit(j).Pstart = addr(pgh_char(k+1));
             Dlinfo_.unit(j).L      = Dlinfo_.unit(j).L - k;
             Dlinfo_.unit(j).S.scn = TRUE;
             Dlinfo_.Nsections = Dlinfo_.Nsections + 1;

                        /* Is section an arg_list section?      */
             if length(Dlinfo_.unit(j).title) >= 16 then
            if substr(Dlinfo_.unit(j).title,1,16) = "Control argument"  |
               substr(Dlinfo_.unit(j).title,1,16) = "Control Argument"  |
               substr(Dlinfo_.unit(j).title,1,8)  = "Argument"  |
               substr(Dlinfo_.unit(j).title,1,8)  = "List of "  then
                 Dlinfo_.unit(j).S.arg_list = TRUE;
            else;
             else if length(Dlinfo_.unit(j).title) >= 8 then
            if substr(Dlinfo_.unit(j).title,1,8) = "Argument"  |
               substr(Dlinfo_.unit(j).title,1,8) = "List of "  then
                 Dlinfo_.unit(j).S.arg_list = TRUE;
             end;
        else if j > 1 then           /* propagate arg_list finding to all pghs of sect.*/
             if Dlinfo_.unit(j-1).S.arg_list then
            Dlinfo_.unit(j).S.arg_list = TRUE;
        do while (Lpgh > 0);     /* Count lines in pgh.          */
             Dlinfo_.unit(j).Nlines = Dlinfo_.unit(j).Nlines + 1;
             i = index(pgh, NL);
             if i = 0 then Lpgh = 0;
             else do;
            if i < Lpgh then
                 Ppgh = addr(pgh_char(i+1));
            Lpgh = Lpgh - i;
            end;
             end;
        end;
         end;

    if Ncommon_units > 0 then do;            /* Add common units onto end of entry point part.   */
         i = Dlinfo_.Nunits + 1;
         Dlinfo_.Nunits = Dlinfo_.Nunits + Ncommon_units;
         addr(Dlinfo_.unit(i))->common_units = common_units;
         end;

                        /* Compute line count of entry point part.  */
    linfo.Nlines = sum(Dlinfo_.unit.Nlines) + (Dlinfo_.Nunits-1)*2;
    if length(linfo.header) > 0 then linfo.Nlines = linfo.Nlines + 2;
    return;
\014

OLD_FORMAT:
    i = verify(lseg, "   
");                     /* strip off HT NL SP chars.            */
    if i = 0 then
         Llseg = 0;
    else do;                    /* process the first unit.          */
         Plseg = addr(lseg_char(i));
         Llseg = Llseg - (i-1);
         Llseg = length(rtrim(lseg, "    
"));                        /* Remove trailing SP HT NL \006 chars.     */
         Dlinfo_.Nunits = 1;
         Dlinfo_.unit(1).Nlines = 0;
         Dlinfo_.unit(1).S      = FALSE;
         Dlinfo_.unit(1).Icommon_unit = 0;
         i = index(lseg, NL);           /* See if first line contains section title.    */
         if i = 0 then i = Llseg + 1;
         j = index(substr(lseg,1,i-1), ":");
         if j > 0 then do;           /* It does.             */
        Dlinfo_.unit(1).title  = substr(lseg,1,j-1);
        Dlinfo_.unit(1).Pstart = addr(lseg_char(j+1));
        Dlinfo_.unit(1).S.scn  = TRUE;
        Plseg = addr(lseg_char(j+1));
        Llseg = Llseg - j;
        end;
         else do;
        Dlinfo_.unit(1).title  = "";
        Dlinfo_.unit(1).Pstart = Plseg;
        end;
         Dlinfo_.unit(1).L = Llseg;     /* store interim length for now.        */
         Iunit = 0;
         end;
    do while( Iunit = 0  |  Llseg > 0);      /* parse old-format info seg into units (pghs). */
         Iunit = Iunit + 1;         /* process the next unit.           */
         Ppgh = Plseg;
         i = index(lseg, OLD_HELP_PGH_CHAR);
         if i = 0 then i = Llseg + 1;
         if  (i >= Llseg)  then do;      /* this is last pgh of log info seg.        */
        Lpgh = Llseg - 1;
        Lpgh = length(rtrim(lseg, "  
"));                        /* strip HT NL SP off end of unit.      */
        Llseg = 0;
        Dlinfo_.unit(Iunit).L = Lpgh;
        end;
         else do;               /* next pgh found.              */
        Dlinfo_.Nunits = Dlinfo_.Nunits + 1;
        Dlinfo_.unit(Iunit+1).S = FALSE;
        Dlinfo_.unit(Iunit+1).Icommon_unit = 0;
        Lpgh = i - 1;
        i = index(reverse(pgh), NL);        /* address last 3 lines of pgh.     */
        j = index(reverse(substr(pgh,1,Lpgh-i)),NL);
        if j > 0 then do;
             j = j + i;
             k = index(reverse(substr(pgh,1,Lpgh-j)),NL);
             if k = 0 then k = Lpgh+1-j;
             end;
        else k = 0;
        if k > 0 then do;
             k = k + j;
             Pline1 = addr(pgh_char(Lpgh-k+2));
             Lline1 = k-j-1;
             end;
        else do;
             Pline1 = Ppgh;
             Lline1 = 0;
             end;
        if j > 0 then do;
             Pline2 = addr(pgh_char(Lpgh-j+2));
             Lline2 = j-i-1;
             end;
        else do;
             Pline2 = Ppgh;
             Lline2 = 0;
             end;
        Pline3 = addr(pgh_char(Lpgh-i+2));
        Lline3 = i - 1;
        if Lpgh+1 < Llseg then       /* address units following the current unit.    */
             Plseg = addr(lseg_char(Lpgh+2));
        Llseg = Llseg - (Lpgh+1);
        if Llseg <= 0 then           /* Check for empty next pgh.            */
             Llseg, Lline1, Lline2, Lline3 = 0;
        if length(ltrim(line3,"  ")) > 0 & /* Section title on line containing PGH char. */
           length(ltrim(line2,"  ")) = 0 then do;
            Dlinfo_.unit(Iunit+1).title = ltrim(rtrim(line3,"    :"), "  ");
            Dlinfo_.unit(Iunit+1).S.scn = TRUE;
            Dlinfo_.Nsections = Dlinfo_.Nsections + 1;
            Lpgh = Lpgh - (j-1);
            end;
        else if length(ltrim(line3,"     ")) = 0 & 
                length(ltrim(line2,"     ")) > 0 & 
                length(ltrim(line1,"     ")) = 0 then do;
                        /* Section title on line preceding PGH char.    */
            Dlinfo_.unit(Iunit+1).title = ltrim(rtrim(line2,"    :"), "  ");
            Dlinfo_.unit(Iunit+1).S.scn = TRUE;
            Dlinfo_.Nsections = Dlinfo_.Nsections + 1;
            Lpgh = Lpgh - (k-1);
            end;
        else do;                /* No section title preceding PGH char.     */
             i = verify(lseg, "  
");          if i = 0 then do;      /* Next pgh is empty.  Forget about it. */
            Llseg = 0;
            Dlinfo_.Nunits = Dlinfo_.Nunits - 1;
            end;
             else do;           /* Ignoring any SP HT NL chars at pgh head, */
            Pline1 = addr(lseg_char(i));    /* see if a title is in 1st line of next pgh.   */
            Lline1 = Llseg - (i-1);
            j = index(line1, NL);
            if j > 0 then
                 Lline1 = j - 1;
            Lline1 = index(line1, ":");
            if Lline1 > 0 then do;
                 Dlinfo_.unit(Iunit+1).title = ltrim(rtrim(line1,"   :"), "  ");
                 Dlinfo_.unit(Iunit+1).S.scn = TRUE;
                 Dlinfo_.Nsections = Dlinfo_.Nsections + 1;
                 if Lline1+1+(i-1) < Llseg then
                Plseg = addr(lseg_char(Lline1+2+(i-1)));
                 Llseg = Llseg - (Lline1+1) - (i-1);
                 end;
            else Dlinfo_.unit(Iunit+1).title = "";
            end;
             end;
        end;
         if Dlinfo_.unit(Iunit).S.scn then
        i = 0;              /* Strip HT SP off pghs not starting a section. */
         else i = verify(pgh,NL);           /* Strip NL off other sections.     */
         if i > 1 then do;
        Ppgh = addr(pgh_char(i));
        Lpgh = Lpgh - (i-1);
        end;
         i = verify(reverse(pgh), "  
");                     /* Strip HT SP NL from end of pgh.      */
         Pline1 = addr(pgh_char(Lpgh-(i-2)));
         Lline1 = (i-1);
         k = index(line1, NL);
         if k = 0 then
        Lpgh = Lpgh - (i-1);
         else Lpgh = Lpgh - (i-1) + k;
         Dlinfo_.unit(Iunit).Pstart = Ppgh;
         Dlinfo_.unit(Iunit).L      = Lpgh;
         Dlinfo_.unit(Iunit).Nlines = 0;
         if Dlinfo_.unit(Iunit).S.scn then do;
        i = index(Dlinfo_.unit(Iunit).title, BS_underscore);
        do while (i > 0);            /* Remove underscoring from title of old info seg.*/
             if i+2 <= length(Dlinfo_.unit(Iunit).title) then
            Dlinfo_.unit(Iunit).title =
                 substr(Dlinfo_.unit(Iunit).title,1,i-1) ||
                 substr(Dlinfo_.unit(Iunit).title,i+2);
             else Dlinfo_.unit(Iunit).title =
                 substr(Dlinfo_.unit(Iunit).title,1,i-1);
             i = index(Dlinfo_.unit(Iunit).title, BS_underscore);
             end;
        i = index(Dlinfo_.unit(Iunit).title, underscore_BS);
        do while (i > 0);
             if i+2 <= length(Dlinfo_.unit(Iunit).title) then
            Dlinfo_.unit(Iunit).title =
                 substr(Dlinfo_.unit(Iunit).title,1,i-1) ||
                 substr(Dlinfo_.unit(Iunit).title,i+2);
             else Dlinfo_.unit(Iunit).title =
                 substr(Dlinfo_.unit(Iunit).title,1,i-1);
             i = index(Dlinfo_.unit(Iunit).title, underscore_BS);
             end;

                        /* Check for an arg_list section.       */
        if length(Dlinfo_.unit(j).title) >= 8 then
             if substr(Dlinfo_.unit(Iunit).title,1,8) = "Argument" then
            Dlinfo_.unit(Iunit).S.arg_list = TRUE;
             else if length(Dlinfo_.unit(Iunit).title) >= 16 then
            if substr(Dlinfo_.unit(Iunit).title,1,16) = "Control argument"  | 
               substr(Dlinfo_.unit(Iunit).title,1,16) = "Control Argument" then
                 Dlinfo_.unit(Iunit).S.arg_list = TRUE;
        end;
         else if Iunit > 1 then          /* propagate arg_list finding to all pghs of sect.*/
        if Dlinfo_.unit(Iunit-1).S.arg_list then
             Dlinfo_.unit(Iunit).S.arg_list = TRUE;

                        /* Count lines in  the pgh.         */
         do while (Lpgh > 0);
              Dlinfo_.unit(Iunit).Nlines = Dlinfo_.unit(Iunit).Nlines + 1;
              i = index(pgh, NL);
              if i = 0 then Lpgh = 0;
              else do;
             if i < Lpgh then
            Ppgh = addr(pgh_char(i+1));
             Lpgh = Lpgh - i;
             end;
              end;
         end;

                        /* Compute line count of log. info.     */
    linfo.Nlines = sum(Dlinfo_.unit.Nlines) + (Dlinfo_.Nunits-1)*2;
    if length(linfo.header) > 0 then linfo.Nlines = linfo.Nlines + 2;

    end parse_entry_point_into_units;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */

\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


parse_info_into_entry_points: procedure (APseg, ALseg, PDeps_);
                        /* Parse logical info into 1 or more entry points   */

     dcl    APseg           ptr,        /* ptr to logical info. (Input)     */
    ALseg           fixed bin(21),  /* length of logical info (in chars). (In)  */
    PDeps_          ptr;        /* ptr to entry points structure to be filled in.   */

     dcl (Llseg, Lseg)      fixed bin(21),
    Lline           fixed bin,
         (Plseg, Pseg)      ptr,
    code            fixed bin(35),
    i           fixed bin(21),
         (j, k)         fixed bin;

     dcl    line            char(Lline) based (Plseg),
    seg         char(Lseg) based(Pseg),
    seg_char (Lseg)     char(1)    based(Pseg),
    lseg            char(Llseg) based(Plseg),
    lseg_char   (Llseg)     char(1)     based(Plseg);

     dcl    1 Deps_         aligned based(PDeps_),
      2 Nlines      fixed bin,
      2 N           fixed bin,
      2 linfo (0: 0 refer (Deps_.N)) like Deps.linfo;

    Pseg = APseg;               /* Address the logical info segment.        */
    Lseg = ALseg;
    Deps_.N = -1;               /* At least 1 entry point will be found.    */
    Deps_.Nlines = 0;               /* No lines counted yet in logical info.    */
    i = index(seg, "


:Entry:");                  /* Search for 1st log info seg divider.     */
    do while(Lseg > 0);              /* record info about log. info seg.     */
         Plseg = Pseg;
         Deps_.N, j = Deps_.N + 1;
         Deps_.linfo(j).Nep_names = 0;
         if  (i = 0)  &  (j = 0)  then do;      /* This info has no :Entry: lines.      */
        Llseg = Lseg;
        Lseg = 0;
        i = verify (lseg, "  
");                     /* Strip off leading SP HT NL chars.        */
        if i = 0 then do;
             Deps_.N = Deps_.N - 1;     /* Empty entry.             */
             go to END_ENTRIES;
             end;
        else if i > 1 then do;
             Plseg = addr(lseg_char(i));
             Llseg = Llseg - (i-1);
             end;
        end;
\014
         else do;
        if i = 0 then do;           /* This is last entry point in the info.    */
             Llseg = Lseg;
             Lseg = 0;
             end;
        else do;                /* One of other entry points in the info.   */
             Llseg = i;
             Pseg = addr(seg_char(i+10));   /* 10 = length("<NL><NL><NL>:Entry:")     */
             Lseg = Lseg - (i+9);
             end;
        Lline = index(lseg, NL);        /* Search for entry names in :Entry: line.  */
        if Lline = 0 then do;
             Deps_.N = Deps_.N - 1;     /*   Nothing left of :Entry: line.      */
             go to END_ENTRIES;
             end;
        i = index(line, ":");       /* Look for : ending first entry point name.    */
        do while (i > 0);            /* Sample line looks like:          */
                        /* :Entry: rs: rsnnl: 05/25/78 ioa_$rs, ioa_$rsnnl*/
             k, Deps_.linfo(j).Nep_names = Deps_.linfo(j).Nep_names+1;
             if k <= dim(Deps_.linfo.ep_name, 2) then
            Deps_.linfo(j).ep_name(k) = ltrim(rtrim(substr(line,1,i), "  :"), "  ");
             Plseg = addr(lseg_char(i+1));  /* Skip over name and :         */
             Llseg = Llseg - i;
             Lline = Lline - i;
             i = index(line, ":");
             end;
        if  (j > 0)  &  (Deps_.linfo(j).Nep_names = 0)  then do;
             Deps_.N = Deps_.N - 1;     /* All but common info must have entry point    */
             go to NEXT_ENTRY;      /* names.  But look!            */
             end;               /* No names!  Can never read this info via help */
        i = verify(lseg, "   
");                     /* Strip off HT SP NL from start of info    */
        if i = 0 then do;           /* Nothing in info!  Forget about it.       */
             Deps_.N = Deps_.N - 1;
             go to NEXT_ENTRY;
             end;
        else if i > 1 then do;
             Plseg = addr(lseg_char(i));
             Llseg = Llseg - (i-1);
             end;
        end;
\014
         Deps_.linfo(j).Pstart  = Plseg;
         Deps_.linfo(j).L       = Llseg;
         Deps_.linfo(j).Nlines  = 0;
         Deps_.linfo(j).S       = "0"b;
         Lline = index(lseg, NL);           /* header is first line of log. info seg.   */
         if Lline >= Llseg then do;      /* header is only line of file.     */
        Deps_.linfo(j).header = "";
        Deps_.linfo(j).date    = 0;
        end;
         else if lseg_char(Lline+1) = NL then do;   /* header line must be followed by 1 blank line,    */
                        /* at least.                */
        Deps_.linfo(j).header = substr(lseg,1,Lline-1);
                        /* Remainder should be an entry point name. */
        Deps_.linfo(j).Pstart = addr(lseg_char(Lline+1));
        Deps_.linfo(j).L      = Llseg - Lline;  /* Remove header from logical info.     */
        i = search(substr(lseg,1,Lline), "   ");
        if i = 0 then
             Deps_.linfo(j).date    = 0;
        else do;
             call convert_date_to_binary_ (substr(line,1,i-1), Deps_.linfo(j).date, code);
             if code ^= 0 then      /* No date!             */
            Deps_.linfo(j).date = 0;
             end;
        end;
         else do;
        Deps_.linfo(j).header = "";
        Deps_.linfo(j).date    = 0;
        end;
         if index(lseg, OLD_HELP_PGH_CHAR) > 0 then  /* check old format info segs with \006 chars   */
        Deps_.linfo(j).S.old_format = TRUE;
NEXT_ENTRY:    i = index(seg, "


:Entry:");
         end;
END_ENTRIES:

    end parse_info_into_entry_points;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


print_header: proc ();              /* This procedure prints a regular heading line.    */

     dcl    Lcount          fixed bin,
    Linfo_name      fixed bin,
    Lpath           fixed bin;
     dcl    case            fixed bin;
     dcl    line            char(256) varying;

    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* Output heading line before other info.  The heading spans at least 2 lines, and has  */
    /* the form:                                */
    /*  pathname   (line counts)                        */
    /*  info_name:  info_title                      */
    /*                                  */
    /* where all parts but the info_title are optional.             */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */

    if help_args.Sctl.he_counts then do;        /* Do following only if line/entry counts wanted.   */
         if Deps.N = 0 then         /*   No subroutine entry points.        */
        if Nlines >=  Deps.linfo(0).Nlines then  /*     Only 1 paragraph.            */
             case = 1;
        else case = 2;          /*     Multiple paragraphs.         */
         else do;               /*   Subroutine entry points.           */
        if Iep = 0 then         /*     Subroutine introduction.     */
             if Nlines >= Deps.linfo(0).Nlines then
            case = 3;           /*       Only 1 paragraph.          */
             else case = 4;         /*       Multiple paragraphs.           */
        else                /*     A subroutine entry point.        */
             if Deps.N = 1 then     /*       Only 1 entry point.            */
            if Nlines >= Deps.linfo(Iep).Nlines then
                 case = 5;      /*   Only 1 paragraph.          */
            else case = 6;      /*   Multiple paragraphs.       */
             else               /*       Multiple entry points.     */
            if Nlines >= Deps.linfo(Iep).Nlines then
                 case = 7;      /*   Only 1 paragraph.          */
            else case = 8;      /*   Multiple paragraphs.       */
        end;
         go to FORM(case);

FORM(1):         call ioa_$rsnnl ("^d line^[s^] in info", line, 0,
        Nlines, (Nlines > 1));
         go to END_FORM;

FORM(2):         call ioa_$rsnnl ("^d ^[lines follow^;line follows^];  ^d in info", line, 0,
        Nlines, (Nlines > 1), Deps.linfo(Iep).Nlines);
         go to END_FORM;

FORM(3):         call ioa_$rsnnl ("^d line^[s^] in introduction;  ^d lines, ^d entry point^[s^] in info", line, 0,
        Nlines, (Nlines > 1), Deps.Nlines, Deps.N, (Deps.N > 1));
         go to END_FORM;
\014
FORM(4):         call ioa_$rsnnl ("^d ^[lines follow^;line follows^], ^d in introduction;  ^d lines, ^d entry point^[s^] in info",
        line, 0, Nlines, (Nlines > 1),  Deps.linfo(Iep).Nlines, Deps.Nlines, Deps.N, (Deps.N > 1));
         go to END_FORM;

FORM(5):         call ioa_$rsnnl ("^d line^[s^] in entry point", line, 0, Nlines, (Nlines > 1));
         go to END_FORM;

FORM(6):         call ioa_$rsnnl ("^d ^[lines follow^;line follows^], ^d in entry point", line, 0,
        Nlines, (Nlines > 1), Deps.linfo(Iep).Nlines);
         go to END_FORM;

FORM(7):         call ioa_$rsnnl ("^d line^[s^] in entry point;  ^d lines, ^d other entry point^[s^] in info",
        line, 0, Nlines, (Nlines > 1), Deps.Nlines, (Deps.N-1), (Deps.N-1 > 1));
         go to END_FORM;

FORM(8):         call ioa_$rsnnl ("^d ^[lines follow^;line follows^], ^d in entry point;  ^d lines, ^d other entry point^[s^] in info",
        line, 0, Nlines, (Nlines > 1), Deps.linfo(Iep).Nlines,
        Deps.Nlines, (Deps.N-1), (Deps.N-1 > 1));

END_FORM:        Lcount =length(line);
         end;
    else Lcount = 0;

    if help_args.Sctl.he_pn then do;        /* Compute length of pathname for heading.  */
         Lpath = length(rtrim(Dinfo_seg_.dir)) + length(rtrim(Dinfo_seg_.ent)) + length("   ");
         if Dinfo_seg_.dir ^= ">" then Lpath = Lpath + 1;
         end;
    else Lpath = 0;

    if help_args.Sctl.he_info_name then do;     /* If info_name to be output, compute its length    */
         Linfo_name = length(rtrim(Dinfo_seg_.info_name));
         if Linfo_name > 0 then          /* + colon + 2 spaces.          */
        Linfo_name = Linfo_name + length(":  ");
         end;
    else Linfo_name = 0;
    
    if  Lpath>0  &  Lcount>0  then
         if  Lpath+Lcount <= Loutput_line  then
        ISnl3 = 2;
         else ISnl3 = 1;
    else ISnl3 = 3;
    
    if Lpath + Lcount > 0 then
         Snl1 = TRUE;
    else Snl1 = FALSE;

    if Linfo_name + length(Deps.linfo(Iep).header) >= 0 then
         Snl2 = TRUE;
    else Snl2 = FALSE;
    
    call ioa_$nnl ("^[^v/^;^s^]^[^a^[>^]^a^;^3s^]^[^/^;   ^;^]^[(^a)^;^s^]^[^/^]^[^a:  ^;^s^]^a^[^/^]",
        (Ninfos_printed > 1),            /* For all but the first info printed,      */
        help_args.Lspace_between_infos, /*   output spaces between infos.       */
         help_args.Sctl.he_pn,          /* Put pathname into heading.           */
        Dinfo_seg_.dir, (Dinfo_seg_.dir ^= ">"), Dinfo_seg_.ent, 
         ISnl3,
         help_args.Sctl.he_counts,      /* Put line/entry point count into heading. */
        line,               /*   Description formulated above.      */
         Snl1,
         (Linfo_name > 0),           /*Put info_name into heading.           */
        Dinfo_seg_.info_name,
         Deps.linfo(Iep).header,            /* Heading line of info or entry point.     */
         Snl2);

    return;
    
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


print_header_only: entry;               /* Print header for an info.            */

    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
    /*                                  */
    /* Output a special-format info heading line when only heading lines are being output.  */
    /* The idea is for the headings to occupy as few lines as possible.         */
    /*                                  */
    /* The heading line has the form:                       */
    /*  pathname   info_name:  info_title   (line counts)               */
    /*                                  */
    /* where all fields but info_title are optional.                    */
    /*                                  */
    /*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */

         if help_args.Sctl.he_pn then do;
        Lpath = length(rtrim(Dinfo_seg_.dir)) + length(rtrim(Dinfo_seg_.ent)) + length("   ");
        if Dinfo_seg_.dir ^= ">" then Lpath = Lpath + 1;
        end;                /* If pathname to be output, compute length of  */
         else Lpath = 0;            /* path + 3 spaces.             */

         if help_args.Sctl.he_info_name then do;    /* If info_name to be output, compute length of */
        Linfo_name = length(rtrim(Dinfo_seg_.info_name));
        if Linfo_name > 0 then       /* info_name + colon + 2 spaces.        */
             Linfo_name = Linfo_name + length(":  ");
        end;
         else Linfo_name = 0;
         
         if help_args.Sctl.he_counts then do;   /* If line/entry point counts to be output, */
        if Dinfo_seg_.Scross_ref then       /* compute length of appropriate format + 3 spaces*/
             Lcount = length("   (another version)");
        else if Deps.N = 0 then
             Lcount = length("   (9999 lines in info)");
        else if Iep = 0 then
             Lcount = length("   (9999 lines, 999 entries in info)");
        else Lcount = length("   (9999 lines, 999 other entries in info)");
        end;
         else Lcount = 0;

         if Lpath + Linfo_name + length(Deps.linfo(Iep).header) + Lcount <= Loutput_line then do;
        Snl1 = FALSE;           /* Compute if heading must be broken into several   */
        Snl2 = FALSE;           /* lines.  Break points are after pathname and  */
        end;                /* before line/entry counts.            */
         else if Lpath + Linfo_name + length(Deps.linfo(Iep).header) <= Loutput_line then do;
        Snl1 = FALSE;
        Snl2 = TRUE;
        end;
         else if Linfo_name + length(Deps.linfo(Iep).header) + Lcount <= Loutput_line then do;
        Snl1 = TRUE;
        Snl2 = FALSE;
        end;
         else do;
        Snl1 = (Lpath > 0);
        Snl2 = (Lcount > 0);
        end;
\014         
         call ioa_ ("^[^a^[>^]^a^;^3s^]^[^/^]   ^[^a:  ^;^s^]^a^[
^]^[   ^[(another version)^;(^d line^[s^]^[, ^[^d^s^;^s^d other^] ^[entries^;entry^]^;^4s^] in info)^]^]",
                        /* Output long heading line.            */
        help_args.Sctl.he_pn,
             Dinfo_seg_.dir, Dinfo_seg_.dir^=">", Dinfo_seg_.ent,
        Snl1,
        Linfo_name > 0,
             Dinfo_seg_.info_name,
        Deps.linfo(Iep).header,     /* Output the  entry point heading.     */
        Snl2,
        help_args.Sctl.he_counts,
             Dinfo_seg_.Scross_ref,     /* Second occurence of info in another dir. */
             Deps.Nlines, (Deps.Nlines > 1), /* Output line count for all entry points.  */
             ((Deps.N > 0) & (Iep = 0)) | (Deps.N > 1),
                        /* Output count of entry points.        */
            (Iep = 0),      /*   Looking at common part.  Output info about */
                        /*   all entry points.          */
                 Deps.N,
                 Deps.N-1,      /*   Looking at entry point.  Output info about */
                        /*   other entry points.            */
                 ((Iep = 0) & (Deps.N > 1)) | (Deps.N-1 > 1));

    end print_header;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


print_pgh_2nl: procedure (unit, Sprint_inhibit);        /* This procedure prints a paragraph.       */

     dcl    1 unit          aligned like Dlinfo.unit,
    Sprint_inhibit      bit(1) aligned;

     dcl    code            fixed bin(35);

     dcl    1 event_info        aligned,
      2 ev_chan     fixed bin(71),
      2 message     fixed bin(71),
      2 sender      bit(36),
      2 origin,
        3 dev_signal        bit(18) unal,
        3 ring      bit(18) unal,
      2 chan_index      fixed bin;

     dcl    1 wait_list     aligned int static,
      2 N           fixed bin,
      2 ev_chan (1)     fixed bin(71);

     dcl    1 write_status      aligned int static,
      2 ev_chan     fixed bin(71) init(0),
      2 output_pending      bit(1);

    if Sprint_inhibit then return;
    call ioa_ ("^/");
    go to PRINT_PGH;


print_pgh: entry (unit, Sprint_inhibit);

    if Sprint_inhibit then return;
    call ioa_ ("");


print_pgh_nnl: entry (unit, Sprint_inhibit);

    if Sprint_inhibit then return;
PRINT_PGH:
    if unit.S.scn then              /* Print section title, if any.     */
         call ioa_$nnl ("^a:", unit.title);
    call iox_$put_chars (iox_$user_output, unit.Pstart, unit.L, code);
    call iox_$control (iox_$user_output, "write_status", addr(write_status), code);
    if  (code = 0)  &  (write_status.output_pending)  then do;
                        /* Wait until output on user's terminal before  */
         wait_list.N = 1;           /* marking pgh seen.            */
         wait_list.ev_chan(1) = write_status.ev_chan;
         call ipc_$block (addr(wait_list), addr(event_info), code);
         end;
    unit.S.seen_by_user = TRUE;         /* Keep track of what we've seen.       */
    if unit.Icommon_unit > 0 then
         common_units(unit.Icommon_unit).S.seen_by_user = TRUE;

    end print_pgh_2nl;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


seen_pgh: proc (unit) returns (bit(1) aligned);     /* Returns TRUE if pgh has been seen by user.   */

     dcl    1 unit          aligned like Dlinfo.unit,
    Sseen           bit(1) aligned;

    if unit.Icommon_unit > 0 then
         Sseen = common_units(unit.Icommon_unit).S.seen_by_user;
    else Sseen = unit.S.seen_by_user;
    return (Sseen);

    end seen_pgh;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */





    end process_info_seg;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


init: entry (procedure_name, search_list_name, search_list_ref_dir, Vrequired, Phelp_args, Acode);
      
     dcl    search_list_name        char(*),        /* Name of search list used in finding infos.   */
                        /*   (input)                */
    search_list_ref_dir     char(*),        /* Referencing dir used in  search rules.   */
                        /*   (input)                */
    Vrequired           fixed bin;  /* Required version of help_args structure. */
                        /*   (input)                */

     dcl    Parea           ptr,
    area            area (25000) based(Parea);

    if Vrequired ^= Vhelp_args_1 then do;
         Acode = error_table_$unimplemented_version;
         return;
         end;
    call get_temp_segment_ (procedure_name, Phelp_args, Acode);
    if Acode ^= 0 then              /* Obtain a temporary segment.      */
         Phelp_args = null;
    else do;
         help_args.version = Vhelp_args_1;      /* Initialize the help argument structure.  */
         string(help_args.Sctl) = "0"b;
         help_args.min_Lpgh = 4;
         help_args.max_Lpgh = 15;
         help_args.Lspace_between_infos = 2;
         help_args.min_date_time = -1;
         help_args.Npaths = 0;
         help_args.Ncas = 0;
         help_args.Nsrhs = 0;
         if search_list_name ^= "" then do;
        help_args.Nsearch_dirs = 1000;  /* Allow room for up to 1000 search dirs.   */
        Parea = set_space_used (Phelp_args, currentsize(help_args));
        area = empty();
        call search_paths_$get (search_list_name, sl_control_default, search_list_ref_dir, null, Parea,
             sl_info_version_1, sl_info_p, Acode);
        if Acode = 0 then do;
             help_args.Nsearch_dirs = sl_info.num_paths;
             if help_args.Nsearch_dirs > 0 then
            help_args.search_dirs(*) = sl_info.paths(*).pathname;
             end;
        else help_args.Nsearch_dirs = 0;
        end;
         else help_args.Nsearch_dirs = 0;
         call hcs_$truncate_seg (Phelp_args, currentsize(help_args), 0);
         end;
    return;
\014

%include sl_info;

%include sl_control_s;

/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
\014
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  **  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */


term:  entry (procedure_name, Phelp_args, Acode);

    call release_temp_segment_ (procedure_name, Phelp_args, Acode);
    return;

    end help_;
\014



            list_help.pl1                   08/12/83  1303.1r   08/12/83  1136.7      113202



/* ***********************************************************
   *                                                         *
   * Copyright, (C) Honeywell Information Systems Inc., 1982 *
   *                                                         *
   * Copyright (c) 1972 by Massachusetts Institute of        *
   * Technology and Honeywell Information Systems, Inc.      *
   *                                                         *
   *********************************************************** */


list_help:
lh:  procedure;


/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */
/*                                  */
/*             Command to list names of "help" files:               */
/*                  if no args are given, a usage message is printed,           */
/*                  arguments are taken as "topics" to be searched for,     */
/*                  only names which contain one of the topics will be listed       */
/*                                  */
/* 0)          June 1974 by John W. Dean III MITIPC             */
/*                                  */
/* 1)          Modified October 1978 by Bernie S. Greenburg for new search facility etc.    */
/*                                  */
/* 2)          Implement [list_help] and add -absolute_pathname 06/11/80 S.Herbst   */
/*                                  */
/* 3)          Modified September, 1978 by Gary E. Johnson:             */
/*                 - increase printed output limit to 4092 characters           */
/*                 - no error message when empty dir encountered            */
/*                 - ignore ".info" suffix                  */
/*                 - allow multiple -pn arguments               */
/*                 - allow -brief on active function invocation         */
/*                 - implement case insensitivity                   */
/*                 - add -long control argument                                           */
/*                                  */
/*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * */

/*   LIST OF AUTOMATIC VARIABLES    */


declare  absp_sw bit (1),               /* -absolute_pathname specified */
         af_sw bit (1),             /* invoked as an active function */
        (alp, eptr, nptr) pointer,
         arglen fixed binary (21),
        (argptr, return_ptr) ptr,
         arg_ptr entry (fixed bin, ptr, fixed bin (21), fixed bin (35), ptr) variable,
         brief bit (1),             /* controls output format */
         complain entry variable options (variable),
         code fixed bin (35),               /* general code variable */
         ecount fixed bin,
         entryname char (32) varying,
         xentryname char (32) varying,
\014
        (numpath, return_len) fixed binary,
         found bit (1),             /* topic found in file name */
        (i, iname, narg, nargs, nentry, nname) fixed bin,
         list_all bit (1),              /* -all option */
         link_target_type fixed binary (2),
         long_sw bit (1),               /* -long option */
         ndir fixed bin,                /* directory counter */
         ntopics fixed bin,             /* number of topics given as arguments */
         number_printed fixed bin,          /* number of files listed */
         outstring char (4092) varying,
         pn_flag bit (1);               /* user has specified the dir */

/* LIST OF BASED VARIABLES  */

declare  area area based (areap),
         arg char (arglen) based (argptr),
         return_arg char (return_len) varying based (return_ptr);

declare 1 entry (ecount) aligned based (eptr),
        2 type bit (2) unaligned,
        2 nnames fixed bin (15) unaligned,
        2 nindex fixed bin (17) unaligned,
         names (iname) char (32) based (nptr);

/*  LIST OF CONDITIONS */

declare  cleanup condition;

declare  absolute_pathname_ entry (char (*), char (*), fixed bin (35)),
         active_fnc_err_ entry options (variable),
         com_err_ entry options (variable),
         check_star_name_$path entry (char (*), fixed bin (35)),
         cu_$af_return_arg entry (fixed bin, ptr, fixed bin, fixed bin (35)),
         cu_$arg_list_ptr entry (pointer),
         cu_$arg_ptr_rel entry (fixed bin, ptr, fixed bin (21), fixed bin (35), ptr),
         cu_$af_arg_ptr_rel entry (fixed bin, ptr, fixed bin (21), fixed bin (35), ptr),
         get_system_free_area_ entry returns (pointer),
         hcs_$star_ entry (char (*), char (*), fixed bin (2), ptr, fixed bin, ptr, ptr, fixed bin (35)),
         hcs_$status_minf entry (char (*), char (*), fixed bin (1), fixed bin (2), fixed bin (24),
         fixed bin (35)),
         ioa_ entry options (variable),
         search_paths_$get entry (char (*), bit (36), char (*), ptr, ptr, fixed bin, ptr, fixed bin (35));


/*      LIST OF STATIC VARIABLES          */

declare  areap ptr int static init (null),
         error_table_$badopt fixed bin (35) external,   /* illegal control argument */
         error_table_$inconsistent fixed bin (35) external,
         error_table_$incorrect_access fixed bin (35) external,
         error_table_$noentry fixed bin(35) ext static,
         error_table_$nomatch fixed bin (35) external,
         error_table_$nostars fixed bin (35) external,
         error_table_$wrong_no_of_args fixed bin (35) external,
         myname char (32) options (constant) static init ("list_help"),
        (upper_case char (26) init ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
         lower_case char (26) init ("abcdefghijklmnopqrstuvwxyz")) static options (constant);


declare (divide, index, length, null, rtrim, substr, translate) builtin;

/* initialization and argument handling */


    call cu_$af_return_arg (nargs, return_ptr, return_len, code);
    if code ^= 0 then do;
         af_sw = "0"b;
         complain = com_err_;
         arg_ptr = cu_$arg_ptr_rel;
    end;
    else do;
         af_sw = "1"b;
         complain = active_fnc_err_;
         return_arg = "";
         arg_ptr = cu_$af_arg_ptr_rel;
    end;

    call cu_$arg_list_ptr (alp);

BLOCK:  begin;
declare  user_dir (divide (nargs, 2, 17, 0)) char (168);    /* for user specified directory */
declare  topic (nargs) char (32) varying;       /* can't be more topics than arguments */

         numpath = 0;               /* for pathname count */
         list_all = "0"b;
         number_printed = 0;
         ntopics = 0;
         absp_sw = "0"b;
         pn_flag = "0"b;
         brief = af_sw;
         do narg = 1 to nargs;
        call arg_ptr (narg, argptr, arglen, code, alp);
        if index (arg, "-") = 1 then do;
             if arg = "-long" | arg = "-lg" then brief = "0"b;
             else if arg = "-brief" | arg = "-bf" then brief = "1"b;
             else if arg = "-a" | arg = "-all" then list_all = "1"b;
             else if arg = "-absolute_pathname" | arg = "-absp" then absp_sw = "1"b;
             else if arg = "-pathname" | arg = "-pn" then do;
            pn_flag = "1"b;
            narg = narg + 1;
            call arg_ptr (narg, argptr, arglen, code, alp);
                        /* get user specified dir path */

            if code ^= 0 then do;
                 call complain (code, myname, "
The -pn control argument must be followed by a pathname.");
                 return;
            end;
\014

            numpath = numpath + 1;
            call absolute_pathname_ (arg, user_dir (numpath), code);
                        /* get absolute path of dir */
            if code ^= 0 then do;
                 call complain (code, myname, "^a", arg);
                 return;
end_path:           end;
            call check_star_name_$path (user_dir (numpath), code);
            if code ^= 0 then do;
                 if code = 1 | code = 2 then code = error_table_$nostars;
                 else call complain (code, myname, "^a", arg);
                 return;
            end;
             end;
             else do;
BAD_OPT:            call complain (error_table_$badopt, myname, """^a""", arg);
            return;
             end;
        end;

        else do;
             ntopics = ntopics+1;
             topic (ntopics) = translate (arg, lower_case, upper_case);
        end;


         end;                   /* of arg getting loop */

         if ntopics > 0 then do;         /* both topics and -all in same command */
        if list_all then do;
             call complain (error_table_$inconsistent, myname, "
Topics cannot be specified with -all.");
             return;
        end;
         end;
         if af_sw then if ^brief then do;
        call complain (error_table_$badopt, myname, "
The -long control arg is not accepted for active function.");
        return;
         end;

         if ntopics = 0 then do;            /* ntopics=0 and -all not specified is wrong */
        if ^list_all then do;
             call complain (error_table_$wrong_no_of_args, myname, "
You must specify topics or use the -all control_arg.  For details, type,

     help lh");
             return;
        end;
         end;
\014

         areap = get_system_free_area_ ();      /* for allocating star info */
         sl_info_p = null;
         eptr, nptr = null;
         on cleanup begin;
        if sl_info_p ^= null then free sl_info in (area);
        call clean_up;
         end;

         if ^pn_flag then do;
        call search_paths_$get ("info_segments", sl_control_default,
             "", null, areap, sl_info_version_1, sl_info_p, code);
        if code ^= 0 then do;
             call complain (code, myname, "Getting search list for info segments.");
             return;
        end;
         end;


/* searching and printing of file names */

         if pn_flag then do ndir = 1 to numpath;    /* specified by -pn option */
        call scan_dir (user_dir (ndir));
         end;
                        /* default directories */
         else do ndir = 1 to sl_info.num_paths;
        call scan_dir (sl_info.pathname (ndir));
         end;

         if number_printed = 0 & ^af_sw then call ioa_ ("No files found.");
         if sl_info_p ^= null then free sl_info in (area);

         return;

         \014                   /* internal proc to do searching and listing */

scan_dir:        procedure (dirname);

declare  dirname char (168) parameter;

        call hcs_$star_ (dirname, "**.info", 3, areap, ecount, eptr, nptr, code);
                        /* use "**" to avoid duplication of effort */
                        /* get all segs and links in specified dir */
        if code ^= 0 then do;       /* problem getting info */
             if code ^= error_table_$nomatch then do;
            call complain (code, myname, "Listing files in ^a.", dirname);

            return;
             end;
        end;
\014

        do nentry = 1 to ecount;        /* scan all entries */
             if entry.type (nentry) = "10"b then go to NEXT_ENTRY; /* ignore directories */
             iname = entry.nindex (nentry);
             if entry.type (nentry) = "00"b then do;
            call hcs_$status_minf (dirname, names(iname), 1, link_target_type,
                 0, code);
            if code = error_table_$noentry then go to NEXT_ENTRY;
            else if code = error_table_$incorrect_access then go to NEXT_ENTRY;
            else if code ^= 0 then do;
                 call complain (code, myname, "^a^[>^]^a",
                dirname, dirname ^= ">", names (iname));
                 go to NEXT_ENTRY;
            end;
            if link_target_type = 00b | link_target_type = 10b then go to NEXT_ENTRY;
             end;
             nname = 1;         /* nname is the number+1 of names with ".info" */
             found = list_all;      /* no topics matched for this seg yet */
             do nname = 1 to entry.nnames (nentry);
                        /* go through all names */
            entryname = rtrim (names (iname));

            if substr (entryname, length (entryname) - length (".info") + 1) = ".info" then do;
                 entryname = substr (entryname, 1, length (entryname) - length (".info"));
                 xentryname = translate (entryname, lower_case, upper_case);
            end;
            else go to NEXT_ENTRY;

            if ^found then do;      /* topics specified and not matched yet */
                 do i = 1 to ntopics
                     while (index (xentryname, topic (i)) = 0);
                 end;
                 found = (i <= ntopics);
            end;

            if nname = 1 then do;   /* first name */
                 if absp_sw then do;
                outstring = rtrim (dirname);
                if dirname ^= ">" then
                     outstring = outstring || ">";
                 end;
                 else outstring = "";
                 outstring = outstring || entryname;
            end;


            if ^brief & nname > 1 then do; /* additional names */
                 if nname = 2 then outstring = outstring || " (";
                 else outstring = outstring || ", ";
                 outstring = outstring || entryname;
            end;

            iname = iname+1;
             end;               /* end of loop for names of one entry */

             if found then do;      /* there is something to print */
            if ^brief & nname > 2 then   /* add closing paren */
                 outstring = outstring||")";

            if af_sw then do;
                 if number_printed > 0 then return_arg = return_arg || " ";
                 return_arg = return_arg || outstring;
            end;
            else call ioa_ ("^a", outstring);

            number_printed = number_printed+1;
             end;

NEXT_ENTRY:
        end;                /* end of processing for one entry */

        call clean_up;

         end scan_dir;              /* end of processing for one directory */

    end BLOCK;              /*  end of begin block for topic allocation */



/* procedure to free things allocated by hcs_$star_ */

clean_up:   proc;
         if nptr ^= null () then do; free names in (area); nptr = null (); end;
         if eptr ^= null () then do; free entry in (area); eptr = null (); end;
    end clean_up;


%include sl_info;
%include sl_control_s;
     end;
\014



            list_ref_names.pl1              04/09/80  1314.1rew 04/09/80  1313.5       89685



/* ******************************************************
   *                                                    *
   *                                                    *
   * Copyright (c) 1972 by Massachusetts Institute of   *
   * Technology and Honeywell Information Systems, Inc. *
   *                                                    *
   *                                                    *
   ****************************************************** */

list_ref_names: lrn: proc;

/* This command lists the reference names of segments */
/* initially coded Jan 1971 by Dan Bricklin */
/* last modified by Dan B. March 1971 */
/* fixed to abort for invalid -from and -to, Steve Herbst 11/8/77 */
/* fixed to not make copy in [pd] if copy switch is on 03/20/80 S. Herbst */

dcl
    (i, alen, from_seg, to_seg, argno, seg_no, num_null) fixed bin,
     code fixed bin (35),
     error_table_$badopt fixed bin (35) ext,
     error_table_$segknown fixed bin (35) ext,
    (aptr, segptr) ptr,
    (brief, prt, allsw, no_zero) bit (1) aligned,
     which char (16) init ("list_ref_names") int static aligned,
     dirname char (168) aligned,
     ename char (32) aligned,
     arg char (alen) based (aptr),
     ret label init (end_loop),
     plural char (1) aligned,

     1 p aligned,
     2 ignore char (31) unaligned,
     2 rname char (32) unaligned,
     2 nl char (1) unaligned,

     cu_$arg_ptr ext entry (fixed bin, ptr, fixed bin, fixed bin (35)),
     cv_oct_check_ ext entry (char (*), fixed bin (35)) returns (fixed bin (35)),
     expand_pathname_ ext entry (char (*), char (*) aligned, char (*) aligned, fixed bin (35)),
     hcs_$initiate ext entry (char (*)aligned, char (*)aligned, char (*)aligned, fixed bin (1), fixed bin (2), ptr, fixed bin (35)),
     com_err_ ext entry options (variable),
     ioa_ ext entry options (variable),
     hcs_$terminate_noname ext entry (ptr, fixed bin (35)),
     hcs_$fs_get_path_name ext entry (ptr, char (*)aligned, fixed bin, char (*)aligned, fixed bin (35)),
     hcs_$high_low_seg_count ext entry (fixed bin, fixed bin),
     hcs_$fs_get_ref_name ext entry (ptr, fixed bin, char (*), fixed bin (35)),
     ring0_get_$name ext entry (char (*)aligned, char (*)aligned, ptr, fixed bin (35)),
     iox_$put_chars entry (ptr, ptr, fixed bin, fixed bin (35)),
     iox_$user_output ptr ext;

dcl (addr, baseno, baseptr, fixed, substr) builtin;

    allsw, brief, no_zero = "0"b;           /* assume print ring-0, and not all and brief options */
    nl = "
";                      /* set nl equal to a newline char */
    argno = 1;              /* start with first argument */

    do i = 1 by 1;              /* look at all arguments */
         call cu_$arg_ptr (i, aptr, alen, code);    /* for the -all and -brief options */
         if code ^= 0 then go to next;      /* end of argument list */
         if arg = "-brief" | arg = "-bf" then brief = "1"b; /* found a brief option */
         if arg = "-all" | arg = "-a" then allsw = "1"b; /* found an all option, so set switch to remember */
    end;

next:   from_seg = 0;               /* default from segment number is zero */

    call cu_$arg_ptr (argno, aptr, alen, code); /* get next argument */

    if code ^= 0 then do;           /* end of argument list */
         if argno = 1 | argno = 2 & brief then do;  /* if nothing was given, print all non-ring zero */
        no_zero = "1"b;         /* don't print ring zero ones */
        go to all;
         end;
         return;                /* else return to caller */
    end;

    if arg = "-to" then do;         /* if the -to option is encountered, do */
         argno = argno + 1;         /* look at next argument */
to_sec:      call cu_$arg_ptr (argno, aptr, alen, code);
         if code ^= 0 then do;          /* if not there then it is an error */
error:      call com_err_ (code, which);        /* print message */
        return;             /* end of arg list, so return */
         end;

         to_seg = cv_oct_check_ (arg, code);    /* convert to number */

         if code ^= 0 | to_seg < 0 then do;
        call com_err_ (0, which, "Invalid -to argument ^a", arg);
        return;
         end;

got_to:      if from_seg>to_seg then do;     /* if lower bound > upper bound then error */
        call com_err_ (0, which, "Lower segment number bound ^o greater than upper bound ^o",
             from_seg, to_seg);
        return;
         end;

num_print:                  /* given segment number, print info */
         prt = "0"b;                /* nothing printed yet */

         do seg_no = from_seg to to_seg;        /* do for each segment number in range */

        call hcs_$fs_get_path_name (baseptr (seg_no), dirname, i, ename, code); /* get path name */
        if code ^= 0 then do;       /* if unable then try the following */
             if no_zero then go to end_loop;    /* if no ring-0 ones to be printed, don't even check */
             call ring0_get_$name (dirname, ename, baseptr (seg_no), code); /* is it in ring 0 ? */
             if code ^= 0 then go to end_loop;  /* if not, then ignore it for now */

             if dirname = "" then call ioa_ ("   ^o ^a (ring 0)", seg_no, ename); /* no dir */

             else do;

            if dirname = ">" then dirname = ""; /* don't have two >'s on root */

            call ioa_ ("   ^o ^a>^a (ring 0)", seg_no, dirname, ename); /* print info */

             end;

             prt = "1"b;

             go to printed;         /* skip around regular print routine */
        end;

        if i = 1 then dirname = "";     /* if name is only >, then remove it, since we have one */

        prt = "1"b;         /* we printed something */

        call ioa_ ("^/   ^o ^a>^a", seg_no, dirname, ename); /* print number and path */

printed:        if ^brief then do;          /* print reference names, if not brief */

             ret = end_loop;        /* pseudo call */

             go to ref_print;

        end;

end_loop:        end;                   /* end of loop for each segment number */

         if ^prt then
        if to_seg = from_seg then call com_err_ (0, which, "Invalid segment number ^o", from_seg);
        else call com_err_ (0, which, "Invalid segment numbers ^o and ^o", from_seg, to_seg);

         argno = argno + 1;
         go to next;
    end;

    if arg = "-from" | arg = "-fm" then do;     /* if -from option encountered */
         argno = argno + 1;         /* look for number after it */
         call cu_$arg_ptr (argno, aptr, alen, code);
         if code ^= 0 then go to error;     /* not found is an error */

         from_seg = cv_oct_check_ (arg, code);  /* make it a number */

         if code ^= 0 | from_seg < 0 then do;
        call com_err_ (0, which, "Invalid -from argument ^a", arg);
        return;
         end;

         call cu_$arg_ptr (argno + 1, aptr, alen, code); /* look for a "-to" after the from number */


all:         call hcs_$high_low_seg_count (to_seg, i);  /* get last allocated segment number */
         to_seg = to_seg + i;           /* by adding high hc to number after hardcore */
         if from_seg>to_seg then do;     /* starting after last seg */
        call com_err_ (0, which, "Lower bound ^o greater than highest segment number ^o.",
             from_seg, to_seg);

        return;

         end;

         if code = 0 then if arg = "-to" then do;
             argno = argno + 2;
             go to to_sec;
        end;


         go to got_to;


    end;

    if arg = "-name" | arg = "-nm" then do;     /* if name option */
         argno = argno + 1;         /* get next argument */
         call cu_$arg_ptr (argno, aptr, alen, code);    /* and treat it as a character string */
         if code = 0 then go to no_num;     /* regardless of how it looks */
         else go to error;          /* if none there, then error */
    end;

    if arg = "-brief" | arg = "-bf" then do;    /* ignore brief options since we already processed it */
         argno = argno + 1;
         go to next;
    end;

    if allsw then do;               /* if all option was present */
         from_seg = 0;              /* simulate -from 0 */
         go to all;
    end;

    if substr (arg, 1, 1) = "-" then do;        /* look for option type args which we can't identify */
         call com_err_ (error_table_$badopt, which, "^a", arg);
         return;
    end;

    seg_no = cv_oct_check_ (arg, code);     /* see if argument can be seen as a number */

    if code = 0 then do;            /* if so, then do */
         to_seg, from_seg = seg_no;     /* pretend that it is: -from num -to num */
         go to num_print;           /* go to numbered segment printing routine */
    end;

no_num: call expand_pathname_ (arg, dirname, ename, code);
    if code ^= 0 then go to error;

    call hcs_$initiate (dirname, ename, "", 0, 1, segptr, code); /* see if it is there already and where */

    if code = 0 then do;            /* wasn't known in advance, no good */
         call com_err_ (0, which, "Segment not known. ^a^[>^]^a", dirname, dirname ^= ">", ename);
         call hcs_$terminate_noname (segptr, code); /* terminate the reference */
         argno = argno + 1;         /* try next argument */
         go to next;
    end;

    if code ^= error_table_$segknown then do;   /* if it wasn't known, another error */
         call com_err_ (code, which, "^a^[>^]^a", dirname, dirname ^= ">", ename);
         argno = argno + 1;         /* try again */
         go to next;
    end;

    seg_no = fixed (baseno (segptr));       /* get segment number part of pointer */

    call hcs_$terminate_noname (segptr, code);  /* this reference dosn't count, so end it */

    call ioa_ ("^/   ^o", seg_no);      /* print segment number */

    argno = argno + 1;              /* get ready for next argument */

    if brief then go to next;           /* skip ref name printing */

    ret = next;             /* pseudo call */

ref_print:
    num_null = 0;               /* no null reference names found so far for this segment */

    do i = 1 by 1;              /* look at all reference names */
         call hcs_$fs_get_ref_name (baseptr (seg_no), i, p.rname, code); /* get reference names from this entry */
         if code ^= 0 then go to fin;       /* when ended, then go to fin */

/* if not null, write structure with it and newline */
         if p.rname ^= "" then call iox_$put_chars (iox_$user_output, addr (p.rname), 33, code);

         else num_null = num_null + 1;      /* else count number of null refs */

    end;

fin:    if num_null>0 then do;           /* print number of null refs if > zero */
         if num_null = 1 then plural = " ";     /* de-pluralize word */
         else plural = "s";         /* pluralize word */
         call ioa_ ("^d null reference name^a", num_null, plural);
    end;

    go to ret;              /* pseudo return */


     end;
\014



            print_motd.pl1                  04/13/82  1454.5rew 04/13/82  1453.8      115119



/* ******************************************************
   *                                                    *
   *                                                    *
   * Copyright (c) 1972 by Massachusetts Institute of   *
   * Technology and Honeywell Information Systems, Inc. *
   *                                                    *
   *                                                    *
   ****************************************************** */

/* ***********************************************************
   *                                                         *
   * Copyright, (C) Honeywell Information Systems Inc., 1982 *
   *                                                         *
   *********************************************************** */


/* Prints all lines in the message-of-the-day (MOTD) segment which have been changed or added since the user last used
   this command */

/* Created:  28 July 1971 by Peter R. Bos */
/* Modified: 14 December 1972 by R. Mullen to convert to version 2 PL/I */
/* Modified: 29 March 1977 by S. Herbst to convert to iox_ */
/* Modified: 14 July 1978 by S. Herbst to use Person_id.motd rather than anonymous.motd for anonymous users */
/* Modified: 12 December 1979 by S. Herbst to fix no_s_permission bug when obtaining date-time-contents-modified */
/* Modified: 23 March 1982 by G. Palter to convert to use the user's value segment */

/* format: style4,delnl,insnl,ifthenstmt,ifthen */


print_motd:
pmotd:
     procedure () options (variable);


/* DECLARATIONS */

dcl  1 user_motd aligned based (user_motd_ptr),
       2 dtcm fixed binary (71),
       2 lth fixed binary (21),
       2 motd character (user_motd_lth refer (user_motd.lth));
dcl  user_motd_ptr pointer;
dcl  user_motd_lth fixed binary (21);

dcl  system_motd character (system_motd_lth) based (system_motd_ptr);
dcl  system_motd_lth fixed binary (21);
dcl  system_motd_ptr pointer;
dcl  system_motd_dtcm fixed binary (71);

dcl  system_area area based (system_area_ptr);
dcl  system_area_ptr pointer;

dcl  code fixed binary (35);

dcl  (used, next_nl) fixed binary (21);

dcl  n_arguments fixed binary;

dcl  PRINT_MOTD character (32) static options (constant) initial ("print_motd");

dcl  NL character (1) static options (constant) initial ("
");

dcl  CHASE fixed binary (1) static options (constant) initial (1);
dcl  DELETE_OR_UNLINK bit (6) static options (constant) initial ("010110"b);

dcl  SYSTEM_CONTROL_DIR character (168) static /* options (constant) */ initial (">system_control_dir");
dcl  MOTD_ENAME character (32) static options (constant) initial ("message_of_the_day");

dcl  DEFAULT_VALUE_SEGMENT pointer static options (constant) initial (null ());
dcl  PERMANENT_VALUE bit (36) aligned static options (constant) initial ("01"b);
dcl  PMOTD_VALUE_NAME character (12) static options (constant) initial ("print_motd._");

dcl  error_table_$action_not_performed fixed binary (35) external;
dcl  error_table_$no_s_permission fixed binary (35) external;
dcl  error_table_$noentry fixed binary (35) external;
dcl  error_table_$oldnamerr fixed binary (35) external;
dcl  error_table_$wrong_no_of_args fixed binary (35) external;

dcl  iox_$user_output pointer external;

dcl  cu_$arg_count entry (fixed binary, fixed binary (35));
dcl  com_err_ entry () options (variable);
dcl  delete_$path entry (character (*), character (*), bit (6), character (*), fixed binary (35));
dcl  get_system_free_area_ entry () returns (pointer);
dcl  hcs_$status_ entry (character (*), character (*), fixed binary (1), pointer, pointer, fixed binary (35));
dcl  initiate_file_ entry (character (*), character (*), bit (*), pointer, fixed binary (24), fixed binary (35));
dcl  iox_$put_chars entry (pointer, pointer, fixed binary (21), fixed binary (35));
dcl  pathname_ entry (character (*), character (*)) returns (character (168));
dcl  terminate_file_ entry (pointer, fixed binary (24), bit (*), fixed binary (35));
dcl  user_info_ entry (character (*));
dcl  user_info_$homedir entry (character (*));
dcl  value_$get_data
    entry (pointer, bit (36) aligned, character (*), pointer, pointer, fixed binary (18), fixed binary (35));
dcl  value_$get_path entry (character (*), fixed binary (35));
dcl  value_$set_data
    entry (pointer, bit (36) aligned, character (*), pointer, fixed binary (18), pointer, pointer,
    fixed binary (18), fixed binary (35));
dcl  value_$set_path entry (character (*), bit (1), fixed binary (35));

dcl  cleanup condition;

dcl  (addr, currentsize, divide, index, null, rtrim, unspec) builtin;

/*\014*/

    call cu_$arg_count (n_arguments, code);     /* insure we are invoked properly */
    if code ^= 0 then do;
         call com_err_ (code, PRINT_MOTD);
         return;
    end;

    if n_arguments ^= 0 then do;
         call com_err_ (error_table_$wrong_no_of_args, PRINT_MOTD, "No arguments are allowed.");
         return;
    end;

    system_area_ptr = get_system_free_area_ ();

    user_motd_ptr, system_motd_ptr = null ();   /* for cleanup handler */

    on condition (cleanup)
         begin;
        if user_motd_ptr ^= null () then free user_motd in (system_area);
        if system_motd_ptr ^= null () then call terminate_file_ (system_motd_ptr, 0, TERM_FILE_TERM, (0));
         end;

    call get_user_motd ();          /* fetch user's MOTD data from the value segment */

    call get_system_motd ();            /* "fetch" system's MOTD data */

    if user_motd.dtcm >= system_motd_dtcm then go to RETURN_FROM_PRINT_MOTD;
                        /* nothing new added to system MOTD yet */


/* System MOTD has changed since this user last checked it: print any lines which do not appear in the old MOTD */

    if system_motd_lth > 0 then          /* don't bother if there's nothing in it */
         if user_motd.lth = 0 then      /* ... user hasn't seen any of it yet */
        call iox_$put_chars (iox_$user_output, system_motd_ptr, system_motd_lth, (0));

         else do;
        used = 0;
        do while (used < system_motd_lth);
             begin;
dcl  rest_of_system_motd character (system_motd_lth - used) unaligned defined (system_motd) position (used + 1);
            next_nl = index (rest_of_system_motd, NL);
            if next_nl = 0 then     /* use reset of segment */
                 next_nl = length (rest_of_system_motd);
            begin;
dcl  system_motd_line character (next_nl) unaligned defined (system_motd) position (used + 1);
                 if index (user_motd.motd, system_motd_line) = 0 then
                call iox_$put_chars (iox_$user_output, addr (system_motd_line),
                     length (system_motd_line), (0));
            end;
            used = used + next_nl;
             end;
        end;
         end;


/* Update user's MOTD to be a copy of the current system MOTD */

    free user_motd in (system_area);        /* get rid of current one */

    user_motd_lth = system_motd_lth;
    allocate user_motd in (system_area) set (user_motd_ptr);

    user_motd.dtcm = system_motd_dtcm;
    user_motd.motd = system_motd;

    call put_user_motd ();          /* put it back into the value segment */

RETURN_FROM_PRINT_MOTD:
    if user_motd_ptr ^= null () then free user_motd in (system_area);

    if system_motd_ptr ^= null () then call terminate_file_ (system_motd_ptr, 0, TERM_FILE_TERM, (0));

    return;

/*\014*/

/* Get the system's MOTD */

get_system_motd:
     procedure ();

dcl  1 short_status aligned like status_branch.short;
dcl  system_motd_bc fixed binary (24);

    call initiate_file_ (SYSTEM_CONTROL_DIR, MOTD_ENAME, R_ACCESS, system_motd_ptr, system_motd_bc, code);
    if code ^= 0 then do;
         call com_err_ (code, PRINT_MOTD, "^a", pathname_ (SYSTEM_CONTROL_DIR, MOTD_ENAME));
         go to RETURN_FROM_PRINT_MOTD;
    end;

    call hcs_$status_ (SYSTEM_CONTROL_DIR, MOTD_ENAME, CHASE, addr (short_status), null (), code);
    if (code ^= 0) & (code ^= error_table_$no_s_permission) then do;
         call com_err_ (code, PRINT_MOTD, "Determining date-time modified of ^a.",
        pathname_ (SYSTEM_CONTROL_DIR, MOTD_ENAME));
         go to RETURN_FROM_PRINT_MOTD;
    end;

    system_motd_lth = divide ((system_motd_bc + 8), 9, 21, 0);
    system_motd_dtcm = cv_fs_time (short_status.dtcm);

    return;

     end get_system_motd;



/* Convert a file-system date/time to a normal clock reading */

cv_fs_time:
     procedure (p_time_bits) returns (fixed binary (71));

dcl  p_time_bits bit (36) parameter;
dcl  time fixed binary (71);

    unspec (time) = (20)"0"b || p_time_bits || (16)"0"b;

    return (time);

     end cv_fs_time;

/*\014*/

/* Get the user's MOTD data from the value segment */

get_user_motd:
     procedure ();

    call value_$get_data (DEFAULT_VALUE_SEGMENT, PERMANENT_VALUE, PMOTD_VALUE_NAME, system_area_ptr, user_motd_ptr,
         (0), code);

    if (code = error_table_$oldnamerr) | (code = error_table_$noentry) then do;
         call convert_motd_segment ();
         call value_$get_data (DEFAULT_VALUE_SEGMENT, PERMANENT_VALUE, PMOTD_VALUE_NAME, system_area_ptr,
        user_motd_ptr, (0), code);
    end;

    if code ^= 0 then do;           /* couldn't find it anywhere: first use of print_motd */
         user_motd_lth = 0;
         allocate user_motd in (system_area) set (user_motd_ptr);
         user_motd.dtcm = 0;
    end;

    return;

/*\014*/

/* Internal to get_user_motd: converts from the old mechanism used to store per-user MOTD data to the value segment.  The
   old mechanism was a segment named Person_id.motd in the user's home directory which contained the text of the last MOTD
   seen; the DTCM of the segment was used to compare against that of the system MOTD */

convert_motd_segment:
    procedure ();

dcl  1 short_status aligned like status_branch.short;

dcl  home_dir character (168);
dcl  person_id character (24);

dcl  old_user_motd character (user_motd_lth) based (old_user_motd_ptr);
dcl  old_user_motd_bc fixed binary (24);
dcl  old_user_motd_ptr pointer;
dcl  old_user_motd_ename character (32);


         call user_info_$homedir (home_dir);
         call user_info_ (person_id);
         old_user_motd_ename = rtrim (person_id) || ".motd";

         old_user_motd_ptr = null ();       /* for cleanup handler */

         on condition (cleanup)
        begin;
             if old_user_motd_ptr ^= null () then
            call terminate_file_ (old_user_motd_ptr, 0, TERM_FILE_TERM, (0));
        end;

         call initiate_file_ (home_dir, old_user_motd_ename, R_ACCESS, old_user_motd_ptr, old_user_motd_bc, code);
         if code ^= 0 then return;      /* no old-style MOTD segment */

         call hcs_$status_ (home_dir, old_user_motd_ename, CHASE, addr (short_status), null (), code);
         if (code ^= 0) & (code ^= error_table_$no_s_permission) then do;
        call com_err_ (code, PRINT_MOTD, "Determining date-time modified of ^a.",
             pathname_ (home_dir, old_user_motd_ename));
        short_status.dtcm = ""b;        /* assume it's very old */
         end;

         user_motd_lth = divide ((old_user_motd_bc + 8), 9, 21, 0);

         allocate user_motd in (system_area) set (user_motd_ptr);
         user_motd.dtcm = cv_fs_time (short_status.dtcm);
         user_motd.motd = old_user_motd;

         call put_user_motd ();         /* returns only if OK */

         call delete_$path (home_dir, old_user_motd_ename, DELETE_OR_UNLINK, PRINT_MOTD, code);
         if code = 0 then old_user_motd_ptr = null ();/* no longer exists */
         else if code = error_table_$action_not_performed then
        call com_err_ (0, PRINT_MOTD, "^a is no longer used by this command and should be deleted.",
             pathname_ (home_dir, old_user_motd_ename));
         else call com_err_ (code, PRINT_MOTD, "Deleting ^a.", pathname_ (home_dir, old_user_motd_ename));

         return;

    end convert_motd_segment;

     end get_user_motd;

/*\014*/

/* Put the updated MOTD data into the user's value segment */

put_user_motd:
     procedure ();

    call value_$set_data (DEFAULT_VALUE_SEGMENT, PERMANENT_VALUE, PMOTD_VALUE_NAME, user_motd_ptr,
         currentsize (user_motd), null (), (null ()), (0), code);

    if code = error_table_$noentry then do;     /* value segment not present: try to create it */
         call create_default_value_segment ();
         call value_$set_data (DEFAULT_VALUE_SEGMENT, PERMANENT_VALUE, PMOTD_VALUE_NAME, user_motd_ptr,
        currentsize (user_motd), null (), (null ()), (0), code);
    end;

    if code ^= 0 then do;           /* abort the whole thing if this fails */
         call com_err_ (code, PRINT_MOTD,
        "Attempting to update message-of-the-day information in default value segment.");
         go to RETURN_FROM_PRINT_MOTD;
    end;

    return;



/* Internal to put_user_motd: create the default value segment (if possible) */

create_default_value_segment:
    procedure ();

dcl  value_segment_path character (168);

         call value_$set_path ("", "1"b, code);

         if code = 0 then do;           /* created it */
        call value_$get_path (value_segment_path, (0));
        call com_err_ (0, PRINT_MOTD, "Created ^a.", value_segment_path);
         end;

         return;

    end create_default_value_segment;

     end put_user_motd;

/*\014*/

%include access_mode_values;

%include terminate_file;
%page;
%include status_structures;

     end print_motd;
\014



            resource_usage.pl1              03/24/82  1351.3rew 03/24/82  1338.7      147411



/* ******************************************************
   *                                                    *
   *                                                    *
   * Copyright (c) 1972 by Massachusetts Institute of   *
   * Technology and Honeywell Information Systems, Inc. *
   *                                                    *
   *                                                    *
   ****************************************************** */


/* RESOURCE_USAGE - Procedure to print user's monthly (to date) resource usage and resource limits.
   This procedure gets this info from the user's PIT, where cpg_ put it.

   This procedure is called in any of four modes:

   1) long mode (specify the control_arg -long or -lg);
   this prints the month-to-date charges, resource limits, interactive usage for all shifts,
   absentee usage in all queues, and io daemon usage in all queues.

   2) default mode (called with no control_arg)
   this lists the month-to-date charge, the resource limit, the interactive usage for all shifts, and the
   absentee and io daemon usage for all queues.

   3) brief mode (called specifying the control_arg -brief or -bf)
   this lists the month-to-date charge, the resource limit, and totals for interactive,
   absentee and io daemon usage.

   4) totals mode (called specifying the control_arg -totals or -tt )
   this lists just the month-to-date charge and the resource limit.


   J. Phillipps and THVV  - June 1972
   J. Phillipps - revised and upgraded for version 2 PL/1 September 1972
   .          - revised for memory and virtual cpu charging on 6180 March 1973
   .            - updated headers and added absolute limit reporting a la sipb April 1976.

   T. Casey - August 1977  - to only print nonzero device charges and to print some new ones.
   C. Hornig    - June 1979  - to print usage even if charge was zero.
   E. N. Kittlitz   - June 1981  - UNCA rate structure changes
   */

resource_usage: ru: procedure;

dcl  crashes char (8) aligned,
     answer char (46) varying,
     answer1 char (47) varying,
     answer2 char (29) varying,
     answer3 char (18) varying,
     j fixed bin,
     logins char (7) aligned,
     datestr char (16) aligned,
     pit_name char (32) int static init ("pit"),
     reset char (16) aligned;               /* formatted string for time last reset PDT */


dcl  an fixed bin init (1),
     al fixed bin (21),
     nargs fixed bin,
     absolute bit (1) init ("0"b),
     cutoff bit (1) init ("0"b),
     month bit (1) init ("0"b),
     ec fixed bin (35),
    (i, ii) fixed bin,
     mode fixed bin init (3),
     temp float bin init (0e0);

dcl (pp, ap) ptr;

dcl  bchr char (al) unaligned based (ap);

dcl  dev_usage_buffer char (160);           /* stuff for printing device usage */
dcl  dub_array (160) char (1) unal based (addr (dev_usage_buffer));
dcl  dubp ptr;
dcl  dubl fixed bin;
dcl  based_dub char (dubl) based (dubp);
dcl (dubi, retlen) fixed bin;
dcl  devh char (8) varying;
dcl  dusw (16) bit (1) aligned;
dcl  rs_name char (32) aligned;
dcl  max_rs_number fixed bin;

dcl  ndevices fixed bin;
dcl 1 dvt (16) aligned,
    2 device_id char (8),
    2 device_price (0:7) float bin;

dcl  MILLION fixed bin (35) internal static init (1000000),
     prettybigfloat float bin int static init (1e36);

dcl  increment (0:5) char (12) aligned initial
    ("never", "daily", "monthly", "yearly", "calendar_yr", "fiscal_yr");

dcl (addr, divide, float, length, mod, null, rtrim, substr) builtin;

/* procedures called by this program */

dcl  com_err_ entry options (variable),
     cu_$arg_ptr entry (fixed bin, ptr, fixed bin (21), fixed bin (35)),
     cu_$arg_count entry (fixed bin, fixed bin (35)),
     date_time_ entry (fixed bin (71), char (*) aligned),
     get_pdir_ entry () returns (char (168)),
     hcs_$initiate entry (char (*), char (*), char (*), fixed bin (1), fixed bin (2), ptr, fixed bin (35)),
     hcs_$terminate_noname entry (ptr, fixed bin (35)),
     ioa_ entry options (variable),
     ioa_$rsnnl entry options (variable);       /* ctl,retstr,retlen,args */
dcl  system_info_$device_prices entry (fixed bin, ptr);
dcl  system_info_$rs_name entry (fixed bin, char (*) aligned, fixed bin (35));
dcl  system_info_$max_rs_number entry (fixed bin);

dcl  error_table_$badopt fixed bin (35) ext;
dcl  error_table_$too_many_args fixed bin (35) ext;


%include user_attributes;
%include pit;
\014


/* ====================================================== */

    call system_info_$max_rs_number (max_rs_number);

    call hcs_$initiate ((get_pdir_ ()), pit_name, "", 0, 1, pp, ec); /* get ptr to PIT in process dir */
    if pp = null then do;
         call com_err_ (ec, "resource_usage", "pit");
         return;
    end;
    call cu_$arg_count (nargs, ec);
    if ec ^= 0 then go to argerr;
    if nargs > 1 then do;
         call com_err_ (error_table_$too_many_args, "resource_usage", "This command only takes one argument.");
         return;
    end;

    do an = 1 to nargs;
         call cu_$arg_ptr (an, ap, al, ec);     /* see which option was specified */
         if ec ^= 0 then go to argerr;
                        /* default mode = 3 */
         else if bchr = "-long" then mode = 2;  /* if arg specifies long option, set mode */
         else if bchr = "-lg" then mode = 2;
         else if bchr = "-brief" then mode = 1; /* if arg specifies brief option, set mode accordingly */
         else if bchr = "-bf" then mode = 1;
         else if bchr = "-tt" | bchr = "-totals" | bchr = "-total" then mode = 0;
                        /* if no arg, default mode is an expanded form of brief */
         else do;
        call com_err_ (error_table_$badopt, "resource_usage", """^a""", bchr);
        return;
         end;
    end;

    if mode = 0 then do;            /* total mode is specified */
         call ioa_$rsnnl ("Month-to-Date: $^9.2f; Limit: $^9a; ",
        answer, j, pp -> pit.dollar_charge, (cv_limit (pp -> pit.dollar_limit)));
                        /* if cutoff set by proj administrator, print it also */
         if substr ((cv_limit (pp -> pit.absolute_limit)), 6) = "open" then do;
        call ioa_$rsnnl ("Total: $^9.2f;", answer3, j, pp -> pit.absolute_spent);
        call ioa_ ("^/^a", answer || answer3);
         end;
         else do;
        call ioa_$rsnnl ("Total: $^9.2f; Absolute Limit: $^9a; ",
             answer1, j, pp -> pit.absolute_spent, (cv_limit (pp -> pit.absolute_limit)));
        call date_time_ (pp -> pit.absolute_cutoff, datestr);
        call ioa_$rsnnl ("Reset: ^a, ^a;",
             answer2, j, substr (datestr, 1, 8), increment (pp -> pit.absolute_increm));
        call ioa_ ("^/^a", answer);
        call ioa_ (answer1 || answer2);
         end;
         goto endit1;

    end;
    call date_time_ (pp -> pit.proc_creation_time, datestr); /* usage from beginning of mo. to time process created */
    call date_time_ (pp -> pit.time_last_reset, reset); /* format time last reset PDT */
    call ioa_ ("^/^a.^a  Report from ^a to ^a", pp -> pit.login_name, pp -> pit.project, reset, datestr);
    if max_rs_number > 0 then do;
         call system_info_$rs_name ((pp -> pit.rs_number), rs_name, ec);
         if ec ^= 0 then call com_err_ (ec, "resource_usage",
        "For rate structure ^d.  Contact your system administrator.", pp -> pit.rs_number);
         call ioa_ ("^5xRate Structure -- ^a", rs_name);
    end;

quick:
    if mode = 2 then do;            /* long mode */
         if substr ((cv_limit (pp -> pit.absolute_limit)), 6) = "open" then do;
        call ioa_$rsnnl ("Month-to-Date: $^9.2f; Limit: $^9a; ",
             answer, j, pp -> pit.dollar_charge, (cv_limit (pp -> pit.dollar_limit)));
        call ioa_$rsnnl ("Total: $^9.2f;", answer3, j, pp -> pit.absolute_spent);
        call ioa_ (answer || answer3);
         end;
         else do;
        call ioa_$rsnnl ("Total: $^9.2f, Absolute Limit: $^9a; ",
             answer1, j, pp -> pit.absolute_spent, (cv_limit (pp -> pit.absolute_limit)));
        call date_time_ (pp -> pit.absolute_cutoff, datestr);
        call ioa_$rsnnl ("Reset: ^a, ^a;",
             answer2, j, substr (datestr, 1, 8), increment (pp -> pit.absolute_increm));
        call ioa_ (answer1 || answer2);
         end;
    end;

    else do;
         call ioa_ ("Month-To-Date: $^9.2f;  Limit: $^9a;",
        pp -> pit.dollar_charge, (cv_limit (pp -> pit.dollar_limit)));
    end;
    do i = 1 to 7, 0;               /* sum interactive charge */
         temp = temp + pp -> pit.interactive (i).charge;
    end;
    if temp = 0e0 then do;          /* if no usage, don't print header */
         call ioa_ ("^/Interactive Usage: none;");
         go to abstee;
    end;

/*    P R I N T  H E A D E R S  */

    if pp -> pit.crashes = 1 then
         crashes = "crash.  ";          /* do singular and plural parse */
    else crashes = "crashes.";
    if pp -> pit.logins = 1 then
         logins = "login, ";
    else logins = "logins,";
    if mode = 2 then do;            /* long mode header for interactive usage */
         call ioa_ ("^/Interactive Usage: $^8.2f;^2x^2d^1x^8a^2d^1x^7a",
        temp, pp -> pit.logins, logins, pp -> pit.crashes, crashes);
         call ioa_ ("^4xshift^2x$charge^4x$limit^8xvcpu^4xconnect^4xterminal i/o^2xmemory/kmu");
    end;
    else if mode = 1 then do;           /* brief mode header for interactive usage */
         call ioa_ ("^/Interactive Usage: $^8.2f;^2x^2d^1x^8a^2d^1x^7a",
        temp, pp -> pit.logins, logins, pp -> pit.crashes, crashes);
         go to abstee;
    end;
    else if mode = 3 then do;           /* default mode */
         call ioa_ ("^/Interactive Usage: $^8.2f;^2x^2d^1x^8a^2d^1x^7a",
        temp, pp -> pit.logins, logins, pp -> pit.crashes, crashes);
         call ioa_ ("^4xshift^2x$charge^4x$limit");
    end;
    temp = 0e0;             /* clear temp */


/*   P R I N T   R E S O U R C E  U S A G E   */


/* print out resource usage */

    do i = 1 to 7, 0;
         if pp -> pit.shift_limit (i) > prettybigfloat
         then if pp -> pit.interactive (i).charge = 0e0
        & pp -> pit.interactive (i).cpu = 0
        & pp -> pit.interactive (i).connect = 0
        & pp -> pit.interactive (i).core = 0
        & pp -> pit.interactive (i).io_ops = 0
        then goto skip;         /* don't print useless lines */
         if mode = 2 then do;           /* long mode */
        call ioa_ ("^5x^1d^4x^8.2f^1x^9a^3x^9a^2x^9a^5x^11.1f^1x^11.1f", i,
             pp -> pit.interactive (i).charge, (cv_limit (pp -> pit.shift_limit (i))),
             (cv_time (pp -> pit.interactive (i).cpu)), (cv_time (pp -> pit.interactive (i).connect)),
             float (pp -> pit.interactive (i).io_ops/1e3), float (pp -> pit.interactive (i).core/1e6));
         end;
         else if mode = 3 then do;      /* default mode */
        call ioa_ ("^5x^1d^4x^8.2f^1x^9a", i, pp -> pit.interactive (i).charge,
             (cv_limit (pp -> pit.shift_limit (i))));
skip2:       end;
skip:   end;

abstee: temp = 0e0;             /* clear temp */
    do ii = 1 to 4;             /* scan for absentee usage */
         temp = temp + pp -> pit.absentee (ii).charge;
    end;
    if temp = 0e0 then do;
         call ioa_ ("^/Absentee Usage:    none;");
         go to iod;
    end;
    else call ioa_ ("^/Absentee Usage:    $^8.2f;", temp);
    if mode = 1 then go to iod;         /* brief mode exit here */
    else if mode = 3 then do;           /* default mode */
         call ioa_ ("^4xqueue^2x$charge^6xjobs");
         do ii = 1 to 4;
        if pp -> pit.absentee (ii).charge = 0e0 then go to next;
        call ioa_ ("^5x^1d^4x^8.2f^6x^4d", ii, pp -> pit.absentee (ii).charge, pp -> pit.absentee (ii).jobs);
next:        end;
    end;
    if mode = 2 then do;            /* long mode */
         call ioa_ ("^4xqueue^2x$charge^6xjobs^8xvcpu^2xmemory/kmu");

         do ii = 1 to 4;
        if pp -> pit.absentee (ii).charge = 0e0 then go to skip3;
        call ioa_ ("^5x^1d^4x^8.2f^6x^4d^3x^9a^2x^10.1f", ii, pp -> pit.absentee (ii).charge,
             pp -> pit.absentee (ii).jobs, cv_time (pp -> absentee (ii).cpu),
             float (pp -> pit.absentee (ii).memory/1e6));
skip3:       end;
    end;
iod:    temp = 0e0;
    do ii = 1 to 4;
         temp = temp + pp -> pit.iod (ii).charge;
    end;
    if temp = 0e0 then do;
         call ioa_ ("^/IO Daemon Usage:   none;");
         go to device;
    end;
    else call ioa_ ("^/IO Daemon Usage:   $^8.2f;", temp);
    if mode = 1 then go to device;      /* brief mode exit here */
    else if mode = 3 then do;           /* default mode */
         call ioa_ ("^4xqueue^2x$charge^6xlines");
         do ii = 1 to 4;
        if pp -> pit.iod (ii).charge = 0e0 then go to next2;
        call ioa_ ("^4x^1x^1d^4x^8.2f^x^10d",
             ii, pp -> pit.iod (ii).charge, pp -> pit.iod (ii).lines);
next2:       end;
    end;
    if mode = 2 then do;            /* long mode */
         call ioa_ ("^4xqueue^2x$charge^4xpieces^7xpages^8xlines");
         do ii = 1 to 4;
        if pp -> pit.iod (ii).charge = 0e0 then go to skip4;
        call ioa_ ("^4x^1x^1d^4x^8.2f^6x^4d^3x^9d^x^12d",
             ii, pp -> pit.iod (ii).charge, pp -> pit.iod (ii).pieces,
             pp -> pit.iod (ii).pages, pp -> pit.iod (ii).lines);
skip4:       end;
    end;
device: temp = 0e0;             /* clear temp */
    do ii = 1 to 16;                /* scan for device usage */
         if pp -> pit.devices (ii) = 0e0
         then dusw (ii) = ""b;
         else dusw (ii) = "1"b;
         temp = temp + pp -> pit.devices (ii);
    end;
    if temp = 0e0 then do;
         if mode = 2 then
        call ioa_ ("^/Device Usage:   none;");  /* only print in long mode */
    end;
    else do;
         call ioa_ ("^/Device Usage:      $^8.2f;", temp);

/* The purpose of the following code is to print a column only for devices with nonzero usage. */

         call system_info_$device_prices (ndevices, addr (dvt)); /* get device names and prices (but only use names) */
         dev_usage_buffer = "";         /* clear buffer before making heading */
         dubi = 1;              /* set index to first character position in buffer */
         do i = 1 to 16;            /* put each device name in heading */
        if dusw (i) then do;        /* but only if it has nonzero usage */
             devh = rtrim (device_id (i));  /* copy name and see how long it really is */
             dubi = dubi + 9 - length (devh);   /* compute how far to skip to right-adjust name */
             substr (dev_usage_buffer, dubi, 1) = "$"; /* put in leading dollar sign */
             substr (dev_usage_buffer, dubi+1, length (devh)) = devh; /* put in the name */
             dubi = dubi + 1 + length (devh);   /* advance char index past name */
        end;
         end;
         call ioa_ ("^a", dev_usage_buffer);    /* print the heading */

         dev_usage_buffer = "";         /* clear buffer before formatting usage figures */
         dubl = length (dev_usage_buffer);      /* initialize char counters */
         dubi = 1;
         do i = 1 to 16;            /* print usage for each device */
        if dusw (i) then do;        /* only if it is nonzero */
             dubp = addr (dub_array (dubi));    /* get addr of where to put next usage field */
             call ioa_$rsnnl ("^3x^7.2f", based_dub, retlen, pp -> pit.devices (i)); /* format the usage figure */
             dubl = dubl - retlen;      /* decrement remaining length of buffer */
             dubi = dubi + retlen;      /* advance index to next available character position */
        end;
         end;
         call ioa_ ("^a", dev_usage_buffer);    /* print the usage figures */
    end;
endit:  call ioa_ ("");
endit1: call hcs_$terminate_noname (pp, ec);
    return;

argerr: call com_err_ (ec, "resource_usage");
    return;
\014

/* =========================================================== */



cv_time:    procedure (time) returns (char (9) aligned);

/* procedure to convert from fixed bin (71) to a nice formatted string of hrs: mins: secs */



dcl  time fixed bin (71),
     j fixed bin,
     hours fixed bin,
     minutes fixed bin,
     seconds fixed bin,
     answer char (9) aligned;


         seconds = divide (time, MILLION, 35, 0);
         minutes = divide (seconds, 60, 35, 0);
         seconds = mod (seconds, 60);       /* get rid of the remainder */
         hours = divide (minutes, 60, 35, 0);
         minutes = mod (minutes, 60);       /* get rid of the remainder */

         call ioa_$rsnnl ("^3d:^2d:^2d", answer, j, hours, minutes, seconds);
         if substr (answer, 5, 1) = " " then substr (answer, 5, 1) = "0";
         if substr (answer, 8, 1) = " " then substr (answer, 8, 1) = "0";

         return (answer);
    end;


\014

/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */

cv_limit:   procedure (limit) returns (char (9) aligned);

/* procedure to convert a float bin $limit into either the string, "open", if $limit is >= 1e37,
   or to convert a float bin $limit into an integer $limit  */

dcl  limit float bin,
     lim char (9) aligned,
     jj fixed bin,
     itemp fixed bin;


         if limit >= prettybigfloat then do;
        lim = "     open";
        go to char;
         end;
         else do;
        itemp = limit;
        call ioa_$rsnnl ("^6d.00", lim, jj, itemp);
         end;
char:        return (lim);
    end;



test_ru:    entry (xpit);               /* enter here if using an experimental PIT */

dcl  xpit char (*);

    pit_name = xpit;



     end resource_usage;
\014



            ring0_get_.pl1                  11/28/77  1537.3rew 11/28/77  1516.6       52101



/* ******************************************************
   *                                                    *
   *                                                    *
   * Copyright (c) 1972 by Massachusetts Institute of   *
   * Technology and Honeywell Information Systems, Inc. *
   *                                                    *
   *                                                    *
   ****************************************************** */

ring0_get_: proc;

/* "Adjusted" by Bernard Greenberg, for hc def seg 07/22/76 */

dcl (sltp1, names_ptr1, defs_ptr1) ptr static init (null),
    (names_ptr2, defs_ptr2) ptr,
    (a_defsp, a_sltp, a_namep, defp, defsp) ptr,
    (i, j) fixed bin,
     hcs_$initiate ext entry (char (*), char (*), char (*), fixed bin, fixed bin, ptr, fixed bin),
     get_definition_ entry (ptr, char (*), char (*), ptr, fixed bin);

dcl  based_bit18 bit (18) aligned based dim (0:511);
dcl  a_defname char (*),
     a_offset fixed bin (18),
     a_type fixed bin;



dcl (error_table_$no_defs, error_table_$noentry) fixed bin (35) ext;
dcl  error_table_$invalidsegno fixed bin (35) ext;

dcl  segptr ptr,
     code fixed bin,
     entryptr ptr,
     dir char (*),
     entry char (*);

dcl  SLDIR char (55) init (">system_library_1") static options (constant);


dcl (addr, addrel, baseno, baseptr, fixed, length, null, reverse, verify) builtin;


/* \014 */

segptr: entry (dir, entry, segptr, code);       /* entry to return segment pointer */

    call get_static_ptrs ("0"b);
    segptr = get_segptr ();
    return;


name:   entry (dir, entry, segptr, code);       /* entry to return segment name */

    call get_static_ptrs ("0"b);
    call get_name (segptr, "0"b);           /* 0 => one */
    return;

names:  entry (dir, entryptr, segptr, code);        /* entry to return pointer to names */

    call get_static_ptrs ("0"b);
    call get_name (segptr, "1"b);           /* 1 => many */
    return;

definition: entry (segptr, entry, a_defname, a_offset, a_type, code);

    call get_static_ptrs ("1"b);            /* 1 => get defs ptr */
    if segptr = null then segptr = get_segptr ();
    call get_definition (segptr);
    return;



/* The following entries are the same as the above except that the caller
   supplies a pointer to the SLT and NAME TABLE to use */

segptr_given_slt: entry (dir, entry, segptr, code, a_sltp, a_namep);

    call get_param_ptrs ("0"b);
    segptr = get_segptr ();
    return;

name_given_slt: entry (dir, entry, segptr, code, a_sltp, a_namep);

    call get_param_ptrs ("0"b);
    call get_name (segptr, "0"b);
    return;


definition_given_slt: entry (segptr, entry, a_defname, a_offset, a_type, code, a_sltp, a_namep, a_defsp);

    call get_param_ptrs ("1"b);
    if segptr = null then segptr = get_segptr ();   /* Look up entry if needed */
    call get_definition (segptr);
    return;

/* \014 */
get_static_ptrs: proc (we_want_defs);

dcl  we_want_defs bit (1) aligned;          /* T => call for defs */

         code = 0;
         if we_want_defs & defs_ptr1 = null then call init_static_ptrs;
         else if names_ptr1 = null then call init_static_ptrs;
         if code ^= 0 then go to error;
         sltp = sltp1;
         names_ptr2 = names_ptr1;
         defs_ptr2 = defs_ptr1;
         return;

init_static_ptrs: proc;
        call hcs_$initiate (SLDIR, "slt", "", 0, 1, sltp1, code);
        if sltp1 = null then return;
        call hcs_$initiate (SLDIR, "name_table", "", 0, 1, names_ptr1, code);
        if names_ptr1 = null then return;
        if we_want_defs then do;
             call hcs_$initiate (SLDIR, "definitions_", "", 0, 1, defs_ptr1, code);
             if defs_ptr1 = null then return;
        end;
        code = 0;               /* Let's hear it for hcs_$initiate! */
         end init_static_ptrs;
    end get_static_ptrs;

get_param_ptrs: proc (we_want_defs);

dcl  we_want_defs bit (1) aligned;          /* We want definitions */
         sltp = a_sltp;
         names_ptr2 = a_namep;
         if we_want_defs then defs_ptr2 = a_defsp;
    end get_param_ptrs;

get_segptr: procedure returns (ptr);


         do i = slt.first_sup_seg to slt.last_sup_seg; /* loop through sup segs searching */
        sltep = addr (slt.seg (i));     /* get pointer to SLT entry */
        namep = addrel (names_ptr2, slte.names_ptr); /* get pointer to names for this segment */
        do j = 1 to namep -> segnam.count;   /* search all names */
             if entry = namep -> segnam.names (j).name then do; /* found it */
            code = 0;
            return (baseptr (i));
             end;
        end;
         end;

         code = error_table_$noentry;
         go to error_segptr;
    end get_segptr;

get_name:   procedure (sp, many);

dcl  many bit (1) aligned, sp ptr;

         i = bin (baseno (sp));         /* get input segment number */
         if i > slt.last_sup_seg | i < slt.first_sup_seg then do; /* bad input segment number */
        code = error_table_$invalidsegno;
        return;
         end;

         sltep = addr (slt.seg (i));        /* get pointer to SLT entry */
         pathp = addrel (names_ptr2, slte.path_ptr);
         namep = addrel (names_ptr2, slte.names_ptr);
         if pathp ^= names_ptr2 then dir = pathp -> path.name; else dir = ""; /* return path name */
         if many then entryptr = namep;
         else entry = namep -> segnam.names (1).name; /* return only one name */
         code = 0;
    end get_name;

get_definition: procedure (textp);
dcl  textp ptr;

         code = 0;
         i = bin (baseno (textp));
         if i < 4 | i > 511 then code = error_table_$no_defs;
         else do;
        defsp = addrel (defs_ptr2, defs_ptr2 -> based_bit18 (i));
        if defsp = defs_ptr2 then code = error_table_$no_defs;
        else do;
             call get_definition_ (defsp, entry, a_defname, defp, code);
             if code = 0 then do;
            a_type = fixed (defp -> definition.class, 3);
            a_offset = fixed (defp -> definition.value, 18);
             end;
        end;
         end;

    end get_definition;

error_segptr: segptr = null;
error:  return;
                        /* \014 */
% include slt;
% include slte;
% include definition;
     end;
\014



            system_info_.pl1                08/12/83  1303.1r   08/12/83  1136.7      175644



/* ***********************************************************
   *                                                         *
   * Copyright, (C) Honeywell Information Systems Inc., 1982 *
   *                                                         *
   * Copyright (c) 1972 by Massachusetts Institute of        *
   * Technology and Honeywell Information Systems, Inc.      *
   *                                                         *
   *********************************************************** */


system_info_: proc;

/* SYSTEM_INFO_ - return various information to user about Multics.

   The information is obtained from the header of "whotab"
   or from "installation_parms" or from the correct "rate_structure_x" seg.

   See AG93 (Multics Subroutines and Input/Output Modules) for documentation
   of the following entries:

   $id (installation_id)
   $sysid (sysid)
   $version_id
   $titles (company, dept, companyds, deptds)
   $users (maxusers, nusers, maxunits, nunits)
   $timeup (time)
   $prices (cpu, log, process, core, disk, registration)
   $prices_rs (rs_number, cpu, log, process, core, disk, registration)
   $device_prices (ndevices, addr (dvt))
   $device_prices_rs (rs_number, ndevices, addr (dvt))
   $abs_limits (default_cpu_limits, default_foreground_cpu_limit, max_cpu_limits)
   $abs_prices (farray)
   $default_absentee_queue (default_queue)
   $abs_prices_rs (rs_number, farray)
   $io_prices (farray)
   $io_prices_rs (rs_number, farray)
   $next_shutdown (time, reason, until)
   $last_shutdown (time, erfno)
   $shift_table (stt)
   $access_ceiling (access_ceiling)
   $level_names (long_names, short_names)
   $category_names (long_names, short_names)
   $log_threshold (state, npages)
   $next_shift_change (cur_shift, change_time, new_shift, start_time)
   $ARPANET_host_number (host_num)
   $resource_price (price_name, price, code)
   $resource_price_rs (rs_number, price_name, price, code)
   $rs_name (rs_number, rs_name, code)
   $rs_number (rs_name, rs_number, code)
   $max_rs_number (rs_count)

   See AN-66 (AS PLM) for documentation of the following internal interfaces:

   $abs_chn (evchn, pid)
   $request_chn (pid, event_channel, mseg_dname, mseg_ename)

   Written by THVV
   Modified 741231 by PG to add entries for new AIM fields.
   Modified 750324 by PG to rename $dial_chn to $request_chn.
   Modified 750912 by PG to give request facility its own process id.
   Modified 751103 by PG to complain if can't initiate whotab/installation_parms.
   Modified April 1976 by T. Casey to return shift start time as fourth argument to next_shift_change entry point.
   Modified 761229 by D. M. Wells to add $ARPANET_host_number entry point.
   Modified May 1978 by T. Casey to add resource_price entry point.
   Modified November 1978 by T. Casey for MR7.0 to add arguments to abs_limits entry point.
   Modified July 1979 by J. N. R. Barnecut for MR8.0 to add rate_structure entry points. (UNCA)
   Modified Feb 1980 by M. B. Armstrong for further changes re rate_structure. (UNCA)
   Modified 17 September 1980 by G. Palter to add default_absentee_queue entrypoint.
   Modified June 1981 by E. N. Kittlitz for UNCA rate structures.
   Modified May 1983 by Art Beattie to add version_id entry.
*/

    return;

/* parameters */

dcl  rs_number fixed bin,
     rs_name char (*),
     ndev fixed bin,
     devp ptr;

/* external variables */

dcl  error_table_$no_entry ext fixed bin (35);

/* entries */

dcl  absolute_pathname_ entry (char (*), char (*), fixed bin (35));
dcl  active_all_rings_data$version_id ext char (8);
dcl  com_err_ entry options (variable);
dcl  cu_$arg_count entry (fixed bin);
dcl  cu_$arg_ptr entry (fixed bin, ptr, fixed bin, fixed bin (35));
dcl  datebin_$next_shift_change entry (fixed bin (71), fixed bin (71), fixed bin, fixed bin);
dcl  get_pdir_ entry () returns (char (168));
dcl  hcs_$terminate_noname entry (ptr, fixed bin (35));
dcl  hcs_$make_seg entry (char (*), char (*), char (*), fixed bin (5), ptr, fixed bin (35));
dcl  hcs_$initiate entry (char (*), char (*), char (*), fixed bin (1), fixed bin (2), ptr, fixed bin (35));


/* automatic */

dcl  ap ptr,                    /* ptr to arg */
     al fixed bin,                  /* lth of arg */
     arg_offset fixed bin,
     cur_rs_ptr ptr init (null),
     ec fixed bin (35),
     nargs fixed bin,
    (i, j) fixed bin,
     t71 fixed bin (71),
    (t1, t2) fixed bin;

/* based */

dcl  bchr char (al) based (ap),         /* character arg */
     bfix fixed bin (35) based (ap),            /* fixed bin arg */
     bflo float bin (27) based (ap),            /* float bin arg */
     b71 fixed bin (71) based (ap),         /* dbl prec arg */
     b36 bit (36) aligned based (ap),           /* bit (36) arg */
     bfa (0: 7) float bin (27) based (ap),      /* float array arg */
     based_fixed_array (4) fixed bin (35) based (ap),   /* fixed array arg */
     based_shift_queue_array (0:7, 4) fixed bin (35) based (ap); /* array arg for per-shift-and-queue absentee parms */

/* internal static */

dcl (whoptr, pp) ptr int static init (null);
dcl  rs_ptrs (0:9) ptr int static init ((10) null);
dcl  ip ptr defined (rs_ptrs (0));
dcl  sysdir char (168) int static init (">system_control_1");

/* builtins */

dcl (char, clock, fixed, hbound, ltrim, max, null) builtin;

/* include files */

%include installation_parms;

%include rate_structure;

%include whotab;

%include pitmsg;

%include user_attributes;

/* ======================================================== */

installation_id: entry;

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);       /* Get ptr to string argument. */
    if ec ^= 0 then return;
    bchr = installation_parms.installation_id;

    return;

/* -------------------------------------------------------- */

sysid:  entry;

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;
    bchr = whotab.sysid;

    return;


/* -------------------------------------------------------- */

version_id:
    entry;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;
    bchr = active_all_rings_data$version_id;

    return;

/* -------------------------------------------------------- */

titles: entry;

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;
    bchr = installation_parms.company;
    call cu_$arg_ptr (2, ap, al, ec);
    if ec ^= 0 then return;
    bchr = installation_parms.department;
    call cu_$arg_ptr (3, ap, al, ec);
    if ec ^= 0 then return;
    bchr = installation_parms.companyds;
    call cu_$arg_ptr (4, ap, al, ec);
    if ec ^= 0 then return;
    bchr = installation_parms.departmentds;

    return;

/* -------------------------------------------------------- */

users:  entry;

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;
    bfix = whotab.mxusers;
    call cu_$arg_ptr (2, ap, al, ec);
    if ec ^= 0 then return;
    bfix = whotab.n_users;
    call cu_$arg_ptr (3, ap, al, ec);
    if ec ^= 0 then return;
    bfix = whotab.mxunits;
    call cu_$arg_ptr (4, ap, al, ec);
    if ec ^= 0 then return;
    bfix = whotab.n_units;

    return;

/* -------------------------------------------------------- */

timeup: entry;

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;
    b71 = whotab.timeup;

    return;

/* -------------------------------------------------------- */

next_shutdown: entry;

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;
    b71 = whotab.nextsd;
    call cu_$arg_ptr (2, ap, al, ec);
    if ec ^= 0 then return;
    if whotab.why < "" then bchr = "";
    else bchr = whotab.why;
    call cu_$arg_ptr (3, ap, al, ec);
    if ec ^= 0 then return;
    b71 = whotab.until;

    return;

/* -------------------------------------------------------- */

last_shutdown: entry;

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;
    b71 = whotab.lastsd;
    call cu_$arg_ptr (2, ap, al, ec);
    if ec ^= 0 then return;
    bchr = whotab.erfno;

    return;

/* -------------------------------------------------------- */

rates:
prices: entry;

    call setup_user_rs;
    arg_offset = 0;

rates_join:
    call cu_$arg_ptr (arg_offset + 1, ap, al, ec);
    if ec ^= 0 then return;
    do i = 0 to 7;
         bfa (i) = rate_structure.cpu_price (i);
    end;
    call cu_$arg_ptr (arg_offset + 2, ap, al, ec);
    if ec ^= 0 then return;
    do i = 0 to 7;
         bfa (i) = rate_structure.log_base_price (i);
    end;
    call cu_$arg_ptr (arg_offset + 3, ap, al, ec);
    if ec ^= 0 then return;
    do i = 0 to 7;
         bfa (i) = rate_structure.io_ops_price (i);
    end;
    call cu_$arg_ptr (arg_offset + 4, ap, al, ec);
    if ec ^= 0 then return;
    do i = 0 to 7;
         bfa (i) = rate_structure.core_price (i);
    end;
    call cu_$arg_ptr (arg_offset + 5, ap, al, ec);
    if ec ^= 0 then return;
    bflo = rate_structure.disk_price;
    call cu_$arg_ptr (arg_offset + 6, ap, al, ec);
    if ec ^= 0 then return;
    bflo = rate_structure.registration_price;

    return;

/* -------------------------------------------------------- */

rates_rs:
prices_rs: entry (rs_number);

    call setup_rs (rs_number);
    arg_offset = 1;
    go to rates_join;

/* -------------------------------------------------------- */

device_rates:
device_prices: entry (ndev, devp);

dcl 1 dvt (16) aligned based,
    2 device_id char (8),
    2 device_price (0: 7) float bin;

    call setup_user_rs;
    arg_offset = 0;

device_rates_join:
    ndev = rate_structure.ndevices;
    call cu_$arg_ptr (arg_offset + 2, ap, al, ec);
    if ec ^= 0 then return;
    if devp ^= null then do i = 1 to rate_structure.ndevices;
         devp -> dvt.device_id (i) = rate_structure.devtab.device_id (i);
         do j = 0 to 7;
        devp -> dvt.device_price (i, j) = rate_structure.devtab.device_price (i, j);
         end;
    end;

    return;

/* -------------------------------------------------------- */

device_rates_rs:
device_prices_rs: entry (rs_number, ndev, devp);

    call setup_rs (rs_number);
    arg_offset = 1;
    go to device_rates_join;

/* -------------------------------------------------------- */

/* dcl system_info_$abs_limits ((4) fixed bin (35), fixed bin (35), (0:7,4) fixed bin (35));
   call system_info_$abs_limits (default_cpu_limits, default_foreground_cpu_limit, max_cpu_limits); */


abs_limits:
    entry;

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;

    based_fixed_array (*) = installation_parms.abs_cpu_default_limit (*);

    call cu_$arg_ptr (2, ap, al, ec);
    if ec ^= 0 then return;

    bfix = installation_parms.foreground_cpu_default_limit;

    call cu_$arg_ptr (3, ap, al, ec);
    if ec ^= 0 then return;

    based_shift_queue_array (*, *) = installation_parms.abs_cpu_max_limit (*, *);

    return;

/* -------------------------------------------------------- */

default_absentee_queue: entry;

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;

    bfix = installation_parms.default_absentee_queue;

    return;

/* -------------------------------------------------------- */

abs_prices: entry;

    call setup_user_rs;
    arg_offset = 0;

abs_prices_join:
    call cu_$arg_ptr (arg_offset + 1, ap, al, ec);
    if ec ^= 0 then return;
    do i = 1 to 4;
         bfa (i-1) = rate_structure.abs_cpu_price (i);
    end;
    call cu_$arg_ptr (arg_offset + 2, ap, al, ec);
    if ec ^= 0 then return;
    do i = 1 to 4;
         bfa (i-1) = rate_structure.abs_mem_price (i);
    end;

    return;

/* -------------------------------------------------------- */

abs_prices_rs: entry (rs_number);

    call setup_rs (rs_number);
    arg_offset = 1;
    go to abs_prices_join;

/* -------------------------------------------------------- */

io_prices: entry;

    call setup_user_rs;
    arg_offset = 0;

io_prices_join:
    call cu_$arg_ptr (arg_offset + 1, ap, al, ec);
    if ec ^= 0 then return;
    do i = 1 to 4;
         bfa (i-1) = rate_structure.iod_rec_price (i);
    end;

    return;

/* --------------------------------------------------------- */

io_prices_rs: entry (rs_number);

    call setup_rs (rs_number);
    arg_offset = 1;
    go to io_prices_join;

/* ------------------------------------------------------- */

abs_chn:    entry (ev, pid);

dcl  ev fixed bin (71), pid bit (36);

    if whoptr = null then call setup;

    call cu_$arg_count (nargs);

    ev = whotab.abs_event;
    if nargs > 1 then pid = whotab.abs_procid;

    return;

/* ------------------------------------------------------ */

next_shift_change: entry (curshft, shftime, newshft, starttime);

dcl (curshft, newshft) fixed bin, (shftime, starttime) fixed bin (71);

    call cu_$arg_count (nargs);

    call datebin_$next_shift_change ((clock ()), t71, t1, t2);
    curshft = t1;
    if nargs > 1 then shftime = t71;
    if nargs > 2 then newshft = t2;
    if nargs > 3 then do;
         if whoptr = null then call setup;
         starttime = whotab.last_shift_change_time;
    end;

    return;

/* -------------------------------------------------------- */

shift_table: entry (stt);

dcl  stt (336) fixed bin;

    if whoptr = null then call setup;

    do i = 1 to 336;
         stt (i) = fixed (installation_parms.shifttab (i), 3);
    end;

    return;

/* -------------------------------------------------------- */

request_chn:
    entry;

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;
    b36 = whotab.request_process_id;

    call cu_$arg_ptr (2, ap, al, ec);
    if ec ^= 0 then return;
    b71 = whotab.request_channel;

    call cu_$arg_ptr (3, ap, al, ec);
    if ec ^= 0 then return;
    bchr = sysdir;

    call cu_$arg_ptr (4, ap, al, ec);
    if ec ^= 0 then return;
    bchr = "as_request.ms";

    return;

/* -------------------------------------------------------- */

access_ceiling:
    entry (access_ceiling);

dcl  access_ceiling bit (72) aligned;

    if whoptr = null then call setup;

    access_ceiling = installation_parms.access_authorization_ceiling;
    return;

/* -------------------------------------------------------- */

log_threshold:
    entry;

dcl (state char (al), npages fixed bin) based (ap);

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;

    i = installation_parms.syserr_log_copy_threshold;

    if i < 0 then state = "off";
    else if i = 0 then state = "default";
    else state = "on";

    call cu_$arg_ptr (2, ap, al, ec);
    if ec = 0 then npages = max (i, 0);

    return;

/* -------------------------------------------------------- */

level_names:
    entry;

dcl (long_level_names char (32), short_level_names char (8)) dim (0:7) based (ap);

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;
    long_level_names (*) = installation_parms.level_names (*);

    call cu_$arg_ptr (2, ap, al, ec);
    if ec = 0 then short_level_names (*) = installation_parms.short_level_names (*);

    return;

/* -------------------------------------------------------- */

category_names:
    entry;

dcl (long_category_names char (32), short_category_names char (8)) dim (18) based (ap);

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;
    long_category_names (*) = installation_parms.category_names (*);

    call cu_$arg_ptr (2, ap, al, ec);
    if ec = 0 then short_category_names (*) = installation_parms.short_category_names (*);

    return;

/* -------------------------------------------------------- */

ARPANET_host_number:
    entry;

    if whoptr = null then call setup;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0 then return;
    bfix = installation_parms.ARPANET_host_number;

    return;

/* -------------------------------------------------------- */

resource_price: entry (a_price_name, a_price, a_code);

dcl  a_price_name char (*);
dcl  a_price float bin;
dcl  a_code fixed bin (35);

dcl  error_table_$noentry ext fixed bin (35);

    call setup_user_rs;

resource_price_join:
    do i = 1 to rate_structure.nrscp;
         if a_price_name = rate_structure.resource (i).name then do;
        a_price = rate_structure.resource (i).price;
        a_code = 0;
        return;
         end;
    end;
    a_code = error_table_$noentry;
    a_price = 0;
    return;

/* -------------------------------------------------------- */

resource_price_rs: entry (rs_number, a_price_name, a_price, a_code);

    call setup_rs (rs_number);
    go to resource_price_join;

/* -------------------------------------------------------- */

rs_name:    entry (rs_number, rs_name, a_code);

    if whoptr = null then call setup;

    if rs_number < 0 | rs_number > whotab.n_rate_structures then do;
         a_code = error_table_$noentry;
         rs_name = " INVALID_RS_" || ltrim (char (rs_number)); /* leading space so it won't match any name */
    end;
    else do;
         a_code = 0;
         rs_name = installation_parms.rate_structures (rs_number);
    end;
    return;

/* --------------------------------------------------------- */

rs_number: entry (rs_name, rs_number, a_code);

    if whoptr = null then call setup;

    do i = 0 to whotab.n_rate_structures;
         if installation_parms.rate_structures (i) = rs_name then do;
        rs_number = i;
        a_code = 0;
        return;
         end;
    end;

    a_code = error_table_$noentry;
    rs_number = 0;
    return;

/* --------------------------------------------------------- */

max_rs_number: entry (rs_count);

dcl  rs_count fixed bin;

    if whoptr = null then call setup;
    rs_count = whotab.n_rate_structures;
    return;

/* --------------------------------------------------------- */

setup:  proc;

dcl  ec fixed bin (35);

         call hcs_$initiate (sysdir, "whotab", "", 0, 1, whoptr, ec);
         if whoptr = null
         then call com_err_ (ec, "system_info_", "^a>whotab", sysdir);

         call hcs_$initiate (sysdir, "installation_parms", "", 0, 1, ip, ec);
         if ip = null
         then call com_err_ (ec, "system_info_", "^a>installation_parms", sysdir);

    end setup;

/* --------------------------------------------- */

setup_rs:   proc (rsnum);

dcl  ec fixed bin (35);
dcl  rsnum fixed bin;
dcl  rsn fixed bin;
dcl  en char (32);

         if whoptr = null then call setup;

         if rsnum < 0 | rsnum > whotab.n_rate_structures then do;
        call com_err_ (0, "system_info_", "Invalid rate_structure number ^d. Default rates will be used.", rsnum);
        rsn = 0;
         end;
         else rsn = rsnum;

         if rs_ptrs (rsn) = null then do;
        en = "rate_structure_" || ltrim (char (rsn));
        call hcs_$initiate (sysdir, en, "", 0, 0, rs_ptrs (rsn), ec);
        if rs_ptrs (rsn) = null then do;
             call com_err_ (ec, "system_info_", "^a>^a.  Default rates will be used.", sysdir, en);
             rsn = 0;           /* user loses if rsn already 0 */
        end;
         end;
         cur_rs_ptr = rs_ptrs (rsn);

    end setup_rs;

/* --------------------------------------------- */

setup_user_rs: proc;

dcl  ec fixed bin (35);
dcl  rsn fixed bin;

         if pp = null then call hcs_$initiate (get_pdir_ (), "pit", "", 0, 1, pp, ec);
         if pp = null then do;
        call com_err_ (ec, "system_info_", "pit");
        rsn = 0;
         end;
         else rsn = pp -> pit.rs_number;
         call setup_rs (rsn);

    end setup_user_rs;

/* --------------------------------------------- */

test_system_info: entry (xdirn);

dcl  xdirn char (*) parameter;
dcl  nsd char (168);

    call absolute_pathname_ (xdirn, nsd, ec);
    if ec ^= 0 then do;
         call com_err_ (ec, "system_info_$test_system_info", xdirn);
         return;
    end;
    sysdir = nsd;
    if whoptr ^= null then          /* need to cleanup? */
         call hcs_$terminate_noname (whoptr, ec);
    whoptr = null;              /* re-initiate whotab and installation_parms */
    do i = 0 to hbound (rs_ptrs, 1);        /* more tidying? */
         if rs_ptrs (i) ^= null then do;
        call hcs_$terminate_noname (rs_ptrs (i), ec);
        rs_ptrs (i) = null;
         end;
    end;

    return;

     end system_info_;
\014



            user_info_.pl1                  11/03/82  1450.9rew 11/03/82  1430.8      142425



/* ***********************************************************
   *                                                         *
   * Copyright, (C) Honeywell Information Systems Inc., 1982 *
   *                                                         *
   * Copyright (c) 1972 by Massachusetts Institute of        *
   * Technology and Honeywell Information Systems, Inc.      *
   *                                                         *
   *********************************************************** */


/* format: style2 */

user_info_:
     proc;

/* USER_INFO_ - procedure to return selected information from the PIT

   The information returned was put there by the procedure "cpg_"
   when the process was created.

   The following entries exist:

   .    user_info_$user_info_ (name, proj, acct)
   .    user_info_$whoami (name, proj, acct)
   .    user_info_$login_data (name, proj, acct, anon, stby, weight, time_login, login_word)
   .    user_info_$usage_data (n_processes, cputime_old_procs, time_login, time_proc_create,core_old_procs,io_old_procs)
   .    user_info_$homedir (home_directory)
   .    user_info_$responder (login_responder)
   .    user_info_$tty_data (terminal_id, terminal_type, channel_id, line_type)
   .    user_info_$terminal_data (terminal_id, terminal_type_name, channel_id, line_type, charge_type)
   .      user_info_$service_type (service_type)
   .      user_info_$process_type (process_type)
   .    user_info_$logout_data (logout_channel, logout_processid)
   .    user_info_$login_line (login_string)
   .    user_info_$absentee_queue (q)
   .    user_info_$absin (path)
   .    user_info_$absout (path)
   .    user_info_$outer_module (om)
   .    user_info_$load_ctl_info (group, sb, bumpclock, weight)
   .    user_info_$attributes (att)
   .    user_info_$limits (mlim, clim, cdate, crf, shlim, msp, csp, shsp)
   .    user_info_$rs_name (rs_name)
   .    user_info_$rs_number (rs_number)
   .    user_info_$absentee_request_id (request_id)

   If an entry which takes multiple arguments is called with too few arguments,
   only those supplied will be set.

   THVV 9/70
   Modified 761229 by D. M. Wells to add $service_type and $process_type entry
   points, to add line_type parameter to $tty_data, and to get info for
   $tty_data from PIT rather than the user_i/o IOSIM.
   Modified 6/20/77 by J. Stern to add $terminal_data (obsoletes $tty_data)
   Modified April 1979 by T. Casey to return correct information in foreground absentee jobs.
   Modified Feb 1980 by M. B. Armstrong to implement multiple rate structures. (UNCA)
   Modified June 1981 by E. N. Kittlitz for UNCA rate structures.
   Modified December 1981 by E. N. Kittlitz for login_arg_ptr, login_arg_count.
   Modified 11/81 by B. Margulies for sub_err_ vs. com_err_.
   Modified 10/82 by E. N. Kittlitz for absentee_request_id.
 */

    dcl     arg_infop        pointer;
    dcl     arg_offset       fixed bin;
    dcl     (pp, whoptr)     pointer,
            i            fixed bin,
            ii       fixed bin,
            hcs_$initiate    entry (char (*), char (*), char (*), fixed bin (1), fixed bin (2), ptr,
                 fixed bin (35)),
                  current_validation     fixed bin (3),
            hcs_$level_set         entry (fixed bin (3)),
                hcs_$level_get         entry (fixed bin (3)),
                get_ring_        entry() returns(fixed bin(3)),
            get_pdir_        entry returns (char (168)),
            format_attributes_   entry (ptr, char (*) var),
            ec       fixed bin (35),
            sub_err_         entry options (variable),
            sysdir       char (64) int static init (">system_control_dir") options (constant),
            system_info_$device_prices
                 entry (fixed bin, ptr),
            system_info_$rs_name     entry (fixed bin, char (*), fixed bin (35)),
            cu_$arg_count    entry (fixed bin, fixed bin (35)),
            cu_$arg_ptr      entry (fixed bin, ptr, fixed bin, fixed bin (35));

    dcl     error_table_$noarg   fixed bin (35) ext static;

    declare n_args       fixed bin;
    dcl     1 dvt        (16),
        2 device_id  char (8),
        2 device_price   (0:7) float bin;

    dcl     ndev         fixed bin;

    dcl     ap       ptr,
            al       fixed bin,
            bchr         char (al) based (ap) unaligned,
            bfix         fixed bin (35) based (ap),
            bf17         fixed bin based (ap),
            bf21         fixed bin (21) based (ap),
            bf71         fixed bin (71) based (ap),
            bflt         float bin based (ap),
            bb36         bit (36) based (ap),
            bptr         ptr based (ap),
            tvcs         char (512) var,
            bftary       (0:7) float bin based (ap);
    dcl     (addr, null, index, substr)
                 builtin;


%include pit;
%include user_attributes;

%include whotab;

%page;

fillpp:
     proc;                  /* internal proc to fill in PIT ptr on first call */

    declare whotab_$         bit (36) aligned external static;
    declare linkage_error    condition;

    on linkage_error                /* AS12.0 INSTALLATION KLUGE */
         begin;             /* to be removed after hardcore is installed */

              call hcs_$level_get (current_validation);
                  call hcs_$level_set (get_ring_ ());
        call hcs_$initiate ((get_pdir_ ()), "pit", "pit_", 0, 1, pp, ec);
        call hcs_$level_set (current_validation);
        if pp = null
        then call sub_err_ (ec, "user_info_", "s", null (), "pit");
         end;                   /* END OF KLUGE */

    pp = addr (pit_$);              /* we depend on this refname being in the environment */
    on linkage_error
         begin;

              call hcs_$level_get (current_validation);
                  call hcs_$level_set (get_ring_ ());
        call hcs_$initiate (sysdir, "whotab", "whotab_", 0, 1, whoptr, ec);
        call hcs_$level_set (current_validation);
        if whoptr = null
        then call sub_err_ (ec, "user_info_", "s", null (), "whotab");
         end;

    whoptr = addr (whotab_$);

     end fillpp;

%page;

whoami:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    bchr = pp -> pit.login_name;
    call cu_$arg_ptr (2, ap, al, ec);
    if ec ^= 0
    then return;
    bchr = pp -> pit.project;
    call cu_$arg_ptr (3, ap, al, ec);
    if ec ^= 0
    then return;
    bchr = pp -> pit.account;

    return;

login_data:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    bchr = pp -> pit.login_name;
    call cu_$arg_ptr (2, ap, al, ec);
    if ec ^= 0
    then return;
    bchr = pp -> pit.project;
    call cu_$arg_ptr (3, ap, al, ec);
    if ec ^= 0
    then return;
    bchr = pp -> pit.account;
    call cu_$arg_ptr (4, ap, al, ec);
    if ec ^= 0
    then return;
    bfix = pp -> pit.anonymous;
    call cu_$arg_ptr (5, ap, al, ec);
    if ec ^= 0
    then return;
    i = pp -> pit.whox;              /* use current data from whotab */
    if i = 0
    then bfix = pp -> pit.standby;       /* oof. unlisted users */
    else bfix = whotab.e (i).stby;      /* user may have been promoted since login */
    call cu_$arg_ptr (6, ap, al, ec);
    if ec ^= 0
    then return;
    bfix = pp -> pit.user_weight;
    call cu_$arg_ptr (7, ap, al, ec);
    if ec ^= 0
    then return;
    bf71 = pp -> pit.login_time;
    call cu_$arg_ptr (8, ap, al, ec);
    if ec ^= 0
    then return;
    if pp -> pit.anonymous = 1
    then bchr = "enter";
    else bchr = "login";

    return;

usage_data:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    bfix = pp -> pit.n_processes;
    call cu_$arg_ptr (2, ap, al, ec);
    if ec ^= 0
    then return;
    bf71 = pp -> pit.old_proc_cpu;
    call cu_$arg_ptr (3, ap, al, ec);
    if ec ^= 0
    then return;
    bf71 = pp -> pit.login_time;
    call cu_$arg_ptr (4, ap, al, ec);
    if ec ^= 0
    then return;
    bf71 = pp -> pit.proc_creation_time;
    call cu_$arg_ptr (5, ap, al, ec);
    if ec ^= 0
    then return;
    bf71 = pp -> pit.old_proc_core;
    call cu_$arg_ptr (6, ap, al, ec);
    if ec ^= 0
    then return;
    bf71 = pp -> pit.old_proc_io_ops;

    return;

homedir:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    bchr = pp -> pit.homedir;

    return;

responder:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    bchr = pp -> pit.login_responder;

    return;

tty_data:
     entry;

    call fillpp;

    if pp -> pit.process_type = 2
    then do;
        call cu_$arg_ptr (1, ap, al, ec);
        if ec ^= 0
        then return;
        bchr = "abs";
        call cu_$arg_ptr (2, ap, al, ec);
        if ec ^= 0
        then return;
        bfix = pp -> pit.tty_type;
        call cu_$arg_ptr (3, ap, al, ec);
        if ec ^= 0
        then return;
        bchr = pp -> pit.tty;
        call cu_$arg_ptr (4, ap, al, ec);
        if ec ^= 0
        then return;
        bfix = pp -> pit.line_type;
         end;

    else do;
        call cu_$arg_ptr (1, ap, al, ec);
        if ec ^= 0
        then return;
        bchr = pp -> pit.tty_answerback;
        call cu_$arg_ptr (2, ap, al, ec);
        if ec ^= 0
        then return;
        bfix = pp -> pit.tty_type;
        call cu_$arg_ptr (3, ap, al, ec);
        if ec ^= 0
        then return;
        bchr = pp -> pit.tty;
        call cu_$arg_ptr (4, ap, al, ec);
        if ec ^= 0
        then return;
        bfix = pp -> pit.line_type;
         end;

    return;

terminal_data:
     entry;

    call fillpp;

    if pp -> pit.process_type = 2
    then do;
        call cu_$arg_ptr (1, ap, al, ec);
        if ec ^= 0
        then return;
        bchr = "abs";
        call cu_$arg_ptr (2, ap, al, ec);
        if ec ^= 0
        then return;
        bchr = pp -> pit.term_type_name;
        call cu_$arg_ptr (3, ap, al, ec);
        if ec ^= 0
        then return;
        bchr = pp -> pit.tty;
        call cu_$arg_ptr (4, ap, al, ec);
        if ec ^= 0
        then return;
        bfix = pp -> pit.line_type;
        call cu_$arg_ptr (5, ap, al, ec);
        if ec ^= 0
        then return;
        if pp -> pit.charge_type = 0
        then bchr = "none";
        else do;
            call system_info_$device_prices (ndev, addr (dvt));
            bchr = dvt (pp -> pit.charge_type).device_id;
             end;
         end;

    else do;
        call cu_$arg_ptr (1, ap, al, ec);
        if ec ^= 0
        then return;
        bchr = pp -> pit.tty_answerback;
        call cu_$arg_ptr (2, ap, al, ec);
        if ec ^= 0
        then return;
        bchr = pp -> pit.term_type_name;
        call cu_$arg_ptr (3, ap, al, ec);
        if ec ^= 0
        then return;
        bchr = pp -> pit.tty;
        call cu_$arg_ptr (4, ap, al, ec);
        if ec ^= 0
        then return;
        bfix = pp -> pit.line_type;
        call cu_$arg_ptr (5, ap, al, ec);
        if ec ^= 0
        then return;
        if pp -> pit.charge_type = 0
        then bchr = "none";
        else do;
            call system_info_$device_prices (ndev, addr (dvt));
            bchr = dvt (pp -> pit.charge_type).device_id;
             end;
         end;

    return;

service_type:
     entry ();

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    bfix = pp -> pit.service_type;

    return;

process_type:
     entry ();

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    bfix = pp -> pit.process_type;

    return;

logout_data:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    bf71 = pp -> pit.logout_channel;
    call cu_$arg_ptr (2, ap, al, ec);
    if ec ^= 0
    then return;
    bb36 = pp -> pit.logout_pid;

    return;


login_line:
     entry;
    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    bchr = pp -> pit.login_line;

    return;

absentee_queue:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    if pp -> pit.process_type = 2
    then bfix = pp -> pit.abs_queue;
    else bfix = -1;

    return;

load_ctl_info:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    bchr = pp -> pit.group;
    call cu_$arg_ptr (2, ap, al, ec);
    if ec ^= 0
    then return;

    i = pp -> pit.whox;              /* use current data from whotab */
    if i = 0
    then do;                    /* unlisted user? */
        bfix = pp -> pit.standby;
        call cu_$arg_ptr (3, ap, al, ec);
        if ec ^= 0
        then return;
        bf71 = pp -> pit.cant_bump_until;
         end;
    else do;
        bfix = whotab.e (i).stby;
        call cu_$arg_ptr (3, ap, al, ec);
        if ec ^= 0
        then return;
        bf71 = whotab.e (i).cant_bump_until;
         end;
    call cu_$arg_ptr (4, ap, al, ec);
    if ec ^= 0
    then return;
    bfix = pp -> pit.user_weight;
    return;

attributes:
     entry (atts);

    dcl     atts         char (*) var;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    call format_attributes_ (addr (pp -> pit.at), tvcs);
    atts = tvcs;

    return;

absin:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;

    if pp -> pit.process_type ^= 2
    then bchr = "";
    else bchr = pp -> pit.input_seg;

    return;

absout:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;

    if pp -> pit.process_type ^= 2
    then bchr = "";
    else do;
        if pp -> pit.output_seg = ""
        then do;
            bchr = before (pp -> pit.input_seg, ".absin") || ".absout";
             end;
        else bchr = pp -> pit.output_seg;
         end;

    return;

outer_module:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    bchr = pp -> pit.outer_module;

    return;

limits:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    bflt = pp -> pit.dollar_limit;
    call cu_$arg_ptr (2, ap, al, ec);
    if ec ^= 0
    then return;
    bflt = pp -> pit.absolute_limit;
    call cu_$arg_ptr (3, ap, al, ec);
    if ec ^= 0
    then return;
    bf71 = pp -> pit.absolute_cutoff;
    call cu_$arg_ptr (4, ap, al, ec);
    if ec ^= 0
    then return;
    bfix = pp -> pit.absolute_increm;
    call cu_$arg_ptr (5, ap, al, ec);
    if ec ^= 0
    then return;
    do i = 0 to 7;
         bftary (i) = pp -> pit.shift_limit (i);
    end;
    call cu_$arg_ptr (6, ap, al, ec);
    if ec ^= 0
    then return;
    bflt = pp -> pit.dollar_charge;
    call cu_$arg_ptr (7, ap, al, ec);
    if ec ^= 0
    then return;
    bflt = pp -> pit.absolute_spent;
    call cu_$arg_ptr (8, ap, al, ec);
    if ec ^= 0
    then return;
    do i = 0 to 7;
         bftary (i) = pp -> pit.interactive (i).charge;
    end;
    return;

rs_name:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    call system_info_$rs_name ((pp -> pit.rs_number), bchr, ec);
    if ec ^= 0
    then call sub_err_ (ec, "user_info_", "s", null (), (0), "Rate structure ^d invalid.
Contact your system administrator.", pp -> pit.rs_number);
    return;

rs_number:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    bf17 = pp -> pit.rs_number;
    return;

login_arg_count:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    if pp -> pit.arg_info_ptr = 0
    then /* no login arguments */
         bf17 = 0;
    else bf17 = ptr (pp, pp -> pit.arg_info_ptr) -> arg_info.arg_count;

    call cu_$arg_ptr (2, ap, al, ec);
    if ec ^= 0
    then return;

    bf21 = 0;
    if pp -> pit.arg_info_ptr = 0
    then return;
    else do ii = 1 to ptr (pp, pp -> pit.arg_info_ptr) -> arg_info.arg_count;
        bf21 = max (ptr (pp, pp -> pit.arg_info_ptr) -> arg_info.arg_lengths (ii), bf21);
         end;

    call cu_$arg_ptr (3, ap, al, ec);
    if ec ^= 0
    then return;

    if pp -> pit.arg_info_ptr = 0
    then bf21 = 0;
    else bf21 = ptr (pp, pp -> pit.arg_info_ptr) -> arg_info.ln_args;
    return;


login_arg_ptr:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec ^= 0
    then return;
    i = bf17;                   /* get argument number */
    call cu_$arg_ptr (2, ap, al, ec);       /* argument pointer */
    if ec ^= 0
    then return;

    if pp -> pit.arg_info_ptr = 0
    then do;
        arg_infop = null;
        i = -1;
         end;
    else arg_infop = ptr (pp, pp -> pit.arg_info_ptr);
    if i < 1
    then bptr = null;
    else if i > arg_infop -> arg_info.arg_count
    then do;
        bptr = null;
        i = -1;
         end;
    else do;
        arg_offset = 1;
        do ii = 1 to i - 1;
             arg_offset = arg_offset + arg_infop -> arg_info.arg_lengths (ii);
        end;
        bptr = addr (substr (arg_infop -> arg_info.args, arg_offset, 1));
                        /* illegal pl1 */
         end;
    call cu_$arg_ptr (3, ap, al, ec);       /* argument length */
    if ec ^= 0
    then return;
    if i < 0
    then bf21 = 0;
    else bf21 = arg_infop -> arg_info.arg_lengths (i);
    call cu_$arg_ptr (4, ap, al, ec);       /* return code */
    if ec ^= 0
    then return;
    if i < 0
    then bfix = error_table_$noarg;
    else bfix = 0;
    return;


absentee_request_id:
     entry;

    call fillpp;

    call cu_$arg_ptr (1, ap, al, ec);
    if ec = 0
    then bf71 = pp -> pit.request_id;
    return;

     end user_info_;
\014



            where.pl1                       10/12/82  1155.2rew 10/12/82  1155.0      148779



/* ***********************************************************
   *                                                         *
   * Copyright, (C) Honeywell Information Systems Inc., 1982 *
   *                                                         *
   * Copyright (c) 1972 by Massachusetts Institute of        *
   * Technology and Honeywell Information Systems, Inc.      *
   *                                                         *
   *********************************************************** */


where: wh: procedure options (variable);

/* This command prints the primary pathname of the first segment or
   entry point with a given name found using the object segment search rules.

   Usage:
   where names -control_args-

   where control_args are:

   -all, -a     list all segments or entry points in the search path.
   -inhibit_error, -ihe supress error message when segment not found and returns null string as AF.
   -entry_point, -ep    look for name$name when name does not contain a $.
   -segment, -sm        look for the segment named name even if name contains a $.

   The default is to look for an entry point if name contains a $, segment otherwise.

   Usage as an active function:

   [where name -control_arg-]

   where control_arg is either -entry_point (-ep) or -segment (-sm).
*/
/* Written 3/5/76 by Steve Herbst */
/* Entry point feature added 12/3/76 by S. Herbst */
/* fixed to show orig not copy if uninitiated seg has copysw on 03/20/80 S. Herbst */
/* Modified: 06/06/80, W. Olin Sibert, to add where -brief */
/* Fixed to work on gates 07/15/81 S. Herbst */
/* Fixed bugs and made -brief -all work 10/06/82 S. Herbst */


%include access_mode_values;

%include branch_status;

%include object_info;
dcl 1 obj_info like object_info;

dcl  refnames (32) char (168);

dcl 1 search_rules aligned,             /* from hcs_$get_search_rules */
    2 rule_count fixed bin,
    2 rule (21) char (168);

dcl 1 search_dirs (21),             /* directories to search through */
    2 dir char (168),
    2 uid bit (36),
    2 rule_number fixed bin;

dcl  area area based (area_ptr);

dcl  arg char (arg_len) based (arg_ptr);
dcl  return_arg char (return_len) varying based (return_ptr); /* if called as active function */
dcl  primary_name char (32) aligned based;
dcl (dn, entry_point_name, name) char (168);
dcl (en, unique_name) char (32);
dcl  out_str char (256);

dcl (af_sw, all_sw, brief_sw, long_sw, all_entry_points, all_segments, entry_point, inhibit_error, printed_sw,
     search_manually, some_output, some_segs, terminate, try_initiated_segs) bit (1) aligned;

dcl (area_ptr, arg_ptr, entry_point_ptr, names_ptr, return_ptr, seg_ptr) ptr;

dcl  fmode fixed bin (5);
dcl (arg_count, arg_len, dir_count, refname_count, return_len) fixed bin;
dcl (argno, dir_idx, idx, refname_idx, uid_idx) fixed bin;
dcl  bit_count fixed bin (24);
dcl  code fixed bin (35);

dcl (error_table_$badopt,
     error_table_$inconsistent,
     error_table_$dirseg,
     error_table_$no_dir,
     error_table_$no_s_permission,
     error_table_$noentry,
     error_table_$entlong,
     error_table_$not_act_fnc) fixed bin (35) external static;

dcl  complain entry variable options (variable);        /* com_err_ or active_fnc_err_ */
dcl  get_arg variable entry (fixed bin, ptr, fixed bin, fixed bin (35));

dcl  active_fnc_err_ entry options (variable);
dcl  com_err_ entry options (variable);
dcl  cu_$af_arg_ptr entry (fixed bin, ptr, fixed bin, fixed bin (35));
dcl  cu_$af_return_arg entry (fixed bin, ptr, fixed bin, fixed bin (35));
dcl  cu_$arg_ptr entry (fixed bin, ptr, fixed bin, fixed bin (35));
dcl  get_definition_ entry (ptr, char (*), char (*), ptr, fixed bin (35));
dcl  get_system_free_area_ entry returns (ptr);
dcl  get_wdir_ entry returns (char (168));
dcl  hcs_$fs_get_mode entry (ptr, fixed bin (5), fixed bin (35));
dcl  hcs_$fs_get_path_name entry (ptr, char (*), fixed bin, char (*), fixed bin (35));
dcl  hcs_$get_search_rules entry (ptr);
dcl  hcs_$fs_get_seg_ptr entry (char (*), ptr, fixed bin (35));
dcl  hcs_$initiate entry (char (*), char (*), char (*), fixed bin (1), fixed bin (2), ptr, fixed bin (35));
dcl  hcs_$make_ptr entry (ptr, char (*), char (*), ptr, fixed bin (35));
dcl  hcs_$status_ entry (char (*), char (*), fixed bin (1), ptr, ptr, fixed bin (35));
dcl  hcs_$status_long entry (char (*), char (*), fixed bin (1), ptr, ptr, fixed bin (35));
dcl  hcs_$status_minf entry (char (*), char (*), fixed bin (1), fixed bin (1), fixed bin (24), fixed bin (35));
dcl  hcs_$terminate_name entry (char (*), fixed bin (35));
dcl  hcs_$terminate_noname entry (ptr, fixed bin (35));
dcl  ioa_ entry options (variable);
dcl  ioa_$rsnnl entry options (variable);
dcl  ioa_$nnl entry options (variable);
dcl  object_info_$brief entry (ptr, fixed bin (24), ptr, fixed bin (35));
dcl  pathname_ entry (char (*), char (*)) returns (char (168));
dcl  unique_chars_ entry (bit (*)) returns (char (15));

dcl  WHOAMI char (32) internal static options (constant) init ("where");

dcl  cleanup condition;

dcl (addr, bit, char, hbound, index, length, null, ptr, rtrim, substr) builtin;

/* \014 */

    all_sw, all_entry_points, all_segments, brief_sw = "0"b;
    inhibit_error, long_sw, search_manually, some_output = "0"b;
    names_ptr = null ();
    area_ptr = get_system_free_area_ ();

    call cu_$af_return_arg (arg_count, return_ptr, return_len, code);
    if code = error_table_$not_act_fnc then do;
         af_sw = "0"b;
         complain = com_err_;
         get_arg = cu_$arg_ptr;
    end;
    else do;
         af_sw = "1"b;
         complain = active_fnc_err_;
         get_arg = cu_$af_arg_ptr;
         return_arg = "";
    end;

    if arg_count = 0 then do;
USAGE:       if af_sw then call active_fnc_err_ (0, WHOAMI, "Usage:  [^a refname {-control_args}]", WHOAMI);
         else call com_err_ (0, WHOAMI, "Usage:  ^a refnames {-control_args}", WHOAMI);
         goto MAIN_RETURN;
    end;

    on condition (cleanup) call clean_up;

    refname_count = 0;

    do argno = 1 to arg_count;
         call get_arg (argno, arg_ptr, arg_len, code);

         if char (arg, 1) ^= "-" then do;       /* a refname we should locate */
        refname_count = refname_count+1;
        if refname_count > hbound (refnames, 1) then do;
             call complain (0, WHOAMI, "Too many reference names specified. Max is ^d.",
            hbound (refnames, 1));
             goto MAIN_RETURN;
        end;

        refnames (refname_count) = arg;
         end;

         else if (arg = "-all" | arg = "-a") then
        if af_sw then do;
AF_BAD_OPT:
             call complain (0, WHOAMI, "Control arg not allowed for the active function.  ^a", arg);
             return;
        end;
        else all_sw = "1"b;

         else if (arg = "-long" | arg = "-lg") then
        if af_sw then go to AF_BAD_OPT;
        else do;
             long_sw = "1"b;
             brief_sw = "0"b;
        end;

         else if (arg = "-brief" | arg = "-bf") then
        if af_sw then go to AF_BAD_OPT;
        else do;
             brief_sw = "1"b;
             long_sw = "0"b;
        end;

         else if (arg = "-entry_point") | (arg = "-ep") then all_entry_points = "1"b;
         else if (arg = "-segment") | (arg = "-sm") then all_segments = "1"b;

         else if (arg = "-inhibit_error") | (arg = "-ihe") then inhibit_error = "1"b;
         else if (arg = "-no_inhibit_error") | (arg = "-nihe") then inhibit_error = "0"b;
         else do;
        call complain (error_table_$badopt, WHOAMI, "^a", arg);
        goto MAIN_RETURN;
         end;
    end;

    if refname_count = 0 | (af_sw & refname_count > 1) then
         goto USAGE;

    if all_entry_points & all_segments then do;
         call complain (error_table_$inconsistent, WHOAMI, "-segment and -entry_point");
         goto MAIN_RETURN;
    end;

    if all_sw & ^brief_sw then long_sw = "1"b;

    if long_sw | all_sw then do;            /* must get search rules, to locate manually */

         search_manually = "1"b;

         call hcs_$get_search_rules (addr (search_rules));
         dir_count = 0;
         try_initiated_segs = "0"b;

         do idx = 1 to rule_count;          /* find all the genuine directories */
        if rule (idx) = "initiated_segments" then try_initiated_segs = "1"b;
        else if rule (idx) ^= "referencing_dir" then do;
             dir_count = dir_count + 1;
             if rule (idx) = "working_dir" then dir (dir_count) = get_wdir_ ();
             else dir (dir_count) = rule (idx);
             rule_number (dir_count) = idx;
             end;
         end;
    end;

/* \014 */

    do refname_idx = 1 to refname_count;        /* Now, decide what to do with each of out reference name */

         name = refnames (refname_idx);

         idx = index (name, "$");
         if (idx ^= 0) & ^all_segments then do; /* name$entry */
        entry_point_name = substr (name, idx + 1);
        name = substr (name, 1, idx - 1);
        if entry_point_name = "" then entry_point = "0"b;
        else entry_point = "1"b;
         end;

         else if all_entry_points then do;      /* -entry_point specified */
        entry_point = "1"b;
        entry_point_name = name;
         end;

         else do;
        entry_point = "0"b;         /* reference name */
        entry_point_name = "";
         end;

         if length (rtrim (name)) > 32 then do;
        call complain (error_table_$entlong, WHOAMI, "^a", name);
        goto NEXT;
         end;

         else if length (rtrim (entry_point_name)) > 32 then do;
        call complain (error_table_$entlong, WHOAMI, "^a", entry_point_name);
        goto NEXT;
         end;

/* \014 */

         if ^search_manually then do;       /* locate by the usual (linker) mechanism */

        terminate = "0"b;
        call hcs_$fs_get_seg_ptr (name, seg_ptr, code); /* already initiated? */
        if seg_ptr = null then do;
             terminate = "1"b;
             call hcs_$make_ptr (null (), name, "", seg_ptr, code);
             if code ^= 0 then do;
            if ^inhibit_error then call complain (code, WHOAMI, "^a", name);
            goto NEXT;
             end;
        end;

        call hcs_$fs_get_path_name (seg_ptr, dn, (0), en, code);
        if code ^= 0 then do;
             call complain (code, WHOAMI, "^a", name);
             goto NEXT;
        end;

        if entry_point then do;

             call find_entry_point;

             if terminate then call hcs_$terminate_name (name, (0));
             if code ^= 0 then do;
            call complain (code, WHOAMI, "^a$^a", pathname_ (dn, en), entry_point_name);
            goto NEXT;
             end;
        end;

        else if terminate then call hcs_$terminate_name (name, code);

        call ioa_$rsnnl ("^a^[$^a^;^s^]", out_str, (0),
             pathname_ (dn, en), entry_point, entry_point_name);

        if af_sw then do;           /* just assign it to the return arg, and punt */
             return_arg = out_str;
             goto MAIN_RETURN;
        end;

        else call ioa_ ("^a", out_str);
         end;                   /* of searching non-manually (via hcs_$make_ptr) */

/* \014 */

         else do;               /* we must search for the segment manually */
                        /* Note that this is never done for an AF. */
        if (idx > 1) & some_output & all_sw then /* separate -all outputs by a blank line */
             call ioa_ ("");

        some_output, some_segs = "0"b;

        if try_initiated_segs then do;  /* search rules contained "initiated_segments" */
             dir_idx = 0;
             call hcs_$fs_get_seg_ptr (name, seg_ptr, code);
             if seg_ptr ^= null () then do;
            some_segs = "1"b;
            call hcs_$fs_get_path_name (seg_ptr, dn, (168), en, code);
            if code ^= 0 then call complain (code, WHOAMI, "^a", name);

            else do;
                 if entry_point then call find_entry_point ();

                 if code ^= 0 then call complain (code, WHOAMI,
                "^a$^a  Search rule ""initiated_segments""",
                pathname_ (dn, en), entry_point_name);

                 else call print_pathname ();

                 if ^all_sw then go to NEXT;
            end;
             end;               /* of successfully finding segment by refname */
        end;                /* of trying "initiated_segments" */

        do dir_idx = 1 to dir_count;        /* try to initiate in each of the dirs in the search rules */

             call hcs_$initiate (dir (dir_idx), name, "", 0, 1, seg_ptr, code);
             if seg_ptr ^= null then do;
            some_segs = "1"b;
            call hcs_$fs_get_path_name (seg_ptr, dn, (0), en, code);
            if code ^= 0 then do;
                 code = 0;
                 dn = dir (dir_idx);
                 en = name;
            end;

            if entry_point then call find_entry_point ();
            if code ^= 0 then call complain (code, WHOAMI, "^a$^a (Search rule ""^a"")",
                 pathname_ (dn, en), entry_point_name, rule (rule_number (dir_idx)));

            else call print_pathname ();

            call hcs_$terminate_noname (seg_ptr, (0));
            if ^all_sw then go to NEXT;
             end;               /* end of case for being able to initiate segment */

             else if code ^= error_table_$noentry & code ^= error_table_$no_dir & 
               code ^= error_table_$dirseg then do;

            some_output, some_segs = "1"b;

            if entry_point then call complain (code, WHOAMI, "^a (Search rule ""^a"")",
                 pathname_ (dir (dir_idx), name), rule (rule_number (dir_idx)));

            else do;
                 if brief_sw & all_sw then do;
                call hcs_$status_long (dir (dir_idx), name, 1, addr (branch_status), null, code);
                uid (dir_idx) = branch_status.unique_id;
                printed_sw = "0"b;
                do uid_idx = 1 to dir_idx - 1;    /* print each path only once */
                     if uid (uid_idx) = branch_status.unique_id then printed_sw = "1"b;
                end;
                if ^printed_sw then call ioa_ ("^a", pathname_ (dir (dir_idx), name));
                 end;
                 else do;
                call hcs_$status_ (dir (dir_idx), name, 1, addr (branch_status), area_ptr, code);
                if code = 0 then do;    /* print formatted line */
                     names_ptr = ptr (area_ptr, branch_status.names_rel_pointer);
                     if long_sw then call ioa_ ("^a (^a) Search rule ""^a""",
                    pathname_ (dir (dir_idx), (names_ptr -> primary_name)),
                    get_mode_letters (branch_status.mode), rule (rule_number (dir_idx)));
                     else call ioa_ ("^a", pathname_ (dir (dir_idx), name));

                     if ^all_sw then go to NEXT;
                end;

                else if code = error_table_$no_s_permission then
                     call complain (0, WHOAMI,
                    "No status permission on ^a (Search rule ""^a"")",
                    dir (dir_idx), rule (rule_number (dir_idx)));

                else if code ^= error_table_$noentry then
                     call complain (code, WHOAMI, "^a (Search rule ""^a"")",
                    dir (dir_idx), rule (rule_number (dir_idx)));

                else some_output = "0"b;
                 end;
            end;
             end;               /* of case for unable to initiate segment */
        end;                /* of loop through dirs in search rules */

        if ^some_output & ^inhibit_error then
             if entry_point & some_segs then call complain (0, WHOAMI, "Entry point not found.  ^a$^a",
            name, entry_point_name);
             else call complain (0, WHOAMI, "Segment not found. ^a", name);

         end;                   /* of case for searching manually */
NEXT:   end;                    /* end of refname loop */

MAIN_RETURN:
    call clean_up;
    return;

/* \014 */


clean_up:   proc;

    if names_ptr ^= null then free names_ptr -> primary_name in (area);

end clean_up;


find_entry_point: proc;

/* This internal procedure looks for an external definition. */

    call hcs_$fs_get_mode (seg_ptr, fmode, code);
    if fmode < R_ACCESS_BIN then do;     /* inner ring seg: gate? */
                        /* make sure make_ptr finds this one */
         unique_name = unique_chars_ ("0"b);
         call hcs_$initiate (dn, en, unique_name, 0, 1, seg_ptr, code);
         call hcs_$make_ptr (null, unique_name, entry_point_name, entry_point_ptr, code);
         call hcs_$terminate_name (unique_name, 0);
    end;
    else do;
         call hcs_$status_minf (dn, en, 1, (0), bit_count, code);
         call object_info_$brief (seg_ptr, bit_count, addr (obj_info), code);
         if code ^= 0 then return;
         call get_definition_ (obj_info.defp, name, entry_point_name, null, code);
    end;

end find_entry_point;


print_pathname: proc;

    some_output = "1"b;
    call hcs_$fs_get_mode (seg_ptr, fmode, code);
    if code ^= 0 then fmode = 0;

    if long_sw then do;
         call ioa_$nnl ("^a^[$^a^;^s^] (^a) Search rule ",
        pathname_ (dn, en), entry_point, entry_point_name, get_mode_letters (bit (fmode)));
         if dir_idx = 0 then call ioa_ ("""initiated_segments""");
         else call ioa_ ("""^a""", rule (rule_number (dir_idx)));
    end;
    else do;
         call hcs_$status_long (dn, en, 1, addr (branch_status), null, code);
         uid (dir_idx) = branch_status.unique_id;
         printed_sw = "0"b;
         do uid_idx = 1 to dir_idx-1;       /* only print each path once */
        if uid (uid_idx) = branch_status.unique_id then printed_sw = "1"b;
         end;
         if ^printed_sw then call ioa_ ("^a", pathname_ (dn, en));
    end;

end print_pathname;


get_mode_letters: proc (mode_bits) returns (char (4)varying);

dcl  mode_bits bit (5);
dcl  amode char (4) varying;

    amode = "";
    if substr (mode_bits, 2, 1) ^= "0"b then amode = "r";
    if substr (mode_bits, 3, 1) ^= "0"b then amode = amode||"e";
    if substr (mode_bits, 4, 1) ^= "0"b then amode = amode||"w";
    if amode = "" then amode = "null";
    return (amode);

end get_mode_letters;

end where;
\014



            who.pl1                         11/09/82  1212.0rew 11/09/82  1211.8      144135



/* ***********************************************************
   *                                                         *
   * Copyright, (C) Honeywell Information Systems Inc., 1982 *
   *                                                         *
   * Copyright (c) 1972 by Massachusetts Institute of        *
   * Technology and Honeywell Information Systems, Inc.      *
   *                                                         *
   *********************************************************** */


/* format: style4 */
who: procedure;

/* WHO - print information about who's on Multics.

   HMU, HOW_MANY_USERS - give header lines with nusers and load.

   This command types out the userid's of listed logged-in users
   from the segment "whotab", which is maintained by the answering service
   program "lg_ctl_". The possible arguments are as follows:

   .      -bf       suppress header  (not allowed for af)
   .    -lg print "long who"  (not allowed for af)
   .    -nm sort lines on user name
   .    -pj sort lines on project id
   .        (the default sort is by time logged in)
   .    -as print information on absentee users
   .    -ia print information on interactive users
   .    -dmn    print information on daemon users
   .        (default is -as -ia if none of -as -ia -dmn given)
   .      -all      -as, -ia -dmn
   .    Name    list only users with person name "Name"
   .    .Proj   list only users with project name "Proj"
   .    Name.Proj   list only users with person name "Name" and project "Proj"

   Initial coding by THVV, 9/6/70 */
/* changed for absentee by EDS 7/71 */
/* various changes by RBR 7/72      */
/* error messages changed 09/15/78 S. Herbst */
/* Modified May 1979 by T. Casey and S. Herbst for MR7.0a to add -interactive and -daemon,
   and to list foreground absentee users correctly */
/* who active function added 01/12/81 S. Herbst */
/* 12/24/81 E. N. Kittlitz.  whotab changes */
/* 9/82 BIM -all, no daemons by default */
/* 11/82 E. N. Kittlitz. list daemons if name explicitly given, do selection for af call */

dcl  return_arg char (return_len) varying based (return_ptr);
dcl  return_ptr ptr;
dcl  return_len fixed bin;
dcl  af_sw bit (1);


dcl  argno fixed bin init (1),          /* number of argument */
     arg_count fixed bin,
     ap ptr,                    /* ptr to argument */
     al fixed bin,                  /* lth of argument */
     ec fixed bin (35),             /* file-system error code */
     (f1, f2) float bin,                /* conversion temps */
     sort fixed bin init (0),               /* type of sort. 0=date, 1=name, 2=proj */
     hmucnt fixed bin init (0),         /* number of names|projects in */
     abscnt fixed bin init (0),         /* hmucnt as absentee users */
     long bit (1) aligned init ("0"b),          /* 1 if long who wanted */
     abs bit (1) aligned init ("0"b),           /* 1 if listing absentee users */
     only_abs bit (1) aligned init ("0"b),      /* if only listing absentees */
     daemon bit (1) aligned init ("0"b),        /* if listing daemon users */
     interactive bit (1) aligned init ("0"b),       /* if listing interactive users */
     brief bit (1) aligned init ("0"b),         /* 1 for no heading at all */
     hmuflg bit (1) aligned init ("0"b),        /* selective hmu flag */
     selx fixed bin init (0),               /* if particular users wanted */
     dotl fixed bin,                /* location of dot in arg */
     nm (50) char (24) aligned,         /* user names wanted */
     pj (50) char (12) aligned,         /* user projs wanted */
     caller char (14) varying,          /* name of caller to com_err */
     why char (128) aligned,                /* reason for shutdown */
     arg char (al) unaligned based (ap),        /* pickup for args */
     sort_arg char (32) init (""),
     whoptr ptr int static init (null),         /* ptr to whotab */
     ip ptr int static init (null),         /* ptr to installation_parms */
     sysdir char (64) aligned int static init (">system_control_1"), /* name of dir in which who table resides */
     j fixed bin,                   /* index */
     d fixed bin,                   /* distance between sorted elems */
     last fixed bin,                /* highest index in whotab */
     swap fixed bin,                /* 1 if a swap was done */
     ajd fixed bin,                 /* temp for sort, ary(j+d) */
     sss char (1) aligned init ("s"),           /* pretty for user-not-on */
     (time, time1) char (16) aligned init (""),     /* ASCII time */
     aj fixed bin,                  /* temp, ary(j) */
     did fixed bin init (0),                /* count of lines printed */
     mark char (3) aligned,             /* denotation of absentee user if = "*" */
     k fixed bin;                   /* index */

%include whotab;

%include installation_parms;

dcl  complain entry variable options (variable);

dcl  ioa_ ext entry options (variable),         /* library procedures */
     active_fnc_err_ entry options (variable),
     com_err_ ext entry options (variable),
     date_time_ ext entry (fixed bin (71), char (*) aligned),
     cu_$af_return_arg entry (fixed bin, ptr, fixed bin, fixed bin (35)),
     cu_$arg_ptr ext entry (fixed bin, ptr, fixed bin, fixed bin (35)),
     hcs_$initiate ext entry (char (*) aligned, char (*), char (*), fixed bin (1), fixed bin (2), ptr, fixed bin (35)),
     requote_string_ entry (char (*)) returns (char (*));

dcl  (divide, fixed, hbound, index, null, substr) builtin;
dcl  (error_table_$badopt,
     error_table_$not_act_fnc,
     error_table_$too_many_args) ext fixed bin;

/* - - - - - - - - - - */

    caller = "who";             /* set name of caller to com_err_ */
    go to join;

how_many_users: hmu: entry;

    caller = "how_many_users";
    hmuflg = "1"b;

join:
    call cu_$af_return_arg (arg_count, return_ptr, return_len, ec);
    if ec = error_table_$not_act_fnc then do;
         af_sw = "0"b;
         complain = com_err_;
    end;
    else if caller = "how_many_users" then do;
         call active_fnc_err_ (0, caller, "Cannot be called as an active function.");
         return;
    end;
    else do;
         af_sw = "1"b;
         complain = active_fnc_err_;
    end;

    do argno = 1 to arg_count;
         call cu_$arg_ptr (argno, ap, al, ec);  /* get nth argument */

         if arg = "-absentee" | arg = "-as" then abs = "1"b;
         else if arg = "-daemon" | arg = "-dmn" then daemon = "1"b;
         else if arg = "-interactive" | arg = "-ia" then interactive = "1"b;
         else if arg = "-all" | arg= "-a" then interactive, daemon, abs = "1"b;
         else if arg = "-name" | arg = "-nm" then sort = 1;
         else if arg = "-project" | arg = "-pj" then sort = 2;

         else if arg = "-brief" | arg = "-bf" then
        if af_sw then do;
BAD_AF_OPT:      call active_fnc_err_ (0, caller, "Invalid active function control arg ^a", arg);
             return;
        end;
        else brief = "1"b;
         else if arg = "-long" | arg = "-lg" then
        if af_sw then go to BAD_AF_OPT;
        else long = "1"b;

         else if substr (arg, 1, 1) = "-" then do;  /* then it must be a name or project */
bad_opt:        call complain (error_table_$badopt, caller, "^a", arg);
        return;
         end;
         else do;               /* save Name | .Project */
        selx = selx + 1;            /* up index in select array */
        if selx > hbound (nm, 1) then do;
             call complain (error_table_$too_many_args, caller);
             return;
        end;
        nm (selx), pj (selx) = "";      /* blank selectors */
        dotl = index (arg, ".");        /* where's the dot? */
        if dotl = 0 then nm (selx) = arg;   /* no dot. is user name. */
        else if dotl = 1 then pj (selx) = substr (arg, 2, al - 1);
        else do;                /* dot in middle, is name.proj */
             nm (selx) = substr (arg, 1, dotl - 1); /* get name */
             pj (selx) = substr (arg, dotl + 1, al - dotl);
        end;
         end;
         if sort ^= 0 & hmuflg then go to bad_opt;
    end;

    if ^interactive & ^abs & ^daemon then do;   /* if process type not specified */
         interactive, abs = "1"b;           /* default is to list abs and ia */
         if selx > 0 then daemon = "1"b;     /* but if name/proj given, list everything */
    end;
    only_abs = abs & ^interactive & ^daemon;    /* see if abs only */

go: if whoptr = null then do;           /* is this the first call? */
         call hcs_$initiate (sysdir, "whotab", "", 0, 1, whoptr, ec);
         if whoptr = null then do;
        call complain (ec, caller, "^a>whotab", sysdir);
        return;
         end;
    end;

    if ^brief & ^af_sw then do;         /* suppress header */
         if hmuflg then go to head;     /* always a header for hmu, except after brief */
         if selx = 0 then do;           /* no header with who select */
        if only_abs         /* what type absentee header if any */
        then if long
             then go to print_long_abs_totals;
             else go to print_abs_totals;
head:       f1 = whotab.n_units / 10.0e0;       /* format up units */
        f2 = whotab.mxunits / 10.0e0;       /* ... */
        j = whotab.n_users - whotab.abs_users - whotab.fg_abs_users - whotab.n_daemons; /* compute interactive users */

        if long then do;            /* long who? */
             if ip = null then do;
            call hcs_$initiate (sysdir, "installation_parms", "", 0, 1, ip, ec);
            if ip = null then do;
                 call complain (ec, caller, "Insufficient access for -long option");
                 return;
            end;
             end;
             call date_time_ (whotab.timeup, time); /* yup. make heading */
             call ioa_ ("^/Multics ^a; ^a", whotab.sysid, installation_parms.installation_id);
             call ioa_ ("Load = ^.1f out of ^.1f units; users = ^d, ^d interactive, ^d daemons.",
            f1, f2, whotab.n_users, j, whotab.n_daemons);
             if (whotab.abs_users + whotab.max_abs_users) ^= 0
             then               /* ! */
print_long_abs_totals:  call ioa_ ("^[^/^]Absentee users = ^d background^[, ^d foreground^;^s^]; Max background absentee users = ^d^[^/^]",
                 only_abs, whotab.abs_users, (whotab.fg_abs_users > 0), whotab.fg_abs_users, whotab.max_abs_users, only_abs);
             if only_abs then go to check_hmu;
             call ioa_ ("System up since ^a", time);
             if whotab.nextsd ^= 0 then do;
            why = whotab.why;
            if why < "" then why = "";
            call date_time_ (whotab.nextsd, time);
            if whotab.until = 0 then call ioa_ ("Scheduled shutdown at ^a ^a", time, why);
            else do;
                 call date_time_ (whotab.until, time1);
                 call ioa_ ("Scheduled shutdown from ^a to ^a ^a", time, time1, why);
            end;
             end;
             call date_time_ (whotab.lastsd, time);
             if whotab.erfno = "crash" then call ioa_ ("Last crash was at ^a^/", time);
             else if whotab.lastsd = 0 then call ioa_ ("");
             else if whotab.erfno = "" then call ioa_ ("Last shutdown was at ^a^/", time);
             else call ioa_ ("Last crash (ERF ^a) was at ^a^/", whotab.erfno, time);
             if hmuflg then if selx = 0 then return;
            else go to shell_sort;
             call ioa_ ("^4xLogin at^6xTTY  Load^3xUser ID^/");
        end;
        else do;                /* short who. */
             call ioa_ ("^/Multics ^a, load ^.1f/^.1f; ^d users, ^d interactive, ^d daemons.",
            whotab.sysid, f1, f2, whotab.n_users, j, whotab.n_daemons);
             if (whotab.max_abs_users + whotab.abs_users) ^= 0
             then               /* print absentee totals under certain conditions */
print_abs_totals:       call ioa_
                 ("^[^/^]Absentee users ^d/^d^[^x(+^d FG)^;^s^]^[^/^]",
                 only_abs, whotab.abs_users, whotab.max_abs_users, (whotab.fg_abs_users > 0), whotab.fg_abs_users, only_abs);
             if ^abs then call ioa_ ("");
        end;
         end;
    end;
check_hmu:
    if hmuflg & selx = 0            /* if a simple hmu entry then finished */
    then return;

shell_sort: last = whotab.laste;            /* save high limit on whotab */
    if hmuflg then go to count;         /* go to selective hmu counting */

    begin;

dcl  sort_array (last) fixed bin;

         do j = 1 to last;          /* set up sort array */
        sort_array (j) = j;         /* ... */
         end;

         d = last;              /* set up for Shell sort */
pass:        d = divide (d + 1, 2, 17, 0);      /* ... */
         swap = 0;              /* ... */
         do j = 1 to last - d;          /* comparison loop */
        aj = sort_array (j);        /* make temps */
        ajd = sort_array (j + d);       /* ... */
        if sort = 0 then if whotab.timeon (aj) > whotab.timeon (ajd) then go to ic;
        if sort = 1 then if whotab.person (aj) > whotab.person (ajd) then go to ic;
        if sort = 2 then if whotab.project (aj) > whotab.project (ajd) then go to ic;
             else if whotab.project (aj) = whotab.project (ajd) then if whotab.person (aj)
                 > whotab.person (ajd) then do; /* Are items in order? */
ic:              sort_array (j) = ajd;  /* No. Swap entries */
                 sort_array (j + d) = aj; /* ... */
                 swap = swap + 1;   /* remember a swap */
            end;
         end;
         if swap > 0 then go to pass;        /* if out of order do it again */
         if d > 1 then go to pass;       /* ... */



         if af_sw then return_arg = "";

         do j = 1 to last;          /* now the print loop */
        aj = sort_array (j);        /* set up speed temp */
        if whotab.active (aj) = 0 then go to skip; /* skip deads */
        if selx = 0 then go to print;       /* any users selected? */
        do k = 1 to selx;           /* check for selected users */
             if nm (k) = whotab.person (aj) then if pj (k) = "" then go to print;
            else if pj (k) = whotab.project (aj) then go to print;
             if nm (k) = "" then if pj (k) = whotab.project (aj) then go to print;
        end;
        go to skip;         /* user not in selected group */

print:
        if whotab.proc_type (aj) = 1 & ^interactive
             | whotab.proc_type (aj) = 2 & ^abs
             | whotab.proc_type (aj) = 3 & ^daemon
        then goto skip;

        if af_sw then do;
             if return_arg ^= "" then return_arg = return_arg || " ";
             return_arg = return_arg ||
            requote_string_ (rtrim (whotab.person (aj)) || "." || rtrim (whotab.project (aj)));
             go to skip;
        end;

        if whotab.proc_type (aj) ^= 2 then  /* if not absentee */
             mark = "";         /* clear absentee flag */
        else if whotab.fg_abs (aj) then /* if foreground absentee */
             mark = "*FG";          /* flag it as such */
        else mark = "*";            /* else flag it as background absentee */

        did = did + 1;          /* remember we did one */
        if long then do;            /* long who? */
             call date_time_ (whotab.timeon (aj), time); /* yup. */
             if substr (time, 1, 8) = substr (time1, 1, 8) then substr (time, 1, 8) = (8)" ";
                        /* Suppress date if it is the same as last printed date */
             else time1 = time;
             f1 = whotab.units (aj) / 10.0e0;   /* get nice units */
             call ioa_ ("^16a  ^4a ^4.1f^3x^a.^a^a^x^[D^]^[S^]",
            time, whotab.idcode (aj), f1, whotab.person (aj), whotab.project (aj),
            mark, whotab.disconnected (aj), whotab.suspended (aj));
        end;
        else do;                /* short who. */
             call ioa_ ("^a.^a^a^x^[D^]^[S^]", whotab.person (aj), whotab.project (aj), mark,
            whotab.disconnected (aj), whotab.suspended (aj));
        end;

skip:        end;

    end;

    if ^af_sw then do;
         if ^brief then

        if did = 0 then do;         /* if printed nobody */
             if selx = 1 then if nm (1) ^= "" then sss = "";
             call ioa_ ("User^a not logged in.", sss);
        end;

         call ioa_ ("");            /* extra CR */
    end;

    return;                 /* done. */

/* - - - - - - - - */


count:  do j = 1 to selx;               /* selective hmu counting */
         hmucnt = 0;                /* reset counters */
         abscnt = 0;

         if nm (j) = "" then do;            /* selected project counting */
        do aj = 1 to last;
             if pj (j) = whotab.project (aj) then
            if whotab.proc_type (aj) ^= 2
            then hmucnt = hmucnt + 1;
            else abscnt = abscnt + 1;
        end;
        call ioa_ (".^a = ^d + ^d*", pj (j), hmucnt, abscnt);
         end;

         if nm (j) ^= "" then
        if pj (j) ^= ""         /* selected name.project counting */
        then do;
             do aj = 1 to last;
            if nm (j) = whotab.person (aj)
            then if pj (j) = whotab.project (aj)
                 then if whotab.proc_type (aj) ^= 2
                then hmucnt = hmucnt + 1;
                else abscnt = abscnt + 1;
             end;
             call ioa_ ("^a.^a = ^d + ^d*", nm (j), pj (j), hmucnt, abscnt);
        end;
        else do;                /* selected name counting */
             do aj = 1 to last;

            if nm (j) = whotab.person (aj) then
                 if whotab.proc_type (aj) ^= 2
                 then hmucnt = hmucnt + 1;
                 else abscnt = abscnt + 1;
             end;
             call ioa_ ("^a = ^d + ^d*", nm (j), hmucnt, abscnt);
        end;
    end;
    return;

who_init: entry (system_directory);         /* entry used for testing who command */

dcl  system_directory char (*);

    sysdir = system_directory;          /* copy name of directory containing who table */

    whoptr = null;              /* set pointer to null */

    return;

     end who;

"This material is presented to ensure dissemination of scholarly and technical work. Copyright and all rights therein are retained by authors or by other copyright holders. All persons copying this information are expected to adhere to the terms and constraints invoked by each author's copyright. In most cases, these works may not be reposted without the explicit permission of the copyright holder."