CCCCCCCC        III      III
                      CCCCCCCCCC       III      III
                     CCC      CCC      III      III
                     CCC       CC      III      III
                     CCC               III     IIII
                     CCC               III    IIIII
                     CCC               III   IIIIII
                     CCC               III  III III
                     CCC               III III  III
                     CCC               IIIIII   III   
                     CCC               IIIII    III
                     CCC       CC      IIII     III
                     CCC      CCC      III      III
                      CCCCCCCCCC       III      III
                       CCCCCCCC        III      III





















                              ЛГУ





                              - 1 -


                           СОДЕРЖАНИЕ


1. Язык СИ. Руководство программиста.  Функции  стандартной
   библиотеки........................................................ 2
    1.1. Форматный ввод/вывод........................................ 8
    1.2. Нефайловая работа с терминалом..............................13
    1.3. Управление распределением динамической памяти...............14
    1.4. Работа со строками и символами..............................16
    1.5. Средства отладки............................................23

2. Библиотека математических функций CMLIB.OBJ.......................26

3. Графбиблиотека CGLIB.OBJ..........................................28

4. Библиотека системных функций ОС RT11 CRLIB.OBJ....................30

5. Язык СИ. Руководство оператора....................................32
    5.1. Стадии подготовки программы.................................32
    5.2. Проблемы промежуточных файлов...............................34
    5.3. Эмуляция EIS................................................35
    5.4. Вычисление с плавающей запятой..............................35
    5.5. Инициализация FPU...........................................36
    5.6. Форматный вывод чисел с плавающей запятой...................36
    5.7. Оптимизация.................................................36
    5.8. Компоновка..................................................37
    5.9. Создание автономных программ................................37
    5.10.Сообщения оператору.........................................38

6. Дополнения к комплекту СИ.........................................39
    6.1. Ввод и обработка командной строки...........................41
    6.2. Управление стартовыми действиями............................42

7. Пакет работы с терминалом.........................................43
    7.1. Обновление экрана...........................................43
    7.2. Запуск......................................................45
    7.3. Вывод.......................................................45
    7.4. Ввод........................................................45
    7.5. Завершение работы...........................................45
    7.6. Оптимизация движения курсора................................46

8. Графический редактор..............................................52

9. Краткое описание компилятора СИ фирмы DEC.........................53

10.Перечень ошибок...................................................54

















                              - 2 -


       1. ЯЗЫК СИ. РУКОВОДСТВО ПРОГРАММИСТА. 
            ФУНКЦИИ СТАНДАРТНОЙ БИБЛИОТЕКИ

    Многие  обьекты,  которые  встречаются  в   настоящем   описании,
используются, в основном,  через  указатели  на  них,  поэтому  часто
указатели на обьект при описании отождествляются  с  самим  обьектом.
Файл, например, определяется дескриптором  файла  -  структурой  типа
"FILE"  (тип  описан  в  файле  "STDIO.H"),  однако,  т.к.  на   файл
ссылаются при помощи указателя на его дескриптор, этот указатель  при
описании  отождествляют  с  файлом.  Это,  как  правило,  не  создает
неоднозначностей.
    При использовании данного  пакета  ввода/вывода  программа  может
открыть до 16. файлов. Вся информация об открытом файле содержится  в
так называемом дескрипторе файла - структуре типа FILE. Этот  тип,  а
также некоторые другие  переменные  и  определения  описаны  в  файле
"STDIO.H", который  должен  быть  включен  в  начало  каждого  файла,
содержащего функции данного пакета. Если существует описание
        FILE *iop; /* указатель на дескриптор файла */
,то выражение "файл iop" в дальнейшем описании будет означать  "файл,
на дескриптор которого указывает iop". В файле  "STDIO.H"  определены
символы "NULL" (0) и EOF  (-1).  Эти  символы  определяют  выделенные
значения, возвращаемые некоторыми функциям для информации  об  ошибке
или нестандартном событии.
    При  старте  программы,   написанной   на   "СИ",   автоматически
открываются (если это явно не подавлено) три файла:
        stdin - файл стандартного ввода
        stdout - Файл стандартного вывода
        stderr - файл стандартного вывода ошибок
эти   файлы   по   умолчанию   связываются   с   терминалом,   однако
соответствующими аргументами  в  командной  строке  файлы  "STDIN"  и
"STDOUT" могут быть перенаправлены на другие устройства и файлы  (см.
"руководство пограммиста. библиотеки. Исполняющая система").
    fopen
        char *name; /* спецификация файла */
        char *mode; /* способ открытия */
        file *
        fopen(name,mode);
открывает новый или существуюший файл.  Спецификация  файла  задается
в строке "name" в формате CSI. Если в  имени  файла  отсутствует  имя
устройства, то  предполагается  "DK:",  если  отсутствует  длина,  то
предполагается  "[0]"  (для  файлов,  открываемых  на  чтение,  длина
игнорируется). Если устройство  нефайловое,например  "TT:",  "LP:"  и
т.п., то  можно  опустить  имя  и  тип  файла.  Отметим,  что  пустая
спецификация файла не  расширится  в  "DK:",  а  будет  зафиксирована
ошибка. примеры допустимых имен:
        RK1:MAMA.FOR
        RK1:MAMA.FOR
        AMA.FOR
        DX0:PROG10.C[12]
        KISS.TXT
        LP:
способ  открытия  файлов  определяется  аргументом  "mode",   который
представляет способ открытия файлов определяется  аргументом  "mode",
который представляет собой строку,  содержащую  некоторую  комбинацию
сдедующих ключей:
    R - открытие существующего файла на ввод.
    W - открытие нового файла на вывод.
    N - открытие файла в "двоичном" (не символьном) режиме:
в ОС "UNIX" в символьных файлах строка ограничивается  символом  '\N'
(<ПС>), символ '\R' (<ВК>) отсутствует.
В ОС RT11  ограничителем  строки  является  комбинация  "\R\N".  т.к.
наиболее часто используется ввод и вывод  именно  символьных  файлов,
для совместимости по умолчанию файл открывается  таким  образом,  что
                              - 3 -


при вводе '\R' выбрасываются, а при выводе  вставляются  перед  '\N'.
однако, при чтении не символьного (двоичного) файла этот режим  может
приводить к ошибкам, и следует открывать файл с ключом 'N'.
    U - производить посимвольный вывод 
имеет смысл только для файлов,  открытых  на  терминал  (в  противном
случае  игнорируется);  обычно  вывод  на  терминал   выполняется   с
буферизацией и по .PRINT, при открытии же файла с  ключом  'U'  вывод
будет выполняться посимвольно (без буферизации) по  .TTYOUT  (ввод  с
терминала всегда осуществляется по .TTYIN, и для посимвольного  ввода
необходимо воспользоваться функцией $ttymode()). Заметим, что  stderr
открыт с ключом 'U'.
    D - позволяет выполнить нефайловое открытие файлового устройства:
без этого ключа при попытке выполнить "fopen("RK1:","R");" мы получим
ошибку,   однако   "fopen("RK1:","RD");"   выполнится   без   ошибки.
Нефайловое открытие устройства  с  файловой  структурой  полезно  при
создании программ, работающих непосредственно с каталогом (примеры  -
fwild() и rdir() из CRLIB) или программ, предназначенных  для  работы
с дисками, на котрых отсутствует файловая  система  ОС  RT11.  Ошибка
будет возникать, если ключ 'D' используется с  ключом  'W',  а  также
если   устройство,   к   которому   происходит    обращение,    имеет
нестандартную файловую структуру (например "MT:").

Один из ключей 'R' или 'W'  должен  присутствовать  (но  не  оба!!!),
остальные ключи могут присутствовать или отсутствовать.

    При удачном открытии fopen() резервирует  (по  malloc())  область
для дескриптора  файла  и  буфера  (около  530.  байт)  и  возвращает
указатель на нее. При  неудачном  открытии  fopen()  возвращает  NULL
(0).

    При  открытии  на  чтение  существующего   файла   в   глобальную
переменную  "$$FLEN"  заносится  его  длина  (0   для   устройств   с
нефайловой структурой).

    Если  драйвер  устройства,  указанного  при  открытии  файла,  не
загружен, то fopen() загружает его, резервируя  память  по  malloc().
Драйвер остается загруженным, а память под него зарезервированной  до
окончания задачи.

    Один и тот же файл не может быть одновременно  открыт  на  запись
и на чтение. При  работе  с  временными  файлами  после  записи  файл
должен быть закрыт, а  потом  открыт  снова,  уже  на  чтение.  Можно
использовать также функцию rewind(), которая для файлов, открытых  на
запись, делает именно это.

    Ограниченные возможности непосредственного доступа  к  записям  в
файле предоставляются функциями ftell() - fseek().

    Приведем пример использования функции fopen() на примере  простой
программы, копирующей поизвольный файл "a.txt" в файл "b.txt".

    #include 
    char inname[]="A.TXT";  /* имя входного файла */
    char outname[]="B.TXT"; /* имя выходного файла */
    main()
    {
        register c;
        file *inf;      /* файл ввода */
        file *outf;     /* файл вывода */
        if((inf=fopen(inname,"RN")) == null)
                error("ошибка открытия файла ввода \"%s\",
                                                inname);

                              - 4 -


        if((outf=fopen(outname,"WN")) == null)
                error("ошибка открытия файла вывода \"%s\",
                                                outfname);
    /* копирование */
        while((c = getc(inf)) != eof) putc(c);
    }
    freopen
        char    *name;  /* имя файла */
        char   *mode; /* способ открытия */
        file   *iop;
        file *
        freopen(name,mode,iop);
    freopen() аналогична  fopen(),  но  использует  уже  существующий
дескриптор файла, на который указывает iop. Файл, ранее  связанный  с
iop, закрывается. Если произошла ошибка  открытия  файла,  дескриптор
файла освобождается. Ошибки закрытия файла игнорируются.
    freopen() нормально возвращает "iop", и NULL при ошибке.

    fclose
        file    *iop;   /* указатель на дескриптор файла */
        fclose(iop);
    Закрывает файл iop и освобождает память, занятую под  дескриптор.
возвращает 0, если ошибок нет, и -1 при ошибке. Заметим,  что  stderr
закрыть  нельзя.  Последний  блок  файла,  открытого  для  записи  на
устройстве с произвольным доступом (диске) дополняется нулями.

    fpclose
        file *iop;
        fpclose(iop);
    Функция fpclose()  аналогична  fclose(),  однако  если  файл  был
открыт на вывод и при его  закрытии  произошла  ошибка,  то  файл  не
закрывается (т.е. теряется).

    fpurge
        file *iop;
        fpurge(iop);
    Освобождает канал,  не  закрывая  файла,  и  освобождает  память,
занятую под дескриптор. Если файл был открыт на вывод, он теряется.

    getchar, getc
        file *iop;
        getchar();
        getc(iop);
    getchar() вводит один символ из стандартного файла ввода (stdin).
getc() вводит один символ из  файла  iop.  Возвращают  EOF  (-1)  при
ошибке или при конце файла.  Если  файл  ввода  открыт  без  указания
ключа 'N' (по умолчанию stdin открывается именно так), то  при  вводе
символ '\R' () будет игнорироваться.

    putchar, putc
        char    c;
        file    *iop;
        putchar(c);
        putc(c,iop);
putchar() выводит один символ в  стандартный  файл  вывода  (stdout).
putc() выводит один символ в файл iop. Нормально возвращают  значение
символа и eof при ошибке. Если  выходной  файл  открыт  без  указания
ключа 'n',  то  при  выводе  символа  '\N'  (<ПС>)  перед  ним  будет
добавлен символ '\R' (<ВК>).

    ungetc
        char    c;
        file    *iop;
                              - 5 -


        ungetc(c,iop);
возвращает один символ в указанный файл ввода (символ  сохраняется  в
специально  отведенном  элементе  дескриптора  файла).  При   попытке
возвратить  в  файл  более  одного   символа   происходит   аварийное
Ппрерывание программы по инструкции BPT.

    getw
        file    *iop;
        getw(iop);
читает из файла  iop  два  байта  и  возвращает  их  как  одно  16-ти
разрядное слово.  Никакого  выравнивания  не  производится.  Если  вы
читаете  двоичный  файл,  его  необходимо  открыть  с   ключом   'N'.
Используйте feof() и ferror() для проверок на конец файла и ошибки.

    putw
        int     word;
        file    *iop;
        putw(word,iop);
записывает слово word в файл iop  побайтно  (сначала  младший  байт),
никакого выравнивания не производится. Файл  рекомендуется  открывать
с  ключом  'N',  иначе  возможно  искажение  информации  и  перекосы.
Возвращает 0 при удачном завершении и EOF при ошибке.

    gets, fgets, fgetss
        char    *buffer;
        int     maxbytes;
        file    *iop;
        char *
        gets(buffer);

        char *
        fgets(buffer,maxbytes,iop);

        char *
        fgets(buffer,maxbytes,iop);

gets()   - вводит строку  в  буфер  "buffer"  из  стандартного  файла
           ввода. Ограничение на длину вводимой  строки  отсутствует.
           Строкой     считается     последовательность     символов,
           заканчивающаяся символом '\N'. Символ  '\N'  заменяется  в
           буфере на '\0'.
fgets()  - вводит строку из файла iop в буфер "buffer".  Если  строка
           длиннее maxbytes-1  байт,  то  строка  обрезается  и  '\N'
           добавляется.  Строка  в буфере  всегда  заканчивается сим-
           волами '\N' '\0'.
fgetss() - делает то же, что  и  fgets(),  однако  '\N'  в  буфер  не
           заносится, строка ограничивается нулем. Это  совместимо  с
           gets()  и  fget(),  но   не   совпадает   со   стандартами
           библиотеки ОС UNIX.
Нормально возвращают buffer, при ошибке или  конце  файла  возвращают
NULL.

    puts, fputs, fputss
        char    *s;
        file    *iop;
        puts(s);

        fputs(s,iop);

        fputss(s,iop);

puts()   - записывает строку s в стандартный  файл  вывода.  В  конец
           строки добавляется '\N'.
                              - 6 -


fputs()  - записывает строку s в указаннный файл. '\N' в конец строки
           не добавляется.
fputss() - записывает строку s  в  указанный  файл.  В  конец  строки 
           добавляется '\N'. Функции возвращают EOF в  случае  ошибки
           или конца файла.

    fread
        struct object   *op;
        int     nitem;
        file    *iop;
        fread(op, sizeof(*op), nitem, iop);
вводит указанное (nitem) число двоичных записей (записи  неформатные,
функции fread() - fwrite() не имеют  никакого  отношения  к  функциям
fget() - fput()) из указанного файла в буфер,  на  который  указывает
op. Длина записи указывается во втором аргументе  (в  примере  -  как
sizeof(*op)). Возвращает  число  фактически  введенных  записей.  Оно
может быть нулем, если при вводе возникли ошибки  или  был  достигнут
конец файла.

    fwrite
        struct object   *op;
        int     nitem;
        file    *iop;
        fread(op, sizeof(*op), nitem, iop);
выводит "nitem" двоичных записей в файл iop  из  буфера,  на  который
указывает  op.  Длина  записи  указывается  во  втором  аргументе  (в
примере - как sizeof(*op)). Возвращает  число  фактически  выведенных
записей. Оно может быть нулем, если при записи  возникли  ошибки  или
был достигнут конец файла.

    fget
        char    *buffer;
        int     maxbytes;
        file    *iop;
        fget(buffer, maxbytes, iop);
вводит форматную запись (запись в формате RSX, каждой записи (строке)
предшествует счетчик  байт,  занимающий  2  байта)  из  файла  iop  в
"buffer". Если длина записи превосходит maxbytes, то  в  буфер  будет
занесено maxbytes байт, остальные байты этой записи будут потеряны.

    В ОС RT11 функцию fget() можно использовать  для  чтения  файлов,
записанных при помощи функции fput(), и для  перекодирования  файлов,
записанных в RSX. Файл должен быть открыт с ключом 'N'.

    Возвращает  число  фактически  прочитанных   байт   (может   быть
больше maxbytes). Используйте feof() для проверки на конец файла.

    fput
        char    *buffer;
        int     nbytes;
        file    *iop;
        fput(buffer,nbytes,iop);
выводит nbytes байт из буфера "buffer" в файл "iop" в виде  форматной
двоичной записи (в формате RSX). Файл должен  быть  открыт  с  ключом
'N'. Читать эти записи можно только при помощи функции fget(). Всегда
возвращает nbytes. Используйте ferror() после fput() для проверки  на
ошибки записи.

    fseek
        file    *iop;
        long    offset;
        int     param;
        fseek(iop,offset,param);
                              - 7 -


перемещает указатель текущей позиции  чтения  файла  iop  в  позицию,
указанную в offset. Возвращает 0 при нормальном завершении,  eof  при
ошибке. При нормальном завершении сбрасываются флаги ошибки  и  конца
файла  и  символ,  возвращенный  по   ungetc().   Устройство   должно
допускать непосредственный доступ (иначе ошибка),
    param должен быть равен 0 (иначе ошибка).
    param введен для совместимости с ОС UNIX, где 0 означает
абсолютное позиционирование - в ОС UNIX возможно и относительное.
    offset должен быть получен от функции ftell(). В ОС UNIX при
param=0 offset является номером байта от начала файла. В данной
реализации это не так: offset в младшем слове содержит номер блока
файла, а в старшем слове - номер байта в блоке.
    fseek не возвращает кода ошибки через $$ferr.

    ftell
        file    *iop;
        long
        ftell(iop);
возвращает текущее положение указателя  позиции  чтения/записи  файла
iop. Это положение (при  чтении)  может  быть  восстановлено  вызовом
fseek().

    rewind
        file    *iop;
        rewind(iop);
