?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Permanent Files: Process Storage' ??
MODULE pfm$process_storage;
?? NEWTITLE := 'Global Declarations referenced by this module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc avc$accounting_statistics
*copyc ave$validation_interface_errors
*copyc cle$ecc_command_processing
*copyc fst$goi_object_information
*copyc oss$job_paged_literal
*copyc pft$complete_path
*copyc pfe$error_condition_codes
?? POP ??
*copyc avp$change_user_pf_space_limit
*copyc avp$family_administrator
*copyc avp$replace_total_limits
*copyc avp$system_administrator
*copyc avp$validation_level
*copyc clp$convert_integer_to_string
*copyc clp$count_list_elements
*copyc clp$evaluate_parameters
*copyc clp$get_processing_phase
*copyc fsp$build_file_ref_from_elems
*copyc i#current_sequence_position
*copyc mmp$create_scratch_segment
*copyc mmp$delete_scratch_segment
*copyc osp$append_status_file
*copyc osp$append_status_integer
*copyc osp$append_status_parameter
*copyc osp$disestablish_cond_handler
*copyc osp$establish_block_exit_hndlr
*copyc osp$generate_error_message
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc pfp$get_object_information
*copyc pmp$date_time_compare
*copyc pmp$get_family_names
*copyc pmp$get_user_identification
*copyc sfp$emit_statistic
*copyc pfv$space_character

?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations declared by this module', EJECT ??

  CONST
    include_radix = TRUE,
    process_storage_command = 'PROCESS_STORAGE',
    radix = 10,
    update_perm_file_space_command = 'UPDATE_PERM_FILE_SPACE_LIMIT';

  TYPE
    project_info_type = (pit_catalog_info, pit_cycle_info);

  TYPE
    project_information = record
      info_type: project_info_type,
      account_name: avt$account_name,
      project_name: avt$project_name,
      allocated_size: integer,
      online_eoi_size: integer,
      offline_eoi_size: integer,
      offline_total_eoi_size: integer,
      count: integer, {holds number of files or catalogs depending on info type}
      next_project_info: ^project_information,
    recend;

  TYPE
    user_information = record
      cycle_allocated_size: integer,
      cycle_online_eoi_size: integer,
      cycle_offline_eoi_size: integer,
      cycle_offline_total_eoi_size: integer,
      cycle_count: integer,
      catalog_allocated_size: integer,
      catalog_online_eoi_size: integer,
      catalog_count: integer,
    recend;

  VAR
    catalog_information_request: [STATIC, READ, oss$job_paged_literal] fst$goi_information_request :=
          [[fsc$specific_depth, 1], $fst$goi_object_info_requests
          [fsc$goi_catalog_identity, fsc$goi_catalog_device_info, fsc$goi_catalog_info, fsc$goi_catalog_size,
          fsc$goi_catalog_object_list, fsc$goi_file_object_list, fsc$goi_file_identity, fsc$goi_file_info,
          fsc$goi_cycle_object_list, fsc$goi_cycle_identity, fsc$goi_archive_info, fsc$goi_cycle_device_info,
          fsc$goi_cycle_info, fsc$goi_cycle_size]];

?? OLDTITLE ??
?? NEWTITLE := '[XDCL] pfp$process_storage', EJECT ??

  PROCEDURE [XDCL] pfp$process_storage
    (    parameter_list: clt$parameter_list;
     VAR status: ost$status);

