







        Утвержден 

        2б.00152-01 97 09-1-ЛУ















                      Операционная система МОС ВП 

                    Подсистема разработки программ

               Руководство по программированию в системе

                          Справочный материал 

                         2б.00152-01 97 09-2

                             Листов 216/4К























                                 1987 

           Перв. Примен.                            Литера O
           2б.00152-01

 
                                - 7K -

                          2б.00152-01 97 09-2


      3.3.8.  Рассылка сообщений                              193

      3.4.  Системные  служебные   процедуры   SYS$QIO   и

            SYS$QIOW                                          196

      3.4.1.  Операции чтения                                 197

      3.4.2.  Операции записи                                 203

      3.4.3.  Проверка типа устройства                        206

      3.4.4.  Характеристики терминала                        208

      3.5.  Терминаторы (ограничители) записей                211


      Перечень ссылочных документов                           212К










































 
                                - 212K -

                          2б.00152-01 97 09-2





                     Перечень ссылочных документов




      1. Операционная система МОС ВП.  Подсистема  исполнительной 

         библиотеки. Руководство программиста. 2б.00152-01 33 09. 

      2. Операционная система МОС ВП. Подсистема системного  сер- 

         виса. Интерфейсные программы. Руководство  программиста. 

         2б.00152-01 33 02. 

      3. Операционная система МОС ВП. Подсистема управления. Диа- 

         логовый   командный   язык.    Руководство  пользователя.

         2б.00152-01 92 19. 

      4. Операционная система МОС ВП. Подсистема системного  сер- 

         виса. Программы системного обслуживания. Справочный  ма- 

         териал. 2б.00152-01 97 06. 

      5. Операционная система МОС ВП. Подсистема системного  сер- 

         виса. Управление  вводом-выводом.  Руководство  програм- 

         миста.  2б.00152-01 33 03. 

      6. Операционная система МОС ВП. Подсистема разработки прог- 

         рамм. Программа сортировки и слияния. Руководство прог-

         раммиста. 2б.00152-01 33 06. 


















 
                              - 213K -

                         2б.00152-01 97 09-2

    ----------------------------------------------------------- 
    I               Лист регистрации изменений                I 
    ----------------------------------------------------------- 
    I Номера листов (страниц)IВсего I       IВходяшийI    I   I 
    I------------------------IлистовI    N  I    N   I    IДа-I 
    I   IИз- IЗа- I   IАнну- I(стра-I       Iсопрово-IподпIта I 
    IИзмIме- Iме- IНо-Iлиро- Iниц) вIдокуменIдительноI    I   I 
    I   Iнен-Iнен-IвыхIванныхIдокум.I   та  Iго докумI    I   I 
    I   Iных Iных I   I      I      I       I и дата I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 
    I   I    I    I   I      I      I       I        I    I   I 
    ----------------------------------------------------------- 



 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
                      Операционная система МОС ВП
 
                    Подсистема разработки программ
 
               Руководство по программированию в системе
 
                          Справочный материал
 
                           00152-01 97 09-2
 
                              Листов 212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
                                 1987
 


                                - 2 -
 
                          00152-01 97 09-2
 
 
 
 
 
 
                              СОДЕРЖАНИЕ
 
 
 
 
 
      1.  Структуры данных                                    8
 
      1.1.  Определение элементов данных                      8
 
      1.2.  Инициализация переменных                          11
 
      1.3.  Числовые данные                                   12
 
      1.3.1.  Байты                                           12
 
      1.3.2.  Целые числа                                     13
 
      1.3.3.  Действительные (вещественные) числа             14
 
      1.3.4.  Комплексные числа                               16
 
      1.4.  Числовые операции                                 17
 
      1.4.1.  Арифметические операции                         17
 
      1.4.2.  Операции отношения                              18
 
      1.4.3.  Системные арифметические подпрограммы           19
 
      1.4.4.  Арифметические ошибки                           21
 
      1.5.  Логические данные                                 22
 
      1.6.  Символьные данные                                 24
 
      1.6.1.  Определение строки символов                     24
 
      1.6.2.  Символьные константы                            25
 
      1.6.3.  Ссылка на строку символов                       26
 
      1.6.4.  Операция со строкой символов                    26
 
      1.6.4.1.  Заполнение и усечение                         28
 
      1.6.4.2.  Соединение строк (конкатенация)               28
 
      1.6.4.3.  Встроенные функции                            29
 
      1.6.5.  Специальные символы                             29
 
      1.6.6.  Подсчитанные строки                             30
 
      1.7.  Данные без об'явления типа                        31


                                - 3 -
 
                          00152-01 97 09-2
 
 
 
      1.7.1.  Константы без об'явления типа                   31
 
      1.7.1.1.  Шестнадцатеричные константы                   32
 
      1.7.1.2.  Восьмеричные константы                        34
 
      1.7.1.3.  Холлеритовские константы                      35
 
      1.7.2.  Работа с битами                                 36
 
      1.8.  Преобразования типов данных                       37
 
      1.8.1.  Числовые преобразования                         37
 
      1.8.2.  Форматные преобразования                        39
 
      1.8.3.  Автоматическое преобразование                   41
 
      1.9.  Массивы                                           42
 
      1.9.1.  Определение массивов                            42
 
      1.9.2.  Ссылка на массивы                               43
 
      1.9.2.1.  Циклическая обработка массивов                44
 
      1.9.2.2.  Обработка полного массива                     45
 
      1.9.3.  Память и границы массивов                       46
 
      1.9.4.  Многомерные массивы                             48
 
      1.9.4.1.  Размещение многомерных массивов в памяти      48
 
      1.9.4.2.  Обработка многомерных массивов                50
 
      1.10. Записи                                            52
 
      1.10.1. Определение структуры записи                    54
 
      1.10.1.1. Данные с указателем типа                      55
 
      1.10.1.2. Подструктуры                                  56
 
      1.10.1.3. Об'единения                                   57
 
      1.10.2. Об'явление записи                               60
 
      1.10.3. Ссылка на записи                                61
 
      1.10.4. Хранение в памяти структур записей              63
 
      1.11. Системная информация                              65
 
      1.11.1. Статистика от таймера                           65
 
      1.11.2. Системное время                                 70
 


                                - 4 -
 
                          00152-01 97 09-2
 
 
      1.11.2.1. Текущее время                                 71
 
      1.11.2.2. Работа со значениями времени                  72
 
 
 
      2.  Ввод и синтаксический анализ команд                 75
 
      2.1.  Описание команды                                  75
 
      2.1.1.  Имя и образ команды                             75
 
      2.1.2.  Параметры                                       77
 
      2.1.2.1.  Метки                                         77
 
      2.1.2.2.  Обязательные параметры                        78
 
      2.1.2.3.  Значения                                      79
 
      2.1.2.4.  Подсказка                                     80
 
      2.1.3.  Квалификаторы                                   80
 
      2.1.3.1.  Квалификаторы с отрицанием                    81
 
      2.1.3.2.  Квалификаторы по умолчанию                    82
 
      2.1.3.3.  Расположение квалификатора                    82
 
      2.1.3.4.  Значения                                      83
 
      2.1.4.  Типы значений                                   84
 
      2.1.4.1.  Встроенные типы                               84
 
      2.1.4.2.  Ключевые слова                                85
 
      2.1.5.  Запрещение об'ектов и сочетаний                 86
 
      2.1.6.  Синтаксические изменения                        87
 
      2.1.7.  Пути ключевых слов и определений                88
 
      2.1.7.1.  Пути ключевых слов                            89
 
      2.1.7.2.  Пути определения                              89
 
      2.2.  Установка команд                                  91
 
      2.2.1.  Таблица команд процесса                         91
 
      2.2.2.  Таблица команд DCL                              92
 
      2.2.3.  Таблица команд пользователя                     92
 
      2.2.3.1.  Копирование таблицы команд DCL                93
 
      2.2.3.2.  УстановкA таблицы пользователя                93


                                - 5 -
 
                          00152-01 97 09-2
 
 
 
      2.2.3.3.  Создание таблицы заново                       94
 
      2.2.4.  Удаление команд                                 95
 
      2.3.  Синтаксический анализ                             96
 
      2.3.1.  Проверка наличия элементов                      96
 
      2.3.2.  Прием значений элементов                        100
 
      2.3.3.  Определение положения квалификатора             103
 
      2.3.4.  Анализ командной строки и имени команды         106
 
      2.4.  Подкоманды и внутренний разбор                    106
 
      2.4.1.  Определение подкоманд                           107
 
      2.4.2.  Разбор подкоманд                                108
 
      2.4.3.  Диспетчеризация подпрограмм                     113
 
      2.4.4.  Возвращение к вызывающей команде                116
 
 
 
      3.  Пользовательский ввод-вывод                         116
 
      3.1.  Диалоговый ввод-вывод                             118
 
      3.1.1.  Выбор устройства                                118
 
      3.1.1.1.  Неявные устройства ввода-вывода               119
 
      3.1.1.2.  Явные устройства ввода-вывода                 121
 
      3.1.2.  Получение записи с устройства ввода             122
 
      3.1.2.1.  Процедура LIB$GET_INPUT                       123
 
      3.1.2.2.  Оператор ACCEPT                               124
 
      3.1.2.3.  Оператор READ                                 125
 
      3.1.3.  Вывод данных                                    131
 
      3.1.3.1.  Процедура LIB$PUT_OUTPUT                      131
 
      3.1.3.2.  Оператор WRITE                                132
 
      3.1.3.3.  Форматирование числового вывода               133
 
      3.1.3.4.  Вывод нескольких записей                      134
 
      3.1.3.5.  Оператор TYPE                                 135
 
      3.1.3.6.  Обработка массивов и записей                  135
 


                                - 6 -
 
                          00152-01 97 09-2
 
 
      3.1.3.7.  Символы управления кареткой                   137
 
      3.2.  Управление экраном                                138
 
      3.2.1.  Макеты                                          141
 
      3.2.2.  Виртуальные экспозиции                          143
 
      3.2.2.1.  Создание виртуальной экспозиции               143
 
      3.2.2.2.  Вставка виртуальной экспозиции в макет        144
 
      3.2.2.3.  Перегруппировка виртуальных экспозиций        147
 
      3.2.2.4.  Удаление виртуальной эспозиции из макета      148
 
      3.2.2.5.  Модификация виртуальной экспозиции            149
 
      3.2.3.  Запись                                          151
 
      3.2.3.1.  Позиционирование курсора                      151
 
      3.2.3.2.  Посимвольная запись данных                    152
 
      3.2.3.3.  Построчная запись данных                      154
 
      3.2.3.4.  Вычерчивание линий                            155
 
      3.2.3.5.  Удаление текста                               157
 
      3.2.4.  Чтение                                          158
 
      3.2.4.1.  Чтение из экспозиции                          159
 
      3.2.4.2.  Чтение с виртуальной клавиатуры               160
 
      3.2.4.3.  Чтение со вспомогательной клавиатуры          162
 
      3.2.4.4.  Составной ввод                                167
 
      3.2.5.  Управление обновлениями экрана                  171
 
      3.2.6.  Соглашения по использованию макетов             171
 
      3.3.  Специальные операции ввода-вывода                 174
 
      3.3.1.  Прерывания CTRL/C и CTRL/Y                      175
 
      3.3.2.  Незапланированный ввод                          181
 
      3.3.3.  Буфер предварительного ввода                    185
 
      3.3.4.  Эхо-отображение                                 187
 
      3.3.5.  Тайм-аут (время задержки ответа)                189
 
      3.3.6.  Преобразование нижнего регистра в верхний       191
 
      3.3.7.  Редактирование строки и управляющие действия    192


                                - 7 -
 
                          00152-01 97 09-2
 
 
 
      3.3.8.  Рассылка сообщений                              193
 
      3.4.  Системные служебные процедуры SYS$QIO и SYS$QIOW  196
 
      3.4.1.  Операции чтения                                 197
 
      3.4.2.  Операции записи                                 203
 
      3.4.3.  Проверка типа устройства                        206
 
      3.4.4.  Характеристики терминала                        208
 
      3.5.  Терминаторы (ограничители) записей                211
 
 
 
      Перечень ссылочных документов                           212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


                                - 8 -
 
                          00152-01 97 09-2
 
 
                         1. Структуры данных
 
 
 
           В языке программирования фортран (в дальнейшем -  язык
 
      фортран) предоставляется  возможность  определять  элементы
 
      д6+ных, используемых в программе, и связывать их с символи-
 
      ческими именами (идентификаторами), по которым на них можно
 
      ссылаться. Элементы данных  могут  быть  числовыми  (целые,
 
      вещественные, комплексные числа), символьными или  логичес-
 
      кими. Кроме того, целые можно рассматривать как  логические
 
      или не имеющие типа.
 
 
 
           1.1. Определение элементов данных
 
 
           Определить элемент данных - это значит создать некото-
 
      рый об'ект и задать его характеристики. Ссылка  на  элемент
 
      данных есть использование этого об'екта. Язык фортран  поз-
 
      воляет определить и использовать следующие элементы данных:
 
           1) переменные. Элементы данных, которые могут изменить
 
      значение в процессе выполнения программы, называются  пере-
 
      менными. Оператор явного определения переменной имеет вид:
 
      [тип] символическое_имя [,...]
 
           В документе [1] описываются разрешенные  типы  данных;
 
      символическое имя (идентификатор)  состоит  не  более,  чем
 
      из 31 символа - алфавитно-цифровых, знаков денежной единицы
 
      и подчеркивания. Если описывается  не  системноопределенный
 
      идентификатор, то символическое имя допустимо любое.  Боль-
 
      шинство системноопределенных идентификаторов содержат  знак
 
      денежной единицы; отказ от применения этого символа в обыч-
 
      ных идентификаторах позволит избежать  ошибочного  двойного
 
      определения идентификатора. Любое  число  данных  одного  и
 


                                - 9 -
 
                          00152-01 97 09-2
 
 
      того же типа можно  определить  одним  оператором  описания
 
      типа,  разделяя  символические  имена  запятыми.  Например,
 
      определяется  целая  переменная  с   символическим   именем
 
      TOTAL_HOUSES:
 
           INTEGER TOTAL_HOUSES
 
           Оператор описания типа должен находится в описательной
 
      части программной единицы. В выполняемой части  программной
 
      единицы переменные можно  определять  неявно,  ссылаясь  на
 
      неопределенный идентификатор.  Такая  ссылка  по  умолчанию
 
      определяет целую переменную для  идентификаторов,  начинаю-
 
      щихся с букв I, J, K, L, M и N, и  вещественную  переменную
 
      для идентификаторов, начинающихся с других букв. С  помощью
 
      оператора IMPLICIT можно изменить это умолчание или  вообще
 
      запретить неявное определение переменных. Например, если  I
 
      не определена явно, то она неявно определяется как целая:
 
      DO I = 1, TOTAL_HOUSES
 
           2) константы. Элементы данных с неизменными значениями
 
      называются константами. Константа определяется неявно ссыл-
 
      кой на ее значение. Обычно никакое символическое имя с  ней
 
      не связано. Например, в правой части оператора присваивания
 
      определяется константа со значением 1:
 
          STATUS = 1
 
      константе можно присвоить символическое имя с помощью  опе-
 
      ратора PARAMETER, который должен находиться в  описательной
 
      части программной единицы. Например, этим оператором  опре-
 
      деляются две константы типа INTEGER:
 
      INTEGER STATUS_OK,
 
      2       IO_OK
 
      PARAMETER (STATUS_OK = 1,
 
      2       IO_OK = 0)


                                - 10 -
 
                          00152-01 97 09-2
 
 
      при присвоении константе символического имени тип константы
 
      определяется оператором описания типа. Если такой  оператор
 
      не задан, то тип константы  определяется  как  неявный  тип
 
      символического имени;
 
           3) ссылки на функцию. При выполнении функции ее  вызов
 
      заменяется значением.  Например,  встроенная  функция  SQRT
 
      вычисляет квадратный  корень  из  заданного  ей  параметра,
 
      используется значение 9.0:
 
      SQRT(81.0)
 
           Для всех функций, за исключением встроенных, тип функ-
 
      ции должен быть задан в операторе описания  типа,  как  для
 
      переменных или констант, иначе будет воспринят неявный тип.
 
      Неявные типы для функций указаны в [1];
 
           4) выражения. Выражения - это значения,  получаемые  с
 
      помощью знаков арифметических операций. Значения могут быть
 
      любыми сочетаниями переменных, констант, ссылок на функции,
 
      или других выражений. Например, значения переменной и конс-
 
      танты соединены знаком операции сложения:
 
      TOTAL_HOUSES + 1
 
           Соединяемые значения должны иметь один и тот  же  тип,
 
      либо должны быть приводимыми к одному типу - нельзя, напри-
 
      мер, складывать символьное значение с  числовым.  Во  время
 
      выполнения программы  выражение  вычисляется  и  заменяется
 
      одним значением.
 


                                - 11 -
 
                          00152-01 97 09-2
 
 
           1.2. Инициализация переменных
 
 
           Оператор присваивания может появляться только в выпол-
 
      няемой части программной единицы, и предназначен для  зада-
 
      ния значений переменных. Общий вид оператора:
 
      переменная = значение
 
      Значение (правая часть оператора присваивания)  может  быть
 
      переменной, константой, ссылкой на функцию или  выражением.
 
      Значение и переменная должны иметь один и тот же тип,  либо
 
      тип значения должен быть преобразуем к типу  переменной.  В
 
      процессе выполнения программы переменная принимает значение
 
      правой части оператора присваивания.
 
           Примеры:
 
           1. TOTAL_HOUSES = 1
 
      TOTAL_HOUSES принимает целое значение 1.
 
           2. TOTAL_HOUSE = TOTAL_HOUSES + 1
 
      значение TOTAL_HOUSES увеличивается на 1.
 
           3. AVG_PERSONS_HOUSE = PERSONS/TOTAL_HOUSES
 
      значение переменной PERSONS, деленное на значение  перемен-
 
      ной      TOTAL_HOUSES,       присваивается       переменной
 
      AVG_PERSONS_HOUSE.
 
           4. ROOT = SQRT (BASE)
 
      квадратный корень из значения переменной BASE присваивается
 
      переменной ROOT.
 
           Автоматическая инициализация переменных в языке  форт-
 
      ран не предусмотрена (статические переменные имеют  началь-
 
      ное значение 0, однако на это не стоит полагаться). Инициа-
 
      лизировать переменную  можно,  поместив  отделенную  косыми
 
      чертами константу немедленно после имени переменной в  опе-
 
      раторе описания типа. Например, переменная LINES  инициали-
 


                                - 12 -
 
                          00152-01 97 09-2
 
 
      зируется значением 1:
 
           INTEGER LINES/1/
 
      можно и по-другому: назвать переменную  в  операторе  DATA,
 
      который должен помещаться в описательной части  программной
 
      единицы, например:
 
           INTEGER LINES
 
           DATA LINES/1/
 
           Инициализация данных операторами DATA и описания  типа
 
      происходит только один раз - при запуске программы. В част-
 
      ности, локальные переменные подпрограмм не инициализируются
 
      при каждом вызове подпрограммы, а сохраняют  свои  значения
 
      от прошлых вызовов. Кроме того, в подпрограмме нельзя  при-
 
      менять оператор DATA для инициализации переменных,  переда-
 
      ваемых в подпрограмму в качестве фактических  параметров  и
 
      переменных, находящихся в блоке общей памяти,  определенном
 
      другой программной единицей.
 
 
 
           1.3. Числовые данные
 
 
           Элементарное числовое данное - это  об'ект,  состоящий
 
      из 1, 2, 4, 8 или 16 байтов,  трактуемый  как  единственное
 
      число. С данными типа LOGICAL также можно  работать  как  с
 
      числовыми.
 
 
 
           1.3.1. Байты
 
 
           Тип BYTE определяет переменную, занимающую  один  байт
 
      памяти. Байтовая переменная может  принимать  целочисленное
 
      значение (в диапазоне от минус 128 до 127)  или  логическое
 
      значение (эквивалентно LOGICAL*1).
 


                                - 13 -
 
                          00152-01 97 09-2
 
 
           1.3.2. Целые числа
 
 
           Целое - это положительное или отрицательное целое чис-
 
      ло. Типы целых таковы:
 
           1) INTEGER*4 - определяет целое значение  в  диапазоне
 
      от минус 2147483648 до 2147483647. Значение типа INITEGER*4
 
      занимает черыре байта памяти;
 
           2) INTEGER*2 - определяет целое значение  в  диапазоне
 
      от минус 32768 до 32767. Значение типа  INTEGER*2  занимает
 
      два байта памяти;
 
           3) INTEGER -  решение  о  типе  принимается  во  время
 
      трансляции.  Если  программная  единица  транслируется  (по
 
      команде FORTRAN) с квалификатором /I4  (принят  по  умолча-
 
      нию), то INTEGER означает INTEGER*4.  Если  же  программная
 
      единица транслируется с квалификатором  /NOI4,  то  INTEGER
 
      означает INTEGER*2.
 
           Целая константа задается целым (без десятичной  точки)
 
      числом, которому может предшествовать знак плюс или  минус.
 
      Число без знака рассматриваEтся как  положительное.  Напри-
 
      мер, символическое имя TOTAL_HOUSES определяется как  целое
 
      и ему присваивается значение целой константы 1:
 
      !описательная часть программы
 
      INTEGER TOTAL_HOUSES
 
      !выполняемая часть программы
 
      TOTAL_HOUSES = 1
 
           Для определения целых более чем в четыре байта (напри-
 
      мер,  целое  квадрослово  для  хранения  времени   системы)
 
      используется массив целых.
 


                                - 14 -
 
                          00152-01 97 09-2
 
 
           1.3.3. Действительные (вещественные) числа
 
 
           Действительные числа - это  числа,  положительные  или
 
      отрицательные, с десятичной точкой или  порядком.  Машинные
 
      инструкции для работы с действительными числами реализованы
 
      аппаратно или  эмулируются  программным  обеспечением  -  в
 
      зависимости от конфигурации аппаратуры.
 
           Типы действительных чисел таковы:
 
           1) REAL и REAL*4 - определяет вещественное число, оди-
 
      нарной точности в диапазоне (для  положительных  чисел)  от
 
      .29*10**(-38) до 1.7*10**(38), но задается с точностью око-
 
      ло семи знаков. Значения, выходящие за пределы семи знаков,
 
      округляются. Значение типа REAL*4 хранится в формате с пла-
 
      вающей запятой F_, занимая четыре байта памяти;
 
           2) REAL*8 и DOUBLE PRECISION - определяет действитель-
 
      ное число с двойной точностью в формате с плавающей запятой
 
      D_ (по  умолчанию)  или  G_  (если  при  трансляции  задано
 
      /G_FLOATING). Число с двойной точностью в формате с плаваю-
 
      щей запятой D_ имеет тот же диапазон значений, что и  число
 
      с одинарной точностью (REAL*4), но  с  существенно  большей
 
      точностью - около 16 знаков. Число с  двойной  точностью  в
 
      формате   G_   имеет   больший   диапазон   значений    (от
 
      .56*10**(-308) до .9*10**(308)), но меньшую точность  (око-
 
      ло 15 знаков), чем число с двойной точностью  в  формате  с
 
      плавающей запятой D_. Значение REAL*8, хранимое в формате с
 
      плавающей запятой D_ или в формате с плавающей запятой  G_,
 
      занимает восемь байтов памяти;
 
           3) REAL*16  -  определяет  число  четверной  точности,
 
      которое  может   принимать   значения   из   диапазона   от
 
      .84*10**(-4932) до .9*10**(4932) с точностью около 33  зна-
 


                                - 15 -
 
                          00152-01 97 09-2
 
 
      ков. Значение REAL*16 хранится в формате с плавающей  запя-
 
      той H_, занимая 16 байтов памяти.
 
           Действительные константы задаются в виде:
 
      [знак] мантисса E [знак] порядок - REAL*4 ;
 
      [знак] мантисса D [знак] порядок - REAL*8 ;
 
      [знак] мантисса Q [знак] порядок - REAL*16 .
 
           Мантисса может быть целым числом с десятичной  точкой.
 
      Порядок должен быть целым числом. Значение  константы  есть
 
      мантисса, умноженная на 10  в  степени  порядок.  Например,
 
      2.1E3 означает 2.1*10**(3), или 2100. Всей константе  может
 
      предшествовать знак плюс (ставится по умолчанию)  или  знак
 
      минус, обозначая этим положительное или отрицательное  чис-
 
      ло, например, -2.1E3 означает  минус  2100.  Порядку  также
 
      может предшествовать знак плюс (по  умолчанию)  или  минус,
 
      обозначая  этим  положительные  или  отрицательные  степени
 
      десяти. Например, 2.1E-3 означает 2.1*10**(-3), или 0.0021.
 
           Константу типа REAL*4 можно задать числом с десятичной
 
      точкой, которому может предшествовать знак плюс или  минус.
 
      Десятичную точку необходимо ставить и тогда, когда число не
 
      дробное - например, 1 задается как 1.0. Целое  число  будет
 
      целой константой. Число без знака считается  положительным.
 
      Например, символическое  имя  ROOT_ADD_1  определяется  как
 
      действительное число (в формате с  плавающей  запятой  F_),
 
      ему  присваивается  значение  (корень  квадратный  из   81)
 
      плюс 1, причем константы задаются как действительные числа:
 
      !описательная часть программы;
 
      REAL ROOT_ADD_1
 
      !выполняемая часть программы;
 
      ROOT_ADD_1 = SQRT (81.0) + 1.0
 


                                - 16 -
 
                          00152-01 97 09-2
 
 
           1.3.4. Комплексные числа
 
 
           Комплексные числа состоят из действительной  и  мнимой
 
      частей. Типы комплексных чисел таковы:
 
           1) COMPLEX и COMPLEX*8 - определяется комплексное чис-
 
      ло так, что его  действительная  и  мнимая  часть  являются
 
      вещественными числами одинарной точности (REAL*4). Значение
 
      COMPLEX*8 хранится в виде  двух  чисел  одинарной  точности
 
      формата с плавающей запятой F_, занимая восемь байтов памя-
 
      ти;
 
           2) COMPLEX*16 - определяет комплексное число,  исполь-
 
      зуя для действительной и мнимой части числа с двойной  точ-
 
      ностью (REAL*8). Значение типа COMPLEX*16 хранится  в  виде
 
      двух чисел в формате с плавающей запятой D_ (или  G_,  если
 
      при трансляции было задано /G_FLOATING), занимая 16  байтов
 
      памяти.
 
           Комплексная константа задается в виде:
 
      (вещественная_часть, мнимая_часть)
 
      Вещественная и мнимая части могут  быть  вещественными  или
 
      целыми константами в любых  сочетаниях.  Однако,  включение
 
      константы типа REAL*8 в качестве любой из компонент опреде-
 
      лит константу типа COMPLEX*16, а  не  COMPLEX*8;  включение
 
      константы типа REAL*16 в качестве  любой  из  компонент  не
 
      допускается. Например, (1.3,-1) определяет  константу  типа
 
      COMPLEX*8,   а   (1.3D0,-1)   определяет   константу   типа
 
      COMPLEX*16.
 


                                - 17 -
 
                          00152-01 97 09-2
 
 
           1.4. Числовые операции
 
 
           Числовые выражения дают возможность сочетать  числа  и
 
      сравнивать их с помощью специальных операций.  Кроме  того,
 
      множество операций расширяется за счет  использования  сис-
 
      темно-определенных  процедур  и  встроенных  функций  языка
 
      фортран.
 
 
 
           1.4.1. Арифметические операции
 
 
      В языке фортран предусмотрены следующие арифметические опе-
 
      рации:  возведение  в  степень (**),  умножение (*),  деле-
 
      ние  (/),  сложение ( + ) и  вычитание (-). Операцию указы-
 
      вают, помещая знак операции в выражении между двумя  значе-
 
      ниями (знаки плюс и  минус  можно  использовать  как  знаки
 
      унарных операций для обозначения  положительных  и  отрица-
 
      тельных значений). Значения могут быть числовыми переменны-
 
      ми, константами, вызовами функций и выражениями.
 
           В одном выражении можно задать произвольное число опе-
 
      раций. Эти операции выполняются  в  обычном  арифметическом
 
      порядке: сначала возведение в степень,  затем  умножение  и
 
      деление, затем сложение  и  вычитание.  В  первую,  очередь
 
      выполняются операции, заключенные в скобки. Если есть  пары
 
      скобок, вложенные друг в друга, то в первую очередь  выпол-
 
      няются операции в самых внутренних скобках. Операции одного
 
      уровня выполняются слева направо, кроме возведений  в  сте-
 
      пень, которые выполняются справа налево.
 
           Примеры:
 
           1.TOTAL_HOUSES + 1
 
      Значение TOTAL_HOUSES увеличивается на 1.
 
           2.PERSONS(1) /TOTAL_HOUSES


                                - 18 -
 
                          00152-01 97 09-2
 
 
      Значение первого элемента массива PERSONS делится на значе-
 
      ние TOTAL_HOUSES.
 
           3.SQRT(J)*I + 1
 
      Корень квадратный из J (SQRT(J) есть вызов  функции)  умно-
 
      жается на значение I и к результату прибавляется 1.
 
           4. SQRT(J)*(I + 1)
 
      Корень квадратный из J умножается на сумму значений I и 1.
 
           5. 3 + 3**2 + 4*2
 
      Вычисляется тройка во второй степени, затем 4*2 = 8; 3 +  9
 
      + 8 = 20 .
 
           6. 2**3**2
 
      Вычисляется: тройка во второй степени, затем двойка в девя-
 
      той степени; результат равен 512.
 
           7. (3 + 3)**2 + 4*2
 
      Вычисляется: 3 + 3 = 6; шесть в квадрате, затем 4*2 = 8; 36
 
      + 8 = 44 .
 
           8. 3 + 3**(2 + 4)*2
 
      Вычисляется: 2 + 4 = 6;  тройка  в  шестой  степени,  затем
 
      729*2 = 1458; 1458 + 3 = 1461 .
 
           9. (3 + (3**2 + 4))*2
 
      Вычисляется: тройка в квадрате, затем 9 + 4 = 13;  13  +  3
 
      = 16; 16*2 = 32 .
 
 
 
           1.4.2. Операции отношения
 
 
           Для сравнения значений в языке  фортран  предусмотрены
 
      операции  "равно"  (.EQ.),  "не  равно"  (.NE.),   "меньше"
 
      (.LT.), "меньше или равно" (.LE.), "больше" (.GT.) и "боль-
 
      ше или равно" (.GE.). Сравнение основано на числовых значе-
 
      ниях. Для сравнения знак операции сравнения помещается меж-
 


                                - 19 -
 
                          00152-01 97 09-2
 
 
      ду двумя значениями. Значения могут быть переменными, конс-
 
      тантами, вызовами функций или выражениями. Результатом опе-
 
      рации отношения является логическое значение "истина"  если
 
      отношение справедливо, и логическое значение "ложь" в  про-
 
      тивном случае.
 
           В выражениях операции отношения и арифметические  опе-
 
      рации могут встречаться  одновременно.  Операции  отношения
 
      имеют ранг более низкий, чем у всех  арифметических  опера-
 
      ций. Как правило, сравниваются два выражения или  элемента,
 
      стоящие по разные стороны операции отношения (этот  порядок
 
      действий может быть изменен с помощью скобок).
 
           Примеры:
 
           1. TOTAL HOUSES .EQ. 0
 
      Истина, если значение TOTAL_HOUSES равно нулю.
 
           2. IOSTAT .NE. IO_OK
 
      Истина, если IOSTAT не равно IO_OK.
 
           3. HOUSE_NO .GT. MAX_STATS
 
      Истина, если значение HOUSE_NO больше значения MAX_STATS.
 
           4. HOUSE_NO + 1 .GT. MAX_STAT
 
      Истина, если сумма значений HOUSE_NO и 1 больше значения
 
      MAX_STAT.
 
           5. 3**2 .EQ. 3*3
 
      Истина.
 
 
 
           1.4.3. Системные арифметические подпрограммы
 
 
           В табл. 1 приведены встроенные функции  языка  фортран
 
      для выполнения арифметических операций.
 


                                - 20 -
 
                          00152-01 97 09-2
 
 
                                                        Таблица 1
 
      -----------------------------------------------------------
                   !
         Функция   !                Назначение
                   !
      -----------------------------------------------------------
                   !
       ABS         !     Абсолютная величина
                   !
       ACOS        !     Арккосинус
                   !
       ASIN        !     Арксинус
                   !
       ATAN        !     Арктангенс
                   !
       ATAN2       !     Арктангенс A1/A2
                   !
       COS         !     Косинус
                   !
       COSH        !     Косинус гиперболический
                   !
       DIM         !     Положительная разность
                   !
       EXP         !     Экспонента
                   !
       LOG         !     Натуральный логарифм
                   !
       LOG10       !     Десятичный логарифм
                   !
       MAX         !     Максимальное значение в списке
                   !
       MIN         !     Минимальное значение в списке
                   !
       MOD         !     Остаток
                   !
       RAN         !     Псевдослучайное число
                   !
       SIGN        !     Передача знака (функция SIGNUM)
                   !
       SIN         !     Синус
                   !
       SINH        !     Синус гиперболический
                   !
       SQRT        !     Корень квадратный
                   !
       TAN         !     Тангенс
                   !
       TANH        !     Тангенс гиперболический
 
 
           Типы данных языка фортран позволяют применять  арифме-
 
      тические  операции  к  однобайтным  целым  (тип  BYTE   или
 
      LOGICAL*1),  однословным  (INTEGER*2)   или   длиннословным
 
      (INTEGER*4). Для сложения и вычитания целых, имеющих  длину
 


                                - 21 -
 
                          00152-01 97 09-2
 
 
      в квадрослове  или  другую  длину,  используются  процедуры
 
      LIB$ADDX и LIB$SUBX, описанные в [2].
 
 
 
           1.4.4. Арифметические ошибки
 
 
           МОС ВП опознает и диагностирует на этапе прогона ариф-
 
      метические ошибки (при использовании констант некоторые  из
 
      этих ошибок опознаются еще на этапе  трансляции),  перечис-
 
      ленные в табл. 2.
 
                                                        Таблица 2
 
      -----------------------------------------------------------
                                !
               Ошибка           !         Идентификатор
                                !
      -----------------------------------------------------------
                                !
        Целое переполнение      !    SS$_INTOVF
                                !
        Целое деление на 0      !    SS$_INTDIV
                                !
        Плавающее переполнение  !    SS$_FLTOVF_F
                                !
        Плавающее переполнение  !    SS$_FLTUND_F
                                !
        снизу                   !
                                !
        Плавающее деление на 0  !    SS$_FLTDIV_F
                                !
        Десятичное переполнение !    SS$_DECOVF
                                !
        Неопределенная          !    SS$_ARTRES
 
 
           Управление сигнализацией о целом  переполнении и пере-
 
      полнении снизу с плавающей запятой задается любым из квали-
 
      фикаторов /CHECK = [NO]OVERFLOW или  /CHECK = [NO]UNDERFLOW
 
      во время трансляции. По умолчанию ошибки переполнения снизу
 
      с плавающей запятой не фиксируются, а ошибки  целого  пере-
 
      полнения фиксируются.
 


                                - 22 -
 
                          00152-01 97 09-2
 
 
           1.5. Логические данные
 
 
           Элементарное логическое данное - это об'ект,  занимаю-
 
      щий один, два или четыре байта и  трактуемый  как  значение
 
      "истина" или "ложь". Об'ект имеет значение истина, если его
 
      значение как целого нечетно - то есть установлен в  единицу
 
      его младший бит; об'ект имеет значение ложь, если его  зна-
 
      чение как целого четно - то есть младший бит сброшен. Логи-
 
      ческие данные могут иметь один из следующих типов:
 
           LOGICAL*1 - логическое данное занимает один байт памя-
 
      ти;
 
           LOGICAL*2 - логическое данное занимает два байта памя-
 
      ти;
 
           LOGICAL*4 - логическое данное  занимает  четыре  байта
 
      памяти;
 
           LOGICAL - решение о типе принимается в процессе транс-
 
      ляции.  Если  программная  единица  транслируется  (команда
 
      FORTRAN) с квалификатором /I4  (принят  по  умолчанию),  то
 
      LOGICAL означает LOGICAL*4. Если при трансляции программной
 
      единицы  задан  квалификатор  /NOI4,  то  LOGICAL  означает
 
      LOGICAL*2.
 
           Целые и  логические  данные  взаимозаменяемы:  данное,
 
      определенное как целое, может использоваться  как  логичес-
 
      кое, и наоборот.
 
           Логические значения могут быть заданы следующие:
 
           1) константы. Специальная  константа  .TRUE.  Означает
 
      истину, численно .TRUE. Равно минус 1 (все биты  установле-
 
      ны). Специальная константа .FALSE. Означает ложь,  численно
 
      она равна  0  (все  биты  сброшены).  Например,  переменной
 
      READ_ONLY присваивается значение "истина":
 


                                - 23 -
 
                          00152-01 97 09-2
 
 
      !описательная часть программы
 
      LOGICAL READ_ONLY
 
      !выполняемая часть программы
 
      READ_ONLY = .TRUE.
 
 
           2) числа. Нечетные целые значения означают  истину,  а
 
      ноль или четные целые означают ложь.  В  следующем  примере
 
      переменной присваивается логическое значение истина:
 
 
 
      !описательная часть программы
 
      INTEGER STATUS
 
      !выполняемые операторы
 
      STATUS = 1
 
 
           3) отношения. Операция сравнения с  логическим  значе-
 
      нием "истина" (отношение справедливо) имеет числовое значе-
 
      ние минус 1.  Операция  сравнения  с  логическим  значением
 
      "ложь" (отношение не  выполняется)  имеет  числовое  значе-
 
      ние 0. Например, переменная STATUS получает логическое зна-
 
      чение "истина", если IOSTAT равно IO_OK, и логическое  зна-
 
      чение "ложь" в противном случае:
 
 
      STATUS = IOSTAT .EQ. IO_OK
 
 
           4) логические операции. Специальные логические  опера-
 
      ции .NOT., .AND., .OR., .XOR., .NEQV. И  .EQV.  Имеют  ранг
 
      меньший в порядке действий, чем операторы сравнения. Напри-
 
      мер, STATUS получает значение истина, если IOSTAT ложно,  и
 
      значение ложь, если IOSTAT истинно:
 
 
      STATUS = .NOT. IOSTAT
 
 
           Операторы IF и DO WHILE проверяют логическое значение,


                                - 24 -
 
                          00152-01 97 09-2
 
 
      чтобы решить, выполнять или нет последующий  блок  операто-
 
      ров. Значение "истина" означает, что  блок  следует  выпол-
 
      нить, значение "ложь" требует,  чтобы  блок  был  пропущен.
 
      Например, блок операторов будет выполняться, если  значение
 
      STATUS есть истина:
 
 
      IF (STATUS) THEN
 
      ! блок операторов
 
      END IF
 
 


          Иногда язык фортран может определить  результат  всего

      выражения до окончания вычисления всех подвыражений. Напри-
 
      мер, если в выражении  STATUS  .AND.  (IOSTAT  .EQ.  IO_OK)
 
      STATUS имеет значение "ложь", то подвыражение (IOSTAT  .EQ.
 
      IO_OK) не вычисляется. Поэтому  не  следует  полагаться  на
 
      побочный эффект функций, вызываемых  в  логических  выраже-
 
      ниях, поскольку эти функции могут и не быть выполнены.
 
 
 
           1.6. Символьные данные
 
 
           Строка символов - это последовательность, состоящая из
 
      одного или нескольких символов. Каждый символ  в  строке  -
 
      это однобайтный об'ект, трактуемый согласно КОИ-8.
 
 
 
           1.6.1. Определение строки символов
 
 
           Оператор описания типа для строки символов имеет вид:
 
      CHARACTER*размер идентификатор
 
      Все строки символов в языке фортран являются строками  фик-
 
      сированной длины. Размер  означает  количество  символов  в
 
      строке и должен  быть  задан  целой  константой.  Например,
 
      строка длиной 12 символов определяется оператором:


                                - 25 -
 
                          00152-01 97 09-2
 
 
      CHARACTER*12 FIX_HOUSE_STRING
 
      Строки символов воспринимаемой длины дают возможность опус-
 
      тить размер строки.  Пользоваться  строками  воспринимаемой
 
      длины можно в следующих случаях:
 
           1) если строка символов является  формальным  парамет-
 
      ром, то длина (размер) строки совпадает с длиной  строки  -
 
      фактического параметра;
 
           2) когда строка символов есть константа,  определенная
 
      оператором PARAMETER, ее размер есть длина  заданной  конс-
 
      танты.
 
           Строка символов переменной длины  задается  помещением
 
      звездочки в скобках вместо длины массива. Например,  строка
 
      символов INSTRUCTIONS воспринимает размер символьной  конс-
 
      танты, заданной в операторе PARAMETER:
 
      CHARACTER*(*) INSTRUCTIONS
 
      PARAMETER    (INSTRUCTIONS = 'введите  STATISTIC  и нажмите
 
      2             клавишу RETURN')
 
 
 
           1.6.2. Символьные константы
 
 
           Символьные константы задаются в виде последовательнос-
 
      ти печатаемых символов, заключенных в апострофы. Для  вклю-
 
      чения самого апострофа  в  строку  необходимо  указать  его
 
      дважды:
 
      STATUS = 'I SAID IT COULDN''T BE TRUE.'
 


                                - 26 -
 
                          00152-01 97 09-2
 
 
           1.6.3. Ссылка на строку символов
 
 
           На переменную, являющуюся строкой символов, можно ссы-
 
      латься в контексте, допускающем данные типа CHARACTER.  Для
 
      ссылки на часть строки (называемую подстрокой), использует-
 
      ся ссылка вида:
 
      идентификатор (первый : последний)
 
      Т.е. в скобках задается первая и последняя позиции в  стро-
 
      ке, разделенные двоеточием. Эти позиции могут  быть  заданы
 
      как целые константы, переменные, вызовы функции или выраже-
 
      ния. По умолчанию первая позиция относится к первому симво-
 
      лу строки, а последняя - к последнему символу. Например,  в
 
      строке STRING записывается некоторое число символов, задан-
 
      ное целой переменной SIZE:
 
      !операторы описаний
 
      CHARACTER*12 STRING
 
      INTEGER SIZE
 
      !выполняемые операторы
 
      TYPE *, STRING (1:SIZE)
 
           Необходимо внимательно следить за правильностью ссылки
 
      на подстроку из одного символа. Например, первый символ  из
 
      STRING - это STRING(1:1), а не STRING(1).
 
 
 
           1.6.4. Операция со строкой символов
 
 
           При операциях со строкой  символов  следует  ссылаться
 
      только на определенную часть строки. Например, если  STRING
 
      есть двенадцатисимвольная строка, а ей  присвоено  значение
 
      длиной менее чем из 12 символов,  то  на  значащие  символы
 
      следует ссылаться с использованием подстроки.  Вывод  части
 
      строки STRING (число символов определено  целой  переменной


                                - 27 -
 
                          00152-01 97 09-2
 
 
      SIZE) на устройство с логическим именем  SYS$OUTPUT  выпол-
 
      няется оператором:
 
      TYPE *, STRING (1:SIZE)
 
           При построении символьного значения подсчитывается его
 
      размер и присваивается целой переменной. При получении сим-
 
      вольного значения (например, по операции  ввода-вывода  или
 
      из процедуры библиотеки исполнительных процедур) его  длина
 
      может быть известна или нет. Если эта длина известна  (нап-
 
      ример, в качестве выходного параметра), то ее следует прис-
 
      воить целой переменной. Если же эта длина не  известна,  то
 
      ее следует вычислить, используя, например,  системноопреде-
 
      ленную процедуру STR$TRIM, возвращающую длину строки симво-
 
      лов без учета хвостовых пробелов и знаков табуляции. Напри-
 
      мер, значение длины строки STRING записывается в переменную
 
      SIZE:
 
      !операторы описаний
 
      CHARACTER*12 STRING
 
      INTEGER SIZE
 
      !описание состояния и системных процедур
 
      INTEGER STATUS,
 
      2       STR$TRIM
 
      !выполняемые операторы
 
      STATUS = STR$TRIM (STRING, !приемник
 
      2                  STRING, !источник
 
      2                  SIZE)   !длина
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 


                                - 28 -
 
                          00152-01 97 09-2
 
 
           1.6.4.1. Заполнение и усечение
 
 
           Если переменной присваивается строка символов, а длина
 
      строки меньше длины, определенной для переменной, то строка
 
      символов выравнивается по левому краю  поля  переменной,  а
 
      поле переменной дополняется справа пробелами. В длине стро-
 
      ки, которую возвращают системно-определенные процедуры, эти
 
      дополняющие пробелы не учитываются.
 
           Если переменной присваивается строка символов и  длина
 
      строки больше длины поля переменной, то строка выравнивает-
 
      ся по левому краю поля и обрезается справа. В длине строки,
 
      возвращаемой системно-определенными процедурами,  отброшен-
 
      ные символы не учитываются.
 
 
 
           1.6.4.2. Соединение строк (конкатенация)
 
 
           Оператор конкатенации (//) соединяет две строки симво-
 
      лов таким образом, что первый символ второй строки  присое-
 
      диняется к последнему символу первой строки. Перед соедине-
 
      нием строк рекомендуется отсечь от них лишние пробелы - это
 
      в особенности относится к первой строке. В следующем приме-
 
      ре проводится соединение символьной константы и  символьной
 
      переменной для формирования первого параметра вызова  функ-
 
      ции:
 
      TYPE *, 'AVERAGE INCOME PER HOUSEHOLD:' //  STRING (1:SIZE)
 


                                - 29 -
 
                          00152-01 97 09-2
 
 
           1.6.4.3. Встроенные функции
 
 
           Со строками  символов  работают  следующие  встроенные
 
      функции:
 
           LEN - определяет длину строки символов;
 
           INDEX - определяет начальную позицию подстроки;
 
           CHAR - возвращает символ, имеющий код, заданный  цело-
 
      численным аргументом;
 
           ICHAR - возвращает код (целое число) символьного аргу-
 
      мента.
 
 
 
           1.6.5. Специальные символы
 
 
           Использование  специальных  (не  имеющих  графического
 
      представления) символов возможно заданием их кода в качест-
 
      ве аргумента функции CHAR. Аргумент этой функции  представ-
 
      ляет собой целое значение, а CHAR возвращает односимвольную
 
      константу. Например,  звуковой  сигнал  терминала  (символ,
 
      представленный десятичным  значением  7,  есть  управляющий
 
      символ BEL - звонок) включается при выполнении операторов:
 
      CHARACTER*1 BEL
 
      PARAMETER (BEL = CHAR(7))
 
      TYPE *, BEL
 
           Специальные  символы  можно  соединить  с  другими   с
 
      помощью символов конкатенации (//). Например, звуковой сим-
 
      вол терминала включается перед выводом строки на терминал:
 
      CHARACTER*1 BEL
 
      PARAMETER (BEL = CHAR(7))
 
      CHARACTER*(*) IMP
 
      PARAMETER (IMP = BEL//'IMPORTANT NOTIGE FOLLOWS')
 
      TYPE *, IMP


                                - 30 -
 
                          00152-01 97 09-2
 
 
           1.6.6. Подсчитанные строки
 
 
           Системноопределенные процедуры могут передавать данные
 
      или получать их в виде "подсчитанной строки".  Подсчитанная
 
      строка - это строка  символов  или  массив,  содержащий  не
 
      более 256 символов. Первый символ этой строки является сим-
 
      вольным представлением числа оставшихся символов.
 
           Пример.
 
           Устанавливается подсчитанная строка из данных,  прочи-
 
      танных с SYS$INPUT
 
      INTEGER COUNT
 
      CHARACTER*256 STRING
 
      READ (UNIT = *,
 
      2     FMT = '(Q, A)') COUNT,
 
      2                     STRING (2:256)
 
      STRING (1:1) = CHAR (COUNT)
 
           Не  рекомендуется  использовать  для  первого  символа
 
      ("счетчикA") переменные типа BYTE (или LOGICAL*1), посколь-
 
      ку для этого типа данных наибольшее положительное  значение
 
      есть 127. Использовать необходимо тип  данных,  по  крайней
 
      мере, INTEGER*2, например:
 
      INTEGER COUNT
 
      CHARACTER*256 STRING
 
      COUNT = ICHAR (STRING(1:1))
 
      WRITE (UNIT = *,
 
      2      FMT = '(A)') STRING (2:COUNT + 1)
 


                                - 31 -
 
                          00152-01 97 09-2
 
 
           1.7. Данные без об'явления типа
 
 
           1.7.1. Константы без об'явления типа
 
 
           Можно  трактовать  шестнадцатеричные,  восьмеричные  и
 
      холлеритовские константы как числовые (целые, вещественные,
 
      комплексные,  логические  и  байтовые)  данные.  Символьные
 
      константы также можно трактовать как числовые данные, одна-
 
      ко символьная константа в числовом контексте рассматривает-
 
      ся как холлеритовская константа с длиной заданной  символь-
 
      ной константы.
 
           Константа без об'явления типа  имеет  значение,  соот-
 
      ветствующие конфигурации битов, она может воспринимать  тип
 
      на основании контекста:
 
           1) если константа без об'явления типа  используется  в
 
      бинарной  операции  (операция,  соединяющая  два  элемента,
 
      включая операцию присваивания), она воспринимает тип второ-
 
      го операнда, например:
 
      INTEGER*2 WORD
 
      INTEGER*4 LONG
 
      REAL*8    DOUBLE
 
      IF (LONG .LE. '123'O)THEN   !INTEGER*4
 
        LONG =  ORD + '123'O      !INTEGER*2
 
        DOUBLE = '123'O           !REAL*8
 
      END IF
 
           2) если константа применяется в  контексте,  требующем
 
      определенный тип данных (обычно целый), то константа прини-
 
      мает требующий тип:
 
      REAL*8 DOUBLE,
 
      2       ARRAY(20)
 
      DOUBLE = ARRAY('16'O) + 3.5   !INTEGER*4


                                - 32 -
 
                          00152-01 97 09-2
 
 
           3) константа без об'явления типа при использовании  ее
 
      в качестве фактического параметра,  не  принимает  никакого
 
      типа. Для шестнадцатеричных и  восьмеричных  костант  пере-
 
      даются в точности четыре  байта.  Холлеритовские  константы
 
      передаются следующим образом: если  константа  короче  фор-
 
      мального параметра, она дополняется нулевыми байтами;  если
 
      она длиннее формального параметра, то она обрезается.
 
           При использовании в любом другом  контексте  константа
 
      без об'явления типа принимает тип INTEGER*4.
 
 
 
           1.7.1.1. Шестнадцатеричные константы
 
 
           Шестнадцатеричная константа задается  с  помощью  цифр
 
      от 0 до 9 и букв латинского алфавита от A  до  F  (верхнего
 
      или нижнего регистра), заключенных в апострофы, за которыми
 
      стоит буква х (верхнего или нижнего регистра). Каждый шест-
 
      надцатиричный символ (цифра) представляет собой  конфигура-
 
      цию из четырех битов, имеющую двоичные и десятичные  значе-
 
      ния, указанные в табл. 3.
 
                                                        Таблица 3
 
      -----------------------------------------------------------
                       !
                       !               Значение
         Шестнадцати-  !
                       !-----------------------------------------
         ричная цифра  !                   !
                       !      Двоичное     !     Десятичное
                       !                   !
      -----------------------------------------------------------
                       !                   !
             0         !        0000       !         0
                       !                   !
             1         !        0001       !         1
                       !                   !
             2         !        0010       !         2
                       !                   !
             3         !        0011       !         3
                       !                   !
             4         !        0100       !         4
                       !                   !


                                - 33 -
 
                          00152-01 97 09-2
 
 
                                              Продолжение табл. 3
      -----------------------------------------------------------
                       !
                       !               Значение
         Шестнадцати-  !
                       !-----------------------------------------
         ричная цифра  !                   !
                       !      Двоичное     !     Десятичное
                       !                   !
      -----------------------------------------------------------
             5         !        0101       !         5
                       !                   !
             6         !        0110       !         6
                       !                   !
             7         !        0111       !         7
                       !                   !
             8         !        1000       !         8
                       !                   !
             9         !        1001       !         9
                       !                   !
             A         !        1010       !         10
                       !                   !
             B         !        1011       !         11
                       !                   !
             C         !        1100       !         12
                       !                   !
             D         !        1101       !         13
                       !                   !
             E         !        1110       !         14
                       !                   !
             F         !        1111       !         15
 
 
           Два шестнадцатеричных символа  составляют  в  точности
 
      один байт. Самый правый символ  составляет  четыре  младших
 
      бита, A самый левый символ составляет четыре старших  бита.
 
      Переменной HEX  может  быть  присвоенно  значение  01000010
 
      двоичное (десятичное 66) следующим образом:
 
      BYTE HEX
 
      HEX = '42'X
 
           В одной константе можно задать до 32 шестнадцатеричных
 
      символов, что эквивалентно 128 бит или 16 байт. Если  шест-
 
      надцатеричная константа содержит меньше символов, чем нужно
 
      для заполнения переменной, то переменная дополняется  слева
 
      нулями. Если шестнадцатеричная  константа  содержит  больше
 
      цифр, чем может принять переменная, то константа обрезается
 


                                - 34 -
 
                          00152-01 97 09-2
 
 
      слева. В случае отбрасывания цифр константы во время транс-
 
      ляции и компоновки будут выданы предупреждающие  сообщения,
 
      однако трансляция и компоновка не прекращаются.
 
 
 
           1.7.1.2. Восьмеричные константы
 
 
           Восьмеричная  константа  задается  как  заключенная  в
 
      апострофы последовательность цифр от 0  до  7,  за  которой
 
      стоит буква  о  (верхнего  или  нижнего  регистра).  Каждая
 
      восьмEричная цифра  представляет  собой  конфигурации  трех
 
      битов, двоичные и десятичные значения которых приводятся  в
 
      табл.4.
 
                                                        Таблица 4
 
      -----------------------------------------------------------
                         !
                         !              Значение
         Восьмеричная    !
                         !---------------------------------------
           Цифра         !                  !
                         !     Двоичное     !     Десятичное
                         !                  !
      -----------------------------------------------------------
                         !                  !
             0           !        000       !         0
                         !                  !
             1           !        001       !         1
                         !                  !
             2           !        010       !         2
                         !                  !
             3           !        011       !         3
                         !                  !
             4           !        100       !         4
                         !                  !
             5           !        101       !         5
                         !                  !
             6           !        110       !         6
                         !                  !
             7           !        111       !         7
 
 
           При присвоении восьмеричной константы переменной самая
 
      правая цифра означает три младших бита переменной,  следую-
 
      щая цифра - три следующих бита и т.д. Восьмеричные констан-
 
      ты в целое число байтов могут и не укладываться. Восьмерич-


                                - 35 -
 
                          00152-01 97 09-2
 
 
      ная константа из трех, например, цифр,  представляет  собой
 
      девять бит, то есть байт с одним лишним битом. В зависимос-
 
      ти от размера переменной, которой присваивается  эта  конс-
 
      танта, лишний бит может стать младшим битом следующего бай-
 
      та переменной. Например,  переменной  OCTAL1  присваивается
 
      двоичное значение 00111111 (десятичное значение 63),  в  то
 
      время  как  младшему  байту  OCTAL2  присваивается   значе-
 
      ние 11111111, а не старшему байту OCTAL2 - значение 1:
 
      BYTE OCTAL1
 
      INTEGER*2 OCTAL2
 
      OCTAL1 = '77'о
 
      OCTAL2 = '777'о
 
           В одной константе  можно  задать  до  43  восьмеричных
 
      цифр, что соответствует 128 бит или  16  байт  (но  старший
 
      из 43 символов не может превосходить 3). Если  восьмеричная
 
      константа содержит меньше цифр, чем  нужно  для  заполнения
 
      переменной, то переменная дополняется  слева  нулями.  Если
 
      восьмеричная константа содержит более символов,  чем  пере-
 
      менная может принять, константа обрезается  слева.  В  этом
 
      случае во время трансляции  и  во  время  компоновки  будут
 
      выданы предупреждающие сообщения,  однако  программа  будет
 
      оттранслирована и скомпонована.
 
 
 
           1.7.1.3. Холлеритовские константы
 
 
           Холлеритовская константа - это целое число, за которым
 
      стоит буква H (на верхнем или нижнем регистре), а за ней  -
 
      символы, количество которых равно  целому  числу,  стоящему
 
      перед H. Каждый следующий за H символ соответствует  одному
 
      байту той переменной, которой присваивается константа,  эта
 


                                - 36 -
 
                          00152-01 97 09-2
 
 
      переменная принимает значение  символа  КОИ-7  (присваивать
 
      холлеритовскую  константу  символьной  переменной  нельзя).
 
      Самый левый символ холлеритовской  константы  соответствует
 
      младшему байту этой числовой переменной, которой  присвоена
 
      константа.
 
           Пример.
 
           Байтовая переменная LETTER1 получает  числовое  значе-
 
      ние 65  (десятичное),  а  двухбайтовая  переменная  LETTER2
 
      будет иметь значение 16961 (десятичный код буквы  B  умень-
 
      шается на 256 и к результату добавляется 65 -  код  символа
 
      A):
 
      BYTE LETTER1
 
      INTEGER*2 LETTER2
 
      LETTER1 = 1HB
 
      LETTER2 = 2HAB
 
           В холлеритовской константе можно задать до 2000 симво-
 
      лов. Если в константе меньше  символов,  чем  в  переменной
 
      байтов, то переменная дополняется справа пробелами  (пробел
 
      имеет десятичное значение 32 в коде КОИ-7). Если в констан-
 
      те больше символов,  чем  в  переменной  байтов,  константа
 
      обрезается справа. В этом случае во время трансляции и ком-
 
      поновки выдаются предупреждающие сообщения, но трансляция и
 
      компоновка программы продолжаются.
 
 
 
           1.7.2. Работа с битами
 
 
           Для работы с битами (установка или проверка  битов  по
 
      значениям масок) используются встроенные функции IAND, IOR,
 
      IEOR, NOT, ISHFT, IBITS, IBCLR, ISHFTC, IBSET и BTEST.  Эти
 
      функции  требуют  целочисленных  операндов  (тип   INTEGER,
 


                                - 37 -
 
                          00152-01 97 09-2
 
 
      LOGICAL, BYTE). Ссылка на позицию бита 0  означает  младший
 
      бит целого, 1 - следующий бит и т.д.).
 
           Пример.
 
           Установка младшего и старшего битов длинного слова:
 
      INTEGER*4 MASK
 
      MASK = IBSET (MASK, 0)
 
      MASK = IBSET (MASK, 31)
 
 
 
           1.8. Преобразования типов данных
 
 
           Внутреннее преобразование типов  данных  необходимо  в
 
      тех случаях, когда переменные и присваиваемые  им  значения
 
      имеют разные типы, или когда значения разных  типов  участ-
 
      вуют в одной операции.
 
           При перемещении  данных  между  внутренней  памятью  и
 
      внешними устройствами (терминал, устройство печати,  диско-
 
      вые файлы), преобразования необходимы в тех случаях,  когда
 
      внутренние данные не являются символьными, а  внешнее  уст-
 
      ройство является символьно-ориентированным (например,  тер-
 
      минал или устройство печати), или вместо внешнего устройст-
 
      ва используется форматный файл.
 
 
 
           1.8.1. Числовые преобразования
 
 
           При присвоении числового значения числовой  переменной
 
      значение преобразуется к типу переменной.  Например,  целое
 
      значение PERSONS преобразуется в вещественное число:
 
 
      REAL PERSONS_HOUSE (2048)
 
      INTEGER PERSONS
 
      PERSONS_HOUSE (I) = PERSONS
 


                                - 38 -
 
                          00152-01 97 09-2
 
 
           Если в бинарной операции  (соединяющей  два  элемента)
 
      используются числовые значения различных типов,  то  значе-
 
      ние, имеющее тип данных низшего ранга, преобразуется к типу
 
      второго значения. Типы данных расположены по рангам: целые,
 
      вещественные, комплексные; ранг увеличивается с увеличением
 
      точности представления (например, REAL*4 имеет  ранг  мень-
 
      ший, чем REAL*8). Например, значение WORD приводится к типу
 
      INTEGER*4 и складывается с LONG; результат преобразуется  в
 
      тип REAL*4 и прибавляется к SINGLE:
 
 
      INTEGER*2 WORD
 
      INTEGER*4 LONG
 
      REAL*4 SINGLE
 
      TYPE *, WORD + LONG + SINGLE
 
 
           При преобразовании целого числа в вещественное к  нему
 
      добавляется дробная часть (.0).  При  преобразовании  типов
 
      низшего  ранга  в  комплексный   тип   добавляется   мнимая
 
      часть (0.0) и, если необходимо, дробная часть  действитель-
 
      ной части (.0). При преобразовании чисел от  типов  низшего
 
      ранга к типам более  высокого  ранга  не  теряется  никакая
 
      часть числа, разве  только  преобразование  очень  большого
 
      целого к типу REAL*4 может привести к потере точности. Нап-
 
      ример, преобразование целого значения 123 456 789  в  число
 
      типа REAL*4 дает 1.2345679E+08 (123 456 790).
 
           При преобразовании комплексного числа  в  тип  низшего
 
      ранга мнимая часть отбрасывается. При преобразовании дейст-
 
      вительного  числа  в  целое  отбрасывается  дробная  часть.
 
      Преобразование числа в тип с меньшим рангом и (или) меньшей
 
      точностью может привести к ошибке переполнения (если  число
 
      слишком велико для того типа данных, к которому оно  преоб-


                                - 39 -
 
                          00152-01 97 09-2
 
 
      разуется) или к потере точности. Такая ситуация может полу-
 
      читься, например, при преобразовании типа PEAL*8 в REAL*4.
 
 
 
           1.8.2. Форматные преобразования
 
 
           Со строкой символов нельзя работать как с числом, даже
 
      если она имеет вид числа. Например, выражение 9+'2' являет-
 
      ся недопустимым. Нельзя  использовать  число  как  значение
 
      символа. Следует преобразовать число в строку символов  или
 
      строку символов в число.
 
           Предпочтительный  механизм  для  выполнения символьно-
 
      числовых преобразований -  это  внутренний  оператор  READ.
 
      Предпочтительный  механизм  выполнения  численно-символьных
 
      преобразований - внутренний оператор WRITE.  Во  внутренних
 
      операторах READ и WRITE  устройство  ввода-вывода  задается
 
      как переменная, содержащая символьное значение  (а  не  как
 
      внешнее устройство или файл), а  список  ввода-вывода  есть
 
      переменная или константа, содержащая число.  Формат  должен
 
      быть задан с помощью  спецификатора  FMT  оператора  ввода-
 
      вывода.
 
           В общем случае внутренний оператор READ (для  преобра-
 
      зования символов в числа) имеет такую форму:
 
 
           READ (UNIT = CH_VAR,
 
           2     FMT = F_SPEC) N_VAR
 
 
 
      где  CH_VAR - символьная переменная;
 
           F_SPEC - спецификатор формата;
 
           N_VAR - числовая переменная.
 
           Пример.
 
           Строка символов C преобразуется в целое число N:


                                - 40 -
 
                          00152-01 97 09-2
 
 
           CHARACTER*10 C
 
           INTEGER N
 
           INTEGER*2 SIZE
 
           CALL LIB$GET_INPUT (C,
 
           2                   'NEXT NUMBER OR END;',
 
           2                   SIZE)
 
           READ (UNIT = C(1:SIZE),
 
           2     FMT = '(BN, I10)') N
 
 
           В общем  случае  оператор  внутреннего  преобразования
 
      WRITE имеет вид:
 
 
           WRITE (UNIT = CH_VAR,
 
           2      FMT = F_SPEC) N_VAR
 
 
      где CH_VAR, F_SPEC, N_VAR имеют такой же  смысл,  что  и  в
 
      операторе READ.
 
           Пример.
 
           Целочисленная переменная N преобразуется в строку сим-
 
      волов C:
 
      CHARACTER*10 C
 
      INTEGER N,
 
      2       SIZE
 
      WRITE (UNIT = C,
 
      2      FMT = '(I10)') N
 
      ! избавиться от хвостовых пробелов
 
      SIZE = 1
 
      DO WRITE ((C(SIZE:SIZE) .EQ.' ')  .AND. (SIZE .LE. 10))
 
      SIZE = SIZE  +  1
 
      END DO
 
      !вывести число
 


                                - 41 -
 
                          00152-01 97 09-2
 
 
      TYPE *, 'NUMBER = '//C(SIZE:10)
 
 
           Можно преобразовывать несколько значений в одном  опе-
 
      раторе READ или WRITE, задавая  символьные  переменные  как
 
      массив. Например, два числа преобразуются в  строки  симво-
 
      лов:
 
      CHARACTER*10 C (2)
 
      INTEGER N1,
 
      2       N2
 
      WRITE (UNIT = C,
 
      2      FMT = '(2I10)') N1,
 
      2                    N2
 
           Спецификации формата можно использовать и для преобра-
 
      зования данных в процессе регулярных операций ввода-вывода.
 
      Например, можно читать с терминала или из форматного  файла
 
      с преобразованием в числовые переменные или выдавать число-
 
      вые значения прямо на терминал или в форматный файл:
 
      INTEGER N
 
      WRITE (UNIT = *,
 
      2      FMT = '(I4')' N
 
 
 
           1.8.3. Автоматическое преобразование
 
 
           При обычных  последовательных  операциях  ввода-вывода
 
      можно преобразовать данные и без указания формата. В опера-
 
      торах ввода-вывода, ориентированных на список или на список
 
      имен, преобразование производится автоматически;  для  всех
 
      используемых данных применяется  формат,  установленный  по
 
      умолчанию для данного типа. Например, значение целого числа
 
      N можно вывести на терминал следующим образом:
 


                                - 42 -
 
                          00152-01 97 09-2
 
 
      INTEGER N
 
      TYPE *, N
 
 
 
           1.9. Массивы
 
 
           Массив - это последовательность данных, имеющих один и
 
      тот же тип и об'единенных под одним именем. Каждое из  этих
 
      данных называется элементом и связано  с  целым  значением,
 
      определяющим его место в массиве.
 
 
 
           1.9.1. Определение массивов
 
 
           Массив может быть определен с помощью оператора  зада-
 
      ния типа:
 
      тип имя ([N1:] N2) [,...]
 
      где тип - тип массива данных (INTEGER, BYTE и т.д.);
 
           Имя - имя массива (идентификатор);
 
           N1 - нижняя граница массива;
 
           N2 - верхняя граница массива, не  должна  быть  меньше
 
      нижней границы.
 
           Квадратные скобки заключают необязательные элементы.
 
           Пример.
 
           Описываются массивы PH1  и  PH2,  содержащие  по  2048
 
      вещественных элементов. Номера элементов в массиве PH1 при-
 
      нимают значения от 0 до 2047,  а  в  массиве  PH2  -  от  1
 
      до 2048:
 
           REAL PH1 (0:2047), PH2(2048)
 
           Массив можно  определить  также  с  помощью  оператора
 
      DIMENSION, который должен находится  в  описательной  части
 
      программы:
 


                                - 43 -
 
                          00152-01 97 09-2
 
 
           DIMENSION имя ([N1:]N2) [,...]
 
      где имя, N1, N2 имеют такой же смысл, что  и  в  предыдущем
 
      случае. Тип каждого массива надо об'явить  явно  с  помощью
 
      оператора описания типа или принять тип,  установленный  по
 
      умолчанию. Массив из 2048 действительных чисел  можно  сле-
 
      дующим образом:
 
      REAL PERSONS_HOUSE
 
      DIMENSION PERSONS_HOUSE (2048)
 
           Значения, определяющие первую и последнюю позиции мас-
 
      сива в операторах описания типа  и  в  операторе  DIMENSION
 
      должны быть константами, или выражениями, содержащими толь-
 
      ко константы, или переменными, определенными в блоке  общей
 
      памяти или являющимися формальными параметрами (если массив
 
      есть формальный параметр).
 
 
 
           1.9.2. Ссылка на массивы
 
 
           На конкретный элемент  массива  ссылаются  по  индексу
 
      после имени массива. Индекс представляет собой  заключенное
 
      в круглые скобки целое значение и обозначает  позицию  эле-
 
      мента в массиве. Например, PERSONS_HOUSE(5)  означает  эле-
 
      мент массива PERSONS_HOUSE в пятой позиции. Значение индек-
 
      са может быть представлено константой, переменной,  вызовом
 
      функции или выражением.  Использование  переменной  в  роли
 
      индексов дает мощное средство работы с данными.
 
           При ссылке на элемент массива  строк  символов  индекс
 
      элемента помещается перед спецификацией  подстроки.  Напри-
 
      мер, первый символ пятого элемента массива  строк  символов
 
      BOOK_NAME присваивается символьной переменной LETTER:
 


                                - 44 -
 
                          00152-01 97 09-2
 
 
      CHARACTER*31 BOOK_NAME (101)
 
      CHARACTER LETTER
 
      !выполняемые операторы
 
      LETTER = BOOK_NAME (5) (1:1)
 
 
 
           1.9.2.1. Циклическая обработка массивов
 
 
           Обычно элементы всего массива или его части  обрабаты-
 
      ваются с помощью оператора цикла DO. В начале первого  про-
 
      хода цикла следует присвоить индексу значение младшей  (или
 
      старшей) позиции в массиве, и после каждого  прохода  цикла
 
      увеличивать (или уменьшать) индекс, пока не будет достигнут
 
      старший (или младший) элемент массива.
 
           Пример.
 
           Подсчет суммы элементов массива PERSONS_HOUSE:
 
      TOTAL_PERCONS = 0
 
      DO I = 1, TOTAL_HOUSES
 
         TOTAL_PERSONS = TOTAL_PERSONS + PERSONS_HOUSE(I)
 
      END DO
 
           В операторах ввода-вывода языка фортран можно  переда-
 
      вать массив целиком, используя его имя  без  индексов.  Для
 
      передачи какой-либо части массива можно передавать  данные,
 
      указывая верхнюю и нижнюю их границы в неявном цикле. Неяв-
 
      ный цикл обработки массива имеет вид:
 
      оператор (управл. список) (имя массива (инд), инд=нач, KOH)
 
      где  инд - управляющая переменная цикла (индекс);
 
           Нач - номер начального элемента для передачи;
 
           Кон - номер конечного элемента для передачи.
 
           Пример.
 


                                - 45 -
 
                          00152-01 97 09-2
 
 
      WRITE (UNIT = STATS_LUN)
 
      2     (PERSONS_HOUSE (I), I = 1, TOTAL_HOUSES)
 
           В общем случае необходимо организовывать передачу дан-
 
      ных только той части  массива,  которая  содержит  полезные
 
      значения. Например, если PERSONS_HOUSE  -  массив  из  2048
 
      элементов - содержит (на момент передачи данных) только 921
 
      значащий элемент (что отражено в значении TOTAL_HOUSE),  то
 
      нет смысла передавать все 2048 элементов. При чтении данных
 
      таким же способом следует соблюдать точность. Если читаемая
 
      запись содержит 921  элемент  массива,  то  и  передаваться
 
      должны точно столько же элементов.
 
 
 
           1.9.2.2. Обработка полного массива
 
 
           Элементы массива могут использоваться в  тех  же  кон-              
 
      текстах что и переменные одного с ними типа.  Использование
 
      массива целиком более ограничено. Например,  полный  массив
 
      не может появляться ни в правой, ни в левой части оператора
 
      присваивания. Если необходимо скопировать  элементы  одного
 
      массива в другой, то это надо делать поэлементно  в  цикле.
 
      Полностью массивы можно указывать в следующих контекстах:
 
           COMMON - определяет, что массив целиком помещен в блок
 
      общей памяти;
 
           DATA - задается начальное  значение  каждому  элементу
 
      массива (инициализация массива). Значение  следует  указать
 
      для каждого  элемента.  Например,  каждый  элемент  массива
 
      PERSONS_HOUSE  можно  инициализировать   начальным   значе-
 
      нием 9999 следующим образом:
 
      REAL PERSONS_HOUSE (2048)
 
      DATA PERSONS_HOUSE /2048*9999/
 


                                - 46 -
 
                          00152-01 97 09-2
 
 
           Для инициализации  части  массива  можно  использовать
 
      неявный цикл. Например, в операторе  DATA  инициализируются
 
      первые 1024 элемента массива PERSONS_HOUSE:
 
      DATA (PERSONS_HOUSE (I), I = 1, 1024)
 
      2    /1024*99999/
 
           EQUIVALENCE - отводит  двум  массивам  одну  и  ту  же
 
      область памяти;
 
           FUNCTION - полный массив можно задать формальным пара-
 
      метром;
 
           SUBROUTINE - полный  массив  можно  задать  формальным
 
      параметром;
 
           ENTRY - полный массив можно задать формальным парамет-
 
      ром;
 
           SAVE - ссылается на полный массив.
 
           Имя массива без индексов можно  также  использовать  в
 
      операторах ввода/вывода и в качестве фактического параметра
 
      в операторах CALL и вызовах функций. Если определена только
 
      часть  массива,  то  при  задании  массива   в   операторах
 
      ввода/вывода можно пользоваться неявным циклом.
 
 
 
           1.9.3. Память и границы массивов
 
 
           Рекомендуется задавать массивы несколько больших  раз-
 
      меров, чем это необходимо по минимальной оценке (если толь-
 
      ко заранее не известно точное число элементов), это  умень-
 
      шит вероятность появления  ошибок,  связанных  с  нехваткой
 
      места. Затребованная память не используется, пока на нее не
 
      будет ссылки, а  выполняемое  системой  управление  памятью
 
      проще в использовании и обычно более эффективно, чем самос-
 
      тоятельные попытки, заниматься ее распределением (например,
 


                                - 47 -
 
                          00152-01 97 09-2
 
 
      разбивая массив на отдельные записи и обработка их по запи-
 
      сям).
 
           При ссылке на массив следует  убедиться,  что  индексы
 
      находятся в пределах заданных границ. Например, если  пере-
 
      менная, служащая индексом (HOUSE_NO),  выходит  за  пределы
 
      массива (I:MAXSTATS), будет возвращен код  ошибочного  сос-
 
      тояния STATUS:
 
      REAL PERSONS_HOUSE (2048)
 
      INTEGER HOUSE_NO
 
      INTEGER MAXSTATS
 
      PARAMETER (MAXSTATS = 2048)
 
      EXTERNAL INCOME_MAXSTATS
 
 
 
      HOUSE_NO = HOUSE_NO + 1
 
      IF ((HOUSE_NO .LT. 1) .OR.
 
      2   (HOUSE_NO .GT. MAXSTATS)) THEN
 
        STATUS = %LOG (INCOME_MAXSTATS)
 
      END IF
 
           Можно воспользоваться системной  ловушкой  для  выхода
 
      индексов за границы массива,  задав  /CHECK  =  BOUNDS  при
 
      трансляции программной единицы.  При  выполнении  программы
 
      выдается сообщение о неисправимой ошибке  SS$_SUBRNG,  если
 
      индекс выйдет за границы массива (если выходящий за границы
 
      массива индекс есть константа, то такая  ошибка  диагности-
 
      руется на этапе трансляции).
 
           Если в программе не  отлавливать  выходы  индексов  за
 
      границы массива или не пользоваться системной ловушкой  для
 
      этой цели, то результаты могут быть непредсказуемыми.  Воз-
 
      можно попадание на данные или команды в другой части данной
 
      программы, или в область памяти, занятую другим  процессом,


                                - 48 -
 
                          00152-01 97 09-2
 
 
      что приведет к ошибке по защите памяти.
 
 
 
           1.9.4. Многомерные массивы
 
 
           Можно определить массивы, имеющие до  семи  размернос-
 
      тей. Многомерные массивы определяются  заданием  нескольких
 
      спецификаторов границ в операторах описания типа или опера-
 
      торах DIMENSION; спецификаторы отделяются запятыми.  Напри-
 
      мер, STATS - двумерный массив, имеющий по первой размернос-
 
      ти границы 1 и 3, по второй размерности границы 1 и 2048:
 
      REAL STATS (3, 2048)
 
           Общее  число  элементов  многомерного  массива  -  это
 
      произведение чисел,  указывающих  количество  элементов  по
 
      всем размерностям. Число элементов  в  массиве  STATS  рав-
 
      но 6144 элемента.
 
           На элемент многомерного массива можно ссылаться,  ука-
 
      зывая положение по каждой размерности:
 
      <предполагается, что TOTAL_HOUSES равно 957>
 
      TOTAL_HOUSES = TOTAL_HOUSES + 1
 
      STATS (1, TOTAL_HOUSES) = PERSONS
 
      STATS (2, TOTAL_HOUSES) = ADULTS
 
 
 
           1.9.4.1. Размещение многомерных массивов в памяти
 
 
           Массивы хранятся в памяти таким образом, что чем левее
 
      индекс, тем быстрее он изменяется. В табл. 5 показаны  эле-
 
      менты памяти для массива, имеющего три  позиции  по  первой
 
      размерности и 2048 позиций по второй размерности.
 


                                - 49 -
 
                          00152-01 97 09-2
 
 
                                                        Таблица 5
 
      -----------------------------------------------------------
                               !
           Элемент массива     !       Элемент памяти
                               !
      -----------------------------------------------------------
                               !
         (1,1)                 !        1
                               !
         (2,1)                 !        2
                               !
         (3,1)                 !        3
                               !
         (1,2)                 !        4
                               !
         (2,2)                 !        5
                               !
         .                     !        .
                               !
         .                     !        .
                               !
         .                     !        .
                               !
        (2,2047)               !        6140
                               !
        (3,2047)               !        6141
                               !
        (1,2048)               !        6142
                               !
        (2,2048)               !        6143
                               !
        (3,2048)               !        6144
 
 
 
           Если представлять  двумерный  массив  как  таблицу  из
 
      столбцов (размерность 1) и строк (размерность 2), то  поря-
 
      док хранения будет таков: все элементы  первой  строки,  за
 
      ними все элементы второй строки и  т.д.  Если  представлять
 
      себе трехмерный массив как некоторое число таблиц,  находя-
 
      щихся на  последовательных  страницах  (столбцы  -  размер-
 
      ность 1, строки - размерность 2, страницы - размерность 3),
 
      то сначала будут храниться элементы первой страницы (строка
 
      за строкой), затем второй страницы (строка за  строкой),  и
 
      т.д.
 


                                - 50 -
 
                          00152-01 97 09-2
 
 
           1.9.4.2. Обработка многомерных массивов
 
 
           Для обработки многомерных массивов  применяются  циклы
 
      DO по каждой размерности. Способ вложения циклов зависит от
 
      желательного порядка обработки элементов. Самый  внутренний
 
      цикл соответствует  наиболее  быстро  меняющемуся  индексу;
 
      самый внешний цикл соответствует индексу  той  размерности,
 
      которая меняется в последнюю очередь. Для большей эффектив-
 
      ности программ желательно вести обработку элементов  в  том
 
      порядке, в котором они расположены.
 
           Например, трехмерный массив можно представить как пос-
 
      ледовательность страниц  (размерность  3)  с  таблицами  из
 
      столбцов (размерность 1) и  строк  (размерность  2).  Чтобы
 
      вывести элементы всех строк в порядке, начинающемся с  пер-
 
      вой страницы, следует наиболее быстро  изменить  индекс  по
 
      размерности 1 (чтобы пройти всю строку  до  начала  следую-
 
      щей), затем индекс по размерности 2 (чтобы пройти  вниз  по
 
      строкам до перехода на  следующую  страницу),  а  медленнее
 
      всех меняется индекс по размерности 3:
 
      !массив
 
      CHARACTER*3 CODES (10, 20, 6)
 
      !размерности
 
      INTEGER*4  MAX_PAGE /6/,
 
      2          MAX_ROW /20/,
 
      2          MAX_COLUMN /10/
 
      !физическое положение страницы
 
      INTEGER*4 LINE,
 
      2       HEADER /2/,
 
      2       LEFT_MARGIN /6/,
 
      2       BEGIN_TABLE /5/
 


                                - 51 -
 
                          00152-01 97 09-2
 
 
      !номер страницы
 
      CHARACTER*3 PAGE
 
      DO K = 1, MAX_PAGE
 
        !печать номера страницы перед ее началом
 
        !преобразование номера страницы в символьный вид
 
        WRITE (UNIT = PAGE,
 
      2        FMT = '(14)') K
 
        STATUS = LIB$PUT_SCREEN ('TABLE-PAGE '//PAGE,   !текст
 
      2                          HEADER,                !строка
 
      2                          LEFT_MARGIN)           !поле
 
        IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
        !начальная строка таблицы
 
        LINE = BEGIN_TABLE
 
        DO J = 1, MAX_ROW
 
          DO I = 1, MAX_COLUMN
 
            !оставить между элементами три пробела
 
            !3 пробела + 3 символа = 6 символов на элемент
 
            STATUS = LIB$PUT_SCREEN (CODES (I,J,K),
 
      2                              LINE,
 
      2                              I*6)
 
          END DO    !столбцы
 
          !в конце каждой строки обновить номер строки
 
          LINE = LINE + 1
 
        END DO      !строки
 
      END DO        !страницы
 
 
 
           В операторах ввода-вывода  языка  фортран  многомерные
 
      массивы передаются с помощью вложенных неявных циклов. Вло-
 
      женные неявные циклы имеют вид:
 


                                - 52 -
 
                          00152-01 97 09-2
 
 
      оператор (управление выводом)
 
      2 (...((массив (A, B, ...), A = нач,кон), B = нач,кон)...)
 
      где оператор - имя оператора ввода/вывода;
 
          массив  -  имя многомерного массива;
 
          A, Б,... - индекс массива;
 
          нач,кон -  начальное и конечное значение для соответст-
 
                     вующих индексов.
 
           Пример.
 
           Трехмерный массив записывается в файл:
 
      WRITE (UNIT = STATS_LUN)
 
      2  (((CODES (I, J, K), I = 1, MAX_COLUMN), J = 1, MAX_ROW),
 
      2     K = 1, MAX_PAGE)
 
           Примечание. При передаче массива следует ограничивать-
 
      ся только той частью  массива,  которая  содержит  полезные
 
      значения. При передаче данных таким способом следует соблю-
 
      дать точность.
 
 
 
           1.10. Записи
 
 
           Запись дает возможность строить  структуры  данных,  к
 
      которым можно обращаться на различных уровнях.  На  верхнем
 
      уровне адресуются все данные записи. На более низких  уров-
 
      нях можно адресоваться к полям и подполям записи.
 
           Для использования записи по ее имени необходимо:
 
           1) определить структуру записи с помощью  структурного
 
      блока STRUCTURE;
 
           2) об'явить запись с помощью оператора RECORD;
 
           3) сослаться на запись, пользуясь  именем  об'явленной
 
      записи, или на поле внутри  записи,  поставив  после  имени
 
      записи точку и имя поля записи.
 


                                - 53 -
 
                          00152-01 97 09-2
 
 
           Пример.
 
           В программном сегменте запись используется для переда-
 
      чи   списка   об'ектов   системной   служебной    процедуре
 
      SYS$GETJPIW. В описательной  части  программы  определяется
 
      структура записи ITMLST и об'является запись  типа  ITMLST.
 
      Структура ITMLST состоит  или  из  четырех  полей  (BUFLEN,
 
      CODE, BUFADR и RETLENADR), или из одного поля (END_LIST). В
 
      выполняемой части программы каждому  полю  записи  JPI_LIST
 
      присваивается значение, а затем запись передается системной
 
      служебной процедуре SYS$GETJPIW:
 
 
      !определение структуры
 
      STRUCTURE /ITMLST/
 
       UNION
 
        MAP
 
         INTEGER*2 BUFLEN,
 
      2            CODE
 
         INTEGER*4 BUFADR,
 
      2            RETLENADR
 
        END MAP
 
        MAP
 
         INTEGER*4 END_LIST
 
        END MAP
 
       END UNION
 
      END STRUCTURE
 
      !описание записной переменной
 
      RECORD /ITMLST/ JPI_LIST
 
      !буфера для SYS$GETJPIW
 
      INTEGER*4 PRIORITY,
 
      2         PRIORITY_LEN
 


                                - 54 -
 
                          00152-01 97 09-2
 
 
      !присвоение значений полей
 
      JPI_LIST.BUFLEN = 4
 
      JPI_LIST.CODE = JPI$_PRI
 
      JPI_LIST.BUFADR = %LOC(PRIORITY)
 
      JPI_LIST.RETLENADR = %LOC(PRIORITY_LEN)
 
      JPI_LIST.END_LIST = 0
 
      !вызов SYS$GETJPIW
 
      STATUS = SYS$GETJPIW (...
 
      2                     JPI_LIST...)
 
 
           Хранится запись в  памяти  в  виде  последовательности
 
      значений. Первое поле хранится первым, последнее -  послед-
 
      ним. Свободных промежутков  между  полями  нет.  В  массиве
 
      записей каждый элемент хранится тем же способом без свобод-
 
      ных промежутков между элементами массива.
 
 
 
           1.10.1. Определение структуры записи
 
 
           Структура записи определяется количеством, порядком  и
 
      типом ее внутренних полей. Для определения структуры записи
 
      используется структурный блок вида:
 
      STRUCTURE /имя структуры/
 
        Описание полей
 
      END STRUCTURE
 
           Оператор STRUCTURE начинает структурный  блок  и  дает
 
      имя структуре записи. Каждое описание поля определяет  одно
 
      поле записи, порядок полей определяется порядком  описаний.
 
      Имя поля не должно повторяться в  структурном  блоке  более
 
      одного раза. Оператор END STRUCTURE заканчивает структурный
 
      блок. Содержимое структурного блока не размещается в  памя-
 
      ти, этот блок определяет формат записи и дает  имена  полям


                                - 55 -
 
                          00152-01 97 09-2
 
 
      записи. Для создания записи (в памяти) используется  опера-
 
      тор RECORD.
 
           Примечание. Так как в ссылке на отдельные поля исполь-
 
      зуется точка, то следует  избегать  использования  операций
 
      отношения (.EQ., .XOR. и т.д.), логических констант (.TRUE.
 
      или .FALSE.) и логических операций ( .AND., .NOT. и т.д.) в
 
      качестве имен полей.
 
           Поле записи может содержать данное с указателем  типа,
 
      подструктуру или  об'единение.  Типы  полей  внутри  записи
 
      могут от поля к полю меняться.
 
 
 
           1.10.1.1. Данные с указателем типа
 
 
           Поле записи  может  содержать  переменную  или  массив
 
      любого допустимого в языке фортран типа. Описание поля  для
 
      таких данных имеет вид соответствующего описания типа. Мас-
 
      сивы с переменной или воспринимаемой длиной, а также строки
 
      символов передаваемой длины в структурных блоках недопусти-
 
      мы.
 
           Пример.
 
           Определяется структура записи,  содержащая  три  поля:
 
      первое - поле типа BYTE с именем CLASS, второе - поле  типа
 
      INTEGER*2 с именем WIDTH, а третье - поле типа BYTE с  име-
 
      нем LENGTH:
 
      STRUCTURE /CHARACTERISTICS/
 
        BYTE    CLASS
 
        INTEGER*2 WIDTH
 
        BYTE LENGTH(4)
 
      END STRUCTURE
 
           Все требуемые разномерности массива должны быть заданы
 


                                - 56 -
 
                          00152-01 97 09-2
 
 
      в операторах описания полей; оператор DIMENSION для опреде-
 
      ления имен полей использовать нельзя.
 
           Оператор IMPLICIT на имена полей не действует;  каждое
 
      имя должно иметь явное об'явление типа в описании имени. По
 
      умолчанию значения всех полей изначально неопределены.  Для
 
      задания начального значения полю это значение  должно  быть
 
      указано за именем поля и отделено косыми чертами (в  опера-
 
      торе DATA ссылки на записи  не  разрешены).  Если  значение
 
      поля в структурном блоке инициализировано  то  все  записи,
 
      об'являемые  с  использованием  этого  структурного  блока,
 
      получаT указанное в нем значение поля.
 
           Язык фортран представляет пользователю имя  псевдополя
 
      %FILL, которое создает пустое пространство  внутри  записи.
 
      Например, %FILL используется для того, чтобы создать проме-
 
      жуток в один байт между полями FLAG и WORD:
 
      STRUCTURE /ALIGNED/
 
        BYTE      FLAG,
 
      2           %FILL
 
        INTEGER*2 WORD
 
      END STRUCTURE
 
 
 
           1.10.1.2. Подструктуры
 
 
           В качестве поля записи  может  быть  об'явлена  другая
 
      структура. Такая  внутренняя  структура  (подструктура)  не
 
      может иметь то же имя, что и внешняя структура (определение
 
      структуры не может включать себя же ни на каком уровне вло-
 
      жения). Описания полей для подструктур могут быть как  опе-
 
      раторами RECORD, так и структурными блоками.
 
           Если для описания поля используется  оператор  RECORD,
 


                                - 57 -
 
                          00152-01 97 09-2
 
 
      то имя заданной структуры должно быть  определено  заранее.
 
      Если описание поля выполняет  структурный  блок,  то  имена
 
      всех полей подструктуры (за исключением %FILL),  не  должны
 
      внутри подструктуры повторяться.
 
           Пример.
 
           Определяется  структура  APPOINTMENT,  содержащая  два
 
      поля: поле APP_DATE является подструктурой типа DATE,  поле
 
      APP_TIME - подструктурой типа TIME.
 
           Оператор STRUCTURE внутри другого  структурного  блока
 
      может включать в себя список имен полей; блок  подструктуры
 
      определяет формат каждого имени из списка:
 
      STRUCTURE /DATE/
 
       INTEGER*4 DAY,
 
      2          MONTH,
 
      2          YEAR
 
      END STRUCTURE
 
      STRUCTURE /APPOINTMENT/
 
       RECORD /DATE/ APP_DATE       !Поле 1
 
       STRUCTURE /TIME/ APP_TIME    !Поле 2
 
        INTEGER*4 HOUR,
 
      2           MINUTE
 
       END STRUCTURE
 
      END STRUCTURE
 
 
 
           1.10.1.3. Об'единения
 
 
           Об'единение - это  два  или  больше  полей,  логически
 
      имеющих общее местоположение в памяти. Описания  полей  для
 
      об'единений имеют вид:
 


                                - 58 -
 
                          00152-01 97 09-2
 
 
      STRUCTURE /имя структуры/
 
       UNION
 
        Описание карт
 
       END UNION
 
      END STRUCTURE
 
           Описание карты имеет вид:
 
      MAP
 
       описание полей
 
      END MAP
 
           Несколько карт разделяют одну и ту же область структу-
 
      ры записи. Размером разделяемой области будет  размер  наи-
 
      большей карты из блока UNION. В  каждый  момент  выполнения
 
      программы определEна ровно одна из карт об'единения.  Одна-
 
      ко, если карта перекрывается меньшей картой, то  все  непе-
 
      рекрытые области большей карты остаются неизменными.
 
           Пример.
 
           Устанавливается запись для приема характеристик терми-
 
      нала. Так как основные характеристики возвращаются  в  трех
 
      младших байтах четвертого поля (поле BASIC), а длина записи
 
      возвращается в старшем байте того же поля  (четвертый  эле-
 
      мент поля LENGTH), то об'единение действует как эквивалент-
 
      ность для полей BASIC и LENGTH:
 
      STRUCTURE /CHARACTERISTICS/
 
        BYTE      CLASS    !Поле 1
 
      2           TYPE     !Поле 2
 
        INTEGER*2 WIDTH    !Поле 3
 
        UNION              !Поле 4
 
         MAP
 
          INTEGUER*4 BASIC
 
         END MAP


                                - 59 -
 
                          00152-01 97 09-2
 
 
         MAP
 
          BYTE LENGTH(4)
 
         END MAP
 
        END UNION
 
        INTEGER*4 EXTENDED !Поле 5
 
      END STRUCTURE
 
           Действие об'единения отличается от действия  оператора
 
      EQUIVALENCE тем, что данные в операторе EQUIVALENCE связан-
 
      ны с общим местоположением одновременно,  в  то  время  как
 
      карты в блоке об'единения связаны с одним и тем же местопо-
 
      ложением альтернативно.
 
           Другое распространенное применение  блока  об'единения
 
      состоит в том, что он позволяет  дать  несколько  различных
 
      определений одной и той же записи. Например, многие систем-
 
      ные служебные процедуры требуют входной информации  в  виде
 
      списка об'ектов,  где  каждый  об'ект  состоит  из  четырех
 
      отдельных полей, и в конце списка - длинного слова со  зна-
 
      чением 0  для  обозначения  конца  списка  об'ектов.  Чтобы
 
      задать список об'ектов как массив записей, нужно иметь воз-
 
      можность использовать одну и ту же запись и как четырехэле-
 
      ментный об'ект, и как одноэлементный конец списка.
 
           Пример.
 
           Об'единение   выполняет  роль   двойного   определения
 
      записи ITMLST:
 
      STRUCTURE /ITMLST/
 
       UNION
 
        MAP
 
         INTEGER*2 BUFLEN,
 
      2            CODE
 
         INTEGER*4 BUFADR,


                                - 60 -
 
                          00152-01 97 09-2
 
 
      2            RETLENADR
 
        END MAP
 
        MAP
 
         INTEGER*4 END_LIST /0/
 
        END MAP
 
       END UNION
 
      END STRUCTURE
 
 
 
           1.10.2. Об'явление записи
 
 
           Для описания (об'явления) записи используется оператор
 
      RECORD в форме:
 
      RECORD /имя_структуры/имя_записи_список
 
      2       [,/имя_структуры/имя_записи_список]
 
      2       !аналогичные продолжения   .
 
           Имя_структуры есть имя структуры записи,  определенной
 
      ранее в структурном блоке. Имя_записи_список  есть  список,
 
      содержащий одно или больше имен переменных,  имен  массивов
 
      или  описателей  массивов,  разделенных  запятыми  (массивы
 
      переменной длины допустимы, если они  являются  формальными
 
      параметрами).
 
           Пример.
 
           Создаются  три  об'екта   структуры   записи:   запись
 
      DVI_LIST и четырехэлементный массив записей  JPI_LIST  (оба
 
      об'екта типа ITMLST), и запись IOSTAT (типа IOSB):
 
      ! список об'ектов для SYS$GETJPI
 
      STRUCTURE /ITMLST/
 
       UNION
 
        MAP
 
         INTEGER*2 BUFLEN,
 


                                - 61 -
 
                          00152-01 97 09-2
 
 
      2            CODE
 
         INTEGER*4 BUFADR,
 
      2            RETLENADR
 
        END MAP
 
        MAP
 
         INTEGER*4 END_LIST
 
        END MAP
 
       END UNION
 
      END STRUCTURE
 
      RECORD /ITMLST/ DVI_LIST,     !об'ект 1
 
      2               JRI_LIST(4)   !об'ект 2
 
      ! блок состояния в/в
 
      STRUCTURE /IOSB/
 
       INTEGER*2 STATUS
 
       INTEGER*2 NOTHING
 
       INTEGER*4 ZERO /0/
 
      END STRUCTURE
 
      RECORD /IOSB/ IOSTAT          !об'ект 3
 
 
 
           1.10.3. Ссылка на записи
 
 
           На записи можно ссылаться двумя способами:
 
           1) уточненная ссылка. Ссылка на поле  записи,  имеющее
 
      тип, то есть на поле, которое само не является записью:
 
      имя_записи [.имя_подзаписи...] .имя_имеющее_тип
 
           2) неуточненная ссылка. Ссылается на структуру записей
 
      или подструктуру:
 
      имя_записи[.имя_подзаписи...]
 
           Как правило, уточненную ссылку можно применять  всюду,
 
      где контекст  разрешает  использовать  данные  этого  типа.
 


                                - 62 -
 
                          00152-01 97 09-2
 
 
      однако, уточненная ссылка на запись не разрешена в операто-
 
      рах COMMON, SAVE, NAMELIST или EQUIVALENCE.
 
           Неуточненная ссылка имеет более ограниченное  примене-
 
      ние и может применяться в следующих контекстах:
 
           1) операторы присваивания. Все  поля  записи  в  левой
 
      части получают значения  соответствующих  полей  записи  из
 
      правой части. Значения с обеих сторон оператора  присваива-
 
      ния должны быть записями, обе записи  должны  быть  описаны
 
      одной и той же структурой;
 
           2) COMMON. Запись может быть  помещена  в  блок  общей
 
      памяти;
 
           3) FUNCTION. Запись может быть как формальным,  так  и
 
      фактическим параметром. Количество,  тип  и  порядок  полей
 
      фактического параметра должны совпадать с теми же величина-
 
      ми формального параметра (порядок карт  внутри  об'единения
 
      не существенен);
 
           4) ENTRY. Запись можно задать в качестве как  формаль-
 
      ного, так и фактического параметра. Количество, тип и поря-
 
      док полей в фактическом параметре должны совпадать  с  теми
 
      же величинами в формальном параметре (порядок карт в  об'е-
 
      динении значения не имеет);
 
           5) SUBROUTINE. Запись может быть как фактическим,  так
 
      и формальным параметром. Количество, порядок и тип полей  в
 
      фактическом параметре должны совпадать с этими же величина-
 
      ми в формальном параметре (порядок карт в об'единении  зна-
 
      чения не имеет);
 
           6)  VOLATILE.  Препятствует  оптимизации  транслятором
 
      операций над записями;
 
           7) ввод-вывод. Неуточненную  ссылку  на  запись  можно
 
      использовать в операторах  бесформатного  ввода-вывода.  Не


                                - 63 -
 
                          00152-01 97 09-2
 
 
      допускается использование в операторах форматного доступа к
 
      списку и операторах ввода-вывода NAMELIST.
 
           Если  обозначения  записей  и  массивов  употребляются
 
      одновременно,  нужно  быть  внимательным  при   расстановке
 
      индексов. Для ссылки на один  элемент  из  массива  записей
 
      индекс следует ставить после имени записи перед всеми  име-
 
      нами полей, например:
 
      STRUCTURE /ORDER_REC/
 
        BYTE            FLAGS(3)
 
        CHARACTER*80    CUSTOMER
 
        INTEGER*4       ITEM_CODE
 
      END STRUCTURE
 
      RECORD /ORDER_REC/ ORDERS(2500)
 
      ORDERS(1).ITEM_CODE
 
           Для ссылки на массив, который представляет собой  поле
 
      в записи, индекс ставится после имени поля, например:
 
      STRUCTURE /ORDER_REC/
 
        BYTE           FLAGS(3)
 
        CHARACTER*80   CUSTOMER
 
        INTEGER*4      ITEM_CODE
 
      END STRUCTURE
 
      RECORD /ORDER_REC/ TEMPORARY
 
      TEMPORARY.FLAGS(1)
 
 
 
           1.10.4. Хранение в памяти структур записей
 
 
           При  наличии  нескольких  часто  применяемых  структур
 
      записей рекомендуется их хранить в текстовой  библиотеке  и
 
      для ссылки на них использовать в программах на языке  форт-
 
      ран  оператор  INCLUDE.  Недостаток  подобного  определения
 


                                - 64 -
 
                          00152-01 97 09-2
 
 
      записей в том, что человек, читающий  такую  программу,  не
 
      имеет  возможности  посмотреть  определение  полей  записи.
 
      Обычно в библиотеку структур включаются  структуры  списков
 
      об'ектов, которые требуются многими  системными  служебными
 
      процедурами.
 
           Пример.
 
           Для  включения  в  библиотеку  расположенной  в  файле
 
      ITMLST.TXT структуры:
 
      STRUCTURE /ITMLST/
 
       UNION
 
        MAP
 
         INTEGER*2 BUFLEN,
 
      2            CODE
 
         INTEGER*4 BUFADR,
 
      2            RETLENADR
 
        END MAP
 
        MAP
 
         INTEGER*4 END_LIST /0/
 
        END MAP
 
       END UNION
 
      END STRUCTURE
 
      Необходимо  создать  библиотеку  и  включить  в  нее   файл
 
      ITMLST.TXT командами на уровне языка DCL:
 
      $ LIBRARY/CREATE/TEXT $DISK1:[DEV.LIBRARY]STRUCTURES
 
      $ LIBRARY/REPLACE/TEXT $DISK1:[DEV.LIBRARY]STRUCTURES-
 
      $ ITMLST
 
           Для включения структурного блока  ITMLST  в  программу
 
      используется оператор INCLUDE. Оператор RECORD для  опреде-
 
      ления записи типа ITMLST по-прежнему необходим, например:
 


                                - 65 -
 
                          00152-01 97 09-2
 
 
      !включение опеделения структуры ITMLST
 
      INCLUDE '$DISK1:[DEV.LIBRARY] STRUCTURES (ITMLST)'
 
      ! описание записи
 
      RECORD /ITMLST/ JPI_LIST
 
      !буферы для SYS$GETJPI
 
      INTEGER*4 PRIORITY,
 
      2         PRIORITY_LEN
 
      ! присвоить значения полям
 
      JPI_LIST.BUFLEN = 4
 
      JPI_LIST.CODE = JPI$_PRI
 
      JPI_LIST.BUFADR = %LOC(PRIORITY_LEN)
 
      ! вызов SYS$GETJPIW
 
      STATUS = SYS$GETJPIW (,,,
 
      2                     JPI_LIST,,,)
 
 
 
           1.11. Системная информация
 
 
           1.11.1. Статистика от таймера
 
 
           Язык фортран позволяет получить следующую статистичес-
 
      кую информацию от таймера:
 
           1) затраченное время (время, прошедшее с момента уста-
 
      новления таймEра для данной программы);
 
           2) время  работы  центрального  процессора  с  момента
 
      установления таймера;
 
           3)  буферизованный  ввод/вывод.  Количество   операций
 
      буферизованного ввода/вывода, имеющих место с момента уста-
 
      новки таймера;
 
           4) непосредственный  ввод/вывод.  Количество  операций
 
      непосредственного  ввода/вывода  имевших  место  с  момента
 
      установки таймера;


                                - 66 -
 
                          00152-01 97 09-2
 
 
           5) страничные отказы. Количество  страничных  отказов,
 
      произошедших после установки таймера.
 
           Эту статистику можно  получить  посредством  следующих
 
      процедур [2]:
 
           LIB$INIT_TIMER - отводится  и  инициализируется  место
 
      для получения статистики.  Необходимо  задать  единственный
 
      параметр, описав его как целую переменную со  значением  0,
 
      чтобы гарантировать автономность отсчета времени для  прог-
 
      раммы (например, на тот случай, когда программная  единица,
 
      вызывающая программную единицу, тоже устанавливает таймер).
 
      Когда параметр задан, система помещает  информацию  в  спе-
 
      циально отведенную область динамической памяти;
 
           LIB$SHOW_TIMER - выдает одну из статистических  харак-
 
      теристик или всю статистику. Первый  параметр  должен  быть
 
      той  же  целой  переменной,   которая   была   задана   для
 
      LIB$INIT_TIMER (ее значение не должно  изменяться).  Второй
 
      параметр задается для  получения  отдельной  статистической
 
      характеристики, а не всей статистики.
 
           Можно  позволить  системе   выводить   статистику   на
 
      SYS$OUTPUT (по умолчанию) или обрабатывать эту статистику с
 
      помощью собственной подпрограммы. Чтобы  обрабатывать  ста-
 
      тистику в подпрограмме, необходимо задать третьим  парамет-
 
      ром имя этой подпрограммы (это имя должно быть  EXTERNAL  и
 
      INTEGER*4). Допускается передать программе  один  параметр,
 
      задав его как четвертый параметр  LIB$SHOW_TIMER.  Подпрог-
 
      рамма обработки должна быть  написана  как  функция  целого
 
      типа, возвращающая код завершения (в случае успеха - едини-
 
      ца),   который   является   кодом   завершения    процедуры
 
      LIB$SHOW_TIMER. Подпрограмме будут переданы два параметра:
 
           1) строка символов передаваемой длины, содержащая фор-


                                - 67 -
 
                          00152-01 97 09-2
 
 
      матированную статистику;
 
           2) значение четвертого параметра (если он  был  задан)
 
      процедуры LIB$SHOW_TIMER;
 
           LIB$STAT_TIMER  -  получение  одной  неформатированной
 
      статистической характеристики. Статистическая характеристи-
 
      ка задается первым параметром. Область  хранения  для  этой
 
      статистики задается вторым параметром: массив из двух  эле-
 
      ментов типа INTEGER*4 для затраченного времени и одно  дан-
 
      ное типа IINTEGER*4 для любой другой  характеристики.  Пос-
 
      ледний параметр должен быть  той  самой  целой  переменной,
 
      которая была задана для LIB$INIT_TIMER;
 
           LIB$FREE_TIMER - эту  процедуру  следует  вызывать  по
 
      окончании работы с таймером для снятия программы с обслужи-
 
      вания. Параметр должен быть той же целой переменной,  кото-
 
      рая была задана для LIB$INIT_TIMER.
 
           Для отведения памяти  под  таймер  необходимо  вызвать
 
      LIB$INIT_TIMER. Перед выходом из программной  единицы  сле-
 
      дует вызвать LIB$FREE_TIMER. В промежутке между этими двумя
 
      процедурами можно вызывать  неоднократно  LIB$SHOW_TIMER  и
 
      LIB$STAT_TIMER.
 
           Пример.
 
           Вызывается LIB$SHOW_TIMER  и  используется  написанная
 
      пользователем подпрограмма либо для  вывода  статистики  на
 
      дисплей, либо для записи ее в файл:
 
      ! параметры таймера
 
      INTEGER*4 TIMER_ADDR,
 
      2         TIMER_DATA,
 
      2         TIMER_ROUTINE
 
      EXTERNAL  TIMER_ROUTINE
 
      ! описать библиотечные процедуры как функции


                                - 68 -
 
                          00152-01 97 09-2
 
 
      INTEGER*4 LIB$INIT_TIMER,
 
      2         LIB$SHOW_TIMER
 
      EXTERNAL  LIB$INIT_TIMER,
 
      2         LIB$SHOW_TIMER
 
      ! рабочие переменные
 
      CHARACTER*5 REQUEST
 
      INTEGER*4   STATUS
 
      ! запрос пользователя: WRITE или FILE
 
      INTEGER*4 WRITE,
 
      2         FILE
 
      PARAMETER (WRITE = 1,
 
      2          FILE = 2)
 
      ! принять запрос
 
      WRITE (UNIT = *, FMT = '($,A)')' 'запрос:'
 
      ACCEPT *, REQUEST
 
      IF (REQUEST .EQ. 'WRITE') TIMER_DATA = WRITE
 
      IF (REQUEST .EQ. 'FILE') TIMER_DATA = FILE
 
      ! установить таймер
 
      STATUS = LIB$INIT_TIMER (TIMER_ADDR)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! продолжение программы:
 
      ! принять статистики
 
      STATUS = LIB$SHOW_TIMER (TIMER (TIMER_ADDR,,
 
      2                               TIMER_ROUTINE,
 
      2                               TIMER_DATA)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! продолжение программы:
 
      ! освободить таймер
 
      STATUS = LIB$FREE_TIMER (TIMER_ADDR)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))


                                - 69 -
 
                          00152-01 97 09-2
 
 
      ! функция обработки статистики от таймера
 
      INTEGER FUNCTION TIMER_ROUTINE (STATS,
 
      2                               TIMER_DATA)
 
      ! формальные параметры
 
      CHARACTER*(*) STATS
 
      INTEGER TIMER_DATA
 
      ! номер логического устройства для файла
 
      INTEGER STATS_FILE
 
      ! запрос пользователя
 
      INTEGER WRITE,
 
      2       FILE
 
      PARAMETER (WRITE = 1,
 
      2          FILE = 2)
 
      ! код возврата
 
      INTEGER SUCCESS,
 
      2       FAILURE
 
      PARAMETER (SUCCESS = 1,
 
      2          FAILURE = 0)
 
      ! установить состояние успешного возврата
 
      TIMER_ROUTINE = SUCCESS
 
      ! вывести статистики или записать их в файл STATS.DAT
 
      IF (TIMER_DATA .EQ. WRITE) THEN
 
        TYPE *, STATS
 
      ELSE IF (TIMER_DATA .EQ. FILE) THEN
 
        CALL LIB$GET_LUN (STATS_FILE)
 
        OPEN (UNIT = STATS_FILE,
 
      2       FILE = 'STATS.DAT')
 
      WRITE (UNIT = STATS_FILE,
 
      2      FMT = '(A)') STATS
 
      ELSE


                                - 70 -
 
                          00152-01 97 09-2
 
 
        TIMER_ROUTINE = FAILURE
 
      END IF
 
      END
 
           Для получения  более  детальной  системной  информации
 
      можно  использовать  системные   процедуры   SYS$GETTIM   и
 
      SYS$GETSYI.
 
 
 
           1.11.2. Системное время
 
 
           МОС ВП распознает два типа времени:
 
           1) абсолютное время - определенная  дата  и/или  время
 
      суток;
 
           2) интервал времени - число дней  и/или  более  мелких
 
      единиц времени.
 
           Абсолютное время задается таким образом, что на полную
 
      запись даты и времени требуется строка из 23 символов (зна-
 
      ки препинания обязательны):
 
           дд-ммм-гггг чч:мм:сс.сс
 
      где  дд - число месяца (два символа);
 
           ммм - первые  три  буквы  названия  месяца на верхнем
 
                 регистре (три символа);
 
           гггг - год (четыре символа);
 
           чч - время суток в часах (два символа);
 
           мм - минуты;
       [6~
           сс.сс - секунды и сотые секунд.
 
           Интервальное время задается так,  что  полная  дата  и
 
      время суток требует строки в 16 символов, знаки  препинания
 
      обязательны:
 
           дддд чч:мм:сс.сс
 
      где  дддд - дни;
 


                                - 71 -
 
                          00152-01 97 09-2
 
 
           чч - часы;
 
           мм - минуты;
 
           сс.сс - секунды и сотые секунд.
 
           Внутри системы абсолютное время поддерживается в  виде
 
      целого значения, представляющего число  интервалов  времени
 
      величиной в 100 наносекунд,  прошедших  после  полуночи  18
 
      ноября 1858 года (дата отсчета для  системы).  Интервальное
 
      время  хранится  в  виде   значения,   являющегося   числом
 
      100-наносекундных единиц. Абсолютное время хранится в  виде
 
      положительного значения, дельта-время - в виде  отрицатель-
 
      ного, оба в виде квадрослов  (в  языке  фортран  внутреннее
 
      время следует определять  в  виде  двухэлементного  массива
 
      типа INTEGER*4).
 
 
 
           1.11.2.1. Текущее время
 
 
           Процедура LIB$DATE_TIME  возвращает  строку  символов,
 
      содержащую текущую дату и время в формате абсолютного  вре-
 
      мени. Полная строка  требует  описания  CHARACTER*23.  Если
 
      задать строку короче, то значение будет также укорочено.  А
 
      задание строки CHARACTER*11 дает возможность получить толь-
 
      ко дату. Например, на экран выводится текущая дата и время:
 
 
      ! форматированные дата и время
 
      CHARACTER*23 DATETIME
 
      ! состояние и библиотечные процедуры
 
      INTEGER*4 STATUS  ,
 
      2         LIB$DATE_TIME
 
      EXTERNAL  LIB$DATE_TIME
 
      STATUS = LIB$DATE_TIME (DATETIME)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))


                                - 72 -
 
                          00152-01 97 09-2
 
 
      TYPE *, DATETIME
 
 
           Текущую дату и время во внутреннем формате можно полу-
 
      чить с помощью системной  служебной  процедуры  SYS$GETTIM.
 
      Произвести  преобразование  в  символьный  формат  можно  с
 
      помощью системной служебной процедуры SYS$ASCTIM или дирек-
 
      тивы системной служебной процедуры SYS$FAO; обратное преоб-
 
      разование во внутренний формат выполняется с помощью проце-
 
      дуры SYS$BINTIM.
 
 
 
           1.11.2.2. Работа со значениями времени
 
 
           Обычные процедуры для  работы  со  значениями  времени
 
      (например, нахождения разности между двумя значениями абсо-
 
      лютного времени в виде интервала времени, добавление интер-
 
      вала к абсолютному времени или вычитание интервала из абсо-
 
      лютного времени) таковы:
 
           1) преобразование во внутренний формат. Получение вре-
 
      мени из внутреннего формата или преобразование  времени  во
 
      внутренний формат. Для преоьразования текущего  времени  во
 
      внутренний формат применяется SYS$BINTIM, а  для  получения
 
      текущего  времени  из  внутреннего   формата   используется
 
      SYS$GETTIM;
 
           2) арифметические операции с временем. Для сложения  и
 
      вычитания значений времени используются процедуры  LIB$ADDX
 
      и LIB$SUBX [2], так как время задается целочисленными  мас-
 
      сивами. При работе с интервальным временем  нужно  помнить,
 
      что оно хранится в виде отрицательных чисел. Например, что-
 
      бы к абсолютному времени прибавить интервальное, нужно вос-
 
      пользоваться операцией вычитания;
 
           3) форматирование  времени.  Показание  времени  можно


                                - 73 -
 
                          00152-01 97 09-2
 
 
      отформатировать с помощью SYS$BINTIM или SYS$FAO.
 
           Пример.
 
           Вычисляется разность между текущим временем и  введен-
 
      ным в абсолютном формате временем, а затем результат  выво-
 
      дится как интервальное время. Если введенное  время  меньше
 
      текущего, то разность будет отрицательным значением (интер-
 
      вальное время) и выводится непосредственно. Если  введенное
 
      время больше текущего, то разность будет положительным зна-
 
      чением (абсолютное  время)  и  перед  выводом  должно  быть
 
      преобразовано в интервальное время. Чтобы сменить  абсолют-
 
      ное время на интервальное, применяется вычитание из нуля  с
 
      помощью процедуры LIB$SUBX:
 
      ! внутренние времена
 
      INTEGER*4 CURRENT_TIME (2),
 
      2         PAST_TIME (2),
 
      2         TIME_DIFFERENCE (2),
 
      2         ZERO (2)
 
      DATA ZERO /0.0/
 
      ! форматированные времена
 
      CHARACTER*23 PAST_TIME_F
 
      CHARACTER*11 TIME_DIFFERENCE_F
 
      ! состояние
 
      INTEGER*4 STATUS
 
      ! функции целого типа
 
      INTEGER*4 SYS$GETTIM,
 
      2         LIB$GET_INPUT,
 
      2         SYS$BINTIM,
 
      2         LIB$SUBX,
 
      2         SYS$ASCTIM
 
      ! принять текущее время


                                - 74 -
 
                          00152-01 97 09-2
 
 
      STATUS = SYS$GETTIM (CURRENT_TIME)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! принять прошедшее время и преобразовать во внутренний
 
      !формат
 
      STATUS = LIB$GET_INPUT (PAST_TIME_F,
 
      2                       'прошедшее время
 
      2                       (в абсолютный формат):')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SYS$BINTIM (PAST_TIME_F,
 
      2                    PAST_TIME)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! вычесть прошедшее время из текущего
 
      STATUS = LIB$SUBX (CURRENT_TIME,
 
      2                  PAST_TIME,
 
      2                  TIME_DIFFERENCE)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! если результирующее время в абсолютном формате
 
      ! (положительное значение - следовательно, наиболее
 
      ! значащий бит не установлен), то перевести его в
 
      ! дельта-время
 
      IF (.NOT. (BTEST (TIME_DIFFERENCE(2), 31))) THEN
 
      STATUS = LIB$SUBX (ZERO,
 
      2                  TIME_DIFFERENCE,
 
      2                  TIME_DIFFERENCE)
 
      END IF
 
      ! форматировать разностное время и вывести его
 
      STATUS = SYS$ASCTIM (,TIME_DIFFERENCE_F,
 
      2                    TIME_DIFFERENCE,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      TYPE *, 'разностное время = ', TIME_DIFFERENCE_F


                                - 75 -
 
                          00152-01 97 09-2
 
 
      END
 
           Для определения интервала времени на уровне даты  дос-
 
      таточно использовать процедуру LIB$DAY.
 
 
 
 
                2. Ввод и синтаксический анализ команд
 
 
 
           Для вызова программы на уровне  командного  языка  DCL
 
      необходимо определить соответствующую команду в  файле  CLD
 
      (файл определения командного языка) и указать его в команде
 
      SET COMMAND [3].
 
           Внутри программы для анализа командной строки, введен-
 
      ной пользователем при вызове этой  программы,  используются
 
      процедуры командного языка CLI.
 
 
 
           2.1. Описание команды
 
 
           Команда определяется в файле CLD. Файл CLD по  умолча-
 
      нию имеет тип CLD;  в  нем  можно  определять  более  одной
 
      команды.
 
 
 
           2.1.1. Имя и образ команды
 
 
           Определение команды начинается оператором DEFINE VERB,
 
      за которым возможен условный оператор IMAGE. Можно снабдить
 
      команду альтернативными именами с помощью условного  опера-
 
      тора SYNONYM. Следует убедиться,  что  имя  команды  и  его
 
      синонимы определяются однозначно первыми четырьмя символами
 
      среди прочих команд системы.  Например,  команда  с  именем
 
      INCOME, чей образ (выполняемая  программа,  которая  должна
 
      вызываться при вводе INCOME как команды  DCL)  находится  в
 


                                - 76 -
 
                          00152-01 97 09-2
 
 
      файле WORKDISK:[INCOME]INCOME.EXE (тип  файла  образа  есть
 
      ехе по умолчанию), определяется так:
 
 
      DEFINE VERB INCOME
 
      IMAGE "WORKDISK:[INCOME]INCOME"
 
 
           Если оператор IMAGE не  задан,  то  имя  файла  образа
 
      будет по умолчанию положено совпадающим с именами  команды,
 
      каталог будет взят SYS$SYSTEM, а тип файла  ехе.  Например,
 
      если бы в примере оператор IMAGE был бы  опущен,  то  образ
 
      был бы задан как файл SYS$SYTEM:INCOME.EXE.  Если  оператор
 
      IMAGE опущен или определяет только имя файла, то для указа-
 
      ния образа можно использовать  логическое  имя,  идентичное
 
      команде или заданному имени файла (эквивалентное имя, поло-
 
      женное файлу образа). Например, команду INCOME можно  опре-
 
      делить без использования оператора IMAGE , введя логическое
 
      имя следующим образом:
 
      DEFINE INCOME WORKDISK:[INCOME] INCOME
 
           Задание полной спецификации файла образа в  файле  CLD
 
      предотвращает  нежелательные  совпадения  логических  имен.
 
      Например, если опустить оператор IMAGE для INCOME  и  поло-
 
      жить файл образа в  SYS$SYSTEM,  а  пользователь  определит
 
      INCOME для какой-нибудь другой цели,  команда  работать  не
 
      будет. Отказ от задания имени образа в файле CDL  позволяет
 
      перемещать образ или использовать иные  версии  образа  без
 
      переопределения команды.
 


                                - 77 -
 
                          00152-01 97 09-2
 
 
           2.1.2. Параметры
 
 
           Параметры определяются с помощью оператора  PARAMETER.
 
      Параметры должны иметь имена P1, P2 и т.д.  до  P8.  Нельзя
 
      опускать первый параметр  или  пропускать  параMетры,  хотя
 
      задавать их можно не  по  порядку.  Например,  имеющая  два
 
      параметра команда INCOME может быть определена так:
 
 
      DEFINE VERB INCOME
 
      IMAGE "SYS$SYSTEM:INCOME"
 
      PARAMETER P1
 
      PARAMETER P2
 
 
 
           2.1.2.1. Метки
 
 
           Оператор LABEL предоставляет программе средства  обра-
 
      щения к параметру. По умолчанию метка относится  (LABEL  не
 
      задан) к имени параметра  (P1,  P2  и  т.д.).  Например,  в
 
      качестве параметров можно передать имена меток, отличных от
 
      P1 и P2:
 
 
      DEFINE VERB INCOME
 
      IMаGE "SYS$SYSTEM:INCOME"
 
      PARAMETER P1
 
              LABEL = STATS_FILE
 
      PARAMETER P2
 
              LABEL = REPORT_FILE
 


                                - 78 -
 
                          00152-01 97 09-2
 
 
           2.1.2.2. Обязательные параметры
 
 
           Параметр может быть  сделан  обязательным  посредством
 
      указания возможности REQUIRED в операторе VALUE. Все обяза-
 
      тельные параметры должны быть  перечислены  перед  необяза-
 
      тельными. Если пользователь опустит в интерактивном  режиме
 
      обязательный параметр, то  DCL  вводит  режим  подсказки  и
 
      выдает подсказку, запрашивая опущенный  обязательный  пара-
 
      метр. Для продолжения выполнения команды необходимо  ввести
 
      значение или ввести два раза подряд кавычки  (""),  обозна-
 
      чающие пустое значение. Пропуск обязательного  параметра  в
 
      неинтерактивном режиме приводит к ошибке.
 
           Пример.
 
           Два параметра команды INCOME  являются  обязательными.
 
      Подсказка для пропущенного  параметра  задается  с  помощью
 
      оператора PROMPT (можно этого не делать,  оставив  подсказ-
 
      ку - метку параметра, как принято по  умолчанию).  В  любом
 
      случае подсказка появляется на экране с подчеркиванием впе-
 
      реди и двоеточием со знаком табуляции после:
 
 
      DEFINE VERB INCOME
 
      IMAGE "SYS$SYSTEM:INCOME"
 
      PARAMETER P1
 
                LABEL = STATS_FILE
 
                VALUE(REQURIED)
 
                PROMPT = "STATISTICS FILE"
 
      PARAMETER P2
 
                LABEL = REPORT_FILE
 
                VALUE(REQUIRED)
 
                PROMPT = "REPORT FILE"
 


                                - 79 -
 
                          00152-01 97 09-2
 
 
           2.1.2.3. Значения
 
 
           При отсутствии возможности  REQUIRED  параметры  будут
 
      необязательными. Для необязательного параметра можно  опре-
 
      делить значение по умолчанию с помощью возможности  DEFAULT
 
      оператора VALUE. Для ввода значений,  разделенных  запятыми
 
      или знаками плюс, необходимо задать  в  условном  операторе
 
      VALUE возможность LIST.
 
           Пример.
 
           Два параметра INCOME являются необязательными и  снаб-
 
      жены значениями по умолчанию.  Имеется  возможность  задать
 
      для P2 список значений. Для всего списка можно задать  одно
 
      значение по умолчанию:
 
 
      DEFINE VERB INCOME
 
      IMAGE "SYS$SYSTEM:INCOME"
 
      PARAMETER P1
 
                LABEL = STATS_FILE
 
                VALUE (DEFAULT = "STATS.SAV")
 
      PARAMETER P2
 
                LABEL = REPORT FILE
 
                VALUE (LIST, DEFаULT = "INCOME.RPT")
 
           Ввод значений, разделенных только знаками плюс, разре-
 
      шается возможностью CONCATENATE (вместо LIST). Чтобы разре-
 
      шить разделение значений только запятыми, задаются  LIST  и
 
      NOCONCATENATE.
 


                                - 80 -
 
                          00152-01 97 09-2
 
 
           2.1.2.4. Подсказка
 
 
           Если при интерактивной работе будет опущен  обязатель-
 
      ный параметр, DCL введет режим подсказки  и  запросит  его.
 
      Отсутствие  обязательного  параметра  при   неинтерактивной
 
      работе вызывает ошибку. Можно нажать <RETURN> для повторной
 
      подсказки, ввести <CTRL/Z>  для  прекращения  команды,  или
 
      ввести нужное значение. Если введенное значение  допустимо,
 
      то DCL запрашивает следующий  параметр  (если  необходимо).
 
      Для обязательных параметров действия  аналогичны.  Если  же
 
      параметр необязателен, то по нажатию <RETURN> команда  нач-
 
      нет выполняться, по нажатию <CTRL/Z> действие команды прек-
 
      ратится, ввод допустимого значения вызовет запрос со сторо-
 
      ны DCL на ввод следующего параметра (если он есть).
 
           Можно использовать оператор PROMPT для  задания  подс-
 
      казки-запроса обязательного или необязательного  параметра;
 
      если этого не делать, то по умолчанию подсказкой будет мет-
 
      ка параметра. Подсказки-запросы  необязательных  параметров
 
      появляются при пропуске обязательного параметра,  что  зас-
 
      тавляет DCL перейти в режим подсказки.
 
 
 
           2.1.3. Квалификаторы
 
 
           Квалификаторы   определяются   с   помощью   оператора
 
      QUALIFIER. Имя квалификатора должно стоять сразу за  словом
 
      QUALIFIER; косая черта перед именем не ставится.
 
           Пример.
 
           Команда INCOME имеет три квалификатора  и  один  пара-
 
      метр:
 
 
      DEFINE VERB INCOME
 


                                - 81 -
 
                          00152-01 97 09-2
 
 
      IMAGE "SYS$SYSTEM:INCOME"
 
      PARAMETER P1
 
                LABEL = STATS_FILE
 
                VALUE(DEFAULT = "STATS.SAV")
 
      QUALIFIER ENTER
 
      QUALIFIER FIX
 
      QUALIFIER REPORT
 
           Имя квалификатора служит меткой, если не  задано  явно
 
      другое имя с помощью оператора LABEL.
 
 
 
           2.1.3.1. Квалификаторы с отрицанием
 
 
           По умолчанию любой квалификатор  допускает  отрицание:
 
      пользователь может снабдить квалификатор приставкой NO  для
 
      явного указания его отсутствия в  командной  строке.  Можно
 
      сделать квалификатор не  допускающим  отрицания  с  помощью
 
      возможности NONNEGATABLE, например:
 
 
      DEFINE VERB INCOME
 
      IMAGE "SYS$SYSTEM:INCOME"
 
      PARAMETER P1
 
                LABEL = STATUS_FILE,
 
                VALUE (DEFAULT = "STATS.SAV")
 
      QUALIFIER ENTER
 
                NONNEGATABLE
 
      QUALIFIER FIX
 
                NONNEGATABLE
 
      QALIFIER  REPORT
 
                NONNEGATABLE
 


                                - 82 -
 
                          00152-01 97 09-2
 
 
           2.1.3.2. Квалификаторы по умолчанию
 
 
           Квалификатор присутствует по  умолчанию,  если  задана
 
      возможность DEFAULT. Квалификатор присутствует по умолчанию
 
      только в пакетных заданиях, если задана возможность  BATCH.
 
      Чтобы отменить квалификатор, присутствующий  по  умолчанию,
 
      необходимо явно указать его отрицание  (программа,  однако,
 
      может отличить явно заданный квалификатор от квалификатора,
 
      присутствующего по умолчанию).
 
           Пример.
 
           Квалификатор ENTER об'явлен присутствующим по  умолча-
 
      нию:
 
 
      DIFINE VERB INCOME
 
      IMAGE "SYS$SYSTEM:INCOME"
 
      PARAMETER P1
 
                LABEL = STATS_FILE,
 
                VALUE (DEVAULT = "STATS.SAV")
 
      QUALIFIER ENTER
 
                DEFAULT
 
      QUALIFIER FIX
 
      QUALIFIER REPORT
 
 
 
           2.1.3.3. Расположение квалификатора
 
 
           Можно ограничить положение квалификатора  в  командной
 
      строке,  задав  одно  из  следующих  значений   возможности
 
      PLACEMENT:
 
           1) GLOBAL (умолчание). Квалификатор действует  на  все
 
      параметры независимо от своего положения в командной  стро-
 
      ке;
 


                                - 83 -
 
                          00152-01 97 09-2
 
 
           2) LOCAL. Квалификатор  может  быть  поставлен  только
 
      после параметра и более чем при одном параметре. Квалифика-
 
      тор действует только на  тот  параметр  (параметры),  после
 
      которого он поставлен;
 
           3) POSITIONAL. Квалификатор может быть поставлен перед
 
      всеми параметрами или после  одного  и  больше  параметров.
 
      Если  квалификатор  предшествует  всем  параметрам,  то  он
 
      действует на все параметры. В противном случае он действует
 
      на тот параметр, (параметры), за которым он поставлен.
 
           Пример.
 
           В команде  INCOME  разрешены  различные  интерпретации
 
      квалификатора в зависимости от  того,  предшествует  ли  он
 
      всем параметрам или стоит за конкретным параметром:
 
 
      DEFINE VERB INCOME
 
      IMAGE "SYS$SYSTEM:INCOME"
 
      PARAMETER P1, VALUE (REQUIRED)
 
                   PROMPT = "INPUT FILE"
 
      PARAMETER P2, VALUE (REQUIRED)
 
                    PROMPT = "OUTPUT FILE"
 
      QUALIFIER FILETYPE, PLACEMENT = POSITIONAL
 
 
 
           2.1.3.4. Значения
 
 
           Оператор VALUE предоставляет возможность задавать зна-
 
      чение квалификатора (/квалификатор = значение). Возможность
 
      REQUIRED в операторе VALUE указывает, что значение задавать
 
      обязательно.  Если  значение  не  обязательно  (не   задано
 
      REQUIRED), то для него можно определить умолчание с помощью
 
      возможности DEFAULT. Возможность LIST разрешает ввод списка
 
      значений (/квалификатор = (значение,...)).


                                - 84 -
 
                          00152-01 97 09-2
 
 
           Пример.
 
           Команда INCOME допускает список значений для  квалифи-
 
      катора REPORT; для всего списка можно задать одно  значение
 
      по умолчанию:
 
 
      DEFINE VERB INCOME
 
      IMAGE "SYS$SYSTEM:INCOME"
 
      PARAMETER P1
 
                LABEL = STATS_FILE,
 
                VALUE (DEFAULT = "STATS.SAV")
 
      QUALIFIER ENTER
 
      QUALIFIER FIX
 
      QUALIFIER REPORT
 
                VALUE (LIST, DEFAULT = "INCOME.RPT")
 
 
 
           2.1.4. Типы значений
 
 
           2.1.4.1. Встроенные типы
 
 
           Встроенные типы  значений  идентифицируются  системно-
 
      определенными именами: $ACL, $DATETIME, $DELTATIME,  $FILE,
 
      $NUMBER, $QUOTED_STRING,$REST_OF_LINE [3].
 
           Пример.
 
           При об'явлении команды INCOME требуется, чтобы  значе-
 
      ние параметра было разрешенной спецификацией файла:
 
      DEFINE VERB INCOME
 
      IMаGE "SYS$SYSTEM:INCOME"
 
      PARAMETER P1
 
                LABEL = STATS_FILE
 
                VALUE (TYPE = $FILE,
 
                       DEFаULT = "STATS.SAV")
 


                                - 85 -
 
                          00152-01 97 09-2
 
 
           2.1.4.2. Ключевые слова
 
 
           Список ключевых слов определяется путем создания блока
 
      DEFINE TYPE, начинающегося оператором DEFINE TYPE, за кото-
 
      рым следует оператор KEYWORD. Для указания,  что  пользова-
 
      тель обязан вводить только ключевые  слова  из  списка  или
 
      использовать их однозначно определяющие сокращения, исполь-
 
      зуется возможность TYPE оператора  VALUE.  Например,  выбор
 
      одного из ключевых слов ENTER, FIX или  REPORT  в  качестве
 
      значения параметра указывается следующим образом:
 
 
      DEFINE VERB INCOME
 
      IMаGE "SYS$SYSTEM:INCOME"
 
      PARAMETER P1
 
                LABEL = ACTION
 
                VALUE(TYPE = ACTION_TYPE)
 
      DEFINE TYPE ACTION_TYPE
 
                  KEYWORD ENTER
 
                  KEYWORD FIX
 
                  KEYWORD REPORT
 
           Ключевые слова можно модифицировать  с  помощью  боль-
 
      шинства  из  операторов,  применяемых  для   квалификаторов
 
      DEFAULT, LABEL, NEGATABLE, NONNEGATABLE,  SYNTAX  и  VALUE.
 
      Однако умолчанием будет NONNEGATABLE.  С  оператором  VALUE
 
      могут быть заданы возможности  DEFAULT,  LIST,  REQUIRED  и
 
      TYPE.
 
           Для задания ключевого слова по умолчанию необходимо:
 
           1) в операторе PARAMETER или QUALIFIER указать  опера-
 
      тором DEFAULT, что значение по умолчанию существует;
 
           2) в блоке DIFINE TYPE при  соответствующем  операторе
 
      KEYWORD использовать оператор DEFAULT, идентифицируя ключе-


                                - 86 -
 
                          00152-01 97 09-2
 
 
      вое слово, установленное по умолчанию.
 
           Пример.
 
           В команде INCOME требуется ввод  в  качестве  значения
 
      параметра слов ALL или FILE с именем файла. Ключевым словом
 
      по умолчанию является ALL. Значением по умолчанию ключевого
 
      слова FILE будет STATS.SAV:
 
 
      DEFINE VERB INCOME
 
      IMAGE "SYS$SYSTEM:INCOME"
 
      PARAMETER P1
 
                LABEL = STATS_FILES
 
                VALUE (TYPE = FILE_TYPE)
 
                DEFAULT
 
      DIFINE TYPE FILE_TYPE
 
      KEYWORD ALL
 
              DEFAULT
 
      KEYWORD FILE
 
              VALUE (TYPE = $FILE, LIST, DEFAULT = "STATS.SAV")
 
 
 
           2.1.5. Запрещение об'ектов и сочетаний
 
 
           Можно подавить  заданные  параметры,  квалификаторы  и
 
      ключевые слова, указав ограничиваемые об'екты  в  операторе
 
      DISALLOW.  Можно  подавлять  некоторые  сочетания  об'ектов
 
      используя выражения в операторе DISALLOW.
 
           Пример.
 
           В команде INCOME разрешается  ввести  только  один  из
 
      трех возможных квалификаторов:
 
 
      DEVFINE VERB INCOME
 
      IMAGE "SYS$SYSTEM:INCOME"
 


                                - 87 -
 
                          00152-01 97 09-2
 
 
      QUALIFIER ENTER, NONNEGATABLE
 
      QUALIFIER FIX, NONNEGATABLE
 
      QUALIFIER REPORT, NONNEGATABLE
 
      DISALLOW ANY2 (ENTER, FIX, REPORT)
 
           Полный список выражений, которые можно использовать  в
 
      операторе DISALLOW указан в документе [3].
 
 
 
           2.1.6. Синтаксические изменения
 
 
           Можно изменить  синтаксические  требования  команды  в
 
      зависимости от введения конкретного параметра, квалификато-
 
      ра или ключевого слова. Параметр, квалификатор  или  слово,
 
      указывающие на новый синтаксис, описываются в блоке  DEFINE
 
      SYNTAX. Например, командой IMAGE вызывается  один  из  трех
 
      различных образов в зависимости от значения, заданного  для
 
      параметра р1; квалификатор /OUTPUT разрешен  только  тогда,
 
      когда в качестве действия введен REPORT:
 
 
      DEFINE VERB INCOME
 
      IMAGE "SYS$SYSTEM:ENTER"
 
      NOQUALIFIERS
 
      PARAMETER P1
 
                LABEL = ACTION
 
                VALUE (TYPE = ACTION_TYPE)
 
                DEFAULT
 
      DEFINE TYPE ACTION_TYPE
 
      KEYWORD ENTER, DEFAULT
 
      KEYWORD FIX, SYNTAX = FIX_SYNTAX
 
      KEYWORD REPORT, SYNTAX = REPORT_SYNTAX
 
      DEFINE SYNTAX FIX_SYNTAX
 
      IMAGE "SYS$SYSTEM:FIX"


                                - 88 -
 
                          00152-01 97 09-2
 
 
      DIFINE SYNTAX REPORT_SYNTAX
 
      IMAGE "SYS$SYSTEM:REPORT"
 
      QUALIFIER OUTPUT
 
                VALUE (TYPE = $FILE)
 
           Если задаются новые  определения  параметров,  то  они
 
      заменяют все старые. Если  задать  NOPARAMETERS,  то  новый
 
      синтаксис разрешит отсутствие параметров. Если не  задается
 
      новых параметров и не указывается NOPARAMETERS,  то  старые
 
      определения параметров остаются в силе.
 
           Задание новых определений квалификаторов отменяет  все
 
      старые определения. Если  указано  NOQUALIFIERS,  то  новый
 
      синтаксис  разрешает  отсутствие  квалификаторов.  Если  не
 
      задано новых квалификаторов и не указано  NOQUALIFIERS,  то
 
      все старые определения квалификаторов остаются.
 
           Задание новых определений DISALLOW заменяет все старые
 
      определения DISALLOW. Если задать NODISALLOW, то новый син-
 
      таксис разрешает отсутствие ограничений. Если не задано  ни
 
      новых определений DISALLOW, ни NODISALLOW, то старые  опре-
 
      деления DISALLOW остаются в силе.
 
 
 
           2.1.7. Пути ключевых слов и определений
 
 
           Пути ключевых слов и определений дают возможность точ-
 
      но идентифицировать об'екты.
 


                                - 89 -
 
                          00152-01 97 09-2
 
 
           2.1.7.1. Пути ключевых слов
 
 
           Пути ключевых слов строятся для ссылки на те  ключевые
 
      слова, которые в противном случае были  бы  неоднозначными.
 
      Путь ключевого слова строится так: перед неоднозначно опре-
 
      деленным об'ектом  ставится  имя  родительского  параметра,
 
      квалификатора или ключевого слова и  точка.  Можно  строить
 
      путь с глубиной не более 8.
 
           Пример.
 
           В команде INCOME путь  ключевого  слова  строится  для
 
      того, чтобы отличить ключевое слово квалификатора /INPUT от
 
      такого же слова квалификатора /OUTPUT:
 
 
      DEFINE VERB INCOME
 
      IMAGE "SYS$SYSTEM:INCOME"
 
      QUALIFIER INPUT, VALUE (TYPE = FILE_TYPE)
 
      QUALIFIER OUTPUT, VALUE (TYPE = FILE_TYPE)
 
      DISALLOW INPUT.FORM1 AND OUTPUT.FORM3
 
      DISALLOW INPUT.FORM2 AND OUTPUT.FORM3
 
      DEFINE TYPE FILE_TYPE
 
      KEYWORD FORM1
 
      KEYWORD FORM2
 
      KEYWORD FORM3
 
 
 
           2.1.7.2. Пути определения
 
 
           Для ссылок на квалификаторы, ключевые слова или значе-
 
      ния параметров, неопределенные  в  одном  и  том  же  блоке
 
      DEFINE VERB или DEFINE SYNTAX, строятся пути определений. В
 
      пути определения перед об'ектом ставится имя  родительского
 
      синтаксиса, заключенного в угловые скобки. Пути определения
 


                                - 90 -
 
                          00152-01 97 09-2
 
 
      полезны при ссылке на квалификаторы старого синтаксиса  или
 
      нового синтаксиса, если старые квалификаторы  не  запрещены
 
      заданием   новых    квалификаторов    или    характеристики
 
      NOQUALIFIERS.
 
           Пример.
 
      DEFINE VERB INCOME
 
      QUALIFIER ENTER, NONNEGATABLE, SYNTAX = ENTER_SYNTAX
 
      QUALIFIER FIX, NONNEGATABLE, SYNTAX = FIX_SYNTAX
 
      QUALIFIER  REPORT, NONNEGETABLE, SYNTAX = REPORT_SYNTAX
 
      DISALLOW ANY2 (ENTER, FIX, REPORT)
 
      QUALIFIER INPUT, VALUE (TYPE = FILE_TYPE)
 
      QUALIFIER OUTPUT, VALUE (TYPE = FILE_TYPE)
 
      DIFINE TYPE FILE_TYPE
 
      KEYWORD FORM1
 
      KEYWORD FORM2
 
      KEYWORD FORM3
 
      DEFINE SYNTAX ENTER_SYNTAX
 
      IMAGE "SYS$SYSTEM:ENTER"
 
      DISALLOW <INCOME>OUTPUT
 
      DIFINE SYNTAX FIX_SYNTAX
 
      IMAGE "SYS$SYSTEM:FIX"
 
      DISALLOW <INCOME>INPUT OR <INCOME>OUTPUT
 
      DEFINE SYNTAX REPORT_SYNTAX
 
      IMAGE "SYS$SYSTEM:REPORT"
 
      DISALLOW <INCOME>INPUT
 


                                - 91 -
 
                          00152-01 97 09-2
 
 
           2.2. Установка команд
 
 
           Набор команд, разрешенных на уровне  командного  языка
 
      DCL определяется таблицей команд пользовательского  процес-
 
      са. Эта таблица существует только во время выполнения  про-
 
      цесса и пересоздается каждый раз, как  только  пользователь
 
      входит в систему или каким-либо образом создает процесс. По
 
      умолчанию  таблица  команд  процесса  создается  из   файла
 
      SYS$LIBRARY:DCLTABLES.EXE ( таблица команд DCL) при  созда-
 
      нии процесса.
 
 
 
           2.2.1. Таблица команд процесса
 
 
           Для замены команды в таблице команд процесса или ввода
 
      новой команды указывается файл CLD в команде  SET  COMMAND.
 
      Каждая команда, определенная в файле  с  помощью  оператора
 
      DEFINE VERB добавится к таблице команд, если там ее  раньше
 
      не было, или заменит ранее бывшую  там  команду.  Например,
 
      добавление или замена команды INCOME (определенной в  файле
 
      INCOME.CLD) выполняется командой:
 
 
      $ SET COMMAND WORKDISK:[INCOME]INCOME
 
 
           Добавка или замена команды в таблице  команд  процесса
 
      делает эту команду доступной вплоть до завершения  процесса
 
      (чтобы команда становилась доступной при новом входе в сис-
 
      тему, нужно включить команду SET COMMAND в  командный  файл
 
      регистрации).
 


                                - 92 -
 
                          00152-01 97 09-2
 
 
           2.2.2. Таблица команд DCL
 
 
           Чтобы добавить или изменить команду в  таблице  команд
 
      DCL, необходимо использовать команду SET  COMMAND,  задавая
 
      входную  таблицу  DCL  с  помощью  квалификатора  /TABLE  и
 
      результирующую таблицу DCL с помощью квалификатора /OUTPUT.
 
      Для этого необходимо иметь доступ WRITE к  DCLTABLES.EXE  в
 
      SYS$LIBRARY.
 
           Пример.
 
           Добавление или замена команды INCOME в таблице DCL:
 
 
      $ SET COMMAND/TABLE = SYS$LIBRARY:DCLTABLES-
 
      _$ /OUTPUT = SYS$LIBRARY:DCLTABLES-
 
      _$ WORKDISK:[INCOME]INCOME
 
 
           Модификация таблицы команд  DCL  сказывается  на  всех
 
      пользователях МОС ВП (кроме тех, которые  переключились  на
 
      собственную  таблицу  команд).  Для  каждого   пользователя
 
      действие  изменения  начинает  сказываться   со   следующей
 
      регистрации. Сделать действие изменений немедленным пользо-
 
      ватель может вводом такой команды:
 
 
      $ SET COMMAND/TABLE = SYS$LIBRARY:DCLTABLES
 
 
 
           2.2.3. Таблица команд пользователя
 
 
           Пользователь может создать  свою  собственную  таблицу
 
      команд.
 


                                - 93 -
 
                          00152-01 97 09-2
 
 
           2.2.3.1. Копирование таблицы команд DCL
 
 
           Для создания копии  таблицы  команд  DCL  используется
 
      команда COPY, либо команда SET  COMMAND,  задающая  таблицу
 
      команд DCL как входную квалификатором /TABLE  и  имя  файла
 
      копии как выходное с помощью квалификатора /OUTPUT.  Напри-
 
      мер,     создание     копии     таблицы     команд      DCL
 
      SYS$LIBRARY:DCLTABLES.EXE в каталоге [INCOME]  на  WORKDISK
 
      выполняется командой:
 
 
      $ SET COMMAND/TABLE = SYS$LIBRARY.DCLTABLES-
 
      _$ /OUTPUT = WORKDISK:[INCOME]DCLTABLES
 
 
           Чтобы в процессе копирования добавить к копии  команду
 
      или команды, нужно задать в  SET  COMMAND  имя  файла  CLD,
 
      содержащего команду. Например, создается копия таблицы ком-
 
      манд DCL и к новой таблице добавляется команда INCOME:
 
 
      $ SET COMMAND/TABLе = SYS$LIBRARY:DCLTABLES-
 
      _$ /OUTPUT = WORKDISK:[INCOME]DCLTABLES -
 
      _$ WORKDISK:[INCOME]INCOME
 
 
 
           2.2.3.2. УстановкA таблицы пользователя
 
 
           Чтобы установить  собственную  таблицу  команд  DCL  в
 
      качестве  таблицы  команд   процесса,   необходимо   вместо
 
      DCLTABLES.EXE  в  SYS$LIBRARY  использовать   команду   SET
 
      COMMAND, указывающую копию таблицы  в  качестве  входной  с
 
      помощью квалификатора /TABLE. Например,  командная  таблица
 
      текущего процесса переключается на пользовательскую  табли-
 
      цу:
 


                                - 94 -
 
                          00152-01 97 09-2
 
 
      $ SET COMMAND/TABLE = WORKDISK:[INCOME]DCLTABLES
 
 
           Переключать таблицы следует сразу после входа в систе-
 
      му, кроме следующих случаев. Если пользовательская  таблица
 
      находится в системном каталоге SYS$LIBRARY, то  допускается
 
      задавать ее использование в качестве таблицы процесса вмес-
 
      то положенной по умолчанию таблицы DCL. Это задание  дается
 
      в момент регистрации с помощью квалификатора /CLITABLES  от
 
      AUTHORIZE. Можно задать и альтернативную  таблицу  (которая
 
      также должна находиться в SYS$LIBRARY) указанием  пользова-
 
      тельского имени с помощью квалификатора /TABLES при входе в
 
      систему.
 
 
 
           2.2.3.3. Создание таблицы заново
 
 
           Использование таблицы, не  являющейся  копией  таблицы
 
      DCL, делает недоступными команды МОС ВП и поэтому не  реко-
 
      мендуется. Если после создания собственной командной табли-
 
      цы возникает необходимость восстановить таблицу команд DCL,
 
      то нужно будет выйти из системы  и  снова  войти.  Так  как
 
      LOGOUT есть команда МОС ВП, то для выхода придется  уничто-
 
      жить процесс командой STOP с другого терминала или, если  в
 
      системе только один терминал, перезагрузить систему.
 
           Для создания собственной командной таблицы без исполь-
 
      зования существующих нужно создать пустую командную  табли-
 
      цу, добавить в нее желательные команды и потом переключить-
 
      ся на эту таблицу.
 
           Пример.
 
           Для создания пустой таблицы создается файл CLD, содер-
 
      жащий оператор MODULE и, возможно, оператор IDENT; файл CLD
 
      транслируется командой SET COMMAND/OBJECT, a результирующий


                                - 95 -
 
                          00152-01 97 09-2
 
 
      об'ектный модуль компонуется с помощью  команды  LINK/SHARE
 
      как разделяемый образ:
 
 
      $ SET DEFAULT WORKDISK:[INCOME]
 
      $ CREATE INCTABLES.CLD
 
       MODULE INCOME_TABLES<CTRL/Z>
 
      $ SET COMMAND/OBJECT INCTABLES
 
      $ LINK/SHARE INCTABLES
 
           В созданную таблицу включаются команды:
 
      $ SET COMMAND/TABLE = WORKDISK:[INCOME]INCTABLES-
 
      _$ /OUTPUT = WORKDISK:[INCOME]INCTABLES-
 
      _$WORKDISK:[INCOME]INCOME
 
           После переключения на новую  таблицу  необходимо  пом-
 
      нить, что команды МОС ВП, не включенные в нее, недоступны.
 
 
 
           2.2.4. Удаление команд
 
 
           Для удаления команды из таблицы применяется квалифика-
 
      тор /DELETE команды SET  COMMAND.  Квалификаторы  /TABLE  и
 
      /OUTPUT используются для создания таблиц  в  файлах,  а  не
 
      командной  таблицы  процесса.  Например,  удаление  команды
 
      INCOME из таблицы текущего процесса выполняется командой:
 
 
      $ SET COMMAND/DELETE = INCOME
 
 
      а для удаления команды INCOME из таблицы DCL нужно  исполь-
 
      зовать комAнды:
 
 
      $ SET COMMAND/TABLE = SYS$LIBRARY:DCLTABLES-
 
      _$ /OUTPUT = SYS$LIBRARY:DCLTABLES-
 
      _$ /DELETE = INCOME
 


                                - 96 -
 
                          00152-01 97 09-2
 
 
           2.3. Синтаксический анализ
 
 
           Процедуры CLI$PRESENT и  CLI$GET_VALUE  из  библиотеки
 
      исполнительных  процедур  позволяют  анализировать  наличие
 
      параметров, квалификаторов, ключевых слов и  читать  значе-
 
      ния, введенные пользователем для этих элементов.
 
 
 
           2.3.1. Проверка наличия элементов
 
 
           CLI$PRESENT имеет только один параметр: строка  симво-
 
      лов, содержащее имя параметра, квалификатора или  ключевого
 
      слова, как оно указано в файле CLD. CLI$PRESENT  возвращает
 
      состояние отказа  (предупреждение),  если  пользователь  не
 
      ввел элемент (а умолчания для него нет),  либо  ввел  явное
 
      отрицание элемента. CLI$PRESENT возвращает состояние успеш-
 
      ного завершения, если элемент задан пользователем или  при-
 
      сутствует по умолчанию. Например, если в файле  CLD  содер-
 
      жится информация о квалификаторах:
 
 
      QUALIFIER ENTER
 
      QUALIFIER FIX
 
      QUALIFIER REPORT
 
      DISALLOW ANY2 (ENTER, FIX, REPORT)
 
 
 
      то анализ наличия элементов можно сделать так:
 
 
 
             INTEGER CLI$PRESENT
 
             !
 
             IF (CLI$PRESENT('ENTER')) THEN
 
             ! действия
 
             ELSE IF (CLI$PRESENT('FIX')) THEN
 


                                - 97 -
 
                          00152-01 97 09-2
 
 
             ! альтернативные действия
 
             ELSE IF (CLI$PRESENT('REPORT')) THEN
 
             ! альтернативные действия
 
             END IF
 
 
           Если разница между явным вводом элемента  и  его  при-
 
      сутствием по умолчанию, или  явным  отрицанием  элемента  и
 
      отказом от его задания существенна,  то  следует  проверять
 
      возвращаемые коды условий, которые могут быть следующими:
 
           1) CLI$_PRESENT - элемент задан пользователем явно;
 
           2) CLI$_DEFAULTED - элемент присутствует по умолчанию;
 
           3) CLI$_ABSENT - элемент не введен пользователем и  не
 
      присутствует по умолчанию;
 
           4) CLI$_NEGATED - пользователем явно задано  отрицание
 
      элемента;
 
           5) CLI$_LOCPRES - квалификатор присутствует локально;
 
           6) CLI$_LOCNEG - локальное отрицание квалификатора.
 
           Коды условий определены в системной об'ектной  библио-
 
      теке.
 
           Примеры:
 
           1. Простая проверка  успеха  или  отказа  иногда  дает
 
      нежелательный результат. Если пользователь введет /FIX, код
 
      успешного завершения будет выдан и на /ENTER (умолчание), и
 
      на /FIX. В файле CLD предоставляется возможность  изменения
 
      умолчания для квалификатора без изменений программы:
 
 
      файл CLD имеет вид:
 
      QUALIFIER ENTER, DEFAULT
 
      QUALIFIER FIX
 
      QUALIFIER REPORT
 
      DISALLOW ANY2(ENTER, FIX, REPORT)


                                - 98 -
 
                          00152-01 97 09-2
 
 
 
 
      фрaгмент программы имет вид:
 
 
 
             INTEGER CLI$PRESENT
 
             EXTERNAL CLI$_PRESENT,
 
             2        CLI$_DEFAULTED
 
 
 
             IF ((CLI$PRESENT('ENTER') .EQ. %LOC (CLI$_PRESENT))
 
             2               .OR.
 
             2 ((CLI$PRESENT('ENTER') .EQ. %LOC (CLI$_DEFAULTED))
 
             2              .AND.
 
             2              (.NOT. CLI$PRESENT ('FIX'))
 
             2              .AND.
 
             2              (.NOT. CLI$PRESENT('REPORT')))) THEN
 
             ! выполняемые действия
 
        ELSE IF ((CLI$PRESENT ('FIX') .EQ. %LOC (CLI$_PRESENT))
 
             2               .OR.
 
             2 ((CLI$PRESENT ('FIX') .EQ. %LOC (CLI$_DEFAULTED))
 
             2               .AND.
 
             2               (.NOT.CLI$PRESENT ('ENTER'))
 
             2               .AND.
 
             2               (.NOT. CLI$PRESENT('REPORT')))) THEN
 
             ! альтернативные действия
 
      еLSE IF ((CLI$PRESENT ('REPORT') .EQ. %LOC(CLI$_DEFAULTED))
 
             2                .OR.
 
             2 ((CLI$PRESENT ('REPORT').EQ. %LOG (CLI$DEFAULTED))
 
             2               .AND.
 
             2               (.NOT. CLI$PRESENT ('ENTER'))
 
             2               .AND.
 
             2               (.NOT. CLI$PRESENT ('FIX')))) THEN


                                - 99 -
 
                          00152-01 97 09-2
 
 
             ! альтернативные действия
 
             END IF
 
           2. Ключевое слово  может  оказаться  неоднозначным;  в
 
      этом случае нужно уKазывать полный путь ключевого слова.  В
 
      примере действия предпринимаются  только  если  в  качестве
 
      значения квалификатора /INPUT введено одно из ключевых слов
 
      FORM1, FORM2 или FORM3:
 
 
      QUALIFIER INPUT, VALUE (TYPE = FILE_TYPE)
 
      QUALIFIER OUTPUT, VALUE (TYPE = FILE_TYPE)
 
      DEFINE TYPE FILE_TYPE
 
      KEYWORD FORM1
 
      KEYWORD FORM2
 
      KEYWORD FORM3
 
 
 
      фрагмент программы имеет вид:
 
 
 
             INTEGER CLI$PRESENT
 
 
 
             IF (CLI$PRESENT ('INPUT.FORM1')) THEN
 
             ! выполняемые действия
 
             ELSE IF (CLI$PRESENT('INPUT.FORM2')) THEN
 
             ! альтернативные действия
 
             ELSE IF (CLI$PRESENT('INPUT FORM3')) THEN
 
             ! альтернативные действия
 
             ELSE IF (CLI$PRESENT('INPUT FORM3')) THEN
 
             ! альтернативные действия
 
             END IF
 


                                - 100 -
 
                          00152-01 97 09-2
 
 
           2.3.2. Прием значений элементов
 
 
           Первый параметр CLI$GET_VALUE задается как строка сим-
 
      волов, содержащая имя параметра, квалификатора или ключево-
 
      го слова, заданное в файле CLD. Второй параметр определяет-
 
      ся как строка сиMволов достаточной длины для приема  значе-
 
      ния параметра, ключевого слова  или  квалификатора.  Третий
 
      (необязательный) параметр задается как целое типа INTEGER*2
 
      для приема длины значения, соответствующего  второму  пара-
 
      метру .
 
           Пример.
 
           Первый параметр команды используется при открытии фай-
 
      ла:
 
 
      PARAMETER P1
 
              LABEL = STATS_FILE
 
              VALUE (DEFAULT = "STATS.SAV")
 
      фрагмент программы:
 
              INTEGER STATUS,
 
              2       CLI$GET_VALUE,
 
              2       LIB$GET_LUN
 
              INTEGER*2 FILE_NAME_SIZE
 
              CHARACTER*255 FILE_NAME
 
              INTEGER FILE_LUN
 
 
 
              STATUS = CLI$GET_VALUE('STATUS_FILE',
 
              2                      FILE_NAME,
 
              2                      FILE_NAME_SIZE)
 
              IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
              STATUS = LIB$GET_LUN (FILE_LUN)
 
              IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))


                                - 101 -
 
                          00152-01 97 09-2
 
 
              OPEN (UNIT = FILE_LUN,
 
              2     FILE,FILE_NAME (1:FILE_NAME_SIZE
 
              2     STATUS = 'OLD'
 
              2     FORM = 'UNFORMATTED')
 
 
           Если значение не присутствует (например, если  оно  не
 
      обязательно и не задано для него умолчание), то будет возв-
 
      ращено состояние CLI$_ABSENT.
 
           Если ожидается ввод списка значений,  то  для  каждого
 
      значения в списке нужно  вызвать  CLI$GET_VALUE.  ПроцедурA
 
      CLI$GET_VALUE каждый раз возвращает одно из следующих  сос-
 
      тояний:
 
           1) SS$_NORMAL -  значение  является  единственным  или
 
      последним элементом списка. Значение возвращается  в  пара-
 
      метре 2;
 
           2) CLI$_COMMA - за значением следует запятая. Значение
 
      возвращается в параметре 2;
 
           3) CLI$_CONCAT - за значением следует знак плюс.  Зна-
 
      чение возвращается в параметре 2;
 
           4) CLI$_ABSENT - список исчерпан. Никакое значение  не
 
      возвращается.
 
           Если не существенно, разделены  элементы  плюсами  или
 
      запятыми, то можно при каждом вызове CLI$GET_/VALUE  просто
 
      проверять на CLI$_ABSENT.
 
           Пример.
 
           Получаемый список значений параметров заносится в мас-
 
      сив. Предусмотрено получение десяти  элементов.  Содержимое
 
      файлA CLD:
 
 
      PARAMETER P1
 
              LABEL = STATS_FILE


                                - 102 -
 
                          00152-01 97 09-2
 
 
              VALUE(LIST,
 
                    DEFAULT = "STATS.SAV")
 
 
 
      фрагмент программы:
 
 
 
             INTEGER STATUS,
 
             2       CLI$GET_VALUE,
 
             2       FN_COUNT
 
             INTEGER*2 FN_SIZE (10)
 
             CHARACTER*255 FILE_NAME (10)
 
             EXTERNAL CLI$_ABSENT
 
 
 
             !принять первое значение в списке
 
             STATUS = CLI$GET_VALUE ('STATS_FILE',
 
             2                       FILE_NAME (1),
 
             2                       FN_SIZE (1))
 
             !цикл до отказа LIB$GET_VALUE
 
             DO WHILE (STATUS)
 
               !обновить счетчик значений
 
               FN_COUNT = FN_COUNT + 1
 
               IF (FN_COUNT .LT. 10 ) THEN
 
                 !принять следующее значение в списке
 
                 STATUS = CLI$GET_VALUE ('STATUS_FILE',
 
             2                           FILE_NAME(FN_COUNT + 1),
 
             2                           FILE_SIZE(FN_COUNT + 1))
 
               ELSE
 
                 !форсировать окончание списков на 10 значениях
 
                 STATUS = %LOC (CLI$_ABSENT)
 
               END IF
 
             END DO


                                - 103 -
 
                          00152-01 97 09-2
 
 
           !убедиться, что состояние отказа совпадает с ожидаемым
 
             IF (STATUS .NE. %LOC (CLI$_ABSENT)) THEN
 
               CALL LIB$SIGNAL (%VAL (STATUS))
 
             END IF
 
 
 
           2.3.3. Определение положения квалификатора
 
 
           Указатель текущей среды, связанной с исходной  команд-
 
      ной строкой, изменяется при каждом вызове CLI$GET_VALUE для
 
      приема значения параметра: перед приемом какого  бы  то  ни
 
      было значения он находится в глобальной среде, после приема
 
      P1 - в среде р1, после приема р2 - в среде р2 и т.д. Прави-
 
      ла определения области  действия  квалификатора  в  текущей
 
      среде следующие:
 
           1) глобальное присутствие - если  пользователь  вводит
 
      позиционный квалификатор  (PLACEMENT  =  POSITIONAL)  перед
 
      вводом   всех   параметров,   то   квалификатор   действует
 
      (CLI$_PRESENT) во всех средах, кроме локальных  сред,  явно
 
      указанных пользователем;
 
           2) глобальное отрицание  -  если  пользователь  задает
 
      отрицание  квалификатора,  определенного  как   позиционный
 
      (PLACEMENT = POSITIONAL), перед вводом всех параметров,  то
 
      отрицание квалификатора будет задано (CLI$_NEGATED) во всех
 
      средах, кроме явно указанных пользователем локальных сред;
 
           3) локальное действие - если пользователь вводит пози-
 
      ционный (PLACEMENT = POSITIONAL) или локальный (PLACEMENT =
 
      LOCAL) квалификатор после параметра, то квалификатор дейст-
 
      вует локально в среде этого параметра (CLI$_LOCPRES);
 
           4) локальное отрицание. Если пользователь ставит отри-
 
      цание позиционного (PLACEMENT = POSITIONAL) или  локального
 


                                - 104 -
 
                          00152-01 97 09-2
 
 
      (PLAGEMENT = LOCAL) квалификатора после параметра, то отри-
 
      цание квалификатора (CLI$_LOCNEG) действует в  среде  этого
 
      параметра;
 
           5) присутсвие по умолчанию - если  квалификатор  пози-
 
      ционный (PLACEMENT =  POSITIONAL)  и  имеет  характеристику
 
      DEFAULT, то он присутствует по умолчанию (CLI$_DEFAULTED) в
 
      средах всех параметров, если это не отменено явной специфи-
 
      кацией. Если локальный  (PLACEMENT  =  LOCAL)  квалификатор
 
      имеет характеристику DEFAULT, то он присутствует по умолча-
 
      нию (CLI$_DEFAULTED) в средах всех параметров, если это  не
 
      отменено спецификацией пользователя.
 
           В большинстве случаев программа застрахована  от  оши-
 
      бок, если просто проверяется наличие квалификаторов и  при-
 
      нимается значение параметров в среде каждого из них. Основ-
 
      ное требование, соблюдаемое при работе с позиционными  ква-
 
      лификаторами, состоит в контроле  принадлежности  обрабаты-
 
      ваемого квалификатора соответствующей среде.
 
           Пример.
 
           Ввод   значения   квалификатора  (/FILETYPE  =  FORM1,
 
      /FILETYPE = FORM2 или /FILETYPE = FORM3) разрешен  для всех
 
      параметров глобальным заданием квалификатора или  локальным
 
      его заданием после каждого параметра. Необходимо  заметить,
 
      что по умолчанию среда каждого параметра получает  значение
 
      квалификаторов FORM1:
 
 
      PARAMETER P1, VALUE (REQUIRED)
 
                    PROMPT = "INPUT FILE"
 
      PARAMETER P2, VALUE (REQUIRED)
 
                    PROMPT = "OUTPUT FILE"
 
      QUALIFIER FILETYPE
 


                                - 105 -
 
                          00152-01 97 09-2
 
 
                DEFAULT
 
                VALUE (TYPE = FILE_TYPE)
 
                PLACEMENT = POSITIONAL
 
      DEFINE TYPE FILE_TYPE
 
      KEYWORD FORM1, DEFAULT
 
      KEYWORD FORM2
 
      KEYWORD FORM3
 
 
 
      фрагмент программы:
 
 
 
             INTEGER STATUS,
 
             2       CLI$PRESENT,
 
             2       CLI$GET_VALUE
 
             INTEGER*2 IF_SIZE,
 
             2         OF_SIZE
 
             CHARACTER*31 INPUT_FILE,
 
             2            OUTPUT_FILE
 
             CHARACTER*5 IF_TYPE,
 
             2           OF_TYPE
 
             STATUS = CLI$GET_VALUE ('P1',
 
             2                       INPUT_FILE,
 
             2                       IF_SIZE)
 
             IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
             STATUS = CLI$GET_VALUE ('P2',
 
             2                       OUTPUT_FILE,
 
             2                       OF_SIZE)
 
             IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
             STATUS = CLI$GET_VALUE ('FILETYPE',
 
             2                       OF_TYPE)
 
             IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))


                                - 106 -
 
                          00152-01 97 09-2
 
 
           2.3.4. Анализ командной строки и имени команды
 
 
           Можно получить содержание команды в  том  точно  виде,
 
      как она  была  введена  пользователем.  Для  этого  следует
 
      задать литерал $LINE первым параметром CLI$GET_VALUE. Можно
 
      получить первые четыре символа имени команды (независимо от
 
      того, какой длины имя задано пользователем), указав  первым
 
      параметром CLI$GET_VALUE литерал $VERB. Например,  содержа-
 
      ние командной строки возвращается в переменной LINE, а пер-
 
      вые четыре символа имени команды - в переменной VERB:
 
 
      INTEGER STATUS
 
      2       CLI$GET_VALUE
 
      INTEGER*2 LINE_SIZE
 
      CHARACTER*1024 LINE
 
      CHARACTER*4 VERB
 
      STATUS = CLI$GET_VALUE ('$LINE'
 
      2                       LINE,
 
      2                       LINE_SIZE)
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      STATUS = CLI$GET_VALUE ('$VERB',
 
      2                       VERB)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
 
 
           2.4. Подкоманды и внутренний разбор
 
 
           Обслуживающая программа определения команд и связанные
 
      с ней процедуры интерпретатора командной строки (CLI)  поз-
 
      воляют  проводить  разбор  строк  символов,  полученных  из
 
      источников, отличных от командной строки,  которой  вызвана
 
      данная программа.
 


                                - 107 -
 
                          00152-01 97 09-2
 
 
           2.4.1. Определение подкоманд
 
 
           Каждая подкоманда определяется с помощью блока  DEFINE
 
      VERB в файле CLD по тем же правилам, что и  любая  команда.
 
      Подкоманды транслируются в об'ектную таблицу  команд  зада-
 
      нием в команде SET COMMAND с квалификатором  /OBJECT  файла
 
      CLD, содержащего определения подкоманд.  Об'ектную  таблицу
 
      нужно скомпоновать с программой.
 
           Таблице команд можно присвоить имя модуля,  включив  в
 
      файл командных определений оператор MODULE.  Если  оператор
 
      MODULE не используется, то можно включить  имя  модуля  как
 
      значение квалификатора /OBJECT, или  позволить,  чтобы  имя
 
      модуля по умолчанию совпадало с именем файла CLD (или  пер-
 
      вого обрабатываемого из файлов CLD).
 
           Пример.
 
           Три подкоманды транслируются в об'ектный модуль с име-
 
      нем INCOME_SUBCOMMAND (имя об'ектного файла INCSUB.OBJ).
 
           Содержимое файла INCSUB.CLD:
 
      MODULE INCOOME_SUBCOMMANDS
 
      DEFINE VERB ENTER
 
      DEFINE VERB FIX
 
      PARAMETER P1, LABEL = HOUSE_NO
 
                    VALUE(REQUIRED,TYPE = $NUMBER)
 
                    PROMPT = "HOUSE NUMBER"
 
      PARAMETER P2, LABEL = PERSONS_HOUSE
 
                    VALUE(REQUIRED, TYPE = $NUMBER)
 
                    PROMPT = "ADULTS PER HOUSE"
 
      PARAMETER P3, LABEL = ADULTS_HOUSE
 
                    VALUE (REQUIRED, TYPE = $NUMBER)
 
                    PROMT = "ADULTS PER HOUSE"
 


                                - 108 -
 
                          00152-01 97 09-2
 
 
      PARAMETER P4, LABEL = INCOME_HOUSE
 
                    VALUE (REQUIRED,TYPE = $NUMBER)
 
                    PROMPT = "INCOME PER HOUSE"
 
      DEFINE VERB REPORT
 
      QUALIFIER OUTPUT VALUE (TYPE = $FILE,
 
                      DEFAULT = "INCOME.RPT")
 
           Команда трансляции:
 
      $ SET COMMAND/OBJECT INCSUB
 
           Об'ектный файл, содержащий модуль командных  определе-
 
      ний должен быть скомпонован с головной программой и другими
 
      програмMными единицами.
 
 
 
           2.4.2. Разбор подкоманд
 
 
           Программа должна явно разбирать подкоманду  с  помощью
 
      вызова   процедуры   CLI$DCL_PARSE   [2].   Параметры   для
 
      CLI$DCL_PARSE задаются следующим образом:
 
           1) входная строка или 0 (первый параметр).  Если  чет-
 
      вертый параметр будет использован для чтения команды  цели-
 
      ком, то первым параметрOм должен быть 0. Параметр передает-
 
      ся по значению. Если команда (или ее  часть)  уже  принята,
 
      нужно задать в качестве  первого  параметра  содержащую  ее
 
      строку символов (и передать ее по дескриптору, как и  уста-
 
      новлено по умолчанию);
 
           2) таблица подкоманд (параметр 2). Задать имя  модуля,
 
      содержащего таблицу подкоманд, определенное внешним;
 
           3) процедура  обязательных  параметров  (параметр  3).
 
      Если открепление команды содержит  обязательные  параметры,
 
      то следует задать имя процедуры для запроса и чтения обяза-
 
      тельных параметров (только в том случае, если они не введе-
 


                                - 109 -
 
                          00152-01 97 09-2
 
 
      ны  в  командной  строке).  Этой  процедурой   может   быть
 
      LIB$GET_INPUT или любая другая  процедура,  имеющая  те  же
 
      параметры и возвращающая код  состояния.  Процедура  должна
 
      быть определена как внешняя;
 
           4) процедура обработки командной строки (параметр  4).
 
      Задается имя процедуры для чтения команды целиком (если  не
 
      принята  команда  и  не  передана   первым   параметром   в
 
      CLI$DCL_PARSE) или строк продолжения команды. Этой процеду-
 
      рой может быть LIB$GET_INPUT или  любая  другая  процедура,
 
      имеющая те же три параметра и возвращающая  код  состояния.
 
      Имя процедуры должно быть определено как внешнее;
 
           5) подсказка командной строки (параметр 5). Если задан
 
      параметр 4, то указывается строка символов,  которая  будет
 
      служить подсказкой при запросе команды или ее остатка.
 
           Пример.
 
           Читается (с помощью LIB$GET_INPUT и подсказки INCOME>)
 
      и разбирается подкоманда,  а  затем  выполняются  действия,
 
      зависящие от содержимого полученной  подкоманды.  Программа
 
      обрабатывает подкоманды до тех пор,  пока  пользователь  не
 
      введет CTRL/Z:
 
 
      INTEGER STATUS,
 
      2       CLI$DCL_PARSE,
 
      2       CLI$GET_VALUE,
 
      INCLUDE '($RMSDEF)'
 
      INCLUDE '($STSDEF)'
 
      EXTERNAL INCOME_SUBCOMMANDS,
 
      2        LIB$GET_INPUT
 
      CHARACTER*4 SUBCOMMAND NAME
 
      STATUS = LIB$PUT_OUTPUT
 


                                - 110 -
 
                          00152-01 97 09-2
 
 
      2 ('подкоманды: ENTER---FIX---REPORT')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      STATUS = LIB$PUT_OUTPUT ('нажать CTRL/Z для завершения')
 
      !принять первую подкоманду
 
      STATUS = CLI$DCL_PARSE (%VAL (0),
 
      2                     INCOME SUBCOMMANDS, !Модуль CLD
 
      2                     LIB$GET_INPUT,      !Процедура обра-
 
                                                !ботки параметров
 
      2                     LIB$GET_INPUT,      !Процедура обра-
 
                                                !ботки команд
 
      2                     'INCOME>')          !Командная под-
 
                                                !сказка
 
      !работать так, пока пользователь не введет CTRL/Z
 
      DO WHILE (STATUS .NE. RMS$_EOF)
 
        !если не было Oшибки
 
        IF (STATUS) THEN
 
          !принять имя подкоманды - всегда четыре символа
 
          STATUS = CLI$GET_VALUE ('$VERB',
 
      2                           SUBCOMMAND_NAME)
 
          IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
          !выполнить соответствующие действия
 
          IF (SUBCOMMAND_NAME .EQ 'ENTE') THEN
 
          ! выполнить действия для ENTER
 
          ELSE IF (SUBCOMMAND_NAME .EQ. 'FIX') THEN
 
          ! выполнить действия для FIX
 
          ELSE IF (SUBCOMMAND_NAME .EQ 'REPO') THEN
 
          ! выполнить действия для REPORT
 
          END IF
 
          !не выдавать предупреждающий сигнал снова
 
        ELSE IF (IBITS (STATUS, 0, 3) .NE. STS$K_WARNING) THEN


                                - 111 -
 
                          00152-01 97 09-2
 
 
          CALL LIB$SIGNAL (%VAL (STATUS))
 
        END IF
 
        !принять другую подкоманду
 
        STATUS = CLI$DCL_PARSE (%VAL (0),
 
      2                       INCOME_SUBCOMMANDS,
 
      2                       LIB$GET_INPUT,
 
      2                       LIB$GET_INPUT,
 
      2                       'INCOME> ')
 
      END DO
 
      END
 
           Если в подкоманде допущена синтаксическая  ошибка,  то
 
      CLI$DCL_PARSE сигнализирует об ощибке, и возвращает ее  код
 
      как код состояния. Так как уровень серьезности синтаксичес-
 
      кой ошибки - это предупреждение, то действием по  умолчанию
 
      будет: выдать сообщение и продолжать  выполнение  (если  не
 
      установлена собственная программа обработки ошибок).  Можно
 
      отличить предупреждение от прочих  ошибок  путем  сравнения
 
      трех младших битов кода  состояния  с  кодом  STS$K_WARNING
 
      (определен в $STSDEF). В приведенном примере не  происходит
 
      явного вызова  LIB$SIGNAL,  если  ошибка,  возвращенная  из
 
      CLI$DCL_PARSE, есть предупреждение; сообщение об ошибке  не
 
      печатается дважды.
 
           Если ошибка происходит в то время, как процедура (ука-
 
      занная в параметре 4) читает  командную  строку,  состояние
 
      ошибки  передается  дальше  как   состояние,   возвращенное
 
      CLI$DCL_PARSE. В  примере  программа  распознает  получение
 
      состояния RMS$_EOF (определено в $RMSDEF) из  CLI$DCL_PARSE
 
      (CLI$DCL_PARSE получает состояние от LIB$GET_INPUT),  соот-
 
      ветствующее нажатию <CTPL/Z>.
 
           Если указывается соответствующая процедура в  качестве


                                - 112 -
 
                          00152-01 97 09-2
 
 
      парамера 3 или 4, то она должна быть оформлена как подпрог-
 
      рамма типа FUNCTION, а в качестве значения  функции  должна
 
      возвращать код состояния ( для продолжения выполнения прог-
 
      раммы следует возвращать SS$_NORMAL). Формальные  параметры
 
      для этой подпрограммы следует задавать так:
 
           1) параметр 1. Строка  символов  воспринимаемой  длины
 
      для приема получаемой строки;
 
           2) параметр 2. Строка  символов  воспринимаемой  длины
 
      для получения текста подсказки, указанной пятым  параметром
 
      CLI$DCL_PARSE;
 
           3) параметр 3. Целое  типа  INTEGER*2  для  размещения
 
      длины получаемой строки.
 
           Пример.
 
           Описание формальных параметров пользовательской проце-
 
      дуры:
 
      INTEGER FUNCTION GETINPUT (INPUT,   !Возвращаемое
 
      2                          PROMPT,  !Передаваемое
 
      2                          SIZE)    !Возвращаемое
 
      CHARACTER*(*) INPUT
 
      2             PROMPT
 
      INTEGER*2     SIZE
 
      INTEGER STATUS
 
 
 
      GETINPUT = STATUS
 
      END
 


                                - 113 -
 
                          00152-01 97 09-2
 
 
           2.4.3. Диспетчеризация подпрограмм
 
 
           Есть возможность вызвать нужную подпрограмму,  опреде-
 
      ляемую подкомандой, введенной пользователем. Для этого  имя
 
      подпрограммы, которая должна быть вызвана в ответ на  конк-
 
      ретную подкоманду, следует задать  в  операторе  ROUTINE  в
 
      блоке DEFINE VERB для этой подкоманды, а в программе  после
 
      вызова CLI$DCL_PARSE употребить вызов CLI$DISPATCH.  Напри-
 
      мер, файл CLD может быть построен так,  что  на  каждую  из
 
      подкоманд, определенных в файле, должна быть  вызвана  одна
 
      из подпрограмм ENTER, FIX или REPORT:
 
 
      MODULE INCOME_SUBCOMMANDS
 
      DEFINE VERB ENTER
 
        ROUTINE VERB ENTER
 
      DEFINE VERB FIX
 
        ROUTINE FIX
 
 
 
      DEFINE VERB REPORT
 
        ROUTINE REPORT
 
        QUALIFIER OUTPUT, VALUE (TYPE = $FILE,
 
                                 DEFAULT = "INCOME.RPT")
 
      PROGRAM INCOME
 
      INTEGER STATUS,
 
      2       CLI$DCL_PARSE,
 
      2       CLI$DISPATCH
 
      INCLUDE '($RMSDEF)'
 
      INCLUDE '($STSDEF)'
 
      EXTERNAL INCOME_SUBCOMMANDS,
 
      2        LIB$GET_INPUT
 
      STATUS = LIB$PUT_OUTPUT


                                - 114 -
 
                          00152-01 97 09-2
 
 
      2 ('подкоманды: ENTER---FIX---REPORT')
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      STATUS = LIB$PUT_OUTPUT
 
      2 ('нажать CTRL/Z для завершения')
 
      !принять первую подкоманду
 
      STATUS = CLI$DCL_PARSE (%VAL (0),
 
      2                       INCOME_SUBCOMMANDS,!Модуль CLD
 
      2                       LIB$GET_INPUT,  !Программа обработ-
 
                                              !ки параметров
 
      2                       LIB$GET_INPUT,     !Программа обра-
 
                                                 !ботки команды
 
      2                       'INCOME>')         !Командная под-
 
                                                 !сказка
 
      !так работать, пока пользователь не введет CTRL/Z
 
      DO WHILE (STATUS .NE. RMS$_EOF)
 
        !если нет ошибки CLI$DCL_PARSE
 
        IF (STATUS) THEN
 
           !предача управления в зависимости от подкоманды
 
           STATUS = CLI$DISPATCH ()
 
           IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
           !не повторять предупреждающий сигнал
 
        ELSE IF (IBITS (STATUS, O, 3) .NE. STS$K_WARNING) THEN
 
           CALL LIB$SIGNAL (%VAL (STATUS))
 
        END IF
 
        !принять другую подкоманду
 
        STATUS = CLI$DCL_PARSE (%VAL (O),
 
      2                         INCOME_SUBCOMMANDS,
 
      2                         LIB$GET_INPUT,
 
      2                         LIB$GET_INPUT,
 
      2                         'INCOME>')


                                - 115 -
 
                          00152-01 97 09-2
 
 
      END DO
 
      END
 
      INTEGER FUNCTION ENTER ()
 
      INTEGER STATUS
 
 
 
      ENTER = STATUS
 
      END
 
      INTEGER FUNCTION FIX ()
 
      INTEGER STATUS
 
 
 
      FIX = STATUS
 
      END
 
      INTEGER FUNCTION REPORT()
 
      INTEGER STATUS
 
 
 
      REPORT = STATUS
 
      END
 
           Если подпрограмма определена как функция целого  типа,
 
      то возвращаемое ею значение используется в  CLI$DISPATCH  в
 
      качестве  кода  состояния  возврата.  В  противном   случае
 
      CLI$DISPATCH не возвращает правильный код состояния.
 
           От CLI$DISPATCH пользовательской программе можно пере-
 
      дать одно (необязательное)  длинное  слово.  Все  остальные
 
      данные должны передаваться через общие области.
 


                                - 116 -
 
                          00152-01 97 09-2
 
 
           2.4.4. Возвращение к вызывающей команде
 
 
           После вызова CLI$DCL_PARSE для подкоманды все дальней-
 
      шие вызовы CLI$PRESENT и  CLI$GET_VALUE  относятся  к  этой
 
      подкоманде; нет более доступа к той команде, которая вызва-
 
      ла программу с  командного  уровня  DCL.  Восстановить  эту
 
      команду  и  снова  ее  разобрать  можно  с  помощью  вызова
 
      CLI$DCL_PARSE, задав нули в качестве двух первых параметров
 
      и опустив остальные. Первый параметр необходимо  передавать
 
      по значению.
 
 
 
 
                    3. Пользовательский ввод-вывод
 
 
 
           Обычно программа устанавливается таким образом, что ее
 
      вызывает  пользователь.  Пользователь  запускает  программу
 
      вводом связанной с ней команды DCL или указав имя ее  файла
 
      образа (например, в команде RUN). Устройства ввода и вывода
 
      пользователя определяются логическими именами  SYS$INPUT  и
 
      SYS$OUTPUT, первоначальное закрепление которых приведено  в
 
      табл.6.
 
                                                       Таблица 6
 
      ----------------------------------------------------------
                   !               !
        Логическое !   Режим       ! Эквивалентное устройство
                   !               !
        имя        ! пользователя  !         или файл
                   !               !
      ---------------------------- -----------------------------
                   !               !
        SYS$INPUT  !  Интерактивный!   Терминал, на котором за-
                   !               !
                   !               ! регистрирован пользователь
                   !               !
                   !  Пакетный     !   Строки данных, следующие
                   !               !
                   !               ! за вызовом программы
                   !               !
                   !  Командная    !   Строки данных, следующие


                                - 117 -
 
                          00152-01 97 09-2
 
 
                                             Продолжение табл. 6
      ----------------------------------------------------------
                   !               !
        Логическое !   Режим       ! Эквивалентное устройство
                   !               !
        Имя        ! пользователя  !         или файл
                   !               !
      ---------------------------- -----------------------------
                   !               !
                   !процедура      ! за вызовом программы
                   !               !
        SYS$OUTPUT !  Интерактивный!   Терминал, на котором за-
                   !               !
                   !               ! регистрирован пользователь
                   !               !
                   !  Пакетный     !   Файл регистрации пакета
                   !               !
                   !  Командная    !   Терминал, на котором за-
                   !               !
                   !процедура      ! регистрирован пользователь
 
 
           Рекомендуется  применение  SYS$INPUT  и  SYS$OUTPUT  в
 
      качестве начальных устройств ввода и  вывода.  Пользователь
 
      программы может переопределить SYS$INPUT и SYS$OUTPUT, что-
 
      бы направить ввод и вывод на желательные для него устройст-
 
      ва.  Например,  пользователь,  работающий  в  интерактивном
 
      режиме, может переопределить SYS$OUTPUT как имя файла, что-
 
      бы сохранить результаты вывода, а не печатать их на  терми-
 
      нал.
 
           Программа может быть спроектирована так,  чтобы  вывод
 
      производить в файл или на устройство, отличное от терминала
 
      пользователя. Файлы полезны при записи больших об'емов дан-
 
      ных, для записи тех данных, которые необходимо сохранить, и
 
      для записи данных, которые потом будут  использоваться  как
 
      входные. Если используются файлы или  устройства,  отличные
 
      от SYS$INPUT или SYS$OUTPUT,  необходимо  обеспечить  имена
 
      файлов или устройств (лучшей формой для этого будет исполь-
 
      зование логических имен) и соглашения об их использовании.
 


                                - 118 -
 
                          00152-01 97 09-2
 
 
           3.1. Диалоговый ввод-вывод
 
 
           Можно запросить информацию от пользователя или  выдать
 
      ему информацию, не заботясь о ее форматировании. Для такого
 
      диалогового ввода-вывода  используются  процедуры  LIB$GET_
 
      INPUT и LIB$PUT_OUTPUT [2] или операторы ввода-вывода READ,
 
      ACCEPT, WRITE, PRINT и TYPE, реализованные в языке фортран.
 
 
 
           3.1.1. Выбор устройства
 
 
           Процедуры LIB$GET_INPUT и  LIB$PUT_OUTPUT  работают  с
 
      логическими  именами  SYS$INPUT   (чтение)   и   SYS$OUTPUT
 
      (запись). Логические имена SYS$INPUT и SYS$OUTPUT  являются
 
      неявными для этих процедур; достаточно вызвать такую проце-
 
      дуру, чтобы получить доступ к устройству ввода-вывода (уст-
 
      ройство или файл), которое связано с соответвующим логичес-
 
      ким именем. С помощью этих процедур нельзя получить  доступ
 
      к устройству, не связанному с данными логическими именами.
 
           С помощью оператора OPEN языка  фортран  можно  задать
 
      устройство ввода-вывода или, опустив оператор  OPEN,  рабо-
 
      тать с неявно указанным устройством, используя другие  опе-
 
      раторы ввода-вывода.
 
           Для идентификации устройства ввода-вывода используется
 
      логический номер устройства - целое число по  выбору  прог-
 
      раммиста. Логический номер устройства задается при открытии
 
      устройства (ключевое слово UNIT в операторе OPEN). Для чте-
 
      ния с устройства ввода  или  записи  на  устройство  вывода
 
      логический номер устройства задается в соответствующем опе-
 
      раторе ввода-вывода языка фортран (ключевое слово UNIT это-
 
      го оператора). Если используется  неявный  оператор  ввода-
 
      вывода, логический номер не задается; язык фортран  исполь-


                                - 119 -
 
                          00152-01 97 09-2
 
 
      зует подразумеваемый логический номер устройства.
 
           Если с программой работает более одного  человека,  то
 
      следует генерировать логические номера устройств процедурой
 
      LIB$GET_LUN [2]; это гарантирует, что номер  будет  единст-
 
      венным среди номеров, используемых в программе.
 
 
 
           3.1.1.1. Неявные устройства ввода-вывода
 
 
           Если в операторе  ввода-вывода  языка  фортран  задать
 
      UNIT как звездочку (*) или опустить его, то операторы ввода
 
      будут использовать неявное устройство SYS$INPUT, а операто-
 
      ры вывода будут использовать неявное устройство SYS$OUTPUT.
 
      Опускать UNIT можно не  во  всяком  операторе  ввода-вывода
 
      языка фортран [3]. Кроме того, устройства ввода-вывода свя-
 
      заны со специальными  логическими  именами  языка  фортран.
 
      Табл. 7 детализирует связь неявных устройств языка  фортран
 
      с системными логическими именами и логическими именами язы-
 
      ка фортран.
 
                                                     Таблица 7
 
      --------------------------------------------------------
                            !
            Оператор        !        Логическое имя
                            !
          ввода-вывода      !----------------------------------
                            !                 !
                            !   Язык фортран  !    Системное
                            !                 !
      --------------------------------------------------------
                            !                 !
      READ (UNIT=*,...)LIST ! FOR$READ        ! SYS$INPUT
                            !                 !
      READ FMT,LIST         ! То же           ! То же
                            !                 !
      аCCEPT FMT, LIST      ! FOR$ACCEPT      ! "
                            !                 !
      WRITE (UNIT=*,...)LIST! FOR$PRINT       ! SYS$OUTPUT
                            !                 !
      PRINT FMT, LIST       ! То же           ! То же
                            !                 !
      TYPE FMT, LIST        ! FOR$TYPE        ! "
 


                                - 120 -
 
                          00152-01 97 09-2
 
 
                                           Продолжение табл. 7
      --------------------------------------------------------
                            !
            Оператор        !        Логическое имя
                            !
          ввода-вывода      !----------------------------------
                            !                 !
                            !   Язык фортран  !    Системное
                            !                 !
      --------------------------------------------------------
 
           Человек, эксплуатирующий программу,  может  перенапра-
 
      вить ввод или вывод, переопределив SYS$INPUT  и  SYS$OUTPUT
 
      или определив одно из логических имен языка фортран.  Логи-
 
      ческие имена языка фортран (если они заданы) имеют  преиму-
 
      щество перед системными логическими именами.
 
           При использовании неявного устройства  оно  не  откры-
 
      вается явно и не  закрывается;  используется  просто  соот-
 
      ветствующий оператор ввода-вывода. В системе программирова-
 
      ния фортран для открытия неявного  устройства  используется
 
      умолчание оператора OPEN (включая ACCESS =  'SEQUENTIAL'  и
 
      CARRIAGECONTROL = 'FORTRAN'), поэтому следует помнить,  что
 
      первый символ строки не должен рассматриваться  как  символ
 
      данных: он будет воспринят как символ управления кареткой.
 
           Пример.
 
           Неявное использование оператора WRITE:
 
       INTEGER       OUTPUT_SIZE
 
       CHARACTER*512 OUTPUT
 
       ! ведущий пробел есть фортранный символ управления
 
       ! кареткой,
 
       ! характеристика по умолчанию форматного последовательного
 
       ! файла
 
       WRITE (UNIT=*,
 
       2      FMT='(2A)') ' ', OUTPUT (1:OUTPUT_SIZE)
 


                                - 121 -
 
                          00152-01 97 09-2
 
 
           3.1.1.2. Явные устройства ввода-вывода
 
 
           Для записи на  устройство,  отличное  от  связанных  с
 
      SYS$INPUT и SYS$OUTPUT нужно задать  имя  этого  устройства
 
      (физическое или логическое) в операторе OPEN и в дальнейших
 
      операторах READ и WRITE использовать логический номер  уст-
 
      ройства, указанного в операторе OPEN.
 
           Пример.
 
           Ведется запись на устройство с именем $TERMINAL1:
 
       INTEGER       STATUS,
 
       2             OUTPUT_SIZE
 
       CHARACTER*512 OUTPUT
 
       ! открыть выходной файл со списочным управлением
 
       ! кареткой
 
       OPEN (UNIT=1,
 
       2     FILE='$TERMINAL1',
 
       2     STATUS='UNKNOWN',
 
       2     CARRIAGECONTROL='LIST')
 
       WRITE (UNIT=1,
 
       2     FMT='(A)') OUTPUT (1:OUTPUT_SIZE)
 
 
           Можно также получить доступ к SYS$INPUT и  SYS$OUTPUT,
 
      указав устройство в операторе OPEN и используя  номер  уст-
 
      ройства из оператора OPEN в дальнейших  операторах  READ  и
 
      WRITE. Этот способ полезен в тех случаях, когда может  воз-
 
      никнуть необходимость изменить принятые по умолчанию харак-
 
      теристики устройства ввода-вывода.
 
           В   операторе   OPEN   атрибут   FILE   задается   как
 
      FILE='SYS$OUTPUT'     (для     операций     вывода)     или
 
      FILE='SYS$INPUT' (для операций  ввода);  атрибут  STATUS  =
 
      'OLD'.


                                - 122 -
 
                          00152-01 97 09-2
 
 
           Пример.
 
           Явное открытие SYS$OUTPUT для использования списочного
 
      управления кареткой (вместо принятого по умолчанию в  языке
 
      фортран):
 
       INTEGER       STATUS,
 
       2             OUTPUT_SIZE
 
       CHARACTER*512 OUTPUT
 
       ! открыть SYS$OUTPUT со списочным управлением кареткой
 
       OPEN (UNIT=1,
 
       2     FILE='SYS$OUTPUT',
 
       2     STATUS='OLD',
 
       2     CARRIAGECONTROL='LIST')
 
       ! писать строку на SYS$OUTPUT
 
       WRITE (UNIT=1,
 
       2      FMT='(A)') OUTPUT (1:OUTPUT_SIZE)
 
 
           Если задается стандартное логическое имя устройства  в
 
      операторе  OPEN  (FOR$READ,   FOR$ACCEPT,   FOR$PRINT   или
 
      FOR$TYPE), то это имя  должно  быть  определено  до  вызова
 
      программы, иначе будет ошибка имени файла.
 
 
 
           3.1.2. Получение записи с устройства ввода
 
 
           Операция чтения получает одну запись со входного  уст-
 
      ройства и делает  ее  значением  указанной  переменной  или
 
      переменных. На терминале запись заканчивается нажатием кла-
 
      виши-разделителя. Разделители - это символы КОИ-8 от NUL до
 
      US (от 0 до 31) кроме LF, VT, FF, TAB и BS. Обычный  разде-
 
      литель - это CR, генерируемый нажатием клавиши <RETURN>.
 
           Для получения символьных данных может применяться про-
 
      цедура  LIB$GET_INPUT  (чтения  данных   после   подсказки-


                                - 123 -
 
                          00152-01 97 09-2
 
 
      запроса). Если должны быть получены несимвольные данные, то
 
      предпочтительнее  использовать  ввод-вывод  языка  фортран,
 
      поскольку он позволяет преобразовывать  данные  в  желаемый
 
      формат [1]. Для ввода в языке фортран предусмотрены  опера-
 
      тор ACCEPT, читающий данные с SYS$INPUT, и  оператор  READ,
 
      читающий данные с любого указанного устройства.
 
           Необходимо следить, чтобы указанные для приема  данных
 
      переменные могли воспринять то наибольшее  число  символов,
 
      которое имеет право  ввести  пользователь  программы  (если
 
      только не предусматривается отбрасывать часть символов  при
 
      вводе).  Переполнение   входной   переменной   вызывает   в
 
      LIB$GET_INPUT неисправимую ошибку LIB$_INPSTRTRU (определе-
 
      но в $LIBDEF); переполнение входной переменной при  исполь-
 
      зовании операторов ввода-вывода языка фортран не обязатель-
 
      но вызывает ошибку, но приводит к потере части данных.
 
 
 
           3.1.2.1. Процедура LIB$GET_INPUT
 
 
           Процедура LIB$GET_INPUT присваивает  вводимые  символы
 
      указанной символьной переменной.  Для  LIB$GET_INPUT  может
 
      быть задана еще одна переменная,  которой  будет  присвоено
 
      число введенных символов; эта переменная должна быть  опре-
 
      делена как INTEGER*2. При вводе с  терминала  LIB$GET_INPUT
 
      может перед чтением выдать  подсказку.  При  нетерминальных
 
      операциях вывод подсказки автоматически подавляется.
 
           Пример.
 
           Получение строки с использованием LIB$GET_INPUT:
 
      INTEGER*4     STATUS,
 
      2             LIB$GET_INPUT
 
      INTEGER*2     INPUT_SIZE
 


                                - 124 -
 
                          00152-01 97 09-2
 
 
      CHARACTER*512 INPUT
 
      STATUS=LIB$GET_INPUT (INPUT,         !Вводимое значение
 
      2                     'INPUT VALUE:',!Возможность подсказки
 
      2                     INPUT_SIZE)    !Длина вводимой
 
                                           !информации
 
                                           !(возможность)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
           В дальнейшем при ссылках на введенные  данные  следует
 
      указывать не символьную переменную целиком, а подстроку или
 
      последовательность элементов массива. Например, если  полу-
 
      ченные символы присвоены переменной символьного типа INPUT,
 
      а число прочитанных символов размещено в переменной с  име-
 
      нем  INPUT_SIZE,   то   ссылаться   необходимо   на   INPUT
 
      (1:INPUT_SIZE), A не на INPUT.
 
 
 
           3.1.2.2. Оператор ACCEPT
 
 
           Оператор ACCEPT читает данные с логического устройства
 
      FOR$ACCEPT (если оно определено) или с  SYS$INPUT.  Данные,
 
      которые должны читаться в  последовательном  режиме,  пере-
 
      даются с устройства ввода переменной или  переменным,  ука-
 
      занным в списке. Можно задать формат данных или  воспользо-
 
      ваться свободным форматом  (списочно-ориентированный  ввод-
 
      вывод), задав вместо формата звездочку (*). Например,  спи-
 
      сочно-ориентированный оператор используется для чтения чис-
 
      ловой переменной с SYS$INPUT:
 
      INTEGER*4 NUMBER
 
      ACCEPT *, NUMBER
 


                                - 125 -
 
                          00152-01 97 09-2
 
 
           3.1.2.3. Оператор READ
 
 
           Оператор READ передает данные с устройства ввода пере-
 
      менной или переменным, указанным  в  списке.  По  умолчанию
 
      вводимая запись не может превышать 132 символа. Это умолча-
 
      ние можно отменить явным открытием  вводного  устройства  и
 
      заданием спецификатора RECL. Переполнение максимальной дли-
 
      ны вызывает неисправимую ошибку FOR$ERRDURREA (определена в
 
      $FORDEF).
 
           Для символьного ввода (то есть ввода без  преобразова-
 
      ния данных) рекомендуются следующие соглашения:
 
           1) размер входной  записи.  Используется  спецификация
 
      формата Q для получения числа символов во  входной  записи.
 
      Переменную, связанную со спецификацией Q, нужно  определить
 
      как целую;
 
           2) входные данные. Для ввода данных используется  фор-
 
      мат а. Переменную, связанную с  этой  спецификацией,  нужно
 
      описать как символьную; использовать как строку символов  с
 
      границами от единицы до величины, прочитанной по специфика-
 
      ции Q;
 
           3) подсказка. Для подсказки  сначала  нужно  выполнить
 
      оператор WRITE со спецификацией формата а и  текстом  подс-
 
      казки, за которым следует знак  денежной  единицы  ($)  для
 
      подавления возврата каретки и перевода  строки  (если  тре-
 
      буется, чтобы входные данные находились в  одной  строке  с
 
      подсказкой). Другой способ: можно не ставить знак  денежной
 
      единицы в спецификации формата, а поставить его первым сим-
 
      волом в строке-подсказке, где он будет воспринят как  форт-
 
      ранный символ управления кареткой.
 
           Примечание. С помощью фортранного оператора READ можно
 


                                - 126 -
 
                          00152-01 97 09-2
 
 
      обрабатывать более одной записи.
 
           Пример.
 
           Выдается подсказка на SYS$OUTPUT и принимаются  данные
 
      с SYS$INPUT:
 
      INTEGER       INPUT_SIZE
 
      CHARACTER*512 INPUT
 
      WRITE (UNIT=*,
 
      2      FMT='(A,$)' )' INPUT VALUE: '
 
      READ (UNIT=*,
 
      2     FMT='(Q,A)') INPUT_SIZE,
 
      2                  INPUT (1:INPUT_SIZE)
 
      ! ведущий пробел в подсказке есть фортранный символ
 
      ! управления кареткой, который должен быть включен, если
 
      ! только не было открытия SYS$INPUT и не задан символ
 
      ! управления кареткой, отличного от фортранного. Хвостовые
 
      ! пробелы построены так, что на терминале вслед за
 
      ! двоеточием появляется пробел.
 
 
           При дальнейших ссылках на введенные символьные  данные
 
      нужно указывать соответствующую подстроку или элементы мас-
 
      сива, а не всю символьную переменную. Например,  если  вве-
 
      денные символы помещены в символьную  переменную  INPUT,  а
 
      число полученных  символов  хранится  в  переменной  INPUT_
 
      SIZE, то ссылаться необходимо на INPUT(1:INPUT_SIZE), а  не
 
      на INPUT.
 
           Если предполагается получать числовые  или  логические
 
      данные, то преобразование их можно сделать частью  операции
 
      ввода. При этом  следует  проверять  наличие  ошибки  FOR$_
 
      INPCONERR (определена в $FORDEF), которая возникает в  слу-
 
      чае, когда вводимый символ не  может  быть  преобразован  в
 


                                - 127 -
 
                          00152-01 97 09-2
 
 
      число.
 
           Пример.
 
           Вводимое числовое значение присваивается переменной:
 
      INTEGER    WHOLE_NUMBER,
 
      2          IOSTAT,
 
      2          STATUS,
 
      2          IO_OK
 
      PARAMETER  (IO_OK = O)
 
      INCLUDE    '($FORDEF)'
 
      WRITE (UNIT=*,
 
      2      IOSTAT=IOSTAT,
 
      2      FMT='(BN,I)') WHOLE_NUMBER
 
      ! цикл по ошибке, если пользователь введет нецелое
 
      ! значение
 
      DO WHILE (IOSTAT .NE. IO_OK)
 
        CALL ERRSNS (,,,,STATUS)
 
        IF(STATUS .EQ. FOR$_INPCONERR) THEN
 
          WRITE (UNIT=*,
 
      2      FMT='(A,$)') 'OOPS. TRY AGAIN:'
 
          READ (UNIT=*,
 
      2     IOSTAT=IOSTAT,
 
      2     FMT='(BN,I)') WHOLE_NUMBER
 
        ELSE
 
          CALL LIB$SIGNAL (%VAL (STATUS))
 
        END IF
 
      END DO
 
           Обычный способ приема переменного числа входных  запи-
 
      сей (значений, для которых выдается  подсказка-запрос)  или
 
      строк из файла данных - это чтение и обработка  записей  до
 
      тех пор, пока не будет обнаружен конец файла. Конец файла -


                                - 128 -
 
                          00152-01 97 09-2
 
 
      это одно из следующих условий:
 
           1) терминал. Пользователь ввел <CTRL/Z>. Для  гарантии
 
      выполнения этого соглашения можно выдать пользователю сооб-
 
      щение, что для прекращения операции ввода необходимо нажать
 
      <CTRL/Z>;
 
           2) командная процедура. Достигнут конец последователь-
 
      ности вводимых строк;
 
           3) файл. Достигнут фактический конец файла.
 
           Записи обрабатываются в цикле (одна запись  на  итера-
 
      цию) и заканчивается цикл по концу файла.  При  обнаружении
 
      конца файла LIB$CET_INPUT возвращает ошибку RMS$_EOF (опре-
 
      делена  в  $RMSDEF);   язык   фортран   генерирует   ошибку
 
      FOR$_ENDDURREA (определена в $FORDEF).  В  операции  чтения
 
      языка фортран также можно обнаружить конец файла,  проверяя
 
      состояние возврата операции ввода (IOSTAT) на отрицательное
 
      значение, либо задав спецификатор END в операторе READ.
 
           Пример.
 
           Оператор READ языка фортран используется в цикле обра-
 
      ботки последовательности целых чисел с SYS$INPUT:
 
 
      ! состояние возврата и коды ошибок
 
      INTEGER    STATUS,
 
      2          IOSTAT,
 
      3          STATUS_OK,
 
      4          IOSTAT_OK
 
      PARAMETER (STATUS_OK = 1,
 
      2          IO_OK = 0)
 
      INCLUDE   '($FORDEF)'
 
      ! читать запись данных при каждой итерации
 
      INTEGER INPUT_NUMBER
 


                                - 129 -
 
                          00152-01 97 09-2
 
 
      ! накопление записи данных
 
      INTEGER   STORAGE_COUNT,
 
      2         STORAGE_MAX
 
      PARAMETER (STORAGE_MAX = 255)
 
      INTEGER    STORAGE_NUMBER (STORAGE_MAX)
 
      ! выдать инструкцию интерактивному пользователю
 
      TYPE *,'вводите значения.
 
      2       для завершения нажмите <CTRL/Z >'
 
      ! принять первое вводимое значение
 
      WRITE (UNIT=*,
 
      2      FMT='(A,$)') ' INPUT VALUE:'
 
      READ (UNIT=*
 
      2     IOSTAT=IOSTAT,
 
      2     FMT='(BN,I)') INPUT_NUMBER
 
      IF (IOSTAT .EQ. IO_OK) THEN
 
        STATUS = STATUS_OK
 
      ELSE
 
        CALL ERRSNS (,,,,STATUS)
 
      END IF
 
      ! обрабатывать каждое вводимое значение до конца файла
 
      DO WHILE ((STATUS .NE. FOR$ENDDURREA) .AND.
 
                (STORAGE_COUNT .LT. STORAGE_MAX))
 
      ! повторять в случае ошибки преобразования
 
        DO WHILE (STATUS .EQ. FOR$_INPCONERR)
 
          WRITE (UNIT=*,
 
      2      FMT'= (A,$)') 'TRY AGAIN:'
 
          READ (UNIT=*,
 
      2     IOSTAT=IOSTAT,
 
      2     FMT='(BN,I)') INPUT_NUMBER
 
          IF (IOSTAT .EQ. IO_OK) THEN


                                - 130 -
 
                          00152-01 97 09-2
 
 
            STATUS = STATUS_OK
 
          ELSE
 
            CALL ERRSNS (,,,,STATUS)
 
          END IF
 
        END DO
 
        ! продолжать, если не введен конец файла
 
        IF (STATUS .NE. FOR$ENDDURREA) THEN
 
          ! проверить состояние последнего чтения
 
          IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
          ! сохранить введенные числа во входном массиве
 
          STORAGE_COUNT = STORAGE_COUNT + 1
 
          STORAGE_NUMBER (STORAGE_COUNT) = INPUT_NUMBER
 
          ! принять следующее вводимое значение
 
          WRITE (UNIT =*,
 
      2      FMT='(A,$)') ' INPUT VALUE:'
 
          READ (UNIT=*,
 
      2      IOSTAT=IOSTAT,
 
      2      FMT='(BN,I)') INPUT_NUMBER
 
          IF (IOSTAT .EQ. IO_OK) THEN
 
            STATUS = STATUS_OK
 
          ELSE
 
            CALL ERRSNS (,,,,STATUS)
 
          END IF
 
        END IF
 
      END DO
 


                                - 131 -
 
                          00152-01 97 09-2
 
 
           3.1.3. Вывод данных
 
 
           Организация вывода символьных данных с помощью  проце-
 
      дуры LIB$PUT_OUTPUT является наиболее простым способом. Для
 
      вывода несимвольных данных предпочтительнее вывод  операто-
 
      ром языка фортран, поскольку он дает возможность преобразо-
 
      вать данные в нужный формат. Для вывода язык фортран распо-
 
      лагает операторами TYPE и PRINT, которые выводят данные  на
 
      SYS$OUTPUT, и оператором WRITE, выводящим  данные  на  уст-
 
      ройство по указанному логическому имени.
 
 
 
           3.1.3.1. Процедура LIB$PUT_OUTPUT
 
 
           Процедура LIB$PUT_OUTPUT выводит одну запись на  логи-
 
      ческое устройство SYS$OUTPUT. Следует избегать вывода запи-
 
      сей, длина которых превосходит разрешенную для данного уст-
 
      ройства. Для терминала длина  выводимой  записи  обычно  80
 
      или 132 символа, в зависимости от установленных  физических
 
      характеристик терминала. Длина  выводимой  строки  на  уст-
 
      ройство печати - до  132  символов.  Если  длина  выводимой
 
      записи превосходит длину строки устройства, то лишние  сим-
 
      волы либо отбрасываются, либо переносятся в следующую стро-
 
      ку в зависимости от установленных физических характеристик.
 
           Выводимое значение должно быть символьного  типа.  Для
 
      переменных символьного типа следует указывать точное  число
 
      символов для вывода, и  не  выводить  неопределенную  часть
 
      переменной.
 
           Пример.
 
           Вывод значения символьного выражения на  устройство  с
 
      логическим именем SYS$OUTPUT
 


                                - 132 -
 
                          00152-01 97 09-2
 
 
      INTEGER*4    STATUS,
 
      2            LIB$PUT_OUTPUT
 
      CHARACTER*40 ANSWER
 
      INTEGER*4    ANSWER_SIZE
 
      STATUS=LIB$PUT_OUTPUT('ANSWER: '// ANSWER (1:ANSWER_SIZE))
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
 
 
           3.1.3.2. Оператор WRITE
 
 
           Оператор WRITE языка  фортран  выводит  на  устройство
 
      вывода одну или больше записей по формату.  Для  символьных
 
      данных используется спецификация формата а.  Если  выходные
 
      данные заданы подстрокой, то добавлять к спецификации  фор-
 
      мата а число не нужно. Если используется фортранное  управ-
 
      ление кареткой, то первым символом записи должен быть  сим-
 
      вол управления кареткой,  а  не  символ  данных.  Например,
 
      вывод символьного выражения можно оформить следующим  обра-
 
      зом:
 
 
      CHARACTER*31 ANSWER
 
      INTEGER      ANSWER_SIZE
 
      ! писать ответ
 
      ! с ведущим пробелом для фортранного управления кареткой
 
      WRITE (UNIT=*,
 
      2      FMT='(A)') ' ANSWER: ' // ANSWER (1:ANSWER_SIZE)
 
 
           Вместо конкатенации (сцепления)  выводимых  данных  их
 
      можно указать в  списке  констант  и  переменных,  разделив
 
      запятыми, и использовать формат, ориентированный на  список
 
      (свободный формат) или задать спецификацию формата для каж-
 
      дого элемента списка. Константы можно включать не в  список
 


                                - 133 -
 
                          00152-01 97 09-2
 
 
      вывода, а в спецификацию формата.
 
           Примеры:
 
           1. Вывод данных списком (свободный формат):
 
      WRITE (UNIT=*,
 
      2      FMT*) 'ANSWER: ', ANSWER (1:ANSWER_SIZE)
 
           2. Вывод по формату для каждого элемента списка:
 
      WRITE (UNIT=*,
 
      2      FMT='(2A)') ' ANSWER: ', ANSWER (1:ANSWER_SIZE)
 
           3. Вывод символьной константы в спецификации формата:
 
      WRITE (UNIT=*,
 
      2      FMT='('' ANSWER: '',A)') ANSWER (1:ANSWER_SIZE)
 
 
 
           3.1.3.3. Форматирование числового вывода
 
 
           Преобразование  числовых  данных  может  быть  сделано
 
      частью операции вывода, например:
 
 
      INTEGER*4 ANSWER
 
      WRITE (UNIT=*,
 
      2      FMT='(A,I)') ' ANSWER: ',
 
      2                   ANSWER
 
 
           При этом вид распечатанных данных может оказаться неп-
 
      риемлемым. Например, по спецификации I символьное представ-
 
      ление числа помещается в поле из 12 позиций,  выравнивается
 
      по правому краю и дополняется слева пробелами. Если  ANSWER
 
      равно 31, то будет напечатано:
 
      ANSWER:          31
 
           Если точно известно, сколько  символов  требуется  для
 
      записи числа, то это число можно задать как часть  специфи-
 
      кации. Например, вместо I можно  использовать  спецификацию
 


                                - 134 -
 
                          00152-01 97 09-2
 
 
      I2. Более естественным решением будет преобразовать число в
 
      строку символов внутренним образом, используя  спецификацию
 
      по умолчанию, а затем перед выводом удалить ведущие  пробе-
 
      лы, например:
 
 
      INTEGER*4    ANSWER,
 
      2            START
 
      CHARACTER*12 STRING
 
      ! преобразовать число в строку символов
 
      WRITE (UNIT=STRING,
 
      2      FMT='(I)') ANSWER
 
      ! найти первый символ - не пробел
 
      START = 1
 
      DO WHILE (STRING (START:START) .EQ. ' ')
 
        START = START + 1
 
      END DO
 
      ! писать подстроку, начиная с первого не пробела
 
      WRITE (UNIT=*,
 
      2      FMT='(2A)') 'ANSWER: ', STRING (START:12)
 
 
 
           3.1.3.4. Вывод нескольких записей
 
 
           Поток выходных данных можно разбить на несколько запи-
 
      сей, разделяя спецификации формата косой чертой (/). Напри-
 
      мер, для печати константы на одной строке, а значения пере-
 
      менной - на другой, можно использовать оператор:
 
      WRITE (UNIT=*,
 
      2      FMT='(A/A)') ' ANSWER: ', ANSWER (1:ANSWER_SIZE)
 


                                - 135 -
 
                          00152-01 97 09-2
 
 
           3.1.3.5. Оператор TYPE
 
 
           Фортранный оператор TYPE передает одну  или  несколько
 
      записей на SYS$OUTPUT (FOR$TYPE, если оно определено). Мож-
 
      но задавать формат данных или использовать спискоориентиро-
 
      ванный ввод-вывод, задав в качестве формата звездочку  (*).
 
      При ориентированном на список выводе запись  строится  так,
 
      что у нее автоматически появляется ведущий пробел,  поэтому
 
      можно не задавать  первым  символом  фортранное  управление
 
      кареткой.
 
           Пример.
 
           На SYS$OUTPUT выводится константа и числовая  перемен-
 
      ная:
 
      INTEGER*4 ANSWER
 
      TYPE *, 'ANSWER: ', ANSWER
 
 
 
           3.1.3.6. Обработка массивов и записей
 
 
           Оператор WRITE дает возможность  задавать  в  операции
 
      вывода, как форматного, так и бесформатного,  имена  масси-
 
      вов. Однако имя  записи  (структуры)  может  использоваться
 
      только в бесформатном выводе. При использовании  форматного
 
      вывода для записей каждое поле записи должно рассматривать-
 
      ся как  отдельная  переменная.  Например,  форматный  вывод
 
      используется для вывода всего массива STOCK_NUMBER и инфор-
 
      мации из записи ITEM:
 
 
      INTEGER*4 STOCK_NUMBER(10)
 
      STRUCTURE /ITEM_DESCR/
 
       INTEGER*4 CODE
 
       CHARACTER*80 DESCRIPTION
 


                                - 136 -
 
                          00152-01 97 09-2
 
 
       INTEGER*4 D_LEN
 
      END STRUCTURE
 
      RECORD /ITEM_DESCR/ ITEM
 
      WRITE (UNIT=*,
 
      2      FMT='(10I4)') STOCK_NUMBER
 
      WRITE (UNIT=*,
 
      2      FMT='(I10,''  '',A)') ITEM.CODE,
 
      2                           ITEM.DESCRIPTION(1:ITEM.D_LEN)
 
 
           Оператор WRITE дает возможность обрабатывать  последо-
 
      вательность элементов с  помощью  неявного  цикла.  Неявные
 
      циклы особенно удобны  при  обработке  массивов.  Например,
 
      выводится одна строка для каждого  элемента  массива  ITEM,
 
      начиная с первого  элемента  и  кончая  элементом,  позиция
 
      которого определяется значением ITEM_COUNT:
 
 
      STRUCTURE /ITEM_DESCR/
 
       INTEGER*4    CODE
 
       CHARACTER*80 DESCRIPTION
 
       INTEGER*4    D_LEN
 
      END STRUCTURE
 
      RECORD /ITEM_DESCR/ ITEMS (255)
 
      INTEGER*4 ITEM_COUNT
 
      WRITE (UNIT=*,
 
      2      FMT='(I10,''   '',A)')
 
      2      (ITEMS(I).CODE,                ! часть 1
 
      2       ITEMS(I).DESCRIPTION (1:ITEMS(I).D_LEN),
 
      2       I = I, ITEM_COUNT)             ! часть 2
 
 
           Список ввода-вывода должен  быть  заключен  в  скобки.
 
      Первая часть списка состоит из  элементов  данных,  которые
 


                                - 137 -
 
                          00152-01 97 09-2
 
 
      должны быть выведены; вторая часть списка ввода вывода есть
 
      задание цикла. Переменная I служит управляющей  переменной.
 
      Управляющая переменная может быть использована и  в  первой
 
      части списка, но не может быть модифицирована.
 
           Приведенный неявный цикл эквивалентен следующему явно-
 
      му циклу:
 
      DO I = 1, ITEM_COUNT
 
        WRITE (UNIT=*,
 
      2      FMT='(I10,'' '',A)')
 
      2               (ITEMS(I), CODE,
 
      2                ITEMS(I), DESCRIPTION(1:ITEMS(I).D_LEN))
 
      END DO
 
 
 
           3.1.3.7. Символы управления кареткой
 
 
           Символы управления  кареткой  должны  быть  первыми  в
 
      выводимой записи. Стандартные операции управления  кареткой
 
      следующие:
 
           1) межстрочный промежуток (пробел). Вывод записи пред-
 
      варяется переводом строки и возвратом  каретки  и  заканчи-
 
      вается возвратом каретки;
 
           2) двойной межстрочный промежуток (ноль). Выводу запи-
 
      си предшествует вывод двух символов (перевод строки и возв-
 
      рат каретки), а заканчивается вывод записи возвратом карет-
 
      ки;
 
           3) перевод страницы (единица). Выводу записи предшест-
 
      вует вывод символа перевода страницы и возврата каретки,  а
 
      заканчивается вывод возвратом каретки;
 
           4) надпечатка (плюс). Выводу записи предшествует вывод
 
      возврата каретки (без перевода строки или перевода  страни-
 


                                - 138 -
 
                          00152-01 97 09-2
 
 
      цы), а заканчивается вывод возвратом каретки;
 
           5) подсказка (знак денежной  единицы).  Выводу  записи
 
      предшествует перевод строки  и  возврат  каретки.  В  конце
 
      записи возврат каретки не выводится. Для  вывода  подсказки
 
      помимо символа управления можно  использовать  спецификацию
 
      формата $.
 
           Перечисленные символы управления кареткой имеют  смысл
 
      (по умолчанию) при использовании операторов WRITE и TYPE со
 
      спецификацией формата. В операторе WRITE это умолчание мож-
 
      но отменить явным открытием устройства с заданием  специфи-
 
      катора CARRIAGECONTROL.
 
 
 
           3.2. Управление экраном
 
 
           Предлагаемые средства для управления  изображением  на
 
      экране терминала - это процедуры из библиотеки исполнитель-
 
      ных процедур [2] SMG, которые обеспечивают простой и  неза-
 
      висимый от типа устройства интерфейс к терминалу. Эти  про-
 
      цедуры рассчитаны в первую очередь для работы с видеотерми-
 
      налами, но их можно применять и для работы с файлами и тер-
 
      миналами с твердой копией типа пишущей машинки.
 
           Порядок использования средств  управления  экраном  на
 
      вывод следующий:
 
           1) создать макет. Макет  -  это  логическая  двумерная
 
      область, в которую помещаются виртуальные  экспозиции.  Для
 
      создания макета и связывания с ним  физического  устройства
 
      используется процедура SMG$CREATE_PASTEBOARD. При ссылке на
 
      макет SMG проводит необходимые операции ввода-вывода;
 
           2) создать виртуальную экспозицию. Виртуальная  экспо-
 
      зиция - это двумерная логическая область, в  которую  поме-
 


                                - 139 -
 
                          00152-01 97 09-2
 
 
      щается подлежащая выводу информация. Для создания виртуаль-
 
      ной        экспозиции        используется         процедура
 
      SMG$CREATE_VIRTUAL_DISPLAY;
 
           3) вставить виртуальную экспозицию в макет. Чтобы сде-
 
      лать виртуальную экспозицию видимой, ее следует  отобразить
 
      (вставить) в макет с помощью  процедуры  SMC$PASTE_VIRTUAL_
 
      DISPLAY. На виртуальную экспозицию можно ссылаться  незави-
 
      симо от того, вставлена ли эта экспозиция в макет в текущий
 
      момент.
 
           Пример.
 
           Макет связывается с терминалом, создается  виртуальная
 
      экспозиция размером  в  экран  терминала  и  вставляется  в
 
      макет. Когда текст пишется  в  виртуальную  экспозицию,  он
 
      появляется на экране терминала:
 
      ! управляющие структуры для управления экраном
 
      INTEGER*4 PBID,   ! ID макета
 
      2         VDID,   ! ID виртуальной экспозиции
 
      2         ROWS,   ! Строки экрана
 
      2         COLS    ! Столбцы экрана
 
      ! переменные состояния и процедуры, вызываемые как функции
 
      INTEGER*4 STATUS,
 
      2         SMG$CREATE_RASTEBOARD,
 
      2         SMG$CREATE_VIRTUAL_DISPLAY,
 
      2         SMG$PASTE_VIRTUAL_DISPLAY
 
      ! установить SYS$OUTPUT для управления экраном
 
      ! принять число столбцов и строк экрана
 
      STATUS=SMG$CREATE_PASTEBOARD (PBID,        ! Возвращаемое
 
                                                 ! значение
 
      2                             'SYS$OUTPUT',
 
      2                              ROWS,       !   - I -


                                - 140 -
 
                          00152-01 97 09-2
 
 
      2                              COLS)       !   - I -
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! создать виртуальную экспозицию, вставляемую в полный
 
      ! экран
 
      STATUS = SMG$CREATE_VIRTUAL_DISPLAY (ROWS,
 
      2                                    COLS,
 
      2                                    VDID) ! RETURN VALUE
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! вставить виртуальную экспозицию в макет
 
      STATUS = SMG$PASTE_VIRTUAL_DISPLAY (VDID,
 
      2                                   PBID,
 
      2                                   1, ! начать с первой
 
      2                                   1) ! строки и первого
 
                                             ! столбца экрана
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
           Для использования процедур SMG в операциях ввода нужно
 
      связать виртуальную клавиатуру с физическим устройством или
 
      файлом,  используя  процедуру  SMG$CREATE_VIRTUAL_KEYBOARD.
 
      Процедуры ввода SMG могут использоваться сами по  себе  или
 
      совместно с процедурами вывода.
 
           Средства  управления  экраном  придерживаются  некоего
 
      внутреннего представления содержимого экрана; поэтому важно
 
      не смешивать процедуры SMG с другими  видами  терминального
 
      ввода-вывода.
 


                                - 141 -
 
                          00152-01 97 09-2
 
 
           3.2.1. Макеты
 
 
           Для создания макета и привязки его к физическому  уст-
 
      ройству  служит  процедура  SMG$CREATE_PASTEBOARD,  которая
 
      возвращает однозначно определенный идентификационный  номер
 
      макета; этот номер в дальнейшем используется в вызовах про-
 
      цедур SMG. После привязки  макета  к  устройству  программа
 
      ссылается только на макет. Средства управления экраном про-
 
      водят всю работу по связи макета с физическим устройством.
 
           При  создании  макета  экран  по  умолчанию  очищается
 
      средствами управления экраном. Все виртуальные  экспозиции,
 
      связанные с макетом, будут убраны с экрана, но их содержда-
 
      ние в памяти не изменится.
 
           Например, для стирания экрана можно использовать  опе-
 
      раторы:
 
 
      STATUS = SMG$ERASE_PASTEBOARD (PBID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
           Вызов SMG$DELETE_PASTEBOARD удаляет макет, делая экран
 
      недоступным для последующих  вставок  экспозиций.  Необяза-
 
      тельный  второй  параметр  процедуры  SMG$DELETE_PASTEBOARD
 
      позволяет задать, должна ли процедура очистить  экран  (как
 
      по умолчанию) или оставить его как есть. Например, удаление
 
      макета и очистка экрана выполняется операторами:
 
 
      STATUS = SMG$DELETE_PASTEBOARD (PBID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) .
 
 
           По умолчанию  экран  очищается  при  создании  макета.
 
      Предпочтительнее, экран очищать в конце сеанса.
 
           Процедурой  SMG$CHANGE_PBD_CHARACTERISTICS  устанавли-
 


                                - 142 -
 
                          00152-01 97 09-2
 
 
      ваются размеры экрана и его основной цвет.  Чтобы  получить
 
      более детальную информацию о физическом устройстве, исполь-
 
      зуется процедура SMG$GET_PASTEBOARD_ATTRIBUTES.
 
           Пример.
 
           Ширина экрана устанавливается равной 132, фон -  свет-
 
      лый; перед выходом восстанавливаются исходная ширина и цвет
 
      фона:
 
      INTEGER*4 WIDTH,
 
      2         COLOR
 
      INCLUDE   '($SMGDEF)'
 
      ! принять текущую ширину и цвет фона
 
      STATUS = SMG$CHANGE_PBD_CHARACTERISTICS (PBID,,
 
      2                                        WIDTH,,,,
 
      2                                        COLOR)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! изменить ширину и цвет фона
 
      STATUS = SMG$CHANGE_PBD_CHARACTERISTICS (PBID,
 
      2                                        132,,,,
 
      2                                        SMG$C_COLOR_WHITE)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
 
      ! восстановить ширину и цвет фона
 
      STATUS = SMG$CHANGE_PBD_CHARACTERISTICS (PBID,
 
      2                                        WIDTH,,,,
 
      2                                        COLOR)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 


                                - 143 -
 
                          00152-01 97 09-2
 
 
           3.2.2. Виртуальные экспозиции
 
 
           С помощью процедур SMG запись производится в виртуаль-
 
      ные экспозиции, представл.ющие собой прямоугольники  (логи-
 
      ческая структура). Вертикальный размер виртуальной экспози-
 
      ции представляет собой число строк, а горизонтальный - чис-
 
      ло столбцов. Позиция в виртуальной экспозиции  обозначается
 
      номером строки и столбца. Номера столбцов и строк начинают-
 
      ся с единицы.
 
 
 
           3.2.2.1. Создание виртуальной экспозиции
 
 
           Для создания виртуальной экспозиции используется  про-
 
      цедура   SMG$CREATE_VIRTUAL_DISPLAY,   которая   возвращает
 
      однозначно определенный идентификационный номер; этот номер
 
      используется для дальнейших ссылок на виртуальную  экспози-
 
      цию.
 
           При обращении к процедуре можно задать пятый (необяза-
 
      тельный) параметр, чтобы указать один или несколько из сле-
 
      дующих атрибутов видеотерминала: фон изображения,  затенен-
 
      ность, подчеркивание и мерцание. Все записанные в эту  экс-
 
      позицию символы будут вводиться согласно указанным  атрибу-
 
      там, если при записи текста в экспозицию не указано  обрат-
 
      ное. Например,  вся  записанная  в  экспозицию  HEADER_VDID
 
      информация будет появляться на затененном  экране  (принято
 
      по умолчанию):
 
 
      INCLUDE '($SMGDEF)'
 
      STATUS = SMG$CREATE_VIRTUAL_DISPLAY (1,  ! строка
 
      2                                    80, ! столбец
 
      2                                    HEADER_VDID,,
 


                                - 144 -
 
                          00152-01 97 09-2
 
 
      2                                    SMG$M_BOLD)
 
 
           С помощью четвертого параметра при вызове  SMG$CREATE_
 
      VIRTUAL_DISPLAY можно создать поле вокруг виртуальной  экс-
 
      позиции.  Это  поле  можно  пометить  с  помощью  процедуры
 
      SMG$LABEL_BORDER. При использовании поля следует предусмот-
 
      реть место для него: требуется на две строки и два  столбца
 
      больше, чем по размерам экспозиции. Например, меченное поле
 
      размещается вокруг  экспозиции  STATS_VDID.  После  вставки
 
      поле занимает строки 2 и 13 и столбцы 1 и 57:
 
      STATUS = SMG$CREATE_VIRTUAL_DISPLAY (10,  ! строки
 
      2                                    55,  ! колонки
 
      2                                    STATS_VDID,
 
      2                                    SMG$M_BORDER)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$LABEL_BORDER (STATS_VDID,
 
      2                          'STATISTICS')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$PASTE_VIRTUAL_DISPLAY (STATS_VDID,
 
      2                                   PBID,
 
      2                                   3,   ! строка
 
      2                                   2)   ! колонка  .
 
 
 
           3.2.2.2. Вставка виртуальной экспозиции в макет
 
 
           Чтобы  виртуальную  экспозицию  сделать  видимой,   ее
 
      вставляют     в     макет     с      помощью      процедуры
 
      SMG$PASTE_VIRTUAL_DISPLAY. Положение виртуальной экспозиции
 
      в макете указывают, задавая номер строки и столбца  макета,
 
      на пересечении которых должен находиться левый верхний угол
 
      экспозиции.


                                - 145 -
 
                          00152-01 97 09-2
 
 
           Пример.
 
           Определение двух виртуальных экспозиций и вставка их в
 
      один макет:
 
      INCLUDE '($SMCDEF)'
 
      INTEGER*4 PBID,
 
      2         HEADER_VDID,
 
      2         STATS_VDID
 
      INTEGER*4 STATUS,
 
      2         SMG$CREATE_PASTEBOARD,
 
      2         SMG$CREATE_VIRTUAL_DISPLAY,
 
      2         SMG$PASTE_VIRTUAL_DISPLAY
 
      ! создать макет для SYS$OUTPUT
 
      STATUS = SMG$CREATE_PASTEBOARD (PBID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! заголовок вставляется в первые строки экрана
 
      STATUS = SMG$CREATE_VIRTUAL_DISPLAY (3,           !строки
 
      2                                    80,          !столбцы
 
      2                                    HEADER_VDID, !имя
 
      2                                    SMG$M_BORDER)!поле
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$PASTE_VIRTUAL_DISPLAY (HEADER_VDID,
 
      2                                   PBID,
 
      2                                   1,            !строка
 
      2                                   1)            !колонка
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! область статистики вставляется с 5-й по 15-ю строку
 
      ! и со 2-го по 56-й столбец
 
      STATUS = SMG$CREATE_VIRTUAL_DISPLAY (10,          !строки
 
      2                                    55,          !колонки
 
      2                                    STATS_VDID,  !имя


                                - 146 -
 
                          00152-01 97 09-2
 
 
      2                                    SMG$_BORDER) !поле
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$PASTE_VIRTUAL_DISPLAY (STATS_VDID,
 
      2                                   PBID,
 
      2                                   5,            !строка
 
      2                                   2)            !колонка
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! продолжение программы   .
 
 
           После выполнения фрагмента программы в  примере  экран
 
      будет разбит на две части: одна часть (верхняя) состоит  из
 
      трех строк по 80 позиций; вторая - из  следующих  10  строк
 
      по 55 позиций.
 
           Одну экспозицию можно поместить в любое число макетов.
 
      При изменении экспозиции все содержащие ее макеты автомати-
 
      чески обновляются.
 
           Макет может содержать любое число виртуальных экспози-
 
      ций. Виртуальные экспозиции можно вставлять одну на  другую
 
      до любой глубины, при этом  нижележащие  экспозиции  блоки-
 
      руются. Блокируются они только на той площади, которая пок-
 
      рывается верхней экспозицией, то есть не  покрытые  участки
 
      остаются видимыми на экране. Когда "вынимается"  из  макета
 
      виртуальная экспозиция,  перекрывающая  другую  виртуальную
 
      экспозицию, закрытая  часть  нижележащей  экспозиции  вновь
 
      становится видимой.
 
           Проверить, не является ли экспозиция перекрытой, можно
 
      с  помощью  процедуры  SMG$CHECK_FOR_OCCLUSION.   Например,
 
      двухстрочная экспозиция итога  вставляется  на  место  двух
 
      последних строк экспозиции статистики, если эта  экспозиция
 
      не перекрыта; если же эта экспозиция перекрыта, то  предпо-
 


                                - 147 -
 
                          00152-01 97 09-2
 
 
      лагается, что она перекрыта как раз итоговой экспозицией, и
 
      итоговая экспозиция вынимается из макета, отчего  две  пос-
 
      ледних строки  в  экспозиции  статистики  снова  становятся
 
      видимыми:
 
      STATUS = SMG$CHECK_FOR_OCCLUSION (STATS_VDID,
 
      2                                 PBID,
 
      2                                 OCCLUDE_STATE)
 
      ! OCCLUDE_STATE определено как INTEGER*4
 
        IF (OCCLUDE_STATE) THEN
 
          STATUS = SMG$UNPASTE_VIRTUAL_DISPLAY (SUM_VDID,
 
      2                                     PBID)
 
          IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
        ELSE
 
          STATUS = SMG$ PASTE_VIRTUAL_DISPLAY (SUM_VDID,
 
      2                                    PBID,
 
      2                                    11,
 
      2                                    2)
 
          IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
        END IF
 
 
 
           3.2.2.3. Перегруппировка виртуальных экспозиций
 
 
           Вставленные в макет экспозиции можно  перегруппировать
 
      с помощью передвижек или перевставок:
 
           1) передвижка. Для передвижки  виртуальной  экспозиции
 
      используется процедура SMG$MOVE_VIRTUAL_DISPLAY.  Например,
 
      для передвижки экспозиции 2 на пять  позиций  вправо  можно
 
      использовать операторы:
 
      STATUS = SMG$MOVE_VIRTUAL_DISPLAY (VDID,
 
      2                                  PBID,
 


                                - 148 -
 
                          00152-01 97 09-2
 
 
      2                                  5,
 
      2                                  10)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
           2) перевставка. Для перевставки экспозиции использует-
 
      ся процедура SMG$REPASTE_VIRTUAL_DISPLAY:
 
      STATUS = SMG$REPASTE_VIRTUAL_DISPLAY (VDID,
 
      2                                     PBID,
 
      2                                     4,
 
      2                                     4)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
 
 
           3.2.2.4. Удаление виртуальной эспозиции из макета
 
 
           Удалить виртуальную экспозицию из  макета  можно  нес-
 
      колькими способами:
 
           1) стереть виртуальную экспозицию.  Например,  вызовом
 
      процедуры SMG$UNPASTE_ VIRTUAL_DISPLAY виртуальная экспози-
 
      ция стирается с экрана,  но  сохраняется  ее  содержание  в
 
      памяти:
 
 
      STATUS = SMG$UNPASTE_VIRTUAL_DISPLAY (STATS_VDID,
 
      2                                     PBID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
           2) уничтожить виртуальную экспозицию. Вызов  процедуры
 
      SMG$DELETE_VIRTUAL_DISPLAY удаляет виртуальную экспозицию с
 
      экрана и удаляет ее содержание из памяти, например:
 
 
      STATUS = SMG$DELETE_VIRTUAL_DISPLAY (STATS_VDID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
           3) уничтожить несколько виртуальных экспозиций.  Вызов
 


                                - 149 -
 
                          00152-01 97 09-2
 
 
      процедуры SMG$POP_VIRTUAL_DISPLAY  удаляет  указанную  вир-
 
      туальную экспозицию и все вставленные после нее с экрана  и
 
      уничтожает их содержание в памяти, например:
 
 
      STATUS = SMG$POP_VIRTUAL_DISPLAY (STATS_VDID,
 
      2                                 PBID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
 
           3.2.2.5. Модификация виртуальной экспозиции
 
 
           Средства управления экраном имеют  две  процедуры  для
 
      модификации характеристик существующей виртульной  экспози-
 
      ции:  процедура   SMG$CHANGE_VIRTUAL_DISPLAY,   позволяющая
 
      менять размер, видеоатрибуты или поле экспозиции, и  проце-
 
      дура SMG$CHANGE_RENDITION, позволяющая менять видеоатрибуты
 
      части экспозиции.
 
           Примеры:
 
           1. SMG$CHANGE_VIRTUAL_DISPLAY используется для измене-
 
      ния величины экспозиции к пяти строкам и  семи  столбцам  и
 
      для отключения всех видеоатрибутов. Если уменьшается  вели-
 
      чина экспозиции, то любые символы, которые окажутся  лишни-
 
      ми, будут стерты с экрана:
 
 
      STATUS = SMG$CHANGE_VIRTUAL_DISPLAY (WHOOPS_VDID,
 
      2                                    5,  ! строки
 
      2                                    7,, ! колонки
 
      2                                    0)  ! изображение
 
 
           2. Используется SMG$CHANGE_RENDITION для  того,  чтобы
 
      привлечь внимание к первым двадцати стобцам экспозиции ста-
 
      тистики. Это делается установкой реверсивного фона  (обрат-
 
      ное свечение) установленому для этой экспозиции по  умолча-


                                - 150 -
 
                          00152-01 97 09-2
 
 
      нию:
 
 
      STATUS=SMG$CHANGE_RENDITION (STATS_VDID,
 
      2                           1,         ! строки
 
      2                           1,         ! колонки
 
      2                           10,        ! но6+р строки
 
      2                           20,        ! номер колонки
 
      2                           ,          ! изображение
 
      2                        SMG$M_REVERSE)
 
 
           SMG$CHANGE_RENDITION использует три набора  видеоатри-
 
      бутов для определения атрибутов, которые должны быть приме-
 
      нены к указанной части экспозиции: видеоатрибуты экспозиции
 
      по умолчанию, атрибуты, заданные в параметре  RENDITION-SET
 
      процедуры  SMG$CHANGE_RENDITION,  и  атрибуты,  заданные  в
 
      параметре    RENDITION-COMPLEMENT     SMG$CHANGE_RENDITION.
 
      Результаты всех возможных комбинаций показаны в табл. 8.
 
                                                      Таблица 8
 
      ---------------------------------------------------------
                                    !
                  Параметр          !
                                    !
      -------------------------------
                      !             !
        RENDITION-SET ! RENDITION-  !        Результат
                      !             !
                      ! COMPLEMENT  !
                      !             !
      ---------------------------------------------------------
                      !             !
        Нет           ! Нет         ! Используется умолчание
                      !             !
        Есть          ! Нет         ! Устанавливается атрибут
                      !             !
        Нет           ! Есть        ! Обратное умолчание
                      !             !
        Есть          ! Есть        ! Сбрасывается атрибут
 
 
           В последнем примере видеоатрибут обращения фона  задан
 
      в RENDITION-COMPLEMENT,  но  не  в  RENDITION-SET,  поэтому
 


                                - 151 -
 
                          00152-01 97 09-2
 
 
      будет установлено значение этого атрибута, обратное к  зна-
 
      чению по умолчанию для экспозиции, и выбранная часть  будет
 
      выделена.
 
           Примечание. Результирующий атрибут выбирается на осно-
 
      ве атрибута, установленного по  умолчанию,  а  не  текущего
 
      состояния атрибута. Если используются процедуры  SMG,  явно
 
      устанавливающие видеоатрибуты, то текущие атрибуты экспози-
 
      ции могут не совпадать с атрибутами по умолчанию.
 
 
 
           3.2.3. Запись
 
 
           Процедуры вывода SMG позволяют записывать текст в экс-
 
      позиции и удалять  или  модифицировать  существующий  текст
 
      экспозиции. Изменения экспозиции видимы только тогда, когда
 
      она вставлена в макет.
 
 
 
           3.2.3.1. Позиционирование курсора
 
 
           Каждая виртуальная экспозиция имеет собственную  логи-
 
      ческую позицию курсора. Управлять позицией курсора  в  вир-
 
      туальной экспозиции можно с помощью следующих процедур:
 
           1) SMG$HOME_CURSOR переводит курсор в угол виртуальной
 
      экспозиции. По умолчанию это есть верхний  левый  угол,  то
 
      есть первая строка и первый столбец экспозиции;
 
           2) SMG$SET_CURSOR_ABS  перемещает  курсор  в  заданную
 
      строку и столбец;
 
           3) SMG$SET_CURSOR_REL сдвигает курсор на заданные сме-
 
      щения от текущей позиции. Отрицательное  значение  означает
 
      путь вверх (по строкам) или влево  (по  столбцам).  Нулевое
 
      смещение движения не вызывает.
 
           Кроме того, многие процедуры позволяют задать  старто-


                                - 152 -
 
                          00152-01 97 09-2
 
 
      вую позицию для операции, отличную от текущей.
 
           Процедура SMG$RETURN_CURSOR_POS возвращает номер стро-
 
      ки и столбца текущего положения курсора внутри  виртуальной
 
      экспозиции; нет необходимости писать специальные  программы
 
      для слежения за курсором.
 
           Обычно курсор физически находится в  логической  пози-
 
      ции, в которой остался после последней записи в экспозицию.
 
      Если необходимо,  то  физическое  положение  курсора  можно
 
      установить с помощью процедуры SMG$GET_PHYSICAL_CURSOR.
 
 
 
           3.2.3.2. Посимвольная запись данных
 
 
           Если данные пишутся посимвольно, то простым  и  точным
 
      инструментом будет процедура SMG$PUT_CHARS.  Эта  процедура
 
      помещает заданные символы на экран, начиная точно с  задан-
 
      ной позиции (по умолчанию  -  текущая  позиция  курсора  на
 
      экране). Запись ведется с замещением тех символов,  которые
 
      раньше стояли на месте записываемых; другие позиции  экрана
 
      не затрагиваются. Перед вызовом SMG$PUT_CHARS числовые дан-
 
      ные следует преобразовать в символьные с помощью внутренне-
 
      го оператора WRITE языка фортран.
 
           Пример.
 
           Целое число переводится в строку символов и помещается
 
      в обозначенную позицию виртуальной экспозиции:
 
 
      CHARACTER*4 HOUSE_NO_STRING
 
      INTEGER*4    HOUSE_NO,
 
      2            LINE_NO,
 
      2            STATS_VDID
 
      WRITE (UNIT=HOUSE_NO_STRING,
 
      2      FMT='(I4)') HOUSE_NO


                                - 153 -
 
                          00152-01 97 09-2
 
 
      STATUS = SMG$PUT_CHARS (STATS_VDID,
 
      2                       HOUSE_NO_STRING,
 
      2                       LINE_NO, ! строка
 
      2                       1)       ! колонка  .
 
 
           Преобразованное целое выравнено  по  правому  краю  от
 
      столбца 4,  поскольку  задана  спецификация  формата  I4  и
 
      пишется полная строка символов. Чтобы выравнивать  преобра-
 
      зованное число по левому краю, следует найти первый символ,
 
      отличный от пробела и писать подстроку, начинающуюся с это-
 
      го символа.
 
           Для того, чтобы записываемый  символ  был  вставлен  в
 
      строку без затирания имеющихся символов, используется  про-
 
      цедура SMG$INSERT_CHARS. Символы,  стоящие  на  том  месте,
 
      куда должна веститсь запись,  сдвигаются  вправо.  Символы,
 
      вытолкнутые  за  край  экспозиции,  отбрасываются;  никаких
 
      перекрытий не происходит и  курсор  остается  за  последним
 
      вставленным символом.
 
           Кроме упомянутых процедур можно использовать  SMG$PUT_
 
      CHARS_WIDE для записи на экран символов двойной ширины  или
 
      SMG$PUT_CHARS_HIGHWIDE для вывода на экран символов двойной
 
      ширины и высоты. При использовании  этих  процедур  следует
 
      отвести два места в строке на каждый символ двойной  ширины
 
      и по два строчных промежутка на каждую  строку,  содержащую
 
      символы двойной высоты. Нельзя  смешивать  в  одной  строке
 
      символы одинарного и двойного размера.
 
           Все эти четыре процедуры имеют параметры RENDITION-SET
 
      и RENDITION-COMPLEMENT, что позволяет задавать видеоатрибу-
 
      ты для вводимых символов.
 


                                - 154 -
 
                          00152-01 97 09-2
 
 
           3.2.3.3. Построчная запись данных
 
 
           Процедуры SMG$PUT_LINE и  SMG$PUT_WITH_SCROLL  записы-
 
      вают строки в виртуальную экспозицию одну за  другой.  Если
 
      поле экспозиции заполнено, то происходит его прокрутка. Нет
 
      необходимости следить за  строкой  своего  местонахождения.
 
      Обе процедуры дают возможность прокрутки вперед (вверх);  а
 
      SMG$PUT_WITH_SCROLL  дает   возможность   прокрутки   назад
 
      (вниз). SMG$PUT_LINE позволяет оставлять  несколько  проме-
 
      жутков.
 
           Примеры:
 
           1. Строки  из  некоторого  буфера   переписываются   в
 
      область экспозиции. Если буфер содержит больше  строк,  чем
 
      область экспозиции, то происходит прокрутка вперед:
 
      INTEGER*4     BUFF_COUNT,
 
      2             BUFF_SIZE (4096)
 
      CHARACTER*512 BUFF (4096)
 
      DO I = 1, BUFF_COUNT
 
        STATUS = SMG$PUT_WITH_SCROLL (VDID,
 
      2                             BUFF (I)(1:BUFF_SIZE (1)))
 
        IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      END DO !;
 
           2. Если буфер содержит больше строк, чем область  экс-
 
      позиции, то происходит прокрутка назад:
 
      DO I = 1, 1, -1
 
        STATUS = SMG$PUT_WITH_SCROLL (VDID,
 
      2                               BUFF (I) (1:BUFF_SIZE (I)),
 
      2                               SMG$M_DOWN)
 
        IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      END DO   !  .
 


                                - 155 -
 
                          00152-01 97 09-2
 
 
           Для точного управления прокруткой и перемещением  кур-
 
      сора можно писать SMG$PUT_CHARS и прокрутку задавать явно с
 
      помощью  SMG$SCROLL_DISPLAY_AREA.  SMG$PUT_CHARS  оставляет
 
      курсор за последним  записанным  символом  и  не  форсирует
 
      прокрутку. SMG$SCROLL_DISPLAY_AREA прокручивает  содержание
 
      экспозиции вперед, назад или в сторону без  записи  в  нее.
 
      Для сужения  прокрутки  на  часть  экспозиции  используется
 
      SMG$SET_DISPLAY_SCROLL_REGION.
 
           Чтобы вставить текст, не затирая содержание экспозиции
 
      используется процедура SMG$INSERT_LINE. Существующие строки
 
      сдвигаются вверх или вниз (задается),  открывая  место  для
 
      нового текста. Если текст длиннее одной строки,  то  лишние
 
      символы  отбрасываются  или  переносятся  в  новую   строку
 
      (задается).
 
           Кроме того, можно использовать процедуру SMG$PUT_LINE_
 
      WIDE для записи на экран  строки  текста  с  использованием
 
      символов двойной ширины. Следует отвести место  под  каждый
 
      символ двойной ширины из строки. Смешивать в  одной  строке
 
      символы одинарной и двойной ширины нельзя.
 
           Все четыре  процедуры  дают  возможность  пользоваться
 
      параметрами RENDITION-SET и RENDITION-COMPLEMENT для  зада-
 
      ния видеоатрибутов выводимого текста.
 
 
 
           3.2.3.4. Вычерчивание линий
 
 
           С помощью процедуры SMG$LINE на  экране  вычерчивается
 
      сплошная линия. На пересечениях и слияниях  линий  ставятся
 
      соответствующие отметки. Можно также использовать процедуру
 
      SMG$DRAW_RECTANGLE для вычерчивания сплошного прямоугольни-
 
      ка. Например, в области  экспозиции  статистики  (10  строк
 


                                - 156 -
 
                          00152-01 97 09-2
 
 
      по 55 позиций) необходимо нарисовать  прямоугольник,  пока-
 
      занный на рисунке.
 
 
      -------------------------------------------------------
      !            !            !                           !
      !            !            !                           !
      !------------!------------!---------------------------!
      !            !            !                           !
      !            !            !                           !
      !            !            !                           !
      !            !            !                           !
      !            !            !                           !
      -------------------------------------------------------
 
 
           Для выполнения этого можно воспользоваться,  следующей
 
      программой:
 
      STATUS = SMG$CREATE_VIRTUAL_DISPLAY (10,
 
      2                                    55,
 
      2                                    STATS_VDID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! чертить прямоугольник с левым верхним углом в строке 1
 
      ! и столбце 1 и правым нижним углом в строке 10 и столбце 5
 
      STATUS = SMG$DRAW_RECTANGLE (STATS_VDID,
 
      2                            1,1,
 
      2                            10,55)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! чертить вертикальные линии в столбцах 11,21 и 31
 
      DO I = 11, 31, 10
 
        STATUS = SMG$DRAW_LINE (STATS_VDID,
 
      2                       1,I,
 
      2                       10,I)
 
        IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      END DO
 
      ! чертить горизонтальную линию в строке 3
 
      STATUS = SMG$DRAW_LINE (STATS_VDID,
 


                                - 157 -
 
                          00152-01 97 09-2
 
 
      2                       3,1,
 
      2                       3,55)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$PASTE_VIRTUAL_DISPLAY (STATS_VDID,
 
      2                                   PBID,
 
      2                                   3,
 
      2                                   2)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
 
           3.2.3.5. Удаление текста
 
 
           Следующие процедуры удаляют указанные символы,  остав-
 
      ляя прочие без изменений:
 
           1) SMG$ERASE_CHARS стирает заданные  символы  в  одной
 
      строке;
 
           2) SMG$ERASE_LINE стирает символы в строке, начиная  с
 
      указанной позиции;
 
           3) SMG$ERASE_DISPLAY стирает заданные символы в  одной
 
      или больше строках.
 
           Следующие процедуры выполняют операцию  удаления.  При
 
      этой операции символ, стоящий правее удаленного, сдвигается
 
      на освободившееся место:
 
           1) SMG$DELETE_CHARS удаляет заданные символы  в  одной
 
      строке. Все символы, стоящие правее  удаленных,  сдвигаются
 
      влево;
 
           2) SMG$DELETE_LINE  удаляет  одну  или  больше  полных
 
      строк. Все оставшиеся  строки  в  экспозиции  подтягиваются
 
      наверх, заполняя освободившееся место.
 
           Пример.
 
           Стираются все символы в строке с номером LINE_NO начи-
 


                                - 158 -
 
                          00152-01 97 09-2
 
 
      ная со столбца с номером COLUMN_NO:
 
 
      STATUS = SMG$ERASE_LINE (STATS_VDID,
 
      2                        LINE_NO,
 
      2                        COLUMN_NO)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) !.
 
 
 
           3.2.4. Чтение
 
 
           Есть возможность читать текст из виртуальной  экспози-
 
      ции (SMG$READ_FROM_DISPLAY) или  с  виртуальной  клавиатуры
 
      (SMG$READ_STRING или SMG$READ_COMPOSED_LINE). Эти две  про-
 
      цедуры для ввода с виртуальной клавиатуры известны как про-
 
      цедуры ввода SMG. SMG$READ_FROM_DISPLAY не является  истин-
 
      ной процедурой ввода, поскольку  она  читает  текст  не  от
 
      пользователя, а и3 виртуальной экспозиции.
 
           Процедуры ввода SMG могут использоваться отдельно  или
 
      совместно с процедурами вывода SMG.  Здесь  предполагается,
 
      что процедуры ввода используются  совместно  с  процедурами
 
      вывода.
 
           При использовании процедур ввода SMG совместно с  про-
 
      цедурами вывода SMG всегда задается необязательный параметр
 
      процедуры  ввода,  указывающий  виртуальную  экспозицию,  в
 
      которую должен происходить ввод. Указанная виртуальная экс-
 
      позиция должна быть вставлена в макет, отвечающий устройст-
 
      ву, связанному с виртуальной  клавиатурой,  которое  задано
 
      как первый параметр процедуры ввода. Эта экспозиция  должна
 
      быть вставлена с первого столбца  и  не  имеет  права  быть
 
      перекрытой; справа от нее не должно быть другой экспозиции;
 
      ввод начинается с текущего  положения  курсора,  но  курсор
 
      должен находиться в первом столбце.


                                - 159 -
 
                          00152-01 97 09-2
 
 
           3.2.4.1. Чтение из экспозиции
 
 
           Прочесть содержание экрана можно с  помощью  процедуры
 
      SMG$READ_FROM_DISPLAY. По  умолчанию  при  операции  чтения
 
      читаются все символы от текущей позиции  курсора  до  конца
 
      строки.  Третий  параметр  SMG$READ_FROM_DISPLAY  позволяет
 
      выбирать начальную позицию  для  чтения  путем  определения
 
      набора  "разделителей".  Если   задан   параметр   "строка-
 
      разделитель", то SMG$READ_FROM_DISPLAY ищет назад от  теку-
 
      щей позиции курсора и читает строку от  первого  найденного
 
      разделителя (или от начала  строки).  Длина  строки  должна
 
      считываться отдельно.
 
           В  следующем  фрагменте  программы  читается   текущее
 
      содержание первой строки экспозиции STATS_VDID. Для  гаран-
 
      тии того, что  экспозиция  соответствует  текущему  моменту
 
      процедура  SMG$READ_FROM_DISRLAY   автоматически   вызывает
 
      SMG$FLUSH_BUFFER перед чтением из экспозиции:
 
 
      CHARACTER*4 STRING
 
      INTEGER*4   SIZE
 
 
 
      STATUS = SMG$HOME_CURSOR (STATS_VDID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      STATUS = SMG$READ_FROM_DISPLAY (STATS,VDID,
 
      2                               STRING)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      SIZE = 55
 
      DO WHILE ((STRING (SIZE:SIZE) .EQ. ' ') .AND.
 
      2         (SIZE .GT. 1))
 
        SIZE = SIZE - 1
 
      END DO


                                - 160 -
 
                          00152-01 97 09-2
 
 
           SMG$READ_FROM_DISPLAY дает простой  способ  ввода  для
 
      приложений, в которых  используется  управление  с  помощью
 
      меню.
 
 
 
           3.2.4.2. Чтение с виртуальной клавиатуры
 
 
           Процедура  SMG$CREATE_VIRTUAL_KEYBOARD   устанавливает
 
      устройство для операций ввода; по умолчанию это  устройство
 
      есть  терминал  пользователя.   Процедура   SMG$READ_STRING
 
      читает печатаемые на экране символы  пока  пользователь  не
 
      введет терминатор (конец строки) или пока не будет превыше-
 
      на максимальная длина (по умолчанию - 512 символов). Обычно
 
      терминатором является возврат каретки; полный список терми-
 
      наторов приведен в описании процедуры [2]. Текущее  положе-
 
      ние курсора на экране определяет,  откуда  начать  операцию
 
      чтения.
 
           Драйвер терминала системы SMG  отрабабатывает  возврат
 
      каретки не так, как процедуры SMG. Поэтому  для  аккуратной
 
      прокрутки экспозиции  необходимо  следить  за  вертикальной
 
      позицией в области экспозиции,  явно  устанавливая  позицию
 
      курсора и  прокручивая  экспозицию.  Если  операция  чтения
 
      происходит не в последней строке, то курсор нужно ставить в
 
      начало следующей строки перед выполнением следующей  опера-
 
      ции. Если операция чтения  происходит  в  последней  строке
 
      экспозиции,  то  нужно  прокрутить  экспозицию  с   помощью
 
      SMG$SCROLL_DISPLAY_AREA и затем установить курсор в  начало
 
      строки;   модифицировать   операцию   чтения   с    помощью
 
      TRM$M_TM_NOTRMECHO, что избавит от лишних прокруток.
 
           Пример.
 
           Чтение продолжается до тех пор, пока не  будет  нажата
 


                                - 161 -
 
                          00152-01 97 09-2
 
 
      клавиша <CTRL/Z>:
 
 
      ! читать первую запись
 
      STATUS = SMG$HOME_CURSOR (VDID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$READ_STRING (KBID,
 
      2                         TEXT,
 
      2                         'PROMPT: ',
 
      2                         4,
 
      2                         TRM$M_TM_TRMNOECHO,,,
 
      2                         TEXT_SIZE,,
 
      2                         VDID)
 
      ! читать оставшиеся записи до CTRL/Z
 
      DO WHILE (STATUS .NE. SMG$_EOF)
 
        IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
        ! обработать запись
 
        ! подготовить экран для текущего чтения
 
        ! область экспозиции содержит четыре строки
 
        STATUS = SMG$RETURN_CURSOR_POS (VDID, ROW, COL)
 
        IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
        IF (ROW .EQ. 4) THEN
 
          STATUS = SMG$SCROLL_DISPLAY_AREA (VDID)
 
          IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
          STATUS = SMG$SET_CURSOR_ABS (VDID, 4, 1)
 
          IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
        ELSE
 
          STATUS = SMG$SET_CURSOR_ABS (VDID,, 1)
 
          IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
          STATUS = SMG$SET_CURSOR_REL (VDID, 1)
 
          IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 


                                - 162 -
 
                          00152-01 97 09-2
 
 
        END IF
 
        ! читать следующую запись
 
        STATUS = SMG$READ_STRING (KBID,
 
      2                           TEXT,
 
      2                           'PROMPT: ',
 
      2                           4,
 
      2                           TRM$M_TM_TRMNOECHO,,,
 
      2                           TEXT_SIZE,,
 
      2                           TEXT_SIZE,,
 
      2                           VDID)
 
      END DO
 
 
           Процедуры  SMG$PUT_LINE  и  SMG$PUT_WITH_SCROLL  могут
 
      вести прокрутку не так, как ожидается. При прокрутке, кото-
 
      рая сочетает ввод и вывод, возможные трудности  можно  пре-
 
      дупредить, используя  процедуру  SMG$PUT_CHARS  и  управляя
 
      прокруткой так, как показано в этом примере.
 
 
 
           3.2.4.3. Чтение со вспомогательной клавиатуры
 
 
           Чтение с клавиатуры в режиме вспомогательной клавиату-
 
      ры (когда пользователь нажимает на символ клавиатуры не для
 
      ввода данных, а для выполнения некоторых специальных дейст-
 
      вий) происходит при модификации операции чтения  с  помощью
 
      модификаторов  TRM$M_TM_ESCAPE  и   TRM$_TM_NOECHO.   Чтобы
 
      узнать, какая клавиша была нажата, нужно анализировать тер-
 
      минатор (введенный символ).
 
           В следующем примере курсор движется по экрану в  ответ
 
      на действия пользователя, нажимающего клавиши вокруг клави-
 
      ши 5 вспомогательной клавиатуры. Клавиша 8 сдвигает  курсор
 
      на север (вверх), клавиша  9  сдвигает  курсор  на  северо-


                                - 163 -
 
                          00152-01 97 09-2
 
 
      восток, клавиша 6 сдвигает курсор  на  восток  (вправо),  и
 
      т.д. Процедура SMG$SET_CURSOR_REL вызывается  как  подпрог-
 
      рамма, а не как функция, поэтому в случае ошибки  не  прои-
 
      зойдет аварийное прекращение работы программы (ошибкой было
 
      бы пытаться вывести курсор за пределы области экспозиции, а
 
      нужно, чтобы в случае такой ошибки курсор просто остался на
 
      месте). Операция чтения модифицирована с помощью  модифика-
 
      тора TRM$M_TM_PURGE, чтобы воспрепятствовать чтению впереди
 
      курсора.
 
           Пример.
 
           Для управления передвижением курсора используются сле-
 
      дующие клавиши дополнительной (цифровой) клавиатуры:
 
           1) клавиша с цифрой 8 сдвигает курсор вверх;
 
           2) клавиша с цифрой 9 сдвигает курсор влево;
 
           3) клавиша 6 сдвигает курсор вправо;
 
           4) клавиша 7 сдвигает курсор вниз.
 
           Процедура SMG$SET_CURSOR_REL вызывается  как  подпрог-
 
      рамма, чтобы предотвратить аварийное  завершение  в  случае
 
      ошибки (попытка вывода курсора за пределы области  приводит
 
      к такой ошибке; курсор просто останется на месте). Операция
 
      чтения модифицируется с помощью модификатора TRM$M_TM_PURGE
 
      для запрещения чтения информации в позиции курсора:
 
 
 
      INTEGER STATUS,
 
      2       PBID,
 
      2       ROWS,
 
      2       COLUMNS,
 
      2       VDID,     ! ID виртуальной экспозиции
 
      2       KID,      ! ID клавиатуры
 
      2       SMG$CREATE_PASTEBOARD,


                                - 164 -
 
                          00152-01 97 09-2
 
 
      2       SMG$CREATE_VIRTUAL_DISPLAY,
 
      2       SMG$CREATE_VIRTUAL_KEYBOARD,
 
      2       SMG$PASTE_VIRTUAL_DISPLAY,
 
      2       SMG$HOME_CURSOR,
 
      2       SMG$READ_CURSOR_REL,
 
      2       SMG$READ_STRING,
 
      2       SMG$ERASE_PASTEBOARD,
 
      2       SMG$PUT_CHARS,
 
      2       SMG$READ_FROM_DISPLAY
 
      CHARACTER*31 INPUT_STRING,
 
      2            MENU_STRING
 
      INTEGER*2    TERMINATOR
 
      INTEGER*4    MODIFIERS
 
      INCLUDE '($SMGDEF)'
 
      INCLUDE '($TRMDEF)'
 
      ! установить экран и клавиатуру
 
      STATUS = SMG$CREATE_PASTEBOARD (PBID,
 
      2                               'SYS$OUTPUT',
 
      2                               ROWS,
 
      2                               COLUMNS)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$CREATE_VIRTUAL_DISPLAY (ROWS,
 
      2                                    COLUMNS,
 
      2                                    VDID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$PUT_CHARS (VDID,
 
      2                       '__MENU CHOICE ONE',
 
      2                       10,30)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$PUT_CHARS (VDID,


                                - 165 -
 
                          00152-01 97 09-2
 
 
      2                       '__MENU CHOICE TWO',
 
      2                       15,30)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (KID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$PASTE_VIRTUAL_DISPLAY (VDID,
 
      2                                   PBID,
 
      2                                   1,
 
      2                                   1)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! поставить курсор в северо-западный угол
 
      STATUS = SMG$HOME_CURSOR (VDID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! читать символ с клавиатуры
 
      MODIFIERS = TRM$M_TM_ESCAPE .OR.
 
      2           TRM$M_TM_NOECHO .OR.
 
      2           TRM$M_TM_PURGE
 
      STATUS = SMG$READ_STRING (KID,
 
      2                         INPUT_STRING,
 
      2                         ,
 
      2                         6,
 
      2                         MODIFIERS,
 
      2                         ,,,
 
      2                         TERMINATOR)
 
      DO WHILE ((STATUS) .AND.
 
      2         (TERMINATOR .NE. SMG$K_TRM_CR))
 
        ! проверить состояние последней операции чтения
 
        IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
        ! на север
 
        IF (TERMINATOR .EQ. SMG$K_TRM_KP8) THEN


                                - 166 -
 
                          00152-01 97 09-2
 
 
          CALL SMG$SET_CURSOR_REL (VDID, -1, 0)
 
        ! на северо-восток
 
        ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP9) THEN
 
          CALL SMG$SET_CURSOR_REL (VDID, -1, 1)
 
        ! на северо-запад
 
        ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP7) THEN
 
          CALL SMG$SET_CURSOR_REL (VDID, -1, -1)
 
        ! на юг
 
        ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP2) THEN
 
          CALL SMG$SET_CURSOR_REL (VDID, 1, 0)
 
        ! на юго-восток
 
        ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP3) THEN
 
          CALL SMG$SET_CURSOR_REL (VDID, 1, 1)
 
        ! на юго-запад
 
        ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP1) THEN
 
          CALL SMG$SET_CURSOR_REL (VDID, 1, -1)
 
        ! на восток
 
        ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP6) THEN
 
          CALL SMG$SET_CURSOR_REL (VDID, 0, 1)
 
        ! на запад
 
        ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP4) THEN
 
          CALL SMG$SET_CURSOR_REL (VDID, 0, -1)
 
        END IF
 
        ! читать другой символ
 
        STATUS = SMG$READ_STRING (KID,
 
      2                         INPUT_STRING,
 
      2                         ,
 
      2                         6,
 
      2                         MODIFIERS,
 
      2                         ,


                                - 167 -
 
                          00152-01 97 09-2
 
 
      2                         ,
 
      2                         ,
 
      2                         TERMINATOR)
 
      END DO
 
      ! читать пункт меню и отработать его
 
      ! наставления к чтению из экспозиции даны в 3.2.4.1.
 
      STATUS = SMG$READ_FROM_DISPLAY (VDID,
 
      2                               MENU_STRING)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
 
      ! очистить экран
 
      STATUS = SMG$ERASE_PASTEBOARD (PBID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      END
 
 
 
           3.2.4.4. Составной ввод
 
 
           Процедура  SMG$CREATE_KEY_TABLE  создает  таблицу,   в
 
      которой клавиши приведены в соответствие строкам  символов.
 
      Ввод с помощью процедуры SMG$READ_COMPOSED_LINE приводит  к
 
      замене кода нажатой клавиши строкой  символов  из  таблицы.
 
      Для   загрузки   таблицы   можно   использовать   процедуру
 
      SMG$ADD_KEY_DEF. Составной ввод позволяет распознавать:
 
           1) условные состояния. Одна и та же клавиша может быть
 
      определена так, что будет в разных состояниях иметь  разные
 
      значения. Можно определить  клавишу,  вызывающую  изменения
 
      состояния. Эти изменения могут быть временными (до  нажатия
 
      следующей определенной клавиши) или постоянными (до нажатия
 
      клавиши, меняющей состояние);
 
           2) прекращение ввода. Можно определить клавишу,  вызы-
 


                                - 168 -
 
                          00152-01 97 09-2
 
 
      вающую прекращение передачи входных данных  (аналог-клавиша
 
      <RETURN>). Если клавиша не определена для прекращения  вво-
 
      да, то пользователь должен нажать терминатор  или  клавишу,
 
      вызывающую прекращение ввода.
 
           Пример.
 
           Клавиши от 1 до 9 на вспомогательной клавиатуре  опре-
 
      делены, и пользователю предоставляется возможность  времен-
 
      ного изменения состояния нажатием клавиши PF1 (золотая кла-
 
      виша). Нажатие клавиши с цифрой 1 на  вспомогательной  кла-
 
      виатуре эквивалентно набору 1000 и нажатию <RETURN>.  Нажа-
 
      тие PF1 и 1 вспомогательной клавиатуры  эквивалентно  набо-
 
      ру 10000 и <RETURN>:
 
 
      INTEGER*4 TABLEID
 
      ! создать таблицу определения клавиш
 
      STATUS = SMG$CREATE KEY_TABLE (TABLEID)
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! загрузить таблицу
 
      ! если пользователь вводит PF1, состояние меняется
 
      ! путем BYTEN
 
      ! состояние BYTEN держится только до нажатия следующей
 
      ! клавиши
 
      STATUS = SMG$ADD_KEY_DEF (TABLEID,
 
      2                         'PF',
 
      2                         ,,,'BYTEN')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! нажатие клавиш от КР1 до КР9 в нулевом состоянии
 
      ! эквивалентно набору от 1000 до 9000 с нажатием <RETURN>
 
      STATUS = SMG$ADD_KEY_DEF (TABLEID,
 
      2                         'KP1' ,
 


                                - 169 -
 
                          00152-01 97 09-2
 
 
      2                         ,
 
      2                         SMG$M_KEY_TERMINATE,
 
      2                         '1000')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$ADD_KEY_DEF (TABLEID,
 
      2                          'KP2',
 
      2                          ,
 
      2                          SMG$M_KEY_TERMINATE,
 
      2                          '2000')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$ADD_KEY_DDF (TABLEID,
 
      2                         'KP9',
 
      2                         ,
 
      2                         SMG$M_KEY_TERMINATE,
 
      2                         '9000')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! нажатие клавиш от КР1 до КР9 эквивалентно набору от
 
      ! 10000 до 90000 с нажатием RETURN
 
      STATUS = SMG$ADD_KEY_DEF (TABLEID,
 
      2                         'KP1',
 
      2                         'BYTEN',
 
      2                         SMG$M_KEY_TERMINATE,
 
      2                         '10000')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$ADD_KEY_DEF (TABLEID,
 
      2                         'KP2',
 
      2                         'BYTEN',
 
      2                         SMG$M_KEY_TERMINATE,
 
      2                         '20000')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))


                                - 170 -
 
                          00152-01 97 09-2
 
 
      STATUS = SMG$ADD_KEY_DEF (TABLEID,
 
      2                         'KP9',
 
      2                         'BYTEN',
 
      2                         SMG$M_KEY_TERMINATE,
 
      2                         '90000')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! конец загрузки таблицы определения клавиш
 
      ! читать входные данные, заменяющие
 
      ! определения клавиш
 
      STATUS = SMG$READ_COMPOSED_LINE (KBID,
 
      2                                TABLEID,
 
      2                                STRING,
 
      2                                SIZE,
 
      2                                VDID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
           Для удаления определения клавиши используется процеду-
 
      ра SMG$DELETE_KEY_DEF, а для анализа определения клавиши  -
 
      процедура SMG$GET_KEY_DEF.  Загружать  таблицы  определений
 
      клавиш  можно   с   помощью   процедур   SMG$DEFINE_KEY   и
 
      SMG$LOAD_KEY_DEFS; входные данные этих процедур имеют форму
 
      DCL команды DEFINE/KEY.
 
           Для использования  клавиш  вспомогательной  клавиатуры
 
      эта  клавиатура  должна  находиться  в  прикладном   режиме
 
      (используется квалификатор  /APPLICATION  команды  DCL  SET
 
      TERMINAL [4].
 


                                - 171 -
 
                          00152-01 97 09-2
 
 
           3.2.5. Управление обновлениями экрана
 
 
           Две процедуры SMG позволяют  производить  изменения  в
 
      виртуальной экспозиции по мере их накопления. Использование
 
      процедуры SMG$BEGIN_DISPLAY_UPDATE приводит к тому, что все
 
      операции вывода в экспозицию, вставленную в макет,  ведутся
 
      только в буфер экспозиции. Процедура SMG$END_DISPLAY_UPDATE
 
      переписывает буфер экспозиции в макет.
 
           Процедуры SMG$BEGIN_DISPLAY_UPDATE и  SMG$END_DISPLAY_
 
      UPDATE увеличивают и  уменьшают  некоторый  счетчик.  Когда
 
      значение этого счетчика равно  нулю,  вывод  в  виртуальную
 
      экспозицию немедленно пересылается в макет. Наличие счетчи-
 
      ка позволяет подпрограмме запрашивать или отключать пакети-
 
      рование, не нарушая пакетного статуса вызывающей программы.
 
           Второй набор процедур,  SMG$BEGIN_PASTEBOARD_UPDATE  и
 
      SMG$END_PASTEBOARD_UPDATE, позволяет буферизировать вывод в
 
      макет тем же способом.
 
 
 
           3.2.6. Соглашения по использованию макетов
 
 
           При использовании процедур  SMG  необходимо  соблюдать
 
      осторожность, чтобы не нарушить соответствие между  экраном
 
      и его внутренним  представлением.  Рекомендуется  придержи-
 
      ваться следующих соглашений:
 
           1) не использовать другие формы обмена  с  терминалом,
 
      когда он активен как макет. Если же этого  избежать  нельзя
 
      (например, вызывается программа,  которая  может  проводить
 
      ввод-вывод не через SMG), то сначала нужно вызвать процеду-
 
      ру SMG$SAVE_PHYSICAL_SCREEN,  затем  выполнить  необходимую
 
      операцию; после завершения операции не через SMG нужно выз-
 
      вать процедуру SMG$RESTORE_ PHYSICAL_SCREEN, например:


                                - 172 -
 
                          00152-01 97 09-2
 
 
      STATUS = SMG$SAVE_PHYSICAL_SCREEN (PBID,
 
      2                                  SAVE_VDID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      CALL GET_EXTRA_INFO (INFO_ARRAY)
 
      STATUS = SMG$RESTORE_PHYSICAL_SCREEN (PBID,
 
      2                                     SAVE_VDID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) !;
 
 
           2) для разделения макета между несколькими программны-
 
      ми единицами (например, подпрограмма использует макет, соз-
 
      данный вызывающей программой) нужно использовать  идентифи-
 
      катор (ID) существующего макета, связанного  с  терминалом.
 
      Поэтому  программная  единица,   вызывающая   подпрограмму,
 
      выполняющую экранный ввод-вывод, должна передавать ID маке-
 
      та. Затем следует уничтожить все созданные  в  подпрограмме
 
      виртуальные экспозиции перед возвратом управления программе
 
      более высокого уровня.  Наиболее  надежный  способ  очистки
 
      виртуальных    экспозиций    -    использовать    процедуру
 
      SMG$POP_VIRTUAL_DISPLAY, задав имя первой из созданных экс-
 
      позиций. Например, вызывается подпрограмма, которая  прово-
 
      дит работу с экраном  терминала,  уничтожает  созданные  ею
 
      виртуальные экспозиции  и  возвращает  управление  головной
 
      программе:
 
 
      ! вызывающая программа
 
      CALL GET_EXTRA_INFO (PBID,
 
      2                    INFO_ARRAY)
 
      CALL STATUS = SMG$CREATE_PASTEBOARD (PBID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! подпрограмма
 
      SUBROUTINE GET_EXTRA_INFO (PBID,


                                - 173 -
 
                          00152-01 97 09-2
 
 
      2                          INFO_ARRAY)
 
      ! начало выполнямых операторов
 
      STATUS = SMG$CREATE_VIRTUAL_DISPLAY (4,
 
      2                                     40,
 
      2                                     INSTR_VDID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$PASTE_VIRTUAL_DISPLAY (INSTR_VDID,
 
      2                                     PBID, 1, 1)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$POP_VIRTUAL_DISPLAY (INSTR_VDID,
 
      2                                  PBID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      END ! ;
 
 
           3) для разделения  виртуальных  экспозиций,  созданных
 
      программой более высокого уровня, программа низкого  уровня
 
      должна использовать  ID  экспозиции,  созданной  программой
 
      высокого  уровня;  вызывающая  программная  единица  должна
 
      передать подпрограмме ID виртуальной экспозиции. Для разде-
 
      ления виртуальной экспозиции,  созданной  программой  более
 
      низкого уровня, программа высокого уровня должна  использо-
 
      вать ID этой  экспозиции;  подпрограмма  должна  возвращать
 
      вызвавшей программе ID  виртуальной  экспозиции.  Например,
 
      подпрограмме предоставляется возможность использовать  экс-
 
      позицию, созданную в вызывающей програмной единице:
 
 
      ! вызывающая программная единица
 
      STATUS = SMG$CREATE_VIRTUAL_DISPLAY (4,
 
      2                                    40,
 
      2                                    INSTR_VDID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))


                                - 174 -
 
                          00152-01 97 09-2
 
 
      STATUS = SMG$PASTE_VIRTUAL_DISPLAY (INSTR_VDID,
 
      2                                   PBID, 1, 1)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      CALL GET_EXTRA_INFO (PBID,
 
      2                    INSTR_VDID)
 
      IF (.NOT. STATUS) CAL LIB$SIGNAL (%VAL (STATUS))
 
      ! подпрограмма
 
      SUBROUTINE GET_EXTRA_INFO (PBID,
 
      2                          INSTR_VDID)
 
 
 
           3.3. Специальные операции ввода-вывода
 
 
           Процедуры управления экраном,  и  системные  служебные
 
      процедуры SYS$QIO и  SYS$QIOW  дают  возможность  выполнять
 
      операции ввода-вывода, которые  иным  способом  могут  быть
 
      доступны для программы, написанной на языке  программирова-
 
      ния высокого уровня. Например, можно позволить пользователю
 
      прервать нормальное выполнение программы вводом специально-
 
      го символа и иметь возможность анализа этого символа, можно
 
      также управлять отображением вводимого  символа,  временем,
 
      отведенным на ввод, а также чтением данных из буфера "запи-
 
      си вперед".
 
           Некоторые  операции  требуют  использования  системных
 
      служебных процедур SYS$QIO и SYS$QIOW. Подробная информация
 
      о системных служебных программах QIO изложена в [5].
 
           Другие операции, описанные в следующих разделах, могут
 
      быть выполнены с помощью процедур ввода SMG. Для  использо-
 
      вания процедур ввода SMG следует:
 
           1) вызвать SMG$CREATE_VIRTUAL_KEYBOARD, чтобы  связать
 
      логическую клавиатуру с устройством или спецификацией файла
 


                                - 175 -
 
                          00152-01 97 09-2
 
 
      (по  умолчанию  -  SYS$INPUT).  SMG$CREATE_VIRTUAL_KEYBOARD
 
      возвращает идентификационный номер  (ID)  клавиатуры;  этот
 
      номер используется для идентификации устройства или файла в
 
      процедурах ввода SMG;
 
           2) вызвать процедуру ввода  SMG  (SMG$READ_STRING  или
 
      SMG$READ_COMPOSED_LINE) для  чтения  данных  с  устройства,
 
      связанного с виртуальной клавиатурой.
 
           При использовании  процедур  ввода  SMG  без  процедур
 
      вывода SMG необязательный параметр для процедуры ввода нуж-
 
      но опустить.
 
 
 
           3.3.1. Прерывания CTRL/C и CTRL/Y
 
 
           Системная служебная  процедура  QIO  дает  возможность
 
      распознавать прерывания CTRL/C и CTRL/Y (пользователь  тер-
 
      минала вводит CTRL/C или CTRL/Y) даже если ввод с терминала
 
      не планировался. Для выполнения этого нужно:
 
           1) поставить в очередь асинхронное системное  прерыва-
 
      ние (AST). Вызвать SYS$QIO или  SYS$QIOW  с  кодом  функции
 
      IO$_SET_MODE и модификатором IO$M_CTRLCAST (для  прерываний
 
      CTRL/C), или IO$M_CTRLYAST  (для  прерываний  CTRL/Y).  Для
 
      параметра P1 задать имя процедуры (должна  быть  определена
 
      как EXTRNAL) для выполнения в случае прерывания. В качестве
 
      р2 можно задать необязательный параметр размером в  длинное
 
      слово для передачи его подпрограмме AST;
 
           2) написать подпрограмму  обработки  AST,  заданную  в
 
      параметре р1 служебной системной процедуры QIO, и скомпоно-
 
      вать ее с программой.  Эта  программа  может  принять  один
 
      параметр размером в длинное слово, который будет  связан  с
 
      параметром Р2 системной процедуры QIO. Для доступа ко  всем
 


                                - 176 -
 
                          00152-01 97 09-2
 
 
      другим данным вызывающей программы из процедуры AST необхо-
 
      димо использовать общие области.
 
           Если пользователь нажмет CTRL/C или CTRL/Y после того,
 
      как программа поставит в очередь запрос на обработку  соот-
 
      ветствующего AST, система прервет  исполнение  программы  и
 
      передаст управление процедуре обработки AST  (это  действие
 
      называется доставкой AST).  После  выполнения  подпрограммы
 
      обработки AST управление будет возвращено прерванной  прог-
 
      рамме в ту точку,  в  которой  случилось  прерывание  (если
 
      только подпрограмма обработки AST не вызовет выход из прог-
 
      раммы или доставку другого аST из очереди). Для корректного
 
      использования AST для CTRL/C и CTRL/Y нужно иметь в виду:
 
           1) AST являются  асинхронными.  Так  как  подпрограмме
 
      обработки AST не известно точное место, в котором произошло
 
      прерывание программы, то следует избегать работы с  данными
 
      или других действий, продолжающих работу основной  програм-
 
      мы. Подпрограмма AST должна известить  основную  программу,
 
      что произошло прерывание (например,  установкой  флага  или
 
      его сбросом) и завершиться;
 
           2) AST нуждаются в новых  каналах  к  терминалу.  Если
 
      выполняется попытка получить доступ к терминалу  с  помощью
 
      логических устройств SYS$INPUT  или  SYS$OUTPUT  (например,
 
      задав UNIT = *), то можно получить ошибку  ввода-вывода  по
 
      избыточности. Необходимо установить другой канал к термина-
 
      лу, открыв его явно;
 
           3) AST для CTRL/C и CTRL/Y являются одноразовыми  AST.
 
      После доставки AST для CTRL/C или CTRL/Y оно  выводится  из
 
      очереди. Если в дальнейшем нужно обработать еще одно преры-
 
      вание, то запрос на обработку нужно снова поставить в  оче-
 
      редь с помощью системной служебной процедуры QIO;


                                - 177 -
 
                          00152-01 97 09-2
 
 
           4) постановка в очередь нескольких AST.  Можно  поста-
 
      вить в очередь запрос на несколько AST (для одинаковых  или
 
      разных подпрограмм AST, на  одном  или  различных  каналах)
 
      путем многократного обращения к системной служебной  проце-
 
      дуре QIO. Система доставляет AST по схеме "первыM пришел  -
 
      последним ушел";
 
           5) перевод необработанных CTRL/C в CTRL/Y. Если  поль-
 
      зователь вводит CTRL/C, а AST для обработки этого  прерыва-
 
      ния в очередь не поставлено, то прерывание  CTRL/C  перево-
 
      дится в прерывание CTRL/Y;
 
           6) обработка прерывания  CTRL/Y  с  помощью  DCL.  DCL
 
      обрабатывает прерывания CTRL/Y возвращением пользователя на
 
      командный уровень DCL, где у пользователя есть  возможность
 
      продолжать выполнение программы или выйти из нее. DCL имеет
 
      преимущество перед заданными подпрограммами AST для  преры-
 
      вания CTRL/Y. Подпрограмма AST для CTRL/Y будет выполняться
 
      лишь в следующих  обязательствах:  если  прерывания  CTRL/Y
 
      отменены на командном уровне DCL  (SET  NOCONTROL_Y)  перед
 
      выполнением программы; если программа  отменяет  прерывания
 
      CTRL/Y    на    уровне    DCL    с    помощью     процедуры
 
      LIB$DISABLE_CTRL [2]; если  пользователь  после  прерывания
 
      программы с уровня DCL решит ее продолжать;
 
           7) снятие запроса из очереди AST для CTRL/Y и  CTRL/Y.
 
      Можно снять из очереди все запросы на AST  для  CTRL/C  или
 
      CTRL/Y на каком-нибудь канале, вызвав соответствующую  сис-
 
      темную служебную процедуру QIO, задав значение 0 для  пара-
 
      метра P1 (передается непосредственно  по  значению).  Можно
 
      снять из очереди все  запросы  на  AST  для  CTRL/с  вызвав
 
      SYS$CANCEL (системная служебная процедура)  для  соответст-
 
      вующего канала. Можно снять из очереди все AST  для  CTRL/Y


                                - 178 -
 
                          00152-01 97 09-2
 
 
      на некотором канале, обратившись к системной служебной про-
 
      цедуре SYS$DASSGN для этого канала;
 
           8) использование процедур SMG. Связаться с  терминалом
 
      с использованием процедур SMG можно как на уровне AST,  так
 
      и на уровне основной программы. Не  следует  пытаться  свя-
 
      заться с терминалом на уровне  AST,  если  это  делается  в
 
      основной программе.
 
           Пример.
 
           В программе разрешено  пользователю  прервать  выдачу,
 
      чтобы посмотреть, сколько уже напечатано строк:
 
 
      ! головная программа
 
      INTEGER STATUS
 
      ! записи с накопленными данными
 
      CHARACTER*132 STORAGE (255)
 
      INTEGER*4     STORAGE_SIZE (255),
 
      2             STORAGE_COUNT
 
      ! структуры QIO и QIOW
 
      INTEGER*2 INPUT_CHAN
 
      INTEGER*4 CODE
 
      STRUCTURE /IOSTAT_BLOCK/
 
        INTEGER*2 IOSTAT
 
        HYTE      TRANSMIT,
 
      2           RECEIVE,
 
      2           CRFILL,
 
      2           LFFILL,
 
      2           PARITY,
 
      2           ZERO
 
      END STRUCTURE
 
      RECORD /IOSTAT_BLOCK/ IOSB
 


                                - 179 -
 
                          00152-01 97 09-2
 
 
      ! флаг для извещения программы о прерывании CTRL/C
 
      LOGICAL*4 STRLC_CALLED
 
      ! подпрограмма AST для обработки прерывания CTRL/C
 
      EXTERNAL CTRLC_AST
 
      ! подпрограммы
 
      INTEGER SYS$ASSING,
 
      2       SYS$QIOW
 
      ! идентификаторы для операций ввода-вывода
 
      INCLUDE '($IODEF)'
 
      ! поместить значения в массив
 
      CALL LOAD_STORAGE (STORAGE,
 
      2                  STORAGE_SIZE,
 
      2                  STORAGE_COUNT)
 
      ! назначить канал и установить структуры QIOW
 
      STATUS = SYS$ASSING ('SYS$INPUT',
 
      2                    INPUT_CHAN,,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      CODE = IO$SETMODE .OR. IO$M_CTRLCAST
 
      ! поставить в очередь AST для обработки прерывания
 
      ! CTRL/C
 
          STATUS = SYS$QIOW (,
 
      2                  %VAL (INPUT_CHAN),
 
      2                  %VAL (CODE),
 
      2                  IOSB,
 
      2                  ,,
 
      2                  CTRLC_AST,   ! имя подпрограммы
 
      2                  CTRLC_CALLED,! параметр подпрограммы
 
                                      ! AST
 
      2                  ,,,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))


                                - 180 -
 
                          00152-01 97 09-2
 
 
      IF (.NOT. IOSB. IOSTAT)
 
      2  CALL LIB$SIGNAL (%VAL (IOSB. IOSTAT))
 
      ! массив STORAGE для экспозиции, по элементу на строку
 
      DO I = 1, STORAGE_COUNT
 
        TYPE *, STORAGE (I) (1. STORAGE_SIZE (I))
 
        ! дополнительные действия в случае введения пользователем
 
        ! CTRL/C
 
        IF (CTRLC_CALLED) THEN
 
          CTRLC_CALLED = .FALSE.
 
          ! показать пользователю число выведенных строк
 
          TYPE *, 'NUMBER OF LINEG: ',I
 
          ! восстановить AST в очереди
 
          STATUS = SYS$QIOW (,
 
      2                  %VAL (INPUT_CHAN),
 
      2                  %VAL (CODE),
 
      2                  IOSB,
 
      2                  ,,
 
      2                  CTRLC/AST,
 
      2                  CTRLC_CALLED,
 
      2                  ,,,)
 
          IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
          IF (.NOT. IOSB.IOSTAT)
 
      2  CALL LIB$SIGNAL (%VAL (IOSB.IOSTAT))
 
        END IF
 
      END DO
 
      END
 
      ! подпрограмма AST
 
      ! сообщить программе, что пользователь ввел CTRL/C
 
      SUBROUTINE CTRLC_AST (CTRLC_CALLED)
 
      LOGICAL*4 CTRLC_CALLED


                                - 181 -
 
                          00152-01 97 09-2
 
 
      CTRLC_CALLED = .TRUE.
 
      END
 
 
 
           3.3.2. Незапланированный ввод
 
 
           Ввод любой информации  с  терминала  можно  распознать
 
      даже если явно не задавать операцию чтения. Это делается  с
 
      помощью  процедуры  SMG$ENABLE_UNSOLICITED_INPUT,   KOTOрая
 
      использует механизм AST для передачи  управления  указанной
 
      пользователем подпрограмме при  вводе  любой  информации  с
 
      терминала; подпрограмма обработки AST  отвечает  за  чтение
 
      всей входной  информации.  Когда  подпрограмма  заканчивает
 
      работу, управление передается основной программе в ту  точ-
 
      ку, в которой ее прервали.
 
           SMG$ENABLE_UNSOLICITED_INPUT  не  является  процедурой
 
      SMG.  Перед  ее  вызовом  необходимо  вызвать   SMG$GREATE_
 
      PASTEBOARD, для  связи  макета  с  терминалом  (экраном)  и
 
      SMG$CREATE_VIRTUAL_KEYBOARD, для связи с тем же  терминалом
 
      виртуальной клавиатуры с клавиатурой терминала.
 
           У  процедуры  SMG$ENABLE_UNSOLICITED_INPUT  есть   три
 
      входных параметра:
 
           1) значение идентификационного номера макета  (исполь-
 
      зуется       значение,       возвращенное        процедурой
 
      SMG$CREATE_PASTEBOARD);
 
           2) имя подпрограммы обработки AST;
 
           3) параметр для передачи  подпрограмме  AST  (дополни-
 
      тельный).
 
           Процедура   SMG$ENABLE_UNSOLICITED_INPUT,   вызывающая
 
      подпрограмму обработки AST, передает этой подпрограмме  два
 
      параметра: идентификационный номер макета и  дополнительный
 


                                - 182 -
 
                          00152-01 97 09-2
 
 
      параметр. Обычно подпрограмма AST пишется с  тем  расчетом,
 
      что она будет читать  несанкционированный  ввод  с  помощью
 
      библиотечной    процедуры    SMG$READ_STRING.    Так    как
 
      SMG$READ_STRING требует задания виртуальной  клавиатуры,  с
 
      которой идет ввод, то идентификационный номер этой  клавиа-
 
      туры обычно передается в качестве дополнительного.
 
           Пример.
 
           Пользователю терминала разрешается прервать выдачу  на
 
      экран серии массивов, чтобы либо перейти к следующему  мас-
 
      сиву (задав ввод, начинающийся с заглавной буквы  N),  либо
 
      выйти из программы (введя что угодно, начинающееся с любого
 
      другого символа):
 
 
      ! головная программа
 
      ! головная программа вызывает DISPLAY_ARRAY однажды для
 
      ! каждого массива
 
      ! DISPLAY_ARRAY выводит массив в цикле
 
      ! если пользователь производит ввод с терминала, цикл
 
      ! прерывается и доставляется AST
 
      ! если ввод начинается с буквы N, то AST устанавливает
 
      ! DO_NEXT и продолжает выполнение---DISPLAY_ARRAY
 
      ! прекращает цикл обработки массива (так как DO_NEXT
 
      ! установлено)---и головная программа вызывает DISPLAY_
 
      ! ARRAY для следующего массива
 
      ! если ввод начинается не с N, то происходит выход
 
      ! программы
 
      INTEGER*4 STATUS,
 
      2         VKID, ! ID виртуальной клавиатуры
 
      2         PBID  ! ID макета
 
      ! массивы памяти
 


                                - 183 -
 
                          00152-01 97 09-2
             6;
 
      INTEGER*4 ARRAY1 (256),
 
      2         ARRAY2 (256),
 
      2         ARRAY3 (256)
 
      ! системные процедуры
 
      INTEGER*4 SMG$CREATE_PASTEBOARD,
 
      2         SMG$CREATE_VIRTUAL_KEYBOARD,
 
      2         SMG$ENABLE_INSOLICITED_INPUT
 
      ! подпрограмма AST
 
      EXTERNAL AST_ROUTINE
 
      ! создать макет
 
      STATUS = SMG$CREATE_PASTEBOARD (PBID,       ! ID макета
 
      2                             'SYS$INPUT')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! создать клавиатуру на том же устройстве
 
      STATUS=SMG$CREATE_VIRTUAL_KEYBOARD (VKID, ! ID клавиатуры
 
      2                                   'SYS$INPUT')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! разрешить несанкционированный ввод
 
      STATUS=SMG$ENABLE_UNSOLICITED_INPUT (PBID, ! ID макета
 
      2                                    AST_ROUTINE,
 
      2                                    VKID) ! передать в AST
 
                                                 ! ID клавиатуры
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! подпрограмму выдачи вызывать на каждый массив
 
      CALL DISPLAY_ARRAY (ARRAY1)
 
      CALL DISPLAY_ARRAY (ARRAY2)
 
      CALL DISPLAY_ARRAY (ARRAY3)
 
      END
 
      ! подпрограмма выдачи массива
 
      ! подпрограмма на выдачу одного массива


                                - 184 -
 
                          00152-01 97 09-2
 
 
      SUBROUTINE DISPLAY_ARRAY (ARRAY)
 
      ! формальный параметр
 
      INTEGER*4 ARRAY (256)
 
      ! состояние
 
      INTEGER*4 STATUS
 
      ! флаг для обработки следующего массива
 
      LOGICAL*4 DO_NEXT
 
      COMMON /DO_NEXT/ DO_NEXT
 
      ! если была доставка AST, восстановить
 
      IF (DO_NEXT) DO_NEXT = .FALSE.
 
      ! выдать целый массив, если пользователь не прервет
 
      I = 1
 
      ! восстановить начальное значение управляющей переменной
 
      ! если было прерывание от пользователя (DO_NEXT
 
      ! установлено), то прекратить цикл
 
      DO WHILE ((I .LE. 256) .AND. (.NOT. DO_NEXT))
 
        TYPE *, ARRAY (I)
 
        I = I + 1
 
      END DO
 
      END
 
      ! подпрограмма AST
 
      ! подпрограмма чтения несанкционированного ввода
 
      SUBROUTINE AST_ROUTINE (PBID,
 
      2                       VKID)
 
      ! формальные параметры
 
      INTEGER*4 PBID,              ! ID макета
 
      2         VKID               ! ID клавиатуры
 
      ! состояние
 
      INTEGER*4 STATUS
 
      ! флаг обработки следующего массива


                                - 185 -
 
                          00152-01 97 09-2
 
 
      LOGICAL*4 DO_NEXT
 
      COMMON /DO_NEXT/ DO_NEXT
 
      ! входная строка
 
      CHARACTER*4 INPUT
 
      ! подпрограммы
 
      INTEGER*4 SMG$READ_STRING
 
      ! чтение ввода
 
      STATUS = SMG$READ_STRING (VKID,  !KEYBOARD ID
 
      2                         INPUT)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! если ввод начинается с N , установить DO_NEXT
 
      ! иначе выйти из программы
 
      IF (INPUT (1:1) .EQ. 'N') THEN
 
      DO_NEXT = .TRUE.
 
      ELSE
 
        CALL EXIT
 
      END IF
 
      END
 
 
 
           3.3.3. Буфер предварительного ввода
 
 
           Вводимая пользователем на клавиатуре терминала  инфор-
 
      мация перед тем, как выдан запрос на чтение, сохраняется  в
 
      специальной структуре данных, называемой  "буфер  предвари-
 
      тельного ввода" и поддерживаемой системой. При  поступлении
 
      запроса на чтение с терминала буфер предварительного  ввода
 
      переписывается в буфер  ввода  программы.  Величина  буфера
 
      предварительного ввода заранее  устанавливается  равной  78
 
      байтов. Если характеристика терминала HOSTSYNC  установлена
 
      (обычное условие), то ввод в буфер  предварительного  ввода
 


                                - 186 -
 
                          00152-01 97 09-2
 
 
      прекращается (происходит запирание  клавиатуры),  когда  до
 
      его  заполнения  остается  8  байтов.  Если  характеристика
 
      HOSTSYNC сброшена, то, когда до заполнения буфера  остается
 
      восемь байтов, включается звуковой сигнал; если переполнить
 
      буфер, то лишние данные будут утеряны.  Системный  параметр
 
      TTY_ALTALARM определяет момент, в который прекращается ввод
 
      или подается звуковой сигнал.
 
           Перед чтением с клавиатуры буфер предварительного вво-
 
      да  можно  очистить  с   помощью   библиотечной   процедуры
 
      SMG$READ_STRING,   указав    модификатор    (параметр    5)
 
      TRM$M_TM_PURGE. Очистка буфера предварительного ввода  при-
 
      водит к тому, что прочитана будет только  информация,  вве-
 
      денная с клавиатуры терминала  после  запроса  чтения.  Все
 
      предыдущие символы в буфере  предварительного  ввода  будут
 
      утеряны, например:
 
 
      INTEGER*4     SMG$CREATE_VIRTUAL_KEYBOARD
 
      2             SMG$READ_STRING,
 
      2             STATUS,           !ID виртуальной клавиатуры
 
      2             VKID,
 
      2             INPUT_SIZE
 
      CHARACTER*512 INPUT
 
      INCLUDE       '($TRMDEF)'
 
      STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID, !ID клавиатуры
 
      2        'SYS$INPUT')            !устройство ввода-вывода
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$READ_STRING (VKID,          !ID клавиатуры
 
      2                         INPUT,         !чтение данных
 
      2                         'PROMPT>',
 
      2                         512,
 


                                - 187 -
 
                          00152-01 97 09-2
 
 
      2                         TRM$M_TM_PURGE,
 
      2                         ,,
 
      2                         INPUT_SIZE)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
           Очистить буфер предварительного ввода  можно  также  с
 
      помощью  операции  чтения  QIO  с  модификацией  IO$M_PURGE
 
      (определено в $IODEF). Можно отключить буфер  предваритель-
 
      ного ввода для последующих операций чтения с помощью опера-
 
      ции установки режима QIO, в которой  в  качестве  одной  из
 
      основных характеристик терминала задать TT$M_NOTYPEAHD.
 
           Можно проанализировать буфер предварительного ввода  с
 
      помощью  операции  анализа  режима  QIO   с   модификатором
 
      IO$M_TYPEAHDCNT. Число находящихся в буфере предварительно-
 
      го ввода символов и значение первого символа будут  возвра-
 
      щены в параметре P1.
 
           Величина буфера  предварительного  ввода  определяется
 
      системным параметром TTY_TYPAHDSZ. Можно  задать  альтерна-
 
      тивный буфер предварительного ввода, установив  характерис-
 
      тику  терминала  ALTYPAHD;  размер  альтернативного  буфера
 
      предварительного ввода  определяется  системным  параметром
 
      TTY_ALTYPEAHD.
 
 
 
           3.3.4. Эхо-отображение
 
 
           Обычно система выводит  на  экран  любой  отображаемый
 
      символ, который пользователь вводит с клавиатуры терминала.
 
      Система также отвечает выводом специальных слов на  некото-
 
      рые управляющие символы; например, в ответ на <CTRL/Z> поя-
 
      вится слово 'EXIT'. Если запроса на чтение с клавиатуры  не
 
      было, то вводимые символы попадают в буфер предварительного


                                - 188 -
 
                          00152-01 97 09-2
 
 
      ввода и на экране не отображаются, пока их  не  прочтут  из
 
      буфера предварительного ввода.
 
           Отображение символов на  экране  можно  отключить  при
 
      запросе ввода с помощью  библиотечной  процедуры  SMG$READ_
 
      STRING, указав модификатор  операции  чтения  (параметр  5)
 
      TRM$M_TM_NOECHO. Можно  отключить  отображение  только  для
 
      управляющих символов, модифицируя операцию чтения с помощью
 
      TRM$M_TM_TRMNOECHO. Например, для  операции  чтения  отклю-
 
      чаются все отображения:
 
 
      INTEGER*4     SMG$CREATE_VIRTUAL_KEYBOARD,
 
      2             SMG$READ_STRING,
 
      2             STATUS,
 
      2             VKID,           ! ID виртуальной клавиатуры
 
      2             INPUT_SIZE
 
      CHARACTER*512 INPUT
 
      INCLUDE       '($TRMDEF)'
 
      STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID, !KEYBOARD ID
 
      2        'SYS$INPUT')           !устройство ввода-вывода
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$READ_STRING (VKID,     !KEYBOARD ID
 
      2                         INPUT,    !чтение данных
 
      2                         'PROMPT>',
 
      2                         512,
 
      2                         TRM$M_TM_NOECHO,
 
      2                         ,,
 
      2                         INPUT_SIZE)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
           Отображение можно отключить также с  помощью  операции
 
      чтения  QIO  с  модификацией  IO$M_NOECHO   (определена   в


                                - 189 -
 
                          00152-01 97 09-2
 
 
      $IODEF). Запретить отображение для дальнейших операций чте-
 
      ния можно  с  помощью  операции  установления  режима  QIO,
 
      задающей TT$M_NOECHO  в  качестве  основной  характеристики
 
      терминала.
 
 
 
           3.3.5. Тайм-аут (время задержки ответа)
 
 
           Можно ограничить для  пользователя  время,  в  течение
 
      которого он должен дать ответ на запрос чтения. Это  дости-
 
      гается использованием для  чтения  с  клавиатуры  терминала
 
      библиотечной процедуры SMG$READ_STRING с параметрOM 6. Этот
 
      параметр задает число  секунд,  отводимых  пользователю  на
 
      ответ. Если пользователь не успевает ответить за отведенное
 
      время, будет возвращена  ошибка  SS$TIMEOUT  (определена  в
 
      $SSDEF). Например, в программе пользователю предоставляется
 
      восемь секунд, в течение которых он должен ответить на зап-
 
      рос чтения:
 
 
      INTEGER*4     SMG$CREATE_VIRTUAL_KEYBOARD,
 
      2             SMG$READ_STRING,
 
      2             STATUS,
 
      2             VKID,
 
      2             INPUT_SIZE
 
      CHARACTER*512 INPUT
 
      INCLUDE       '($SSDEF)'
 
      STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID, !KEYBOARD ID
 
      2        'SYS$INPUT')           !устройство ввода-вывода
 
 
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$READ_STRING (VKID,        !KEYBOARD ID
 
      2                         INPUT,       !чтение данных


                                - 190 -
 
                          00152-01 97 09-2
 
 
      2                         'PROMPT>',
 
      2                         512,
 
      2                         ,
 
      2                         8,
 
      2                         ,
 
      2                         INPUT_SIZE)
 
      IF (.NOT. STATUS) THEN
 
        IF (STATUS .EQ. SS$_TIMEOUT) CALL NO_RESPONSE ()
 
      ELSE
 
        CALL LIB$SIGNAL (%VAL (STATUS))
 
      END IF
 
 
           Задать тайм-аут  можно  и  в  операции  чтения  QIO  с
 
      помощью модификации IO$M_TIMED, задав количество  секунд  в
 
      параметре P3. Сообщение, выведенное на терминал, сбрасывает
 
      таймер, установленный для операции  чтения  (независимо  от
 
      того, была операция  инициализирована  с  помощью  QIO  или
 
      SMG).
 
           Описанные выше операции чтения с  тайм-аутом  работают
 
      на основе посимвольного ввода. Чтобы установить время ввода
 
      записи (тайм-аут для ввода строки)  вместо  ввода  символа,
 
      придется   использовать   системную   служебную   процедуру
 
      SYS$SETIMR. Процедура SYS$SETIMR исполняет подпрограмму AST
 
      в заданное время. Заданное время  -  это  будет  предельное
 
      время ответа; программа  AST  OTMEняет  любую  просроченную
 
      операцию  ввода-вывода  на  канале,  назначенном  терминалу
 
      пользователя.
 


                                - 191 -
 
                          00152-01 97 09-2
 
 
           3.3.6. Преобразование нижнего регистра в верхний
 
 
           Можно автоматически переводить пользовательский ввод в
 
      верхний регистр (то есть все символы нижнего регистра, вве-
 
      денные пользователем, будут  автоматически  переводиться  в
 
      соответствующие символы верхнего регистра). Это делается  с
 
      помощью библиотечной процедуры SMG$READ_STRING с  модифика-
 
      цией (параметр 5) TRM$M_TM_CVTLOW, например:
 
 
      INTEGER*4     SMG$CREATE_VIRTUAL_KEYBOARD,
 
      2             SMG$READ_STRING,
 
      2             STATUS,
 
      2             VKID,          !ID виртуальной клавиатуры
 
      2             INPUT_SIZE
 
      CHARACTER*512 INPUT
 
      INCLUDE       '($TRMDEF)'
 
      STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID, !ID клавиатуры
 
      2        'SYS$INPUT')             !устройство ввода-вывода
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$READ_STRING (VKID,             !ID клавиатуры
 
      2                          INPUT,
 
      2                          'PROMPT>',
 
      2                          512,
 
      2                          TRM$M_TM_CVTLOW,
 
      2                          ,,
 
      2                          INPUT_SIZE)
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
           Можно также преобразовывать символы  нижнего  регистра
 
      операцией чтения QIO с модификацией IO$M_CVTLOW (определена
 
      в $IODEF).
 


                                - 192 -
 
                          00152-01 97 09-2
 
 
           3.3.7. Редактирование строки и управляющие действия
 
 
           Пользователь имеет возможность редактировать  вводимую
 
      информацию так, как описано в [4]. Подавить  редактирование
 
      строки при операции чтения можно, читая с терминала  проце-
 
      дурой   SMG$READ_STRING   с   модификацией   (параметр   5)
 
      TRM$M_TM_NOFILTR, например:
 
 
      INTEGER*4      SMG$CREATE_VIRTUAL_KEYBOARD,
 
      2              SMG$READ_STRING,
 
      2              STATUS,
 
      2              VKID,            !ID виртуальной клавиатуры
 
      2              INPUT_SIZE
 
      CHARACTER*512 INPUT,
 
      INCLUDE        '($TRMDEF)'
 
      STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID, !ID клавиатуры
 
      2        'SYS$INPUT')             !устройство ввода-вывода
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$READ_STRING (VKITD,            !ID клавиатуры
 
      2                         INPUT,            !ввод данных
 
      2                         'PROMPT>',
 
      2                         512,
 
      2                         TRM$M_TM_NOFILTR,
 
      2                         ,,
 
      2                         INPUT_SIZE)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
           Подавить редактирование строки можно также и в  опера-
 
      ции чтения QIO с помощью модификации IO$M_NOFLTR (определе-
 
      на в $IODEF).
 


                                - 193 -
 
                          00152-01 97 09-2
 
 
           3.3.8. Рассылка сообщений
 
 
           Можно писать (посылать сообщения) на любой интерактив-
 
      ный  терминал  с  помощью  системной  служебной   процедуры
 
      SYS$BRKTHRU. Например, фрагмент программы рассылает сообще-
 
      ние на все терминалы, на которых в данный момент есть заре-
 
      гистрированный пользователь; использование SYS$BRKTHRU  для
 
      вывода на терминал, закрепленным за другим процессом,  тре-
 
      бует привилегии OPER:
 
 
      INTEGER*4 STATUS,
 
      2         SYS$BRKTHRUW
 
      INTEGER*2 B_STATUS (4)
 
      INCLUDE   '($BRKDEF)'
 
      STATUS = SYS$BRKTHRUW (,
 
      2                      'ACCOUNTING SYSTEM STARTED',,
 
      2                      %VAL (BRK$S_ALLUSERS),
 
      2                      B_STATUS,,,,,,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
 
           Если пользователь не  предпринимает  никаких  действий
 
      для обработки  присылаемого  сообщения,  то  это  сообщение
 
      пишется на экран с текущего места (после возврата каретки и
 
      перевода строки). Если в этот момент идет операция  записи,
 
      то выдача сообщения произойдет по ее окончанию. Если в этот
 
      момент идет операция чтения, то выдача сообщения произойдет
 
      немедленно; после сообщения весь отображенный ввод  пользо-
 
      вателя в прерванной операции чтения будет повторен на экра-
 
      не (как при нажатии CTRL/R).
 
           Сообщения, присылаемые на терминал, с которым работает
 
      программа, можно обрабатывать с помощью библиотечной проце-
 


                                - 194 -
 
                          00152-01 97 09-2
 
 
      дуры SMG$ENABLE_BROADCAST_TRAPPING. Эта  процедура  исполь-
 
      зует механизм AST для передачи управления  нужной  подпрог-
 
      рамме каждый раз, когда на  текущий  терминал  по  рассылке
 
      попадает сообщение; после выполнения подпрограммы  управле-
 
      ние возвращается основной программе в ту точку,  в  которой
 
      она была прервана.
 
           Процедура  SMG$ENABLE_BROADCAST_TRAPPING  не  является
 
      процедурой типа SMG.  Перед  вызовом  SMG$ENABLE_BROADCAST_
 
      TRAPPING необходимо  вызвать  SMG$CREATE_PASTEBOARD,  чтобы
 
      связать макет с терминалом.  SMG$CREATE_RASTEBOARD  возвра-
 
      щает идентификационный номер макета; этот номер  передается
 
      в SMG$ENABLE_BROADCAST_TRAPPING для идентификации  термина-
 
      ла, о котором  идет  речь.  Читать  содержание  присланного
 
      сообщения можно с помощью  библиотечной  процедуры  SMG$GET
 
      BROADCAST_MESSAGE.
 
           Пример.
 
           Перехват сообщения и вывод его в нижней части экрана:
 
      INTEGER*4 STATUS,
 
      2         PBID,                       !ID макета
 
      2         VDID,                       !ID виртуальной
 
                                            !экспозиции
 
      2         SMG$CREATE_PASTEBOARD,
 
      2         SMG$ENABLE_BROADCAST_TRAPPING,
 
      2         SMG$PASTE_VIRTUAL_DISPLAY
 
      COMMON    /ID/ PBID,
 
      2              VDID
 
      INTEGER*2 B_STATUS(4)
 
      INCLUDE   '($SMGDEF)'
 
      INCLUDE   '($BRKDEF)'
 
      EXTERNAL  BRKTHRU_ROUTINE


                                - 195 -
 
                          00152-01 97 09-2
 
 
      STATUS = SMG$CREATE_PASTEBOARD (PBID)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$CREATE_VIRTUAL_DISPLAY (3,     !высота
 
      2                                    80,    !ширина
 
      2                                    VDID,, !ID экспозиции
 
      2                                    SMG$M_REVERSE)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SMG$ENABLE_BROADCAST_TRAPPING (PBID, ! ID макета
 
      2                                      BRKTHRU_ROUTINE)!AST
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! подпрограмма обработки прерывания
 
      SUBROUTINE BRKTHRU ROUTINE ()
 
      INTEGER*4 STATUS,
 
      2         PBID,               !ID макета
 
      2         VDID,               !ID виртуальной экспозиции
 
      2         SMG$GET_BROADCAST_MESSAGE,
 
      2         SMG$PUT_CHARS,
 
      2         SMG$PASTE_VIRTUAL_DISPLAY
 
      COMMON    /ID/ PBID,
 
      2              VDID
 
      CHARACTER*240 MESAGE
 
      INTEGER*2     MESSAGE_SIZE
 
      ! читать сообщение
 
      STATUS = SMG$GET_BROADCAST_MESSAGE (PBID,
 
      2                                   MESSAGE,
 
      2                                   MESSAGE_SIZE)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! писать сообщение в виртуальную экспозицию
 
      STATUS = SMG$PUT_CHARS (VDID,
 
      2                       MESSAGE (1:MESSAGE_SIZE),


                                - 196 -
 
                          00152-01 97 09-2
 
 
      2                       1,                      !строка
 
      2                       1)                      !столбец
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! визуализация экспозиции путем вставки ее в макет
 
      STATUS = SMG$PASTE_VIRTUAL_DISPLAY (VDID,
 
      2                                   PBID,
 
      2                                   22,         !строка
 
      2                                   1)          !столбец
 
      END
 
 
 
           3.4. Системные служебные процедуры SYS$QIO и SYS$QIOW
 
 
           Системные служебные  процедуры  QIO  разрешают  непос-
 
      редственное взаимодействие с системным драйвером терминала.
 
      Процедуры QIO делают возможными операции, которые не  могут
 
      быть выполнены операторами ввода-вывода языка  фортран  или
 
      процедурами из библиотеки исполнительных  процедур,  умень-
 
      шают расход ресурса и  позволяют  осуществлять  асинхронный
 
      ввод-вывод. Однако, для облегчения программирования  и  для
 
      обеспечения независимости от устройства, использовать  дан-
 
      ный набор QIO не рекомендуется.
 
           Для выполнения операций с терминалом по вводу-выводу с
 
      помощью системных служебных процедур SYS$QIO или  SYS$QIOW,
 
      необходимо вначале связать имя терминала с  каналом  ввода-
 
      вывода системной служебной процедурой SYS$ASSIGN,  а  затем
 
      использовать этот канал  в  системной  служебной  процедуре
 
      SYS$QIO или SYS$QIOW. Номер канала ввода-вывода должен быть
 
      присвоен переменной типа INTEGER*2. Для чтения с  SYS$INPUT
 
      или записи на SYS$OUTPUT нужно задать соответствующее логи-
 
      ческое имя как имя терминала в системной служебной процеду-
 


                                - 197 -
 
                          00152-01 97 09-2
 
 
      ре SYS$ASSIGN. SYS$QIO применяется для  асинхронных  опера-
 
      ций, а SYS$OIOW - для всех прочих.
 
 
 
           3.4.1. Операции чтения
 
 
           Системная служебная  процедура  SYS$QIO  или  SYS$QIOW
 
      присваивает получаемую запись данных с терминала  указанной
 
      переменной. Для  получения  данных  из  файла  или  другого
 
      нетерминального устройства эти процедуры не годятся.
 
           Для синхронного ввода (программа приостанавливается до
 
      завершения ввода) используется SYS$QIOW. Задаются следующие
 
      параметры:
 
           1) канал ввода (параметр 2). Используется канал, возв-
 
      ращенный процедурой SYS$ASSING. Этот параметр  должен  быть
 
      словом, передаваемым по значению (%VAL);
 
           2) код функции и модификаторы (параметр 3). Код  функ-
 
      ции задается как IO$READPROMPT для  выдачи  подсказки,  или
 
      IO$_READVBLK для чтения записи данных без подсказки.  Любые
 
      модификаторы (например,  для  специального  форматирования)
 
      комбинируются с кодом функции с помощью логического  сложе-
 
      ния .OR.. Например, для подавления  отображения  на  экране
 
      вводимых при чтении символов третий параметр  задается  как
 
      IO$_READVBLK .OR. IO$M_NOECHO. Идентификаторы определены  в
 
      $IODEF. Параметр должен передаваться по значению (%VAL);
 
           3) блок состояния (параметр 4). Определяется блок сос-
 
      тояния из четырех слов: состояние возврата, смещение  огра-
 
      ничителя строки в буфере ввода,  значение  первого  символа
 
      ограничителя строки и размер  ограничителя  строки.  Полный
 
      ограничитель строки помещается в буфер  ввода  сразу  после
 
      введенных данных. Состояние возврата в блоке состояния есть
 


                                - 198 -
 
                          00152-01 97 09-2
 
 
      окончательное состояние операции ввода-вывода, это  состоя-
 
      ние недоступно до завершения операции.  Состояние,  возвра-
 
      щаемое  как  значение  функции   процедурой   SYS$QIO   или
 
      SYS$QIOW, есть окончательное  состояние  вызова  процедуры;
 
      это значение состояния становиться  доступным  после  того,
 
      как процедура вернет управление  вызвавшей  программе.  Эти
 
      два состояния различны, одинаково существенны и оба  должны
 
      проверяться;
 
           4) буфер ввода (параметр 7, или P1). Буфер ввода опре-
 
      деляется  как  символьная  переменная,  но   передается   в
 
      SYS$QIOW по ссылке (%REF);
 
           5) размер буфера ввода (параметр 8,  или  P2).  Задает
 
      число символов, определенных для  буфера  ввода.  Например,
 
      если INPUT задано как CHARACTER*132, то можно задать INPUT_
 
      BUFF_SIZE значение 132. Этот параметр  должен  передаваться
 
      по значению (%VAL);
 
           6) буфер подсказки (параметр 11, или  P5).  Необходимо
 
      определить подсказку (если она  оговорена)  как  символьный
 
      литерал или переменную, но  передавать  ее  в  SYS$QIOW  по
 
      ссылке (%REF);
 
           7) размер буфера  подсказки  (параметр  12,  или  P6).
 
      Задать число символов, отведенных на  подсказку  (если  она
 
      есть).  Этот  параметр  должен  передаваться  по   значению
 
      (%VAL).
 
           Системная служебная процедура SYS$QIOW помещает прочи-
 
      танные данные в переменную, переданную как P1. Второе слово
 
      блока состояния содержит смещение терминатора (ограничителя
 
      строки) от начала буфера - это слово содержит число  симво-
 
      лов прочитанных данных. На полученные данные ссылаться нуж-
 
      но как на подстроку,  используя  смещение  терминатора  как


                                - 199 -
 
                          00152-01 97 09-2
 
 
      позицию последнего символа (то  есть  величину  подстроки).
 
      При ссылке на полную строку в состав данных будет включать-
 
      ся терминатор (например, символ CR) и  символы,  оставшиеся
 
      от предыдущих операций в том же буфере (единственное исклю-
 
      чение - это намеренное переполнение буфера с целью  прекра-
 
      щения операции ввода).
 
           Пример.
 
           Происходит чтение строки данных с терминала и ожидание
 
      завершения операции ввода:
 
      INTEGER STATUS
 
      ! структуры QIOW
 
      INTEGER*2 INPUT_CHAN        !канал ввода-вывода
 
      INTEGER CODE,               !тип операции ввода-вывода
 
      2       INPUT_BUFF_SIZE,    !размер буфера ввода
 
      2       PROMPT_SIZE,        !размер подсказки
 
      2       INPUT_SIZE          !размер всей вводимой строки
 
      PARAMETER (PROMPT_SIZE = 13,
 
      2          INPUT_BUFF_SIZE = 132)
 
      CHARACTER*132 INPUT
 
      CHARACTER*(*) PROMPT
 
      PARAMETER (PROMPT = 'INPUT VALUE:')
 
      ! DEFINE SYMBOLS USED IN I/O OPERATIONS
 
      INCLUDE '($IODEF)'
 
      ! STATUS BLOCK FOR QIOW
 
      STRUCTURE /IOSTAT_BLOCK/
 
      INTEGER*2 IOSTAT,        !состояние возврата
 
      2         TERM_OFFSET,   !положение терминатора строки
 
      2         TERMINATOR,    !значение терминатора
 
      2         TERM_SIZE      !размер от терминатора
 
      END STRUKTURE


                                - 200 -
 
                          00152-01 97 09-2
 
 
      RECORD /IOSTAT_BLOCK/ IOSB
 
      ! подпрограммы
 
      INTEGER*4 SYS$ASSIGN,
 
      2         SYS$QIOW
 
      ! назначить канал ввода-вывода на SYS$INPUT
 
      STATUS = SYS$ASSIGN ('SYS$INPUT',
 
      2                    INPUT_CHAN,,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! читать с подсказкой
 
      CODE = IO$READPROMPT
 
      STATUS = SYS$QIOW (,
 
      2                  %VAL (INPUT_CHAN),
 
      2                  %VAL (CODE),
 
      2                  IOSB,
 
      2                  ,,
 
      2                  %REF (INPUT),
 
      2                  %VAL (INPUT_BUFF_SIZE),
 
      2                  ,,
 
      2                  %REF (PROMPT),
 
      2                  %VAL (PROMPT_SIZE))
 
      ! проверить статус QIOW
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! проверить состояние операции ввода-вывода
 
      IF (.NOT. IOSB.IOSTAT) CALL LIB$SIGNAL (%VAL(IOSB.IOSTAT))
 
      ! установить размер введенной строки
 
      INPUT_SIZE = IOSB.TERM_OFFSET
 
 
           Для  выполнения  асинхронных   операций   ввода-вывода
 
      используется системная служебная процедура SYS$QIO с флагом
 
      события (первый параметр, который  должен  передаваться  по
 


                                - 201 -
 
                          00152-01 97 09-2
 
 
      значению). В момент выполнения операции ввода-вывода  прог-
 
      рамма может продолжить свою работу.  В  тот  момент,  когда
 
      понадобятся результаты ввода, нужно вызвать системную  про-
 
      цедуру SYS$SYNCH для проверки завершения операции по  флагу
 
      события и блоку состояния, заданных для SYS$QIO. Если ввод-
 
      вывод не  завершен,  то  программа  приостановится  до  его
 
      завершения. Таким способом можно совмещать  процессы  обра-
 
      ботки в программе. Нельзя использовать вводимые данные,  не
 
      обратившись предварительно к процедуре SYS$SYNCH.
 
           Пример.
 
           Асинхронное выполнение операции чтения строки с терми-
 
      нала:
 
      INTEGER STATUS
 
      ! структуры QIO
 
      INTEGER*2 INPUT_CHAN      !канал ввода-вывода
 
      INTEGER CODE,             !тип операции ввода-вывода
 
      2        INPUT_BUF_SIZE,  !размер буфера ввода
 
      2        PROMPT_SIZE,     !размер подсказки
 
      2        INPUT_SIZE       !размер прочитанной строки
 
      PARAMETER (INPUT_BUFF_SIZE = 132,
 
      2         PROMPT = 13)
 
      CHARACTER*132 INPUT
 
      CHARACTER*(*) PROMPT
 
      PARAMETER (PROMPT = 'INPUT VALUE: ')
 
      INCLUDE '($IODEF)'       !идентификаторы, используемые
 
                               !в оперции ввода-вывода
 
      !STATUS BLOCK FOR QIO
 
      STRUCTURE /IOSTAT_BLOCK/
 
        INTEGER*2 IOSTAT,       !состояние возврата
 
      2         TERM_OFFSET,    !положение терминатора строки


                                - 202 -
 
                          00152-01 97 09-2
 
 
      2         TERMINATOR,     !значение терминатора
 
      2         TERM_SIZE       !размер терминатора
 
      END STRUCTURE
 
      RECORD /IOSTAT_BLOCK/ IOSB
 
      !флаг события для ввода-вывода
 
      INTEGER INPUT_EF
 
      !подпрограммы
 
      !INTEGER*4 SYS$ASSIGN,
 
      2          SYS$QIO,
 
      2          SYS$SYNCH,
 
      2          LIB$GET_IF
 
      !назначить канал ввода-вывода на SYS$INPUT
 
      STATUS = SYS$ASSIGN ('SYS$INPUT',
 
      2                     INPUT_CHAN,,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL STATUS))
 
      !принять флаг события
 
      STATUS = LIB$GET_EF (INPUT_EF)
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      !чтение с подсказкой
 
      CODE = 10$_READPROMPT
 
      STATUS = SYS$QIO (%VAL (INPUT_EF),
 
      2                 %VAL (INPUT_CHAN),
 
      2                 %VAL (CODE),
 
      2                 IOSB,
 
      2                 ,,
 
      2                 %REF (INPUT),
 
      2                 %VAL (INPUT_BUFF_SIZE),
 
      2                 ,,
 
      2                 %REF (PROMPT),
 
      2                 %VAL (PROMPT_SIZE))


                                - 203 -
 
                          00152-01 97 09-2
 
 
      !проверка состояния QIO
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SYS$SYNCH (%VAL (INPUT_EF),
 
      2                    IOSB)
 
      !проверка состояния SYNCH
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      !проверка состояния операции ввода-вывода
 
      IF (.NOT. IOSB.IOSTAT) CALL LIB$SIGNAL (%VAL(IOSB.IOSTAT))
 
      !установить размер введенной строки
 
      INPUT_SIZE = IOSB.TERM_OFFSET
 
 
           Обязательна проверка состояния операции  ввода  в  том
 
      виде, в котором оно  возвращено  в  блок  состояния  ввода-
 
      вывода. В асинхронной операции можно проверить это  состоя-
 
      ние только после гарантированного завершения операции ввода
 
      (то есть после вызова процедуры SYS$SYNCH).
 
 
 
           3.4.2. Операции записи
 
 
           Служебные системные процедуры SYS$QIO и SYS$QIOW пере-
 
      мещают одну запись данных  символьного  типа  на  терминал.
 
      Использовать описанные здесь методы использования этих про-
 
      цедур для вывода в файл или  на  нетерминальное  устройство
 
      нельзя.
 
           Для синхронного вывода  (программа  приостанавливается
 
      до завершения операции) используется процедура  SYS$QIOW  с
 
      опущенным первым параметром (номер флага события). Задаются
 
      следующие параметры:
 
           1)  канал  ввода-вывода  (параметр  2).   Используется
 
      канал, возвращенный процедурой  SYS$ASSIGN.  Этот  параметр
 
      есть слово, которое передается по значению (%VAL);


                                - 204 -
 
                          00152-01 97 09-2
 
 
           2) код функции и модификаторы (параметр  3).  Задается
 
      код функции как IO$WRITEVBLK. Любые  модификаторы  комбини-
 
      руюся с кодом функции логическим сложением .OR..  Например,
 
      чтобы выполнить операцию без форматирования вывода,  третий
 
      параметр задается как IO$_WRITEVBLK.OR.IO$M_NOFORMAT. Иден-
 
      тификаторы определены в $IODEF. Параметр передается по зна-
 
      чению (%VAL);
 
           3) блок состояния (параметр 4). Необходимо  определить
 
      блок состояния из трех слов и двух байтов: состояние  возв-
 
      рата, число записанных байтов (включая любые символы управ-
 
      ления кареткой, заданные для  локальных  устройств  в  р4),
 
      число записанных строк (число переданных символов  перевода
 
      строки), номер столбца в конце передачи и  номер  строки  в
 
      конце передачи. Если используется форматирование,  то  пос-
 
      ледние два байта бесполезны;
 
           4) буфер вывода (параметр 7, или P1). Нужно определить
 
      буфер вывода как символьное значение, но передавать  его  в
 
      процедуру SYS$QIOW по ссылке (%REF);
 
           5) размер буфера вывода (параметр 8, или  P2).  Задать
 
      число символов в выводимом значении. Этот  параметр  должен
 
      передаваться по значению (%VAL);
 
           6) спецификатор управления кареткой (параметр 10,  или
 
      P4). Этот параметр можно опустить только в том случае, ког-
 
      да функция модифицирована с помощью IO$M_NOFORMAT, либо  не
 
      нужны никакие управляющие действия с кареткой после  вывода
 
      строки.  Этот  параметр  должен  передаваться  по  значению
 
      (%VAL).
 
           Пример.
 
           На терминал выводится строка символьных данных:
 


                                - 205 -
 
                          00152-01 97 09-2
 
 
      INTEGER STATUS,
 
      2       ANSWER_SIZE
 
      CHARACTER*31 ANSWER
 
      INTEGER*2 OUT_CHAN
 
      ! блок состояния для QIO
 
      STRUCTURE /IOSTAT_BLOCK/
 
        INTEGER*2 IOSTAT,
 
      2         BYTE_COUNT,
 
      2         LINES_OUTPUT
 
        BYTE      COLUMN,
 
      2         LINE
 
      RECORD /IOSTAT_BLOCK/ IOSB
 
      ! подпрограммы
 
      INTEGER SYS$ASSIGN,
 
      2       SYS$QIOW
 
      ! определение идентификаторов IO$
 
      INCLUDE '($IODEF)'
 
      STATUS = SYS$ASSIGN ('SYS$OUTPUT',
 
      2                    OUT_CHAN,,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      STATUS = SYS$QIOW (,
 
      2                  %VAL (OUT_CHAN),
 
      2                  %VAL (IO$_WRITEVBLK),
 
      2                  IOSB,
 
      2                  ,
 
      2                  ,
 
      2                  %REF ('ANSWER:'//ANSWER(1:ANSWER_SIZE)),
 
      2                  %VAL (8+ANSWER_SIZE),
 
      2                  ,
 
      2                  %VAL (32),,) !одиночный межстрочный


                                - 206 -
 
                          00152-01 97 09-2
 
 
                                      !промежуток
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      IF (.NOT. IOSB.IOSTAT) THEN CALL LIB$SIGNAL (%VAL (IOSB.
 
                                                   IOSTAT))
 
 
 
           3.4.3. Проверка типа устройства
 
 
           Операции QIO используются для работы  с  терминальными
 
      устройствами. Если перенаправить SYS$INPUT  или  SYS$OUTPUT
 
      на файл или на нетерминальное устройство, то при  использо-
 
      вании QIO произойдет ошибка. Необходимо  убедиться  в  том,
 
      что логическое устройство связано с терминалом;  для  этого
 
      можно  воспользоваться   системной   служебной   процедурой
 
      SYS$GETDVIW.  Процедура  SYS$GETDVIW  возвращает  состояние
 
      SS$IVDEVNAM, если логическое  имя  не  соответствует  имени
 
      устройства,например, определено как файл или  другим  обра-
 
      зом.  Тип  устройства  есть  ответ,  связанный  с  запросом
 
      DVI$DEVCLASS, и для терминала он должен быть DC$_TERM.
 
           Пример.
 
      ! структуры GETDVI
 
      STRUCTURE /ITMLST/
 
        INTEGER*2 BUFLEN,       !длина буфера ответа
 
      2        CODE             !запрос о классе устройства
 
        INTEGER*4 BUFADR,       !адрес буфера ответа
 
      2         RETLENADR,      !адрес длины ответа
 
      2         END_LIST /O/    !длинное слово конца списка
 
      END STRUCTURE
 
      RECORD /ITMLST/ DVI_LIST
 
      ! GETDVI BUFFERS
 
      INTEGER CLASS,            !буфер ответа
 


                                - 207 -
 
                          00152-01 97 09-2
 
 
      2       CLASS_LEN         !длина ответа
 
      ! GETDVI SYMBOLS
 
      INCLUDE '($DCDEF)'
 
      INCLUDE '($SSDEF)'
 
      INCLUDE '($DVIDEF)'
 
      EXTERNAL DVI$_DEVCLASS
 
      ! определение программ
 
      INTEGER SYS$GETDVIW
 
      ! определить класс устройства SYS$INPUT
 
      DVI_LIST.BUFLEN = 4
 
      DVI_LIST.CODE = DVI$_DEVCLASS
 
      DVI_LIST.BUFARD = %LOG (CLASS)
 
      DVI_LIST.RETLENADR = %LOG (CLASS_LEN)
 
      STATUS =  SYS$GETDVIW (,,'SYS$INPUT',
 
      2                      DVI_LIST,,,,,)
 
      IF ((.NOT. STATUS) .AND.
 
          (STATUS .NE. SS$_IVDEVNAM)) THEN
 
        CALL LIB$SIGNAL (%VAL (STATUS))
 
      END IF
 
      ! убедиться, что устройство есть терминал
 
      IF ((STATUS .NE. SS$_IVDEVNAM) .AND.
 
          (CLASS .EQ. DC$_TERM)) THEN
 
      ELSE
 
        TYPE *, 'выходное устройство на терминал'
 
      END IF
 


                                - 208 -
 
                          00152-01 97 09-2
 
 
           3.4.4. Характеристики терминала
 
 
           Характеристики  терминала  приведены  в  [6].   Анализ
 
      характеристик возможен системной служебной процедурой QIO с
 
      кодом функции IO$SENSEMODE;  анализируется  соответствующий
 
      бит в структуре, возвращенной в р1. Для изменения  характе-
 
      ристики необходимо:
 
           1) обратиться к системной служебной  процедуре  QIO  с
 
      кодом функции IO$_SENSEMODE;
 
           2) установить или сбросить  нужный  бит  в  структуре,
 
      возвращенной в P1;
 
           3) обратиться к системной служебной  процедуре  QIO  с
 
      кодом функции IO$SETMODE, передав в качестве р1  полученную
 
      модифицированную структуру.
 
           Пример.
 
           Снимается характеристика терминала HOSTSYNC:
 
      INTEGER*4 STATUS
 
      ! канал ввода-вывода
 
      INTEGER*2 INPUT_CHAN
 
      ! блок состояния ввода-вывода
 
      STRUCTURE /IOSTAT_BLOCK/
 
        INTEGER*2 IOSTAT
 
        BYTE      TRANSMIT,
 
      2         RECEIVE,
 
      2         CRFILL,
 
      2         LFFILL,
 
      2         PARITY,
 
      2         ZERO
 
      END STRUCTURE
 
      RECORD /IOSTAT_BLOCK/ IOSB
 


                                - 209 -
 
                          00152-01 97 09-2
 
 
      ! буфер для характеристик
 
      ! замечание: основные характеристики в первых трех
 
      ! байтах второго длинного слова - длина в четвертом байте
 
      STRUCTURE /CHARACTERISTICS/
 
        BYTE         CLASS,
 
      2            TYPE
 
        INTEGER*2    WIDTH
 
        UNION
 
         MAP
 
          INTEGER*4    BASIC
 
         END MAP
 
         MAP
 
          BYTE LENGTH(4)
 
         END MAP
 
        END UNION
 
        INTEGER*4 EXTENDED
 
      END STRUCTURE
 
      RECORD /CHARACTERISTICS/ CHARBUF
 
      ! определить идентификаторы для операций ввода-вывода
 
      ! и терминальных операций
 
      INCLUDE '($IODEF)'
 
      INCLUDE '($TTDEF)'
 
      ! подпрограммы
 
      INTEGER*4 SYS$ASSING,
 
      2         SYS$QIOW
 
      ! назначить канал для терминала
 
      STATUS = SYS$ASSNG ('SYS$INPUT',
 
      2                   INPUT_CHAN,,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! принять текущие характеристики


                                - 210 -
 
                          00152-01 97 09-2
 
 
      STATUS = SYS$QIOW (,
 
      2                  %VAL (INPUT_CHAN),
 
      2                  %VAL (IO$SENSEMODE),
 
      2                  IOSB,,,
 
      2                  CHARBUF,           !буфер
 
      2                  %VAL (12),,,,)     !размер буфера
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      IF (.NOT. IOSB.IOSTAT) CALL LIB$SIGNAL (%VAL(IOSB.IOSTAT))
 
      ! отключение синхронизации
 
      CHARBUF.BASIC = IBCLR (CHARBUF.BASIC, TT$V_HOSTSYNC)
 
      ! установить новые характеристики
 
      STATUS = SYS$QIOW (,
 
      2                  %VAL (INPUT_CHAN),
 
      2                  %VAL (IO$_SETMODE),
 
      2                  IOSB,,,
 
      2                  CHARBUF,
 
      2                  %VAL (12),,,,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      IF (.NOT. IOSB.IOSTAT) CALL LIB$SIGNAL (%VAL(IOSB.IOSTAT))
 
      END
 
           Если модифицируются характеристики терминала с помощью
 
      операций установки режима QIO, то следует  сохранять  буфер
 
      характеристик получаемый при первой операции анализа  режи-
 
      ма, и восстанавливать эти характеристики перед выходом  (не
 
      будет необходимости делать сброс, если при каждой  операции
 
      чтения использовать модификаторы). Для гарантии  выполнения
 
      восстановления при аварийном прекращении  работы  программы
 
      (например, если пользователь нажмет CTRL/Y нужно  восстано-
 
      вить пользовательскую среду в программе обработки  прерыва-
 
      ния).


                                - 211 -
 
                          00152-01 97 09-2
 
 
           3.5. Терминаторы (ограничители) записей
 
 
           Операция чтения QIO заканчивается,  если  пользователь
 
      вводит терминатор или если буфер заполнен - по  первому  из
 
      этих событий. Применяется стандартный  набор  терминаторов,
 
      если не был задан параметр P4 в операции чтения QIO.  Можно
 
      проанализировать терминатор,  завершивший  операцию  ввода,
 
      просмотрев буфер ввода,  начиная  со  смещения  терминатора
 
      (второе слово в блоке состояния ввода-вывода). Длина терми-
 
      натора (в байтах) задана в старшем  слове  блока  состояния
 
      ввода-вывода. Третье слово блока состояния содержит  значе-
 
      ние первого символа терминатора.
 
           Проверка терминатора позволяет читать с терминала пос-
 
      ледовательность AP2  (ESCAPE  -  последовательность),  если
 
      использовать в операции чтения модификатор IO$M_ESCAPE (или
 
      если установлена характеристика терминала  ESCAPE).  Первый
 
      символ терминатора будет символ ESC  (значение  27  КОИ-8).
 
      Оставшиеся символы будут содержать значения  последователь-
 
      ности AP2.
 
           Необходимо проверять терминатор и в  поисках  признака
 
      конца строки (CTRL/Z) при вводе с терминала. На уровне  QIO
 
      не генерируется никакого условия ошибки. Если  пользователь
 
      введет CTRL/Z, то терминатором будет символ  SUB  (значение
 
      КОИ-8 - десятичное 26).
 
 
 
 
 
 
 
 
 
 
 
 
 


                                - 212 -
 
                          00152-01 97 09-2
 
 
                     Перечень ссылочных документов
 
 
 
      1. Операционная система МОС ВП.  Подсистема  исполнительной
 
         библиотеки. Руководство программиста. 00152-01 33 09.
 
      2. Операционная система МОС ВП. Подсистема системного  сер-
 
         виса. Интерфейсные программы. Руководство  программиста.
 
         00152-01 33 02.
 
      3. Операционная система МОС ВП. Подсистема управления. Диа-
 
         логовый командный язык. Руководство пользователя.
 
         00151-01 92 19.
                      
      4. Операционная система МОС ВП. Подсистема системного  сер-
 
         виса. Программы системного обслуживания. Справочный  ма-
 
         териал. 00152-01 97 06.
 
      5. Операционная система МОС ВП. Подсистема системного  сер-
 
         виса. Управление  вводом-выводом.  Руководство  програм-
 
         миста. 00152-01 33 03.
 
      6. Операционная система МОС ВП. Подсистема разработки прог-
 
         рамм. Программа сортировки и слияния. Руководство прог-
 
         раммиста. 00152-01 33 06.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!