производит установку указателя чтения/записи файла на  начало  файла.
Возвращает 0 при нормальном завершении, EOF  при  ошибке.  Если  файл
открыт на запись, то файл закрывается и открывается на  чтение.  Если
при закрытии файла или при открытии  возникает  ошибка,  то  файл  не
открывается и дескриптор файла освобождается.

    delete
        char    *filename;
        delete(filename);
стирает файл, имя которого (в ASCII) указано в строке filename.  Если
драйвер устройства, на которм находится указанный файл, не  загружен,
delete() загружает его, резервируя память по  malloc(),  и  выгружает
драйвер и освобождет память после завершения операции.  Возвращает  1
йпри успешном завершении и 0 при ошибке. В последнем случае код
ошибки находится в $$ferr и errno.

    fmkdl
        file    *iop;
        fmkdl(iop);
закрывает  и  стирает  указанный  файл.  Возвращает  0  при  успешном
завершении, -1 при ошибке. fmkdl() устарела и несколько  некорректна.
используйте delete().

    fgchan
        fgchan();
находит  свободный  канал  ввода/вывода,  помечает  его  в   таблицах
исполняющей системы языка "СИ" как занятый  и  возвращает  его  номер
(>=0). Возвращает -1, если свободных каналов нет.

    frchan
        int     chan;
        frchan(chan);
освобождает канал, полученный по  fgetc(),  помечая  его  в  таблицах
исполняющей системы языка "СИ" как свободный. Возвращает:
    0 - нормальное завершение
    1 - недопустимый номер канала
    2 - канал не закрыт
    3 - канал занят не по fgchan()
                              - 8 -


    fgetname
        file    *iop;
        char    *buffer;
        fgetname(iop,buffer);
заносит  в  буфер  "buffer"  имя  файла  iop  в  ASCII  (хранится   в
дескрипторе).

    feof
        file    *iop;
        feof(iop);
возвращает 1, если в файле iop  достигнут  конец,  и  0  в  противном
случае.

    ferror
        file    *iop;
        ferror(iop);
возвращает 1, если при работе с файлом iop была зафиксирована ошибка,
и 0 в противном случае.

    flun
        file    *iop;
        flun(iop);
возвращает номер канала ввода/вывода, который связан с файлом iop.

    frec
        file    *iop;
        frec(iop);
возвращает 1, если файл iop записеориентированный, и  0  в  противном
случае.  Записеориентированными  считаются  устойства  с   нефайловой
структурой (не включая терминал), например "PC:", "LP:" и т.п.

    ftty
        file    *iop;
        ftty(iop);
возвращает 1, если файл iop связан с  терминалом,  и  0  в  противном
случае.

    gettty
        char    *buffer;
        gettty(buffer);
заносит  в  буфер  "buffer"  строку  имени  устройства   управляющего
терминала. Это актуально в ОС UNIX и RSX, в ОС RT11  gettty()  всегда
возвращает "TT:". Присутствует для совместимости.

    fclear
        file    *iop;
очищает (сбрасывает) флаг ошибки и флаг EOF в дескрипторе файла  iop.
Это может быть полезно при работе с терминалом.


            1.1. ФОРМАТНЫЙ ВВОД/ВЫВОД

    printf, fprintf, sprintf
        char    *format;
        printf(format {, arg} ...)

        char    *format;
        file    *iop;
        fprintf(iop, format {, arg} ...) 

        char    *format;
        char    *buffer;
        sprintf(buffer, format {, arg} ...) 
                              - 9 -


осуществляют форматные преобразования и вывод  указанной  информации.
printf()  помещает  вывод  в  стандартный   файл   вывода   (stdout),
fprintf() помещает вывод в указанный  файл  iop,  sprintf()  помещает
вывод в строку buffer  (при  этом  последним  символом  будет  '\0').
Каждая из этих функций преобразовывает, форматирует и  печатает  свои
аргументы ("arg", аргументы начиная  со  второго  для  printf())  под
управлением    аргумента    "format"     (первый     аргумент     для
printf())."format" представляет собой символьную  строку,  содержащую
два типа обьектов: обыкновенные символы, которые просто копируются  в
выходной поток,  и  спецификации  преобразования,  которые  управляют
преобразованием и печатью очередного аргумента printf().
    Каждая спецификация  преобразования  начинается  с  символа  "%".
Вслед за ним могут идти:
    *необязательный знак  "-",  который  указывает  на  необходимость
левого  выравнивания  информации  в  указанном  поле  (по   умолчанию
выполняется правое выравнивание);
    *необязательная цифровая строка, определяющая ширину  поля:  если
преобразованная  величина  имеет меньше символов,чем ширина поля,  то
оно  будет  заполнено  пробелами  слева  (или  справа,   если   задан
указатель левого выравнивания); если ширина поля начинается  с  нуля,
будет произведено заполнение нулями, а не пробелами;
    *необязательная точка, которая служит для разделения ширины  поля
(см. выше) и цифровой строки, определяющей точность (см. ниже);
    *необязательная  цифровая  строка,  определяющая  точность  (т.е.
число цифр после десятичной точки  для  спецификаций  формата  'e'  и
'f',  или  максимальное  число   символов   печатаемой   строки   для
спецификации формата 's');
    *символ l (малое или большое),указывающий, что следующие  за  ним
спецификации формата d, o, x  или  u  соответствуют  целому  числу  с
двойной  точностью.  Если  после  'l'   не   присутствуют   указанные
спецификации формата,то такая конструкция обрабатывается  как  формат
'u', т.е. нормальное целое без знака;
    *символ,  указывающий  тип  преобразования.   Ширина   поля   или
точность могут вместо цифровой строки определяться  символом  "*".  В
этом  случае  ширина  поля  или  точность  содержится   в   следующем
аргументе, рассматриваемом как целое число.

    Ниже приведены  символы  типов  преобразования  и  обьяснение  их
смысла:
    d, u, o, x 
целое число "arg" преобразовывается соответственно  в  десятичный  со
знаком, десятичный  без  знака,  восьмеричный  или  шестнадцатеричный
виды. (со знаком только десятичный вид, остальные без знака).
    D, U, O, X 
аналогично малым, но считается, что аргумент имеет тип long  (т.е.  D
аналогично ld).
    f
действительное число типа float или double преобразуется  для  печати
в десятичном виде "{-}ddd.ddd", число  цифр  после  десятичной  точки
равно спецификации точности для  аргумента.  Если  точность  опущена,
принимается 6 цифр; если задается точность 0, не печатаются  цифры  и
десятичная точка.
    e
действительное число типа float или double преобразуется  для  печати
в виде "{-}d.ddde+-dd", где перед десятичной точкой  печатается  одна
цифра, а после десятичной  точки  печатается  столько  цифр,  сколько
указано в спецификации тосимволы строки печатаются  до  конца  строки
(до символа '\0') или до исчерпания количества  символов,  указанного
в  спецификации  точности.  Если  точность  равна  0   или   опущена,
печатаются все символы до конца строки.
    r
считается, что следующий аргумент является  указателем  на  структуру
"формат - аргументы",  с  которыми  и  будет  происходить  дальнейшее
                              - 10 -


преобразование. первым элементом структуры должен быть  указатель  на
строку "формат", а следующими элементами -  аргументы,  над  которыми
будут  производится  преобразования.  такая  структура  автоматически
образуется в  стеке  при  вызове  функции  с  аргументами,  подобными
аргументам printf(). Например:
        myerror(args)
        {
                fprintf(stderr,"\n?error: %r", &args);
                exits(4);
        }
   Эта функция может быть вызвана так:
        myerror("ошибка %d в строке %d\n", errnum, strnum);
передача упрауления другому формату по  спецификации  'r'  происходит
без  возврата  управления  вызвавшему  формату.  Если   символ   типа
преобразования не совпадает с выше перечис ленными,  то  этот  символ
просто выводится. Таким образом можно вывести '%',  задав  в  формате
%%".
    Ни в одном из случаев отсутствующая  или  малая  ширина  поля  не
приводит к усечению поля.  Заполнение  поля  (пробелами  или  нулями)
имеет  место  только  в  том  случае,  когда  указанная  ширина  поля
превышает действительную ширину.
    При компоновке  программы  в  зависимости  от  некоторых  условий
могут  быть  исключены  (для  экономии  памяти)   функции   форматных
преобразований чисел с плавающей запятой. если при этом  вывод  чисел
с пз используется, то в поле числа будет помещен один  символ  '?'  и
необходимое для заполнения поля число заполнителей.
    printf() и fprintf() возвращают 0  при  нормальном  завершении  и
EOF при  ошибке  или  конце  файла.  sprintf()  возвращает  "buffer".
примеры:
    для печати даты и времени в виде
        sunday, july 3, 10:02
,где weekday и  month  являются  указателями  на  строки,  используем
следующую функцию printf:
        printf("%s, %s, %d, %02d:%02d",
                weekday, month, day, hour, min);
  для печати числа st с точностью до пяти десятичных цифр используем: 
        printf("st=%.5f", st);

    scanf, fscanf, sscanf
        char *format; 
        scanf(format {,указатель} ...);

        file *iop;
        char *format; 
        fscanf(iop, format {,указатель} ...);

        char *s; 
        char *format; 
        sscanf(s, format {,указатель} ...);
scanf()считывает стандартный входной файл (stdin). fscanf() считывает
указанный входной файл.  sscanf()  считывает  символы  из  строки  s.
Каждая функция считывает символы, интерпретирует их в соответствии  с
форматом  и  записывает  результаты  в  свои  аргументы.  аргументами
каждой функции являются управляющая строка "format"  описанная  ниже,
и ряд аргументов "указатель", указывающих на  переменные  для  записи
преобразованного  ввода.  Преобразование  (работа  функции   scanf())
заканчивает  при  достижении  конца  файла   или   при   несовпадении
ожидаемого ввода с реально присутствующим.
    Управляющая     строка     "format"     содержит     спецификации
преобразования, используемые для  управления  интерпретацией  входной
последовательности.  Управляющая  строка  может  содержать:  пробелы,
символы  табуляции  и  новой  строки,   которые   согласовываются   с
необязательными "белыми" полями во вводе;  обычные  символы  (не'%'),
                              - 11 -


которые должны согласовываться с соответствующими символами  входного
потока. Если в данном месте входного потока необходимых  символов  не
обнаруживается (лидирующее  "белое"  поле  игнорируется),  то  работа
scanf() заканчивается, при этом  следующим  вызовом  getchar()  будет
считан тот  символ,  который  не  удалось  интерпретировать,  scanf()
возвращает его по ungetc().  Спецификации  преобразования,  состоящие
из символа '%', необязательного символа подавления присваивания  '*',
необязательной  численной  максимальной   ширины   поля   и   символа
преобразования.
    Спецификация преобразования управляет преобразованием  следующего
поля ввода: результат помещается в переменную, на  которую  указывает
соответствующий аргумент, за исключением тех  случаев,  когда  указан
символ подавления присваивания '*'.  Вводное  поле  определяется  как
строка   символов   (непробелов).   Оно   простирается   до   первого
неподходящего  символа  или  до  исчерпания  ширины  поля  (если  она
указана). Лидирующее "белое" поле перед полем ввода обычно (если  это
не оговорено особо) игнорируется. Символ преобразования указывает  на
вид интерпретации поля ввода.  Могут  применяться  следующие  символы
преобразования:
    %       - в этой точке  ввода  ожидается  одиночный  символ  '%', 
              присваивание не выполняется;
    d ,o, x - ожидается   целое   указанного   типа   -   десятичное,
              восьмеричное  или   шестнадцатеричное   соответственно.
              Соответствующий  аргумент  должен  быть  указателем  на
              целое.
    s       - ожидается символьная строка.  Соответствующий  аргумент 
              должен быть указателем на char, указывающим  на  массив 
              символов, достаточный для размещения  строки  вместе  с 
              завершающим символом '\0', который будет добавлен. Поле
              ввода завершается  пробелом,  табуляцией  или  символом
              новой строки.
    c      -  ожидается символ. соответствующий аргумент должен  быть
              указан в квадратных скобках.  отметим,  что  лидирующее
              "белое" поле не подавляется. Если первым символом после
              квадратной скобки является '~',  то  набор  символов  в
              квадратных   скобках    рассматривается    как    набор
              "запрещенных" или "стоповых" символов  -  вводятся  все
              символы до  первого  символа,  который  присутствует  в
              оставшейся части (после '~') символов между квадратными
              скобками. Соответствующий аргумент должен указывать  на
              массив символов.
    Если перед d, o или x ставится 'l', это означает,  что  в  списке
аргументов находится указатель на целое  число  с  двойной  точностью
(long), а не на int. Аналогично, если перед e и f ставится  'l',  это
означает, что используется указатель  на  число  с  плавающей  точкой
двойной точности (double), а не  float.  Аналогичного  эффекта  можно
добиться, используя для спецификации  преобразований  большие  буквы,
как в printf. Если символам преобразования  d,o  или  x  предшествует
'h',это  означает,  что  используется  указатель  на  короткое  целое
(short),  а  не  на  int  (в  данной  реализации  short  совпадает  с
int).Функции   scanf(),   fscanf(),   sscanf()    возвращают    число
присвоенных элементов ввода. Необходимо отметить,  что  если  присут-
ствующее поле не подходит для указанной  спецификации  преобразования
(например  символьный  ввод  для  "%d"  и  т.п.),   то   спецификация
пропускается  и  преобразование  не  выполняется.  Если   ни   одного
присвоения не произошло и достигнут конец файла, то  будет  возвращен
EOF (-1). Примеры:
        int i;
        float x;
        char name[50];
        scanf("%d%f%s", &i, &x, name);
При вводе следующей строки:
        25 54.32e-1 tompson
                              - 12 -


scanf() присвоит i значение 25, x  -  значение  5.432  и  name  будет
содержать "tompson\0". Возвращено будет число  3.  При  вводе  строки
        2aaa
i будет присвоено значение 2, f останется без изменений (для него нет
подходящего поля,  спецификация  преобразования  игнорируется),  name
будет содержать "aaa\0". возвращено будет число 2.
    
    atoi
        char *str; /* строка с числом */
        atoi(str);
Преобразует строку символов ASCII в десятичное.
        [" "\t]*[+-]?[0-9]*
ведущие  пробелы,  табуляции  и  знак  '+'  игнорируются,  знак   '-'
обрабатывается  обычным  образом.  Преобразование  заканчивается   на
первом неподходящем  символе.  Если  число  не  помещается  в  слово,
происходит усечение до 16 бит. Возвращает  число.  Ошибок  нет.  Если
преобразования не было вообще, возвращается число 0.

    atoo
        char *str;
        atoo(str);
строку символов ASCII в восьмеричное число. Все остальное как atoi.

    atol
        char *str;
        long
        atol(str);
преобразует строку символов ASCII в  длинное  десятичное.  происходит
усечение до 32 бит. Все остальное как atoi

    atof
        char *str;
        double
        atof(str);
преобразует строку символов ASCII в длинное  десятичное  с  плавающей
точкой (double). Строка ожидается в виде (в нотации lex):
        [" "\T]*[+-]?[0-9]*(\.[0-9]*)?[Ee]?((-)?[0-9]+)?
или,  по  русски,  в  виде  необязательной  подстрочки   пробелов   и
табуляций, необязательного знака, строки  цифр,  возможно  содержащей
десятичную точку, необязательного символа 'Е' (на любом  регистре)  и
необязательного целого со знаком. Если значение  превышает  набольшее
представимое,  то  возвращается  наибольшее  представимое   число   и
устанавливается ошибка "E_RAN" в $$ferr.

    ecvt, fcvt, gcvt - преобразования для вывода  чисел  с  плавающей
                       запятой
        double value;
        int ndigit, *decpt, *sign;
        char *
        ecvt(value, ndigit, decpt, sign);

        char *
        fcvt(value, ndigit, decpt, sign);

        char *buf;
        char *
        gcvt(value, ndigit, buf);
    ecvt() - значение value в  строку  цифр  ASCII  длиной  ndigit  и
возвращает указатель на нее. Позиция  десятичной  точки  относительно
начала строки возвращается через decpt:  нулевое  значение  означает,
что точка стоит пере первым символом строки,  отрицательное  значение
означает, что десятичная точка стоит  слева  от  строки.  Если  value
отрицательно,  то  через  sign  возвращается  ненулевое  значение,  и
                              - 13 -


нулевое в противном случае. Младшая цифра округляется.
    fcvt() аналогична ecvt(), однако ndigit означает не  общее  число
цифр, а число цифр после запятой.
    gcvt() преобразует значение value в строку цифр ASCII  в  буфере,
на которы указывает buf и  возвращает  buf.  В  строке,  возвращаемой
gcvt() присутствует до  ndigit  значащих  цифр  (меньше,  если  число
короткое). Строка пригодна для печати, причем  формат  строки  -  'e'
Jили 'f',  выбирается  по  минимуму  длины  строки,  необходимой  для
представления данного числа в этих форматах.
    Поясним это: 
  число 1.1 может быть представлено:
               1.1     - формат 'f'
               1.1e+00 - формат 'e'.
длина первого представления меньше, поэтому будет выбрано оно;
  число 123000000 может быть представлено так:
               123000000 - формат 'f'
               1.23Е+08  - формат 'e'
в этом случае будет выбрано второе представление.
    gcvt() вызывает ecvt().  необходимо  отметить,  что  эти  функции
возвращают указатели на внутренние статические буфера, которые  будут
перезаписываться при каждом вызове функций.

           1.2. НЕФАЙЛОВАЯ РАБОТА С ТЕРМИНАЛОМ

    Существует много приложений (различные экранные редакторы, игры),
в  которых  требуется  активное  и  нестандартное  взаимодействие   с
терминалом.  Описываемые  в  данной  главе  функции  и   обеспечивают
возможность   такого   взаимодействия.   Необходимо   отметить,   что
описываемые функции  не  зависят  от  стандартных  функций  работы  с
файлами (хотя и оказывают некоторое влияние на работу последних).

        _getchar();