{ NOTE:
{   Procedure pfp$_update_perm_file_space_lim has essentially the same design as this procedure.
{ Any changes made here should be considered for inclusion in that procedure as well.

{ PROCEDURE (osm$pros) process_storage, pros (
{   family, families, f: any of
{       key
{         all
{       keyend
{       list of name
{     anyend = all
{   processing_option, processing_options, po: any of
{       key
{         all
{       keyend
{       list of key
{         (emit_space_statistics, ess)
{         (update_limits, ul)
{       keyend
{     anyend = all
{   status)

?? PUSH (LISTEXT := ON) ??
?? FMT (FORMAT := OFF) ??

  VAR
    pdt: [STATIC, READ, cls$declaration_section] record
      header: clt$pdt_header,
      names: array [1 .. 7] of clt$pdt_parameter_name,
      parameters: array [1 .. 3] of clt$pdt_parameter,
      type1: record
        header: clt$type_specification_header,
        qualifier: clt$union_type_qualifier,
        type_size_1: clt$type_specification_size,
        element_type_spec_1: record
          header: clt$type_specification_header,
          qualifier: clt$keyword_type_qualifier,
          keyword_specs: array [1 .. 1] of clt$keyword_specification,
        recend,
        type_size_2: clt$type_specification_size,
        element_type_spec_2: record
          header: clt$type_specification_header,
          qualifier: clt$list_type_qualifier,
          element_type_spec: record
            header: clt$type_specification_header,
            qualifier: clt$name_type_qualifier,
          recend,
        recend,
        default_value: string (3),
      recend,
      type2: record
        header: clt$type_specification_header,
        qualifier: clt$union_type_qualifier,
        type_size_1: clt$type_specification_size,
        element_type_spec_1: record
          header: clt$type_specification_header,
          qualifier: clt$keyword_type_qualifier,
          keyword_specs: array [1 .. 1] of clt$keyword_specification,
        recend,
        type_size_2: clt$type_specification_size,
        element_type_spec_2: record
          header: clt$type_specification_header,
          qualifier: clt$list_type_qualifier,
          element_type_spec: record
            header: clt$type_specification_header,
            qualifier: clt$keyword_type_qualifier,
            keyword_specs: array [1 .. 4] of clt$keyword_specification,
          recend,
        recend,
        default_value: string (3),
      recend,
      type3: record
        header: clt$type_specification_header,
      recend,
    recend := [
    [1,
    [89, 5, 3, 15, 15, 16, 689],
    clc$command, 7, 3, 0, 0, 0, 0, 3, 'OSM$PROS'], [
    ['F                              ',clc$abbreviation_entry, 1],
    ['FAMILIES                       ',clc$alias_entry, 1],
    ['FAMILY                         ',clc$nominal_entry, 1],
    ['PO                             ',clc$abbreviation_entry, 2],
    ['PROCESSING_OPTION              ',clc$nominal_entry, 2],
    ['PROCESSING_OPTIONS             ',clc$alias_entry, 2],
    ['STATUS                         ',clc$nominal_entry, 3]],
    [
{ PARAMETER 1
    [3, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 85,
  clc$optional_default_parameter, 0, 3],
{ PARAMETER 2
    [5, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 235,
  clc$optional_default_parameter, 0, 3],
{ PARAMETER 3
    [7, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name],
    clc$pass_by_reference, clc$immediate_evaluation, clc$standard_parameter_checking, 3,
  clc$optional_parameter, 0, 0]],
{ PARAMETER 1
    [[1, 0, clc$union_type], [[clc$keyword_type, clc$list_type],
    FALSE, 2],
    44, [[1, 0, clc$keyword_type], [1], [
      ['ALL                            ', clc$nominal_entry, clc$normal_usage_entry, 1]]
      ],
    21, [[1, 0, clc$list_type], [5, 1, clc$max_list_size, FALSE],
        [[1, 0, clc$name_type], [1, osc$max_name_size]]
      ]
    ,
    'all'],
{ PARAMETER 2
    [[1, 0, clc$union_type], [[clc$keyword_type, clc$list_type],
    FALSE, 2],
    44, [[1, 0, clc$keyword_type], [1], [
      ['ALL                            ', clc$nominal_entry, clc$normal_usage_entry, 1]]
      ],
    171, [[1, 0, clc$list_type], [155, 1, clc$max_list_size, FALSE],
        [[1, 0, clc$keyword_type], [4], [
        ['EMIT_SPACE_STATISTICS          ', clc$nominal_entry, clc$normal_usage_entry, 1],
        ['ESS                            ', clc$abbreviation_entry, clc$normal_usage_entry, 1],
        ['UL                             ', clc$abbreviation_entry, clc$normal_usage_entry, 2],
        ['UPDATE_LIMITS                  ', clc$nominal_entry, clc$normal_usage_entry, 2]]
        ]
      ]
    ,
    'all'],
{ PARAMETER 3
    [[1, 0, clc$status_type]]];

?? FMT (FORMAT := ON) ??
?? POP ??

    CONST
      p$family = 1,
      p$processing_option = 2,
      p$status = 3;

    VAR
      pvt: array [1 .. 3] of clt$parameter_value;

    VAR
      active_family_list: ^pmt$family_name_list,
      active_family_list_count: pmt$family_name_count,
      current_processing_option: ^clt$data_value,
      emit_space_statistics: boolean,
      family_catalog_info_p: ^fst$goi_object,
      family_catalog_reference: fst$path,
      family_found: boolean,
      family_list: ^pmt$family_name_list,
      family_name: ost$family_name,
      family_object_info_p: ^fst$goi_object_information,
      first_project_info: ^project_information,
      index1: integer,
      index2: integer,
      information_request: [STATIC, READ, oss$job_paged_literal] fst$goi_information_request :=
            [[fsc$specific_depth, 1], $fst$goi_object_info_requests
            [fsc$goi_catalog_identity, fsc$goi_catalog_size, fsc$goi_catalog_object_list]],
      limit_entries_array: ^array [1 .. * ] of avt$total_limit_update_record,
      limit_info_p: ^SEQ ( * ),
      limit_segment_pointer: amt$segment_pointer,
      master_catalog_array_p: ^fst$goi_object_list,
      master_catalog_path: array [1 .. 2] of pft$name,
      number_of_errors: integer,
      number_of_limit_entries: integer,
      object_index: integer,
      object_info_p: ^SEQ ( * ),
      object_segment_pointer: amt$segment_pointer,
      project_info_p: ^SEQ ( * ),
      project_segment_pointer: amt$segment_pointer,
      specified_family_list: ^clt$data_value,
      sub_object_info_p: ^SEQ ( * ),
      update_limits: boolean,
      user_info: user_information,
      user_name: ost$user_name,
      validation_level: avt$validation_level;

?? NEWTITLE := '[INLINE] append_to_descr_data', EJECT ??

{ PURPOSE
{   This procedure is used to append information to a descriptive data string that will be placed on a file
{   space statistic.

    PROCEDURE [INLINE] append_to_descr_data
      (    separator: string ( * <= osc$max_string_size);
           string_to_append: string ( * <= osc$max_string_size);
       VAR descriptive_data: ost$string);

      VAR
        result: boolean,
        first_blank: integer;

      IF STRLENGTH (separator) <> 0 THEN
        descriptive_data.value (descriptive_data.size + 1, STRLENGTH (separator)) := separator;
        descriptive_data.size := descriptive_data.size + STRLENGTH (separator);
      IFEND;
      IF STRLENGTH (string_to_append) <> 0 THEN
        #SCAN (pfv$space_character, string_to_append, first_blank, {ignore} result);
        descriptive_data.value (descriptive_data.size + 1, first_blank - 1) :=
              string_to_append (1, first_blank - 1);
        descriptive_data.size := descriptive_data.size + first_blank - 1;
      IFEND;

    PROCEND append_to_descr_data;
?? OLDTITLE ??
*copy pfp$add_to_project_info
?? NEWTITLE := 'condition_handler', EJECT ??

{ PURPOSE:
{   This is a block exit condition handler that is used to insure that the end emit space statistics statistic
{   is emitted and the scratch segments are deleted.

    PROCEDURE condition_handler
      (    condition: pmt$condition;
           condition_information: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      VAR
        ignore_status: ost$status;

      mmp$delete_scratch_segment (object_segment_pointer, ignore_status);
      mmp$delete_scratch_segment (project_segment_pointer, ignore_status);
      mmp$delete_scratch_segment (limit_segment_pointer, ignore_status);

      IF emit_space_statistics THEN
        sfp$emit_statistic (avc$end_emit_space_statistics, 'Process storage terminated abnormally.', NIL,
              ignore_status);
      IFEND;

    PROCEND condition_handler;
?? OLDTITLE ??
*copy pfp$create_scratch_segments
?? NEWTITLE := 'process_catalog', EJECT ??

{ PURPOSE
{   This procedure processes a catalog object entry that was obtained by get object information.  A statistic
{   for the catalog itself is emitted and information is added to the project totals and user totals for the
{   master catalog.

    PROCEDURE process_catalog
      (    catalog_path: ^pft$path;
           object_entry: ^fst$goi_object;
           object_info_p: ^SEQ ( * );
       VAR status: ost$status);

      VAR
        allocated_size: integer,
        catalog_object_entry_p: ^fst$goi_object,
        catalog_object_info_p: ^fst$goi_object_information,
        catalog_reference: fst$path,
        current_catalog_index: pft$catalog_path_index,
        current_object_info_p: ^SEQ ( * ),
        number_of_files: integer,
        number_of_subcatalogs: integer,
        object_index: integer,
        object_path: ^pft$path,
        object_status: ost$status,
        online_eoi_size: integer,
        path_index: pft$catalog_path_index,
        statistic_counters: sft$counters,
        statistic_descr_data: ost$string,
        sub_object_array_p: ^fst$goi_object_list,
        sub_object_info_p: ^SEQ ( * );

?? NEWTITLE := 'process_file', EJECT ??

{ PURPOSE
{   This procedure processes a file object entry that was obtained by get object information.  A statistic for
{   each cycle of the file is emitted and information is added to the project totals and user totals for the
{   master catalog.

      PROCEDURE process_file
        (    file_path: pft$path;
             file_object_entry: ^fst$goi_object;
         VAR status: ost$status);

        VAR
          allocated_size: integer,
          archive_index: integer,
          comparison_result: pmt$comparison_result,
          cycle_number_string: ost$string,
          cycle_statistic_counters: sft$counters,
          cycle_statistic_descr_data: ost$string,
          object_index: integer,
          offline_eoi_size: integer,
          offline_total_eoi_size: integer,
          online_eoi_size: integer;

?? NEWTITLE := 'record_file_error', EJECT ??

        PROCEDURE record_file_error
          (    error_path: ^pft$path;
               cycle_number: fst$cycle_number;
           VAR number_of_errors: integer;
           VAR status: ost$status);

          VAR
            ignore_status: ost$status,
            path: fst$path,
            recorded_status: ost$status;

          number_of_errors := number_of_errors + 1;
          fsp$build_file_ref_from_elems (error_path, path, ignore_status);
          osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$skipped_file, process_storage_command,
                recorded_status);
          osp$append_status_file (osc$status_parameter_delimiter, path, recorded_status);
          osp$append_status_integer (osc$status_parameter_delimiter, cycle_number, radix, NOT include_radix,
                recorded_status);
          osp$generate_error_message (recorded_status, ignore_status);
          osp$generate_error_message (status, ignore_status);

        PROCEND record_file_error;

?? OLDTITLE, EJECT ??

        status.normal := TRUE;

{ Process each cycle of the file.

        IF file_object_entry^.cycle_object_list <> NIL THEN

        /process_cycle/
          FOR object_index := 1 TO UPPERBOUND (file_object_entry^.cycle_object_list^) DO
            CASE file_object_entry^.cycle_object_list^ [object_index].cycle_device_class OF
            = rmc$mass_storage_device =

{ Get file size information.

              allocated_size := 0;
              online_eoi_size := 0;
              offline_eoi_size := 0;
              offline_total_eoi_size := 0;
              IF file_object_entry^.cycle_object_list^ [object_index].cycle_device_information^.
                    mass_storage_device_info.resides_online THEN
                online_eoi_size := file_object_entry^.cycle_object_list^ [object_index].cycle_size^;
                allocated_size := file_object_entry^.cycle_object_list^ [object_index].
                      cycle_device_information^.mass_storage_device_info.bytes_allocated;
              IFEND;

{ Get the archived sizes from each "current" archive information list entry.

              IF file_object_entry^.cycle_object_list^ [object_index].archive_information_list <> NIL THEN
                IF file_object_entry^.cycle_object_list^ [object_index].cycle_device_information^.
                      mass_storage_device_info.resides_online THEN

{ If the file cycle resides online, check to see if it has been modified since the last archive.  If so,
{ then do not include it as part of the size of files that are duplicated.

                /check_for_archived_files/
                  FOR archive_index := 1 TO UPPERBOUND (file_object_entry^.cycle_object_list^ [object_index].
                        archive_information_list^) DO
                    pmp$date_time_compare (file_object_entry^.cycle_object_list^ [object_index].
                          cycle_information^.data_modification_date_time,
                          file_object_entry^.cycle_object_list^ [object_index].
                          archive_information_list^ [archive_index].archive_entry.archive_date_time,
                          comparison_result, status);
                    IF NOT status.normal THEN
                      record_file_error (^file_path, file_object_entry^.cycle_object_list^ [object_index].
                            cycle_number, number_of_errors, status);
                      CYCLE /process_cycle/;
                    IFEND;
                    IF comparison_result = pmc$right_is_greater THEN
                      offline_total_eoi_size := file_object_entry^.cycle_object_list^ [object_index].
                            archive_information_list^ [archive_index].archive_entry.file_size;
                      EXIT /check_for_archived_files/;
                    IFEND;
                  FOREND /check_for_archived_files/;
                ELSE

{ In this case, the file is archived and released - it should be counted in both offline counters.

                  offline_total_eoi_size := file_object_entry^.cycle_object_list^ [object_index].cycle_size^;
                  offline_eoi_size := file_object_entry^.cycle_object_list^ [object_index].cycle_size^;
                IFEND;

              IFEND;

              IF emit_space_statistics THEN
                clp$convert_integer_to_string (file_object_entry^.cycle_object_list^ [object_index].
                      cycle_number, 10, FALSE, cycle_number_string, status);
                IF NOT status.normal THEN
                  record_file_error (^file_path, file_object_entry^.cycle_object_list^ [object_index].
                        cycle_number, number_of_errors, status);
                  CYCLE /process_cycle/;
                IFEND;

{ Set up the statistic counters.

                PUSH cycle_statistic_counters: [1 .. 6];
                cycle_statistic_counters^ [1] := online_eoi_size;
                cycle_statistic_counters^ [2] := 0; {attach status}
                cycle_statistic_counters^ [3] := UPPERBOUND (file_path) - 2; {depth}
                cycle_statistic_counters^ [4] := allocated_size;
                cycle_statistic_counters^ [5] := offline_eoi_size;
                cycle_statistic_counters^ [6] := offline_total_eoi_size;

{ Set up the statistic descriptive data.

                cycle_statistic_descr_data.value := '';
                cycle_statistic_descr_data.size := 0;
                append_to_descr_data ('', family_name, cycle_statistic_descr_data);
                append_to_descr_data (', ', user_name, cycle_statistic_descr_data);
                append_to_descr_data (', ', file_object_entry^.file_information^.account,
                      cycle_statistic_descr_data);
                append_to_descr_data (', ', file_object_entry^.file_information^.project,
                      cycle_statistic_descr_data);
                append_to_descr_data (', ', '', cycle_statistic_descr_data);
                append_to_descr_data (', ', file_object_entry^.file_name, cycle_statistic_descr_data);
                append_to_descr_data ('.', cycle_number_string.value (1, cycle_number_string.size),
                      cycle_statistic_descr_data);

{ Emit a file cycle statistic.

                sfp$emit_statistic (avc$cycle, cycle_statistic_descr_data.
                      value (1, cycle_statistic_descr_data.size), cycle_statistic_counters, status);
                IF NOT status.normal THEN
                  record_file_error (^file_path, file_object_entry^.cycle_object_list^ [object_index].
                        cycle_number, number_of_errors, status);
                  CYCLE /process_cycle/;
                IFEND;
              IFEND;

{ Add the current file information to the account, project and user info.

              pfp$add_to_project_info (pit_cycle_info, file_object_entry^.file_information^.account,
                    osc$null_name, online_eoi_size, offline_eoi_size, offline_total_eoi_size, allocated_size,
                    first_project_info);
              pfp$add_to_project_info (pit_cycle_info, file_object_entry^.file_information^.account,
                    file_object_entry^.file_information^.project, online_eoi_size, offline_eoi_size,
                    offline_total_eoi_size, allocated_size, first_project_info);
              user_info.cycle_online_eoi_size := user_info.cycle_online_eoi_size + online_eoi_size;
              user_info.cycle_offline_eoi_size := user_info.cycle_offline_eoi_size + offline_eoi_size;
              user_info.cycle_offline_total_eoi_size := user_info.cycle_offline_total_eoi_size +
                    offline_total_eoi_size;
              user_info.cycle_allocated_size := user_info.cycle_allocated_size + allocated_size;
              user_info.cycle_count := user_info.cycle_count + 1;
            ELSE

{ Device type not currently reported on.

            CASEND;
          FOREND /process_cycle/;
        IFEND;

      PROCEND process_file;

?? OLDTITLE, EJECT ??

      status.normal := TRUE;

{ Allocate a path to hold the next level object.

      current_catalog_index := UPPERBOUND (catalog_path^);
      PUSH object_path: [1 .. current_catalog_index + 1];
      FOR path_index := 1 TO current_catalog_index DO
        object_path^ [path_index] := catalog_path^ [path_index];
      FOREND;

      fsp$build_file_ref_from_elems (catalog_path, catalog_reference, status);
      IF NOT status.normal THEN
        pfp$record_catalog_error (catalog_path, process_storage_command, number_of_errors, status);
        RETURN;
      IFEND;

      current_object_info_p := object_info_p;

{ Place the object information for the current catalog into the beginning of the object info sequence.

      pfp$get_object_information (catalog_reference, catalog_information_request, {validation_criteria} NIL,
            current_object_info_p, status);
      IF NOT status.normal THEN
        pfp$record_catalog_error (catalog_path, process_storage_command, number_of_errors, status);
        RETURN;
      IFEND;

{ Insert a sequence to be used for the next level of object information into the object info sequence.

      NEXT sub_object_info_p: [[REP #SIZE (current_object_info_p^) -
            i#current_sequence_position (current_object_info_p) OF cell]] IN current_object_info_p;
      RESET sub_object_info_p;

{ Retrieve the sub_object array for the current catalog.

      RESET current_object_info_p;
      NEXT catalog_object_info_p IN current_object_info_p;
      catalog_object_entry_p := catalog_object_info_p^.object;
      sub_object_array_p := catalog_object_entry_p^.subcatalog_and_file_object_list;

{ Get the catalog size information.

      allocated_size := catalog_object_entry_p^.catalog_device_information^.mass_storage_device_info.
            bytes_allocated;
      online_eoi_size := catalog_object_entry_p^.catalog_size^;

{ If catalog archiving is ever implemented code must be added here.

      IF emit_space_statistics THEN

{ Add up the number of files and/or subcatalogs in the current catalog.

        number_of_files := 0;
        number_of_subcatalogs := 0;
        IF sub_object_array_p <> NIL THEN
          FOR object_index := LOWERBOUND (sub_object_array_p^) TO UPPERBOUND (sub_object_array_p^) DO
            IF sub_object_array_p^ [object_index].object_type = fsc$goi_file_object THEN
              number_of_files := number_of_files + 1;
            ELSEIF sub_object_array_p^ [object_index].object_type = fsc$goi_catalog_object THEN
              number_of_subcatalogs := number_of_subcatalogs + 1;
            IFEND;
          FOREND;
        IFEND;

{ Set up the statistic counters.

        PUSH statistic_counters: [1 .. 7];
        statistic_counters^ [1] := online_eoi_size;
        statistic_counters^ [2] := number_of_files;
        statistic_counters^ [3] := number_of_subcatalogs;
        statistic_counters^ [4] := UPPERBOUND (catalog_path^) - 1;
        statistic_counters^ [5] := allocated_size;
        statistic_counters^ [6] := 0; {offline_eoi_size}
        statistic_counters^ [7] := 0; {offline_total_eoi_size}

{ Set up the statistic descriptive data.

        statistic_descr_data.value := '';
        statistic_descr_data.size := 0;
        append_to_descr_data ('', family_name, statistic_descr_data);
        append_to_descr_data (', ', user_name, statistic_descr_data);
        append_to_descr_data (', ', catalog_object_entry_p^.catalog_information^.account,
              statistic_descr_data);
        append_to_descr_data (', ', catalog_object_entry_p^.catalog_information^.project,
              statistic_descr_data);
        append_to_descr_data (', ', '', statistic_descr_data);
        append_to_descr_data (', ', catalog_path^ [UPPERBOUND (catalog_path^)], statistic_descr_data);

{ Emit a catalog statistic for the current catalog.

        sfp$emit_statistic (avc$catalog, statistic_descr_data.value (1, statistic_descr_data.size),
              statistic_counters, status);
        IF NOT status.normal THEN
          pfp$record_catalog_error (catalog_path, process_storage_command, number_of_errors, status);
          RETURN;
        IFEND;
      IFEND;

{ Add the current catalog information to the account, project and user info.

      pfp$add_to_project_info (pit_catalog_info, catalog_object_entry_p^.catalog_information^.account,
            osc$null_name, online_eoi_size, {offline_eoi_size} 0, {offline_total_eoi_size} 0, allocated_size,
            first_project_info);
      pfp$add_to_project_info (pit_catalog_info, catalog_object_entry_p^.catalog_information^.account,
            catalog_object_entry_p^.catalog_information^.project, online_eoi_size, {offline_eoi_size} 0,
            {offline_total_eoi_size} 0, allocated_size, first_project_info);
      user_info.catalog_online_eoi_size := user_info.catalog_online_eoi_size + online_eoi_size;
      user_info.catalog_allocated_size := user_info.catalog_allocated_size + allocated_size;
      user_info.catalog_count := user_info.catalog_count + 1;

{ Process the sub objects within the current catalog.

      IF sub_object_array_p <> NIL THEN
        FOR object_index := LOWERBOUND (sub_object_array_p^) TO UPPERBOUND (sub_object_array_p^) DO
          IF sub_object_array_p^ [object_index].object_type = fsc$goi_file_object THEN
            object_path^ [current_catalog_index + 1] := sub_object_array_p^ [object_index].file_name;
            process_file (object_path^, ^sub_object_array_p^ [object_index], object_status);
          ELSEIF sub_object_array_p^ [object_index].object_type = fsc$goi_catalog_object THEN
            object_path^ [current_catalog_index + 1] := sub_object_array_p^ [object_index].catalog_name;
            process_catalog (object_path, ^sub_object_array_p^ [object_index], sub_object_info_p,
                  object_status);
          IFEND;
        FOREND;
      IFEND;

    PROCEND process_catalog;
?? OLDTITLE ??
?? NEWTITLE := 'process_project_info', EJECT ??

{ PURPOSE
{   This procedure reads through the linked list of project information entries for a user, emits
{   account_member and project member statistics, and writes out account , account member, project and project
{   member limit updates into a sequence of limit information to be used to update the permanent file space
{   accumulator in the validation file.

    PROCEDURE process_project_info
      (    family_name: ost$family_name;
           user_name: ost$user_name;
       VAR first_project_info {I/O} : ^project_information;
       VAR status: ost$status);

      VAR
        current_project_info: ^project_information,
        project_info_counters: sft$counters,
        project_info_descr_data: ost$string;

      current_project_info := first_project_info;

      WHILE current_project_info <> NIL DO

{ Set up the statistic counters.

        PUSH project_info_counters: [1 .. 5];
        project_info_counters^ [1] := current_project_info^.online_eoi_size;
        project_info_counters^ [2] := current_project_info^.count;
        project_info_counters^ [3] := current_project_info^.allocated_size;
        project_info_counters^ [4] := current_project_info^.offline_eoi_size;
        project_info_counters^ [5] := current_project_info^.offline_total_eoi_size;

{ Set up the statistic descriptive data.

        project_info_descr_data.value := '';
        project_info_descr_data.size := 0;
        append_to_descr_data ('', family_name, project_info_descr_data);
        append_to_descr_data (', ', user_name, project_info_descr_data);
        append_to_descr_data (', ', current_project_info^.account_name, project_info_descr_data);
        append_to_descr_data (', ', current_project_info^.project_name, project_info_descr_data);
        append_to_descr_data (', ', '', project_info_descr_data);

        IF current_project_info^.project_name = osc$null_name THEN

{ If the project is null for this entry then it is account information.

          IF emit_space_statistics THEN

{ Emit an account member statistic.

            IF current_project_info^.info_type = pit_catalog_info THEN
              sfp$emit_statistic (avc$account_member_catalog, project_info_descr_data.
                    value (1, project_info_descr_data.size), project_info_counters, status);
            ELSE
              sfp$emit_statistic (avc$account_member_cycle, project_info_descr_data.
                    value (1, project_info_descr_data.size), project_info_counters, status);
            IFEND;
            IF NOT status.normal THEN
              RETURN;
            IFEND;
          IFEND;
          IF (update_limits) AND (current_project_info^.info_type = pit_cycle_info) AND
                (validation_level > avc$user_level) THEN

{ Write out an account limit entry.

            pfp$write_limit_entry (family_name, current_project_info^.account_name, osc$null_name,
                  osc$null_name, current_project_info^.allocated_size, number_of_limit_entries, limit_info_p);

{ Write out an account member limit entry.

            pfp$write_limit_entry (family_name, current_project_info^.account_name, osc$null_name, user_name,
                  current_project_info^.allocated_size, number_of_limit_entries, limit_info_p);
          IFEND;
        ELSE

{ This entry is project information.

          IF emit_space_statistics THEN

{ Emit a project member statistic.

            IF current_project_info^.info_type = pit_catalog_info THEN
              sfp$emit_statistic (avc$project_member_catalog, project_info_descr_data.
                    value (1, project_info_descr_data.size), project_info_counters, status);
            ELSE
              sfp$emit_statistic (avc$project_member_cycle, project_info_descr_data.
                    value (1, project_info_descr_data.size), project_info_counters, status);
            IFEND;
            IF NOT status.normal THEN
              RETURN;
            IFEND;
          IFEND;

          IF (update_limits) AND (current_project_info^.info_type = pit_cycle_info) AND
                (validation_level > avc$account_level) THEN

{ Write out a project limit entry.

            pfp$write_limit_entry (family_name, current_project_info^.account_name,
                  current_project_info^.project_name, osc$null_name, current_project_info^.allocated_size,
                  number_of_limit_entries, limit_info_p);

{ Write out a project member limit entry.

            pfp$write_limit_entry (family_name, current_project_info^.account_name,
                  current_project_info^.project_name, user_name, current_project_info^.allocated_size,
                  number_of_limit_entries, limit_info_p);
          IFEND;
        IFEND;
        current_project_info := current_project_info^.next_project_info;
      WHILEND;
      first_project_info := NIL;

    PROCEND process_project_info;
?? OLDTITLE ??
?? NEWTITLE := 'process_user_info', EJECT ??

{ PURPOSE
{   This procedure emits user cycle and catalog statistics, and writes out user limit updates into a sequence
{   of limit information to be used to update the permanent file space accumulator in the validation file.

    PROCEDURE process_user_info
      (VAR user_info: user_information;
       VAR status: ost$status);

      VAR
        user_info_counters: sft$counters,
        user_info_descr_data: ost$string;

      IF emit_space_statistics THEN

{ Set up the statistic descriptive data.

        user_info_descr_data.value := '';
        user_info_descr_data.size := 0;
        append_to_descr_data ('', family_name, user_info_descr_data);
        append_to_descr_data (', ', user_name, user_info_descr_data);
        append_to_descr_data (', ', '', user_info_descr_data);
        append_to_descr_data (', ', '', user_info_descr_data);
        append_to_descr_data (', ', '', user_info_descr_data);

{ Set up the user catalog statistic counters.

        PUSH user_info_counters: [1 .. 5];
        user_info_counters^ [1] := user_info.catalog_online_eoi_size;
        user_info_counters^ [2] := user_info.catalog_count;
        user_info_counters^ [3] := user_info.catalog_allocated_size;
        user_info_counters^ [4] := 0; {offline_eoi_size}
        user_info_counters^ [5] := 0; {offline_total_eoi_size}

{ Emit a user catalog statistic.

        sfp$emit_statistic (avc$user_catalog, user_info_descr_data.value (1, user_info_descr_data.size),
              user_info_counters, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;

{ Set up the user cycle statistic counters.

        user_info_counters^ [1] := user_info.cycle_online_eoi_size;
        user_info_counters^ [2] := user_info.cycle_count;
        user_info_counters^ [3] := user_info.cycle_allocated_size;
        user_info_counters^ [4] := user_info.cycle_offline_eoi_size;
        user_info_counters^ [5] := user_info.cycle_offline_total_eoi_size;

{ Emit a user cycle statistic.

        sfp$emit_statistic (avc$user_cycle, user_info_descr_data.value (1, user_info_descr_data.size),
              user_info_counters, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      IFEND;

      IF update_limits THEN

{ Write out a user limit entry.

        pfp$write_limit_entry (family_name, avc$high_value_name, avc$high_value_name, user_name,
              user_info.cycle_allocated_size, number_of_limit_entries, limit_info_p);
      IFEND;

{ Reset the user information totals for the next user.

      user_info.catalog_allocated_size := 0;
      user_info.catalog_online_eoi_size := 0;
      user_info.catalog_count := 0;
      user_info.cycle_allocated_size := 0;
      user_info.cycle_online_eoi_size := 0;
      user_info.cycle_offline_eoi_size := 0;
      user_info.cycle_offline_total_eoi_size := 0;
      user_info.cycle_count := 0;

    PROCEND process_user_info;

?? OLDTITLE ??
*copy pfp$record_catalog_error
*copy pfp$write_limit_entry

    clp$evaluate_parameters (parameter_list, #SEQ (pdt), NIL, ^pvt, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    IF NOT avp$system_administrator () THEN
      osp$set_status_abnormal ('CL', cle$command_aborted,
            'Only System Administrator may execute PROCESS_STORAGE.', status);
      RETURN;
    IFEND;

    validation_level := avp$validation_level ();
    number_of_limit_entries := 0;
    number_of_errors := 0;

{ Get the processing options.

    emit_space_statistics := FALSE;
    #SPOIL (emit_space_statistics);
    update_limits := FALSE;
    IF pvt [p$processing_option].value^.kind = clc$keyword THEN { ALL was specified }
      emit_space_statistics := TRUE;
      update_limits := TRUE;
    ELSE {clc$list_kind}
      current_processing_option := pvt [p$processing_option].value;
      WHILE current_processing_option <> NIL DO
        IF current_processing_option^.element_value^.keyword_value = 'EMIT_SPACE_STATISTICS' THEN
          emit_space_statistics := TRUE;
        ELSEIF current_processing_option^.element_value^.keyword_value = 'UPDATE_LIMITS' THEN
          update_limits := TRUE;
        IFEND;
        current_processing_option := current_processing_option^.link;
      WHILEND;
    IFEND;

{ Get the list of active families for this system.

    active_family_list_count := 100;
    REPEAT
      PUSH active_family_list: [1 .. active_family_list_count];
      pmp$get_family_names (active_family_list^, active_family_list_count, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
    UNTIL active_family_list_count <= UPPERBOUND (active_family_list^);

{ Get the list of families to be processed.

    IF pvt [p$family].value^.kind = clc$keyword THEN {ALL was specified}

{ Get the family list from the active family list if all was specified.

      PUSH family_list: [1 .. active_family_list_count];
      family_list^ := active_family_list^;
    ELSE { Get the family list from the specified family list.}

      specified_family_list := pvt [p$family].value;
      PUSH family_list: [1 .. clp$count_list_elements (specified_family_list)];
      FOR index1 := 1 TO UPPERBOUND (family_list^) DO
        family_list^ [index1] := specified_family_list^.element_value^.name_value;
        family_found := FALSE;

{ Verify that each specified family is a valid active family.

      /search_loop/
        FOR index2 := 1 TO active_family_list_count DO
          IF active_family_list^ [index2] = family_list^ [index1] THEN
            family_found := TRUE;
            EXIT /search_loop/;
          IFEND;
        FOREND /search_loop/;
        IF NOT family_found THEN
          osp$set_status_abnormal ('PF', pfe$unknown_family, family_list^ [index1], status);
          RETURN;
        IFEND;
        specified_family_list := pvt [p$family].value^.link;
      FOREND;
    IFEND;

    IF emit_space_statistics THEN
      sfp$emit_statistic (avc$begin_emit_space_statistics, ' ', NIL, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
    IFEND;

    pfp$create_scratch_segments (object_segment_pointer, project_segment_pointer, limit_segment_pointer,
          status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    #SPOIL (object_segment_pointer, project_segment_pointer, limit_segment_pointer);

{ Set up the catalog information request one time which will be used by process catalog
{ for each getoi request.

    osp$establish_block_exit_hndlr (^condition_handler);

    RESET object_segment_pointer.sequence_pointer;
    object_info_p := object_segment_pointer.sequence_pointer;
    RESET project_segment_pointer.sequence_pointer;
    project_info_p := project_segment_pointer.sequence_pointer;
    RESET limit_segment_pointer.sequence_pointer;
    limit_info_p := limit_segment_pointer.sequence_pointer;

{ Loop through the families to be processed.

    FOR index1 := 1 TO UPPERBOUND (family_list^) DO
      family_name := family_list^ [index1];
      family_catalog_reference := ':';
      family_catalog_reference (2, * ) := family_name;

{ Reset the object info scratch segment to the beginning when starting on a new family.

      RESET object_info_p;

{ Place the object information for the family into the beginning of the object info sequence.

      pfp$get_object_information (family_catalog_reference, information_request, {validation_criteria} NIL,
            object_info_p, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

{ Insert a sequence to be used later for the sub_object information into the object info sequence immediately
{ after the family object information.

      NEXT sub_object_info_p: [[REP #SIZE (object_info_p^) - i#current_sequence_position (object_info_p) OF
            cell]] IN object_info_p;
      RESET sub_object_info_p;

{ Retrieve the master catalog array from the family object info.

      RESET object_info_p;
      NEXT family_object_info_p IN object_info_p;
      family_catalog_info_p := family_object_info_p^.object;
      master_catalog_array_p := family_catalog_info_p^.subcatalog_and_file_object_list;

      first_project_info := NIL;
      user_info.catalog_allocated_size := 0;
      user_info.catalog_online_eoi_size := 0;
      user_info.catalog_count := 0;
      user_info.cycle_allocated_size := 0;
      user_info.cycle_online_eoi_size := 0;
      user_info.cycle_offline_eoi_size := 0;
      user_info.cycle_offline_total_eoi_size := 0;
      user_info.cycle_count := 0;

{ Process each master catalog within the master catalog array.

    /process_master_catalog/
      FOR object_index := LOWERBOUND (master_catalog_array_p^) TO UPPERBOUND (master_catalog_array_p^) DO
        RESET project_info_p;
        user_name := master_catalog_array_p^ [object_index].catalog_name;

        master_catalog_path [1] := family_name;
        master_catalog_path [2] := user_name;

{ Process catalog will recursively loop through the catalog structure for each master catalog.

        process_catalog (^master_catalog_path, ^master_catalog_array_p^ [object_index], sub_object_info_p,
              status);

        IF NOT status.normal THEN
          CYCLE /process_master_catalog/;
        IFEND;

{ After the complete master catalog has been processed the member and user information collected for the user
{ is processed.

        process_project_info (family_name, user_name, first_project_info, status);
        IF NOT status.normal THEN
          pfp$record_catalog_error (^master_catalog_path, process_storage_command, number_of_errors, status);
          CYCLE /process_master_catalog/;
        IFEND;
        process_user_info (user_info, status);
        IF NOT status.normal THEN
          pfp$record_catalog_error (^master_catalog_path, process_storage_command, number_of_errors, status);
        IFEND;
      FOREND /process_master_catalog/;

    FOREND;

    IF update_limits THEN
      RESET limit_info_p;
      NEXT limit_entries_array: [1 .. number_of_limit_entries] IN limit_info_p;

      avp$replace_total_limits (avc$permanent_file_space_limit, limit_entries_array, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
    IFEND;

    osp$disestablish_cond_handler;

    IF emit_space_statistics THEN
      sfp$emit_statistic (avc$end_emit_space_statistics, ' ', NIL, {ignore} status);
    IFEND;
    mmp$delete_scratch_segment (object_segment_pointer, {ignore} status);
    mmp$delete_scratch_segment (project_segment_pointer, {ignore} status);
    mmp$delete_scratch_segment (limit_segment_pointer, {ignore} status);

    IF number_of_errors = 0 THEN
      status.normal := TRUE;
    ELSE
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$process_storage_errors, '', status);
      osp$append_status_integer (osc$status_parameter_delimiter, number_of_errors, radix, NOT include_radix,
            status);
    IFEND;

  PROCEND pfp$process_storage;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] pfp$_update_perm_file_space_lim', EJECT ??

  PROCEDURE [XDCL] pfp$_update_perm_file_space_lim
    (    parameter_list: clt$parameter_list;
     VAR status: ost$status);

{ PURPOSE:
{   This procedure is the command processor for the update_perm_file_space_limit command.
{
{ DESIGN:
{   The design of this procedure is essentially the same as that of procedure pfp$process_storage,
{ except that it works for only one user on one family, processing options cannot be specified by
{ the caller and it does not emit statistics.

{ PROCEDURE (osm$updpfsl) update_perm_file_space_limit, updpfsl (
{   user, u: name = $job(login_user)
{   family, f: name = $job(login_family)
{   status)

?? PUSH (LISTEXT := ON) ??
?? FMT (FORMAT := OFF) ??

  VAR
    pdt: [STATIC, READ, cls$declaration_section] record
      header: clt$pdt_header,
      names: array [1 .. 5] of clt$pdt_parameter_name,
      parameters: array [1 .. 3] of clt$pdt_parameter,
      type1: record
        header: clt$type_specification_header,
        qualifier: clt$name_type_qualifier,
        default_value: string (16),
      recend,
      type2: record
        header: clt$type_specification_header,
        qualifier: clt$name_type_qualifier,
        default_value: string (18),
      recend,
      type3: record
        header: clt$type_specification_header,
      recend,
    recend := [
    [1,
    [92, 5, 27, 9, 2, 51, 915],
    clc$command, 5, 3, 0, 0, 0, 0, 3, 'OSM$UPDPFSL'], [
    ['F                              ',clc$abbreviation_entry, 2],
    ['FAMILY                         ',clc$nominal_entry, 2],
    ['STATUS                         ',clc$nominal_entry, 3],
    ['U                              ',clc$abbreviation_entry, 1],
    ['USER                           ',clc$nominal_entry, 1]],
    [
{ PARAMETER 1
    [5, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation,
  clc$standard_parameter_checking, 5, clc$optional_default_parameter, 0, 16],
{ PARAMETER 2
    [2, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation,
  clc$standard_parameter_checking, 5, clc$optional_default_parameter, 0, 18],
{ PARAMETER 3
    [3, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name],
    clc$pass_by_reference, clc$immediate_evaluation,
  clc$standard_parameter_checking, 3, clc$optional_parameter, 0, 0]],
{ PARAMETER 1
    [[1, 0, clc$name_type], [1, osc$max_name_size],
    '$job(login_user)'],
{ PARAMETER 2
    [[1, 0, clc$name_type], [1, osc$max_name_size],
    '$job(login_family)'],
{ PARAMETER 3
    [[1, 0, clc$status_type]]];

?? FMT (FORMAT := ON) ??
?? POP ??

    CONST
      p$user = 1,
      p$family = 2,
      p$status = 3;

    VAR
      pvt: array [1 .. 3] of clt$parameter_value;

    VAR
      active_family_list: ^pmt$family_name_list,
      active_family_list_count: pmt$family_name_count,
      family_found: boolean,
      family_name: ost$family_name,
      first_project_info: ^project_information,
      index1: integer,
      job_processing_phase: clt$processing_phase,
      limit_entries_array: ^array [1 .. * ] of avt$total_limit_update_record,
      limit_info_p: ^SEQ ( * ),
      limit_segment_pointer: amt$segment_pointer,
      master_catalog_path: array [1 .. 2] of pft$name,
      number_of_errors: integer,
      number_of_limit_entries: integer,
      object_index: integer,
      object_info_p: ^SEQ ( * ),
      object_segment_pointer: amt$segment_pointer,
      project_segment_pointer: amt$segment_pointer,
      sub_object_info_p: ^SEQ ( * ),
      user_identification: ost$user_identification,
      user_info: user_information,
      user_name: ost$user_name,
      validation_level: avt$validation_level;

?? NEWTITLE := 'condition_handler', EJECT ??

{ PURPOSE:
{   This is a block exit condition handler that is used to insure that the scratch segments are deleted.

    PROCEDURE condition_handler
      (    condition: pmt$condition;
           condition_information: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      VAR
        ignore_status: ost$status;

      mmp$delete_scratch_segment (object_segment_pointer, ignore_status);
      mmp$delete_scratch_segment (project_segment_pointer, ignore_status);
      mmp$delete_scratch_segment (limit_segment_pointer, ignore_status);

    PROCEND condition_handler;
?? OLDTITLE ??
*copy pfp$create_scratch_segments
?? NEWTITLE := 'process_catalog', EJECT ??

{ PURPOSE
{   This procedure processes a catalog object entry that was obtained by get object information.
{   Information is added to the user totals for the master catalog.

    PROCEDURE process_catalog
      (    catalog_path: ^pft$path;
           object_info_p: ^SEQ ( * );
       VAR status: ost$status);

      VAR
        allocated_size: integer,
        catalog_object_entry_p: ^fst$goi_object,
        catalog_object_info_p: ^fst$goi_object_information,
        catalog_reference: fst$path,
        current_catalog_index: pft$catalog_path_index,
        current_object_info_p: ^SEQ ( * ),
        number_of_files: integer,
        number_of_subcatalogs: integer,
        object_index: integer,
        object_path: ^pft$path,
        object_status: ost$status,
        path_index: pft$catalog_path_index,
        sub_object_array_p: ^fst$goi_object_list,
        sub_object_info_p: ^SEQ ( * );

?? NEWTITLE := 'process_file', EJECT ??

{ PURPOSE
{   This procedure processes a file object entry that was obtained by get object information.
{   Information is added to the user total for the master catalog.

      PROCEDURE process_file
        (    file_path: pft$path;
             file_object_entry: ^fst$goi_object;
         VAR status: ost$status);

        VAR
          allocated_size: integer,
          archive_index: integer,
          comparison_result: pmt$comparison_result,
          cycle_number_string: ost$string,
          object_index: integer;

?? NEWTITLE := 'record_file_error', EJECT ??

        PROCEDURE record_file_error
          (    error_path: ^pft$path;
               cycle_number: fst$cycle_number;
           VAR number_of_errors: integer;
           VAR status: ost$status);

          VAR
            ignore_status: ost$status,
            path: fst$path,
            recorded_status: ost$status;

          number_of_errors := number_of_errors + 1;
          fsp$build_file_ref_from_elems (error_path, path, ignore_status);
          osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$skipped_file,
                update_perm_file_space_command, recorded_status);
          osp$append_status_file (osc$status_parameter_delimiter, path, recorded_status);
          osp$append_status_integer (osc$status_parameter_delimiter, cycle_number, radix, NOT include_radix,
                recorded_status);
          osp$generate_error_message (recorded_status, ignore_status);
          osp$generate_error_message (status, ignore_status);

        PROCEND record_file_error;

?? OLDTITLE, EJECT ??

        status.normal := TRUE;

{ Process each cycle of the file.

        IF file_object_entry^.cycle_object_list <> NIL THEN

        /process_cycle/
          FOR object_index := 1 TO UPPERBOUND (file_object_entry^.cycle_object_list^) DO
            CASE file_object_entry^.cycle_object_list^ [object_index].cycle_device_class OF
            = rmc$mass_storage_device =

{ Get file size information.

              allocated_size := 0;
              IF file_object_entry^.cycle_object_list^ [object_index].cycle_device_information^.
                    mass_storage_device_info.resides_online THEN
                allocated_size := file_object_entry^.cycle_object_list^ [object_index].
                      cycle_device_information^.mass_storage_device_info.bytes_allocated;
              IFEND;

{ Since offline files are not counted toward the permanent file space limit, do not get that info.

              user_info.cycle_allocated_size := user_info.cycle_allocated_size + allocated_size;
            ELSE

{ Device type not currently reported on.

            CASEND;
          FOREND /process_cycle/;
        IFEND;

      PROCEND process_file;

?? OLDTITLE, EJECT ??

      status.normal := TRUE;

{ Allocate a path to hold the next level object.

      current_catalog_index := UPPERBOUND (catalog_path^);
      PUSH object_path: [1 .. current_catalog_index + 1];
      FOR path_index := 1 TO current_catalog_index DO
        object_path^ [path_index] := catalog_path^ [path_index];
      FOREND;

      fsp$build_file_ref_from_elems (catalog_path, catalog_reference, status);
      IF NOT status.normal THEN
        pfp$record_catalog_error (catalog_path, update_perm_file_space_command, number_of_errors, status);
        RETURN;
      IFEND;

      current_object_info_p := object_info_p;

{ Place the object information for the current catalog into the beginning of the object info sequence.

      pfp$get_object_information (catalog_reference, catalog_information_request, {validation_criteria} NIL,
            current_object_info_p, status);
      IF NOT status.normal THEN
        pfp$record_catalog_error (catalog_path, update_perm_file_space_command, number_of_errors, status);
        RETURN;
      IFEND;

{ Insert a sequence to be used for the next level of object information into the object info sequence.

      NEXT sub_object_info_p: [[REP #SIZE (current_object_info_p^) -
            i#current_sequence_position (current_object_info_p) OF cell]] IN current_object_info_p;
      RESET sub_object_info_p;

{ Retrieve the sub_object array for the current catalog.

      RESET current_object_info_p;
      NEXT catalog_object_info_p IN current_object_info_p;
      catalog_object_entry_p := catalog_object_info_p^.object;
      sub_object_array_p := catalog_object_entry_p^.subcatalog_and_file_object_list;

{ Get the catalog size information.

      allocated_size := catalog_object_entry_p^.catalog_device_information^.mass_storage_device_info.
            bytes_allocated;

{ If catalog archiving is ever implemented code must be added here.

{ Add the current catalog information to the account, project and user info.

      user_info.catalog_allocated_size := user_info.catalog_allocated_size + allocated_size;

{ Process the sub objects within the current catalog.

      IF sub_object_array_p <> NIL THEN
        FOR object_index := LOWERBOUND (sub_object_array_p^) TO UPPERBOUND (sub_object_array_p^) DO
          IF sub_object_array_p^ [object_index].object_type = fsc$goi_file_object THEN
            object_path^ [current_catalog_index + 1] := sub_object_array_p^ [object_index].file_name;
            process_file (object_path^, ^sub_object_array_p^ [object_index], object_status);
          ELSEIF sub_object_array_p^ [object_index].object_type = fsc$goi_catalog_object THEN
            object_path^ [current_catalog_index + 1] := sub_object_array_p^ [object_index].catalog_name;
            process_catalog (object_path, sub_object_info_p, object_status);
          IFEND;
        FOREND;
      IFEND;

    PROCEND process_catalog;
?? OLDTITLE ??
*copy pfp$record_catalog_error

    clp$evaluate_parameters (parameter_list, #SEQ (pdt), NIL, ^pvt, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    user_name := pvt [p$user].value^.name_value;
    family_name := pvt [p$family].value^.name_value;

    IF NOT (avp$system_administrator () OR avp$family_administrator ()) THEN
      clp$get_processing_phase (job_processing_phase, status);
      IF (NOT status.normal) OR (job_processing_phase <> clc$system_epilog_phase) THEN
        pmp$get_user_identification (user_identification, status);
        IF (NOT status.normal) OR (user_identification.user <> user_name) OR (user_identification.family <>
              family_name) THEN
          osp$set_status_condition (ave$insufficient_authority, status);
          RETURN;
        IFEND;
      IFEND;
    IFEND;

    validation_level := avp$validation_level ();
    number_of_limit_entries := 0;
    number_of_errors := 0;

{ Get the list of active families for this system.

    active_family_list_count := 100;
    REPEAT
      PUSH active_family_list: [1 .. active_family_list_count];
      pmp$get_family_names (active_family_list^, active_family_list_count, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
    UNTIL active_family_list_count <= UPPERBOUND (active_family_list^);

    family_found := FALSE;

{ Verify that the specified family is a valid active family.

  /search_loop/
    FOR index1 := 1 TO active_family_list_count DO
      IF active_family_list^ [index1] = family_name THEN
        family_found := TRUE;
        EXIT /search_loop/;
      IFEND;
    FOREND /search_loop/;
    IF NOT family_found THEN
      osp$set_status_abnormal ('PF', pfe$unknown_family, family_name, status);
      RETURN;
    IFEND;

    pfp$create_scratch_segments (object_segment_pointer, project_segment_pointer, limit_segment_pointer,
          status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    #SPOIL (object_segment_pointer, project_segment_pointer, limit_segment_pointer);

{ Set up the catalog information request one time which will be used by process catalog
{ for each getoi request.

    osp$establish_block_exit_hndlr (^condition_handler);

    RESET object_segment_pointer.sequence_pointer;
    object_info_p := object_segment_pointer.sequence_pointer;
    RESET limit_segment_pointer.sequence_pointer;
    limit_info_p := limit_segment_pointer.sequence_pointer;

    RESET object_info_p;

    user_info.catalog_allocated_size := 0;
    user_info.catalog_online_eoi_size := 0;
    user_info.catalog_count := 0;
    user_info.cycle_allocated_size := 0;
    user_info.cycle_online_eoi_size := 0;
    user_info.cycle_offline_eoi_size := 0;
    user_info.cycle_offline_total_eoi_size := 0;
    user_info.cycle_count := 0;

    master_catalog_path [1] := family_name;
    master_catalog_path [2] := user_name;

{ Process catalog will recursively loop through the catalog structure.

    process_catalog (^master_catalog_path, object_info_p, status);
    IF status.normal THEN
      avp$change_user_pf_space_limit (family_name, ^user_info.cycle_allocated_size, user_name, status);
    IFEND;

    osp$disestablish_cond_handler;

    mmp$delete_scratch_segment (object_segment_pointer, {ignore} status);
    mmp$delete_scratch_segment (project_segment_pointer, {ignore} status);
    mmp$delete_scratch_segment (limit_segment_pointer, {ignore} status);

    IF number_of_errors = 0 THEN
      status.normal := TRUE;
    ELSE
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$process_storage_errors, '', status);
      osp$append_status_integer (osc$status_parameter_delimiter, number_of_errors, radix, NOT include_radix,
            status);
    IFEND;

  PROCEND pfp$_update_perm_file_space_lim;
?? OLDTITLE ??

MODEND pfm$process_storage;