вводит символ непосредственно с терминала. Возвращает  <ВК>  как  два
символа:'\r' и '\n' (<ПС> как '\n').

        char    c;
        _putchar(c);
выводит символ 'c' на терминал. Не добавляет '\r' при выводе '\n'.

        char    *s;
        _puts(s);
выводит строку "s" на терминал. Не добавляет '\r'  при  выводе  '\n',
добавляет "\r\n" в  конце  строки  в  данной  реализации!!!!!!!!!  По
стандарту не должна добавлять "\r\n" в конце строки

        int    mode;   /* 0,1,2 */
        $ttymode(mode);
устанавливает режим ввода с терминала в зависимости от значения  mode
   0 - стандартный режим - ввод осуществляется с системным эхоотобра-
жением и буферизацией строки (программа получает символы только после
завершения строки). Этот режим установлен по умолчанию.
   1 - режим посимвольной активации - каждый  введенный  символ сразу
же передается программе, системное эхоотображение отсутствует.
   2 - режим  посимвольной активации без ожидания - как и в  предыду-
щем режиме, однако при отсутствии  введенного  символа  ожидание   не
выполняеся, функция  _getchar()  (и  getchar(),  если  файл  ввода  с
терминала  открыт  с   ключом   'n')   возвращают   '\0'.   Оказывает
соответствующее влияние на  ввод  с  терминала  и  при  использовании
стандартных функций ввода. Корректно работает  и  в  TS  мониторе  ОС
РАФОС.
        int     mode;   /* 0 - запретить, 1 - разрешить */
        $ttylc(mode);
                              - 14 -


разрешает или запрещает ввод букв нижнего регистра (lower case)

           1.3. УПРАВЛЕНИЕ РАСПРЕДЕЛЕНИЕМ ДИНАМИЧЕСКОЙ ПАМЯТИ

    Средства управления динамической памятью не входят в  язык  "СИ",
однако существует стандартный набор функций управления памятью.  Этот
набор  содержит  функции,  позволяющие  получать  необходимые   блоки
памяти, а также  возвращать  ненужные  больше  блоки  для  повторного
использования. Длина блоков указывается в байтах, и часто в  качестве
длины используется результат, возвращаемый конструкцией  sizeof(...).
Структура   памяти   при   работе   системы   распределения    памяти
представляет собой связанный  (и  даже  замкнутый  в  кольцо)  список
блоков, адреса элементов которого  монотонно  возрастают  на  рабочем
участке. При запросе памяти всегда выделяется четное  число  байт  (и
возвращается  указатель,  выровненный  по  границе   слова),   причем
дополнительно резервируется  служебное  слово  (два  байта),  которое
располагается в начале блока и  содержит  адрес  следующего  блока  и
информацию о занятости текущего блока (младший бит  установлен,  если
блок занят, и сброшен,  если  свободен).  В  конце  рабочего  участка
памяти находится служебное слово, соответствующее фиктивному блоку  и
содержащее указатель на начало списка. Более подробную  информацию  о
внутренней  организации  функций  распределения  динамической  памяти
СМ."Руководство программиста. Библиотеки.  Управление  распределением
динамической памяти".
        unsigned size;
        char *
        malloc(size);

        char*
        alloc(size);

резервирует в области динамической памяти указанное в size число байт
и возвращает указатель на  на  первый  из  них  (всегда  выровнен  по
границе   слова).    При    необходимости    дополнительная    память
резервируется у операционной системы (при помощи  функции  sbreak()).
Если запрос не может быть  удовлетворен,  malloc()  возвращает  null.
alloc()  аналогична  malloc()  за  исключением  того,  что  в  случае
?невозможности удовлетворить запрос она возвращает не NULL, а -1.  Эта
функция является устаревшей и присутствует лишь для  совместимости  с
библиотекой ОС UNIX V6. Пользоваться ей не рекомендуется (тем  более,
что она работает через malloc()).

        char *p; /* указатель на блок */
        free(p);
        mfree(p);
освобождает участок (блок)  памяти,  зарезервированный  по  malloc().
После освобождения этот участок (или  его  часть)  вновь  может  быть
распределен  при  помощи  функции  malloc().  Ничего  не  возвращает.
Следует  отметить,  что  попытка  возвратить   участок   памяти,   не
Jполученный по malloc(), приведет к аварийному завершению  задачи  по
инструкции BPT. mfree()  полностью  аналогична  free()  (является  ее
альтернативным входом).

        int     n; /* чило элементов */
        int     m; /* размер одного эл-та в байтах */
        char *
        calloc(n, m);   /* NULL если нет памяти */
        char *p;
        cfree(p);
резервирует участок памяти, достаточный для  размещения  n  элементов
длиной по m байт  каждый.  Возвращаемый  участок  памяти  обнуляется.
Возвращает указатель на  начало  участка  или  NULL,  если  требуемый
участок  не  может  быть  выделен.  Резервирует  память  при   помощи
                              - 15 -


malloc(), поэтому освободить зарезервированный участок можно  вызовом
free(), однако присутствует и  функция  cfree(),  которая  аналогична
free() (и работает через нее).

        char   *buff;  /* адрес участка, полученного по malloc */
        unsigned size; /* новый размер */
        char *
        realloc(buff, size);
изменяет размер участка памяти, ранее  полученного  по  malloc(),  на
size.  buff  -  указатель  на  этот  участок.  realloc()   возвращает
указатель на участок требуемого размера  или  NULL,  если  запрос  не
может быть выполнен. При выполнении realloc()  участок  памяти  может
быть перемещен, однако содержимое участка сохраняется  (переносится).
Понятно,  что  сохраняется  участок  размера,  равного  минимуму   из
старого и нового размеров. Если realloc() возвращает NULL, то  старый
участок освобождается и содержимое его теряется.

        int    amount;
        char *
        sbreak(amount);
резервирует участок памяти у операционной  системы.  Возвратить  этот
участок памяти  невозможно.  При  последовательных  вызовах  sbreak()
выделяется  непрерывная  память,  никаких   служебных   областей   не
заводится. Является основным  и  единственным  (легальным)  средством
получить участок памяти у ОС. Функция malloc() в  конце  концов  тоже
обращается к sbreak(). Функция  sbreak()  корректно  работает  как  в
SJ/FB/XM мониторах ОС RT11, так и в TS  мониторе  (через  .tstop),  а
также в эмуляторе ОС RT11 ОС ДОС  КП.  Возвращает  адрес  выделенного
участка или NULL, если запрос не может быть  удовлетворен.  sbreak(0)
возвращает  текущую  верхнюю  границу  используемой   памяти   (адрес
первого свободного слова памяти).

        int    n;
        char *
        salloc(n);      /* NULL если нет места */
резервирует n байт в стеке задачи. Возвращает указатель  (выровненный
по границе слова)  на  начало  зарезервированного  участка,или  NULL,
если удовлетворить запрос невозможно  (стек  опускается  ниже  0700).
Зарезервированный  участок   будет   автоматически   освобожден   при
возврате  из   функции,   выполнившей   salloc();   явно   освободить
зарезервированный  участок  невозможно.  Не  может  использоваться  в
качестве аргумента другой функции, т.к.при вызове функции  происходят
операции со стеком: выполнение
        subr(salloc(10),20);
приведет к ошибке. обойти это можно так:
        temp = salloc(10);
        subr(temp, 20);
        char    *in;    /* откуда */
        char    *out;   /* куда */
        char   nbytes; /* сколько байт */
        copy(out, in, nbytes);
коприрует  указанное  число  байт.  Возвращает  указатель  на  первый
свободный после копирования байт в области out.

        char    *addr;
        int     nbytes;
        zero(addr, nbytes);
Обнуляет участок памяти. Ничего не возвращает.





                              - 16 -


           1.4. РАБОТА СО СТРОКАМИ И СИМВОЛАМИ 

    Во время выполнения программы, написанной на языке  "СИ",  строка
символов  представляется  последовательностью  байт,  заканчивающейся
нулевым байтом. принято нулевой байт записывать не как 0, а как  '\0'
или  kak  "EOS"  (определен  как  0  в   файле   "stdio.h").   Строка
использутся, как правило, при помощи  указателя  на  ее  начало  и  в
дальнейшем выражение "строка str" будет означать "строка,  на  начало
которой указывает str", причем подразумевается, что "str"  имеет  тип
"char *".

        char *str;
        strlen(str);
возвращает длину строки  str  в  байтах.  Конечний  нулевой  байт  не
считается, так что строка "" будет иметь длину 0.

        char *in;       /* исходная строка */
        char *out;      /* указатель на буфер */
        char *
        strcpy(out, in);
        char *
        cpystr(out, in);
копируют строку in в буфер, на который указывает out.
    strcpy() возвращает out.
    cpystr() возвращает указатель на конец строки, записанной в  out.
Это позволяет использовать функцию cpystr() следующим образом:
        char   *ptr;
        char    buffer{bufsiz};
        extern  char *cpystr();
        ptr = cpystr(buffer, text);
        ptr = cpystr(ptr, more_text);
В приведенном примере первым вызовом cpystr() строка text  копируется
в buffer, после чего вторым вызовом функции cpystr()  к  этой  строке
добавляется (приписывается) строка more_text.

        char    *out;   /* вых. буфер */
        char    *in;    /* вх. строка */
        int     count;  /* число байт */
        char *
        strncpy(out, in, count);
копирует строку in в буфер out. В out записывается ровно count байт -
если строка in короче, то out дополняется нулями,  если  длиннее,  то
остаток отбрасывается, причем в последнем случае out  не  завершается
нулем. Возвращает out.
        char    *in;
        char    *out;
        char *
        strcat(out, in);
присоединяет  ("приписывает")  строку  in   к   концу   строки,   уже
находящейся в буфере out. Возвращает out.

        char    *out;
        char    *in;
        int     count;
        char *
        strncat(out, in, count);
присоединяет  ("приписывает")  строку  in   к   концу   строки,   уже
находящейся в  буфере  out.  Если  строка  in  содержит  более  count
символов, то остаток строки отбрасывается. В count не входит  нулевой
байт, которым в любом случае завершается строка out. Возвращает out.

        char    *out;   /* указатель на буфер */
        char    *in0;   /* первая вх. строка  */
                              - 17 -


        char    *in1;   /* вторая --- // ---  */
        .......................................
        char *
        concat(out, in0, in1, ...., NULL);
копирует  в   буфер,   на   который   указывает   out,   конкатенацию
(обьединение) входных строк. У функции может быть произвольное  число
входных  аргументов,  последний  из  которых   должен   быть   нулем.
Возвращает out.

        char *a, *b;    /* stroki */
        strcmp(a, b);
сравнение двух строк возвращает результат:
        -1     a <  b
         0     a == b
        +1     a >  b
Сравнение производится  в  смысле  алфавитной  упорядоченности,  т.е.
строки просматриваются с начала, совпадающие символы пропускаются,  а
результат сравнения строк есть  результат  сравнения  (кодов)  первых
несовпадающих   символов.   Необходимо   отметить,   что   алфавитное
упорядочивание  будет  выполняться  правильно  только  для  текстовых
строк латинского алфавита, для текстовых строк,  рассматриваемых  как
строки русского алфавита, порядок будет неалфавитным.

        char   *a, *b;
        int     count;  /* число символов */
        strncmp(a, b, count);
сравнение строк, причем рассматриваются только первые count  символов
(если строки длиннее count). Возвращает результат сравнения:
        -1     a <  b
         0      a == b
        +1      a >  b
О смысле сравнения см. strcmp().

        char *a, *b;
        streq(a, b);
проверяет строки на совпадение. Возвращает 1, если строки  совпадают,
и 0, если нет.

        char   *strn;
        char    sim;    /* символ для поиска */
        char *
        index(strn, sim);
просмотр строки strn до первого появления символа sim. Если символ  в
строке найден, возвращает указатель на него, иначе возвращает NULL.

        char    *strn;
        char    sim;
        char *
        rindex(strn, sim);
возвращает указатель на место последнего появления  символа  "sim"  в
строке "strn", или NULL, если "sim" в "strn" отсутствует.

    Существует (в данной библиотеке) два варианта реализации  функций
тестирования и модификации символов: в виде обычных функций и в  виде
макросов  с  аргументами.  Второй  вариант  содержит  большее   число
функций, работает быстрее,  однако  требует  0.5  Кб  памяти  (причем
независимо от числа  используемых  функций,  если  оно,  конечно,  не
равно нулю). Первый  вариант  работает  несколько  медленней,  однако
требует меньше памяти, особенно когда  используется  всего  одна  или
две функции. Разумно в программах,  где  тестирование  и  модификация
символов используется "между делом" использовать первый вариант, а  в
программах,  существенно  ориентированных  на  работу   с   символами
(например, в лексических анализаторах) - второй. Необходимо  обратить
                              - 18 -


внимание  на  то,   что   эти   функции   рассчитаны   на   латинский
двухрегистровый алфавит, и будут не  совсем  корректны  для  русского
алфавита. Так, например, символы 'э', 'ш', 'щ',  'ч',  'ю'  не  будут
трактоваться как буквы.

Первый вариант, настоящие функции:
        char c;
        isupper(c);     /* буква верхнего регистра */
        islower(c);     /* буква нижнего  регистра */
        isalpha(c);     /* буква                   */
        isdigit(c);     /* цифра                   */
        toupper(c);     /* преобразование к верхнему регистру */
        tolower(c);     /* преобразование к нижнему  регистру */
Возвращают  ненулевое  значение,  если  символ,  являющийся   входным
аргументом,  принадлежит  соответствующему  классу  (класс  указан  в
комментарии  к   обращению),  и   0   в   противном   случае. Классы,
используемые  при  определении  функций,следующие:   буквы   верхнего
регистра  -латинские  большие  буквы,  A-Z,коды  символов  0101-0132.
буквы  нижнего  регистра  -  латинские  малые  (или,  для   некоторых
терминалов, русские большие), коды символов 0141-0172. Буквы -  буквы
на верхнем или на нижнем регистре. Цифры - цифры 0-9,  коды  060-071.
функции   преобразования   (tolower()   и    toupper())    возвращают
преобразованный код символа. Преобразования  выполняются  только  для
букв (латинских).
    Второй вариант, макросы:
    Обращение к функциям:
        #include  /* файл макроопределений */
        isupper(c)         /* буква верхнего регистра     */
        islower(c)         /* буква нижнего регистра      */
        isalpha(c)         /* буква                       */
        isdigit(c)         /* цифра                       */
        isalnum(c)         /* буква или цифра             */
        isxdigit(c)        /* шестнадцатеричная цифра     */
        isspace(c)         /* разделитель                 */
        ispunct(c)         /* символ пунктуации           */
        isgraph(c)         /* видимый символ              */
        isprint(c)         /* печатный символ             */
        iscntrl(c)         /* управляющий символ          */
        isascii(c)         /* 7-битный символ ASCII       */
        _toupper(c)        /* преобразование к верхнему регистру    */
        _tolower(c)        /* преовразование к нижнему  регистру    */
        toascii(c)         /* преобразование к 7-битному коду ASCII */
Макросы  isxxxxx()  возвращают  ненулевое  значение,   если   символ,
являющийся входным аргументом,  принадлежит  соответствующему  классу
(класс указан в комментарии к обращению), и  0  в  противном  случае.
Классы,   дополнительно   используемые   при   определении   функций,
следующие: шестнадцатеричные цифры -  цифры  и  буквы  a-f  на  любом
регистре.  разделители  -  символы  '\t'  ('\011'),  '\n'   ('\012'),
('\013'), '\f' ('\014'), '\r' ('\015'),  "пробел"  ('\040').  Символы
пунктуации - разнообразные символы (все печатные небуквы  и  нецифры)
с кодами 041-057, 072-0100, 0133-0140, 0173-0176. Видимые  символы  -
все буквы, цифры и символы пунктуации.  Печатные  символы  -  видимые
символы и пробел. управляющие символы - все  неупомянутые  символы  с
кодами 0-010, 016-037 и 0177. 7-битные символы ASCII -  все  символы,
код которых (без учета знака) не больше 0177. Макросы  преобразования
_tolower()  и  _toupper()  возвращают  преобразованный  код  символа.
преобразования выполняются только для букв,  причем  эти  макросы  не
должны  использоваться  с  аргументами,  имеющими  побочный   эффект,
например вида
        _toupper(*ptr++);
В таких случаях применяйте функции "toupper()" и "tolower()".  Макрос
toascii() возвращает  аргумент,  преобразованный  в  7-битный  символ
ASCII (см. выше) и может использоваться без ограничений.
                              - 19 -


        int val;
        abs(val);
возвращает абсолютное значение своего аргумента.

        int start; /* стартовое состояние */
        srand(start); /* запуск генератора случайных чисел */
        rand();    /* возвращает случайное число */
При последовательных вызовах возвращает целые положительные  числа  в
диапазоне   от   0   до    2**15,    входящие    в    псевдослучайную
последовательность длиной 2**32. Распределение  равномерное.  Функция
srand() может  быть  вызвана  в  любой  момент  для  инициализации  и
реинициализации  генератора,  т.е.  определения  некоторой  точки   в
псевдослучайной  последовательности.  отсутствие  вызова  srand()  до
первого использования rand() равносильно вызову srand(1).

        char *buffer;   /* указатель на строчку из 2-х байт */
        swabb(buffer);
возвращает целое, полученное из  переставленных  байтов  buffer{0}  и
buffer{1}. buffer может не быть выровнен по границе слова.

        int value;
        swabi(value);
возвращает целое, полученное из value перестановкой байт (инструкцией
"swab").

        char *base; /* начало массива элементов */
        int  nel;   /* число элементов */
        int  width; /* длина элемента в байтах */
        int (*compar)( ); /* функция сравнения */
        qsort(base, nel, width, compar); /* сортировка */
реализует алгоритм быстрой сортировки. аргументы: "base" -  указатель
на начало массива элементов, подлежащих  сортировке,  "nel"  -  число
элементов, "width" - длина элемента в байтах,  "compar"  -  указатель
на функцию сравнения. Функция  сравнения  будет  вызываться  с  двумя
аргументами  -  указателями   на   сравниваемые   элементы.   Функция
сравнения должна возвращать целое меньше, равное или  больше  нуля  в
зависимости от того, меньше, равен  или  больше  первый  элемент  чем
второй соответственно.

        int count;     /* число символов ASCII */
        int *output;    /* указатель на буфер для вывода */
        char *input;    /* указатель на входную строку */
        ascr50(count, input, output);
преобразовывает "count"  символов  ASCII  из  строки  "input"  в  код
RADIX-50, и записывает полученное в "output"  (на  каждые  3  входных
байта получается одно слово на выходе). Если "count" не кратно  трем,
или если строка короче "count", строка дополняется пробелами.

        char *buff;    /* выходной буфер */
        int  *r5vec;   /* входной массив слов в RAD50 */
        int  r5cnt;     /* число слов R50 для преобразования */
        r50toa(buff, r5vec, count);
преобразует "count" слов из массива, на который указывает  "r5vec"  в
строку символов ASCII, которая  помещается  в  массив  "buff"  (по  3
выходных символа  на  каждое  входное  слово).  Строка  в  "buff"  не
завершается  нулем,  все   символы   будут   на   верхнем   регистре.
"Зарезервированный" символ  кода  RADIX-50  (035)  будет  представлен
символом '/'.

        int count;      /* число аргументов */
        extern int routine(); /* имя функции */
        return_value = call(routine, count, &arg1, ... &argn);
позволяет вызывать функции с интерфейсом языка  FORTRAN-IV,  например
                              - 20 -


функции из системной  обьектной  библиотеки,  из  программ  на  языке
"СИ".  Функция  call(),  кроме  того,   сохраняет   регистры   R2-R5.
"routine" -  имя  подпрограммы  (или  функции)  с  интерфейсом  языка
FORTRAN-IV,  "count"  -  число   аргументов,   передаваемых   функции
"routine", "arg1" - "argn" - эти аргументы (отметим,  что  передаются
адреса  аргументов,  поэтому  среди   аргументов   не   должно   быть
регистровых переменных, к которым неприменима операция '&').
**********Необходимо  заметить,  что  вызов  функций,   действительно
написанных на ФОРТРАНе, приведет к интерференции  исполняющих  систем
и  достаточно   сложным   проблемам,   которые   станут   практически
неразрешимыми,  если  в   этих   функциях   производится   ввод/вывод
средствами языка FORTRAN-IV. Возвращение  чисел  с  плавающей  точкой
непредусмотрено.

        long *tloc;
        long
        time(0);
        long
        time(tloc);
эмулирует системный вызов time ОС  UNIX.  Возвращает  длинное  целое,
содержащее число секунд, прошедших с 00:00 (полночи)  1  января  1970
г.  Если  аргумент  "tloc"  отличен  от  нуля,  то  то  же   значение
копируется  по  адресу  "tloc". В ОС UNIX time() возвращает GMT, а не
локальное  время,  здесь  же  - локальное  системное  время. Алгоритм
вычисления  времени  будет  работать  корректно  для  дат,  у которых
1970 <= год < 2100.

        #include 
        long *clock; /* указатель на время,
                        полученное вызовом time() */
        struct tm *
        localtime(clock);
заполняет структуру tm, описанную в файле "time.h", в соответствии  с
системным временем в формате ОС UNIX, полученным,  например,  вызовом
time(),  и  возвращает  указатель  на  эту  структуру.   Возвращаемое
функцией значение указывает  на  статические  данные,  которые  будут
перезаписываться при каждом вызове функции.

        #include 
        jmpbuf env;
        int val;
        int setjmp(env);
        int longjmp(env,val);  /* выполнение нелокального goto */
используются, в основном, для обработки ошибок и событий, возникающих
в  глубоко  вложенных  функциях.  При  этом  неудобно  возвращать  по
цепочке функций некое "особое" значение,  существенно  удобнее  иметь
возможность выполнить прямой переход (возврат)  в  функцию,  стояющую
где-то у  начала  цепочки  вызовов.  Возможность  такого  перехода  и
предоставляют функции setjmp() и longjmp(). setjmp(env) запоминает  в
статическом буфере "env"  информацию  о  текущей  точке  программы  и
возвращает 0.  Функция  longjmp(env,  val),  вызванная  после  вызова
setjmp(env), вызванная возможно в другой, глубоко вложенной  функции,
востанавливает информацию о состоянии, запомненной setjmp(). При этом
выполнение программы продолжается  (как-бы)  с  возврата  из  функции
setjmp(), но возвращаемое  значение  равно  "val",  установленой  при
вызове longjmp() и не равно 0. (Если val == 0,  то  возвращается  1).
Как правило, эти функции используются так:
        if(i = setjmp(env)) {
                /* действия, выполняемые при longjmp() */
        }

                              - 21 -


        else {
                /* действия, выполняемые после начальной
                   установки точки возврата вызовом setjmp().
                   как правило действия, приводящие к
                   вызову цепочки вложенных функций, в которых
                   используется longjmp(). */
        }
        .....................
                longjmp(env, 1); /* во вложенной функции */
        .....................
при возвращении по  longjmp()  глобальные  и  статические  переменные
сохраняют значения, которые они имели непосредственно  перед  вызовом
longjmp(), регистровые переменные имеют значения, которые  они  имели
перед вызовом setjmp(), а  автоматические  (локальные)  переменные  -
значения, которые они имели перед вызовом из данной функции  функции,
породившей  цепочку  вызовов,  на  которой  был  выполнен  longjmp().
Необходимо  отметить,  что  вызов  longjmp(env,  val)   с   env,   не
установленным setjmp(env) приведет к непредсказуемым результатам.  То
же произойдет, если функция, выполнившая в последний раз  setjmp(env)
в момент вызова longjmp(env, val) не активна, т.е. не находится  выше
в цепочке функций, вызвавших выполняющую longjmp() функцию.

      exit();
      int stat; /* статус завершения */
      exits(stat);
      extern int $$exst;/* фактический статус завершения для exit() */
осуществляют  выход  из  программы  на  "СИ".  При  этом  выполняются
следующие  действия   в   указанном   порядке:   вызывается   функция
"wrapup()" (возможно написанная пользователем,  см  ниже).  Выводится
(если необходимо) статистика выполнения  ("профиль")  программы  (см.
"средства отладки". "$$prof").  Закрываются  все  файлы.  Выполняется
фактический  выход  в  монитор  ОС   (.EXIT).   Вызывая   exits()   с
аргументом, программа может передать статус  завершения  операционной
системе. Возможные значения аргумента такие:
        1 - success      (нормально)
        2 - warning      (замечание)
        4 - error        (ошибка)
        8 - severe error (грубая ошибка)
вызов exit() эквивалентен  exits(1).  Заметим  также,  что  программа
может  занести  статус  завершения   непосредственно   в   глобальную
переменную $$exst и выйти по exit():

        extern int $$exst;
        ...
        $$exst = 4;
        exit();
если функция main() завершается,это равносильно  выполнению  в  конце
нее exit().

        wrapup();
(библиотечный ее вариант не делает ничего) вызывается при  завершении
программы - выполнении функции  exit(),  вызванной  явно  или  неявно
(например, через error() или при завершении функции main()). Смысл  в
том, что  пользователь  может  написать  собственную  функцию,которая
заменит библиотечный вариант, и  которая  может  выполнять  некоторые
действия при завершении программы  на  "СИ"  вызывается  только  один
раз, т.е. если функция  wrapup()  сама  выполнит  exit(),  то  нового
вызова wrapup() не будет.

        int mytr4();     /* функция обраб. trap to 4  */
        int mytr10();    /* функция обраб. trap to 10 */
        $set4 (mytr4);   /* перехват trap to 4  */
        $set4 (0);       /* возвращение стандартной реакции */
                              - 22 -


        $set10(mytr10);  /* перехват trap to 10 */
        $set10(0);       /* возвращение стандартной реакции */
служат для перехвата и собственной обработки прерываний по векторам 4
(недопустимый адрес и др.)  и  10  (недопустимая  инструкция).  После
выполнения  "$set4(mytr4)"  при  прерывании  по   вектору   4   будет
вызываться  функция  "mytr4()",  написанная  пользователем.   Функция
"mytr4()" должна быть написана на "СИ" (в смысле "сохранять  регистры
R2-R5") и может обращаться к четырем аргументам типа  int.  Например,
при описании

        mytr4(r0, r1, pc, psw)
        int r0, r1, pc, psw;
        {
        ............
        }
    r0 - значение регистра R0 при прерывании, 
    r1 - значение регистра R1 при прерывании, 
    pc - счетчик команд после прерывания, 
    psw - слово состояния процессора.
  Если функция mytr4() изменит эти  аргументы,  то  при  возврате  из
прерывания будут использованы новые значения.
  Если функция mytr4() возвратит 1,  то  выполнение  программы  будет
продолжено  (обратите  внимание  -  если  прерывание   произошло   на
многословной инструкции, то иногда необходимо скорректировать PC  для
нормального продолжения работы).
   Если mytr4() возвратит 0, то будет выполнена  стандартная  реакция
на  прерывание.   Для   возвращения   стандартной   реакции   системы
необходимо вызвать функцию с  нулевым  аргументом.  Функция  $set10()
работает полностью  аналогично  $set4(),  только  для  прерывания  по
вектору 10. Использование других способов перехвата прерываний  может
нарущить работу эмуляторов EIS и FPU.

        char *fmt; /* формат сообщения */
        error(fmt, arg1, ... , argn);
завершает программу, выполняя следующие действия в указанном порядке:
выводит сообщение об ошибке, заданное форматом  "fmt"  и  аргументами
"arg1 ... argn" (см. описание функции printf()) в stderr.
 -если используются средства отладки, в stderr выводится путь  вызова
(см. calltr());
 -выполняется  выход  из  программы  вызовом  exit(),  причем  статус
завершения (см. exit()) устанавливается в "error".

        char *fmt; /* формат сообщения */
        errmsg(fmt, arg1, ... ,argn);
выводит строку, заданную форматом "fmt" и аргументами в stderr. После
сообщения в stderr выводится '\n'.  errmsg()  ничего  не  возвращает.
perror, errtxt  -  вывести  (получить)  причину  ошибки  обращение  к
функции:

        char *umsg; /* дополнительное сообщение */
        perror(umsg); /* вывод сообщения в stderr */
        char *
        errtxt();     /* получить указатель на сообщение */
При ошибке многие функции, работающие с файлами,  а  также  некоторые
функции, работающие с числами с плавающей запятой (double) не  только
отмечают факт возникновения ошибки, но  и  возвращают  дополнительную
информацию  о  ней.  Эта  информация  возвращается  через  глобальную
переменную "$$ferr" (эта переменная имеет альтернативное имя  "errno"
для  совместимости  с  ОС  UNIX)  в  виде  небольшого  положительного
целого, причем 0 означает отсутствие ошибки.

       0   "NO ERROR"
E_ILF  2   "ILLEGAL FILE NAME"
                              - 23 -


E_NOR  4   "NO ROOM FOR USER ON DEVICE"
E_FNF  5   "FILE NOT FOUND"
E_NOD  6   "NO A VALID DEVICE"
E_ILU  7   "I/O CHANNEL IN USE"
E_NOO  9   "I/O CHANNEL NOT OPEN"
E_EOF  11  "END OF FILE ON DEVICE"
E_FAT  12  "FATAL SYSTEM I/O FAILURE"
E_ERR  13  "USER DATA ERROR ON DEVICE"
E_FND  15  "FILE ALREADY FOUND (PROTECTED)"
E_NOC  17  "NO FREE I/O CHANNELS"
E_NSP  32  "NO MEMORY SPACE LEFT"
E_DOM  33  "ILLEGAL ARGUMENT"
E_RAN  34  "FLOATING OVERFLOW/UNDERFLOW"
-----  ??  "UNINDENTIFY ERROR"

    Коды  ошибок  можно  анализировать   непосредственно,   в   файле
"ERRNO.H" содержатся мнемоники для  них,  однако  при  помощи  данных
функций можно получить сообщения в текстовом виде.  Функция  errtxt()
возвращает указатель на строку, содержащую сообщение об  ошибке,  код
которой записан в $$ferr. Функция perror() выводит в  stderr  строку,
заданную аргументом, двоеточие, пробел, сообщение об ошибке  и  '\N',
т.е.,  фактически  выполняет  fprintf(stderr,   "%s:   %s\n",   umsg,
errtxt()); функция perror() ничего не возвращает.

          1.5. СРЕДСТВА ОТЛАДКИ

    При  отладке  программ,   написанных   на   языке   "СИ",   может
использоваться  стандартная  техника  отладки:   отладочная   печать,
отладчик ODT и др. Кроме того,существует ряд дополнительных  функций,
часть  из  которых  должна  включаться  явно,  а   часть   включается
автоматически, если при трансляции  хотя  бы  одного  модуля  (файла)
программы был задан ключ '-P'  и  хотя  бы  одна  функция  из  такого
модуля была вызвана. Более подробную информацию о  средствах  отладки
см. "Руководство программиста. Библиотеки. Средства отладки".

        #include 
        extern file  *$$flow;
        $$flow = fopen("trace.out", "w");
     /* или
        $$flow = stderr;
        для вывода трассировки на терминал */
        $$flow = null; /* прекращение трассировки */
Модуль трассировки вызывается при входе в каждую функцию  из  файлов,
оттранслированных с ключом '-P'. В  модуле  выполняется  проверка  на
переполнение стека - выполнение программы прерывается с диагностикой
        ?io-trace-stack below 600 octal
Eсли указатель стека опустится ниже 0600. кроме того, если требуется,
выполняется трассировка: если $$flow указывает на  дескриптор  файла,
то имя вызванной  функции,  адрес  возврата  в  вызвавшую  функцию  и
значение R5 - указателя локальной среды - выводятся в указанный  файл
для всех функций, оттранслированных  с  ключом  '-P'.  Формат  вывода
следующий:
        fun_namecallerenvironment 
,где fun_name    - имя вызванной функции (в ASCII),
     caller      - адрес   возврата   (восьмеричный)   в    вызвавшую 
                   программу,
     environment - указатель (восьмеричный) локальной среды вызванной
                   функции (R5). Информация выводится  с  добавлением
                   '\n' перед  и  после  каждой  строки.  При  первом
                   вызове модуля трассировки  устанавливается  модуль
                   "traps" (см.  ниже)  для  собственной  диагностики
                   аварийных  прерываний   программы   и   включается
                   "профиллер" (см. $$prof() ниже).
                              - 24 -


     traps       - перехват  и   диагностика   аварийных   синхронных 
                   прерываний   модуль    traps    перехватывает    и
                   диагностирует  аварийные   синхронные   прерывания
                   программы (по векторам 4, 10, 14,  20,  34,  250).
                   При прерывании сначала непосредственно на терминал 
                   выводится диагностика и дамп регистров, после чего
                   вызывается   error().   Диагностики,    выдаваемые
                   модулем traps,  следующие  (указан  перевод  и,  в
                   скобках, восьмеричное значение вектора 
                   прерывания):
    ?CC-F-Odd or nonexistent address trap
        прерывание по отсутствующему или недопустимому адресу (4)
    ?CC-F-Illegal instruction
        прерывание по недопустимой инструкции (10)
    ?CC-F-BPT trap or C run-time library error
        BPT прерывание  --  возможно,  ошибка  использования  функций
        библиотеки языка "СИ" (14)
    ?CC-F-Illegal IOT
        недопустимое IOT прерывание (20)
    ?CC-F-TRAP trap or FORTRAN error signal
        TRAP  прерывание  --  возможно,  сигнал  ошибки,   выдаваемый
        функциями, написанными на языке FORTRAN (34)
    ?CC-F-Memory protect violation
        прерывание по нарушению защиты памяти (от диспетчера  памяти) 
        (250) модуль traps включается, если программа транслируется с
        ключом '-P', а так же  при  использовании  эмуляторов  EIS  и 
        (или) FPU и (или) функций $set4() и $set10().

        $$prof();
        extern char     *$$pfil;
        extern int      $$prnl;
Неявно вызывается при нормальном или аварийном выходе из программы, в
которой были использованы функции, оттранслированные с  ключом  '-P'.
При выполнении функции $$prof() открывается  файл,  на  имя  которого
указывает $$pfil. По умолчанию имя этого файла  "PROFIL.OUT",  однако
его можно изменить явным присваиванием:  например,  дря  того,  чтобы
вывести "профиль" на терминал, необходимо выполнить
        $$pfil = "tt:";
В этот файл выводится "профиль"  выполнения  программы  --  для  всех
функций, отттранслированных с ключом '-P', выводится имя  функкции  и
количество  ее  вызовов  во   время   выполнения   программы.   Вывод
выполнияется по формату "%8s  %6u",  по  умолчанию  в  каждой  строке
выводися (через пробел) информация  о  четырех  функциях.  Это  число
можно  изменить,  выполнив  явное  присваивание  переменной   $$prnl.
например, при выполнении
        $$prnl = 0;
В каждой строке выходного файла будет выведена информация  только  об
одной функции, что удобно для сортировки.

        abort();
После закрытия всех каналов ввода/вывода (функцией $$cl16()) аварийно
завершает программу по инструкции BPT. При работе  с  отладчиком  ODT
управление будет передано отладчику.

        $$bpt();
прерывает  программу  выполнением  инструкции  BPT.  При   работе   с
отладчиком ODT управление будет передано отладчику.

        char *message; /* строка сообщения */
        msg(message);  /* вывести сообщение на терминал */
выводит указанную строку на терминал.  msg()  не  использует  функции
библиотеки ввода/вывода (вывод осуществляется по .PRINT).

                              - 25 -


        char *
        caller();
возвращает указатель на имя  (в  ASCII)  функции,  вызвавшей  текущую
функцию  (т.е.  на  имя   функции,   вызвавшей   функцию,   вызвавшую
caller()).  В  случае,  если  вызвавшая  (текущую  функцию)   функция
скомпилирована без указания  ключа  'P',  возвращается  указатель  на
пустую строку. Если функция caller() вызвана из  функции  main(),  то
она возвращает указатель на строку "$$init".

        file *outf;
        calltr(outf);
выводит в файл "outf" путь вызова (последовательность  имен  функций,
находящихся в цепочке вызова) начиная с функции main().  Путь  вызова
печатается в следующем виде:
        [ main sub1 sub2 ]
последним в пути вызова указано имя функции, вызвавшей calltr(). Если
функция, находящаяся в цепочке вызовов, скомпилирована  без  указания
ключа 'P', она будет  отображена  в  пути  вызова  как  ,  где
"NNNNN" - восьмеричный адрес входа в данную  функцию.  Если  функция,
стоящая  в  цепочке  вызовов,  не  использует  стандартные  для  "СИ"
функции сохранения и восстановления  регистров  (csv$()  и  cret$()),
т.е. написана на MACRO или на AS нестандартным  (для  "СИ")  образом,
то если эта функция не изменяет "R5", то она просто не будет видна  в
пути вызова, в противном случае результаты непредсказуемы  (вероятнее
всего  прерывание  программы  по   недопустимому   адресу).   функция
calltr() вызывается (с stderr в качестве аргумента), если  программа,
скомпилированная с  ключом  'P',  завершается  аварийно  или  вызовом
функции error().

        char *start;    /* начало дампа */
        char *end;      /* конец дампа */
        memdmp(start, end); /* вывод дампа */
        regdmp();       /* дамп регистров */
    memdmp() выводит непосредственно на  терминал  (по  .PRINT)  дамп
(восмеричное содержимое) области памяти от "start" до "end"  (точнее,
"start" и "end" округляются до чисел, кратных 0100, "start"  вниз,  а
"end" вверх). Если "start" равен 0, то печатается дамп стека  начиная
от его  положения  в  момент  вызова  memdmp()  и  до  его  исходного
положения (исходное положение берется из 042 ячейки).
   regdmp() выводит непосредственно на терминал дамп регистров.























                              - 26 -


          2. БИБЛИОТЕКА МАТЕМАТИЧЕСКИХ ФУНКЦИЙ

    Язык "СИ" не является языком, ориентированным  на  вычислительные
задачи,  но,  как  любой  язык  высокого  уровня,  имеет  пакет   для
вычисления  стандартных  математических  функций.  Все  функции   (за
исключением floor()  и  ceil())  возвращают  результат  типа  double,
агрументами всех функций могут быть величины как типа double,  так  и
float.
    В стандартном файле определений    описаны  (как  double)
функции данного пакета, а также определены (по #define) имена  "HUGE"
как максимальное вещественное число, представимое  на  данной  машине
(чуть больше 1.7e38), и  "LOGHUGE"  как  максимальный  порядок  (39).
Некоторые функции пакета возвращают код ошибки через errno ($$ferr).

        double  x;
        double  fabs(x);
абсолютное значение x.
        double  x;
        floor(x);
наибольшее целое, не превосходящее x.
        double  x;
        ceil(x);
наименьшее целое, превосходящее x.
        double  x, *fp;
        double  modf(x, *fp);
положительную дробную часть числа "wel" и пишет целую часть по адресу
fp. Функция находится в CXLIB.
        double  x;
        double exp(x);
экспоненциальная функция от x. Если  значение  функции  больше  HUGE,
возвращается HUGE и устанавливается ошибка "E_RAN".
        double x;
        double log(x), log10(x);
логарифм x - натурального и десятичного соответственно. Если аргумент
равен или меньше  нуля,  то  функции  возвращают  0  и  устанавливают
ошибку "E_DOM".
        double  x;
        double sqrt(x);
квадратный корня из x. ошибка как у log.
        double  x, y;
        double  pow(x, y);
x в степени y. Ошибка как exp. Если x меньше нуля и y не  целый,  или
если x и y равны 0, возвращается 0 и устанавливается ошибка E_DOM.
        double  x, y;
        double  hypot(x, y);
эффективно  и  точно  вычисляет  sqrt(x*x  +  y*y)  (т.е.,  например,
гипотенузу треугольника по двум катетам).  Контроля  переполнения  не
производится.
        typedef struct {
                double  re; /* веществ. часть */
                double  im; /* мнимая   часть */
        } complex;
        complex z;
        double  cabs(z);
абсолютное значение комплексного числа (определенного,  как  показано
выше), т.е. sqrt(re*re+im*im). Использует hypot().
        double fi;          /* угол в радианах */
        double
        sin(fi), cos(fi),tan(fi);
синус, косинус, тангенс. Если  величина  тангенса  превосходит  HUGE,
возвращается HUGE и устанавливается ошибка E_RAN.
        double  x;
если аргумент по абсолютной величине больше 1, функции возвращают 0 и
                              - 27 -


устанавливают ошибку E_DOM. Работают через atan().
        double  x, y;
        double  atan(x);
        double  atan2(y, x);
atan()  -  арктангенс  x.  Величина  результата  лежит  в   диапазоне
[-PI/2..PI/2].
atan2() - арктангенс  y/x.  Величина  результата  лежит  в  диапазоне
[-PI..PI] благодаря учету  квадранта  (если  считать  y  и  x  соотв.
координатами).

sinh, cosh - гиперболические синус и  косинус  при  переполнении  они
возвращают HUGE/2 и устанавливают ошибку E_RAN.
        double  x;
        double  tanh(x);
при больших (>21.) абсолютных значениях аргумента tanh() == sign.
        double  x;
        int     n;
        double  j0(x), j1(x), jn(n, x);
        double  y0(x), y1(x), yn(n, x);
j0(), j1(), jn() возвращают значение  функций  бесселя  первого  рода
порядков 0, 1 и n. Значения  функций  определены  для  всех  значений
аргумента,  ошибок  нет.  j0()  и  j1()  используют   sin(),   cos(),
sqrt().Функция jn() использует j0() и j1().

y0(), y1(), yn() возвращают значение  функций  бесселя  второго  рода
порядков  0,  1  и  n.  Значения  функций   определены   только   для
положительных значений аргумента -- если x <= 0, возвращают  -HUGE  и
устанавливают ошибку E_DOM. y0() I y1()  используют  log()  и  соотв.
функции j?().yn() использует y0() и y1().
        double  wel;
        int     *eptr;
        double  frexp(wel, eptr);
возвращает m-мантиссу числа wel (m < 1.)и записывает по  адресу  eprt
целое p, такое, что wel == m * 2**p. Находится в CXLIB.
        double  wel;
        int     p;
        double  ldexp(wel, p);
возвращает wel * (2**p). Находится в CXLIB.

























                              - 28 -


       3. ОПИСАНИЕ ГРАФБИБЛИОТЕКИ CGLIB.OBJ ДЛЯ ДВК-3. 

Данная библиотека написана Пудгородским  Юрием  на  почве  извращений
Яковлева С.С. на макроассемблере (библиотека MGLIB).
    Описание составил Яковлев С.С.
Last correction : Date 10/03/90  Time 00:01:48

    Для  подключения  данной  библиотеки  в  СИ  требуется  иметь   в
программе следующую строку:
    #include 
    Тем самым вы подключите файл  макроопределений,  в  котором  есть
следующие операции:
    grafon; /* включение графэкрана */
    grafoff; /* выключение графэкрана */
    alphaon; /* включение симв.экрана */
    alphaoff; /* выключение ---//---- */

    Кроме того, у вас появляется глобальная переменная int  color;  и
следующие константы:
    XMAX=399
    YMAX=285
    Она  определяет  тип  рисования  всех  графических  примитивов  и
принимает следующие значения:
    cmlor=black;     /* Рисует черным */
    color=white;     /* Белым */
    color=invers;    /* Инверсией */
    color=readcolor; /* Не рисует вообще, но выполняет все положенные
                        действия.    Используется    для    получения 
                        информации о действительном  цвете  точки  на 
                        экране */

Заливка всего экрана производится процедурой:
    erase(cwet);     /* cwet принимает значения white,black,invers */

Вызов процедуры "точка":
    plot(x,y);       /* рисует точку с координатами 0
        char *name; /* имя файла в ASCII */
        char *dext; /* расширение по умолчанию */
        dblk db; /* блок устройства - 4 слова в RAD50 */
        atodbl(name,dext,&db); 
Преобразует имя файла в 4 слова RAD50, размещенные в  структуре  типа
dblk. Возвращает 1, если преобразование прошло успешно, и 0, если имя
файла недопустимо.

        #include 
        char *arg;      /* исходное имя файла */
        filename *buff; /* структура для разобранного имени */
        i=breakout(arg,buff);
разбирает имя файла  из  arg  ("filnam.ext")  на  "filnam"  и  "ext",
которые помещает  в  структуру  типа  filename  (описана  в match.h).
Возвращает  "TRUE",  если   удачно   разобрала,   и   "FALSE",   если
недопустимое имя.

        int sysdat;
        char *buff;
        good=dattoa(sysdat,buff);
преобразует системную дату в формате RT11  (одно  слово,  биты  14-10
номер месяца  (1-12.),  9-5  день  (1-31.),  4-0  год  (ЬЬ-72.)  )  в
символьную строку вида DD-MMM-YY  (напр.  06-JAN-85)  и  заносит  эту
строку в buff. Возвращает:
        1 - все нормально
        0 - недопустимая дата

        #include 
        dblk *db;   /* указатель на блок устройства */
        char *buff; /* буфер для приема строки */
        dbltoa(db,buff);
обратная atodbl

        #include 
        FILE *iop;
        char *name; /* групповое имя файла */
        char *mode; /* метод открытия */
        iop=fwild(name,mode);
        iop=fnext(iop);
связывает структуру *iop с групповым именем файла, заданного в  name.
Групповое имя должно иметь вид [DEV:]FILNAM.EXT, причем части  FILNAM
и EXT кроме обычных символов могут содержать спецсимволы "*",  "?"  и
"%".

"*" может заменять любую (даже пустую) подстрочку, но не ".",  а  "?"
("%" является синонимом для "?") может заменять один непустой  символ
(но не ".").

mode - метод открытия. Допустим только "r", с атрибутами "u"  и  "n".

Если октрытие  группы  файлов  прошло   успешно,  fwild()  возвращает
указатель на структуру типа FILE, иначе 0  и  код  ошибки  в  $$ferr.
Даже  при  удачном  открытии  возвращенная  структура  не  связана  с
конкретным файлом (т.е.  операции  передачи  запрещены).  Фактическое
открытие файла производится по fnext().
fnext()  открывает  следующий  файл,   имя   которого   удовлетворяет
групповому имени, закрывая предыдущий, если он  был.  В  этом  случае
возвращает указатель на ту же структуру.  Если  больше  (или  вообще)
подходящих файлов нет, то возвращает 0, и  $$ferr==0.  Если  возникла
ошибка, то тоже возвращет 0, но при этом $$FERR содержит код ошибки.
                              - 31 -


Eсли fnext() возвратила 0, то структура *iop освобождена  -  fclose()
не требу- ется. Кроме того, в любой момент можно дать  fclose(iop)  -
для освобождения *iop и прекращения работы с данной группой файлов.

        char   *name;
        char    *pattern;
        i=match(name,pattern);
сравнивает имя файла (name) в  виде  FILNAM.EXT  с  групповым  именем
файла (pattren). Возвращает "TRUE" (1) если имя  файла  соответствует
групповому имени, и "FALSE" (0), если нет.

        char *name;
        char *pattern;
        i=match1(name,pattern);
сравнивает символьную строку с описателем (pattern). Возвращает  TRUE
(1) если подходит под описатель, и FALSE (0), если не подходит.

        #include 
        int *rtdir; /* указатель на структуру
                       описания текущего состояния */
        int devnam; /* имя устройства (в rad50) */
        rtdir=diropen(devnam);
        nfile nfp; /* структура, через которую
                      возвращается информация о файлах */
        i=dirnext(rtdir,&nfp);
  diropen(devnam)  открывает  каталог  устройства   прямого   доступа
формата  RT11,  резервирует  и   заполняет   структуру   описания   и
возвращает указатель на нее. В  случае  ошибки  возвращает  0  и  код
ошибки в $$FERR.
  dirnext(rtdir,&nfl) возвращает информацию  о  следующем  постоянном
файле в каталоге, который (каталог) был открыт по diropen().

rtdir - адрес структуры, возвращенный diropen().
nfl - структура типа nfile (определен в rtio.h):

возвращает:
        1 - информация о следующем файле помещена в nfile.
        0 - каталог исчерпан или ошибка. См. $$FERR - там  код ошибки
            (0 при успешном окончании)
























                              - 32 -


      5. ЯЗЫК СИ. РУКОВОДСТВО ОПЕРАТОРА.

    Компилятор языка "СИ" (используемый компилятором ассемблер АС  мы
считаем входящим в компилятор) может функционировать под  управлением
всех мониторов ОС RT11. Для работы с программным комплексом  на  эвм,
работающей под управлением одного из мониторов  ОС  RT11,  необходимо
не менее 41 кбайт оперативной памяти и  около  610  блоков  дискового
пространства   для   размещения   загрузочных   файлов   компилятора,
стандартных  файлов  описаний  и  обьектных  файлов  библиотек   (см.
приложение 3). Необходимо отметить,  что  программы,  полученные  при
помощи  данного  программного  комплекса,   могут   выполняться   под
управлением  любого  из  мониторов  ОС  RT11,  а  при   использовании
библиотеки автономной поддержки и автономно, на ЭВМ без  операционной
системы.

          5.1. СТАДИИ ПОДГОТОВКИ ПРОГРАММЫ

Компиляция, компоновка  и  запуск  программы  (считается,  что  текст
пограммы   уже   подготовлен).   Компиляция   начинается   с   вызова
компилятора (CC.SAV) и передачи  ему  командной  строки,  описывающей
входной  файл,  и,  возможно,  выходной  файл  и  ключи,   уточняющие
действия, требуемые от  компилятора.  Компилятор  в  процессе  работы
создает  обьектные  файлы,  которые  перед  выполнением  должны  быть
скомпонованы  с  библиотеками,  и,   возможно,   другими   обьектными
файлами. Более строго так: компилятор  создает  файлы  на  ассемблере
(язык данного ассемблера отличается  от  MACRO-11  и  похож  на  язык
ассемблера ОС "UNIX"). Ассемблер (AS.SAV) обрабатывает  эти  файлы  и
создает обьектные файлы в  формате  ОС  RT11.  Ассемблер  запускается
после компилятора автоматически (если  в  командной  строке  явно  не
указано     обратное).     Компоновка      выполняется      системной
программой-компоновщиком.

После компоновки программа может быть запущена  командами  "RUN"  или
"R".  Запущенная  программа  ожидает  (если  это  явно  не  отменено)
командную строку,после ввода которой  исполняющая  система  эмулирует
стартовые  действия   интерпретатора   "SHELL"   ОС   UNIX,   включая
перенаправление стандартного ввода/вывода (см. ниже).

Компилятор запускается командой
  RUN DEV:CC

Перед запуском компилятора необходимо присвоить логическое  имя  "C:"
устройству, на котором  размещен  ассемблер  (AS.SAV)  и  стандартные
файлы описаний (файлы с расширением ".H",  после  запуска  компилятор
выводит  подсказку  "#  "   и   ожидает   ввода   командной   строки.

Командная строка должна иметь вид

[-CEFJKLMNPRSTXZ] [-IDEV] [-DSYM[=VAL]] [-USYM] [-O OUTFILE] INFILE

,где  квадратные  скобки  ограничивают,  как  обычно,  необязательные
аргументы. иначе говоря, командная строка состоит из:  необязательных
ключей  без  значений;  строчке  ключей  должен  предшествовать   без
пробела  символ   '-'.   может   быть   несколько   строчек   ключей.
Необязательных  ключей  со  значениями.   Значение   ключа   (строчка
символов) должно следовать за  ключом  без  пробела.  Необязательного
ключа определения выходного  файла  ('O')  и  спецификации  выходного
файла (в следующем аргументе). Обязательного  имени  входного  файла.
Аргументы в командной строке разделяются пробелами  или  табуляциями,
порядок элементов в командной строке некритичен. Ключи со  значениями
и ключ 'O' могут входить в общую строчку ключей (ключи со  значениями
-  только  последними).  Имя  входного  файла  (INFILE)  ожидается  в
формате CSI, тип по умолчанию .C (или .S  при  одновременном  наличии
                              - 33 -


ключей 'M' и 'C', см.  ниже).  В  имени  входного  файла  может  быть
указана длина. Эта длина используется при  открытии  промежуточных  и
выходного файлов, см. "проблемы промежуточных файлов". При задании  в
командной строке только имени входного  файла  при  трансляции  будут
выполнены  стадии  препроцессора  (CPP),  компилятора  (C0,   C1)   и
ассемблера (AS). Выходной файл будет создан  на  устройстве  "DK:"  с
именем входного файла и типом ".OBJ".

 Примеры командных строк:

 MYPROG - выполнить трансляцию файла MYPROG.C в файл MYPROG.OBJ.

 -LXTO RK3:BBB
 - Выполнить  трансляцию  файла  DK:BBB.C  в   файл   RK3:BBB.OBJ   с
оптимизацией  ('X'),  выдачей   статистики   оптимизатора   ('T')   и
сообщениями об окончании проходов ('L').

 -LX BBB -O RK3:-T то же, что и в пердыдущем  примере,  но  с  другим
расположением ключей - иллюстрирует возможности  расположения  ключей
в командной  строке.  при  помощи  ключей  можно  уточнить  действия,
требуемые при трансляции:

 C - подавляет стадию компилятора (остаются  стадии  препроцессора  и
     ассемблера).
 E - если явно (в имени входного файла) не  была  указана  длина,  то
     для промежуточных файлов (и для выходного, если длина  выходного
     файла явно не указана) используется длина "[-1]". См.  "Проблемы
     промежуточных файлов".
 F - приводит к  включению  (при  компоновке)  в  загрузочный  модуль
     эмулятора команд FPU. См. "Эмуляция FPU".
 J - подавляет включение эмулятора FPU в загрузочный модуль.
 K - подавляет инициализацию FPU.
 L - включает печать сообщений об окончании каждой стадии компиляции.
     сообщения имеют вид "XX COMPLETE", где XX - имя стадии:
  CPP - стадия препроцессора
  C0  - первый проход стадии компилятора
  C1  - второй проход стадии компилятора
  C2  - стадия оптимизатора
  AS  - стадия ассемблера
 
 M - подавляет стадию препроцессора.
 N - запрещает стирание промежуточных файлов.
 P - приводит к включению  в  генерируемый  код  команд,  позволяющих
     получить  дополнительную  информацию  о  работе  программ.   Это
     полезно при отладке и оценке производительности.  См.  "Средства
     отладки".
 R - разрешает рекурсию в препроцессоре.
 S - подавляет стадию ассемблера.
 T - включает  выдачу  статистики  оптимизатора  (C2).  Имеет   смысл
     только совместно с ключом '-X'. См. ниже.
 X - включает стадию оптимизатора (C2). См. "Оптимизация".
 Z - подавляет включение функций форматного вывода чисел с  плавающей
     запятой в загрузочный модуль  при  компоновке  IDEV  -  включает
     устройство "DEV:" в число  устройств,  на  которых  препроцессор
     производит поиск файлов, указанных в качестве аргумента операто-
ра "#include".  Имя  устроиства  не  должно  содержать  ':', например
"-IRK1". При поиске файлов, указанных  в "#include",  последовательно
просматривает устройства по следующему списку: #include "file" с уст-
ройства SR:, откуда вводится текущий входной файл. Затем  -  SR:=DEV:
#include    с   устройства   SR:=C:.
                              - 34 -


DSYM DSYM=VAL -  включает  символ  "SYM"  в  число  определенных  (по
"#define")  в  препроцессоре  символов;  символ   получает   значение
(текстовое) VAL. Если конструкция "=VAL" отсутствует, то  1.  USYM  -
отменяет  в  препроцессоре  определение   предопределенного   символа
"SYM".
 В препроцессоре описываемого компилятора предопределены два символа: 
rt11 и pdp11 как 1. Если один и тот же символ задан в ключах  D  и  U
одновременно, то символ будет  неопределен.  O  OUTFILE  -  позволяет
явно задать имя выходного файла. По умолчанию DK: имя входного и  тип
в зависимости  от  ключей:  '+'  обозначает  присутствие  ключа,  '-'
- отсутствие, 'x' - безразлично:
   +-------------------+
   ! ключи ! тип       !
   +-----------+ вых.  !
   ! M ! C ! S ! файла !
   +---+---+---+-------+
   ! X ! X ! - ! .OBJ  !
   +---+---+---+-------+
   ! X ! - ! + ! .S    !
   +---+---+---+-------+
   ! - ! + ! + ! .I    !
   +---+---+---+-------+

Необходимо  отметить,  что  если   заданное   имя   выходного   файла
синтаксически  верно,  однако  открыть   выходной   файл   невозможно
(например, указано  несуществующее  устройство  или  слишком  большая
длина), то ошибка открытия будет зафиксирована  только  на  последней
стадии компиляции, т.е. тогда,  когда  будет  фактически  выполняться
открытие выходного файла.

          5.2. ПРОБЛЕМЫ  ПРОМЕЖУТОЧНЫХ  ФАЙЛОВ

Данный  программный  комплекс  использует  промежуточные  файлы   для
Pередачи  информации  между  стадиями  и   между   проходами   стадии
компилятора.  Для   более   рационального   использования   дискового
пространства предприняты следующие меры:

      - компилятор одновременно  открывает  для  вывода  только  один
файл (промежуточный или выходной);
      - может быть указано устройство  для  размещения  промежуточных
файлов путем назначения логических имен "WF:" или "TMP:"
(в  таблице  '-'  означает,  что  имя  неопределено, '+' - определено,
'X' - безразлично).
  +--------------------------------+
  ! логические имена  ! устройство !
  +-------------------+ для врем.  !
  !   TMP:  !    WF:  ! файлов     !
  +---------+---------+------------+
  !    -    !    -    !     DK:    !
  +---------+---------+------------+
  !    -    !    +    !     WF:    !
  +---------+---------+------------+
  !    +    !    X    !    TMP:    !
  +---------+---------+------------+
Два логических имени, выполняющих  одинаковые  функции,  введены  для
соответствия соглашениям на имя устройства для временных  файлов  как
ОС UNIX (лог. имя "TMP:"), так и ОС RT11 (лог. имя  "WF:").  Возможно
явное указание длины пространства, которое будет резервироваться  при
открытии всех промежуточных и выходного файлов:  длина  пространства,
резервируемого при открытии промежуточных файлов, может  определяться
явным указанием длины в спецификации  входного  файла  и  при  помощи
ключа '-Е'; длина пространства, резервируемого для  выходного  файла,
может дополнительно регулироваться явным  указанием  длины  выходного
                              - 35 -


файла. Если нет ни явного  указания  длины  в  спецификации  входного
файла, ни ключа '-Е', то  все  промежуточные  файлы  открываются  без
указания длины. Если есть явное указание  длины  входного  файла,  то
эта длина используется при открытии всех промежуточных  файлов.  Если
явного  указания  длины  в  спецификации   входного   файла   нет   и
присутствует ключ '-Е', то для  открытия  всех  промежуточных  файлов
используется длина [-1] (т.е.  резервируется  максимальное  возможное
пространство). При  наличии  явного  указания  длины  в  спецификации
входного файла ключ  -Е  игнорируется.  Если  спецификация  выходного
файла отсутствует или  в  спецификации  выходного  файла  отсутствует
длина, то при открытии выходного файла используется та же длина,  что
и при открытии промежуточных файлов, в противном случае  используется
указанная  длина.  Обычно  промежуточные  файлы  стираются  как   при
успешном  завершении,  так  и  при  обнаружении  ошибок.   Исключение
составляет ассемблер, который при обнаружении ошибок  оставляет  свои
входной  и  выходной  файлы.  Ключом  'Н'  можно  запретить  стирание
промежуточных файлов. Их типы следующие:
 .TMP - выход стадии препроцессора (CPP).
 .ТМ1 - выход первого прохода стадии компилятора (C0).
 .S   - выход   второго   прохода   стадии   компилятора   и   стадии
        оптимизатора (C1 и C2).

          5.3. ЭМУЛЯЦИЯ EIS

    Данный компилятор генерирует прямой (INLINE) код  с  инструкциями
EIS.  для  выполнения   программ,   полученных   при   помощи   этого
компилятора на ЭВМ, не имеющего аппаратного  EIS  (например,  на  ЭВМ
"Электроника НЦ80-01Д"), необходима программная  эмуляция  инструкций
ЕИС.  Эмулятор  EIS  может   быть   включен   при   компоновке   (при
использовании головного модуля SPNHD, а не SPEIS).

    Эмулятор EIS  эмулирует  команды  "MUL",  "DIV",  "ASH",  "ASHC".
Размер эмулятора около 3-х блоков (1,5 Кб).  Эмулятор  выполняет  (на
ЭВМ  "Электроника  НЦ-80")  команду  "MUL"  примерно  1.2  мс  против
примерно 30 мкс для  СМ-4,  т.е.  примерно  в  40  раз  медленнее,  и
команду "DIV" примерно 4,2 мс против 30 мкс, т.е примерно в  140  раз
медленнее.  Компилятор  поставляется  с  включенным  эмулятором  EIS.

          5.4. ВЫЧИСЛЕНИЯ С ПЛАВАЮЩЕЙ ЗАПЯТОЙ

    Компилятор генерирует прямой  (INLINE)  код  с  инструкциями  FPU
("Floating Point Unit"),  что  позволяет  получить  высокую  скорость
выполнения  программ  на  ЭВМ,  имеющих  соответствующее   аппаратное
обеспечение. Для выполнения  программ, полученных  при  помощи  этого
компилятора и  использующих  вычисления  с  ПЗ  на  ЭВМ,  не  имеющих
аппаратного FPU, необходима програмная эмуляция инструкций FPU.

    Будет ли включен эмулятор FPU, определяется наличием  операций  с
ПЗ  в   транслируемой   программе   и   ключами   командной   строки:

Если присутствует ключ '-F', то эмулятор FPU  безусловно  включается.
Если  присутствует  ключ  '-J',  то  эмулятор   FPU   безусловно   не
включается. При отсутствии  ключей  'F'  и  'J'  эмулятор  включается
только при наличии в компилируемой программе операций с ПЗ.

    Размер эмулятора около 6 блоков (3  Кбайт).  Скорость  вычислений
на СМ-4 (с эмулятором, в РАФОС/TS (TSX-PLUS)) - примерно 100  делений
с  двойной  точностью  в  секунду,  причем  на  системную   обработку
(прерываний)  тратится  примерно  10%   времени,   и   примерно   90%
собственно на работу эмулятора. Вычисления с  эмулятором  выполняются
примерно в 8 раз быстрее, чем в UNIX V7  (из-за  малой  эффективности
системной обработки ОС UNIX), и примерно  в  3  раза  медленнее,  чем
выполняет  подобные  вычисления  FORTRAN-IV  с  FIS  на  СМ-4  в   ОС
                              - 36 -


РАФОС/TS. Эмулятор не использует и не эмулирует  команд  FIS  и  EIS.
Включение эмулятора происходит при компоновке и управляется  наличием
глобальной  ссылки  "$$fpem",  которая  вставляется  в  программу  на
стадии компиляции (C1).  Компилятор  использует  вычисления  с  ПЗ  и
поставляется с включенным FPU.

          5.5. ИНИЦИАЛИЗАЦИЯ FPU

    Перед   использованием    команд    FPU    необходимо    провести
инициализацию и установку режима процессора FPU  -  как  аппаратного,
так и реализованного  эмулятором.  Обычно  инициализация  выполняется
при старте программы, в том случае,  если  в  программе  используются
операции с ПЗ. Инициализацию можно  безусловно  отключить,  используя
ключ 'К'.  Это  полезно  при  компиляции  функций,  которые  содержат
операции  с  ПЗ,  но  могут  быть  использованы   в   чисто   "целых"
приложениях, в которых  операции  с  ПЗ  не  используются  (пример  -
функция  _doscan()  из  стандартной  библиотеки,   которая   содержит
команды преобразования для чисел с ПЗ, однако часто  он  используется
в  программах,  не  использующих   вещественных   чисел).   Включение
инициализации FPU (она выполняется  в  модуле  $$init())  управляется
наличием глобальной ссылки "fpin$$", которая вставляется в  программу
на стадии компилятора (C1).  

          5.6. ФОРМАТНЫЙ  ВЫВОД  ЧИСЕЛ  С  ПЛАВАЮЩЕЙ ЗАПЯТОЙ

    Поскольку арифметика с  ПЗ  используется  во  многих  приложениях
языка "СИ" не так  уж  часто,  существует  механизм  исключения  (при
компоновке) функций  форматного  вывода  чисел  с  ПЗ.  По  умолчанию
включение  функций  форматного  вывода  чисел  с  ПЗ  происходит  при
наличии в программе операций с  ПЗ,  однако  включение  этих  функций
может быть подавлено указанием в командной  строке  ключа  '-Z'.  Это
может  быть  полезно  для   экономии   памяти,   если   в   программе
используются операции с ПЗ, а вывод чисел с ПЗ не используется.  Если
функции не включены, а вывод чисел  с  ПЗ  используется,  то  в  поле
числа будет выведен один символ'?' и необходимое для заполнения  поля
число заполнителей. Размер подпрограмм форматного вывода чисел  с  ПЗ
около 2 блоков (1 Кбайт).
    Включение функций форматного вывода чисел  с  ПЗ  происходит  при
компоновкe  и  управляется  наличием  глобальной   ссылки   "$$fppr",
которая  (при  необходимости)  включается  в  программу   на   стадии
компиляции (C1).

          5.7. ОПТИМИЗАЦИЯ

    Компилятор  выполняет  различные  оптимизационные  преобразования
программы, в основном касающиеся оптимизации выражений.  Кроме  того,
ключом 'X' может быть  включена  стадия  оптимизатора  (C2),  которая
будет  выполняться  после  стадии  компилятора.  Стадия  оптимизатора
увеличивает  общее  время  компиляции  приблизительно  на  20%   (при
наличии всех других  стадий)  и  приводит,  в-среднем,  к  уменьшению
обьема результирующей программы и увеличению скорости  ее  выполнения
на 3-5% (числа относятся и к скорости, и к обьему). При помощи  ключа
'Т' можно  запросить  статистику  стадии  оптимизатора.  Особенностью
работы  оптимизатора  является  то,  что  при  обработке  функции  он
помещает в оперативную память весь ее (ассемблерный)  текст,  поэтому
для  больших  функций  оптимизатору  может  не  хватить   оперативной
памяти. Выход в такой ситуации - разбивать программу на более  мелкие
функции.





                              - 37 -


          5.8. КОМПОНОВКА

    После  компиляции  полученные   обьектные   файлы   должны   быть
скомпонованы  с  библиотеками  (и  друг   с   другом).   Поставляются
следующие модули и библиотеки: 
 SPEIS.OBJ  Головной модуль для ЭВМ с EIS. 
 SPNHD.OBJ  Головной модуль для ЭВМ без EIS (включение эмулятора ЕИС).  
 CXLIB.OBJ  Стандартная библиотека.    
 INITNF.OBJ Модуль инициализации без эмуляции запроса # ОС UNIX.  
 NFTIO.OBJ  Модуль нефайлового терминального ввода-вывода.   
 CRLIB.OBJ  Библиотека системных функций ОС RT11.  
 CTLIB.OBJ  Библиотека дополнительных системных функций TS монитора.
 CMLIB.OBJ  Библиотека стандартных математических функций. 
 ASPEIS.OBJ, ASPNHD.OBJ, CALIB.OBJ  Головные  модули  для  автономных 
            программ и библиотека автономной поддержки.
 ANTIO.OBJ  Модуль/заглушка для отключения терминального ввода/вывода
в автономных программах.

 LINK PROG,C:SPEIS,C:CXLIB/BOTTOM:2000 или
 R LINK
 PROG=PROG,C:SPEIS,CXLIB/B:2000
 ^C
  В общем случае файлы при компоновке должны указываться в  следующем 
порядке:

1.Объектные файлы программы пользователя.
2.Модули нестандартной конфигурации  исполняющей  системы  (если  они
необходимы): INITNF.OBJ, NFTIO.OBJ, AFTIO.OBJ.
3.Головной модуль SPEIS или SPNHD
    Головной  модуль  не  включен  в  стандартную  библиотеку,   т.к.
некоторые   старые   версии   компоновщика   ОС   RT11   отказываются
компоновать программу с правильным стартовым адресом, если модуль,  с
которого  производится  запуск  программы,  находится  в  библиотеке.
4.дополнительные библиотеки (CRLIB,  CTLIB,  CMLIB  и  др.,  а  также
библиотеки пользователя).
5.Стандартная библиотека CXLIB.

Ключ "BOTTOM" в командной строке определяет (octal)  верхнюю  границу
пространства, отводимого  под  стек  (по  умолчанию  верхняя  граница
равна 01000). Причина необходимости увеличения  отводимого  под  стек
пространства  в  том, что программы,  написанные  на  "СИ",  в  стеке
размещают  свои  локальные   (автоматические)   переменные.   Простые
программы могут работать и с обычным стеком (01000),  а  для  сложных
программ (с большим обьемом автоматических переменных или  с  большой
глубиной  вложенности   вызовов)   для   стека   может   понадобиться
пространство с верхней  границей  больше  02000.  Переполнение  стека
приводит, как правило, к фатальным системным  ошибкам  типа  и  может
нарушить  работу  операционной  системы  (если  используется  не   TS
монитор).   Рекомендуется   при   отладке   использовать   ключ   'P'
компилятора,    см.    "Средства    отладки".    При    этом    будет
диагностироваться опускание стека ниже 0600.

          5.9. СОЗДАНИЕ АВТОНОМНЫХ ПРОГРАММ

    Для  этого  программа  должна  быть  скомпонована  с  библиотекой
CALIB  вместо  CXLIB,  и   в   качестве   головных   модулей   должны
использоваться   ASPEIS   или   ASPNHD   вместо   SPEIS    и    SPNHD
соответственно. Пакет автономной поддержки,  реализованный  в  CALIB,
поддерживает системный терминал, включенный на стандартные  адреса  и
системный таймер (нестандартным образом). Интерфейс с любыми  другими
устройствами должен реализовываться в прикладной программе.
    Должен ограничиваться набор  используемых  библиотечных  функций:
запрещается  использовать  многие  функции  ввода-вывода  и  отладки,
                              - 38 -


запрещается использовать любые функции библиотек CRLIB и CTLIB.
    При компоновке программа может быть  получена  в  формате  "LDA",
что позволяет загружать ее для исполнения  с  перфоленты  при  помощи
стандартного  абсолютного  загрузчика.  Для  получения  программы   в
формате "LDA" необходимо указать ключ "LDA" в команде LINK

 LINK/LDA PROG,C:ASPNHD,C:CALIB/BOTTOM:2000 или
 R LINK
 PROG=PROG,C:ASPNHD,CALIB/L/B:2000
 ^C


          5.10. СООБЩЕНИЯ ОПЕРАТОРУ

    В  описании  после  каждого  сообщения  через  дефис  в   скобках
указана стадия компиляции,на которой генерируется  данное  сообщение.
Обозначения следующие:

 CC - головная стадия
 CPP - стадия препроцессора
 C0 - первый проход стадии компиляции
 C1 - второй проход стадии компиляции
 C2 - стадия оптимизации
 AS - стадия ассемблера

    Сообщениям, выданным на стадиях "CPP"  и  "C0"  предшествует  имя
файла и номер строки в файле, разделенные между  собой  и  отделенные
от сообщения символами  ':'.  Сообщениям,  выданным  на  стадии  "C1"
предшествует номер строки в файле, отделенный от  сообщения  символом
':'. Сообщения выводятся  с  использованием  букв  нижнего  регистра,
которые  будут  отображаться   как   русские   буквы   на   некоторых
терминалах.
    Сообщения приведены в  алфавитном  пордке,  причем  имя  файла  и
номер строки, которые могут предшествовать сообщению  (см.  выше)  не
учитываются. Малые и большие буквы в  описании  не  различаются,  имя
стадии компиляции в описании представлено как "XX",  имя  какого-либо
обьекта (например  переменной)  -  как  "NAME",  причем  заключено  в
кавычки.

























                              - 39 -


        6. ДОПОЛНЕНИЕ К ОПИСАНИЮ КОМПЛЕКТА СИ. 

Составлено Яковлевым С.С.  с  непосредственным  и  активным  участием
Пудгородского  Юрия.  Значительную  помощь  оказал  Игорь  Борисенко.
Данный комплект компилятора  СИ  отличается  некоторыми  приятными  и
неприятными особенностями:

  Комплект для повседневной работы состоит из двух дискет:
 14-Jan-89
 Volume ID: Система СИ
 Owner    : Яковлев С.
UCL   .SAV     4  04-Jan-89     8   UCL   .DAT     1  11-Jan-89    12
RT11CD.SYS    70  23-Aug-86    13   SWAP  .SYS     2  04-Jan-89    83
TT    .SYS     2  04-Jan-89    85   DE    .SYS     2  07-Jan-89    87
SL    .SYS    10  04-Jan-89    89   MX    .SYS    10  04-Jan-89    99
LP    .SYS     2  04-Jan-89   109   DIR   .SAV     6  24-Mar-88   111
NED   .SAV    13  04-Jan-89   117   LINK  .SAV    28  21-Jul-88   130
INITNF.OBJ     2  10-Feb-86   158   NFTIO .OBJ     2  16-Jan-86   160
SYSLIB.OBJ   162  04-Jan-89   162   SPEIS .OBJ     2  16-Jan-86   324
PIP   .SAV    25  21-Jul-88   326   DUP   .SAV    44  21-Jul-88   351
LIBR  .SAV    17  21-Jul-88   395   DAY   .SAV     4  21-Jul-88   412
DESS  .SAV    18  21-Jul-88   416   CGLIB .OBJ    17  06-Jan-89   434
 22 Files, 443 Blocks
 0 Free blocks

 14-Jan-89
 Volume ID: Рабочая СИ
 Owner    : Яковлев С.
CC    .SAV   227P 17-Jan-86     8   AS    .SAV    65P 29-Apr-85   235
STDIO .H       2  05-Sep-88   300   GRAFIC.H       3  14-Jan-89   302
MATH  .H       1  11-Jan-89   305   SETJMP.H       1  11-Jan-89   306
CTYPE .H       2  12-Jan-89   307   RDIR  .H       1  13-Jan-89   309
RDSTAT.H       1  13-Jan-89   310   RCSISP.H       1  13-Jan-89   311
RCSTAT.H       1  13-Jan-89   312   ERRNO .H       1  13-Jan-89   313
DBLK  .H       1  13-Jan-89   314   < UNUSED >   136              315
 13 Files, 307 Blocks
136 Free blocks

    В таком комплекте вы располагаете всеми возможностями стандартной
библиотеки  ввода-вывода  СИ.  Кроме  того,  в   SY:SYSLIB   включена
библиотека  математических  функций,  библиотека  функций  ОС   RT11,
преобразования чисел в  строковое  представление  и  обратно  (помимо
форматного ввода-вывода).
    Однако исключены:  профиллер,  дамп  регистров  и  памяти,  вызов
функций с интерфейсом ФОРТРАНа, обращение к функции времени в  форма-
те ОС UNIX.
    Вы располагаете командой опреационной  системы,  реализованной  в
UCL.DAT:
 CC/EDIT/COM/FPU/NFT/ASSEM/LINK/PROM/RUN/INC:<биб-ка> <имя_программы>
(здесь указаны все  возможные ключи команды)
CC <имя_программы>   компилирует текст до обьектного файла
/EDIT                Запускает предварительно редактор NED
/COMPILE             Компилирует только до ассемблерного текста
/FPU                 не включает эмуляцию ФПУ, включает иниц.ФПУ
/NFTIO               На этапе компоновки - только нефайловый вв/выв.
/ASSEMBLER           Ассемблирует ассемблерный текст.
/LINK                Запускает компоновку
/PROMPT              Этот ключ передается компоновщику
/RUN                 Запускает программу после компоновки
/INCLUDE:            Сюда можно написать дополнительную библиотеку.

    Наличие   мощного    оптимизатора.    Сравнение    быстродействия
графбиблиотек,  написанных  на  МАКРО  и  на  СИ  по  очень   близким
                              - 40 -


алгоритмам, показало его эффективность - скорость заливки  экрана  по
одной точке оказалась равной, а скорость заполнения экрана  наклонной
линией на СИ была  чуть-чуть  больше  из-за  не  совсем  продуманного
механизма передачи параметров  в  макровской  графбиблиотеке.  Анализ
странслированной  программы  отладчиком  позволил   утверждать,   что
опытный программист на  МАКРО  напишет  ненамного  более  эффективную
программу, чем столь же опытный программист на СИ с оптимизатором.
    Однако  время,  затраченное   на   отладку   (даже   у   опытного
программиста), на МАКРО на порядок больше.
    Высокая скорость трансляции, особенно - этапа ассемблирования  по
сравнению с версиями, транслирующими в текст на МАКРО.
    Возможность  размещать  промежуточные   файлы   даже   на   таком
маленьком  электронном  диске,  как  у  ДВК-3  (30   блоков).   Обьем
программы, которую можно целиком (от текста до  загрузочного  модуля)
хранить на электронном диске, составляет  примерно  1  экран  текста.
Размера DE: для промежуточных файлов хватает для программ примерно  в
2-3 страницы на принтере, или порядка 200 строк.
    Незначительность отличий  от  стандарта.  Для  работы  на  данном
компиляторе можно практически  без  поправок  пользоваться  печатными
справочными руководствами  (Болски,  "Справочник  для  программиста";
Керниган, Ричи, и многие другие).
    Возможность значительно варьировать стандартное обрамление программ:
включать и выключать:
    файловый ввод-вывод;
    стандартный запрос #;
    форматный ввод-вывод;
    При этом соответствуюие модули из библиотек подключаются  или  не
подключаются, что позволяет создавать программы малого обьема.
    Тип  unsigned  допустим  только  для  переменных   типа   int   и
недопустим для char и long.
    Тип функции void компилятор считает недопустимым. Однако  если  в
теле  функции   отсутствует   оператор   return();,   это   полностью
эквивалентно типу void.  Поэтому  можно  ради  совместимости  вводить
строку:
    #define void int
    Ни один пробегавший мимо  компилятор  СИ  (также  и  на  IBM  PC,
LABTAM)   не    позволяет    нормально    обрабатывать    рекурсивные
макроопределения. Поскольку это  нигде  не  оговаривается,  мы  сочли
нужным отметить этот факт.
    Обнаружен ГЛЮК оптимизатора: если вы определили массив
    static char *txt[]={"rrwsowps","olorol","1ororo"};
то можете схлопотать при компоновке неопределенные глобальные  метки.
Оптимизатор умудряется удалить  метку  в  секции  .data.,  с  которой
начинается массив  указателей  на  строки.  Существуют  условия,  при
которых все проходит нормально, однако  единственный  надежный  метод
борьбы с данной погрешностью - обьявить массивы указателей на  тексты
глобальными,  вынести  их  в  отдельный  файл  и  скомпилировать  без
оптимизатора (без ключей T,X).
    Просим сообщать нам обо всех  отмеченных  погрешностях  работы  с
плавающей запятой! При компилировании программ без  эмуляции  ФПУ  на
Электронике-85 мы получили удовольствие от  высокой  скорости  работы
плавающей арифметики, но столкнулись с  ее  неадекватным  поведением.
Пока ничего определенного сказать нельзя.
    Весьма приятными оказались сообщения компилятора:
.....Warning: "*=" operator assumed
    Это означает, что если вы пишете оператор присваивания  и  правая
его часть начинается со знака действия  или  операций  взятия  адреса
или значения по  адресу,  для  получения  адекватного  результата  вы
должны или заключить правую часть  в  скобки,  или  поставить  пробел
после знака равенства.
   И вообще - не жалейте скобок!


                              - 41 -


    После старта программы, если явно не запрещено, выполняется  ввод
и разбор  командной  строки  для  программы.  при  разборе  командной
строки производится эмуляция действий интерпретатора  команд  "shell"
ОС UNIX: создается массив аргументов для передачи функции  "main"  и,
при необходимости, происходит перенаправление ввода/вывода.

          6.1. ВВОД И ОБРАБОТКА КОМАНДНОЙ СТРОКИ

    Командная  строка  в  данном   контексте   -   строка   символов,
завершенная символами <ВК>  или  <ПС>.  В  командной  строке  символы
пробела  являются  разделителями  аргументов.  Аргументом   считается
последовательность   символов,   не    включающая    пробелов,    или
последовательность произвольных  символов,  заключенная  в  одинарные
или двойные кавычки, например, строка:
        param1 param2 "a a a" '"e"'
содержит 4 аргумента:
        param1
        param2
        a a a
        "e"
    Если программа запущена из командного файла, то командная  строка
тоже будет взята из  командного  файла.  Если  программа  запущена  с
терминала, то она выведет  на  терминал  приглашение  "#  "  и  будет
ожидать ввода  командной  строки.  приглашение  для  ввода  командной
строки можно изменить, см. ниже.
    Если явно не запрещено (см.  ниже),  то  перед  вводом  командной
строки разрешается ввод символов  нижнего  регистра.  Существует  еще
одна возможность передать программе команднуЮ строку: если  программа
запущена макрокомандой ". CHAIN" из другой  программы,  то  программа
вводит команднуЮ строку из области связи (начиная с адреса 0512 и  до
первого  байта  '\0').  При  разборе  командной   строки   выделяЮтся
аргументы, и информация о них передается  главной  функции  программы
пользователя "main()" при  помощи  двух  параметров,  которые  обычно
называЮт "argc" и "argv". "argc" имеет тип  "int"  и  содержит  число
аргументов,  переданных  функции,  "argv"  имеет  тип  "char  **"   и
является  указателем  на  массив  указателей  на  аргументы   (каждый
аргумент при разборе завершается символом '\0'). Для совместимости  с
ОС UNIX функции "main()" передается на один аргумент больше,  чем  их
(аргументов) выделено из командной  строки.  Дополнительный  аргумент
является первым и в данной системе он содержит строку  "XXXX".  В  ОС
UNIX этот аргумент  формируется  интерпретатором  команд  "shell"  и,
Jобычно, содержит имя программы.  Благодаря  наличию  дополнительного
аргумента параметр "argc" не может быть  меньше  1.  Приведем  пример
программы, печатающей свои аргументы:
    main(argc, argv)
    {
    int argc;
    char* argv[];
    int i;
    printf("программа имеет %d аргументов:\n",
                                          argc);
    for(i=0; i < argc; i++)
        printf("ARG %d = \"%s\"\n", i, argv{i});
    } при вызове этой программы с командной строкой
        abc "def ghi" 122 'm m' получим:
    программа имеет 5 аргументов:
    ARG 0 = "XXXX"
    ARG 1 = "abc"
    ARG 2 = "def ghi"
    ARG 3 = "122"
    ARG 4 = "m m"
    Командная  строка  может  содержать  аргументы  вида  "file",  т.е.  аргументы,  состоящие  из  символа  '<'  или  '>'   и
                              - 42 -


следующей за этим символом без пробела спецификации файла  в  формате
CSI. Такие аргументы  не  передаются  функции  "main()",  а  вызывают
перенаправление  ввода/вывода.  Вот  что  это  означает:  при  старте
программы открываются три файла:
        stdin  - файл стандартного ввода
        stdout - файл стандартного вывода
        stderr - файл стандартного вывода ошибок
    Все эти файлы по умолчанию связаны  с  терминалом,  однако  файлы
стандартного ввода и  вывода  могут  быть  перенаправлены  на  другие
устройства или файлы  при  помощи  специальных  аргументов  командной
строки:
    Если в командной строке присутствует аргумент  вида  "file" "stdout"  будет  связан
с файлом "file". рассмотрим следующую программу:
    # include 
    main()
    {
        register c;

        while ((c = getchar()) != eof) putchar(c);
    }
    Если командная строка пуста, то эта программа  вводит  символы  с
терминала и выводит их обратно на терминал, однако,  задав  командную
строку  b.c
При помощи этой программы мы скопируем файл "a.c" в файл  "b.c".  При
перенаправлении стандартного вывода  в  спецификации  файла  ("file")
может  быть  указана  длина,  она  обрабатывается  обычным   образом.
Сообщения исполняющей системы при  старте  при  выполнении  стартовых
действий в функции $$init() могут  возникать  ошибки.  При  появлении
ошибки выводится диагностика и выполнение программы прекращается.
    Диагностики следующие (в сообщениях использованы малые  латинские
буквы,  которые  на  некоторых  терминалах  будут  отображаться   как
большие русские):
    "?io-start-cannot parse command line"
        - ошибка при разборе командной строки
    "?io-start-no memory"
        - недостаточно динамической памяти
    "?io-start-cannot open standard input "file", code = dd."
        ошибка открытия файла "file" при перенаправлении
        файла стандартного ввода. код ошибки dd.
    "?io-start-cannot open standard output "file", code = dd."
        ошибка открытия файла "file" при перенаправлении
        файла стандартного вывода. код ошибки dd.
Значение ошибки с определенным кодом см. приложение 2.

          6.2 УПРАВЛЕНИЕ СТАРТОВЫМИ ДЕЙСТВИЯМИ

    Существует  возможность  при  помощи  определения   в   программе
некоторых  глобальных  переменных  управлять  параметрами   действий,
выполняемых при старте программы:
    Можно подавить разрешение ввода символов нижнего  регистра  перед
вводом командной строки (распространяется и на дальнейшее  выполнение
программы). Для этого необходимо определить в программе $$nolc = 1;
    Можно изменить вид приглашения для ввода командной  строки,  явно
задав: char $$prom[]="...";
    Строка подсказки завершается символом '\200' для того, чтобы  при
выводе подсказки (по .GTLIN) не произошел переход  на  новую  строку.
Можно запретить ввод командной строки  (и  печать  приглашения).  Для
этого необходимо определить в программе
        $$narg = 1;
При запрещении ввода командной строки реакция исполнительной  системы
точно такая же, как при вводе пустой командной строки.
                              - 43 -


         7. ПАКЕТ РАБОТЫ С ТЕРМИНАЛОМ

    Данный   документ   описывает   пакет    библиотечных    функций,
позволяющих пользователю для различных  типов  терминалов  оптимально
обновлять экран осуществлять ввод с терминала с ориентацией на  экран
оптимально перемещать указатель от одной точки экрана к  другой.  Вся
информация о  возможностях  и  командах  конкретного  типа  терминала
сосредоточена в одном файле и легко модифицируема.
    Идея данного пакета -  дать  программисту  возможность  выполнять
стандартным  образом  наиболее  общий  тип  функций,   зависящих   от
терминала. Пакет состоит из 3-х частей:
        обновление экрана;
        ввод и обновление экрана при вводе;
        оптимизация движения курсора.
    Можно использовать оптимизацию движения курсора без использования
первых  двух  частей пакета. Обновление экрана и ввод можно осуществ-
лять без изучения функций оптимизации движения курсора.
    В данном документе поддерживается следующая терминология:
       - окно (window)
Внутреннее представление, содержащее образ части экрана  терминала  в
определенный момент времени. Это "окно" может охватывать  весь  экран
или часть его (вплоть до одного символа);

       - терминал или экран терминала 
То, что в настоящее время пользователь  видит на экране терминала.

       - экран (screen)
Поднабор окон, которые по размерам равны экрану терминала,  т.е.  они
начинаются в верхнем левом углу  и  заканчиваются  в  нижнем  правом.
Одно из них, stdscr, автоматически обеспечивается для пользователя.

    Для того, чтобы  использовать  библиотеку,  необходимо  одной  из
первых строк программы пользователя иметь строку:
        #include 
Файл "curses.h" содержит различные необходимые определения,  а  также
включает файл "stdio.h". При компоновке пользователь  должен  указать
дополнительно   библиотеку,   содержащую   описываемые   функции    и
размещенную обычно в файле CSLIB.OBJ.

В файле curses.h:
   описаны (как  внешние)  описатели  терминала  (см.  "информация  о
терминале"  и  приложение   1);описан   тип   "WINDOW",   описывающий
дескриптор окна.
   определены "OK" (1) и "ERR" (0) - мнемоники для кодов  удачного  и
неудачного завершения функций пакета.
   описаны (как "extern WINDOW *") curscr и stdscr
   определен логический тип - "bool" (как "char")
   при использовании данного пакета имена функций могут быть  набраны
на любом регистре, имена макросов - на нижнем. Макросы  при  описании
помечены /* + */

         7.1. ОБНОВЛЕНИЕ ЭКРАНА
    
    Для  того,  чтобы  оптимально  обновлять   терминал,   процедурам
необходимо знать, как в настоящее время  выглядит  терминал  и  каким
желает его видеть пользователь. Для  этого  определяется  тип  данных
(структура) типа WINDOW, который описывает образ окна  для  процедур,
включая начальное положение  на  экране  (координаты  (y,x)  верхнего
левого угла) и его размер. Всегда  заводится  одна  структура  такого
типа  (называемая  curscr).  Она  содержит  информацию  о  том,   как
выглядит терминал в настоящее время. Все другие структуры этого  типа
являются внутренним представлением. Они используется для  создания  и
хранения потенциального образа экрана терминала или части  его  и  не
                              - 44 -


имеют прямого  отношения  к  тому,  что  действительно  находится  на
экране.
    Если  у  пользователя  имеется  окно,  описывающее,  как   должна
выглядеть некоторая часть терминала,  то  он  может  вызвать  функцию
refresh() (или wrefresh(), см. ниже).  refresh()  делает  терминал  в
области, покрываемой окном, похожим на оное.
    Отметим, что изменение  окна  не  изменяет  терминал.  Обновление
экрана  терминала   производится   только   вызовом   refresh()   или
wrefresh().  Это  позволяет   пользователю   поддерживать   несколько
различных  вариантов  части  экрана  терминала.  Кроме  того,   можно
изменять окна в любом порядке, не обращая внимания  на  эффективность
выполнения   изменений.   После   того,   как   пользователь   задаст
окончательный вид окна и вызовет refresh() пакет решит, какой  способ
изменения оптимальный и выполнит его.

    Соглашения об именах функций
    Как отмечено выше, функции могут работать с  несколькими  окнами,
два из которых задаются автоматически. Это:
     curscr
образ терминала; пользователь не должен непосредственно обращаться  к
curscr.
     stdscr
"стандартное окно" - экран, на котором, как правило, ведется основная 
работа.
    Вся  работа  с  окнами  осуществляется  вызовами  соответствующих
функций,  причем  для  работы   со   стандартным   экраном   (stdscr)
существует   специальный    набор    функций    (реализованных    как
макрообращения к соответствующим функциям общего вида),  для  которых
ненужно указывать имя окна. Например, чтобы  добавить  символ  'c'  к
некоторому окну "win" необходимо использовать функцию  waddch(win,c),
но к stdscr символ 'c' можно добавить вызвав addch(c).
    В общем случае, если функции применяются не для stdscr,  к  имени
функции добавляется буква  'w'  (слева),  и  первым  параметром  этой
функции должен быть описатель окна. К именам  процедур,  для  которых
всегда необходимо явно указывать окно, никогда не добавляется 'w'.
    Изменение  текущих  координат   (y,x)   в   окне   обеспечивается
функциями move() и wmove().  Однако  частым  является  случай,  когда
сначала необходимо выполнить движение, а  затем  сразу  же  ввод  или
вывод.   Поэтому,   для   удобства,   перед   большинством   функций,
выполняющих ввод/вывод, может ставится  префикс  "mv"  и  необходимые
координаты (y,x) могут быть добавлены к аргументам функций:
        move (y,x);
        addch (ch);
   можно заменить на
        mvaddch (y,x,ch);
   ,а 
        wmove (win,y,x); 
        waddch (win,ch);
   можно заменить на
        mvwaddch (win,y,x,ch); 
    Указатель  описания  окна  (win)  должен  всегда   стоять   перед
координатами (y,x). 
    Предполагается, что все действия выполняются с stdscr, однако все
инструкции будут работать с любым окном при явном его  указании  (см.
выше).








                              - 45 -


         7.2. ЗАПУСК

    Для   того,   чтобы   использовать   пакет   экрана,   необходимо
распределить пространство для curscr  и  stdscr,  что  осуществляются
функцией initscr(). при попытке распределения пространства  для  окон
памяти может нехватить - в  этом  случае  initscr()  возвращает  ERR.
initscr() необходимо вызывать  перед  использованием  любых  функций,
работающих с окнами.  После  распределения  окон  пользователь  может
использовать их. Для разрешения скролинга (смещения информации  вверх
при переходе на следующую строку  из  последней)  в  окне  необходимо
использовать макрос scrollok(). Новые окна могут быть  созданы  путем
использования  функций  newwin()   и   subwin().   Функция   delwin()
уничтожает старые окна.

         7.3. ВЫВОД

    Допустим, что  необходимо  обновить  терминал.  для  этого  нужно
сначала изменить окно,  а  потом  перенести  изменения  на  терминал.
Основными  функциями,  используемыми  для  изменения  окна   являются
addch() и move(). addch()  добавляет  символ  в  позицию  с  текущими
координатам  (y,x),  а  при  помощи  move()  можно  изменить  текущие
координаты. Для выполнения  этих  функций  разом  можно  использовать
mvaddch(). Другие функции вывола,  такие  как  addstr()  и  printw(),
вызывают addch() для добавления символов к окну.
    После  того,  как  окно  изменено,  необходимо  вызвать   функцию
refresh()  для  обновления  терминала.  Для   оптимизации   изменений
refresh() считает, что не измененные вами части  окна  не  изменились
на терминале. Если это не  так,  то  при  помощи  функции  touchwin()
можно заставить refresh() обновить окно на  терминале.  Заметим,  что
все обновления refresh() делает по отношению к curscr. Если  терминал
не соответствует curscr (например на терминал было нечто выведено  не
функциями  пакета  или  произошел  аппаратный  сбой),  то  необходимо
полностью обновить терминал, выполнив wrefresh(curscr);

         7.4. ВВОД

    Основная функция ввода - getch(), которая вводит  один  символ  с
терминала и, если эхо включено,  вызывает  addch()  и  refresh()  для
отображения символа (системное эхо  недопустимо,  т.к.  пакет  экрана
должен  всегда  знать,  что  происходит  на  терминале).   Существует
возможность ввести строку функцией getstr() и  осуществить  форматный
ввод при помощи функции scanf().

         7.5. ЗАВЕРШЕНИЕ РАБОТЫ

    В  начале  работы  функция   initscr()   при   помощи   setterм()
осуществляет   установку   необходимого   режима    терминала.    Для
прекращения   или   приостановки   использования   процедур    пакета
необходимо  вызвать  функцию  endwin(),  которая  выдаст  завершающие
последовательности  и  установит  режим  терминала,  который  был  до
вызова initscr().  Необходимо  заметить,  что  окна  по  endwin()  не
освобождаются, и работа с окнами  может  быть  возобновлена  вызовами
setterm() и wrefresh(curscr).
    Информация   о   терминале   хранится   в   наборе    переменных,
определенных в файле "curses.h". имена их состоят из 2 букв  верхнего
регистра  и  имеют  (в  английском  языке)  мнемоническое   значение.
Например, "HO"  -  строка,  которая  перемещает  курсор  к  исходному
положению (HOME). Переменные  описания  образуют  два  набора:  набор
параметров и набор команд. Параметры  имеют  логический  ("bool"  см.
ниже)  и  определяют,  имеет  данный  терминал  некоторую  аппаратную
возможность или особенность, или нет. Переменные набора команд  имеют
тип "char *" и  являются  указателями  на  строки,  которые  (строки)
содержат  управляющие  последовательности  терминала,  приводящие   к
                              - 46 -


выполнеию определенных функций. Например "UP" - указатель на  строку,
приводящую к перемещению курсора на экране терминала на  одну  строку
вверх.
    Необходимо отметить здесь одну особенность: если у терминала  при
выводе  последнего  символа  в  строке  курсор  переходит  в   начало
следующей строки (параметр AN == TRUE) и переход из последней  строки
приводит к аппаратному скроллингу (параметр SD == TRUE), то  последнй
символ последней строки  терминала  на  терминал  не  выводится  (его
вывод  привел  бы  к  смещению  экрана).   при   этом   этот   символ
записывается  в  окно  ошибки  не  возникает.  Если   данный   символ
сместится с этого "проклятого" места (например  при  скроллинге),  то
он появится на экране терминала.

         7.6. ОПТИМИЗАЦИЯ ДВИЖЕНИЯ КУРСОРА

    На непосредственное  управление  перемещением  курсора  выполняет
функция mvcur(). Ей необходимо  задать  исходное  положение  и  место
назначения. например:
        mvcur(0, 0, LINES/2, COLS/2);
передвинет строки ('\n'), то строка будет очищена до конца и  текущие
координаты (y,x) будут изменены на начало следующей строки.
    При возврате каретки ('\r') курсор передвинется на начало строки.
знаки табуляции ('\t') будут выводятся по отношению к  текущему  окну
(табуляция  имеет  длину  8)  (а  не  экрану  терминала).   Табуляция
выводится  соответствующим  числом  пробелов,  и,  если  они  не  все
помещаются на  текущей  строке,  то  оставшиеся  будут  перенесены  в
начало следующей (что,  повидимому,  не  совсем  корректно).  addch()
возвращает ERR, если после вывода  символа  курсор  должен  выйти  за
пределы окна (вывод  последнего  символа  в  последней  строке,  если
скроллинг запрещен, см. scrollok()). Ошибка не возникает  при  выводе
табуляции, при запрещенном  скроллинге  лишние  пробелы  выводятся  в
начало последней строки (это тоже не корректно).
        char *str;
        window *win;
        addstr(str); /* + */
        waddstr (win, str);
добавляет строку str в окно, начиная с текущих координат (y,x вращает
ERR, если строка попытается выйти за границы экрана (см. addch()).
        char vert,hor; /* символы для рамки */
        window *win;
        box (win, vert, hor);
создает рамку по границе (внутри) окна,  используя  vert  в  качестве
символа для создания вертикальных сторон и hor - для  горизонтальных.
Углы остаются пустыми.
        window *win;
        clear(); /* + */ 
        wclear(win);
очищает окно и устанавливает для этого окна флаг "clear".  Если  окно
является  экраном,  то  при  вызове  refresh()  для  такого  окна   с
установленным  флагом  "clear"  будет  выполнена  аппаратная  очистка
экрана. Текущие координаты (y,x) в окне устанавливаются в (0,0).
        window   *scr;
        bool     bollf;
        clearok (scr,boolf); /* + */
Управляет флагов "clear"  для  экрана  scr.  (если  окно  экраном  не
является, то функция  смысла  не  имеет).  Если  boolf  -  TRUE,  это
вызывает очистку экрана (аппаратную) при следующем вызове  refresh(),
или ее (очистки) подавление, если boolf - FALSE. В отличии  от  clear
clearok() не изменяет содержание экрана. Если scr -  curscr,  то  при
вызове  refresh()  произойдет  очистка  экрана,   даже   если   окно,
переданное refresh не является экраном.
        window *win;
        erase(); /* + */
                              - 47 -


        werase(win);
стирает окно, не устанавливая флаг очистки. Это аналогично clear(),за
исключением того,  что  никогда  не  вызывает  выполнения  аппаратной
очистки экрана  в  refresh().  Функция  erase()  не  имеет  связанной
команды с "mv".
        window *win;
        ctobot(); /* + */
        wctobot(win);
очищает окно, начиная от  текущих  координат  (y,x)  до  конца  окна.
Команда не имеет связанной команды с "mv".
        window   *win; 
        ctoeol(); /* + */
        wctoeol(win);
стирает информацию в окне, начиная с текущих  координат  (y,x)  и  до
конца строки. Функция не имеет связанной команды с "мv".
        window  *win; 
        delch(); /* + */
        wdelch(win);
стирает символ в текущих координатах  (y,x).  Каждый  символ  строки,
стоящий после уничтоженного символа, перемещается влево  и  последний
символ строки становится пробелом. текущие координаты не изменяются.
        window  *win;
        deleteln(); /* + */
        wdeleteln (win);
стирает текущую  строку.  Каждая  строка  ниже  текущей  перемещается
вверх, по-  следняя  строка  становится  пустой.  Текущие  координаты
неизменяются.
        char   c; 
        window  *win;
        insch(c); /* + */
        winsch (win, c);
вставляет символ 'c'  в  текущие  координаты  (y,x).Каждый  символ  в
строке после вставленного с перемещается вправо  и  последний  символ
строки исчезает.
        window    *win;
        insertln(); /* + */
        winsertln (win);
вставляет пустую строку  над  текущей.  Каждая  строка  ниже  текущей
смещается  вниз  и  последняя   строка   исчезает.   Текущая   строка
очищается. Текущие координаты (y,x) не изменятся.
        int   y,x;
        window *win; 
        move(y,x); /* + */
        wmove(win, y, x);
меняет текущие кординаты в окне на  (y,x).  Функция  возвращает  ERR,
если координаты выходят за границы окна, при этом текущие  координаты
неизменяются.
        window *win1, *win2;
        overlay(win1, win2);
производит наложение win1 на  win2  в  области,  где  они  совпадают.
Пробелы на win1 оставляют содержимое win2 под собой неизменным.
        window    *win1, *win2; 
        overwrite(win1, win2);
производит запись win1 на win2 в области, где они совпадают.
        char   *fmt; 
        window *win;
        printw(fmt, arg1, arg2,...);
        wprintw(win, fmt, arg1, arg2,...);
printw() выполняет форматное преобразование над своими аргументами  и
записывает его результат в окно, начиная с текущих  координат  (y,x).
Используется  addstr()  для  добавления  строки  к   окну.   printw()
возвращает ошибку (ERR), если addstr() возвращает ошибку.
        window   *win;
                              - 48 -


        refresh(); /* + */
        wrefresh(win);
refresh() обновляет  указанное  окно  на  экране  терминала,  приводя
изображение окна в соответствие  с  его  содержимым.  Считается,  что
изображение окна на терминале  не  испорчено  со  времени  последнего
вызова refresh(), т.е. не выполнялось отображение  какого-либо  окна,
перекрывающего данное окно.
    для перевывода всего  окна  необходимо  перед  refresh()  вызвать
touchwin() для данного окна.
        window    *win;
        standout(); /* + */
        wstandout(win);
        standend(); /* + */
        wstandend(win);
standout()  включает  альтернативный  режим   отображения   символов,
записываемых в окно (например подчеркивание или режим  русских  букв,
в    зависимости    от    аппаратных     возможностей     терминала).

standend() выключает альтернативный режим отображения для добавляемых
символов.

    Функции ввода
        echo(); /* + */
разрешает эхоотображение вводимых символов в окне,
        noecho(); /* + */
запрещает.
        window *win;
        getch(); /* + */
        wgetch (win);
вводит один символ с терминала, и,  если  необходимо,  выполняет  его
эхоотображение. Возвращает ERR, если после ввода  текущие  координаты
вынуждены выйти  за  пределы  окна,  в  противном  случае  возвращает
введенный символ.
        window     *win; 
        char       *str; 
        getstr(str); /* + */
        wgetstr(win, str);
вводит (используя getch()) символы и помещает их в строку str пока не
встретит '\n', или пока не произойдет ошибка в getch(). '\n' в  конце
строки  удаляется,  строка  завершается   символом   '\0'.   getstr()
возвращает ERR, если  getch()  возвращает  ERR.  В  этом  случае  str
содержит  символы,  введенные  до  появления  ошибки  и   завершенные
символом '\0'. При нормальном завершении getstr() возвращает OK.
        char *fmt;
        window *win;
        scanw(fmt, arg1, arg2,...);
        wscanw(win, fmt, arg1, arg2,...);
осуществляет форматный ввод (см. scanf())с терминала,вводя символы по
getch(). возвращает ERR,  если  getch()  возвращает  ЕRR.  Необходимо
заметить,что,поскольку   ERR   определено   как   0,   то    возможна
неоднозначность   между   значением   ERR,   и   нулевым   значением,
возвращаемым scanf().

    Различные функции

        window  *win;
        delwin(win);
уничтожает окно, освобождая занятую им память. если данное окно имеет
подокна (subwin), то последние не уничтожаются, но информация  в  них
нарушается, поэтому они также должны быть уничтожены.
        endwin();
прекращает  (приостанавливает)  работу  с   окнами:   очищает   экран
терминала  и  переводит  терминал  в  режим,  который  он   имел   до
                              - 49 -


выполнения initscr().работу с  окнами  можно  возобновить  вызовомами
setterm()   для   установки   необходимого   режима    терминала    и
wrefresh(curscr)) для воосановления информации на экране.
        window    *win; 
        int       y, x;
        getyx(win, y, x); /* + */
помещает текущие координаты (y,x) окна win в перемещеные y и  x.  Это
возможно без передачи адресов переменных, т.к. getyx() - макрос.
        window   *win;
        inch(); /* + */
        winch(win); /* + */
возвращает символ, находящийся  в  текущих  координатах  (y,x)  окна.
Никаких изменений с окном не происходит. Функция не  имеет  связанной
команды "mv".
        initscr();
выполняет начальную инициализацию. Зту  функцию  необходимо  вызывать
перед использованием любых процедур пакета  за  исключением  mvcur(),
т.е. за иск- лючением случая, когда используется  только  оптимизация
движения курсора.
    Выполняет  установку  необходимого  режима  терминала   (функцией
setterm()) и создает  окна  curscr  и  stdscr,  возвращая  ERR,  если
недостаточно свободной памяти, и stdscr при нормальном завершении.
        window  *win;
        bool    boolf;
        leaveok(win, boolf); /* + */
управляет логическим флагом, определяющим поведение курсора на экране
после выполнения изменений (функцией refresh()). Если boolf ==  FALSE
(а именно так  по  умолчанию),  то  после  обновления  окна  функцией
refresh()  курсор  возвращается  в  позицию,  определяемую   текущими
координатами в данном окне. Если boolf == TRUE, то  после  выполнения
изменений  курсор   остается   на   месте,   а   текущие   координаты
корректируются на координаты курсора, если курсор  в  пределах  окна,
или устанавливаются в (0,0) если курсор вне окна.
        window  *win;
        int     y,x;
        mvwin(win, y, x);
перемещает указанное  окно,  устанавливая  его  начальные  координаты
(координаты левого верхнего  угла)  в  (y,x).  Если  окно  выйдет  за
границы