Добавить в избранное | Сделать стартовой страницей

Большая Linux библиотека для пользователей OS Linux и ПО для нее
Есть что сказать? Нужен совет? Посети наш форум.




Библиотека языка C GNU glibc. часть 2

7. Ввод-вывод на потоках

Эта глава описывает функции для создания потоков и выполнения ввода и вывода на них. Как обсждается в Главе 6 [Краткий обзор ввода-вывода], поток - довольно абстрактное понятие, представляющее канал связи с файлом, устройством, или процессом.

7.1 Потоки

По историческим причинам, С тип структуры данных, которая представляет поток называется FILE,а не "поток". Так как большинство библиотечных функций имеет дело с объектами типа FILE *, иногда термина указатель на файл также используется, чтобы обозначить "поток". Это ведет к беспорядку терминологии во многих книгах о C, это руководство, однако, использует термины "файл" и "поток" только в техническом смысле.

Тип FILE объявлен в заголовочном файле "stdio.h".

FILE (тип_данных) - это тип данных, используемый, чтобы представить объекты потока. Объект FILE содержит всю внутреннюю информацию о состоянии относительно соединения со связанным файлом, включая такие вещи как индикатор позиции файла и информация буферизации. Каждый поток также имеет индикаторы ошибки и состояния конца файла, которые могут быть проверены функциями feof и ferror; см. раздел 7.13 [EOF и ошибки].

Объекты FILE размещены и управляются внутренне в соответствии c библиотечными функциями ввода -вывода. Не пробуйте создавать ваши собственные объекты типа FILE; пусть библиотеки, делают это. Ваши программы должны иметь дело только с указателями на эти объекты (то есть FILE*).

7.2 Стандартные потоки

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

Эти потоки объявлены в заголовочном файле "stdio.h".

FILE * stdin (переменная)

стандартный входной поток, который является нормальным источником ввода для программы.

FILE * stdout (переменная)

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

FILE * stderr (переменная)

стандартный поток ошибки, который используется для сообщений об ошибках и диагностики, выданной программой.

В системе GNU, Вы можете определять то, какие файлы или процессы соответствуют этим потокам, использующим трубопровод и средства переадресации, обеспеченные оболочкой. (Использование примитивов оболочки описано в Главе 9 [Интерфейс файловой системы]) Большинство других операционных систем обеспечивает подобные механизмы, но подробности того, как их использовать могут изменяться.

В библиотеке GNU C, stdin, stdout, и stderr - обычные переменные, которые Вы можете устанавливать точно так же как любые другие. Например, чтобы переназначить стандартный вывод файла, нужно выполнить:

      fclose (stdout);
      stdout = fopen ("standard-output-file","w");
Обратите внимание, что в других системах stdin, stdout, и stderr являются макрокомандами, которые Вы не можете назначать обычным способом. Но Вы можете использовать freopen, чтобы получить эффект закрытия того и повторного открытия этого. См. раздел 7.3 [Открытие потоков].

7.3 Открытие потоков

Открытие файла функцией fopen создает новый поток и устанавливает соединение между потоком и файлом, возможно включая создание нового файла.

Все описанное в этом разделе объявлено в заголовочном файле "stdio.h".

      FILE * fopen (const char *filename , const char * opentype) (функция)
функция fopen открывает поток для ввода - вывода в файл, и возвращает указатель на поток.

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

"r" Открывает существующий файл для чтения.

"w" Открывает файл для записи. Если файл уже существует, его длина обнуляется. Иначе создается новый файл.

"а" Открывает файл для добавления; то есть записи в конец файла. Если файл уже существует начальное содержимое неизменяется, и вывод потока добавляется в конец файла. Иначе, создается новый пустой файл.

"r+" Открывает существующий файл, и для чтения и для заипси. Начальное содержимое файла неизменяется, и начальная позиция файла - в начале файла.

"w+" Открывает файл, и для чтения и для запмси. Если файл уже существует, он усекается, чтобы обнулить длину. Иначе, создается новый файл.

"а+" Открывает или создают файл, и для чтения и для добавления в конец. Если файл существует, начальное содержимое неизменяется. Иначе, создается новый файл. Начальная позиция файла для чтения - в начале файла, но вывод всегда добавляется к концу файла.

Вы видите, что "а+" запрашивает поток, который может делать и ввод и вывод. Стандарт ANSI говорит, что при использовании такого потока, Вы должны вызвать fflush (см. раздел 7.17 [Буферизация потока]) или позиционирующую файл функцию типа fseek (см. раздел 7.15 [Позиционирование файла]) при переключении чтения на запись или наоборот. Иначе, внутренние буфера не будут освобождены правильно. Библиотека GNU С не имеет этого ограничения; Вы можете делать произвольное чтение и запись на потоке в любом порядке.

Библиотека GNU С определяет один дополнительный символ для использования в opentype: символ "x" настаивает на создании нового файла, если имя файла уже существует, fopen выдаст ошибку. Это эквивалентно O_EXCL опции открыавющей функции (см. раздел 8.10 [Флаги состояния файла]).

Символ "b" в opentype имеет стандартное значение; он запрашивает двоичный поток, а не текстовый поток. Но это не имеет никакое значения в POSIX системах (включая систему GNU). Если и "+" и "b" определен, они могут применяться в любом порядке. См. раздел 7.14 [Двоичные потоки].

Любые другие символы в opentype просто игнорируются. Они могут быть значимы в других системах.

Ели происходит ошибка , fopen возвращает пустой указатель.

Вы можете иметь многократные потоки (или описатели файла) указывающие на тот же самый файл, открытый в то же самое время. Если Вы только вводите, это работает правильно, но Вы должны быть внимательны если какой-то поток выходной. См. раздел 8.5 [Предосторожности, связанные с потоком/описателем].

Это верно в равной степени, в зависимости от того, находятся ли потоки в одной программе или в отдельных программах (что может легко случиться). Может оказаться более безопасным использование средств закрытия файла, для того, чтобы избежать одновременного доступа. См. раздел 8.11 [Блокировки файла].

      int FOPEN_MAX (макрос)
Значение этой макрокоманды - целочисленное постоянное выражение, которое представляет минимальное число потоков, что могут быть открыты одновременно. Значение этой константы - по крайней мере восемь, включая три стандартных потока stdin, stdout, и stderr.

      FILE * freopen (const char *filename, const char * opentype, FILE *stream) (функция)
Эта функция - подобна комбинации fclose и fopen. Она сначала закрывает упоминаемый поток, игнорируя любые ошибки, которые обнаружены в процессе. (Т.к. ошибки игнорируются, Вы не должны использовать freopen на выходном потоке, если Вы фактически выполнили вывод, используя поток.) А затем открывает файл filename с режимом opentype как в fopen, и связывает его с тем же самым потоком.

Если, если операция терпит неудачу, возвращается пустой указатель; иначе, freopen возвращает поток.

freopen традиционно используется, чтобы соединить стандартный поток типа stdin с файлом вашего собственного выбора. Это полезно в программах, в которых использование стандартного потока для некоторых целей является жестко закодированным. В библиотеке GNU С, Вы можете просто закрывать стандартные потоки и открывать новые через fopen. Но другие системы испытывают недостаток этой способности, так что использование freopen более переносимо.

7.4 Закрытие потоков

Когда поток закрывается с помощью fclose, соединение между потоком и файлом отменяется. После того, как Вы закрыли поток, Вы не можете выполнять какие-нибудь дополнительные операции на нем.

      int fclose (FILE *stream) (функция)
Эта функция заркыает поток и прерывает соединение с соответствующим файлом. Любой буферизированный вывод дописывается, и любой буферизированный ввод отбрасывается. Функция fclose возвратит значение 0, если файл будет закрыт успешно, и EOF, если будет обнаружена ошибка.

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

Функция fclose объявлена в "stdio.h".

Если ваша программа завершается, или если Вы вызываете функцию выхода (см. 22.3.1 [Нормальное окончание]), все открытые потоки автоматически закрываются.

Если ваша программа завершается каким-нибудь другим способом, типа, вызыва функции аварийного прекращения работы (см. раздел 22.3.4 [Прерывание выполнения программы]) или фатального сигнала (см. Главу 21 [Обработка сигналов]), открытые потоки могут быть закрыты неправильно. Буферизированный вывод может быть не дописан. Для подробной информации относительно буферизации потоков, см. раздел 7.17 [Буферизация потока].

7.5 Простой вывод символами или строками

Этот раздел описывает функции для выполнения символьно- и строчноориентированного вывода.

Эти функции объявлены в заголовочном файле "stdio.h".

      int fputc ( int C, FILE *stream) (функция)
Функция fputc преобразовывает символ C, чтобы напечатать char без знака, и запишет его в поток stream. EOF возвращается, если происходит ошибка; иначе возвращается символ C.

      int putc ( int C, FILE *stream) (функция)
Это аналог fputc, за исключением того, что большинство систем выполняет ее как макрокоманду, делая это быстрее. Одно из следствий - то, что она может оценивать аргумент потока больше чем один раз. putc ­ обычно лучшая функция, для записи одиночного символа.

      int putchar ( int c) (функция)
Функция putchar эквивалентна putc со stdout как значением аргумента потока.

      int fputs (const char * s, FILE *stream) (функция)
Функция fputs запишет строку s в поток stream. Пустой символ завершения не пишется. Эта функция так же не добавляет символ перевода строки.

Эта функция возвращает EOF, если происходит оибк записи, а иначе неотрицательное значение.

Например:

      fputs ("Are ", stdout);
      fputs ("you ", stdout);
      puts ("hungry?\n", stdout);
Выводит текст `Are you hungry?' сопровождаемый символом перевода строки.

      int puts (const char * s) (функция)
Эта функция запишет строку s в поток stdout сопровождая ее символом перевода строки. Пустой символ завершения строки не запишет.

      puts - наиболее удобная функция для печати простых сообщений.
Например:

      puts ("Это - сообщение.");
      int putw ( int w, FILE *stream) (функция)
Эта функция напишет w (int) в поток stream. Она предусматривает совместимость с SVID, но мы рекомендуем, чтобы Вы использовали fwrite взамен (см. раздел 7.12 [Блочный ввод-вывод]).

7.6 Символьный ввод

Этот раздел описывает функции для выполнения символьно- и строчноориентированного ввода. Эти функции объявлены в заголовочном файле "stdio.h".

      int fgetc (FILE * stream) (функция)
Эта функция читает следующий символ как char без знака из потока stream и возвращает значение, преобразованное в int. Если происходит условие конца файла или ошибка чтения, возвращается EOF.

      int getc (FILE * stream) (функция)
Это - аналог fgetc, за исключением того, что для нее допустимо (и типично) выполнение как макрокоманды, которая оценивает аргумент stream больше чем один раз. getc часто сильно оптимизирована, так что это ­ обычно лучшая функция, чтобы читать одиночный символ.

      int getchar (void) (функция)
Функция getchar эквивалентна getc с stdin вместо аргумента stream.

Вот пример функции, которая вводит используя fgetc. Она работала бы, точно также используя getc взамен, или используя getchar () вместо fgetc (stdin).

      int y_or_n_p (const char *question)
      {
       fputs (question, stdout);
       while (1)
       {
        int c, answer; /* Напишем пробел, чтобы отделить ответ от вопроса. */
        fputc (" ", stdout);
      /* Читаем первый символ строки. Это должен быть символ ответа, но
 может и не быть. */
        c = tolower (fgetc (stdin));
        answer = c /* Отбрасываем остальную входную строку. */
        while (c != '\n') c = fgetc (stdin);
      /* Примем ответ, если он был допустим. */
        if (answer == 'y') return 1;
        if (answer == 'n') return 0;
      /* Ответ был недопустим: просим о допустимом ответе. */
        fputs ("Please answer y or n:", stdout);
       }
      }
      int getw (FILE * stream) (функция)
Эта функция читает word (то есть int) из stream. Она предусматривает совместимость с SVID. Мы рекомендуем, чтобы Вы использовали вместо этого fread (см. раздел 7.12 [Блочный ввод-вывод]) .

7.7 Строчно ориетированный ввод

Так как много программ интерпретируют ввод на основе строк, удобно иметь функции, чтобы читать строку из stream.

Стандартный C имеет функции, чтобы делать это, но они не очень безопасны: пустые символы и даже длинные строки могут сбивать их. Библиотека GNU обеспечивает нестандартную функцию getline, которая позволяет читать строки надежно.

Другое расширение GNU, getdelim, обобщает getline. Она читает разграниченную запись, определенную как все после следующего вхождения заданного символа-разделителя.

Все эти функции объявлены в "stdio.h".

      size_t getline (char ** lineptr, size _t * n, FILE * stream) (функция)
Эта функция читает всю строку из stream, сохраняя текст (включая символ перевода строки и пустой символ завершения) в буфере и хранит буферный адрес в * lineptr.

Перед вызовом getline, Вы должны поместить в *lineptr адрес буфера *n байт длиной, размещенный malloc. Если этот буфер достаточно большой чтобы вместить строку, getline, сохранит строку в этом буфере. Иначе, getline делает больший буфер используя realloc, сохраняя новый буферный адрес обратно в *lineptr и увеличенный size обратно в *n. См. раздел 3.3 [Беспрепятственное резервирование].

Если Вы устанавливаете *lineptr как пустой указатель, и обнуляете *n, перед обращением, то getline, зарезервирует начальный буфер для Вас, вызывая malloc.

В любом случае, когда getline завершается, *lineptr - это char * который указывает на текст строки.

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

Эта функция - расширение GNU, но это - рекомендуемый способ читать строки из stream. Альтернативные стандартные функции ненадежны.

Если происходитошибка происходит или достигнут конец файла, getline возвращает -1.

      size_t getdelim (char ** lineptr, size _t * n, int delimiter, FILE * stream) (функция)
Эта функция - подобна getline за исключением того, что символ, который сообщает, чтобы она прекратила читать - не обязательно символ перевода строки. Аргумент delimiter определяет символ - разделитель; getdelim будет читать, пока не увидит этот символ (или конец файла).

Текст сохраняется в lineptr, включая символ - разделитель и пустой символ завершения. Подобно getline, getdelim делает lineptr большим, если он не достаточно большой.

getline фактически реализована в терминах getdelim, как показано ниже:

      size_t getline (char **lineptr, size_t *n, FILE *stream)
      {
       return getdelim (lineptr, n, '\n', stream);
      }

      сhar * fgets (char * s, int count, FILE * stream) (функция)
Функция fgets читает символы из потока stream включая символ перевода строки и сохраняет их в строке s, добавляя пустой символ, чтобы отметить конец строки. Вы должны обеспечить место для count символов в s, но читается count - 1 символов. Дополнительное символьное место используется, чтобы содержать пустой символ в конце строки.

Если, система уже в конце файла, когда Вы вызываете fgets, то содержимое массива s, неизменяется, и возвращается пустой указатель. Пустой указатель также возвращается, если происходит ошибка чтения, возвращаемое значение - указатель s.

Предупреждение: если входные данные имеют пустой символ, Вы не может использовать fgets. Так что не используйте fgets, если Вы не знаете, что данные не могут содержать пустой символ. Не используйте ее, чтобы читать файлы, отредактированные пользователем, потому что, если пользователь вставляет пустой символ, Вы должны обработать это правильно или напечатать сообщение об ошибках. Мы рекомендуем использовать getline вместо fgets.

      сhar * gets (char * s) (функция)
Эта функция читает символы из потока stdin до следующего символа символа перевода строки, и сохраняет их в строке s. Символ перевода строки отбрасывается (обратите внимание, что это отличает ее от поведения fgets, которая копирует символ перевода строки в строку). Если она сталкивается с ошибкой чтения или концом файла, она возвращает пустой указатель; иначе она возвращает s.

Предупреждение: эта функция очень опасна, потому что она не обеспечивает никакой защиты против переполнения строки s. Библиотека GNU включает ее только для совместимости. Вы должны всегда использовать fgets или getline взамен. Чтобы напомнить Вам это, компоновщик (при использовании GNU ld) выдаст предупреждение всякий раз, когда Вы используете gets.

7.8 Обратное чтение

В программах синтаксического анализатора часто полезно исследовать следующий символ во входном потокек без того, чтобы удалить его из потока. Это называется "заглядывание вперед" при вводе, потому что ваша программа бросает взгляд на то что она затем будет читать.

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

Что такое способ обратного чтения

Это иллюстрированное объяснение обратного чтения. Предположим, что Вы имеете поток, читая файл, который содержит только шесть символов, символы "foobar". Предположите, что Вы пока прочитали три символа. Ситуация выглядит следующим образом:

      f o o b а r
           ^
так что следующий входной символ будет "b".

Если вместо того, чтобы читать "b" Вы выполняете обратное чтение символа "o", Вы получаете примерно такую ситуацию:

      f o o b а r
           |
      o­
      ^
так, что следующие входные символы будут "o" и "b".

Если Вы обратно читаете "9" вместо "o", Вы получите это:

      f o o b а r
           |

      9­
      ^
так, что следующие входные символы будут "9" и "b".

Использование ungetc для осуществления обратного чтения

Функция для чтения символа обратно называется ungetc, потому что она обращает действие getc.

      int ungetc ( int C, FILE *sream) (функция)
Функция ungetc помещает символ C обратно во входной поток. Так что следующий ввод из потока будет читать C прежде, чем что-нибудь еще.

Если С - EOF, ungetc не делает ничего и только возвращает EOF. Это позволяет Вам вызывть ungetc с возвращаемым значением getc без проверки ошибки из getc.

Символ, который Вы помещаете обратно, не обязательно тот который фактически читался из потока. Т. е. читать какой-нибудь символ из потока перед его обратным чтением не обязательно! Но это - странный способ писать программу; обычно ungetc используется только, чтобы читить обратно символ, который только что читался из того же самого потока.

Библиотека GNU С поддерживает только один символ pushback другими словами, нельзя вызвать ungetc дважды без ввода между вызовами. Другие системы могут позволять Вам, помещать обратно много символов; тогда чтение из потока восстанавливает символы в обратном порядке, от того как они были помещены.

Выталкивание обратных символов не изменяет файл; и воздействует только на внутреннюю буферизацию потока. Если вызывается позиционирующая файл функция (типа fseek или rewind; см. раздел 7.15 [Позиционирование файла]), все отложенные помещаемые-обратно символы отбрасываются.

Обратное чтение символа в поток, который находится в конце файла, стирает индикатор конца файла для потока. После того, как Вы читаете тот символ, пробуя читать снова Вы столкнетесь с концом файла.

Вот пример, показывающий использование getc и ungetc, чтобы перескочить символы промежутков. Когда эта функция достигает символа не-промежутка, она читает обратно этот символ, и он будет замечен снова на следующей операции чтения из потока.

      #include 
      #include 

      void skip_whitespace (FILE * stream)
      {
       int с;
       do
      /* Нет нужды проверять EOF, потому что это - не isspace, а ungetc
 игнорирует EOF. */
        c = getc (stream);
       while (isspace (c));
       ungetc (c, stream);
      }

7.9 Форматированный вывод

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

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

Основы форматированного вывода

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

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

      int pct = 37;
      char filename[] = "foo.txt";
      printf ("Processing of `%s' is %d%% finished.\nPlease be
 patient.\n", filename, pct);
Производит вывод:

      Processing of `foo.txt' is 37% finished. Please be patient.
Этот пример показывает использование "%d" преобразования, чтобы определить, что int аргумент должен быть напечатан в десятичной записи, "%s" преобразования, чтобы определить печать строкового аргумента, и "%%" преобразования, чтобы печатать непосредственно символ "%".

Имеются также преобразования для печати целочисленного аргумента как значения без знака в восьмеричной, десятичной, или шестнадцатеричной системе счисления ("%o", "%u", или "%x", соответственно); или как символьного значения ("%c").

Числа с плавающей запятой могут быть напечатаны в нормальной, с фиксированной запятой записи, используя "%f" преобразование или в экспоненциальном представлении чисел, используя "%e" преобразование. "%g" преобразование использует или "%e" или формат "%f", в зависимости от того что более подходит для заданного числа.

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

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

Синтаксис преобразования вывода

Этот раздел обеспечивает подробности относительно точного синтаксиса спецификаций преобразования, которые могут появляться в printf строке шаблона.

Символы в строке шаблона, которые - не часть спецификации преобразования, печатаются как есть в выходной поток. Последовательности мультисимволов (см. Главу 18 [Расширенные символы]) разрешены в строке шаблона.

Спецификации преобразования в строке шаблона имеют общую форму:

% флаги ширины [. точность] тип преобразования

Например, в спецификаторе преобразования "%-10.8ld", "-" является флагом, "10" определяет ширину поля, точность - "8", символ "l" является модификатором типа, и "d" определяет стиль преобразования. (Этот специфический спецификатор типа говорит, что печатается long int аргумент в десятичной записи, с минимумом 8 цифр, выровненных по левому краю в поле по крайней мере шириной 10 символов.)

Более подробно, спецификации преобразования вывода состоят из начального символа `%', сопровождаемого последовательностью:

  • Нуль или большее количество символов флага, которые изменяют нормальное поведение спецификации преобразования.
  • Десятичное целое число, определяющее минимальную ширину поля. Если нормальное преобразование производит меньшее количество символов чем этот, поле, дополняется пробелами до заданной ширины. Это ­ минимальное значение; если нормальное преобразование производит большее количество символов чем этот, поле не усечено. Обычно, вывод выравнен по правому краю внутри поля. Вы можете также определять ширину поля "*". Это означает что следующий аргумент в списке параметров (до фактического значения, которое будет напечатано) используется как ширина поля. Значение должно быть int. Если значение является отрицательным, это означает, установить "-" флаг (см. ниже) и использовать абсолютное значение как ширину поля.
  • Точность, чтобы определить число цифр, которые нужно написать для числовых преобразований. Если точность определена, она состоит из точки (".") сопровождаемой необязательно десятичным целым числом (если оно опущено - значение округляется).
Вы можете также определять точность "*". Это означает что следующий аргумент в списке параметров (до фактического значения, которое будет напечатано) используется как точность. Значение должно быть int и игнорируется, если оно отрицательное. Если Вы определяете "*" и для ширины и для точности, то аргумент ширны предшествует аргументу точности. Другие С библиотеки могут не распознавать такой синтаксис.

Таблица форматов вывода Эта таблица содержит различные форматы вывода:

'%d', '%i':

Вывод целого числа как десятичного числа со знаком. См. Раздел 7.9.4 [Целочисленные Форматы].

'%d' и '%i' являются синонимами для printf, но отличаются при использовании scanf для ввода (см. Раздел 7.11.3 [Таблица форматов ввода]).

'%o':

Печатает целое число как восьмеричное число без знака. См. Раздел 7.9.4 [Целочисленные форматы].

'%u':

Печатает целое число как десятичное число без знака. См. Раздел 7.9.4 [Целочисленные форматы].

'%Z':

Печатает целое число как десятичное число без знака, принимая как тип size_t. Детали см. в Разделе 7.9.4 [Целочисленные Форматы]. Этот формат является расширением GNU.

'%x', '%X':

Печатают целое число как шестнадцатеричное без знака. '%x' использует символы нижнего регистра а '%X' - верхнего регистра. См. Раздел 7.9.4 [Целочисленные форматы].

'%f':

Печатает число с плавающей запятой в нормальной записи (с фиксированной запятой). См. подробности в Разделе 7.9.5 [Форматы с плавающей запятой].

'%e', '%E':

Печатают число с плавающей запятой в экспоненциальном представлении чисел. '%e' использует символы нижнего регистра, а '%E' - верхнего регистра.

'%g', '%G':

Выводят число с плавающей запятой либо в нормальном, либо в экспоненциальном представлении. '%g' использует символы нижнего регистра, а '%G' - верхнего регистра.

'%c':

Печатает одиночный символ. См. Раздел 7.9.6 [Другие форматы Вывода].

'%s':

Печатает строку. См. Раздел 7.9.6 [Другие форматы Вывода].

'%p':

Выводит значение указателя. См. Раздел 7.9.6 [Другие форматы Вывода].

'%n':

Содержит число уже напечатанных символов. См. Раздел 7.9.6 [Другие Форматы Вывода]. Обратите внимание, что эта спецификация формата никогда не производит никакого вывода.

'%m':

Печатает строку, соответствующую значению errno. (Этот формат формат является расширением GNU.) См. Раздел 7.9.6 [Другие Форматы Вывода].

'%%':

Печатает символ `%'. См. Раздел 7.9.6 [Другие Форматы Вывода].

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

Целочисленные Форматы

Этот раздел описывает опции для '%d', '%i', '%o', '%u', '%x', '%X', и '%Z' спецификаций преобразования. Эти преобразования печата­ ют целых числа в различных форматах. Форматы '%d' и '%i' печатают целочисленный аргумент как деся­ тичное число со знаком; в то время как '%o', '%u', и '%x' печатают аргумент как восьмеричное, десятичное, или шестнадцатеричное без знака соответственно. Формат '%X' - точно то же что и '%x' за исключением того, что она использует символы 'ABCDEF' в качестве цифр вместо 'abcdef'. '%Z' подобна '%u' но принимает аргумент типа size_t.

Имеют значение следующие флаги:

'-'

Выравнивание слева результата в поле вывода (вместо нор­ мального выравнивания справа).

'+'

Для знаковых форматов '%d' и '%i', печатает знак '+', если значение положительно.

' '

Для знаковых форматов '%d' и '%i', если результат не начи­ нается со знака '+' или знака '-', то ставит перед ним пробел. '*' Для формата '%o' ставит '0' первой цифрой, как будто, увеличивая точность. Не делает ничего полезного для форматов '%d', '%i', или '%u'. Использование этого флага производит вывод, который может анализироваться функциями strtoul (см. Раздел 14.7.1 [Синтаксический анализ Целых чисел]) и scanf с форматом '%i' (см. Раздел 7.11.4 [Числовые форматы ввода]).

'0'

Дополняют поле нулями вместо пробелов. Нули помещаются пос­ ле какой-нибудь индикации относительно знака. Этот флаг игнорируется, если указан флаг '-' , или если указана точность.

Если указана точность, она определяет минимальное число цифр; в случае необходимости выводятся дополнительные нули вначале. Если Вы не указываете точность, печатается столько цифр числа, сколько требуется. Если Вы преобразовываете значение нуля с явной нулевой точностью, то никакие символы не выводятся вообще.

Без модификатора типа, соответствующий аргумент обрабатывается как int (для знаковых преобразований '%i' и '%d') или int без знака (для преобразований без знака '%o', '%u', '%x', и '%X'). Заметьте, что т. к. printf и ее производные функции, любой аргумент типа char и short автоматически приводится к типу int аргументами, заданными по умолчанию. Для аргументов других целочисленных типов, Вы можете использовать следующие модификаторы:

' H '

Определяет, что аргумент - short int или short unsigned int.

' l '

Определяет, что аргумент - long int или long unsigned int

' L '

Определяет, что аргумент - long long int. (Этот тип яв­ ляется расширением, обеспечиваемым компилятором GNU С. На системах, которые не поддерживают сверхдлинные целые числа, это - тоже что long int.)

Модификаторы для типа аргумента не применимы к '%Z', так как единственной целью '%Z' является указать тип данных size_t.

Вот пример использования строки шаблона:

        '| %5d| %-5d| %+5d| %+-5d| %5d| %05d| %5.0d| %5.2d| %d|\n '
для печати числа, используя различные опции для преобразования '%d':
       | 0    | 0    | + 0    | + 0  | 0    | 00000|      |  00  |  0 |
       | 1    | 1    | + 1    | + 1  | 1    | 00001| 1    |  01  |  1 |
       | -1   | -1   | -1     | -1   | -1   | -0001| -1   | -01  | -1 |
       |100000|100000|+ 100000|100000|100000|100000|100000|100000|...
В частности обратите внимание на то, что получается в последнем случае, где аргумент слишком большой, чтобы поместиться в минимальной заданной ширине поля.

Вот еще несколько примеров, показывающих, как выводятся беззнаковые целые под различными опциями формата, используя строку шаблона:

      '| %5u | %5o | %5x | %5X | %*5o | %*5x | %*5X | %*10.8x | \n'
  |    0 |    0 |   0 |   0 |     0 |   0x0 |   0X0 |0x00000000|
  |    1 |    1 |   1 |   1 |    01 |   0x1 |   0X1 |0x00000001|
  |100000|303240|186a0|186A0|0303240|0x186a0|0X186A0|0x000186a0|

Преобразования с плавающей запятой

Этот раздел содержит спецификации форматов вывода чисел с плаваю­ щей запятой: '%f', '%e', '%E', '%g', и '%G'. Формат '%f' печатает ар­ гумент в формате с фиксированной запятой, выводя его на экран в виде [-] ddd.ddd, где число цифр после десятичной точки определяется точ­ ностью, которую Вы указали.

Формат '%e' печатает аргумент в экспоненциальном представлении, выводя его на экран в виде [-] d.ddde [+ | -] dd. Число цифр после десятичной точки также определяется точностью. Экспонента всегда содержит по крайней мере две цифры. На этот формат похож '%E', но экспонента отмечена символом ' E ' вместо ' e '.

Форматы '%g' и '%G' печатают аргумент в стиле '%e' и '%E' соответственно, если экспонента меньше чем -4 или больше либо равна точности; в противном случае они используют стиль формата '%f'. Конечные нули из дробной части результата удаляются, а символ десятичной точки появляется только, если он сопровождается цифрой.

Чтобы изменить поведение функции, могут применяться следующие флаги:

'-'

Выравнивание слева результата в поле вывода. Обычно результат выравнивается справа.

'+'

Всегда выводится знак 'плюс' или 'минус'.

' '

Если результат не начинается со знака 'плюс' или 'минус', ставит перед ним пробел.

'#'

Определяет, что результат должен всегда включать десятичную точку, даже если за ней не следует никаких цифр. Для форматов '%g' и '%G', конечные нули после десятичной точки удаляться не будут.

'0'

Дополняет поле нулями вместо пробелов; нули помещаются после знака.

Этот флаг игнорируется, если указан флаг '-'.

Точность определяет, сколько цифр следуют за символом десятичной точки для форматов '%f','%e', и '%E'. Точность, заданная по умолчанию для этих форматов - 6. Если она явно задана как 0, то символ десятичной точки подавляется. Для форматов '%g' и '%G', точность определяет сколько значащих цифр печатать. Значащие цифры это первая цифра перед десятичной точкой, и все цифры после нее. Если для '%g' или '%G' точность - 0 или не задана, то она обрабатывается как если бы была 1. Если напечатанное значение не может быть выражено точно заданным количеством цифр, то значение округляется до ближайшего подходящего значения.

Без модификатора типа, форматы с плавающей запятой используют аргумент двойного типа. (По умолчанию любой аргумент типа float автоматически преобразуется в double.) Поддерживаются следующие модификаторы типов:

'L' Определяет, что аргумент типа long double. Вот несколько примеров

как при выводе используются различные форматы чисел с плавающей запятой. Все числа были напечатаны, используя следующий шаблон строки:

      '|%12.4f|%12.4e|%12.4g|\n'

      |      0.0000|  0.0000e+00|           0|
      |      1.0000|  1.0000e+00|           1|
      |     -1.0000| -1.0000e+00|          -1|
      |    100.0000|  1.0000e+02|         100|
      |   1000.0000|  1.0000e+03|        1000|
      |  10000.0000|  1.0000e+04|       1e+04|
      |  12345.0000|  1.2345e+04|   1.234e+04|
      | 100000.0000|  1.0000e+05|       1e+05|
      | 123456.0000|  1.2346e+05|   1.234e+05|
Обратите внимание как формат '%g' выводит конечные нули.

Другие Форматы Вывода

Этот раздел описывает различные форматы, используемые printf.

Формат '%c' печатает одиночный символ. Аргумент типа int сначала преобразовывается в unsigned char. Может использоваться флаг '-' для задания выравнивания слева в поле вывода, но точность или модификатор типа не могут быть определены. Например:

       printf ('%c%c%c%c%c', 'h', 'e', 'l', 'l', 'o');
выводит
        `hello'.

        printf ('%3s%-6s', 'no', 'where');
выводит
        `nowhere'.

Если Вы случайно передаете в качестве аргумента для формата '%s' пустой указатель преобразования, библиотека GNU выведет '(null)'. Мы думаем, что это более полезно чем сообщение об ошибке.

     fprintf (stderr,   'can't  open  `%s':  %m\n',  filename);
является эквивалентным:
     fprintf (stderr,   'can't   open  `%s':  %s\n',  filename,  strerror (errno));
Формат '%m' - расширение библиотеки GNU С.

Формат '%p' печатает значение указателя. Соответствующий аргумент должен иметь тип void*. Практически, Вы можете использовать любой тип указателя.

В системе GNU, непустые указатели печатаются как integers unsigned, как при использовании формата '%#x'. Пустые указатели печатаются как '(nil)'. (В других системах указатели могут печататься по-другому.)

Например:

              printf ('%p',   'testing');
печатает '0x' сопровождаемый шестнадцатеричным числом адреса строковой константы 'testing'.

Вы можете добавить флаг '-' к формату '%p', чтобы обеспечить выравнивание слева, но не должны определяться никакие другие флаги, точность, или модификаторы типа.

Формат '%n' - отличается от всех других форматов вывода. Он использует аргумент, который должен быть указателем на int, но вместо того, чтобы печатать что-нибудь, он содержит число уже напечатанных символов. Модификаторы типа 'h' и 'l' задают, что указывается аргумент типа short int* или long int * вместо int *, но не позволяется указывать никакие флаги, ширину поля, или точность.

Например,

   int nchar; printf ('%d %s%n\n', 3, 'bears', &nchar);
печатает:
          3 bears
и устанавливает nchar в значение 7, потому что строка '3 bears' содержит семь символов.

Формат '%%' выводит символ `%'. Этот формат не использует аргумент.

Функции Форматированного Вывода

Этот раздел описывает, как вызвать printf и относящиеся к ней функции. Прототипы для этих функций находятся в файле 'stdio.h'. Т. к. эти функции принимают переменное число аргументов, Вы должны объявить прототипы для них перед использованием. Конечно, самый простой способ удостовериться, что Ваши прототипы все правильные, это включить 'stdio.h'.

- Функция: int printf (const char *TEMPLATE, ...)

Функция printf выводит указываемые аргументы под управлением шаблона строки TEMPLATE в поток stdout. Она возвращает число напечатанных символов, или отрицательное значение при ошибке вывода.

- Функция: int fprintf (FILE *stream, const char *template, ...)

Эта функция - аналог printf, за исключением того, что вывод записывается в указанный поток вместо stdout.

- Функция: int sprintf (char *s, const char *template, ...)

Она подобна printf, за исключением того, что вывод сохраняется в символьном массиве s вместо записи в поток. Пустой символ записывается в конец строки.

Функция sprintf возвращает число символов, содержащихся в массиве s, исключая пустой символ завершения.

Поведение этой функции неопределено, если копирование происходит между пересекающимися объектами. Например, если s также является аргументом, который выводится под управлением формата '%s'. См. Раздел 5.4 [Копирование и Конкатенация].

Предупреждение: функция sprintf может быть опасна, т. к. она может потенциально выводить большее количество символов чем размер, зарезервированный для строки s. Не забудьте, что ширина поля, заданная в спецификации формата - только минимальное значение.

Чтобы избежать этой проблемы, Вы можете использовать snprintf или asprintf, описанные ниже.

- Функция: int snprintf (char *s, size_t size, const char *TEMPLATE, ...)

Функция snprintf подобна sprintf, за исключением того, что аргумент размера определяет максимальное число выводимых символов. Конечный пустой символ подпадает под это ограничение

Возвращаемое значение - число сохраненных символов, не включая пустой символ завершения. Если это значение равняется size-1, то в s недостаточно места для всего вывода. Вы должны пробовать снова с большей строкой вывода. Вот пример такого кода:

           /* Выдаем сообщение, описывающее значение переменной
              с именем  NAME  и  значением  VALUE  */ char * make_message
           (char *name, char *value) {
             /* Предположим, что нам понадобится не больше чем 100
                символов. */ int size = 100;  char  *buffer  =  (char  *)
             xmalloc (size);

           while (1) {
                 /* Попытаемся напечатать в отведенном пространстве. */
                 int nchars  =  snprintf (buffer,  size,  'value of %s is
                                        %s', name, value);
                 /* If that worked, return the string. */
                 if (nchars < size) return buffer;
                 /* Иначе попытаемся еще раз с удвоенным количеством
                    символов */ size *= 2;  buffer =  (char  *)  xrealloc
                 (size, buffer);
               }
           }

На деле, часто проще использовать asprintf, см. ниже.

Форматируемый Вывод, Размещаемый Динамически

Функции в этом разделе форматируют вывод и помещают результаты в динамически размещенную память.

- Функция : int asprintf (char **ptr, const char *template, ...)

Эта функция похожа на sprintf, за исключением того, что она динамически распределяет строку для вывода (как malloc; см. Раздел 3.3 [Беспрепятственное Резервирование], вместо того, чтобы помещать вывод в буфер, определяемый заранее. Аргумент ptr должен быть адресом объекта char*, и asprintf сохраняет в нем указатель на размещенную строку.

   char * make_message (char *name, char *value) {
     char *result;  asprintf (&result, 'value of %s is %s', name,
     value); return result;
   }
-Функция: int obstack_printf(struct obstack* obstack,const char*

template,...)

Эта функция подобна asprintf, за исключением того, что она использует obstack, чтобы зарезервировать пространство в памяти. См. Раздел 3.4 [Obstack].

Символы дописываются в конец текущего объекта. Чтобы добраться до них, Вы должны закончить объект функцией obstack_finish (см. Раздел 3.4.6 [Возрастастающие Объекты]).

Переменные Аргументы Функций Вывода

Функции vprintf и подобные позволяют Вам определять ваши собственные различные printf-подобные функции, которые используют ту же самую внутреннюю организацию как и встроенные форматирующие функции вывода.

Наиболее естественный способ определять такие функции состоит в том, чтобы использовать конструкцию типа 'Вызвать printf и передать ему этот шаблон плюс все мои аргументы пропуская первые пять.' Но не имеется никакого способа сделать это на C, и было бы трудно это сделать, потому что на уровне Языка C не имеется никакого способа указать сколько аргументов получает ваша функция.

Так как тот метод невозможен, мы поставляем дополнительные функции типа vprintf, которые позволяет Вам передавать va_list, чтобы описать 'все мои аргументы после первых пяти.'

Перед вызовом vprintf или других функций, перечисленных в этом разделе, Вы должны вызвать va_start (см. Раздел A. 2 [Variadic Функции]) чтобы установить указатель на переменные аргументы. После этого Вы можете вызывать va_arg, чтобы выбрать аргументы, которые Вы хотели обработать.

Если ваш vуказатель a_list указывает на аргументы вашего выбора, Вы можете вызвать vprintf. Этот аргумент и все последующие аргументы, которые были переданы вашей функции, используются vprintf наряду с шаблоном, который Вы определили отдельно.

В некоторых других системах, указатель va_list может стать недопустимым после обращения к vprintf, так что Вы не должны использовать va_arg после того, как Вы вызываете vprintf. Вместо этого, Вы должны вызвать va_end, чтобы отменить указатель. Затем Вы можете безопасно вызывать va_start для другой переменной указателя и начинать выбирать аргументы снова через этот указатель. Вызов vprintf не разрушает список параметров вашей функции.

GNU C не имеет таких ограничений. Вы можете продолжать выбирать аргументы из списка va_list после выполнения через vprintf, тогда va_end - пустая команда. (Примечание, последующие обращения va_arg выберут те же самые аргументы, как и те, что предварительно использованы vprintf.)

Прототипы для этих функций объявлены в 'stdio.h'.

- Функция: int vprintf (const char *template, va_list ap)

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

- Функция: int vfprintf (FILE *stream, const char *template, va_list ap)

Эта функция - эквивалент fprintf с переменным списком параметров, заданным непосредственно как и для vprintf.

- Функция: int vsprintf (char *s, const char *template, va_list ap)

Эта функция - эквивалент sprintf с переменным списком параметров, заданным непосредственно как в vprintf.

- Функция: int vsnprintf (char *s, size_t size, const char *template, va_list ap)

Это функция - эквивалент snprintf с переменным списком параметров, заданным непосредственно как в vprintf.

- Функция: int vasprintf (char **ptr, const char *template, va_list ap)

Функция vasprintf - эквивалент asprintf с переменным списком параметров, заданным непосредственно как и для vprintf.

- Функция: int obstack_vprintf (struct obstack *obstack, const char* template, va_list ap)

Функция obstack_vprintf - эквивалент obstack_printf с переменным списком параметров, заданным непосредственно как для vprintf.

Ниже приводится пример, показывающий, как Вы могли бы использовать vfprintf. Это - функция, которая выводит сообщения об ошибках в поток stderr, вместе с префиксом, указывающим имя программы (см. Раздел 2.3 [Сообщения об ошибках], описание program_invocation_short_name).

      #include 
      #include 

      void
      eprintf (const char *template, ...)
      {
        va_list ap;
        extern char *program_invocation_short_name;

        fprintf (stderr, '%s: ', program_invocation_short_name);
        va_start (ap, count);
        vfprintf (stderr, template, ap);
        va_end (ap);
      }
Вы могли бы вызывать eprintf например так:
      eprintf ('file `%s' does not exist\n', filename);

Синтаксический разбор Строки Шаблона

Вы можете использовать функцию parse_printf_format, для получения информации относительно числа и типов аргументов, которые ожидаются данной строкой шаблона. Эта функция вызывает интерпретаторы, которые обеспечивают интерфейс в printf во избежание передачи недопустимых аргументов.

Все символы, описанные в этом разделе объявлены в файле заголовков 'printf.h'.

- Функция: size_t parse_printf_format (const char *template, size_t n, int* argtypes)

Эта функция возвращает информацию относительно числа и типов аргументов, ожидаемых printf в строке шаблонов. Информация сохраняется в массиве argtypes; каждый элемент этого массива описывает один аргумент. Эта информация предоставляется в виде различных 'PA_' макрокоманд, которые перечисляются ниже.

Аргумент n определяет число элементов в массиве argtypes. Это наибольшее число элементов, которые parse_printf_format пробует написать.

Parse_printf_format возвращает общее число аргументов, требуемых шаблоном. Если это число больше n, то возвращаемая информация, описывает только первые n аргументов. Если Вы хотите получить информацию относительно большего, чем n, числа аргументов, зарезервируйте больший массив и вызовете parse_printf_format снова.

Типы аргумента закодированы как комбинация базисного типа и битов флага модификатора.

- Макрос: int PA_FLAG_MASK

Эта макрокоманда - маска для битов флага модификатора типа. Вы можете написать выражение (argtypes [i] & PA_FLAG_MASK) чтобы извлечь только биты флага для аргумента, или (argtypes [i] & ~PA_FLAG_MASK) чтобы извлечь только базисный код типа.

Имеются символические константы, которые представляют базисные типы; они устанавливаются для значений integer.

       PA_INT     Определяет, что исходный тип - int.
       PA_CHAR    Определяет, что исходный тип - int, приведеннное к char.
       PA_STRING  Определяет, что исходный тип - char *, строка с нулевым
                  символом в конце.
       PA_POINTER Определяет, что исходный тип - void *, произвольный
                  указатель.
       PA_FLOAT   Определяет,  что  исходный  тип  с  плавающей точкой.
       PA_DOUBLE  Определяет,  что исходный тип - double.
       PA_LAST    Вы можете определять дополнительные исходные типы для ваших
                  собственных программ как смещения из PA_LAST.
Например, если у Вас определены типы данных 'foo' и 'bar' с их собственными специализированными форматами для printf, то Вы можете определять эти типы как:
       #define PA_FOO  PA_LAST
       #define PA_BAR  (PA_LAST + 1)
Имеются биты флага, которые изменяют базисный тип. Они объединены с кодом для базисного типа, используя операцию или.
PA_FLAG_PTR

Если этот бит устанавливается, это указывает, что закодированный тип - указатель на исходный тип, а не непосредственное значение.

Например, 'PA_INT | PA_FLAG_PTR' представляет тип `int *'.

PA_FLAG_SHORT

Если этот бит устанавливается, это указывает, что исходный тип изменяется как short. (Соответствует модификатору типа 'h'.)

PA_FLAG_LONG

Если этот бит устанавливается, это указывает, что исходный тип изменяется как long. (Соответствует модификатору типа 'l'.)

PA_FLAG_LONG_LONG

Если этот бит устанавливается, это указывает, что исходный тип изменяется как long long.

PA_FLAG_LONG_DOUBLE

Это - синоним для PA_FLAG_LONG_LONG, используемого обычно с исходным типом PA_DOUBLE для обозначения типа long double.

Пример Синтаксического анализа Строки Шаблона

      /* Проверка, является ли NARGS, определяющий объекты вектора
         ARGS формату строки FORMAT:
         если да, возвращает 1.
         в противном случае возвращает 0 после вывода сообщения об ошибке */

      int
      validate_args (char *format, int nargs, OBJECT *args)
      {
        int *argtypes;
        int nwanted;

        /* Получить информацию об аргументах.
           Каждый спецификатор формата должен быть длиной по крайней мере
           в два символа, поэтому не может быть спецификаторов длиной
           длиной больше половины длины строки.
        */

        argtypes = (int *) alloca (strlen (format) / 2 * sizeof (int));
        nwanted = parse_printf_format (string, nelts, argtypes);

        /* Проверим количество аргументов  */
        if (nwanted > nargs)
          {
            error ('too few arguments (at least %d required)', nwanted);
            return 0;
          }

        /* Проверим тип, требуемый для каждого аргумента,
           и соответствие ему данного аргумента.  */
        for (i = 0; i < nwanted; i++)
          {
            int wanted;

            if (argtypes[i] & PA_FLAG_PTR)
              wanted = STRUCTURE;
            else
              switch (argtypes[i] & ~PA_FLAG_MASK)
                {
                case PA_INT:
                case PA_FLOAT:
                case PA_DOUBLE:
                  wanted = NUMBER;
                  break;
                case PA_CHAR:
                  wanted = CHAR;
                  break;
                case PA_STRING:
                  wanted = STRING;
                  break;
                case PA_POINTER:
                  wanted = STRUCTURE;
                  break;
                }
            if (TYPE (args[i]) != wanted)
              {
                error ('type mismatch for arg number %d', i);
                return 0;
              }
          }
        return 1;
      }

7.10 Настройка printf

Библиотека GNU C позволяет Вам определять ваши собственные спецификаторы преобразования для строк шаблона printf, т.е. научить printf выводить важные структуры данных вашей программы так, как Вам этого хочется.

Это можно сделать, указав формат преобразования с помощью функции register_printf_function; см. Раздел 7.10.1 [Указание Новых Форматов Вывода]. Один из аргументов, которые Вы передаете этой функции ­ указатель на функцию обработчика, которая производит фактический вывод; за более подробной информацией о написании этой функции обращайтесь к Разделу 7.10.3 [Определение Обработчика Вывода].

Вы можете также установить функцию, которая возвращает информацию относительно числа и типа аргументов, ожидаемых спецификатором формата преобразования. См. Раздел 7.9.10 [Синтаксический анализ Строки Шаблона], для получения дополнительной информации об этом.

Средства этого раздела объявлены в файле 'printf.h'.

Примечание о Переносимости: возможность изменения синтаксиса printf строк шаблона - расширение GNU. Стандарт ANSI C не имеет ничего подобного.

Указание Новых Форматов Вывода

Функция для регистрирования новых преобразований вывода register_printf_function, объявлена в `printf.h'.

- Функция: int register_printf_function(int SPEC,printf_function HANDLER_FUNCTION, printf_arginfo_function ARGINFO_FUNCTION)

Эта функция определяет символ спецификатора преобразования SPEC.

Так, если SPEC равен 'q', то определяется модификатор '%q'.

HANDLER_FUNCTION - функция, вызываемая printf, когда это модификатор появляется в строке шаблона. См. Раздел 7.10.3 [Определение Обработчика Вывода], для уточнения информации относительно того, как определить функцию в качестве этого аргумента. Если Вы задаете пустой указатель, существующая функция обработчика для спецификаций удаляется.

Arginfo_function - функция, вызываемая parse_printf_format, когда это преобразование появляется в строке шаблона. См. Раздел 7.9.10 [Синтаксический анализ Строки Шаблона]. Обычно Вы устанавливаете обе функции обработки вывода одновременно, но если Вы никогда не обращаетесь к parse_printf_format, Вы не должны определять функцию arginfo_function.

Возвращаемое значение - 0 в случае успеха, и -1 при сбое (который происходит, если спецификации находятся вне диапазона).

Вы можете переопределять форматы стандартного вывода, но это ­ возможно не лучшая идея из-за потенциального путаницы. Если Вы это сделаете, могут пострадать библиотечные подпрограммы, написанные другими людьми.

Ключи Спецификатора Преобразования

Если Вы определяете какое-либо значение для '%q',то что произойдет, если шаблон содержит '%+23q' или '%-#q'? Чтобы реализовать обработку этого, обработчик должен быть способен получить ключи, определенные в шаблоне.

Оба аргумента функции register_printf_function - HANDLER_FUNCTION и ARGINFO_FUNCTION получает в качестве аргумента типа struct printf_info, который содержит информацию относительно ключей, появляющихся в образце спецификатора формата вывода. Этот тип данных объявлен в заглавном файле 'printf.h'.

- Тип: struct printf_info

Эта структура используется для передачи информации о ключах, появляющихся в образце спецификатора формата вывода в printf строке шаблона в обработчик и функции arginfo для их спецификатора. Она содержит следующие элементы:

'int prec'

Эта переменная содержит задаваемую точность. Значение -1, если никакая точность не была определена. Если, точность была задана как '*', структура printf_info, переданная в функцию обработчика, содержит фактическое значение, взятое из списка параметров. Но структура, переданная в функция arginfo содержит значение INT_MIN, так как фактическое значение не известно.

'int width'

Эта переменная содержит задаваемую минимальную ширину поля вывода. Значение 0, если ширина не была определена. Если, ширина поля была задана как '*', структура printf_info, переданная в функцию обработчика, содержит фактическое значение, взятое из списка параметров. Но структура, переданная в функция arginfo содержит значение INT_MIN, так как фактическое значение не известно.

'сhar spec'

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

'unsigned int is_long_double'

Это - логическая переменная, которая содержит значение истина, если модификатор типа 'L' был определен.

'unsigned int is_short'

Это - логическая переменная, которая содержит значение истина, если модификатор типа 'h' был определен.

'unsigned int is_long'

Это - логическая переменная, которая содержит значение истина, если модификатор типа 'l' был определен.

'unsigned int alt'

Это - логическая переменная, которая содержит значение истина, если был определен флаг '#'.

'unsigned int space'

Это - логическая переменная, которая содержит значение истина, если был определен флаг ' '.

'unsigned int left'

Это - логическая переменная, которая содержит значение истина, если был определен флаг '-'.

'unsigned int showsign'

Это - логическая переменная, которая содержит значение истина, если был определен флаг '+'.

'char pad'

Это - символ, использующийся для дополнения вывода в минимальную ширину поля. Значение - '0' если был определен определен флаг '0' , иначе ' '.

Определение Обработчика Вывода

Теперь рассмотрим, как определить функцию обработчика и функции arginfo, которые передаются как аргументы для register_printf_functi­ on.

Вы должны определить ваши функции обработчика с прототипом следу­ ющим образом:

 int function (FILE *stream,  const  struct  printf_info  *info,  va_list *ap_pointer)

Аргумент stream, переданный функции обработчика - это поток, в который она должна записать вывод.

Аргумент info - указатель на структуру, которая содержит информацию относительно различных ключей, которые были включены в строку шаблона. Вы не должны изменять эту структуру внутри вашей функции обработчика. См. Раздел 7.10.2 [Опции Спецификатора Преобразования], для описания этой структуры данных.

Аргумент ap_pointer используется для передачи хвоста списка параметров, содержащего значения котторые Ваш обработчик должен напечатать. В отличие от большинства других функций, которым может быть передан явный список параметров, здесь передается указатель на va_list, а не сам va_list. Таким образом, Вы должны обрабатывать аргументы с помощью va_arg(TYPE, * ap_pointer).

( Введение указателя здесь позволяет указать функцию, которая вызывает вашу функцию обработчика, чтобы модифицировать собственную переменную va_list, для обновления информации о параметрах, которые ваш обработчик обрабатывает. См. Раздел A. 2 [Variadic (функция)] 2.)

Ваша функция обработчика должна возвратить значение точно так же как это делает printf: она должна возвратить число символов, которое она написала, или отрицательное значение, чтобы указать ошибку.

-Тип данных: printf_function

Это тип данных, который должна иметь функция обработчика. Если Вы собираетесь использовать parse_printf_format в вашем приложении, то Вы должны также определить функцию, являющуюся параметром arginfo_function для каждого нового формата, который Вы устанавливаете с помощью register_printf_function.

Вот прототип подобной функции:

 int function  (const struct printf_info *info,size_t n,int *argtypes);

Функция должна возвращать число параметров, которые обрабатывает формат вывода. Кроме того, функция не должна заполнять больше, чем n элементов argtypes массива с информацией относительно типов каждого из этих параметров. Эта информация закодирована с помощью 'PA_' макрокоманд. (Обратите внимание, что это - то же самое что и соглашение о вызовах parse_printf_format.)

-Тип данных: printf_arginfo_function

Этот тип используется, для описания функций, которые возвращают информацию о числе и типе параметров, используемых спецификатором формата вывода.

t

Пример Расширения Printf

Вот пример, показывающий, как определять printf функцию обработчика. Эта программа определяет структуру данных называемую Widget и определяет формат '%M', для печати информации о параметре Widget* включая значение указателя и имя, содержащееся в структуре данных. Формат вывода '%W' поддерживает минимальную ширину поля и опции левого выравнивания, но игнорирует все остальные.

      #include 
      #include 
      #include 
      typedef struct
        {
          char *name;
        } Widget;



      int
      print_widget (FILE *stream, const struct printf_info *info, va_list *app)
      {
        Widget *w;
        char *buffer;
        int len;

        /* Преобразуем выходную информацию в строку. */
        w = va_arg (*app, Widget *);
        len = asprintf (&buffer, '', w, w->name);
        if (len == -1)
          return -1;
        /* Заполняем поле минимальной длины и выводим в поток.*/
        len = fprintf (stream, '%*s',
                       (info->left ? - info->width : info->width),
                       buffer);

        /* Сброс и возврат. */
        free (buffer);
        return len;
      }


      int
      main (void)
      {

         / * Создаем widget, который необходимо напечатать. * /
         Widget mywidget;
         mywidget.name = 'mywidget';

         / * Теперь печатаем widget. * /
         printf ('|%W|\n',  &mywidget);
         printf ('|%35W|\n',  &mywidget);
         printf ('|%-35W|\n', &mywidget);
         return 0;
 }
Программа выводит:
      ||
      |      |
      |      |

7.11 Форматируемый Ввод

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

Основы Форматируемого Ввода

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

Другая отличие между scanf и printf - то, что Вы должны не забыть обеспечивать указатели а не непосредственные значения как необязательные параметры scanf; значения, которые читаются, сохраняются в объектах, на которые указывают указатели. Даже опытные программисты имеют тенденцию забывать это иногда, так если ваша программа получает странные ошибки, которые, кажется, связаны со scanf, Вам следует дважды проверить это.

Когда происходит ошибка несоответствия , scanf немедленно прерывается, оставляя первый символ несоответствия как следующий символ, который нужно читать из потока. Нормальное возвращаемое значение из scanf - число значений, которые были назначены, так что Вы можете использовать это, чтобы определить, случалась ли ошибка соответствия прежде, чем все прочитались ожидаемые значения.

Функция scanf обычно используется для программ вроде считывания содержания таблиц. Ниже приводится функция, которая использует scanf для того, чтобы инициализировать массив элементов double:

 void  readarray (double *array, int n) {
         int i; for (i=0; i
Функции форматируемого ввода используются не так часто как функции форматируемого вывода.

Если Вы пытаетесь считывать входную информацию, которая не соответствует ни простому, фиксированному шаблону, то может быть Вам лучше использовать средства типа Flex, чтобы сгенерировать лексический сканер, или Bison, чтобы сгенерировать синтаксический анализатор, а не использовать scanf. Для получения более подробной информации, см. раздел 'Flex' в Flex: Лексический Генератор Ввода и раздел 'Bison' в Справочном описании Bison.

Синтаксис Входных Форматов

Строка шаблона для scanf это строка, которая содержит обычные многобайтовые символы со спецификациями преобразования, которые начинаются с '%'.

Любой пробельный символ (как определено функцией isspace; см. Раздел 4.1 [Классификация Символов]) в шаблоне заставляет любое число символов промежутка во входном потоке читаться и отбрасываться. Символы промежутка, которые согласованы могут быть не точно те же самые символы промежутка которые появляются в строке шаблона. Например, добавьте ',' к шаблону, чтобы распознать запятую с любым количеством пробелов до и после нее.

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

Спецификации преобразования в строке scanf шаблона имеют общую форму:

       % FLAGS WIDTH TYPE CONVERSION
Более подробно, входная спецификация преобразования состоит из начального символа '%', сопровождаемого последовательностью:
  • Необязательный флаг '*', который говорит игнорировать чтение текста для этой спецификации. Когда scanf находит спецификацию формата вывода, который использует этот флаг, она читает ввод так, как это задается остальной частью спецификации формата, но она отбрасывает этот ввод, не использует аргумент указателя, и не увеличивает число успешных назначений.
  • Необязательный флаг 'а' (допустимый со строковыми форматами) задает резервирование достаточно большого буфера чтобы хранения строки. (Это - расширение GNU.) См. Раздел 7.11.6 [Динамический Строковый Ввод].
  • Необязательный десятичное целое число, которое определяет максимальную ширину поля. Чтение символов из входного потока останавливается либо когда достигнут этот максимум, либо когда найден символ несоответствия. Большинство преобразований отбрасывает начальные символы промежутка (если не явно указаны), и эти отброшенные символы не увеличивают счетчик максимальной ширины поля. Строковые входные преобразования сохраняют пустой символ, отмечая конец ввода; максимальная ширина поля не включает этот признак конца.
  • Необязательный символ модификатора типа. Например, Вы можете задавать модификатор типа 'l' с преобразованиями integer типа '%d' чтобы указать, что аргумент - указатель на long int а не указатель на int.
  • Символ, который определяет применяемый формат. Точные опции и как они интерпретируются, все это изменяется между различными спецификаторами преобразования. См. описания конкретных спецификаторов для уточнения информации относительно опций, которые они допускают.

Таблица Входных Преобразований

Ниже приводится таблица, которая содержит различные спецификации форматов:

'%d'

Соответствует необязательно целому числу со знаком, которое записано в десятичном виде. См. Раздел 7.11.4 [Входные Форматы Чисел].

'%i'

Соответствует необязательно целому числу со знаком в любом из форматов, которые Язык C определяет для определения константы integer. См. Раздел 7.11.4 [Числовые Входные Преобразования].

'%o'

Соответствует integer unsigned, который записан в восьмеричной системе счисления. См. Раздел 7.11.4 [Числовые Входные Форматы].

'%u'

Соответствует беззнаковому целому, записанному в десятичной системе счисления. См. Раздел 7.11.4 [Числовые Входные Преобразования].

'%x', '%X'

Соответствуют беззнаковому целому, записанному в шестнадцатеричной системе счисления. См. Раздел 7.11.4 [Числовые Входные Преобразования].

'%e', '%f', '%g', '%E', '%G'

Соответствуют необязательному знаковому числу с плавающей запятой. См. Раздел 7.11.4 [Числовые Входные Преобразования].

'%s'

Соответствует строке, содержащей только символы непромежутка. См. Раздел 7.11.5 [Входные Преобразования Строки].

'%['

Соответствует строке символов, которые принадлежат заданному множеству. См. Раздел 7.11.5 [Входные Преобразования Строки].

'%c'

Соответствует строке из одного или большего количества символов; чтение числа символов управляется максимальной шириной поля, заданной для формата.

'%p'

Соответствует значению указателя в том же самом определенном реализацией формате, используемом форматом вывода printf '%p'. См. Раздел 7.11.7 [Другие Входные Форматы].

'%n'

Это преобразование не читает никакие символы; оно записывает число прочитанных символов. См. Раздел 7.11.7 [Другие Входные Преобразования].

'%%'

Это соответствует символу `%' во входном потоке. См. Раздел 7.11.7 [Другие Входные Преобразования]. Если синтаксис спецификации преобразования является недопустимым, поведение неопределено. Если не достаточно аргументов функции, чтобы обнспечить адреса для всех спецификаций формата в строках шаблона, которые выполняют назначения, или если аргументы имеют неправильные типы, поведение также неопределено. С другой стороны, дополнительные аргументы просто игнорируются.

Числовые Входные Преобразования

Этот раздел описывает преобразования scanf для чтения числовых значений.

Формат ввода '%d' соответствует необязательно целому числу со знаком в десятичной системе счисления. Синтаксис - такой же как и для функции strtol (см. Раздел 14.7.1 [Синтаксический анализ Целых чисел] ) со значением 10 для основного аргумента.

Формат ввода '%i' соответствует необязательному целому числу со знаком в любом из форматов, которые Язык C определяет для целой константы. Синтаксис - такой же как для функции strtol (см. Раздел 14.7.1 [Синтаксический анализ Целых чисел] ) со значением 0 для основного аргумента. (Вы можете печатать integer в этом синтаксисе через printf, используя флаг '*' с форматами '%x', '%o', или '%d'. См. Раздел 7.9.4 [Целочисленные Преобразования].)

Например, любая из строк '10', '0xa', или '012' может cчитываться как целая константа при преобразовании '%i'. Каждая из этих строк определяет число с десятичным значением 10.

Форматы ввода '%o', '%u', и '%x' соответствуют беззнаковому целому в восьмеричном, десятичном, и шестнадцатеричном форматах соответственно. Синтаксис - такой же как для функции strtoul (см. Раздел 14.7.1 [Синтаксический анализ Целых чисел] ) с соответствующим значением (8, 10, или 16) для основного аргумента.

Формат '%X' идентичен формату '%x'. Оба они разрешают использовать как цифры или символы верхнего регистра или символы нижнего регистра.

Заданный по умолчанию тип соответствующего аргумента форматов '%d' и '%i' является 'int *', и 'unsigned int *' для других целочисленных форматов. Вы можете использовать следующие модификаторы типа, чтобы задать другие размеры integer:

  • 'h' Определяет, что аргумент - short int * или short unsigned int*.
  • 'l' Определяет, что аргумент - long int * или long unsigned int*.
  • 'L' Определяет, что аргумент - long long int * или long long unsigned int *. (Тип long long - расширение, обеспечиваемое компилятором GNU C. Для систем, которые не обеспечивают сверхдлинных целых, это ­ то же что long int.)
Все входные форматы '%e', '%f', '%g', '%E', и '%G' взаимозаменяемы. Они все соответствуют необязательному знаковому числу с плавающей запятой, в том же самом синтаксисе как для функции strtod (см. Раздел 14.7.2 [Синтаксический анализ Чисел с Плавающей Запятой] ).

Для преобразований с плавающей запятой, заданный по умолчанию тип аргумента - float *. (Это отличается от соответствующих преобразований вывода, где заданный по умолчанию тип - double; не забудьте, что аргументы float в printf преобразовываются в double заданными по умолчанию поддержками аргумента, но float * аргументы не преобразуются в double *.) Вы можете задавать другие размеры float, используя следующие модификаторы типа:

  • ' l ' Определяет, что аргумент имеет тип double *.
  • ' L ' Определяет, что аргумент имеет тип long double *.

Строковые Входные Преобразования

Этот раздел описывает преобразования ввода scanf для чтения строковых и символьных значений: '%s', '%[', и '%c'.

Имеется два способа получить ввод от этих преобразований:

  • Определить буфер для хранения входной информации. Этот способ задан по умолчанию. Вы должны указать аргумент типа char*. Предупреждение: для того, чтобы сделать надежную программу, Вы должны удостовериться, что входная информации (плюс пустой символ завершения) не может превышать размер буфера, котрый Вы определили. Вообще, единственный способ сделать это - задать максимальную ширину поля на один меньше чем размер буфера. Если Вы определяете буфер, всегда задавайте максимальную ширину поля для предотвращения переполнения.
  • Указать функции scanf зарезервировать достаточно большой буфер, задавая флаг 'f'. Это является расширением GNU. Вы должны указать аргумент типа char ** для сохранения адреса буфера. См. Раздел 7.11.6 [Динамический Строковый Ввод].
Формат '%c' (самое простое): оно соответствует фиксированному числу символов. Если Вы не задаете максимум длины поля ввода, значение по умолчанию 1. Это преобразование не добавляет пустой символ в конец текста, которую оно читает. Оно также не пропускает начальные пробельные символы. Оно читает ровно n символов, и выдает ошибку, если оно не может их получить. Так как при '%c' всегда имеется максимальная ширина поля (заданная, или 1 по умолчанию), Вы всегда можете предотвратить переполнение, делая буфер достаточно большим.

Формат '%s' соответствует строке непробельных символов. Оно пропускает и отбрасывает начальные пробельные символы, но останавливается, когда сталкивается с пробельным символом когда уже что-то считано. Он сохраняет пустой символ в конце текста, который считывает. Например, при считывании:

       hello, world
формат '%10c' производит 'hello, wo', а считывании того же самого с форматом '%10s' производит 'hello,'.

Предупреждение: если Вы не задаете ширину поля для '%s', то число символов, ограничено только тем, где появится следующий пробельный символ. Это конечно означает что недопустимый ввод может вызвать ошибку.

Чтобы считывать символы, которые принадлежат произвольному набору по вашему выбору, используйте формат '%['. Вы определяете набор между символом `[' и следующим символом `]', используя синтаксис, используемый в регулярных выражениях. Частные случаи:

  • Литеральный символ `]' может быть определен как первый символ набора.
  • Внутренний символ `-' (то есть тот, который не первый или не последний символ набора) используется, чтобы определить диапазон символов.
  • Если, за символом '^' немедленно следует начальный '[', а затем набор символы, то позволяемые входные символы - все за исключением перечисленных символов.
'%[' преобразование не перескакивает начальные пробельные символы. Ниже приводятся некоторые примеры форматов '%[' преобразования и что они означают:
       `%25[1234567890]'
Соответствует строке до 25 цифр.
       `%25[][]'
Соответствует строке до 25 квадратных скобок.
       `%25[^ \f\n\r\t\v]'
Соответствует строке до 25 символов, которая не содержит любых из стандартных пробельных символов. Это немного отличается от '%s', т. к., если ввод начинается с символа пропуска, '%[' возвращает ошибку несоответствия, в то время как '%s' просто отбрасывает начальный пропуск.
       `%25[a-z]'
Соответствует до 25 символам нижнего регистра. Еще одно напоминание: форматы '%s' и '%[' опасны, если Вы не определяете максимальную ширину или используете флаг 'a', т. к. слишком длинный ввод переполнит любой буфер. Хорошо написанная программа сообщает о недопустимом вводе сообщением об ошибках, а не завершается аварийным отказом.

Динамическое Распределение Форматов Строки

Расширение GNU для форматируемого ввода позволяет Вам безопасно считывать строку безо всякого указания максимального размера. При использовании этой возможности, Вы не указываете буфер; а вместо этого, scanf распределяет буфер, достаточно большой, чтобы содержать данные и возвращает Вам его адрес. Чтобы использовать эту возможность, укажите 'а' в качестве флага, например '%as' или '%a[0-9a-z]'.

Аргумент указателя, который Вы обеспечиваете для размещения входной информации, должен иметь тип char **. Функция scanf резервирует буфер и сохраняет адрес там, куда указывает аргумент. Вы должны освободить буфер функцией free, когда Вы больше не нуждаетесь в нем.

Вот пример использования флага 'а' со спецификацией преобразования '%[...]' для чтения 'переменного назначения' в форме 'переменная = значение'.

      {
        char *variable, *value;

        if (2 > scanf ('%a[a-zA-Z0-9] = %a[^\n]\n',
                       &variable, &value))
          {
            invalid_input_error ();
            return 0;
          }

        ...
      }

Другие Входные Форматы

Этот раздел описывает разнообразные входные форматы.

Формат '%p' используется, для того чтобы считывать значение указателя. Оно распознает тот же самый синтаксис, как и формат вывода '%p' для printf (см. Раздел 7.9.6 [Другие Преобразования Вывода]). Соответствующий параметр должен иметь тип void **; то есть адрес места, где разместить указатель.

Формат '%n' возвращает число прочитанных символов. Соответствующий параметр должен иметь тип int *. Это преобразование работает таким же образом как и формат '%n' для printf; см. примеры в Разделе 7.9.6 [Другие Форматы Вывода].

Формат '%n' - единственный механизм для определения успеха буквального соответствия или преобразования с подавляемыми назначениями. Если '%n' следует за ошибкой несоответствия, scanf возвратится, не успев обработать '%n'. Если Вы поместите -1 в этот параметр перед вызовом scanf, присутствие -1 после вызова scanf указывает, что ошибка произошла перед обработкой '%n'.

В заключение, формат '%%' соответствует символу `%' во входном потоке, без использования параметра. Это преобразование не позволяет определение никаких флагов, ширины поля, или модификаторов типа.

Форматируемые Входные Функции

Ниже приводятся описания функций для выполнения форматируемого ввода. Прототипы для этих функций находятся в файле 'stdio.h'.

-Функция: int scanf (const char *template, ...)

Функция scanf читает форматируемый ввод из потока stdin под управлением строки шаблона. Необязательные параметры - указатели на места, которые получают возникающие в результате значения.

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

-Функция: int fscanf (FILE *stream, const char *template, ...)

Эта функция - аналог scanf, за исключением того, что ввод осуществляется из вместо stdin указанного потока.

-Функция: int sscanf (const char *s, const char *template, ...)

Подобна scanf, за исключением того, что символы берутся из строки s с нулевым символом в конце, а не из потока. Достижение конца строки обрабатывается как условие конца файла.

Поведение этой функции неопределено, если копирование происходит между объектами, которые пересекаются, например, если s задан еще и как аргумент для получения считанной строки под управлением формата '%s'.

Функции Ввода С Переменными Аргументами

Функции vscanf и ей подобные работают так, чтобы Вы могли определять ваши собственные scanf-подобные функции, которые используют ту же самую внутреннюю организацию как встроенные форматируемые функции вывода. См. Раздел 7.9.9 [Вывод Аргументов Переменной].

Примечание о Переносимости: функции, перечисленные в этом разделе являются расширением GNU.

-Функция: int vscanf (const char *template, va_list ap)

Эта функция похожа на scanf за исключением того, что вместо того, чтобы принимать переменное число аргументов непосредственно, она берет указатель на список параметров - ар типа va_list (см. Раздел A.2 Variadic Функции] ).

-Функция: int vfscanf (FILE *stream,const char *template,va_list ap)

Эта функция - эквивалент fscanf с переменным списком параметров, заданным непосредственно как для vscanf.

7.12 Блочный Ввод-Вывод

Этот раздел описывает операции ввода и вывода на блоках данных. Вы можете использовать эти функции для чтения и записи двоичных данных, также как читать и писать текст блоками устанавливаемого размера - а не символами или строками.

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

Эти функции объявлены в ' stdio.h '.

-Функция:size_t fread (void *data, size_t size, size_t count, FILE *stream)

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

Если fread достигает конца файла в середине объекта, она возвращает номер прочитанных полностью объектов, и отбрасывает несчитанные до конца.

-Функция: size_t fwrite (const void *data, size_t size, size_t count, FILE *stream)

Эта функция записывает до count объектов из массива данных в указанный поток. Возвращаемое значение - обычно count, если считывание успешно. Любой другое значение указывает какую-либо ошибку, например нехватку памяти.

КОНЕЦ ФАЙЛА и Ошибки

Многие из функций, описанных в этой главе возвращают значение макрокоманды EOF, указывающей неудачное завершение операции. Когда используется EOF, для сообщения о конце файла или о случайной ошибке, часто лучше использовать функцию feof, чтобы явно проверить конец файла и ferror, чтобы проверить наличие ошибки. Это контрольные индикаторные функции, которые являются частью внутреннего состояния объекта потока, индикаторы устанавливаются если соответствующее условие было обнаружено предыдущей операцией ввода - вывода на этом потоке.

Эти символы объявлены в заглавном файле ' stdio.h '.

- Макрос: int EOF

Этот макрос имеет целое значение, которое возвращается рядом функций, чтобы указать условие конца файла, или какую-нибудь другую ошибку. В библиотеке GNU, EOF имеет значение -1. В других библиотеках, значением может быть некоторое другое отрицательное число.

-Функция: void clearerr (FILE *stream)

Эта функция очищает индикаторы конца файла и ошибки для указанного потока. Позиционирующие файл функции (см. Раздел 7.15 [Позиционирование Файла]) также, очищают индикатор конца файла для потока.

-Функция: int feof (FILE *stream)

Функция feof возвращает отличное от нуля число, только если установлен индикатор конца файла для потока.

-Функция: int ferror (FILE *stream)

Функция ferror возвращает отличное от нуля число, только если индикатор ошибки для потока установлен, указывая что ошибка произошла на предыдущей операции на потоке.

В дополнение к установке индикатора ошибки, связанного с потоком, функции, которые работают с потоками, устанавливают errno таким же образом как соответствующие функции низкого уровня, которые работают с дескрипторами файла. Например, все функции, которые выполняют вывод в поток, такие как fputc, printf, и fflush, осуществлены в терминах записи, и все условия ошибки errno, определенные для записи, имеют значение и для этих функций. Для получения более подробной информации о функциях ввода - вывода на уровне дескрипторов, см. Главу 8 [ввод - вывод низкого уровня] 3.

7.13 Текстовые и Двоичные Потоки

Система GNU и другие posix-совместимые операционные системы организовывает все файлы как однородные последовательности символов. Однако, некоторые другие системы делают различие между файлами, содержащими текст и файлами, содержащими двоичные данные, и средства ввода и вывода ANSI C предусматривают это различие. Этот раздел сообщает Вам, как написать программы, переносимые на такие системы.

Когда Вы открываете поток, Вы можете определять или текстовый поток или двоичный поток. Вы указываете, что Вы хотите двоичный поток, определяя модификатор 'b' в параметре opentype для fopen; см. Раздел 7.3 [Открытие Потоков]. Без этой опции, fopen открывает файл как текстовый поток.

Текстовые и двоичные потоки имеют различия:

  • Данные из текстового потока разделены на строки, которые завершены символом перевода строки (' \n ') символы, в то время как двоичный поток - просто ряд символов. Текстовый поток может на некоторых системах быть не в состоянии обрабатывать строки больше чем 254 символа (включая символ перевода строки).
  • На некоторых системах, текстовые файлы могут содержать только символы печати, горизонтальные cимволы табуляции, и символы перевода строки, и так что текстовые потоки не могут поддерживать другие символы. Однако, двоичные потоки могут обрабатывать любое символьное значение.
  • Символы пробела, которые написаны перед символом перевода строки в текстовом потоке, могут исчезать.
  • В более общем смысле, не обязательно имеется взаимнооднозначное отображение между символами, которые считываются или записываются в текстовый поток, и символами в фактическом файле.
Так как двоичный поток более общий и более предсказуемый чем текстовый поток, Вы могли бы задаться вопросом, в чем цель текстовых потоков. Почему не всегда используют двоичные потоки? Ответ в том, что на разных операционных системах, текстовые и двоичные потоки используют различные форматы файла, и единственный способ читать или записать 'обычный текстовый файл' который может работать с другими ориентируемыми текстом программами - через текстовый поток.

В библиотеке GNU, и на всех POSIX системах, не имеется никакого различия между текстовыми потоками и двоичными потоками. Когда Вы открываете поток, Вы получаете тот же самый вид потока, даже если Вы заказывали двоичный. Этот поток может обрабатывать любое содержание файла, и не имеет ни каких ограничений, которые в отличие от текстовые потоков.

7.14 Позиционирование Файла

Позиция файла потока описывает, где в файле поток в настоящее время читает или производит запись. Ввод-вывод на потоке продвигает позицию файла через весь файл. В системе GNU, позиция файла представляется как целое число, которое содержит число байтов от начала файла. См. Раздел 6.1.2 [Позиция Файла].

В течение ввода-вывода в обычный дисковый файл, Вы можете менять позицию в файле всякий раз, когда Вы желаете читать или записывать в любую часть файла. Некоторые другие виды файлов также позволяют делать это. Файлы, которые поддерживают изменение позиции файла иногда упоминается как файлы прямого доступа.

Вы можете использовать функции в этом разделе, чтобы исследовать или изменить индикатор позиции файла, связанный с потоком. Символы, перечисленные ниже объявлены в заглавном файле 'stdio.h'.

-Функция: long int ftell (FILE *stream)

Эта функция возвращает текущую позицию файла указанного потока.

Эта функция может выдать ошибку, если поток не поддерживает позиционирование файла, или если позиция файла не может представляться как long int, или возможно по другим причинам.

Если происходит ошибка, возвращаемое значение -1.

-Функция: int fseek (FILE *stream, long int offset, int whence)

Функция fseek используется для изменения позиции файла указанного потока. Значение whence должно быть одной из констант SEEK_SET, SEEK_CUR, или SEEK_END, т. е. указывать является ли смещение относительно начала файла, текущей позиции файла, или конца файла, соответственно. Эта функция возвратит нуль, если операция была успешна, и значение, отличное от нуля чтобы указать отказ.

Успешное обращение также очищает индикатор конца файла потока и отбрасывает любые символы, которые были 'помещены обратно' использованием ungetc. Fseek дописывает любой буферизированный вывод перед позиционированием файла, или еще запоминает его, так что он будет записан позже в соответствующем месте файла.

Примечание о Переносимости: В не-posix системах, ftell и fseek могут работать надежно только на двоичных потоках. См. Раздел 7.14 [Двоичные Потоки].

Следующие символические константы определены для использования в качестве аргумента whenceа для fseek. Они также используются функцией lseek (см. Раздел 8.2 [Примитивы ввода - вывода] ) и для указания смещения для блокировок файла (см. Раздел 8.7 [Операции Управления] ).

-Макрос: int SEEK_SET

Это целая константа которая, когда используется как аргумент whence функции fseek и определяет, что смещение указывается относительно начала файла.

-Макрос: int SEEK_CUR

Это целая константа которая используется как аргумент whence функции fseek и определяет, что смещение указывается относительно текущей позиции файла.

-Макрос: int SEEK_END

Это целая константа которая используется как аргумент whence функции fseek и определяет, что смещение указывается относительно конца файла.

-Функция: void rewind (FILE *stream)

Функция rewind позиционирует указанный поток в начало файла. Это эквивалентно вызову fseek на потоке с аргументом смещения 0L и аргументом whence SEEK_SET, за исключением того, что возвращаемое значение отбрасывается, и индикатор ошибки для потока сброшен.

Эти три побочных результата исследования для констант 'SEEK_...' существуют ради совместимости с более старыми BSD системами. Они определены в двух различных файлах: 'fcntl.h' и 'sys/file.h'.

L_SET синоним SEEK_SET.

L_INCR синоним SEEK_CUR.

L_XTND синоним SEEK_END.

7.15 Переносимые Функции позиционирования файла

В системе GNU, позиция файла - просто символьный счетчик. Вы можете задавать любое значение count как аргумента в fseek и получать надежные результаты для любого файла произвольного доступа. Однако, некоторые ANSI C системы не представляет позиции файла таким образом,.

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

Как следствие, если Вы хотите чтобы ваши программы были переносимы на эти системы, Вы должны соблюдать некоторые правила:

  • Значение, возвращенное ftell на текстовом потоке не имеет никакой предсказуемостной связи с числом символов, которое Вы читали из пока. Единственная вещь, на которую Вы можете полагаться - то, что Вы сможете использовать его впоследствии, поскольку аргумент смещения в fseek двигается обратно в ту же самую позицию файла.
  • При обращении к fseek на текстовом потоке, смещение должно быть либо нуль; либо SEEK_SET, и смещение должно быть результатом более раннего обращения к ftell на том же самом потоке.
  • Значение индикатора позиции файла текстового потока неопределено, если имеются символы, которые были помещены обратно функцией ungetc, которые не читались или не отбрасывались. См. Раздел 7.8 [Обратное чтение].

Но даже если Вы соблюдаете эти правила, Вы можете все еще иметь проблемы длинными файлами, т. к. ftell и fseek используют значение int long, для представления позицию файла. Этот тип может не иметь участка памяти, для кодирования всех позиций файла в большом файле.

Так, если Вы хотите поддерживать системы со специфическими кодированием для позиций файла, то лучше использовать функции fgetpos и fsetpos. Эти функции представляют позицию файла, используя тип данных fpos_t, чье внутреннее представление меняется от системы к системе.

Эти символы объявлены в заглавном файле ' stdio.h '.

-Тип данных: fpos_t

Это - тип объекта, который может кодировать информацию относительно файловой позиции потока, для использования функциями fgetpos и fsetpos.

В системе GNU, fpos_t эквивалентен off_t или long int. В других системах, он может иметь различное внутреннее представление.

-Функция: int fgetpos (FILE *stream, fpos_t *position)

Эта функция сохраняет значение индикатора файловой позиции для указанного потока в указанном объекте fpos_t. Обращение успешно если fgetpos возвращает нуль; иначе она возвращает значение отличное от нуля и сохраняет определенное реализацией положительное значение в errno.

-Функция: int fsetpos (FILE *stream, const fpos_t position)

Эта функция устанавливает индикатор файловой позиции для указанного потока в позицию position, которая должна определяться предыдущим обращением к fgetpos на том же самом потоке. Если обращение успешно, fsetpos очищает индикатор конца файла на потоке, отбрасывает любые символы, которые были 'помещены обратно' использованием ungetc, и возвращает значение нуля. Иначе, fsetpos возвращает значение отличное от нуля и сохраняет определенное реализацией положительное значение в errno.

7.16 Буферизация Потока

Символы, которые записаны в поток, обычно накапливаются и передаются в файл блоками асинхронно, вместо того, чтобы появляться, как только они выводятся прикладной программой. Аналогично, потоки часто восстанавливают ввод из главной среды в блоках а не по принципу символ-за-символ. Это называется буферизацией.

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

Этот раздел имеет дело только с управлением передачей символов между потоком и файлом или устройством.

Вы можете обходить средства буферизации потока в целом(вполне), используя ввод и вывод низкого уровня, которые функционируют на описателях файла. См. Главу 8 [ввод - вывод низкого уровня].

Понятие Буферизации

Имеются три различных вида cтратегий буферизации:

  • Символы, записываемые или читаемые из небуферизованного потока, передаются индивидуально в файл или из файла как можно скорее.
  • Символы, записываемые или читаемые из строчно буферизированного потока передаются в или из файла в блоках, когда прочитан с символ перевода строки.
  • Символы, записываемые или читаемые из полностью буферизированного потока, передаются в или из файла в блоках произвольного размера.
Вновь открываемые потоки обычно полностью буферизируются, с одним исключением: поток, соединенный с интерактивным устройством типа терминала - изначально буферизирован строчно. См. Раздел 7.17.3 [Управление Буферизацией], для уточнения информации о том, как в выбирают различные виды буферизации.

Использование строчной буферизации для интерактивных устройств подразумевает окончание вывода сообщения с символом перевода строки. Вывод, который не заканчивается на символе перевода строки, может и не быть обнаружен немедленно, так если Вы хотите вывести его немедленно, Вы должны очистить буферизированный вывод функцией fflush, как описано в Разделе 7.17.2 [Очистка буфера].

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

Они включают программы, которые читают одиночно - символьные команды (подобно Emacs) и программам, которые делают их собственное редактирование ввода (типа тех что используют readline). Чтобы просто читать символ, не достаточно выключить буферизацию во входном потоке; Вы должны также выключить редактирование ввода, в операционной системе. Это требует изменения режима терминала (см. Раздел 12.4 [Режимы Терминала] ).

Промывание Буфера

Сброс вывода на буферизированном потоке, передает все накопленные символы в файл. Имеются много обстоятельств когда буферизированный вывод на потоке, сбрасывается автоматически:

  • Когда Вы пробуете выводить, и буфер вывода полон.
  • Когда поток закрыт. См. Раздел 7.4 [Закрывание Потоков].
  • Когда программа завершается, вызывая exit. См. Раздел 22.3.1 [Нормальное Окончание].
  • Когда введен символ перевода строки, если поток буферизирован строчно.
  • Всякий раз, когда операция ввода на любом потоке фактически читает данные из файла.

Если Вы хотите сбросить буферизированный вывод в другой момент, вызывайте fflush, которая объявлен в заглавном файле ' stdio.h '.

-Функция: int fflush (FILE *stream)

Эта функция заставляет любой буферизированный вывод на потоке дописываться в файл. Если поток - нулевой указатель, то буферизированный вывод на всех открытых выходных потоках будет сброшен.

Эти функции возвращают EOF, если происходит ошибка записи, и нуль в другом случае.

Примечание о Совместимости: Некоторые поврежденные в уме операционные системы, как известно, были настолько помешаны на строчно ориентированном вводе, что для сброса строчно буферизированного потока должен быть введен символ перевода строки! К счастью, эта 'удобство', кажется, становится менее распространенным. В системе GNU беспокоиться об этом Вам нет нужды.

Управление Видом Буферизации

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

Средства, перечисленные в этом разделе объявлены в файле 'stdio.h'.

-Функция: int setvbuf (FILE *stream, char *buf, int mode, size_t size)

Эта функция используется, чтобы определить, что указанный поток должен иметь заданный режим буферизации, который может быть: _IOFBF (для полной буферизации), _IOLBF (для буферизации строки), или _IONBF (для небуферизованного ввода -вывода).

Если Вы определяете нулевой указатель как параметр buf, то setvbuf, распределяет буфер, непосредственно используя malloc. Этот буфер будет освобожден, когда Вы закроете поток.

Иначе, buf должен быть символьным массивом, который может содержать по крайней мере size символов. Вы не должны трогать пространство для этого массива, пока поток остается открытым и этот массив остается буфером. Использование автоматического массива - не очень хорошая идея, если Вы не закрываете файл перед выходом из блока, который объявляет массив.

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

Функция setvbuf возвращает ноль в случае успеха, или значение отличное от нуля, если значение режима не допустимо или если запрос не мог быть удовлетворен.

-Макрос: int _IOFBF

Значение этой макрокоманды - константа integer, которая может использоваться как параметр режима для функции setvbuf, чтобы определить, что поток должен быть полностью буферизирован.

-Макрос: int _IOLBF

Значение этой макрокоманды - константа integer, которая может использоваться как параметр режима для функции setvbuf, чтобы определить, что поток должен быть буферизирован строчно.

-Макрос: int _IONBF

Значение этой макрокоманды - константа integer, которая может использоваться как параметр режима для функции setvbuf, чтобы определить, что поток должен быть небуферизован.

-Макрос: int BUFSIZ

Значение этой макрокоманды - константа integer, которую удобно использовать как параметр size для setvbuf. Это значение, как гарантируют, будет по крайней мере 256.

Значение BUFSIZ выбрано в каждой системе таким, чтобы делать ввод - вывод потока наиболее эффективным.

Фактически, Вы можете лучшее значение, чтобы использовать для размера буфера посредством fstat системного вызова: его можно найти в st_blksize поле атрибутов файла. См. Раздел 9.8.1 [Значения Атрибута].

Иногда также используют BUFSIZ как размер распределения буферов, используемых для соответствующих целей, типа строк, используемых, чтобы получить строку ввода через fgets (см. Раздел 7.6 [Символьный Ввод]). Не имеется никакой специфической причины использовать BUFSIZ для этого вместо любого другого integer, за исключением того, что это могло бы привести к выполнению ввода - вывода в кусках эффективного размера.

-Функция: void setbuf (FILE *stream, char *buf )

Если buf - нулевой указатель, эффект этой функции эквивалентен вызову setvbuf с параметром режима _IONBF. Иначе, это эквивалентно вызову setvbuf с buf, и режимом _IOFBF и параметром size - BUFSIZ. Функция setbuf предусмотрена для совместимости со старым кодом; используйте setvbuf во всех новых программах.

-Функция: void setbuffer (FILE *stream, char *buf, size_t size)

Если buf - нулевой указатель, эта функция делает поток небуферизованным. Иначе, это делает поток полностью буферизированным с использованием buf в качестве буфера. Параметр size определяет длину buf.

Эта функция предусмотрена для совместимости со старым BSD кодом. Используйте вместо нее setvbuf.

-Функция: void setlinebuf (FILE *stream)

Эта функция делает поток буферизированным строчно, и распределяет буфер для Вас. Эта функция предусмотрена совместимость со старым BSD кодом. Используйте setvbuf вместо нее.

7.17 Другие Виды Потоков

Библиотека GNU обеспечивает способы определить дополнительные виды потоков, которые не обязательно соответствуют открытому файлу.

Один такой тип потока берет ввод из или пишет вывод в строку. Эти виды потоков используются внутренне, чтобы выполнить функции sprintf и sscanf. Вы можете также создавать такой поток явно, при использовании функций, описанных в Разделе 7.18.1 [Строковые Потоки].

В более общем смысле, Вы можете определять потоки, которые делают ввод -вывод для произвольных объектов, используя функции, обеспеченные вашей программой. Этот протокол обсужден в Разделе 7.18.3 [Заказные Потоки] .

Примечание о Переносимости: средства, описанные в этом разделе специфические для GNU. Другие системы или реализации C могли и не обеспечивать эквивалентные функциональные возможности.

Строковые Потоки

Функции fmemopen и open_memstream делали ввод - вывод в буфер памяти или строку. Эти средства объявлены в 'stdio.h '.

-Функция: FILE * fmemopen (void *buf, size_t size, const char *opentype)

Эта функция открывает поток, который допускает доступ, определенный параметром opentype, и который считывается или записывается в буфер, определенный параметром buf. Этот массив должен быть по крайней мере size байтов длиной.

Если Вы определяете нулевой указатель как параметр buf, fmemopen динамически распределяет (как с malloc; см. Раздел 3.3 [Беспрепятственное Распределение]) size байтовый массив. Это действительно полезно только, если Вы собираетесь записывать что-то в буфер и затем читать это обратно, т. к. Вы не имеете никакого способа фактически получить указатель на буфер (для этого, попробуете open_memstream, ниже). Буфер освобождается, когда открывается поток. Параметр opentype - такой же как в fopen (См. Раздел 7.3 [Открытие Потоков]). Если opentype определяет режим конкатенирования, то начальная файловая позиция устанавливается на первый символ пробела в буфере. Иначе начальная файловая позиция - в начале буфера.

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

Вот пример использования fmemopen для создания потока для чтения из строки:

 #include 

      static char buffer[] = 'foobar';

      int
      main (void)
      {
        int ch;
        FILE *stream;

        stream = fmemopen (buffer, strlen (buffer), 'r');
        while ((ch = fgetc (stream)) != EOF)
          printf ('Got %c\n', ch);
        fclose (stream);

        return 0;
      }

Эта программа производит следующий вывод:

      Got f
      Got o
      Got o
      Got b
      Got a
      Got r
-Функция: FILE * open_memstream (char **ptr, size_t *sizeloc)

Эта функция открывает поток для записи в буфер. Буфер размещен динамически (как с malloc; см. Раздел 3.3 [Беспрепятственное Резервирование]) и растет по мере необходимости.

Когда поток закрывается с помощью fclose или сбрасывается с помощью fflush, указатели ptr и sizeloc модифицируются, и содержат указатель на буфер и size. Значения, таким образом сохраненные остаются допустимыми только, пока не происходит никакой дальнейший вывод на потоке. Если, Вы выводите еще, Вы должны промыть поток, чтобы сохранить новые значения прежде, чем Вы используете его снова.

Пустой символ записывается в конце буфера. Этот пустой символ не включен в значение size, сохраненное в sizeloc.

Вы можете перемещать файловую позицию потока функцией fseek (см. Раздел 7.15 [Позиционирование Файла]). Перемещение файловой позиции после конца уже записанных данных, заполняет захваченное пространство нулями.

Вот пример использования open_memstream:

      #include 

      int
      main (void)
      {
        char *bp;
        size_t size;
        FILE *stream;

        stream = open_memstream (&bp, &size);
        fprintf (stream, 'hello');
        fflush (stream);
        printf ('buf = `%s', size = %d\n', bp, size);
        fprintf (stream, ', world');
        fclose (stream);
        printf ('buf = `%s', size = %d\n', bp, size);

        return 0;
      }
     Эта программа производит следующий вывод:
      buf = `hello', size = 5
      buf = `hello, world', size = 12

Obstack Потоки

Вы можете открывать выходной поток, который помещает данные в obstack. См. Раздел 3.4 [Obstack].

-Функция: FILE * open_obstack_stream (struct obstack *obstack)

Эта функция открывает поток для записи данных в obstack. Она начинает объект в obstack и увеличивает его, при записывании данных (см. Раздел 3.4.6 [Возрастастающие Объекты]).

Вызов fflush на этом потоке модифицирует текущий размер объекта, в соответствии с количеством данных, которое было записано. После обращения к fflush, Вы можете исследовать объект.

Вы можете перемещать файловую позицию obstack потока функцией fseek (см. Раздел 7.15 [Позиционирование Файла]).

Чтобы сделать объект постоянным, модифицируйте obstack с fflush, и тогда используйте obstack_finish для завершения объекта и получения адреса. Следующая запись в поток, начинает новый объект в obstack.

Но как Вы узнаете, какой длины объект? Вы можете получать длину в байтах, вызывая obstack_object_size (см. Раздел 3.4.8 [Состояние Obstack]), или Вы можете пустым символом завершить объект, примерно так:

   obstack_1grow (obstack, 0);

Вот типичная функция, которая использует open_obstack_stream:

 char *     make_message_string (const char *a, int b)
      {
        FILE *stream = open_obstack_stream (&message_obstack);
        output_task (stream);
        fprintf (stream, ': ');
        fprintf (stream, a, b);
        fprintf (stream, '\n');
        fclose (stream);
        obstack_1grow (&message_obstack, 0);
        return obstack_finish (&message_obstack);
      }

Программирование Ваших Собственных Потоков

Этот раздел описывает, как Вы можете создавать потоки, которые берут ввод из произвольного источника данных или пишут вывод в произвольный сток данных, программируемый Вами. Назовем эти потоки пользовательскими.

Пользовательские Потоки и Cookies

Внутри каждого пользовательского потока есть специальный объект называемый cookie. Это - объект, определяемый Вами, который указывает, где берутся или сохраняются данные.

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

Когда Вы создаете пользовательский поток, Вы должны задать указатель на cookie, а также четыре функции ловушки, содержащиеся в структуре типа struct cookie_io_functions.

Эти средства объявлены в ' stdio.h '.

-Тип даннаых: struct cookie_io_functions

Это - тип структуры, который содержит функции, которые определяют протокол связи между потоком и cookie. Он имеет следующие элементы:

cookie_read_function *read

Это функция, которая читает данные из cookie. Если значение является пустым указателем вместо функции, то операции чтения на потоке всегда возвращает EOF.

cookie_write_function *write

Это - функция, которая пишет данные в cookie. Если значение является пустым указателем вместо функции, то данные, записанные в поток отбрасываются.

cookie_seek_function *seek

Это функция, которая выполняет эквивалент позиционирования файла на cookie. Если значение является пустым указателем вместо функции, обращения к fseek на этом потоке, может искать расположения только внутри буфера; любая попытка искать снаружи буфера, возвратит ESPIPE ошибку.

cookie_close_function *close

Эта функция выполняет любую соответствующую сброс cookie при закрытии потока. Если значение является пустым указателем вместо функции, то при закрытии потока ничего не делается для закрытия cookie.

-Функция: FILE * fopencookie (void *cookie, const char *opentype, struct

Эта функция фактически создает поток для сообщения с cookie используя функции в io_functions аргументе. Opentype аргумент интерпретируется как для fopen; см. Раздел 7.3 [Открытие Потоков]. (Но отметьте что опция 'усечения при открытии' игнорируется.) Новый поток полностью буферизирован.

Функция fopencookie возвращает недавно созданный поток, или пустой указатель в случае ошибки.

Пользовательские Функции-Ловушки Потока

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

Вы должны определить функцию для чтения данных из cookie как:

       ssize_t reader (void *cookie, void *buffer, size_t size)
Это очень похоже на функцию read; см. Раздел 8.2 [Примитивы ввода - вывода]. Ваша функция должна передать до size байтов в буфер, и возвращать число прочитанных байтов, или нуль указывая конец файла. Вы можете возвращать значение -1 для указания ошибки.

Вы должны определить функцию, для записи данных в cookie как:

       ssize_t writer (void *cookie, const void *buffer, size_t size)
Это очень похоже на функцию write; см. Раздел 8.2 [Примитивы ввода - вывода]. Ваша функция должна передать до size байтов из буфера, и возвращать число записанных байтов.

Вы должны определить функцию, для выполнения операции позиционирования на cookie как:

       int seeker (void *cookie,  fpos_t *position,  int whence)

Для этой функции, position и whence аргументы интерпретируются как для fgetpos; см. Раздел 7.16 [Переносимое Позиционирование]. В библиотеке GNU, fpos_t эквивалентен off_t или long int, и просто представляет число байтов от начала файла.

После выполнения операции установки, ваша функция должна сохранить возникающую в результате файловую позицию относительно начала файла в position. Ваша функция должна возвратить значение 0 в случае успеха, и -1 в случае ошибки.

Вы должны определить функцию, для операции очистки на cookie, при закрытии потока как:

       int cleaner  (void  *cookie)
Ваша функция должна возвратить -1, указывая ошибку, или 0 иначе.
-Тип данных: cookie_read_function

Это - тип данных, который должна иметь функция чтения для пользовательского потока. Если Вы объявляете функцию как показано выше, это тип, который она будет иметь.

-Тип данных: cookie_write_function

Тип данных пишущей функции для пользовательского потока.

-Тип данных: cookie_seek_function

Тип данных функции инициализации для пользовательского потока.

-Тип данных: cookie_close_function

Тип данных функции close для заказного потока.

8. Ввод-Вывод низкого уровня

Эта глава описывает функции для выполнения операций ввод-вывода низкого уровня с помощью дескрипторов. Эти функции включают примитивы для функций ввода - вывода с более высоким уровнем, описанные в Главе 7 [Ввод - вывод на Потоках], также как функции для выполнения операций управления низкого уровня, для которых не имеется никаких эквивалентов на потоках. Ввод-вывод на уровне потоков более гибок и обычно более удобен; поэтому, программисты используют функции дескрипторного уровня только при необходимости. Вот несколько ситуаций, в которых могут понабиться дескрипторы:

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

8.1 Открытие и Закрытие Файлов

Этот раздел описывает примитивы для открытия и закрытия файлов, используя дескрипторы файла. Функции creat и open объявлены в файле 'fcntl.h', в то время как close объявлена в 'unistd.h'.

-Функция: int open (const char *filename, int flags[, mode_t mode])

Функция open создает и возвращает новый описатель файла для указанного файла. Первоначально, индикатор файловой позиции для файла находится в начале файла. Аргумент mode используется только, когда файл создан.

Аргумент flags управляет тем, как файл будет открыт. Это ­ битовая маска; Вы создаете значение поразрядным ИЛИ соответствующих параметров (используя оператор `|' в C).

Аргумент flags должен включять точно одно из этих значений, для задания режима доступа к файлу:

       O_RDONLY  Открывает  файл  для  чтения.
       O_WRONLY  Открывает  файл  для  записи.
       O_RDWR    Открывает файл,  и для чтения и для записи.
Аргумент flags может также включать любую комбинацию этих флагов:
       O_APPEND Если установлен,  то все операции записи запишут данные в
                 конец файла, расширяя его, независимо от текущей файловой 
                 позиции.
       O_CREAT  Если установлен,  будет создан файл,  если он еще  не
                существует.
       O_EXCL   Если  и  O_CREAT  и  O_EXCL - установлены,  то open выдает
                ошибку, если заданный файл уже существует.
       O_NOCTTY Если filename имя терминала,  не делайте его терминалом
                управления для  процесса.  См.  Главу  24  [Управление  
                заданиями],  для уточнения информации относительно того, 
                что означает терминал управления.
       O_NONBLOCK Устанавливает  режим  неблокирования.  Эта опция обычно
                полезна для специальных файлов  типа  FIFO  (см.  Главу  10  
                [Каналы и FIFO]) и устройств типа терминалов.  
                Обычно,  для этих файлов  open блокируется, пока файл не 'готов'.
                Если O_NONBLOCK установлен,  open возвращается немедленно.
                O_NONBLOCK бит  также  воздействует  на  чтение  и  на запись:  он
                разрешает им  возвращаться  немедленно  с  состоянием  ошибки,
                если  не имеется никакого доступного ввода, или если вывод не 
                может быть записан.
       O_TRUNC Если файл существует и открыт для записи, усекает это, до
               нулевой длины.  Эта  опция  полезна  только для регулярных 
               файлов,  а не специальных файлов типа каталогов или FIFO. Для 
               получения более подробной информации об этих символических 
               константах см. Раздел 8.10 [Флаги Состояния  Файла].
Нормальное возвращаемое значение open - неотрицательный описатель файла типа integer. В случае ошибки возвращается значение -1. В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Ошибки Имени файла]), следующие errno условия ошибки определены для этой функции:
EACCES

Файл существует, но не читаем/перезаписываем как запрошено аргументом flags.

EEXIST

И O_CREAT и O_EXCL - установлены, а именованный файл уже существует.

EINTR

Операция open была прервана сигналом. См. Раздел 21.5 [Прерванные Примитивы] 3.

EISDIR

Аргумент flags задает доступ для записи, а файл - каталог.

EMFILE

Процесс имеет слишком много открытых файлов.

ENFILE

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

ENOENT

Именованный файл не существует, но O_CREAT не определен.

ENOSPC

Каталог или файловая система, которая содержала бы новый файл,не может быть расширена, т. к. там не осталось дискового пространства.

ENXIO

O_NONBLOCK и O_WRONLY установлены в аргументе flags, файл, именованный filename - FIFO (см. Главу 10 [Каналы и FIFO]), и никакой процесс имеет файл открытым для чтения.

EROFS

Файл постоянно находится в файловой системе только для чтения и любой из O_WRONLY, O_RDWR, O_CREAT, и O_TRUNC ­ установлены в аргументе flags.

Функция open - основной примитив для fopen и freopen функций, которые создают потоки.

-Функция: int creat (const char *filename, mode_t mode)

Эта функция устаревает. Обращение: creat (filename, mode) эквивалентно: open (filename, O_WRONLY | O_CREAT | O_TRUNC, mode)

-Функция: int close (int filedes)

Функция закрывает дескриптор файла filedes. Закрытие файла имеет следующие последствия:

  • Описатель файла освобожден.
  • Любые блокировки записи, принадлежащие процессу на файле разблокирутся.
  • Когда все описатели файла, связанные с каналом или в порядке поступления были закрыты, любые читаемые обратно данные отброшены.
Нормальное возвращаемое значение - 0; значение -1 возвращается в случае ошибки.

Следующие errno условия ошибки определены для этой функции:

  • EBADF Filedes аргумент - не допустимый дескриптор файла.
  • EINTR Обращение было прервано сигналом. См. Раздел 21.5 [Прерванные Примитивы]. Вот пример правильной обработки EINTR:
           TEMP_FAILURE_RETRY (close (desc));
Для закрытия потока, вызовите fclose (см. Раздел 7.4 [Закрытие Потоков]) вместо того, чтобы пробовать закрыть основной описатель файла. Она дописывает любой буферизированный вывод и модифицирует объект потока указывая, что он закрыт.

8.2 Примитивы Ввода и Вывода

Этот раздел описывает функции для выполнения ввода и вывода с помощьюю дескрипторов файла: read, write, и lseek. Эти функции объявлены в файле ' unistd.h '.

-Тип данных: ssize_t

Этот тип данных используется для представления размеров блоков, которые могут быть прочитаны или записаны одиночной операцией. Он подобен size _t, но должен быть знаковым типом.

-Функция: ssize_t read (int filedes, void *buffer, size_t size)

Функция read читает до size байтов из файла с описателем filedes, сохраняя результаты в буфере. (Это - не обязательно символьная строка и не имеется никакого добавленного пустого символа завершения.)

Возвращаемое значение - число байтов фактически прочитанных. Оно может быть меньше чем size.

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

Если read возвращает по крайней мере один символ, не имеется никакого способа, которым Вы можете узнать, был ли конец файла достигнут. Но если Вы достигали конца, следующий read возвратит нуль.

В случае ошибки read возвращает -1. Следующие errno условия ошибки определены для этой функции:

EAGAIN

Обычно, когда никакой ввод недоступен, read ждет какого-нибудь ввода. Но если установлен флаг O_NONBLOCK (см. Раздел 8.10 [Флаги Состояния Файла]), read возвращается немедленно не читая никаких данных, и сообщает эту ошибку.

Примечание о Совместимости: Большинство версий BSD UNIX использует различный код ошибки для EWOULDBLOCK. В библиотеке GNU, EWOULDBLOCK - синоним EAGAIN, так что не имеет значения, акое название Вы используете.

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

EBADF

Filedes аргумент - не допустимый описатель файла.

EINTR

Чтение было прервано сигналом, в то время как он ждал ввода.

См. Раздел 21.5 [Прерванные Примитивы]. EIO Для многих устройств, и для файлов на диске, этот код ошибки указывает аппаратную ошибку. EIO также происходит, когда фоновый процесс пробует читать с терминала управления, и нормальное действие остановки процесса, т. е. Посылка сигнала SIGTTIN не работает. Это может случиться, если сигнал блокируется или игнорируется, или т. к. группа процесса - orphaned. См. Главу 24 [Управление заданиями].

Функция read - основной примитив для всех функций, которые читают из потоков, типа fgetc.

-Функция: ssize_t write (int filedes, const void *buffer, size_t size)

Функция write пишет до size байтов из буфера в файл с описателем filedes. Данные в буфере - не обязательно символьная строка и вывод пустого символа подобен любому другому.

Возвращаемое значение - число байтов, фактически записанных. Это - обычно size, но могло бы быть и меньшее количество. В случае ошибки, write возвращает -1. Следующие errno условия ошибки определены для этой функции:

EAGAIN

Обычно, write блокируется до завершения операции записи. Но если для файла устанавлен O_NONBLOCK флаг (см. Раздел 8.7 [Операции Управления]), она возвращается немедленно не позводя записи любых данных, и сообщает эту ошибку. Пример ситуации, котаря могла бы вызывать блокировани процесса на выводе, запись в терминальное устройство, которое поддерживает управление потоком данных, где вывод был приостановлен получением символа STOP.

Примечание Совместимости: Большинство версий BSD UNIX использует различный код ошибки для EWOULDBLOCK. В библиотеке GNU, EWOULDBLOCK ­ синоним для EAGAIN, так что не имеет значения, какое название Вы используете.

EBADF

Аргумент Filedes - не допустимый дескриптор файла.

EFBIG

Размер файла больший чем реализация, может поддерживать.

EINTR

Операция write была прервана сигналом, в то время как она была блокирована, и ждала завершения. См. Раздел 21.5 [Прерванные Примитивы].

EIO

Для многих устройств, и для файлов на диске, этот код ошибки указывает аппаратную ошибку. EIO также происходит, когда фоновый процесс пробует читать с терминала управления, и нормальное действие остановки процесса, посылая сигнал SIGTTIN не работает. Это может случиться, если сигнал блокируется или игнорируется, или т. к. группа процесса - orphaned. См. Главу 24 [Управление заданиями].

ENOSPC

переполнение устройства.

EPIPE

Эта ошибка возвращается, когда Вы пробуете писать в канал или в FIFO, который не открыт для чтения процессом. Когда это случается, сигнал SIGPIPE также посылается в процесс; см. Главу 21 [Обработка Сигнала].

Если Вы не предотвращаете EINTR ошибки, Вам следует проверять errno после каждого выдающего ошибку обращения к write, и если ошибка была EINTR, Вы должны просто повторить обращение. См. Раздел 21.5 [Прерванные Примитивы]. Простой способ реализовать это - макрокомандой TEMP_FAILURE_RETRY, следующим образом:
       nbytes = TEMP_FAILURE_RETRY (write (desc, buffer, count));
Функция write - основной примитив для всех функций записи в поток, типа fputc.

8.3 Установка Файловой позиции Дескриптора

Точно как Вы можете устанавливать файловую позицию потока функцией fseek, Вы можете устанавливать файловую позицию дескриптора функцией lseek. Она определяет позицию в файле для следующей операции read или write. См. Раздел 7.15 [Позиционирование Файла], для подробной информации относительно файловой позиции и что это означает.

Для получения текущего значения файловой позиции из описателя, используйте lseek (desc, 0, SEEK_CUR).

-Функция: off_t lseek (int filedes, off_t offset, int whence)

Функция lseek используется, чтобы изменить файловую позицию файла с описателем filedes. Аргумент whence определяет, как смещение должно интерпретироваться, таким же образом как в функции fseek, и может быть одной из символических констант SEEK_SET, SEEK_CUR, или SEEK_END.

SEEK_SET

Определяет, что whence - яисло символов от начала файла.

SEEK_CUR

Определяет, что whence - число символов от текущей файловой позиции. Этот число может быть положительно или отрицательно.

SEEK_END

Определяет, что whence - число символов с конца файла. Отрицательное число определяет позицию внутри текущего тела файла; положительное число определяет позицию после текущего конца. Если Вы устанавливаете позицию после текущего конца, и фактически записываете данные, Вы расширяете файл нулями до этой позиции.

Возвращаемое значение lseek - обычно возникающая в результате файловая позиция, измеряемая в байтах от начала файла. Вы можете использовать это средство вместе с SEEK_CUR для чтения текущей файловой позиции.

Вы можете устанавливать файловую позицию после текущего конца файла. Это делает файл больше; lseek никогда не изменяет файл. Но последующий вывод в ту позицию расширит файла.

Если файловая позиция не может быть изменена, или операция выполняется некоторым недопустимым способом, lseek возвращает значение -1. Следующие errno условия ошибки определены для этой функции:

EBADF

Filedes - не допустимый описатель файла.

EINVAL

Значение аргумента whence не допустимо, или возникающее в результате смещение файла не допустимо.

ESPIPE

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

Функция lseek - основной примитив для fseek, ftell и rewind функций, которые функционируют на потоках вместо описателей файла.

Вы можете иметь многократные описатели для того же самого файла, если Вы открываете файл больше чем один раз, или если Вы дублируете описатель с dup. Дескрипторы, которые исходят из отдельных обращений open, имеют независимые файловые позиции; использование lseek на одном дескрипторе не произодит никакого эффекта на другой. Например,

   {
        int d1, d2;
        char buf[4];
        d1 = open ('foo', O_RDONLY);
        d2 = open ('foo', O_RDONLY);
        lseek (d1, 1024, SEEK_SET);
        read (d2, buf, 4);
      }
будет читать первые четыре символа файла 'foo'. (Код с обнаружением ошибок, необходимый для реальной программы был опущен здесь для краткости.)

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

 {
        int d1, d2, d3;
        char buf1[4], buf2[4];
        d1 = open ('foo', O_RDONLY);
        d2 = dup (d1);
        d3 = dup (d2);
        lseek (d3, 1024, SEEK_SET);
        read (d1, buf1, 4);
        read (d2, buf2, 4);
      }

Будет читать четыре символа, начиная с 1024-го символа 'foo', и еще четыре символа, начиная с 1028-го символа.

-Тип данных: оff_t

Это - арифметический тип данных, используемый, чтобы представить размеры файла. В системе GNU, он эквивалентен fpos_t или long int. Эти три синонима константы 'SEEK_...' существуют ради совместимости с более старыми BSD системами. Они определены в двух различных файлах: 'fcntl.h' и 'sys/file.h'.

L_SET синоним для SEEK_SET.

L_INCR синоним SEEK_CUR.

L_XTND синоним SEEK_END.

8.4 Дескрипторы и Потоки

Определяя дескриптор файла с помощью open, Вы можете создавать поток для него функцией fdopen. Вы можете получить основной описатель файла для существующего потока функцией fileno. Эти функции объявлены в заглавном файле 'stdio.h'.

-Функция: FILE * fdopen (int filedes, const char *opentype)

Функция fdopen возвращает новый поток для описателя файла filedes. Opentype аргумент интерпретируется таким же образом как в функции fopen (см. Раздел 7.3 [Открытие Потоков]), за исключением того, что опция 'b' не разрешается; это оттого, что GNU не делает никакого различия между текстом и двоичными файлами. Также, 'w' и 'w+' не вызывают усечение файла; они воздействуют только при открытии файла, а в этом случае, файл уже открыт. Вы должны удостовериться, что opentype аргумент соответствует фактическому режиму дескриптора файла.

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

Для примера, показывающего использование функции fdopen, см. Раздел 10.1 [Создание Канала].

-Функция: int fileno (FILE *stream)

Эта функция возвращает описатель файла, связанный с указанным потоком. Если обнаружена ошибка (например, если поток не допустим) или если поток, не делает ввод - вывод в файл, fileno возвращает - 1.

Имеются также символические константы, определенные в 'unistd.h' для описателей файла, принадлежащих к стандартным потокам stdin, stdout, и stderr; см. Раздел 7.2 [Стандартные Потоки].

STDIN_FILENO

Эта макрокоманда имеет значение 0, которое является дескриптором файла для стандартного ввода.

STDOUT_FILENO

Эта макрокоманда имеет значение 1, которое является дескриптором файла для стандартного вывода.

STDERR_FILENO

Эта макрокоманда имеет значение 2, которое является дескриптором файла для стандартного вывода ошибки.

8.5 Опасности Смешивания Потоков и Дескрипторов

Вы можете иметь многочисленные дескрипторы файла и потоки (назывем и потоки и дескрипторы 'каналами', для краткости), связанными с одним и тем же файлом, но Вы должны соблюдать осторожность, чтобы избежать путаницы между каналами. Имеются два случая, для рассмотрения: связанные каналы, которые совместно используют одино значение файловой позиции, и независимые каналы которые имеют свои собственные файловые позиции.

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

Связанные Каналы

Каналы, которые исходят из одного открытия, совместно используют ту же самую файловую позицию; мы называем их связанными каналами. Связанные каналы кончаются, когда Вы делаете поток из дескриптора, используя fdopen, и когда Вы получаете описатель из потока с fileno, и когда Вы копируете описатель с dup или dup2. Для файлов, которые не поддерживают, произвольный доступ, типа терминалов и канадов, все каналы действительно связаны. На файлах прямого доступа, все выходные потоки конкатенирующего типа действительно связаны друг с другом.

Если Вы использовали поток для ввода - вывода, и Вы хотите делать ввод - вывод, используя другой канал (или поток или дескриптор) который связан с этим, Вы должны сначала очистить поток, который Вы использовали. См. Раздел 8.5.3 [Очистка Потоков].

Завершение процесса, или выполнение новой программы в процессе, уничтожает все потоки в процессе. Если описатели, связанные с этими потоками сохраняются в других процессах, их файловые позиции станут неопределенными. Чтобы предотвратить это, Вы должны очистить потоки перед их разрушением.

Независимые Каналы

Когда Вы открываете каналы (потоки или описатели) отдельно, каждый канал имеет собственную файловую позицию. Они называются независимыми каналами.

Система обрабатывает каждый канал независимо. В большинстве случаев, это совершенно предсказуемостно и естественно (особенно для ввода): каждый канал может читать или писать последовательно в собственном месте в файле. Однако, если некоторые из каналов - потоки, Вы должны соблюдать предосторожности:

  • Вы должны oчистить выходной поток после использования, перед выполнением чего - нибудь еще, что могло бы читать или писать в ту же самую часть файла.
  • Вы должны oчистить входной поток перед чтением данных, которые могут изменяться использованием независимого канала. Иначе, Вы можете читать устаревшие данные, которые были в буфере потоков.

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

Двум каналам невозможно иметь отдельные указатели файла для файла, который не поддерживает произвольный доступ. Таким образом, каналы для чтения или записи в такие файлы всегда связаны. Каналы конкатенирующего типа также всегда связаны. Для этих каналов, соблюдайте правила связанных каналов; см. Раздел 8.5.1 [Связанные Каналы].

Очистка Потоков

В системе GNU, Вы можете очистить любой поток функцией fclean:

-Функция: int fclean (FILE *stream)

Очищает указанный поток так, чтобы буфер был пуст. На других системах, Вы можете использовать fflush, чтобы oчистить поток в большинстве случаев. Вы можете пропускать fclean или fflush, если Вы знаете, что поток уже пуст. Поток пуст всякий раз, когда буфер пуст. Например, небуферизованный поток всегда пуст. Входной поток, который находится в конце файла, пуст. Буферизированный строчно поток пуст, когда последний выходной символ был символ перевода строки.

Имеется один случай, в котором сброс потока является невозможным на большинстве систем. Тогда, когда поток делает ввод из файла, не являющегося файлом прямого доступа. Такие потоки обычно читают вперед, и когда файл ­ не произвольного доступа, то нет никакого способа сбросить обратно данные излишка уже прочтитанные. Когда входной поток читается из файла прямого доступа, fflush oчищает поток, но оставляет указатель файла в непредсказуемом месте; Вы должны установить указатель файла перед выполнением любого дальнейшего ввода - вывода. В системе GNU, используя fclean Вы избежите обе эти проблемы.

fflush также делает закрытие выходного потока, так что это ­ допустимый способ очистки выходного потока. В системе GNU, закрытие входного потока вызывает fclean.

Вы не нуждаетесь в чистке потока перед использованием дескриптора для операций управления, типа установки режимов терминала; эти операции не воздействуют на файловую позицию и не зависят от нее. Вы можете использовать любой дескриптор для этих операций, и на все каналы воздействовать одновременно. Однако, текст уже 'выведенный' в поток но все еще буферизируемый потоком будет подчинен новым режимам терминала. Чтобы удостовериться что 'прошлый' вывод подчинен установкам терминала, которые были в действительности во время, сбрасывайте выходные потоки для того терминала перед установкой режимов. См. Раздел 12.4 [Режимы Терминала].

8.6 Ожидание Ввода или Вывода

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

Вы не можете обычно использовать для этой цели read, потому что она блокирует программу до ввода на одном указанном описателе файла; ввод на других каналах не будет иметь действия. Вы могли бы установить режим не блокирования и опрашивать каждый описатель файла по очереди, но это очень неэффективно.

Лучшее решение состоит в том, чтобы использовать функцию выбора. Она блокирует программу до ввода или вывода по заданному набору описателей файла, или по таймеру. Это средство объявлено в заглавном файле 'sys/types.h'.

Наборы описателей файла для функции выбора определены как объекты fd_set. Вот описание типа данных и некоторых макрокоманд для управления этих объектов.

fd_set (тип данных)

Этот тип данных представляет наборы описателей файла для функции выбора. Это - фактически битовый массив.

int FD_SETSIZE (макрос)

Значение этой макрокоманды - максимальное число описателей файла, о которых объект fd_set может содержать информацию. На системах с фиксированным максимальным номером, FD_SETSIZE - по крайней мере это число. На некоторых системах, включая GNU, не имеется никакого абсолютного ограничения числа описателей, но эта макрокоманда все еще имеет постоянное значение, которые управляет числом битов в fd_set.

void FD_ZERO (fd_set *set) (макрос)

Эта макрокоманда инициализирует набор наборов описателей файла, как пустое множество.

void FD_SET (int filedes, fd_set *set) (макрос)

Эта макрокоманда добавляет filedes к набору описателей файлов.

void FD_CLR (int filedes, fd_set *set) (макрос)

Эта макрокоманда удаляет filedes из набора дескрипторов файлов.

int FD_ISSET (int filedes, fd_set *set) (макрос)

Эта макрокоманда возвращает значение отличное от нуля (истина), если filedes - элемент набора описателей файлов, и нуль (ложь) иначе.

Вот описание функции выбора непосредственно.

        int select (int nfds, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds,  struct  timeval  *timeout)

Функция выбора блокирует процесс вызова до наличия действий на любом из заданных наборов описателей файла, или пока период блокировки по времени не истечет.

Описатели файла, заданные read_fds аргументом проверяются, являются ли они готовыми для чтения; write_fds описатели файла проверяются, являются ли они готовыми записи; и except_fds описатели файла задают исключительные условия. Вы можете передавать пустой указатель для любого из этих аргументов, если Вы не заинтересованы проверкой этого вида условия.

'Исключительные условия' не означают, что ошибки сообщаются немедленно, когда выполнен ошибочный системный вызов. Они включают условия, типа присутствия срочного сообщения на гнезде. (См. Главу 11 [Гнезда], для уточнения информации о срочных сообщениях.)

Функция выбора проверяет только первые nfds описателей файла. Обычно она передает FD_SETSIZE как значение этого аргумента.

Блокировка по времени определяет максимальное время ожидания. Если Вы передаете пустой указатель в качестве этого аргумента, это означает, что блокировать неопределенно, пока один из описателей файла не готов. Иначе, Вы должны обеспечить время в формате struct timeval; см. Раздел 17.2.2 [Календарь с высоким разрешением]. Определите нуль как время (struct timeval содержащий все нули) если Вы хотите выяснять, какие описатели готовы в данный момент, без ожидания.

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

Если select возвращается, потому что период блокировки по времени истекает, то возвращаемое значение - нуль.

Любой сигнал заставит select возвращаться немедленно. Так, если ваша программа использует сигналы, Вы не можете полагаться на select, чтобы ждать полное заданное время. Если Вы хотите убедиться в правильности ожидания, Вы должны проверить EINTR и повторять select с расчетной блокировкой по времени, основанной на текущем времени. См. пример ниже. См. также Раздел 21.5 [Прерванные Примитивы].

Если происходит ошибка, select возвращает -1 и не изменяет аргумент наборов описателей файла. Следующие errno условия ошибки определены для этой функции:

EBADF

Один из набора описателей файла определил недопустимый дескриптор файла.

EINTR

Операция была прервана сигналом. См. Раздел 21.5 [Прерванные Примитивы].

EINVAL

Аргумент блокировки по времени недопустим; один из компонентов отрицателен или слишком большой.

Примечание о Переносимости: функция select - BSD возможность UNIX. Вот пример, показывающий, как Вы можете использовать select, чтобы установить период блокировки по времени для чтения из дескриптора файла. Input_timeout функция блокирует процесс вызова, пока ввод не доступен (на описателе файла), или пока период блокировки по времени не истек.

      #include 
      #include 
      #include 
      #include 

      int
      input_timeout (int filedes, unsigned int seconds)
      {
        fd_set set;
        struct timeval timeout;

        /* Инициализируем набор дескрипторов файлов. */
        FD_ZERO (&set);
        FD_SET (filedes, &set);

        /* Инициализируем структуру timeout. */
        timeout.tv_sec = seconds;
        timeout.tv_usec = 0;

        /* `select' returns 0 if timeout, 1 if input available, -1 if error.
 */
        return TEMP_FAILURE_RETRY (select (FD_SETSIZE,
                                           &set, NULL, NULL,
                                           &timeout));
      }

      int
      main (void)
      {
        fprintf (stderr, 'select returned %d.\n',
                 input_timeout (STDIN_FILENO, 5));
        return 0;
      }

8.7 Операции Управления Файлами

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

Второй аргумент fcntl функции - команда, которая определяет которую операцию выполнить. Функция и макрокоманды, которые обозначают различные флаги, объявлены в заглавном файле 'fcntl.h'. (Многие из этих флагов также используются функцией open; см. Раздел 8.1 [Открытие и Закрытие Файлов].)

-Функция: int fcntl (int filedes, int command, ...)

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

Вот краткий список различных команд.

F_DUPFD

Дублирует дескриптор файла (возвращает другой дескриптор файла, указывающий на тот же самый открытый файл). См. Раздел 8.8 [Дублирование Дескрипторов].

F_GETFD

Получает флаги, связанные с описателем файла. См. Раздел 8.9 [Дескрипторные Флаги]. F_SETFD Устанавливает флаги, связанные с дескриптором файла. См. Раздел 8.9 [Дескрипторные Флаги].

F_GETFL

Получает флаги, связанные с открытым файлом. См. Раздел 8.10 [Флаги Состояния Файла].

F_SETFL

Устанавливает флаги, связанные с открытым файлом. См. Раздел 8.10 [Флаги Состояния Файла].

F_GETLK

Получает блокировку файла. См. Раздел 8.11 [Блокировки Файла].

F_SETLK

Устанавливает или снимает блокировку файла. См. Раздел 8.11 [Блокировки Файла].

F_SETLKW

Подобен F_SETLK, но ждет завершения. См. Раздел 8.11 [Блокировки Файла].

F_GETOWN

Получает процесс или ID группы процессов, чтобы получить сигналы SIGIO. См. Раздел 8.12 [Ввод Прерывания].

F_SETOWN

Устанавливает процесс или ID группы процессов, чтобы получить сигналы SIGIO. См. Раздел 8.12 [Ввод Прерывания].

8.8 Дублирование Дескрипторов

Вы можете дублировать дескриптор файла, или зарезервировать другой дескриптор файла, который относится к тому же самому открытому файлу, что и первый. Двойные дескрипторы совместно используют одну и ту же файловую позицию и один набор флагов состояния файла (см. Раздел 8.10 [Флаги Состояния Файла]), но каждый имеет собственный набор флагов дескриптора файла (см. Раздел 8.9 [Дескрипторные Флаги]).

Основное использование дублирования дескриптора файла это переадресация ввода или вывода: то есть замена файла или канала, которому указанный дескриптор файла соответствует.

Вы можете выполнять эту операцию, используя fcntl функцию с командой F_DUPFD, но имеются также удобные функции dup и dup2 для дублирования дескрипторов.

Fcntl функция и флаги объявлена в 'fcntl.h', в то время как прототипы для dup и dup2 находятся в файле 'unistd.h'.

-Функция: int dup (int old)

Эта функция копирует дескриптор old в первый доступный дескрипторный номер (первый номер не открытый сейчас). Это эквивалентно fcntl (old, F_DUPFD, 0).

-Функция: int dup2 (int old, int new)

Эта функция копирует дескриптор old в дескриптор new.

Если оld - недопустимый дескриптор, то dup2 не делает ничего. Иначе, новый дубликат old заменяет любое предыдущее значение дескриптора new, как будто он был закрыт сначала.

Если оld и new - различные числа, и old - допустимый дескрипторный номер, то dup2 эквивалентен:

                        close (new);
                        fcntl (old, F_DUPFD, new)
Однако, dup2 делает это автоматически; нет никакого момента в середине вызова dup2, когда new закрыт, а дубликата old еще нет.
-Макрос int F_DUPFD

Эта макрокоманда используется как аргумент команды fcntl, для копирования дескриптора файла, данного как первый аргумент.

Форма обращения в этом случае:

        fcntl (old, F_DUPFD, next_filedes)
Next_filedes аргумент имеет тип int и определяет, что возвращенный дескриптор файла должен быть следующим доступным больше или равным этому значению.

Возвращаемое значение из fcntl с этой командой - обычно значение нового дескриптора файла. Возвращаемое значение -1 указывает ошибку. Следующие errno условия ошибки определены для этой команды:

EBADF

Аргумент old недопустим.

EINVAL

Next_filedes аргумент недопустим.

EMFILE

Дескрипторов файла, доступных вашей программе, больще нет.

ENFILE

- не возможный код ошибки для dup2, потому что dup2 не создает новое открытие файла; двойные дескрипторные подпадают под ограничение, которое указывает ENFILE.

EMFILE

возможен, потому что это относится к ограничению различных дескрипторных чисел для использования в одном процессе.

Вот пример, показывающий, как использовать dup2, чтобы делать переадресацию. Обычно, переадресация стандартных потоков (подобно stdin) выполняется командным интерпретатором или подобной программой перед вызовом одной из запускаемых функций (см. Раздел 23.5 [Выполнение Файла]) чтобы выполнить новую программу в дочернем процессе. Когда новая программа выполнена, она создает и инициализирует стандартные потоки, чтобы указать на соответствующие дескрипторы файла, прежде, чем функция main вызывается.

Так, чтобы переназначить стандартный ввод в файл, оболочка могла бы делать что - нибудь вроде:

                pid = fork ();
                if (pid == 0)
                        {
                                char *filename;
                                char *program;
                                int file;
                                .       . .
                                file = TEMP_FAILURE_RETRY (open
                                (filename, O_RDONLY));
                                dup2 (file, STDIN_FILENO);
                                TEMP_FAILURE_RETRY (close (file));
                                execv (program, NULL);
                        }

8.9 Флаги Дескриптора Файла

Флаги дескриптора Файла - разнообразные атрибуты дескриптора файла. Эти флаги связаны со специфическими дескрипторами файла, так, что если Вы создаете двойные дескрипторы файла из одиночного открытия файла, то каждый дескриптор имеет собственный набор флагов.

В настоящее время имеется только один флаг дескриптора файла: FD_CLOEXEC, который заставляет дескриптор быть закрытым если Вы используете любую из запускаемых функций (см. Раздел 23.5 [Выполнение Файла]).

Символы в этом разделе определены в заглавном файле 'fcntl.h'.

int F_GETFD (макрос)

Эта макрокоманда используется как аргумент команды fcntl, чтобы определять, что она должна возвратить флаги дескриптора файла, связанные с filedes аргументом.

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

В случае ошибки, fcntl возвращает -1. Следующие errno условия ошибки определены для этой команды:

EBADF filedes аргумент недопустим.

int F_SETFD (макрос)

Эта макрокоманда используется как аргумент команды fcntl, для определения, что она должна установить флаги дескриптора файла, связанные с filedes аргументом. Она требует третий int аргумент, чтобы определить новые флаги, так что форма обращения:

        fcntl (filedes, F_SETFD, new_flags)
Нормальное возвращаемое значение из fcntl с этой командой ­ неопределенное значение отличное от -1, которое указывает ошибку. Флаги и условия ошибки - также как для команды F_GETFD.

Следующая макрокоманда определена для использования как флаг дескриптора файла с fcntl функцией. Значение - константа integer пригодное для использования как значение битовой маски.

int FD_CLOEXEC (макрос)

Этот флаг определяет, что дескриптор файла должен быть закрыт, когда вызывается запускаемая функция; см. Раздел 23.5 [Выполнение Файл]. Когда дескриптор файла размещен (как с open или dup), этот бит первоначально очищен на новом описателе файла, означая, что дескриптор останется в живых в новой программе после запуска.

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

                int
                set_cloexec_flag (int desc, int value)
                {
                        int oldflags = fcntl (desc, F_GETFD, 0);

                        if (oldflags < 0)
                                        return oldflags;
                        /* Установим только флаг который мы хотим
                        установить. */
                        if (value != 0)
                                        oldflags |= FD_CLOEXEC;
                        else
                                        oldflags &= ~FD_CLOEXEC;

                        return fcntl (desc, F_SETFD, oldflags);
                }

8.10 Флаги Состояния Файла

Флаги состояния Файла используются, чтобы определить атрибуты открытия файла. В отличие от флагов дескриптора файла, обсужденных в Разделе 8.9 [Дескрипторные Флаги], флаги состояния файла разделяются дублированными дескрипторами файла, следующими из одиночного открытия файла.

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

Несколько флагов состояния файла могут быть изменены, в любое время используя fcntl. Они включают O_APPEND и O_NONBLOCK.

Символы в этом разделе определены в заглавном файле 'fcntl.h'.

int F_GETFL (макрос)

Эта макрокоманда используется как аргумент команды fcntl, для чтения флагов состояния файла для файла открытого с описателем filedes.

Нормальное возвращаемое значение из fcntl с этой командой ­ неотрицательное число, которое может интерпретироваться как поразрядное ИЛИ индивидуальных флагов. Флаги закодированы так же, как аргумент флагов для open (см. Раздел 8.1 [Открытие и Закрытие Файлов]), но здесь значимы только режимы доступа файла и O_APPEND и O_NONBLOCK флаги. Так как режимы доступа файла - не одноразрядные значения, Вы можете маскировать другие биты в возвращенных флагах с O_ACCMODE, чтобы сравнить их.

В случае ошибки, fcntl возвращает -1. Следующие errno условия ошибки определены для этой команды:

EBADF filedes аргумент недопустим.

-Макрос: int F_SETFL

Эта макрокоманда используется как аргумент команды fcntl, для установки флагов состояния файла для открытого файла, соответствующего filedes аргументу. Эта команда требует третьего int аргумента, чтобы определить новые флаги, так что обращение походит на это:

        fcntl (filedes, F_SETFL, new_flags)
Вы не можете изменять режим доступа для файла таким образом; то есть был ли дескриптор файла открыт для чтения или для записи. Вы можете только заменять флаги O_APPEND и O_NONBLOCK.

Нормальное возвращаемое значение из fcntl с этой командой ­ неопределенное значение отличное от -1, которое указывает ошибку. Условия ошибки - такие же как для команды F_GETFL.

Следующие макрокоманды определены для анализа и построения значений флага состояния файла:

  • O_APPEND - бит, который делает возможным конкатенирующий режим для файла. Если установлен, то все операции write пишут данные в конце файла, расширяя его, независимо от текущей файловой позиции.
  • O_NONBLOCK Бит, который делает возможным режим неблокирования для файла. Если этот бит установлен, запрос чтения в файле может возвращаться немедленно с состоянием отказа, если не имеется никакого немедленно доступного ввода кроме блокирования. Аналогично, запросы write могут также возвращаться немедленно с состоянием отказа, если вывод нельзя написать немедленно.
  • O_NDELAY Это - синоним для O_NONBLOCK, предусматривающий совместимость с BSD.
-Макрос: int O_ACCMODE

Эта макрокоманда замещает маску, которая может быть поразрядна AND со значением флага состояния файла, чтобы произвести значение, представляющее режим доступа файла. Режим будет O_RDONLY, O_WRONLY, или O_RDWR.

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

                int
                set_nonblock_flag (int desc, int value)
                {
                        int oldflags = fcntl (desc, F_GETFL, 0);

                        if (oldflags < 0)
                                        return oldflags;

                        if (value != 0)
                                        oldflags |= O_NONBLOCK;
                        else
                                        oldflags &= ~O_NONBLOCK;
                        return fcntl (desc, F_SETFL, oldflags);
                }

8.11 Блокировки Файла

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

Исключительная блокировка или блокировка записи дает монопольный доступ процессу для записи в заданной части файла. В то время как установлена блокировка никакой другой процесс не может блокировать эту часть файла.

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

Функции read и write фактически не выясняет, имеются ли блокировки в данном месте.

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

Блокировки связаны с процессами. Процесс может иметь только один вид набора блокировок для каждого байта данного файла. Когда любой дескриптор файла для того файла закрыт процессом, все блокировки, которые обрабатывают связи с тем файлом, опущены, даже если блокировки были сделаны, используя другие описатели, которые остаются открытыми. Аналогично, блокировки опущены, когда процесс выходит, и не наследуются дочерними созданными процессами (см. Раздел 23.4 [Создание Процесса]).

При создании блокировки, используйте struct flock, чтобы определить какую блокировку и где Вы хотите выполнить. Этот тип данных и связанные макрокоманды для fcntl функции объявлен в заглавном файле 'fcntl.h'.

       struct flock  (тип данных)

Эта структура используется с fcntl функцией, чтобы описать блокировку файла. Она имеет следующие элементы:

                short int l_type

Определяет тип блокировки; один из F_RDLCK, F_WRLCK, или F_UNLCK.

                short int l_whence

Соответствует к аргументу whence для fseek или lseek, и определяет то, относительно чего задано смещение. Значение может быть одно из SEEK_SET, SEEK_CUR, или SEEK_END.

                off_t l_start

Определяет смещение начала области, к которой блокировка применяется, и дана в байтах относительно отметки, заданной элементом l_whence.

                off_t l_len

Определяет длину области, которая будет блокирована. Значение 0 обрабатывается особенно; это означает область, что простирается до конца файла.

                pid_t l_pid

Это поле - ID процесса (см. Раздел 23.2 [Понятия Создания Процесса]) процесса, содержащего блокировку. Оно задается вызовом fcntl с командой F_GETLK, но игнорируется при создании блокировки. -Макрос int F_GETLK

Эта макрокоманда используется как аргумент команды fcntl, для определения, что она должна получить информацию относительно блокировки. Эта команда требует третьего аргумента типа struct flock *, чтобы быть переданной к fcntl, так, чтобы форма обращения была:

        fcntl (filedes, F_GETLK, lockp)

Вы должны определить тип блокировки F_WRLCK, если Вы хотите выяснять относительно блокировок чтения и записи, или F_RDLCK, если Вы хотите выяснять относительно блокировок только записи.

Может иметься больше чем одна блокировка, воздействующая на область, заданную lockp аргументом, но fcntl только возвращает информацию относительно одной из них. l_whence элемент структуры lockp установлен в SEEK_SET и l_start и l_len набор полей, чтобы идентифицировать блокированную область.

Если никакая блокировка не применяется, единственное изменение для структуры lockp, должно модифицировать l_type в значение F_UNLCK.

Нормальное возвращаемое значение из fcntl с этой командой ­ неопределенное значение отличное от -1, которое зарезервировано, чтобы указать ошибку. Следующие errno условия ошибки определены для этой команды:

EBADF filedes аргумент недопустим.

EINVAL

Или lockp аргумент не определяет допустимую информацию блокировки, или файл, связанный с filedes не поддерживает блокировки.

-Макрос int F_SETLK

Эта макрокоманда используется как аргумент команды fcntl, для определения, что она должна установить или снять блокировку. Эта команда требует третьего аргумента типа struct flock *, так, чтобы форма обращения была:

        fcntl (filedes, F_SETLK, lockp)
Если процесс уже имеет блокировку на любой части области, старая блокировка на той части, заменяется на новую блокировку. Вы можете удалять блокировку, определяя тип блокировки F_UNLCK.

Если блокировка не может быть установлена, fcntl возвращается немедленно со значением -1. Эта функция не блокирует ожидание пока другие процессы снимут блокировки. Если fcntl преуспевает, она возвращает значение отличное от -1.

Следующие errno условия ошибки определены для этой функции:

EACCES

EAGAIN

Блокировка не может быть установлена, потому что это блокировано существующей блокировкой на файле.

Некоторые системы используют EAGAIN в этом случае, и другие системы используют EACCES; ваша программа должна обработать их одинаково, после F_SETLK.

EBADF

Также: filedes аргумент недопустим; Вы запросили блокировку чтения, но filedes - не открыт для чтения; или, Вы запросили блокировку записи, но filedes - не открыт для записи.

EINVAL

Или lockp аргумент определяет недопустимую информацию блокировки, или файл, связанный с filedes не поддерживает блокировки.

ENOLCK

Система работает без ресурсов блокировки файла; имеются уже слишком много блокировок файла.

Хорошо разработанные файловые системы никогда не сообщают эту ошибку, потому что они не имеют никакого ограничения на число блокировок. Однако, Вы должны все еще принимать во внимание возможности этой ошибки, поскольку это может следовать из доступа к сети к файловой системе на другой машине.

int F_SETLKW (макрос)

Эта макрокоманда используется как аргумент команды fcntl, для определения, что она должна установить или снять блокировку. Это ­ тоже что команда F_SETLK, но заставляет процесс блокировать (или ждать) пока запрос не будет определен.

Эта команда требует третьего аргумента типа struct flock *, как для команды F_SETLK.

Fcntl возвращаемые значения и ошибки - те же что для команды F_SETLK, но эти дополнительные errno условия ошибки определены для этой команды:

EINTR

Функция была прервана сигналом, во время ожидания. См. Раздел 21.5 [Прерванные Примитивы].

EDEADLK

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

Следующие макрокоманды определены для использования как значения для l_type элемента структуры flock. Значения - константы integer.

F_RDLCK

Эта макрокоманда используется, чтобы установить блокировку чтения.

F_WRLCK

Эта макрокоманда используется, чтобы установить блокировку записи.

F_UNLCK

Эта макрокоманда используется, чтобы установить, что область разблокируется.

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

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

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

8.12 Управляемый прерываниями Ввод

Если Вы устанавливаете FASYNC флаг состояния на дескрипторе файла (см. Раздел 8.10 [Флаги Состояния Файла]), сигнал SIGIO посланы всякий раз, когда производится ввод или вывод, становится возможным на этом описателе файла. Процесс или группа процессов, может быть выбрана чтобы получить сигнал, используя команду F_SETOWN для fcntl функции. Если дескриптор файла является гнездом, он также, выбирает получателя сигналов SIGURG, которые возникают, когда внепоточные данные прибывает в это гнездо; см. Раздел 11.8.8 [Внепоточные Данные].

Если дескриптор файла соответствует терминалу, то SIGIO сигналы посылаются группе приоритетного процесса терминала. См. Главу 24 [Управление заданиями].

Символы в этом разделе определены в заглавном файле 'fcntl.h'.

-Макрос int F_GETOWN

Эта макрокоманда используется как аргумент команды fcntl, для определения того, что она должна получить информацию относительно процесса или группы процессов, которой посланы сигналы SIGIO. (Для терминала, это - фактически приоритетный ID группы процессов, которую Вы можете получать использованием tcgetpgrp; см. Раздел 24.7.3 [Функции Доступа Терминала].)

Возвращаемое значение интерпретируется как ID процесса; если оно отрицательно, то абсолютное значение - ID группы процессов.

Следующее errno условие ошибки определено для этой команды:

EBADF

Filedes аргумент недопустим.

int F_SETOWN (макрос)

       fcntl (filedes, F_SETOWN, pid)

Возвращаемое значение из fcntl с этой командой - -1 в случае ошибки и некоторое другого значения если обращение успешно. Следующие errno условия ошибки определены для этой команды:

EBADF

Filedes аргумент недопустим.

ESRCH

Не имеется никакого процесса или группы процессов, соответствующей pid.

9. Интерфейсы Файловой системы

Эта глава описывает функции библиотеки GNU C для управления файлами. В отличие от функций ввода и функций вывода, описанных в Главе 7 [ввод - вывод на Потоках] и Главе 8 [ввод - вывод низкого уровня], эти функции имеют отношение к действиям на файлах непосредственно.

Среди средств, описанных в этой главе - функции для исследования или изменения каталогов, функции для переименования и удаления файлов, и функции для исследования и установки атрибутов файла типа прав доступа и изменения времени.

9.1 Рабочий каталог

Каждый процесс связанный с этим каталогом, называемым текущим рабочим каталогом или просто рабочим каталогом, который используется в уточнении имен файла прямого доступа (см. Раздел 6.2.2 [Уточнение Имени файла]).

Когда Вы регистрируетесь в системе и начинаете новый сеанс, ваш рабочий каталог первоначально устанавливается в исходный каталог, связанный с вашим логическим входом в систему в базе данных пользователей системы. Вы можете находить исходный каталог любого пользователя, используются getpwuid или getpwnam функции; см. Раздел 25.12 [База данных Пользователей].

Пользователи могут изменять рабочий каталог, используя команды оболочки подобно cd. Функции, описанные в этом разделе - примитивы, используемые командами и другими программами для исследования и изменения рабочего каталога.

Прототипы для этих функций объявлены в файле 'unistd.h'.

       char * getcwd (char *buffer, size_t size)  (функция)

Getcwd функция возвращает абсолютное имя файла, представляющее текущий рабочий каталог, сохраняя его в символьном буфере, который Вы указываете. Аргумент size - то, как Вы сообщаете системе размер резервирования буфера.

Версия GNU этой функции также разрешает Вам определять пустой указатель для буферного аргумента. Тогда getcwd зарезервирует буфер автоматически, как malloc (см. Раздел 3.3 [Беспрепятственное Резервирование]).

Возвращаемое значение - буфер в случае успеха и пустой указатель при отказе. Следующие errno условия ошибки определены для этой функции:

EINVAL

аргумент size - нуль, и буфер - не пустой указатель.

ERANGE

аргумент size - меньше чем длина имени рабочего каталога.

Вы должны зарезервировать больший массив и попытаться снова.

EACCES

Права на чтение или поиск компонент имени файла были превышены.

Вот пример, показывающий, как Вы могли бы реализовать GNU getcwd (NULL, 0) используя только стандартное поведение getcwd:

                char *
                gnu_getcwd ()
                {
                        int size = 100;
                        char *buffer = (char *) xmalloc (size);
                        while (1)
                                {
                                        char*value = getcwd(buffer,size);
                                        if (value != 0)
                                                return buffer;
                                        size *= 2;
                                        free (buffer);
                                        buffer = (char *) xmalloc (size);
                                }
                }

См. Раздел 3.3.2 [Примеры Malloc], для уточнения информации относительно xmalloc, которая - не библиотечная функция, но ­ обычное имя, используемое в большинстве программного обеспечения GNU.

       char * getwd (char *buffer)  (функция)

Подобна getcwd. Библиотека GNU обеспечивает getwd для совместимости в обратном направлении с BSD. Буфер должен быть указатель на массив по крайней мере PATH_MAX байтов.

 -Функция:    int chdir (const char *filename)

Эта функция используется, чтобы установить рабочий каталог процесса filename.

Нормальное, успешное возвращаемое значение из chdir - 0. Значение -1 возвращается, чтобы указать ошибку. Errno условия ошибки, определенные для этой функции - обычные синтаксические ошибки имени файла (см. Раздел 6.2.3 [Ошибки Имени файла]), плюс ENOTDIR, если filename файла - не каталог.

9.2 Доступ в Каталоги

Средства, описанные в этом разделе допускают Вам читать содержимое файла каталога. Это полезно, если Вы хотите, чтобы ваша программа перечислила все файлы в каталоге, возможно как часть меню.

Opendir функция открывает поток каталога, чьи элементы являются входами каталога. Вы используете readdir функцию на потоке каталога, чтобы отыскать эти входы, представляемые как объекты struct dirent. Имя файла для каждого входа сохранено в d_name элементе этой структуры. Имеются очевидные параллели здесь со средствами потока для обычных файлов, описанных в Главе 7 [ввод ­ вывод на Потоках].

Формат Входа в Каталог

Этот раздел описывает то, что Вы находите входя в каталога, поскольку Вы могли бы получать это из потока каталога. Все символы объявлены в заглавном файле 'dirent.h'.

-Тип данных: struct dirent

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

                        char *d_name
Это - компонента имени файла с нулевым символом в конце. Это ­ единственное поле, на которое Вы можете рассчитывать во всех POSIX системах.
                        ino_t d_fileno
Это - серийный номер файла. Для BSD совместимости, Вы можете также обратиться к этому элементу как к d_ino.
                        size_t d_namlen
Это - длина имени файла, исключая пустой символ завершения.

Эта структура в будущем может содержать дополнительные элементы.

Когда файл имеет многочисленные имена, каждое имя имеет собственный вход в каталог. Единственный способ которым Вы можете сообщать, что входы каталога принадлежат одиночному файлу - это, если они имеют то же самое значение для d_fileno поля.

Атрибуты Файла типа размера, числа изменений, и т.п. - часть файла непосредственно, см. Раздел 9.8 [Атрибуты Файла].

Открытие Потока Каталога

Этот раздел описывает, как открыть поток каталога. Все символы объявлены в заглавном файле 'dirent.h'.

-Тип данных DIR

Тип данных DIR представляет поток каталога.

Вы не должны когда-либо резервировать объекты struct dirent или типов данных DIR, так как функции доступа каталога делают это для Вас. Вместо этого, Вы обращаетесь к этим объектам, используя указатели, возвращенные следующими функциями.

       DIR * opendir (const char *dirname)  (функция)
Opendir функция открывает и возвращает поток каталога для чтения каталога, чье имя dirname. Поток имеет тип DIR *.

Если происходит ошибка opendir возвращает пустой указатель. В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Имя файла Errors]), следующие errno условия ошибки определены для этой функции:

EACCES право чтения отклонено для каталога dirname.

EMFILE процесс имеет слишком много открытых файлов.

ENFILE вся система, или возможно файловая система, которая

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

Тип DIR обычно реализован, используя дескриптор файла, а opendir функция в терминах функции open. См. Главу 8 [ввод - вывод низкого уровня]. Потоки Каталога и основные дескрипторы файла закрываются при запуске (см. Раздел 23.5 [выполнение Файла]).

Чтение и Закрытие Потока Каталога

Этот раздел описывает, как читать входы каталога из потока каталога, и как закрыть поток, когда Вы закончили работу с ним. Все символы объявлены в файле 'dirent.h'.

       struct dirent * readdir (DIR *dirstream)  (функция)

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

Примечание Переносимости: На некоторых системах, readdir не может возвращать входы для '. ' и ' .. ', даже если они - всегда допустимые имена файла в любом каталоге. См. Раздел 6.2.2 [Уточнение Имени файла].

Если нет больше входов в каталоге, или обнаружена ошибка, readdir возвращает пустой указатель. Следующие errno условия ошибки определены для этой функции:

EBADF

irstream аргумент не допустим.

       int closedir (DIR *dirstream)  (функция)
Эта функция закрывает поток каталога dirstream. Она возвращает 0 при успехе и -1 при отказе.

Следующие errno условия ошибки определены для этой функции:

EBADF

dirstream аргумент не допустим.

Простая Программа Просмотра Каталога

Имеется простая программа, которая печатает имена файлов в текущем рабочем каталоге:

                #include 
                #include 
                #include 
                #include 
                int
                main (void)
                {
                        DIR *dp;
                        struct dirent *ep;
                        dp = opendir ('./');
                        if (dp != NULL)
                                {
                                        while (ep = readdir (dp))
                                                puts (ep->d_name);
                                        (void) closedir (dp);
                                }
                        else
                                puts ('Couldn't open the directory.');
                        return 0;
                }

Произвольный доступ в Потоке Каталога

Этот раздел описывает, как повторно читать части каталога, который Вы уже читали из открытого потока каталога. Все символы объявлены в заглавном файле 'dirent.h'.

       void rewinddir (DIR *dirstream)  (функция)

Rewinddir функция используется, чтобы повторно инициализировать поток каталога dirstream, так, чтобы, если Вы вызвали readdir, она возвращала информацию относительно первого входа в каталоге снова.

       off_t telldir (DIR *dirstream)  (функция)

Telldir функция возвращает файловую позицию потока каталога dirstream. Вы можете использовать это значение с seekdir, чтобы восстановить поток каталога в эту позицию.

       void seekdir (DIR *dirstream, off_t pos) Function

Seekdir функция устанавливает файловую позицию потока каталога dirstream в pos.

Значение pos должно быть результатом предыдущего обращения к telldir на этом специфическом потоке; закрытие и повторное открытие каталога может объявить неверным значения, возвращенные telldir.

9.3 Жесткие Связи

В POSIX системах, один файл может иметь много имен в то же самое время. Все имена в равной степени реальны, и никакое из них не лучше других.

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

Один файл может иметь имена в отдельных каталогах, так что, организация файловой системы - не строгая иерархия или дерево.

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

Прототип для функции link объявлен в заглавном файле 'unistd.h'.

       int link (const char *oldname, const char *newname)  (функция)

Функция link делает новую ссылка с существующим файлом, называемым oldname, под новым именем newname.

Эта функция возвращает значение 0, если она успешна и -1 при отказе. В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Ошибки Имени файла]) и для oldname и newname, следующие errno условия ошибки определены для этой функции:

EACCES

каталог, в котором link должна написать, нельзя перезаписывать.

EEXIST

имеется уже файл, именованный newname. Если Вы хотите заменить эту ссылку на новую ссылку, Вы должны сначала явно удалить старую ссылку.

EMLINK

имеются уже слишком много связей к файлу, именованному oldname. (Максимальное число связей к файлу - LINK_MAX; см. Раздел 27.6 [Ограничения для Файлов].)

Хорошо разработанные файловые системы никогда не сообщают эту ошибку, потому что они разрешают большее количество связей чем ваш диск мог бы содержать. Однако, Вы должны все еще принимать во внимание возможности этой ошибки, поскольку она могла бы следовать из доступа к сети к файловой системе на другой машине.

ENOENT

файл, именованный oldname не существует. Вы не можете делать ссылок к файлу, который не существует.

ENOSPC

каталог или файловая система, которая содержала бы, новую ссылка 'полна' и не может быть расширена.

EPERM

Некоторые реализации позволяют только привилегированным пользователям делать связи к каталогам, а другие запрещают эту операцию полностью. Эта ошибка используется, чтобы сообщить эту проблему.

EROFS

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

EXDEV

Каталог, заданный в newname находится в другой файловой системе чем существующий файл.

9.4 Символические Связи

Система GNU поддерживает связи программного обеспечения или символические связи. Это - вид 'файла', который является по существу указателем на другое имя файла. В отличие от жестких обращений, символические связи могут быть сделаны к каталогам или между файловыми системами без ограничений.

Функция open понимает, что Вы передали имя ссылки, и читает имя файла, вместо этого открывает файл, указываемый ссылкой. Stat функция аналогично функционирует на файле, на который указывает символическая ссылка, а не на связи непосредственно. Как связывается, функция, которая делает жесткое обращение.

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

Прототипы для функций, перечисленных в этом разделе находятся в 'unistd.h'.

-Функция: int symlink (const char *oldname, const char *newname)

Symlink функция делает символическую ссылку к oldname, с именем newname.

Нормальное возвращаемое значение из symlink - 0. Возвращаемое значение -1 указывает ошибку.

В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Ошибки Имени файла]), следующие errno условия ошибки определены для этой функции:

EEXIST

имеется уже существующий файл по имени newname.

EROFS

файл newname существовал бы на файловой системе только для чтения.

ENOSPC

каталог или файловая система не может быть расширена, чтобы делать новую ссылка.

EIO

аппаратная ошибка произошла при чтении или записи данных относительно диска.

-Функция: int readlink (const char *filename, char *buffer, size_t size)

Readlink функция получает значение символической filename связи. Имя файла, на которое ссылка указывает, скопировано в буфер. Эта строка имени файла не с нулевым символом в конце; readlink обычно возвращает скопированное число символов. Аргумент size определяет максимальное число символов, для копирования, обычно размер резервирования буфера.

Если возвращаемое значение равняется size, Вы не можете распознать, имелся или нет участок памяти, чтобы возвратить все имя. Так что делайте больший буфер, и вызовите readlink снова. Вот пример:

        char *
        readlink_malloc (char *filename)
        {
                int size = 100;
                while (1)
                {
                        char *buffer = (char *) xmalloc (size);
                        int nchars= readlink(filename,buffer,size);
                        if (nchars < size)
                        return buffer;
                        free (buffer);
                        size *= 2;
                }
        }
Значение -1 возвращается в случае ошибки. В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Ошибки Имени файла]), следующие errno условия ошибки определены для этой функции:
EINVAL

именованный файл - не символическая ссылка.

EIO

аппаратная ошибка произошла при чтении или записи данных относительно диска.

9.5 Удаление Файлов

Вы можете удалять файл функциями unlink или remove. (Эти имена синонимичны.)

Стирание фактически удаляет имя файла. Если это является только именем файла, то файл удален также. Если файл имеет другие имена также (см. Раздел 9.3 [Жесткие обращения]), то он остается доступным под другими именами.

-Функция: int unlink (const char *filename)

Функция unlink удаляет filename имя файла. Если это является единственным именем файла, файл непосредственно также удален. (Фактически, если любой процесс работает с файлом, когда это случается, стирание откладывается, пока все процессы не закрыли файл.)

Unlink функция объявлена в заглавном файле 'unistd.h'.

Эта функция возвращает 0 при успешном завершении, и -1 при ошибке. В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Имя файла Errors]), следующие errno условия ошибки определены для этой функции:

EACCESS

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

EBUSY

Эта ошибка указывает, что файл используется системой таким способом, что он не может быть удален.

ENOENT

имя файла, которое будет удалено не существует.

EPERM

На некоторых системах, unlink не может использоваться, чтобы удалить имя каталога, или может использоваться только привилегированным пользователем. Чтобы избегать таких проблем, используйте rmdir, чтобы удалить каталоги.

EROFS

Каталог, в котором имя файла должно быть удалено, находится в файловой системе только для чтения, и не может изменяться.

-Функция: int remove (const char *filename)

Функция remove - другое имя для unlink. Remove - имя ANSI C, в то время как unlink - POSIX.1 имя. Remove имя объявлено в 'stdio.h'.

-Функция: int rmdir (const char *filename)

Rmdir функция удаляет каталог. Каталог должен быть пуст прежде, чем он может быть удален; другими словами, он может только содержать элементы '. ' и ' .. '.

В большинстве других отношений, rmdir ведет себя подобно unlink. Имеются два дополнительных errno условия ошибки, определенные для rmdir:

EEXIST

ENOTEMPTY

Каталог, который будет удален не пуст.

Эти два кода ошибки синонимичны; некоторые системы используют один, а некоторые - другой.

Прототип для этой функции объявлен в заглавном файле 'unistd.h'.

9.6 Переименование Файлов

Функция rename используется, чтобы заменить имя файла.

       int rename (const char *oldname, const char *newname) (функция)

Функция rename переименовывает имя файла oldname в newname. Файл, прежде доступный под именем oldname позже доступен как newname. (Если файл имел любые другие имена кроме oldname, он продолжает иметь те имена.)

Каталог, содержащий имя newname должен быть в той же самой файловой системе как файл (что обозначен именем oldname).

Один частный случай для rename - то, когда oldname и newname ­ два имени для того же самого файла. Непротиворечивый способ обрабатывать этот случай состоит в том, чтобы удалить oldname. Однако, POSIX говорит, что rename в этом случае не делает ничего, и не сообщает об ошибке. Мы не знаем то, что ваша операционная система будет делать. Система GNU будет возможно делать правильно (удалять oldname) если Вы явно не запрашиваете строгую POSIX совместимость 'даже когда она причиняет вред'.

Если oldname - не каталог, то любой существующий файл, именованный newname, удален в течение операции переименования. Однако, если newname - имя каталога, происходит сбой rename в этом случае.

Если oldname является каталогом, то или newname, не должен существовать, или должен быть указан каталог, который является пустым. В последнем случае, существующий каталог, именованный newname удален сначала. Имя newname не должно определить подкаталог каталога oldname, который переименовывается.

Если rename терпит неудачу, она возвращает -1. В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Имя файла Errors]), следующие errno условия ошибки определены для этой функции:

EACCES

Один из каталогов, содержащих newname или oldname отказывает в записи; или newname и oldname - каталоги, и в праве записи отказано для одного из них.

EBUSY

Каталог, именованный oldname или newname используется системой способом, который предотвращает переименование во время работы. Это включает каталоги, которые являются точками крепления для filesystems, и каталогов, которые являются текущими рабочими каталогами процессов.

EEXIST

каталог newname существует.

ENOTEMPTY

Каталог newname не пуст.

EINVAL

oldname - каталог, который содержит newname.

EISDIR

newname называет каталог, а oldname нет.

EMLINK

Каталог предыдущего уровня newname имел бы слишком много связей.

Хорошо разработанные файловые системы никогда не сообщают эту ошибку, потому что они разрешают большое количество связей чем ваш диск, мог бы содержать. Однако, Вы должны все еще принимать во внимание возможности этой ошибки, поскольку она могла бы следовать из доступа к сети к файловой системе на другой машине.

ENOENT

файл, именованный oldname не существует.

ENOSPC

каталог, который содержал бы, newname не имеет никакого участка памяти для другого входа, и нет никакого места, оставшегося в файловой системе, чтобы расширить его.

EROFS

Операция включила бы запись в каталогу на файловой системе только для чтения.

EXDEV

Два имени файла newname и oldnames находятся в различных файловых системах.

9.7 Создание Каталогов

Каталоги создаются функцией mkdir. (Имеется также команда оболочки mkdir, которая делает то же самое.)

 -Функция:     int mkdir (const char *filename, mode_t mode)

Mkdir функция создает новый, пустой каталог, с именем filename.

Mode аргумент определяет права файла для нового файла каталога. См. Раздел 9.8.5 [Биты Прав], для получения более подробной информации об этом.

Возвращаемое значение 0 указывает на успешное завершение, а -1 указывает на отказ. В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Ошибки Имени файла]), следующие errno условия ошибки определены для этой функции:

EACCES

право записи отклонено для директории предыдущего уровня, в которую новый каталог должен быть добавлен.

EEXIST

файл, именованный filename уже существует.

EMLINK

директория предыдущего уровня имеет слишком много связей.

Хорошо разработанные файловые системы никогда не сообщают эту ошибку, потому что они разрешают большое количество связей чем ваш диск мог бы содержать. Однако, Вы должны все еще принимать во внимание возможности этой ошибки, поскольку она могла бы следовать из доступа к сети к файловой системе на другой машине.

ENOSPC

файловая система не имеет достаточного участка памяти, чтобы создать новый каталог.

EROFS

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

Чтобы использовать эту функцию, ваша программа должна включить заглавный файл 'sys/stat.h'.

9.8 Атрибуты Файла

Когда Вы применяете 'ls -l' команду оболочки на файле, это дает Вам информацию относительно размера файла, кто его хозяин, когда было последнее изменение, и т.п.. Этот вид информации называется атрибутами файла; они связаны с файлом непосредственно и не часть одного из имен.

Этот раздел содержит информацию относительно того, как Вы можете запрашивать и изменять эти атрибуты файлов.

Что Означают Атрибуты Файла

Когда Вы читаете атрибуты файла, они возвращаются в структуре, называемой struct stat. Этот раздел описывает имена атрибутов, их типов данных, и что они означают.

Заглавный файл 'sys/stat.h' объявляет все символы, определенные в этом разделе.

      struct stat         (тип данных)

Тип структуры stat используется, чтобы возвратить информацию относительно атрибутов файла. Она содержит по крайней мере следующие элементы:

                mode_t st_mode

Определяет режим файла. Включая информацию о типе файла (см. Раздел 9.8.3 [Тестирование Типа Файла]) и биты прав файла (см. Раздел 9.8.5 [Биты Права]).

        ino_t st_ino

Серийный номер файла, который отличает этот файл от всех других файлов на том же самом устройстве.

        dev_t st_dev

Идентифицирует устройство, содержащее файл. St_ino и st_dev, вместе, однозначно идентифицируют файл.

        nlink_t st_nlink

Число жестких связей с файлом. Этот счетчик следит, сколько каталогов имеют входы для этого файла. Если счетчик когда-либо уменьшится до нуля, то файл непосредственно отбрасывается. Символические связи не рассчитываются.

        uid_t st_uid

ID владельца файла. См. Раздел 9.8.4 [Владелец Файла].

        gid_t st_gid

ID группы файла. См. Раздел 9.8.4 [Владелец Файла].

        off_t st_size

Это определяет размер файла в байтах. Для файлов, которые являются устройствами и т.п. это поле не значимо.

        time_t st_atime

Это - время последнего доступа к файлу. См. Раздел 9.8.9 [Времена Файла].

        unsigned long int st_atime_usec

Это - дробная часть времени последнего доступа к файлу.

        time_t st_mtime

Это - время последней модификации содержимого файла.

        unsigned long int st_mtime_usec

Это - дробная часть времени последней модификации содержимого файла.

        time_t st_ctime

Это - время последней модификации атрибутов файла. См. Раздел 9.8.9 [Времена Файла].

        unsigned long int st_ctime_usec

Это - дробная часть времени последней модификации атрибутов файла.

        unsigned int st_nblocks

Это - количество дискового пространства, которое файл занимает, измеряемое в модулях (512-байтовых блоках).

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

Вы можете узнать (приблизительно) является ли файл разреженен, сравнивая это значение с st_size, примерно так:

                        (st.st_blocks * 512 < st.st_size)

Этот тест не совершенен, потому что файл, который только немного разрежен, не мог бы быть обнаружен как разреженный вообще. Для практических приложений, это - не проблема.

                unsigned int st_blksize

Оптимальный размер блока для чтения или записи в этот файл. Вы могли бы использовать этот размер для распределения пространства буфера для чтения или для записи в файл.

Некоторые из атрибутов файла имеют специальные имена типа данных, которые существуют специально для этих атрибутов. (Они ­ все побочные результаты исследования для общеизвестных типов integer, которые Вы знаете и любите.) Эти typedef-имена определены в заглавном файле 'sys/types.h' также как в 'sys/stat.h'. Вот их список:

       mode_t     (тип данных)

Это - тип данных integer, используемый, чтобы представить режимы файла. В системе GNU, это эквивалентно unsigned int.

       ino_t      (тип данных)

Это - арифметический тип данных, используемый, чтобы представить серийные номера файла. (В UNIX жаргоне они иногда называются inode числами.) В системе GNU, этот тип эквивалентен long unsigned int.

       dev_t      (тип данных)

Это - арифметический тип данных, используемый, чтобы представить числа файлового устройства. В системе GNU, это эквивалентно int.

       nlink_t    (тип данных)

Это - арифметический тип данных, используемый, чтобы представить число связей файла. В системе GNU, это эквивалентно short unsigned int.

Чтение Атрибутов Файла

Чтобы исследовать атрибуты файлов, используйте функции stat, fstat и lstat. Они возвращают информацию атрибута в объекте struct stat. Все три функции объявлены в заглавном файле 'sys/stat.h'.

       int stat (const char *filename, struct stat *buf )  (функция)

Stat функция возвращает информацию относительно атрибутов файла, именованного filename в структуре, указанной в buf.

Если filename - имя символической связи, атрибуты, которые Вы получаете, описывают файл, на который ссылка указывает. Если ссылка направляет на несуществующее имя файла, то stat сбоит, сообщая несуществующий файл.

Возвращаемое значение - 0, если операция успешна, и -1 при отказе. В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Ошибки Имени файла], следующие errno условия ошибки определены для этой функции:

ENOENT

файл, именованный filename не существует.

       int fstat (int filedes, struct stat *buf )  (функция)

Fstat функция - подобна stat, за исключением того, что она берет открытый дескриптор файла как аргумент, вместо имени файла. См. Главу 8 [ввод - вывод низкого уровня].

Подобно stat, fstat возвращает 0 при успехе и -1 при отказе. Следующие errno условия ошибки определены для fstat:

EBADF

filedes аргумент - не допустимый дескриптор файла.

       int lstat (const char *filename, struct stat *buf )  (функция)

Lstat функция - подобна stat, за исключением того, что она не следует за символическими связями. Если filename - имя символической связи, lstat возвращает информацию относительно связи непосредственно; иначе, lstat работает подобно stat. См. Раздел 9.4 [Символические Связи].

Определение Типа Файла

Режим файла, сохраненный в st_mode поле атрибутов файла, содержит два вида информации: код типа файла, и биты прав доступа. Этот раздел обсуждает только код типа, который Вы можете использовать, чтобы сообщить является ли файл каталогом, является ли он гнездом, и так далее. Для уточнения информации относительно права доступа см. Раздел 9.8.5 [Биты Прав].

Имеются два предопределенных способа, которыми Вы можете обращаться к типу файла. Прежде всего для каждого типа файла, имеется макрокоманда предиката, которая исследует значение режима файла и возвращает истину или ложь если файл того типа, или нет. Во-вторых, Вы можете маскировать снаружи остальную часть режима файла, чтобы получить только код типа файла. Вы можете сравнивать его c различными константами типов файлов.

Все символы, перечисленные в этом разделе определены в заглавном файле 'sys/stat.h'.

Следующие макрокоманды предиката проверяют тип файла, заданный значением m, которое является st_mode полем, возвращенным stat на этом файле:

       int S_ISDIR (mode_t m)  (макрос)

Эта макрокоманда возвращает отличное от нуля значение, если файл - каталог.

       int S_ISCHR (mode_t m)  (макрос)

Эта макрокоманда возвращает отличное от нуля значение, если файл - символьный специальный файл (устройство подобное терминалу).

      int S_ISBLK (mode_t m)  (макрос)

Эта макрокоманда возвращает отличное от нуля значение, если файл - блокированный специальный файл (устройство подобное диску).

       int S_ISREG (mode_t m)  (макрос)

Эта макрокоманда возвращает отличное от нуля значение, если файл - регулярный(правильный) файл.

       int S_ISFIFO (mode_t m)  (макрос)

Эта макрокоманда возвращает отличное от нуля значение, если файл - FIFO специальный файл, или канал. См. Главу 10 [Каналы и FIFO].

       int S_ISLNK (mode_t m)  (макрос)

Эта макрокоманда возвращает отличное от нуля значение, если файл - символическая ссылка. См. Раздел 9.4 [Символические Связи].

       int S_ISSOCK (mode_t m)  (макрос)

Эта макрокоманда возвращает отличное от нуля значение, если файл - гнездо. См. Главу 11 [Гнезда].

Альтернативный, не-POSIX метод тестирования типа файла обеспечивается для совместимости с BSD.

Режим можно поразрядно AND с S_IFMT, чтобы извлечь лод типа файл, и сравнить с соответствующей константой кода типа. Например,

                         S_ISCHR (mode)

эквивалентно:

                ((mode & S_IFMT) == S_IFCHR)
       int S_IFMT  (макрос)

Это - битовая маска, используемая, чтобы извлечь код типа файла.

Вот символические имена для различных типов файлов:

       S_IFDIR Эта макрокоманда представляет значение кода типа файла для файла каталога.
       S_IFCHR Эта макрокоманда представляет значение кода типа файла для файла - устройства с символьной организацией.
       S_IFBLK Эта макрокоманда представляет значение кода типа файла для блочно-ориентированного файла.
       S_IFREG Эта макрокоманда представляет значение кода типа файла для регулярного(правильного) файла.
       S_IFLNK Эта макрокоманда представляет значение кода типа файла для символической связи.
       S_IFSOCK Эта макрокоманда представляет значение кода типа файла для гнезда.
       S_IFIFO Эта макрокоманда представляет значение кода типа файла для FIFO или канала.

Владелец Файла

Каждый файл имеет владельца, который является одним из зарегистрированных имен пользователей, определенных в системе. Каждый файл также имеет группу, которая является одной из определенных групп. Владелец файла может часто быть полезен, но его основная цель - управление доступом.

Владелец файла и группа играет роль в определении доступа, потому что файл имеет набор битов права доступа для пользователя, который является владельцем, другой набор, для тех, кто принадлежат группе владельца файла, и третий набор битов, которые относятся ко всем остальным. См. Раздел 9.8.6 [Право Доступа]

Когда файл создан, его владелец определяется из пользовательского ID процесса, который создает его.

Вы можете изменять владельца и/или группу владельца существующего файла, используя chown функцию. Это - примитив для chown и chgrp команд оболочки.

Прототип для этой функции объявлен в 'unistd.h'.

       int chown (const char *filename, uid_t owner, gid_t group) (функция)

Chown функция изменяет владельца filename файла, и группу.

Изменение владельца файла на некоторых системах очищает ID­ пользователя и ID-группы биты прав файла. (Потому что эти биты не могут соответствовать новому владельцу.) другие биты прав файла не изменяются.

Возвращаемое значение - 0 при успехе и -1 при отказе. В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Ошибки Имени файла]), следующие errno условия ошибки определены для этой функции:

EPERM

Этот процесс испытывает недостаток прав, чтобы делать запрошенное изменение.

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

См. Раздел 27.7 [Опции для Файлов], для уточнения информации относительно _POSIX_CHOWN_RESTRICTED макрокоманды.

EROFS

Файл находится в файловой системе только для чтения.

       int fchown (int filedes, int owner, int group)  (функция)

Подобна chown, за исключением того, что она изменяет владельца файла на описателе файла filedes.

Возвращаемое значение из fchown - 0 при успехе и -1 при отказе. Следующие errno коды ошибки определены для этой функции:

EBADF

filedes аргумент - не допустимый дескриптор файла.

EINVAL

filedes аргумент соответствует каналу или гнезду, а не обычному файлу.

EPERM

Этот процесс испытывает недостаток прав, чтобы делать запрошенное изменение. Для подробностей, см. chmod, выше.

EROFS

Файл постоянно находится в файловой системе только для чтения.

Биты Режима для Прав Доступа

Этот раздел обсуждает биты права доступа, которые управляют чтением и записью в файл. См. Раздел 9.8.3 [Проверка Типа Файла], для уточнения информации относительно кода типа файла.

Все символы, перечисленные в этом разделе определены в файле 'sys/stat.h'.

Эти символические константы определены для битов режима файла, которые управляют правом доступа для файла:

S_IRUSR

S_IREAD

бит права чтения для владельца файла. На многих системах, этот бит - 0400. S_IREAD - устаревший синоним, предусмотрен для BSD совместимости.

S_IWUSR

S_IWRITE

бит права записи для владельца файла. Обычно 0200. S_IWRITE - устаревший синоним, предусмотрен для совместимости с BSD.

S_IXUSR

S_IEXEC

бит права записи для владельца файла. Обычно 0100. S_IWRITE - устаревший синоним, предусмотрен для совместимости cBSD.

S_IRWXU

Это эквивалент ' (S_IRUSR | S_IWUSR | S_IXUSR) '.

S_IRGRP

бит права чтения для владельца группы файла. Обычно 040.

S_IWGRP

бит права записи для владельца группы файла. Обычно 020.

S_IXGRP

бит права выполнения или поиска для владельца группы файла. Обычно 010.

S_IRWXG

Это эквивалент ' (S_IRGRP | S_IWGRP | S_IXGRP) '.

S_IROTH

бит права чтения для других пользователей. Обычно 04.

S_IWOTH

бит права записи для других пользователей. Обычно 02.

S_IXOTH

бит права выполнения или поиска для других пользователей. Обычно 01.

S_IRWXO

Это эквивалент ' (S_IROTH | S_IWOTH | S_IXOTH) '.

S_ISUID

бит выполнения, устанавливающий ID-пользователя, обычно 04000.

S_ISGID

бит выполнения, устанавливающий ID-группы, обычно 02000.

S_ISVTX

Это - бит, обычно 01000.

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

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

Фактические битовые значения символов перечислены в таблице выше, так что Вы можете декодировать значения режима файла при отладке ваших программ. Эти битовые значения правильны для большинства систем, но это не гарантируется.

Предупреждение: Запись явных чисел для прав файла - плохая практика. Это не только непереносимо, но также требует от каждого, кто читает вашу программу, помнить то, что означают конкретные биты. Чтобы сделать вашу программу понятной, используйте символические имена.

Как Разрешается Доступ к Файлу

Операционная система обычно разрешает право доступа к файлу, основываясь на ID пользователя и группы процесса, и на дополнительном ID группы, вместе с битами владельца, группы и битами права файла. Эти понятия обсуждены подробно в Разделе 25.2 [Свойства процесса].

Если ID пользователя процесса соответствует ID владельца файла, то права для чтения, записи, и выполнения/поиска, управляются соответствующими 'пользовательскими' (или 'владельца') битами. Аналогично, если любой из ID группы или дополнительной группы процесса соответствует ID группы владельца файла, то права, управляется битами 'группы'. Иначе, права управляются 'другими' битами.

Привилегированные пользователи, подобно 'root', могут обращаться к любому файлу, независимо от битов права файла. Как частный случай, для выполнения файла даже для привилегированного пользователя, по крайней мере один из битов выполнения должен быть установлен.

Назначение Прав Файла

Примитивные функции для создания файлов (например, open или mkdir) воспринимают аргумент mode, который определяет права файла. Но заданный mode изменяется маской создания файла процесса, или перед использованием.

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

Программы которые создают файлы, обычно определяют аргумент mode, который включает все права, которые имеют смысл для специфического файла. Для обычного файла, это обычно право чтения и право записи для всех классов пользователей. Эти права ограничены как определено собственной маской создания файла индивидуального пользователя.

Чтобы изменять право существующего данного файла, вызовите chmod. Эта функция игнорирует маску создания файла; она использует только заданные биты права.

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

Когда ваша программа должна создать файл и обходить umask для прав доступа, самый простой способ сделать это состоит в том, чтобы использовать fchmod после открытия файла, а не изменять umask.

Фактически, изменение umask обычно делается только оболочками. Они используют umask функцию.

Функции в этом разделе объявлены в 'sys/stat.h'. mode_t umask (mode_t mask) (функция) Umask функция устанавливает маску создания файла текущего процесса, и возвращает предыдущее значение маски создания файла.

Вот пример, показывающий, как читать маску с umask без ее замены:

                mode_t
                read_umask (void)
                {
                        mask = umask (0);
                        umask (mask);
                }

       mode_t getumask (void)  (функция)

Возвращает текущее значение маски создания файла для текущего процесса. Эта функция - расширение GNU.

       int chmod (const char *filename, mode_t mode)  (функция)

Chmod функция устанавливает биты права доступа для файла, именованного filename как mode.

Если filename называет символическую ссылка, chmod изменяет право файла, указанного ссылкаю, а не связи непосредственно. Не имеется фактически никакого способа установить mode связи, который всегда ­ 1.

Эта функция возвращает 0 в случае успеха и -1 если нет. В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Ошибки Имени файла]), следующие errno условия ошибки определены для этой функции:

ENOENT

Именованный файл не существует.

EPERM

Этот процесс не имеет права изменить право доступа этого файла. Только владелец файла или привилегированный пользователь может изменять их.

EROFS

Файл постоянно находится в файловой системе только для чтения.

       int fchmod (int filedes, int mode)  (функция)

Подобна chmod, за исключением того, что она изменяет права файла в настоящее время открытого через дескриптор filedes.

Возвращаемое значение из fchmod - 0 при успехе и -1 при отказе. Следующие errno коды ошибки определены для этой функции:

EBADF

filedes аргумент - не допустимый дескриптор файла.

EINVAL

filedes аргумент соответствует каналу или гнезду, или кое-чему еще, которое не имеет права доступа.

EPERM

Этот процесс не имеет права изменить право доступа этого файла. Только владелец файла или привилегированный пользователь может изменять их.

EROFS

Файл постоянно находится в файловой системе только для чтения.

Тестирование Прав для Обращения к Файлу

Когда программа выполняется привилегированным пользователем, это разрешает ей обращаться к файлам без ограничений на пример, изменять ' /etc/passwd '. Программы разработанные, чтобы быть выполненным обычными пользователями, но обращаться к таким файлам используют setuid так, чтобы они всегда выполнялись под root'ом.

Программа должна явно проверить, имел ли пользователь необходимый доступ к файлу, прежде, чем она начнет читать или писать в файл.

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

Имеется другой способ, которым Вы могли бы проверить доступ, который является простым для описания, но очень интенсивно используемым. Нужно исследовать биты mode файла и подражать вычислению доступа системы. Этот метод нежелателен, потому что много систем имеют дополнительные возможности управления доступом; ваша программа не может корректно подражать им. Использование access просто и автоматически делает то, что соответствует системе, которую Вы используете.

Символы в этом разделе объявлены в ' unistd.h '.

       int access (const char *filename, int how)  (функция)

Функция выясняет, можно ли к файлу, именованному filename обращаться способом, заданным аргументом how. Этот аргумент может быть либо поразрядным ИЛИ флагов R_OK, W_OK, X_OK, либо проверка существования F_OK.

Эта функция использует ID пользователя и группы процесса, а не эффективный ID, для проверки права доступа. В результате, если Вы используете функцию из программы setuid или setgid (см. Раздел 25.4 [Как Изменить Права]), это дает информацию относительно пользователя, кто фактически выполнил программу.

Возвращаемое значение - 0, если доступ разрешается, и -1 иначе. (Другими словами, обрабатываемая как функция предиката, access возвращает истину, если запрошенный доступ отклонен.)

В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Ошибки Имени файла]), следующие errno условия ошибки определены для этой функции:

EACCES

доступ, заданный how, отклонен.

ENOENT

файл не существует.

EROFS

Право записи было запрошено для файла в файловой системе только для чтения.

Эти макрокоманды определены в заглавном файле 'unistd.h' для использования как аргументы функции access. Значения ­ константы integer.

       int R_OK  (макрос)

Аргумент проверки на право чтения.

       int W_OK  (макрос)

Аргумент проверки на право записи.

       int X_OK  (макрос)

Аргумент проверки направо выполнения/поиска.

       int F_OK Macro

Аргумент проверки на существование файла.

Временные Характеристики Файла

Каждый файл имеет три временных метки, связанные с ним: время доступа, время изменения, и время изменения атрибута. Они соответствуют st_atime, st_mtime, и st_ctime элементам структуры stat; см. Раздел 9.8 [Атрибуты Файла].

Все эти времена представляются в календарном формате времени, как объекты time_t. Этот тип данных определен в 'time.h'. Для получения более подробной информации и манипулирования значениями времени см. Раздел 17.2 [Календарное Время].

Когда существующий файл открыт, его атрибуты, такие как время изменения, модифицируются. Чтение из файла модифицирует атрибут времени доступа, а запись модифицирует время изменения.

Когда файл создан, все три временных метки для этого файла установлены на текущее время. Кроме того, атрибут времени изменения каталога, который содержит новый вход, модифицируются.

Добавление нового имени для файла с функцией связи модифицирует атрибут поля времени изменения связываемого файла, и соответствующие атрибуты каталога, содержащего новое имя. Те же самые поля изменяются если имя файла удалено с unlink, remove, или rmdir. Переименование файла с rename воздействует только на атрибут времени изменения и изменения поля времени двух родительских включающих каталогов, а не на времена переименовываемого файла.

Изменение атрибутов файла (например, с chmod) модифицирует атрибут времени изменения.

Вы можете также изменять некоторые из временных меток файла, явно используются utime, за исключением изменения атрибута времени изменения. Вы должны включить заглавный файл 'utime.h' чтобы использовать это средство.

       struct utimbuf  (тип данных)

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

                time_t actime

Это - время доступа(последнего) к файлу.

                time_t modtime

Это - время изменения файла.

       int utime (const char *filename, const struct utimbuf *times) (функция)

Эта функция используется, чтобы изменить файловые времена, связанные с файлом, именованным filename.

Если times являются пустым указателем, то время доступа и изменения файла устанавливаются на текущее время. Иначе, они устанавливаются как значения из actime и modtime элементов (соответственно) структуры utimbuf, указанной times. Utime функция возвращает 0 если обращение успешно и -1 при отказе. В дополнение к обычным синтаксическим ошибкам имени файла, следующие errno условия ошибки определены для этой функции:

EACCES

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

ENOENT

файл не существует.

EPERM

Если аргумент times - не пустой указатель, Вы должны также быть владельцем файла или привилегированным пользователем.

EROFS

Файл живет в файловой системе только для чтения.

Каждая из трех временных меток имеет соответствующую часть измеряющую микросекунды, которая расширяет разрешающую способность. Эти поля называются st_atime_usec, st_mtime_usec, и st_ctime_usec; каждое имеет значение от 0 до 999,999, которое указывает время в микросекундах. Они соответствуют tv_usec полю структуры timeval; см. Раздел 17.2.2 [Календарь с высоким разрешением].

Utimes функция - подобна utime, но также позволяет Вам определять дробную часть времен файла.

Прототип для этой функции находится в файле 'sys/time.h'.

       int utimes (const char *filename, struct timeval tvp[2]) (функция)

Эта функция устанавливает доступ к файлу и времена изменения для файла, именованного filename.

Новый время доступа определено tvp [0], а новое время изменения tvp [1]. Эта функция происходит из BSD.

Возвращаемые значения и условия ошибки - такие же как для utime функции.

9.9 Создание Специальных Файлов

Mknod функция - примитив для создания специальных файлов, типа файлов, которые соответствуют устройствам. Библиотека GNU включает эту функцию для совместимости с BSD.

Прототип для mknod объявлен в 'sys/stat.h'.

       int mknod (const char *filename, int mode, int dev)  (функция)

Mknod функция создает специальный файл с именем filename. Mode определяет режим файла, и может включать различные специальные биты файла, типа S_IFCHR (для символьного специального файла) или S_IFBLK (для блокированного специального файла). См. Раздел 9.8.3 [Тестирование Типа Файла].

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

Возвращаемое значение - 0 при успехе и -1 при ошибке. В дополнение к обычным синтаксическим ошибкам имени файла (см. Раздел 6.2.3 [Ошибки Имени Файла]), следующие errno условия ошибки определены для этой функции:

EPERM

Процесс вызова не привилегирован. Только суперпользователь может создавать специальные файлы.

ENOSPC

каталог или файловая система, которая содержала бы, новый файл 'полна' и не может быть расширена.

EROFS

Каталог, содержащий новый файл не может изменяться, потому что он находится в файловой системе только для чтения.

EEXIST

уже имеется файл, именованный filename. Если Вы хотите заменить этот файл, Вы должны сначала удалить старый файл.

9.10 Временные Файлы

Если Вы должны использовать временный файл в вашей программе, Вы можете использовать tmpfile функцию для его открытия. Или Вы можете использовать tmpnam функцию, что бы сделать имя для временного файла и тогда открыть его обычным способом через fopen. Tempnam функция - подобна tmpnam, но допускает выбирать в какой каталог войдут временные файлы, и кое-что относительно того, на что их имена будут походить.

Эти средства объявлены в заглавном файле 'stdio.h'.

       FILE * tmpfile (void)  (функция)

Эта функция создает временный двоичный файл для режима модификации, как будто, вызывая fopen с режимом ' wb + '. Файл удаляется автоматически, когда он закрыт или когда программа завершается. (На некоторых других системах ANSI C файл может не быть удаленным, если программа завершается неправильно).

       char * tmpnam (char *result)  (функция)

Эта функция создает и возвращает имя файла, которое является допустимым именем файла, и не называет никакой существующий файл. Если аргумент result является пустым указателем, возвращаемое значение - указатель на внутреннюю статическую строку, которая могла бы изменяться последующими обращениями. Иначе, аргумент result должен быть указателем на массив по крайней мере из L_tmpnam символов, и результат будет написан в этот массив.

Возможно tmpnam будет терпеть неудачу, если Вы вызываете ее слишком много раз. Потому что фиксированная длина временного имени файла дает участок памяти для только конечного числа различных имен. Если tmpnam сбоит, она возвращает пустой указатель.

       int L_tmpnam  (макрос)

Значение этой макрокоманды - константное выражение integer, которое представляет минимальный размер резервирования строки, достаточно большой, чтобы содержать имя файла, сгенерированное tmpnam функцией.

       int TMP_MAX  (макрос)

Макрокоманда TMP_MAX - нижняя граница для того, сколько временных имен Вы можете создавать с tmpnam. Вы можете полагаться на способность вызвать tmpnam по крайней мере столько раз прежде, чем она будет терпеть неудачу, говоря что Вы сделали слишком много временных имен файла.

С библиотекой GNU, Вы можете создавать очень большое количество временных имен файла, если Вы фактически создаете файлы, возможно дисковое пространство закончится прежде, чем закончатся имена. Некоторые другие системы имеют фиксированное, малое ограничение числа временных файлов. Ограничение никогда не меньше чем 25. char * tempnam (const char *dir, const char *prefix) (функция)

Эта функция генерирует уникальное временное имя файла. Если prefix не пустой указатель, то до пяти символов этой строки используется как prefix для имени файла. Prefix каталога для временного имени файла определяется проверкой следующей последовательности. Каталог должен существовать и быть перезаписываемым.

  • Переменная среды TMPDIR, если она определена.
  • Аргумент dir, если это - не пустой указатель.
  • Значение P_tmpdir макрокоманды.
  • Каталог ' /tmp '.

Эта функция определена для SVID совместимости.

       char * P_tmpdir (SVID макрос)

Эта макрокоманда - имя заданного по умолчанию каталога для временных файлов.

Более старые системы UNIX не имели таких функций. Взамен они использовали mktemp и mkstemp. Обе из этих функций работают изменяя строку шаблона имени файла, заданную Вами. Последние шесть символов этой строки должны быть ' XXXXXX '. Эти шесть X-ов заменятся на шесть символов, которые делают целую строку уникальным именем файла. Обычно строка шаблона - это что-нибудь вроде ' /tmp/prefixXXXXXX ', и каждая программа использует уникальный prefix.

Обратите внимание: Т. к. mktemp и mkstemp изменяют строку шаблона, Вы не должны передать строковые константы им. Строковые константы - обычно в памяти только для чтения, так что ваша программа разрушилась бы, если бы mktemp или mkstemp пробовали изменять строку.

       char * mktemp (char *template)  (функция)

Mktemp функция генерирует уникальное имя файла, изменяя шаблон как описано выше. В случае успеха она возвращает модифицированный шаблон. Если mktemp не находит уникальное имя файла, она делает шаблон пустой строкой и возвращает ее. Если шаблон не заканчивается на ' XXXXXX ', mktemp возвращает пустой указатель.

       int mkstemp (char *template)  (функция)

Mkstemp функция генерирует уникальное имя файла, точно так же как mktemp, но она также открывает файл для Вас через open (см. Раздел 8.1 [Открытие и Закрытие Файлов]). В случае успеха, она изменяет шаблон на месте и возвращает дескриптор файла открытый на этом файле для чтения и записи. Если mkstemp не может создать однозначно названный файл, она делает шаблон пустой строкой и возвращает -1. если шаблон не заканчивается на ' XXXXXX ', mkstemp возвращает -1 и не изменяет шаблон.

В отличие от mktemp, mkstemp, как гарантируют, создаст уникальный файл, который не может сталкиваться с любой другой программой, пробующей создать временный файл. Потому что она работает, вызывая open с O_EXCL битом флага, который говорит, что Вы хотите всегда создавать новый файл, и получать ошибку, если файл уже существует

10. Каналы и FIFO

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

FIFO специальный файл является подобным каналу, но вместо анонимного, временного соединения, FIFO имеет имя или имена подобно любому другому файлу. Процесс открывает FIFO по имени, чтобы связаться через него.

Канал или FIFO должен быть открыт с обоих концов одновременно. Если Вы читаете из канала или файла FIFO, в который никто ничего не пишет (возможно потому что, они все закрыли файл, или вышли), то чтение возвращает конец файла. Запись в канал или FIFO, который не имеет процесс считывания, обрабатывается как условие ошибки; это генерирует сигнал SIGPIPE, и сбои с кодом ошибки EPIPE, если сигнал обработан или блокируется.

Ни каналы ни FIFO специальные файлы не позволяют позиционирование файла. И чтение и запись происходит последовательно; чтение из начала файла и запись в конец.

10.1 Создание Канала

Примитив для создания канала - функция pipe. Она создает оба, и чтения и записи концы канала. Это не очень полезно для одиночного процесса, использовать канал, чтобы разговаривать с собой. В типичном использовании, процесс создает канал только прежде, чем он ветвится на один или более дочерних процессов (см. Раздел 23.4 [Создание Процесса]). Канал используется для связи или между родителем или дочерними процессами, или между двумя процессами братьями.

Функция pipe объявлена в заглавном файле 'unistd.h'.

       int pipe (int filedes[2])  (функция)

Функция pipe создает канал и помещает дескрипторы файла для чтения и записи (соответственно) в filedes [0] и filedes [1].

При успехе pipe возвращает значение 0. При отказе, -1. Следующие errno условия ошибки определены для этой функции:

EMFILE

процесс имеет слишком много файлов открытыми.

ENFILE

имеются слишком много открытых файлов во всей системе. См. Раздел 2.2 [Коды Ошибки], для получения более подробной информации о ENFILE.

Вот пример простой программы, которая создает канал. Эта программа использует функцию ветвления (см. Раздел 23.4 [Создание Процесса]) чтобы создать дочерний процесс. Родительский процесс напишет данные, которые читается дочерним процессом.

                #include 
                #include 
                #include 
                #include 
                void
                read_from_pipe (int file)
                {
                        FILE *stream;
                        int c;
                        stream = fdopen (file, 'r');
                        while ((c = fgetc (stream)) != EOF)
                                putchar (c);
                        fclose (stream);
                }
        /* Пишем некоторый произвольный текст в канал. */
                void
                write_to_pipe (int file)
                {
                        FILE *stream;
                        stream = fdopen (file, 'w');
                        fprintf (stream, 'hello, world!\n');
                        fprintf (stream, 'goodbye, world!\n');
                        fclose (stream);
                }
                int
                main (void)
                {
                        pid_t pid;
                        int mypipe[2];
                        /* Create the pipe. */
                        if (pipe (mypipe))
                                {
                                        fprintf(stderr,'Pipe failed.\n');
                                        return EXIT_FAILURE;
                                }
                        /* Создаем дочерний процесс. */
                        pid = fork ();
                        if (pid == (pid_t) 0)
                                {
                                        /* Это - дочерний процесс. */
                                        read_from_pipe (mypipe[0]);
                                        return EXIT_SUCCESS;
                                }
                        else if (pid < (pid_t) 0)
                                {
                                        /* The fork failed. */
                                        fprintf(stderr,'Fork failed.\n');
                                        return EXIT_FAILURE;
                                }
                        else
                                {
                                        /* Это - родительский процесс. */
                                        write_to_pipe (mypipe[1]);
                                        return EXIT_SUCCESS;
                                }
                }

10.2 Канал к Подпроцессу

Общее использование каналов должно послать данные к или получать данные из программы, выполняемой как подпроцесс.

Один из способов выполнения этого - использовать комбинацию pipe (чтобы создать канал), fork (чтобы создать подпроцесс), dup2 (чтобы вынудить подпроцесс использовать pipe как стандартный ввод или канал вывода), и exec (чтобы выполнить новую программу). Или, Вы можете использовать popen и pclose.

Преимущество использования popen и pclose - в том, что интерфейс является намного более простым и более удобным для использования. Но они не предлагают так много гибкости, как использование функций низкого уровня непосредственно.

       FILE * popen (const char *command, const char *mode)  (функция)

Popen функция близко связана с функцией системы; см. Раздел 23.1 [Выполнение Команд]. Она выполняет команду оболочки как подпроцесс.

Однако, вместо того, чтобы ждать завершения команды, она создает канал к подпроцессу и возвращает поток, который соответствует этому каналу.

Если Вы определяете аргумент режима 'r', Вы можете читать из потока, чтобы отыскать данные из канала стандартного вывода подпроцесса. Подпроцесс наследует канал стандартного ввода из родительского процесса.

Аналогично, если Вы определяете аргумент режима 'w', Вы можете писать в поток, чтобы посылать данные на канал стандартного ввода подпроцесса. Подпроцесс наследует канал стандартного вывода из родительского процесса.

В случае ошибки, popen возвращает пустой указатель. Это может случаться, если канал или поток не может быть создан, если подпроцесс не может быть раздвоен, или если программа не может быть выполнена.

       int pclose (FILE *stream)  (функция)

Pclose функция используется, чтобы закрыть поток, созданный popen. Она ждет завершения дочернего процесса, и возвращает значение состояния, что касается функции системы.

Вот пример, показывающий, как использовать popen и pclose, чтобы фильтровать вывод через другую программу.

                #include 
                #include 
                void
                write_data (FILE * stream)
                {
                        int i;
                        for (i = 0; i < 100; i++)
                                fprintf (stream, '%d\n', i);
                        if (ferror (stream))
                                {
                                        fprintf (stderr, 'Output to
                                        stream failed.\n');
                                        exit (EXIT_FAILURE);
                                }
                }
                int
                main (void)
                {
                        FILE *output;
                        output = popen ('more', 'w');
                        if (!output)
                                {
                                        fprintf(stderr,'Could not run
                                                        more.\n');
                                        return EXIT_FAILURE;
                                }
                        write_data (output);
                        pclose (output);
                        return EXIT_SUCCESS;
                }

10.3 FIFO Специальные Файлы

FIFO специальный файл подобен каналу, за исключением того, что он создан различным способом. Вместо анонимного канала связи, FIFO специальный файл введен в файловую систему, вызовом mkfifo.

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

Mkfifo функция объявлена в заглавном файле 'sys/stat.h'.

       int mkfifo (const char *filename, mode_t mode)  (функция)

Mkfifo функция делает FIFO специальный файл с именем filename. Аргумент mode используется, чтобы установить права файла; см. Раздел 9.8.7 [Установка Прав].

Нормальное, успешное возвращаемое значение из mkfifo - 0. В случае ошибки возвращается -1. В дополнение к обычным синтаксическим ошибкам имени файла следующие errno условия ошибки определены для этой функции:

EEXIST

именованный файл уже существует.

ENOSPC

каталог или файловая система не может быть расширен.

EROFS

каталог, который содержал бы файл постоянно находится в файловой системе только для чтения.

10.4 Быстрота ввода-вывода Канала

Чтение или запись данных в канал мгновенны, если размер данных меньше чем PIPE_BUF. Это означает что передача данных кажется мгновенной, в этом случае ничто в системе не может наблюдать состояние, в котором он является частично полным. Быстрый ввод - вывод не может начинаться сразу же (может требоваться ждать пространство буфера или для данных), но если только он начинается, то он заканчивается немедленно.

Чтение или запись большего количества данных может не быть быстрым; например, выходные данные из других процессов, совместно использующих дескриптор могут быть разбиты на части.

См. Раздел 27.6 [Ограничения для Файлов], для уточнения информации относительно параметра PIPE_BUF.

11. Гнезда

Эта глава описывает средства GNU для межпроцессорной связи, используя гнезда.

Гнездо - обобщенный межпроцессорный канал связи. Подобно каналу, гнездо представляется как дескриптор файла. Но, в отличие от каналов, гнезда поддерживает ссылка между несвязанными процессами, и даже между процессами, выполняющимися на различных машинах, которые связываются по сети. Гнезда - первичный способ связи с другими машинами; telnet, rlogin, ftp, переговоры, и другие сетевые программы используют гнезда.

Не все операционные системы поддерживают гнезда. В библиотеке GNU, заглавный файл 'sys/socket.h' существует независимо от операционной системы, и функции гнезд всегда существуют, но если система действительно не поддерживает гнезда, эти функции всегда терпят неудачу.

Незавершенность: Мы в настоящее время не описали средства для передачи сообщений или для конфигурирования интерфейса Internet.

11.1 Понятие Гнезда

Когда Вы создаете гнездо, Вы должны определить стиль связи, который Вы хотите использовать и тип протокола, который должен поддерживать ее. Стиль связи гнезда определяет семантику пользовательского уровня посылки и получения данных через гнезда. Выбор стиля связи определяет ответы на вопросы типа:

  • Каковы модули передачи данных? Некоторые стили связи расценивают данные как последовательность байтов, без большей структуры; другие группируют байты в записи (которые известны в этом контексте как пакеты).
  • Могут данные быть потеряны в течение нормальной операции? Некоторые стили связи гарантируют, что все посланные данные прибывают в порядке, как они были посланы (страховка системы или сетевых сбоев); другие стили иногда теряют данные как нормальная часть операции, и могут иногда поставлять пакеты больше чем один раз или в неправильном порядке.
  • Является ли ссылка полностью с одним партнером? Некоторые стили связи - подобно телефонному звонку, Вы делаете соединение с одним отдаленным гнездом, и тогда свободно обмениваетесь данными. Другие стили - подобно отправке по почте символов, Вы определяете адрес адресата для каждого сообщения, которое Вы посылаете.

Вы должны также выбрать именное пространство для наименования гнезда. Имя гнезда ('адрес') значимо только в контексте частного namespace. Фактически, даже тип данных, используемый для имени гнезда может зависеть от именного. Именные пространства также называются 'областями', но мы избегаем этого слова, поскольку оно может быть спутано с другим использованием того же самого термина. Каждое именное пространство имеет символическое имя, которое начинается с 'PF_'. Соответствующее символическое имя, начинающееся с 'AF_' обозначает формат адреса для этого namespace.

В заключение Вы должны выбрать протокол, чтобы установить связь. Протокол определяет какой механизм низкого уровня используется, чтобы передавать и получить данные. Каждый протокол допустим для определенного именного пространства и стиля связи; именное пространство иногда называется совокупностью протоколов из-за этого имена именных пространств начинаются с 'PF_'.

Правила протокола относятся к данным, передающимся между двумя программами, возможно на различных компьютерах; большинство этих правил обработано операционной системой, и Вы не нужно знать о них. Вот что Вы должны знать относительно протоколов:

  • Чтобы иметь связь между двумя гнездами, они должны определить тот же самый протокол.
  • Каждый протокол значим со специфическим стилем/именным пространством и не может использоваться с несоответствующими комбинациями. Например, TCP протокол удовлетворяет только стилю связи потока байтов и именному пространству Internet.
  • Для каждой комбинации стиля и именного пространства, имеется заданный по умолчанию протокол, который Вы можете запрашивать, определяя 0 как номер протокола. И это - то, что Вы должны обычно делать - использовать значение по умолчанию.

11.2 Стили Связи

Библиотека GNU поддерживает различные виды сокетов. В этом разделе описываются различные типы сокетов предоставляемые в бибилотекой GNU. Упомянутые в данном разделе символические константы определены в "sys/socket.h".

    int SOCK_STREAM (макрос)

Тип сокета SOCK_STREAM предназначен для передачи потоков байтов. Такой стиль передачи схож со стилем передачи данных через каналы (pipe) (см. Главу 10 [Трубопроводы и FIFO]). Он используется для передачи данных с отдаленным соединением. При таком стиле передачи данных обеспечивается высокая надежность.

Более подробное описание работы с использованием этого типа Вы найдете в Разделе 11.8 [Соединения].

    int SOCK_DGRAM (макрос)

Тип сокета SOCK_DGRAM используется для посылки индивидуально адресованных пакетов (ненадежен). Этот тип принципиально отличается от типа SOCK_STREAM.

Каждый раз когда Вы пишите данные в сокет этого типа, данные становятся одним пакетом. Вы должны определить адрес получателя для каждого пакета.

Единственной гарантией, которую предоставляет Вам система относительно запросов передачи данных, является то, что она пробует наилучшим образом посылать каждый пакет. У нее может получится посылка шестого пакета после неудачи с четвертыми и пятыми пакетами; седьмой пакет может прибыть перед шестым, также второй может прибыть как раз после шестого.

Типичное использование типа SOCK_DGRAM в ситуациях, где есть возможность повторной посылки пакетов, если ответ не был получен в приемлемое время.

    int SOCK_RAW  (макрос)

Этот тип обеспечивает доступ к сетевым протоколам и интерфейсам низкого уровня. Обычные пользовательские программы, обычно не имеют потребности использовать этот стиль.

11.3 Адреса сокетов

Имя сокета обычно называется адресом. Функции и символы для имеющихся адресов сокетов могли называться как с использованием термина, так и использованием термина "адрес". Вы можете расценивать эти термины как синонимичные в контексте обсуждения сокетов.

Сокет, созданный при помощи функции socket, не имеет никакого адреса. Другие процессы могут использовать его для связи только после того как Вы дадите ему адрес. Мы называем это - связывание адреса с сокетом. Связывание происходит при помощи функции bind.

В первый раз, когда Вы посылаете данные из сокета, или используете его, чтобы инициализировать соединение, система назначает адрес автоматически.

Подробности адресации сокетов изменяются, в зависимости от именного пространства, которое Вы используете. См. Раздел 11.4 [Именное пространство Файла], или Раздел 11.5 [Именное пространство Internet].

Независимо от именного пространства, Вы используете те же самые функции bind и getsockname, чтобы установить и исследовать адрес гнезда.

Форматы Адреса

Функции bind и getsockname используют обобщенный тип данных struct sockaddr *, чтобы представить указатель на адрес гнезда. Вы не можете использовать этот тип данных действительно, чтобы интерпретировать адрес или создавать его; для этого, Вы должны использовать соответствующий тип данных для именного пространства гнезда.

Таким образом, обычная нужно создать адрес в соответствующем именном пространстве специфического типа, и приводить указатель на struct sockaddr *, когда Вы вызываете bind или getsockname.

Единственная информация, которую Вы можете получить из структуры sockaddr - указатель формата адреса, который сообщает Вам какой тип данных использовать, чтобы понять адрес полностью.

Символы в этом разделе определены в заголовочном файле "sys/socket.h".

    struct sockaddr  (тип данных)

Тип структуры sockaddr непосредственно имеет следующие поля:

      short int sa_family

Это код для формата адреса. Он идентифицирует формат данных.

    сhar sa_data [14]

Это фактические данные адреса сокета, которые являются формато-зависимыми. Длина также зависит от формата, и может быть больше чем 14. Длина 14 из sa_data по существу произвольна.

Каждый формат адреса имеет символическое имя, которое начинается с "AF_ ". Каждый из них соответствует "PF_ " символу, который обозначает соответствующее именное пространство. Вот список названий форматов адресов:

AF_FILE

Обозначает формат адреса, который идет с именным пространством файла. (PF_FILE - имя этого именного пространства.) См. Раздел 11.4.2 [Подробности Именного пространства Файла], для уточнения информации относительно этого формата адреса.

AF_UNIX

Это синоним AF_FILE, для совместимости. (PF_UNIX ­ аналогично синоним для PF_FILE.)

AF_INET

Обозначает формат адреса, который идет в именном пространстве Internet. (PF_INET - имя этого именного пространства.) См. Раздел 11.5.1 [Формат Адреса Internet].

AF_UNSPEC

Не обозначает никакой специфический формат адреса. Он используется только в редких случаях, когда необходимо очистить снаружи заданный по умолчанию адрес адресата от "соединенного" датаграмного сокета. См. Раздел 11.9.1 [Посылка Датаграмм].

Соответствующий символ указателя именного пространства PF_UNSPEC существует для законченности, но нет никакой причины использовать его в программе.

"Sys/socket.h" определяет символы, начинающиеся с " AF_ " для различных видов сетей, большинство из которых фактически не встречается. Мы будем документировать только то, что действительно используется на практике.

Установка Адреса сокета

Используйте функцию bind, чтобы для связывания адреса сокета. Прототип для bind находится в заголовочном файле "sys/socket.h". Для примеров использования см. Раздел 11.4 [Именное пространство Файла].

    int bind (int socket, struct sockaddr *addr, size_t length) (функция)

Функция bind назначает адрес сокет socket. Аргументы Addr и length определяют адрес; детализированный формат адреса зависит от именного пространства. Первая часть адреса - всегда указатель формата, который определяет именное пространство, и говорит, что адрес находится в формате для этого именного пространства.

Возвращаемое значение - 0 при успехе и -1 при отказе. Для этой функции в переменной errno определены следующие виды ошибок:

EBADF

аргумент - не допустимый описатель файла.

ENOTSOCK

дескриптор socket - сокет.

EADDRNOTAVAIL

заданный адрес не доступен на этой машине.

EADDRINUSE

Существует другой сокет использующий заданный адрес.

EINVAL

сокет уже имеет адрес.

EACCESS

Вам не достаточно прав для обращения к запрошенному адресу. (В области Internet, только супер-пользователю позволяют определить номер порта в диапазоне от 0 до IPPORT_RESERVED минус один; см. Раздел 11.5.3 [Порты].) Дополнительные условия могут быть возможны в зависимости от специфического именного пространства сокета.

Чтение Адреса сокета

Используйте функцию getsockname, чтобы исследовать адрес гнезда Internet. Прототип для этой функции находится в заголовочном файле "sys/socket.h".

    int getsockname (int socket, struct sockaddr *addr, size_t *length_ptr) (функция)

Функция getsockname возвращает информацию относительно адреса сокета в заданного аргументами addr и length_ptr. Обратите внимание, что length_ptr - указатель; Вы должны инициализировать его, как размер резервирования addr, и по возвращении он содержит фактический размер данных адреса.

Формат данных адреса зависит от именного пространства сокета. Длина информации обычно устанавливается для данного именного пространства, так что обычно Вы можете знать точно, сколько места необходимо. Обычно нужно зарезервировать место для значения, используя соответствующий тип данных для именного пространства сокета, и тогда привести адрес к struct sockaddr *, чтобы передать его getsockname.

Возвращаемое значение - 0 при успехе и -1 при ошибке. Для этой функции в переменной errno определены следующие виды ошибок:

EBADF

аргумент socket - не допустимый описатель файла.

ENOTSOCK

дескриптор socket - не сокет.

ENOBUFS

не имеется достаточных внутренних буферов, доступных для операции.

Вы не можете читать адрес сокета в именном пространстве файла. Это непротиворечиво с остальной частью системы; вообще, не существует способа найти имя файла из описателя для этого файла.

11.4 Именное пространство Файла

Этот раздел описывает подробности именного пространства файла, чье символическое имя (требуется, когда Вы создаете сокет) ­ PF_FILE.

Понятия Именного пространства Файла

В именном пространстве файла, адреса сокетов - имена файлов. Вы можете определять любое желаемое имя файла для адреса сокета, но Вы должны иметь право записи в каталоге, содержащем его. Для чтобы соединяться с сокетом, Вы должны иметь право чтения для него. Обычно эти файлы помещаются в каталог `/tmp'.

Одна особенность именного пространства файла -- имя используется только при открытии соединения; если только оно было законченно, адрес не значим и может не существовать.

Другая особенность заключается в том, что Вы не можете соединяться с таким сокетом на другой машине, даже если другая машина совместно использует файловую систему, которая содержит имя это имя сокета. Вы можете видеть сокет в распечатке каталога, но соединение с ним никогда не произойдет.

После того, как Вы закрываете сокет в именном пространстве файла, Вы должны удалить имя файла из файловой системы. Используйте unlink или remove, чтобы делать это; см. Раздел 9.5 [Удаление Файлов].

Именное пространство файла поддерживает только один протокол для любого типа связи; 0 - номер протокола.

Подробности Именного пространства Файла

Чтобы создавать сокет в именном пространстве файла, используйте константу PF_FILE как аргумент именного пространства для socket или socketpair. Эта константа определена в "sys/socket.h".

    int PF_FILE  (макрос)

Он обозначает именное пространство файла, в котором адреса сокетов являются именами файлов, и связываются совокупностью протоколов.

    int PF_UNIX  (макрос)

Это - синоним PF_FILE используемый для совместимости.

Структура для определения имен сокетов в именном пространстве файла определена в заголовочном файле "sys/un.h":

    struct sockaddr_un  (тип данных)

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

    short int sun_family

Это поле идентифицирует совокупность адреса или формат адреса сокета. Вы должны сохранить значение AF_FILE, чтобы обозначить именное пространство файла. См. Раздел 11.3 [Адреса Гнезда].

    char sun_path[108]

Это имя используемого файла.

Незавершенность: Почему - 108? RMS предлагает делать его массивом нулевой длины и использовать alloc, чтобы зарезервировать соответствующее количество памяти, основываясь на длине filename.

Вы должны вычислить параметр длины для адреса сокета в именном пространстве файла как сумму размера компоненты sun_family и длины (не размера резервирования!) строки имени файла.

Пример файлового-именного пространства сокетов

Вот пример, показывающий, как создавать и связывать сокет в именном пространстве файла.

                #include 
                #include 
                #include 
                #include 
                #include 
                #include 
                int
                make_named_socket (const char *filename)
                {
                        struct sockaddr_un name;
                        int sock;
                        size_t size;
                        sock = socket (PF_UNIX, SOCK_DGRAM, 0);
                        if (sock < 0)
                                        {
                                                perror ("socket");
                                                exit (EXIT_FAILURE);
                                        }
                        name.sun_family = AF_FILE;
                        strcpy (name.sun_path, filename);
                        size=(offsetof(struct sockaddr_un, sun_path)
                                + strlen (name.sun_path) + 1);
                        if (bind (sock, (struct sockaddr *) &name,
                                        size) < 0) {
                                        perror ("bind");
                                        exit (EXIT_FAILURE);
                                }
                        return sock;
       }

11.5 Именное пространство Internet

Этот раздел описывает подробности протокола и соглашений именования сокетов, используемые в именном пространстве Internet.

Чтобы создать сокет в именном пространстве Internet, используйте символическое имя PF_INET этого именного пространства как аргумент именного пространства socket или socketpair. Эта макрокоманда определена в "sys/socket.h".

       int PF_INET        (макрос)

Обозначает именное пространство Internet и связанную совокупность протоколов.

Адрес сокета для именного пространства Internet включает следующие компоненты:

  • Адрес машины с которой Вы хотите соединяться. Адреса в Internet могут быть определены разными способами; эти способы обсуждаются в Разделе 11.5.1 [Формат Адреса Internet] Разделе 11.5.2 [Главные Адреса], и Разделе 11.5.2.4 [Главные Имена].
  • Номер порта для машины. См. Раздел 11.5.3 [Порты].

Вы должны гарантировать, что адрес и номер порта представляется в каноническиом формате, называемом сетевым байтовым порядком. См. Раздел 11.5.5 [Порядок Байов], для уточнения информации относительно этого.

Формат Адреса сокета Internet

В именном пространстве Internet, адрес состоит из главного адреса и порта на этой главной ЭВМ. Кроме того, протокол, который Вы выбираете, служит как бы частью адреса, потому что местные числа порта значимы только внутри специфического протокола.

Тип данных для представления адресов в именном пространстве Internet определен в заголовочном файле "netinet/in.h".

    struct sockaddr_in  (тип данных)

Это тип данных, используемый, чтобы представить адреса в именном пространстве Internet. Он имеет следующие поля:

    short int sin_family

Это поле идентифицирует совокупность адресов или формат адреса сокета. Вы должны сохранить значение AF_INET в этом элементе. См. Раздел 11.3 [Адреса Гнезда].

    struct in_addr sin_addr

Это Internet адрес главной машины. См. Раздел 11.5.2 [Главные Адреса], и Раздел 11.5.2.4 [Главные Имена].

    unsigned short int sin_port

Это номер порта. См. Раздел 11.5.3 [Порты].

Когда Вы вызываете bind или getsockname, Вы должны определить sizeof (struct sockaddr_in) как параметр длины при использовании адреса в именном пространстве Internet.

Главные Адреса

Каждый компьютер в Internet имеет один, или большое количество Internet адресов, т. е. числа, которые идентифицируют этот компьютер среди остальных на Internet. Пользователи обычно записывают число-адрес главной ЭВМ как последовательность из четырех чисел, отделяемых точками, например "128.52.46.32".

Каждый компьютер также имеет одно или большое количество главных имен, которые являются строками слов, отделяемых точками, например "churchy.gnu.ai.mit.edu".

Программы, которые допускают пользователю определять главную ЭВМ обычно принимают и числовые адреса и главные имена. Но для открытия соединения программе необходим числовой адрес, так что для использования главного имени, Вам нужно преобразовать его в числовой адрес.

Адреса Главной ЭВМ Internet

Адрес главной ЭВМ в Internet - это номер, содержащий четыре байта данных. Они разделены на две части, сетевой номер и местный номер внутри этой сети. Сетевой номер состоит из первых одного, двух или трех байт; остальная часть байтов - местный адрес.

Сетевые числа зарегистрированы в Сетевом Информационном Центре (NIC), и разделены на три класса A, B, и C. Местные числа сетевого адреса индивидуальных машин зарегистрированы администратором в локальной сети.

Сеть класса А имеет одиночно-байтовые числа в диапазоне от 0 до 127. Сетей класса A не так уж много, но каждая из них может поддерживать очень большое количество главных ЭВМ. Сети класса В размера имеет Двух-байтовые сетевые числа, с первым байтом в диапазоне от 128 до 191. Класс C самый маленький; адреса в нем они имеют Трех-байтовые сетевые числа, с первым байтом в диапазоне 192-255. Таким образом, первый 1, 2, или 3 байты адреса Internet определяют сеть. Оставшиеся байты адреса Internet определяют адрес внутри этой сети.

Нулевая сеть класса A зарезервирована для передачи по всем сетям. Кроме того, главный номер 0 внутри каждой сети зарезервирован для передачи на все главные ЭВМ в этой сети.

127-ая сеть класса A зарезервирована для возврата цикла; Вы можете всегда использовать адрес Internet "127.0.0.1", чтобы обратиться к главной машине.

Так как одиночная машина может быть элементом нескольких сетей, она может иметь много адресов главной ЭВМ Internet. Однако, предполагается, что существует не более одной машины с тем же самым главным адресом.

Имеются четыре формы стандартного расположения чисел и точек для Internet адреса:

  1. а.b.c.d определяет все четыре байта адреса индивидуально.
  2. а.b.c последняя часть адреса, интерпретируется как 2-байтовое число. Это полезно для определения главных адресов в сети класса B с сетевым адресом a.
  3. а если дана только одна часть, то она соответствует непосредственно числу главного адреса.
  4. "0x" или "0X" подразумевает шестнадцатеричную систему счисления; "0" подразумевает восьмеричную; в противном случае десятичная система счисления.

Тип Данных Главного Адреса

Адреса главной ЭВМ Internet представляются в некоторых контекстах как integers (long unsigned int). В других контекстах, integer упакован внутри структуры типа struct in_addr. Было бы лучше, если бы использование было сделано непротиворечивым.

Следующие базисные определения для Internet адреса, появляются в файле "netinet/in.h":

    struct in_addr      (тип данных)

Этот тип данных используется в некоторых контекстах, чтобы содержать адрес главной ЭВМ Internet. Он имеет только одно поле, именованное s_addr, в которое записывается адрес как long unsigned int.

    unsigned long int INADDR_LOOPBACK  (макрос)

Вы можете использовать эту константу, в качестве адреса вашей машины вместо того, чтобы искать настоящий адрес. В Internet это адрес "127.0.0.1", который обычно называется "localhost". Эта специальная константа сохраняет Вас от проблемы поиска адреса вашей собственной машины. Используя этот адрес можно имитировать передачу пакетов Internet в пределах одной машины.

    unsigned long int INADDR_ANY  (макрос)

Вы можете использовать эту константу вместо "любого входящего адреса". См. Раздел 11.3.2 [Установка Адреса]. Это обычный адрес, для указания в поле sin_addr структуры sockaddr_in, если Вы хотите установить соединение Internet.

    unsigned long int INADDR_BROADCAST  (макрос)

Эта константа - адрес, который Вы используете для посылки широковещательных сообщений.

    unsigned long int INADDR_NONE (макрос)

Эта константа используется некоторыми функциями для отображения ошибок.

Функции Главного Адреса

Это дополнительные функции для управления Internet адресацией, объявленые в "arpa/inet.h". Они представляют Internet адреса в сетевом порядке байтов; это сетевые числа и числа локальных сетевых адресов в главном порядке байтов. См. Раздел 11.5.5 [Порядок Байтов], для объяснения сетевого и главного порядка байтов.

       int inet_aton (const char *name, struct in_addr *addr) (функция)

Эта функция преобразовывает имя адреса главной ЭВМ Internet из стандарта числа-и-точки в двоичные данные. Inet_aton возвращает отличное от нуля чило, если адрес допустим, и нуль если нет.

    unsigned long int inet_addr (const char *name)  (функция)

Эта функция преобразовывает имя адреса главной ЭВМ Internet из стандарта числа-и-точки в двоичные данные. Если ввод не допустим, inet_addr, возвращает INADDR_NONE. Это - устаревший интерфейс для inet_aton; устаревший, потому что INADDR_NONE - допустимый адрес (255.255.255.255), и inet_aton обеспечивает более чистый способ указать ошибку.

    unsigned long int inet_network (const char *name)  (функция)

Эта функция извлекает сетевой номер из имени адреса, данного в стандарте числа-и-точки. Если ввод не допустим, inet_network, возвращает -1.

    char * inet_ntoa (struct in_addr addr)  (функция)

Эта функция преобразовывает addr Internet адреса главной ЭВМ в строку в стандарте числа-и-точки. Возвращаемое значение ­ указатель на статически размещенный буфер. Последующие обращения запишут поверх в тот же самый буфер, так что Вы должны копировать строку, если Вы должны сохранить ее.

    struct in_addr inet_makeaddr (int net, int local)  (функция)

Эта функция создает Internet адрес главной ЭВМ , объединяя номер сети с местным номером.

    int inet_lnaof (struct in_addr addr)

Эта функция возвращает локальную часть адреса, если Internet адрес главной ЭВМ - addr.

    int inet_netof (struct in_addr addr)  (функция)

Эта функция возвращает сетевую часть addr Internet адреса главной ЭВМ.

Главные Имена

Кроме стандарта числа-и-точки для Internet адреса, Вы можете также обратиться к главной ЭВМ символическим именем. Преимущество символического имени - то, что его обычно проще запомнить. Например, машина с адресом "128.52.46.32" также может иметь адрес "churchy.gnu.ai.mit.edu"; и другие машины в этом домене могут обратиться к ней просто как "churchy".

Система использует базу данных, чтобы следить за отображением между главными именами и главными числами. Эта база данных - файл, обычно "/etc/hosts" или эквивалент, обеспеченный блоком преобразования имен. Функции и другие символы для доступа к этой базе данных объявлены в "netdb.h". Возможности BSD могут использоваться при подключении файла "netdb.h".

    struct hostent  (тип данных)

Этот тип данных используется для представления доступа к базе данных главных ЭВМ. Он имеет следующие элементы:

                char *h_name

Это "официальное" имя главной ЭВМ.

    char **h_aliases

Это альтернативные имена для главной ЭВМ, представляемые как вектор с нулевым символом в конце строк.

    int h_addrtype

Это тип главного адреса; практически, значение - всегда AF_INET. В принципе другие виды адресов могли бы представляться в базе данных, также как Internet адреса; если это было выполнено, Вы могли бы найти значение в этом поле отличным от AF_INET. См. Раздел 11.3 [Адреса Гнезда].

    int h_length

Это длина, в байтах, каждого адреса.

    char **h_addr_list

Это вектор адресов для главной ЭВМ. (Заметим, что главная ЭВМ могла бы быть соединенной с несколькими сетями и иметь различные адреса в каждой.) вектор завершен нулевым указателем.

    char *h_addr

Это синоним для h_addr_list [0]; другими словами, это первый главный адрес.

В главной базе данных каждый адрес только блок памяти h_length байт длиной. Но в других контекстах имеется неявное предположение, что Вы можете преобразовывать его в struct addr_in или long unsigned int. Главные адреса в структуре struct hostent всегда даны в сетевом порядке байтов; см. Раздел 11.5.5 [Порядок Байт].

Вы можете использовать gethostbyname или gethostbyaddr, для уточнения инфрмации базы данных главных ЭВМ относительно специфической главной ЭВМ. Информация возвращена в статически размещенной структуре.

    struct hostent * gethostbyname (const char *name)  (функция)

Функция Gethostbyname возвращает информацию относительно главной ЭВМ, именованной name. Если происходит ошибка поиска, она возвращает пустой указатель.

    struct hostent * gethostbyaddr (const char *addr, int length, (функция)

Функция Gethostbyaddr возвращает информацию относительно главной ЭВМ с адресом addr в Internet. Аргумент length - размер (в байтах) адреса addr. format определяет формат адреса; для адреса Internet, определите значение AF_INET.

Если происходит сбой поиска, gethostbyaddr возвращает пустой указатель.

Если поиск имени gethostbyname или gethostbyaddr окончился неудачно, Вы можете выяснить причину, рассматривая значение переменной h_errno. (Было бы правильнее установить errno, но использование h_errno совместимо с другими системами.) Перед использованием h_errno, Вы должны объявить его примерно так:

    extern int h_errno;

Имеются коды ошибок, которые Вы можете находить в h_errno:

    HOST_NOT_FOUND

Нет такой главной ЭВМ в базе данных.

    TRY_AGAIN

Это происходит, когда с блоком преобразования имен нельзя было бы входить в контакт. Если Вы попробуете сделать это позже, то возможно Вам повезет больше.

    NO_RECOVERY

Произошла невосстанавливаемая ошибка .

    NO_ADDRESS

Главная база данных содержит вход для имени, но он не имеет связанного Internet адреса .

Вы можете также просматривать всю базу данных главных ЭВМ используя sethostent, gethostent, и endhostent. Будьте внимательны при использовании этих функций, потому что они не допускают повторного использования.

    void sethostent (int stayopen)  (функция)

Эта функция открывает базу данных главных ЭВМ для просмотра. Затем Вы можете вызывать gethostent для ее чтения.

Если аргумент stayopen является отличным от нуля, она устанавливает флаг так, чтобы последующие обращения к gethostbyname или gethostbyaddr не закрыли базу данных (что они обычно сделали бы).

Это делается для эффективности, если Вы вызываете эти функции несколько раз, то избегаете повторного открытия базы данных для каждого обращения.

    struct hostent * gethostent ()  (функция)

Эта функция возвращает следующий вход в базе данных главных ЭВМ. Она возвращает пустой указатель, если не имеется больше входов.

    void endhostent ()  (функция)

Эта функция закрывает базу данных главных ЭВМ.

Порты Internet

Адрес сокета в именном пространстве Internet состоит из адреса Internet машины плюс номер порта, который отличает гнездо на данной машине (для данного протокола). Номера портов располагаются от 0 до 65535.

Номера портов меньше, зарезервированых IPPORT_RESERVED для стандартных серверов, типа finger и telnet. Имеется база данных, которая следит за ними, и Вы можете использовать функцию getservbyname для отображения сервисного номера порта; см. Раздел 11.5.4 [База данных Услуг].

Если Вы собираетесь устанавливать сервер, который не является стандартно определенным в базе данных, то Вам необходимо выбрать для него номер порта. Используйте номера большие чем IPPORT_USERRESERVED; такие числа зарезервированы для серверов и никогда не будут генерироваться системой.

Когда Вы используете сокет без определения адреса, система генерирует номер порта для него. Этот номер попадает в интервал между IPPORT_RESERVED и IPPORT_USERRESERVED.

На Internet, фактически, законно иметь два различных сокета с одинаковыми номерами портов, пока они оба не попытаются связаться с тем этим адресом сокета (главный адрес плюс номер порта). Вы не должны дублировать номер порта за исключением специальных обстоятельств, где протокол с более высоким уровнем требует этого. Обычно, система не будет разрешать Вам делать это; bind требует различные номера портов. Чтобы многократно использовать номер порта, Вы должны установить опцию сокета SO_REUSEADDR. См. Раздел 11.11.2 [Опции Сокетов].

Эти макрокоманды определены в заголовочном файле "netinet/in.h".

    int IPPORT_RESERVED  (макрос)

Номера портов меньшие IPPORT_RESERVED зарезервированы для использования суперпользователем.

    int IPPORT_USERRESERVED  (макрос)

Номера портов большие или равные IPPORT_USERRESERVED зарезервированы для явного использования; они никогда не будут размещены автоматически.

База данных Услуг

База данных, которая следит за "общеизвестными" услугами - это обычно или файл "/etc/services" или эквивалент из блока преобразования имен. Вы можете использовать эти утилиты, объявленные в "netdb.h" для обращения к базе данных услуг.

    struct servent  (тип данных)

Этот тип данных содержит информацию относительно входов в базе данных услуг, он имеет следующие элементы:

    char *s_name

Это "официальное" имя обслуживания.

    char **s_aliases

Это альтернативные имена обслуживания, представляемые массивом строк.

Пустой указатель завершает массив.

    int s_port

Это номер порта для обслуживания. Номера портов даны в сетевом порядке байтов; см. Раздел 11.5.5 [Порядок Байтов].

    char *s_proto

Это имя протокола, для использования с этим обслуживанием. См. Раздел 11.5.6 [База данных Протоколов].

Чтобы получать информацию относительно специфического обслуживания, используйте функции getservbyname или getservbyport функции. Информация возвращается в статически размещенной структуре.

    struct servent * getservbyname (const char *name, const char *proto) (функция)

Getservbyname функция возвращает информацию относительно обслуживания, именованного name, используя протокол proto. Если она не может найти такое обслуживание, она возвращает пустой указатель.

Эта функция полезна как для серверов так и для клиентов; серверы используют ее, чтобы определить, на каком порту они должны принимать пакеты (см. Раздел 11.8.2 [Прием]).

    struct servent * getservbyport (int port, const char *proto) (функция)

Функция Getservbyport возвращает информацию относительно обслуживания на порте port, используя протокол proto. Если она не может найти такое обслуживание, она возвращает пустой указатель.

Вы можете также просматривать базу данных услуг, используя setservent, getservent, и endservent. Будьте внимательным в использовании этих функций, потому что они не предназначены для повторного использования.

    void setservent (int stayopen)  (функция)

Эта функция открывает базу данных услуг для просмотра.

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

    struct servent * getservent (void)  (функция)

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

    void endservent (void)  (функция)

Эта функция закрывает базу данных услуг.

Преобразование Порядка Байтов

Различные виды компьютеров используют различные соглашения для упорядочения байтов внутри слова. Некоторые компьютеры помещают старший байт сначала (это называется "big-endian" порядком), а другие помещают его последним ("little-endian" порядок).

Так, чтобы машины с различными соглашениями порядка байтов могли связываться, протоколы Internet определяют каноническое соглашение порядка байтов для данных, переданных по сети. Оно известно как сетевой порядок байта.

При установлении соединения в Internet, Вы должны удостовериться, что данные в sin_port и sin_addr элементах структуры sockaddr_in представляются в сетевом порядке байта. Если Вы кодируете данные integer в сообщениях, посланных через сокет, Вы должны преобразовать их в сетевой порядок байта. Если Вы не делаете этого, ваша программа может работать не правильно при сообщении с другими типами машин.

Если Вы используете getservbyname и gethostbyname или inet_addr, для получения номера порта и главного адреса, то эти значения уже в сетевом порядке байта, и Вы можете копировать их непосредственно в структуру sockaddr_in.

Иначе, Вы должны преобразовать значения явно. Используйте htons и ntohs, чтобы преобразовать значения для sin_port элемента. Используйте htonl и ntohl, чтобы преобразовать значения для sin_addr элемента. (Помните, struct in_addr эквивалентен long unsigned int.) Эти функции описаны в "netinet/in.h".

    unsigned short int htons (unsigned short int hostshort) (функция)

Эта функция преобразовывает short integer hostshort из главного порядка байтов в сетевой порядок байта.

    unsigned short int ntohs (unsigned short int netshort) (функция)

Эта функция преобразовывает short integer netshort из сетевого порядка байта в главный порядок байта.

    unsigned long int htonl (unsigned long int hostlong)

Эта функция преобразовывает long integer hostlong из главного порядка байтов в сетевой порядок байт.

    unsigned long int ntohl (unsigned long int netlong)  (функция)

Эта функция преобразовывает long integer netlong из сетевого порядка байт в главный порядок байт.

База данных Протоколов

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

Заданный по умолчанию протокол связи для именного пространства Internet зависит от стиля связи. Для потокового взаимодействия, значение по умолчанию - TCP ("протокол управления передачей"). Для датаграмной связи, значение по умолчанию - UDP ("протокол датаграммы пользователя"). Для надежной датаграмной связи значение по умолчанию - RDP ("надежный датаграмный протокол"). Вы должны почти всегда использовать это значение по умолчанию.

Протоколы Internet вообще определены именем вместо номера. Сетевые протоколы, которые знает главная ЭВМ, сохранены в базе данных. Она обычно происходит от файла "/etc/protocols", или может быть эквивалент, обеспеченный блоком преобразования имен. Вы можете искать номер протокола, связанный с именованным протоколом в базе данных, используя getprotobyname функцию.

Имеются детализированные описания утилит для доступа к базе данных протоколов. Они объявлены в "netdb.h".

    struct protoent  (тип данных)

Этот тип данных используется, чтобы представить входы в базе данных сетевых протоколов. Он имеет следующие элементы:

    char *p_name

Это официальное имя протокола.

    char **p_aliases

Это альтернативные имена для протокола, заданные как массив строк.

Последний элемент массива - пустой указатель.

    int p_proto

Это номер протокола (в главном порядке байт); используйте этот элемент как аргумент protocol для socket.

Вы можете использовать getprotobyname и getprotobynumber, чтобы искать в базе данных протоколов специфический протокол. Информация возвращается в статически размещенной структуре; Вы должны копировать информацию, если Вы хотите сохранить ее для следующих обращений.

    struct protoent * getprotobyname (const char *name)  (функция)

Функция Getprotobyname возвращает информацию относительно сетевого протокола, именованного name. Если там нет такого протокола, она возвращает пустой указатель.

    struct protoent * getprotobynumber (int protocol)  (функция)

Getprotobynumber функция возвращает информацию относительно сетевого протокола с указанным номером. Если там нет такого протокола, она возвращает пустой указатель.

Вы можете также просматривать целую базу данных протоколов (по одному протоколу одновременно), используя setprotoent, getprotoent, и endprotoent. Будьте внимательным в использовании этих функций, потому что они не предназначены для повторного использования.

    void setprotoent (int stayopen)  (функция)

Эта функция открывает для просмотра базу данных протоколов.

Если аргумент stayopen является отличным от нуля, она устанавливает флаг так, чтобы последующие обращения к getprotobyname или getprotobynumber не закрыли базу данных. Это делается для большей эффективности, если Вы вызываете эти функции несколько раз, избегая повторного открытия базы данных для каждого обращения.

    struct protoent * getprotoent (void)  (функция)

Эта функция возвращает следующий вход в базе данных протоколов. Она возвращает пустой указатель, если не имеется больше входов.

    void endprotoent (void)  (функция)

Эта функция закрывает базу данных протоколов.

Пример Internet сокета.

Вот пример, показывающий, как создавать и называть сокет в именном пространстве Internet. Созданный сокет существует на машине, на которой выполняется программа. Вместо поиска и использования адреса Internet машины, этот пример определяет INADDR_ANY как главный адрес.

                #include 
                #include 
                #include 
                #include 
                int
                make_socket (unsigned short int port)
                {
                        int sock;
                        struct sockaddr_in name;
                        sock = socket (PF_INET, SOCK_STREAM, 0);
                        if (sock < 0)
                                {
                                        perror ("socket");
                                        exit (EXIT_FAILURE);
                                }
                        name.sin_family = AF_INET;
                        name.sin_port = htons (port);
                        name.sin_addr.s_addr = htonl (INADDR_ANY);
                        if (bind (sock, (struct sockaddr *) &name,
                                                sizeof (name)) < 0)
                                {
                                        perror ("bind");
                                        exit (EXIT_FAILURE);
                                }
                        return sock;
                }

Вот другой пример, показывающий как Вы можете вносить в структуре sockaddr_in, данную строку главного имени и номер порта:

                #include 
                #include 
                #include 
                #include 
                #include 
                void
                init_sockaddr (struct sockaddr_in *name, const
        char *hostname, unsigned short int port)
                {
                        struct hostent *hostinfo;
                        name->sin_family = AF_INET;
                        name->sin_port = htons (port);
                        hostinfo = gethostbyname (hostname);
                        if (hostinfo == NULL)
                                {
                                        fprintf (stderr, "Unknown host
                                                %s.\n", hostname);
                                        exit (EXIT_FAILURE);
                                }
                        name->sin_addr = *(struct in_addr *)
                        hostinfo->h_addr;
                }

11.6 Другие именные пространства

Конечно другие именные пространства и связанные семейства протоколов также реализованы, но не описаны здесь, потому что они редко используются. PF_NS обращается к протоколам Программного обеспечения Сети Ксерокса (Xerox Network Software). PF_ISO замещает Открытые системы Связи (Open Systems Interconnect). PF_CCITT обращается к протоколам из МККТТ (CCITT). "Socket.h" определяет эти символы и другие протоколы.

PF_IMPLINK используется для связи между главными ЭВМ и Процессорами Сообщений Internet.

11.7 Открытие и Закрытие сокетов

Этот раздел описывает фактические библиотечные функции для открытия и закрытия сокетов. Те же самые функции работают для всех именных пространств и стилей соединения.

Создание сокета.

Примитив для создания сокета - функция socket, объявлена в "sys/socket.h".

    int socket (int namespace, int style, int protocol)  (функция)

Эта функция создает сокет и определяет style стиль связи, который должен быть одним из стилей сокетов, перечисленных в Разделе 11.2 [Стили Связи]. Аргумент namespace определяет именное пространство; это должно быть PF_FILE (см. Раздел 11.4 [Именное пространство Файла]) или PF_INET (см. Раздел 11.5 [Именное пространство Internet]). protocol обозначает специфический протокол (см. Раздел 11.1 [Понятия Гнезда] ).

Возвращаемое значение из socket - описатель файла для нового сокета, или -1 в случае ошибки. Следующие errno условия ошибки определены для этой функции:

EPROTONOSUPPORT

Протокол или стиль не обеспечивается заданным именным пространством.

EMFILE процесс имеет слишком много открытых описателей файла.

ENFILE

система имеет слишком много открытыми описателей файла

EACCESS

процесс не имеет привилегии, чтобы создать сокет заданного стиля или протокола.

ENOBUFS

в системе закончилось внутреннее пространство буфера.

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

Пример вызова функции socket см. Раздел 11.4 [Именное пространство Файла].

Закрытие сокета.

Когда Вы закончили использование сокета, Вы можете просто закрыть описатель файла примитивом close; см. Раздел 8.1 [Открытие и Закрытие Файлов].

Вы можете также выключать только прием или только передачу на соединении, вызывая shutdown, которая объявлена в "sys/socket.h".

    int shutdown (int socket, int how)  (функция)

Функция shutdown выключает соединение с сокетом socket. Аргумент how определяет какое действие выполнить:

    0 Остановка при получения данных для этого сокета.
    1 Остановка при передаче данных с этого сокета.
    2 Остановка и приема и передачи.

Возвращаемое значение - 0 при успехе и -1 в случае неудачи. В переменной errno определяются следующие коды ошибок для этой функции:

EBADF

socket - не допустимый описатель файла.

ENOTSOCK

socket - не сокет.

ENOTCONN

socket не соединен.

Пары сокетов

Пара socket состоит из пары соединенных (но неименованных) сокетов. Это очень похоже на трубопровод и используется аналогичным способом. Пары сокетов создаются функцией socketpair, описание в файле "sys/socket.h".

    int socketpair (int namespace, int style, int protocol, int fields[Function2])

Эта функция создает пару сокетов, возвращая описатели файла в fields [0] и fields [1]. Пара сокетов - дуплексный канал связи, то есть и чтение и запись могут выполняться в любую сторону.

Аргументы namespace, style и protocol интерпретируется как в функции socket. style должен быть один из стилей связи, перечисленных в Разделе 11.2 [Стили связb]. Аргумент именного пространства определяет именное пространство, которое должно быть AF_FILE (см. Раздел 11.4 [Именное пространство Файла]); protocol определяет протокол связи.

Если style определяет стиль связи без установки логического соединения, то два сокета, которые Вы получаете, не соединены, строго говоря, но каждое из них знает другое как заданный по умолчанию адрес адресата, так что они могут посылать пакеты друг другу.

Функция Socketpair возвращает 0 при успехе и -1 при отказе. В переменной errno определяются следующие коды ошибок для этой функции:

EMFILE

Процесс имеет слишком много открытых описателей файла.

EAFNOSUPPORT

Не обеспечивается заданное именное пространство.

EPROTONOSUPPORT

Не обеспечивается заданный протокол.

EOPNOTSUPP

Заданный протокол не поддерживает создание пар сокетов.

11.8 Использование сокетов с соединениями.

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

  • Раздел 11.8.1 [Соединение], описывает то, что клиентская программа должна делать, чтобы инициализировать соединение с сервером.
  • Раздел 11.8.2 [Прием], и Раздел 11.8.3 [Принятие Соединений], описывает то, что программа сервера должна делать, чтобы ждать и делать после запросов соединения от клиентов.
  • Раздел 11.8.5 [Пересылка Данных], описывает, как данные перемещаются через соединенные сокеты.

Создание Соединения

В создании соединения, клиент делает соединение, в то время как сервер ждет и принимает соединение. Здесь мы обсуждаем то, что клиентская программа должна делать, используя функцию connect, которая объявлена в "sys/socket.h".

    int connect (int socket, struct sockaddr *addr, size_t length) (функция)

Функция connect инициализирует соединение из сокета socket, чей адрес определен аргументами length и addr. (Этот сокет обычно находится на другой машине, и он должен быть установлен как сервер.) См. Раздел 11.3 [Адреса Сокетов], для уточнения инфрмации относительно того, как эти аргументы интерпретируются.

Обычно, connect ждет, пока сервер не отвечает на запрос прежде. Вы можете устанавливать режим неблокирования на сокете socket, чтобы заставить connect возвратиться немедленно без ожидания ответа. См. Раздел 8.10 [Флаги Состояния Файла], для уточнения инфрмации относительно неблокирования.

Нормальное возвращаемое значение connect - 0. Если происходит ошибка, connect возвращает -1. В переменной errno определяются следующие коды ошибок для этой функции:

EBADF

сокет socket - не допустимый описатель файла.

ENOTSOCK

указанный сокет - не сокет.

EADDRNOTAVAIL

заданный адрес не доступен на отдаленной машине.

EAFNOSUPPORT

именное пространство addr не обеспечивается этим сокетом.

EISCONN

указанный сокет уже соединен.

ETIMEDOUT

попытка установить соединение не состоялась.

ECONNREFUSED

сервер активно отказался устанавливать соединение.

ENETUNREACH

сеть данного addr не доступна с этой главной ЭВМ.

EADDRINUSE

адрес сокета для данного addr уже используется.

EINPROGRESS

указанный сокет не-блокируемый, и соединение не могло бы быть установлено немедленно.

EALREADY

указанный сокет не-блокируемый и уже имеет отложенное соединение.

Ожидание Соединений

Теперь рассмотрим то, что процесс сервера должен делать, чтобы принять соединение из сокета. Это включает использование функции listen, чтобы дать возможность запросам на соединения через сокет, и позже использование функции accept (см. Раздел 11.8.3 [Принятие Соединений] ) чтобы действовать по запросу. Функция listen используется только для уже установленного логического соединения.

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

В именном пространстве Файла, обычные биты защиты файла управляют доступом к сокету.

    int listen (int socket, unsigned int n)  (функция)

Функция listen дает возможность указанному сокету воспринимать соединения, таким образом создается сокет сервера.

Аргумент n определяет длину очереди для отложенных соединений.

Функция listen возвращает 0 при успехе и -1 в случае неудачи. В переменной errno определяются следующие коды ошибок для этой функции:

EBADF

аргумент socket - не допустимый описатель файла.

ENOTSOCK

аргумент socket - не сокет.

EOPNOTSUPP

указанный сокет не поддерживает эту операцию.

Принятие Соединений

Когда сервер получает запрос соединения, он может создать соединение, принимая запрос. Для этих целей следует использовать функцию accept.

Сокет, который был установлен как сервер, может принимать запросы соединения от многих клиентов. Этот сокет сервера не станет частью соединения; взамен, accept делает новый сокет, который разделяет соединения. Accept возвращает описатель для этого сокета.

Исходный сокет сервера остается доступным для ожидания дальнейших запросов соединения.

Число отложенных запросов соединения на сокете сервера конечно. Если запросы соединения прибывают быстрее, чем сервер может их обработать, очередь может заполниться, и дополнительные запросы получат отказ с ошибкой ECONNREFUSED. Вы можете определять максимальную длину этой очереди как аргумент функции listen, хотя система может также наложить собственное внутреннее ограничение длины этой очереди.

    int accept (int socket, struct sockaddr *addr, size_t *length_ptr)

Эта функция используется для принятия запроса на соединения в указанном сокете сервера.

Функция accept находится в состоянии ожидания, когда нет возможности принять соединение, если, конечно, указанный сокет не имеет набор режимов неблокирования. (Вы можете использовать select, чтобы ждать отложенное соединение на неблокируемом сокете.) См. Раздел 8.10 [Флаги Состояния Файла], для уточнения информации относительно режима неблокирования.

Аргументы Addr и length_ptr используется, чтобы возвратить информацию относительно имени клиентского сокета, которое инициализировало соединение. См. Раздел 11.3 [Адреса сокетов], для уточнения информации относительно формата.

Сокет, который был установлен как сервер не станет частью соединения; взамен, accept сделает новый сокет. Accept возвращает описатель для этого сокета. Нормальное возвращаемое значение accept - описатель файла для нового сокета.

После accept, первоначально указанный сокет остается открытым и не связанным, и продолжает ожидать, пока Вы не закрываете его. Вы можете принимать дальнейшие соединения с этим сокетом, вызывая accept снова.

Если происходит ошибка, и accept возвращает -1. В переменной errno определяются следующие коды ошибок для этой функции:

EBADF

аргумент socket - не допустимый описатель файла.

ENOTSOCK

дескрипторный аргумент socket - не сокет.

EOPNOTSUPP

описанный сокет не поддерживает эту операцию.

EWOULDBLOCK

сокет имеет набор режимов неблокирования, и нет никаких отложенных соединений.

Функцию accept не позволяется применять для сокета без установления логического соединения.

Кто соединен со Мной?

    int getpeername (int socket, struct sockaddr *addr, size_t *length_ptr)  (функция)

Функция Getpeername возвращает адрес сокета, с которым сокет соединен; она сохраняет адрес в пространстве памяти, заданном addr и length_ptr. Она сохраняет также длину адреса в *length_ptr.

См. Раздел 11.3 [Адреса Сокетов] , для уточнения информации относительно формата адреса. В некоторых операционных системах, getpeername работает только для сокетов в области Internet.

Возвращаемое значение - 0 при успехе и -1 в случае неудачи. В переменной errno определяются следующие коды ошибок для этой функции:

EBADF

аргумент socket - не допустимый описатель файла.

ENOTSOCK

указанный сокет - не сокет.

ENOTCONN

указанный сокет не соединен.

ENOBUFS

нет внутренних доступных буферов.

Пересылка Данных

Если сокет был соединен с равным, Вы можете использовать обычные примитивы read и write (см. Раздел 8.2[Примитивы ввода - вывода]), чтобы передать данные. Сокет - канал двусторонней связеи, так что чтение и запись может выполняться в оба конца.

Имеются также некоторые режимы ввода - вывода, которые являются специфическими для операций с сокетами. Чтобы определять эти режимы, Вы должны использовать функции recv и send вместо более обобщенного чтения и записи. Функции recv и send берут дополнительный аргумент, который Вы можете использовать, чтобы определить различные флаги, для управления специальными режимами ввода - вывода. Например, Вы можете определить флаг MSG_OOB, чтобы читать или писать внепоточные данные, а также флаги MSG_PEEK или MSG_DONTROUTE.

Посылка Данных

Функция send объявлена в файле "sys/socket.h". Если ваш аргумент flags нуль, Вы можете точно также использовать write вместо send. Если сокет был соединен, но соединение прервано, Вы получаете сигнал SIGPIPE для каждого использования send или write (см. Раздел 21.2.6 [Разнообразные Сигналы]).

    int send (int socket, void *buffer, size_t size, int flags) (функция)

Функция send - подобна write, но с дополнительными флагами Flags. Возможные значения flags описаны в Разделе 11.8.5.3 [Опции Данных сокетов].

Эта функция возвращает число переданных байтов, или -1 в противном случае. Если сокет неблокируемый, то send (подобно write) может возвращать после посылки только часть данных. См. Раздел 8.10 [Флаги Состояния Файла], для уточнения информации относительно режима неблокирования.

Обратите внимание, что успешное возвращаемое значение просто указывает, что сообщение было послано без ошибки, и не обязательно, что оно было получено без ошибки. В переменной errno определяются следующие коды ошибок для этой функции:

EBADF

аргумент socket - не допустимый описатель файла.

EINTR

операция был прервана сигналом прежде, чем любые данные были посланы. См. Раздел 21.5 [Прерванные Примитивы].

ENOTSOCK

указанный сокет - не сокет.

EMSGSIZE

тип сокетаа требует, чтобы сообщение было послано быстро, но сообщение слишком большое для этого.

EWOULDBLOCK

на сокете был установле режим неблокирования, а операция записи блокирует. (Обычно send блокирует, пока операция не может быть завершена.)

ENOBUFS

не имеется достаточного внутреннего доступного пространства буфера.

ENOTCONN

Вы не соединили этот сокет.

EPIPE

Этот сокет был соединен, но соединение теперь разбито. В этом случае send генерирует SIGPIPE сначала; если этот сигнал игнорируется или блокируется, или если обработчик возвращается, то происходит сбой send с EPIPE.

Получение Данных

Функция recv объявлена в файле "sys/socket.h". Если ваш аргумент flags является нулем, Вы можете точно также использовать read вместо recv; см. Раздел 8.2 [Примитивы ввода-вывода].

    int recv (int socket, void *buffer, size_t size, int flags) (функция)

Функция recv подобна read, но с дополнительными флагами flags. Возможные значения flags описаны В Разделе 11.8.5.3 [Опции Данных сокетов].

Если режим неблокирования установлен для сокета, и никакие данные не доступны для чтения, recv не ожидает, а сразу возвращает код ошибки. См. Раздел 8.10 [Флаги Состояния Файла], для уточнения информации относительно режима неблокирования.

Эта функция возвращает число полученных байтов, или -1 в противном случае. В переменной errno определяются следующие коды ошибок для этой функции:

EBADF

аргумент socket - не допустимый описатель файла.

ENOTSOCK

дескриптор socket - не сокет.

EWOULDBLOCK

Режим неблокирования был установлен на сокете. (Обычно, recv блокирует пока не имеется входа, доступного для чтения.)

EINTR

операция была прервана сигналом прежде, чем любые данные прочитались. См. Раздел 21.5 [Прерванные Примитивы].

ENOTCONN

Вы не соединили этот сокет.

Опции Данных сокета.

Аргумент flags для send и recv - битовая маска. Вы можете объединить значения следующих макрокоманд вместе (через OR), чтобы получить значение для этого аргумента. Все они определены в файле "sys/socket.h".

    int MSG_OOB   (макрос)

Посылка или получение данных вне потока. См. Раздел 11.8.8 [Данные вне потока].

    int MSG_PEEK  (макрос)

Рассмотрение данных, но не удаление их из входной очереди. Это применимо только для функций типа recv (для send не подходят).

    int MSG_DONTROUTE  (макрос)

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

Пример сокета с потоком байтов.

Вот пример программы клиента, которая устанавливает соединение для сокета в пространстве Internet с поточным типом передачи данных. Она не делает ни чего особенно интересного; если она соединилась с сервером, она посылает текстовую строку серверу и выходит.

                #include 
                #include 
                #include 
        #include 
                #include 
        #include 
        #include 
        #include 
        #define PORT    5555
        #define MESSAGE "Yow!!! Are we having fun yet?!?"
        #define SERVERHOST              "churchy.gnu.ai.mit.edu"
        void
        write_to_server (int filedes)
        {
                int nbytes;
                nbytes=write(filedes,MESSAGE,strlen(MESSAGE)+1);
                if (nbytes < 0)
                        {
                                perror ("write");
                                exit (EXIT_FAILURE);
                        }
        }
        int
        main (void)
        {
                extern void init_sockaddr(struct sockaddr_in*name,
                                                const char *hostname,
                                                unsigned short int port);
                int sock;
                struct sockaddr_in servername;
                sock = socket (PF_INET, SOCK_STREAM, 0);
                if (sock < 0)
                        {
                                perror ("socket (client)");
                                exit (EXIT_FAILURE);
                        }
                init_sockaddr (&servername, SERVERHOST, PORT);
                if (0 > connect (sock,
                                        (struct sockaddr *) &servername,
                                        sizeof (servername)))
                        {
                                perror ("connect (client)");
                                exit (EXIT_FAILURE);
                        }
                write_to_server (sock);
                close (sock);
                exit (EXIT_SUCCESS);
        }

Пример соединения сервера (Тип соединения - поток байтов)

Текст программы сервера намного более сложен. Так как мы хотим предоставлять многим клиентам быть соединенными с сервером, но в то же самое время, было бы неправильно ждать ввод от одиночного клиента, просто вызывая read или recv. Взамен, нужно использовать select (см. Раздел 8.6 [Ждущий ввод - вывод] ), чтобы ждать ввод на всех открытых сокетах. Это также позволяет серверу иметь дело с дополнительными запросами соединения.

Этот специфический сервер не делает хоть что-нибудь интересное, если он получил сообщение от клиента, то он закрывает сокет клиента по получению признака конца файла.

Эта программа использует make_socket и init_sockaddr для устанавления адреса сокета; см. раздел 11.5.7 [Inet Пример].

        #include 
        #include 
        #include 
        #include 
        #include 
        #include 
        #include 
        #include 
        #define PORT   5555
        #define MAXMSG  512
        int
        read_from_client (int filedes)
        {
                char buffer[MAXMSG];
                int nbytes;
                nbytes = read (filedes,buffer,MAXMSG); 
                if (nbytes < 0)
                        {
                                perror ("read");
                                exit (EXIT_FAILURE);
                        }
                else if (nbytes == 0)
                        return -1;
                else
                        {
                                fprintf (stderr, "Server: got message:  
                                        `%s'\n", buffer);
                                        return 0;
                        }
        }
        int
        main (void)
        {
                extern int make_socket (unsigned short int port);
                int sock;
                int status;
                fd_set active_fd_set, read_fd_set;
                int i;
                struct sockaddr_in clientname;
                size_t size;
                sock = make_socket (PORT);
                if (listen (sock, 1) < 0)
                {
                        perror ("listen");
                        exit (EXIT_FAILURE);
                }
                FD_ZERO (&active_fd_set);
                FD_SET (sock, &active_fd_set);
                while (1)
                        {
                                if (select (FD_SETSIZE,
                                        &read_fd_set, NULL,
                                        NULL, NULL) < 0)
                                {
                                        perror ("select");
                                        exit (EXIT_FAILURE);
                                }
                        if (FD_ISSET (i, &read_fd_set))
                        {
                                if (i == sock)
                                {
                                        if (accept (sock,
                        (struct sockaddr *) &clientname, &size) < 0)
                                                                                        
                                {
                                                perror ("accept");
                                                exit (EXIT_FAILURE);
                                        }
                                        fprintf (stderr,
                "Server: connect from host %s, port %hd.\n",
                                        inet_ntoa (clientname.sin_addr),
                                        ntohs (clientname.sin_port));
                                        FD_SET (status, &active_fd_set);
                                }
                                else
                                {
                                        if (read_from_client (i)<0)
                                        {
                                                close (i);
                                                FD_CLR (i, &active_fd_set);
                                        }
                                }
                        }
                }
        }

Данные Вне потока

Потоки с соединениями разрешающими данные вне потока имеют приоритет выше, чем обычные данные. Обычно причина для посылки данных вне потока - исключительные условия. Способ послать данные вне потока использует send с флагом MSG_OOB (см. Раздел 11.8.5.1 [Посылка Данных]).

Данные вне потока посылаются с высшим приоритетом, плюс процесс получения не обрабатывает их в обыкновенное очереди, но чтобы читать доступные данные вне потока следует использовать recv с флагом MSG_OOB (см. Раздел 11.8.5.2 [Получение Данных]). Обычные операции чтения не воспринимают данные вне потока; они читают только обычные данные.

Когда сокет находит, что данные вне потока продвигаются, он посылает сигнал SIGURG процессу владельца или группе процессов сокета. Вы можете определять владельца, используя команду F_SETOWN для функции fcntl; см. Раздел 8.12 [Ввод Прерывания]. Вы должны также установить обработчик для этого сигнала, как описано в Главе 21 [Обработка Сигналов], для соответствующего действия типа чтения данных вне потока.

В качестве альтернативы, Вы можете проверять задержать данные вне потока, или ждать данные вне потока, при использовании функции select; она может ждать исключительное условие на гнезде. См. Раздел 8.6 [Ждущий ввод - вывод].

Уведомление о данных вне потока (с SIGURG или с select) обозначает, что данные вне потока находятся в пути; данные не могут фактически прибывать позже. Если Вы пробуете читать данные вне потока прежде, чем они ппребывают, то recv генерирует ошибку с кодом EWOULDBLOCK.

Посылка таких данных автоматически помещает "метку" в потоке обычных данных, показывающую, где в последовательности данных " были бы "данные вне потока. Это полезно, когда значение данных вне потока - " отменяет все посланное до ". Вот, как Вы можете в процессе получения проверять, были ли любые обычные данные посланы перед меткой:

    success = ioctl (socket, SIOCATMARK, &result);

Имеется функция, чтобы отбросить любые обычные данные, предшествующие данным вне потока:

                int
                discard_until_mark (int socket)
                {
                        while (1)
                        {
                                char buffer[1024];
                                int result, success;
                                success = ioctl (socket, SIOCATMARK,
                                &result); if (success < 0)
                                perror ("ioctl");
                                if (result)
                                        return;
                                success = read (socket, buffer, sizeof
                                                buffer);
                                if (success < 0)
                                perror ("read");
                        }
                }

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

                struct buffer
                {
                        char *buffer;
                        int size;
                        struct buffer *next;
                };
        struct buffer *
        read_oob (int socket)
        {
                struct buffer *tail = 0;
                struct buffer *list = 0;
                while (1)
                {
                        char *buffer = (char *) xmalloc (1024);
                        struct buffer *link;
                        int success;
                        int result;
                        success = recv (socket, buffer, sizeof
                buffer, MSG_OOB);
                        if (success >= 0)
                        {
                                link->size = success;
                                link->next = list;
                                return link;
                        }
                        success = ioctl (socket, SIOCATMARK,
                &result);
                         if (success < 0)
                                perror ("ioctl");
                        if (result)
                        {
                                sleep (1);
                                continue;
                        }
                        success = read (socket, buffer, sizeof
                        buffer);
                        if (success < 0)
                        perror ("read");
                        {
                                link->size = success;
                                if (tail)
                                        tail->next = link;
                                else
                                        list = link;
                                tail = link;
                        }
                }
        }

11.9 Датаграмные операции сокета

Этот раздел описывает, как использовать стили связи, которые не используют соединения (стили SOCK DGRAM и SOCK_RDM). При использовании этих стилей, Вы группируете данные в пакеты, и каждый пакет - независимая связь. Вы определяете адресата для каждого пакета индивидуально.

Датаграмные пакеты подобны письмам: Вы посылаете каждый независимо, с собственным адресом адресата, и они могут прибывать в неправильном порядке или вообще не прибывать.

Функции listen и accept не предназначены для сокетов, использующих стили связи без установки логического соединения.

Посылка Датаграмм

Нормальный способ посылки данных относительно датаграмного сокета использует функцию sendto, объявленную в "sys/socket.h".

!!! Вы можете вызывать connect на датаграмном сокете, но эта функция определяет заданного по умолчанию адресата для дальнейшей пе­ редачи данных на сокете. Когда сокет имеет по умолчанию заданного ад­ ресата, Вы можете использовать send (см. Раздел 11.8.5.1 [Посылка Дан­ ных] ) или write (см. Раздел 8.2 [Примитивы ввода - вывода] ) для по­ сылки пакетов. Вы можете отменять заданного по умолчанию адресата, вы­ зывая connect, и используя формат адреса AF_UNSPEC в аргументе addr. См. Раздел 11.8.1 [Соединение].

    int sendto (int socket, void *buffer. size_t size, int flags, sockaddr *addr, size_t length)

Эта функция пересылает данные из buffer через сокет socket по заданному адресу. size задает число пересылаемых байт.

Flags интерпретируется также как и в send; см. Раздел 11.8.5.3 [Опции данных сокетов].

Возвращаемое значение и условия ошибок такие же как и для send, но Вы не можете полагаться на систему для обнаружения ошибок и сообщения о них; наиболее общая ошибка состоит в том, что пакет потеряется или не имеется никого в заданном адресе, чтобы получить его, и операционная система на вашей машине обычно не знает этого.

Также возможно, что для одного обращения к sendto она сообщит ошибку из-за проблемы связанной с предыдущим обращением.

Получение Датаграмм

Функция recvfrom читает пакет из датаграмного сокета и также сообщает Вам, откуда он был послан. Эта функция объявлена в "sys/socket.h".

    int recvfrom (int socket, void *buffer, size_t size, int flags, struct sockaddr *addr, size_t *length_ptr)

Функция recvfrom читает один пакет из указанного сокета в указанный буфер.

Аргумент size определяет максимальное число байтов, которые нужно читать.

Если пакет является больше чем size байт, то, Вы получаете первые size байт пакета, а остальная часть пакета потеряна. Не существует способа прочитать остальную часть пакета. Таким образом, когда Вы используете протокол пакетов, Вы должны всегда знать длину ожидаемого пакета.

Аргументы addr и length_ptr используются для возвращения адреса источника пакета. См. Раздел 11.3 [Адреса сокетов]. Для сокета в области файла, информация адреса не будет значима, так как Вы не можете читать адрес такого сокета (см. Раздел 11.4 [Именное пространство Файла] ). Вы можете определять пустой указатель как аргумент addr, если Вы не заинтересованы в этой информации.

Flags интерпретируется тем же самым способом как recv (см. Раздел 11.8.5.3 [Опции Данных сокета]). Возвращаемое значение и условия ошибки - такие же как для recv.

Вы можете использовать recv (см. Раздел 11.8.5.2 [Получение Данных]) вместо recvfrom, если знаете, что не должны выяснить, кто послал пакет. Даже read, может использоваться, если Вы не хотите определять flags (см. Раздел 8.2 [Примитивы ввода ­ вывода]).

Датаграмный Пример сокета.

Вот набор примеров программ, которые посылают сообщения используя датаграмный стиль. И клиент и сервер используют функцию make_named_socket, которая была предоставлена в Разделе 11.4 [Именное пространство Файла], для создания и связывания сокетов.

Сначала программа сервера. Очевидно, это не особенно полезная программа, но она показывает общие идеи.

                #include 
                #include 
                #include 
                #include 
                #include 
                #define SERVER  "/tmp/serversocket"
                #define MAXMSG  512
                int
                main (void)
                {
                        int sock;
                        char message[MAXMSG];
                        struct sockaddr_un name;
                        size_t size;
                        int nbytes;
                        sock = make_named_socket (SERVER);
                        while (1)
                        {
                                size = sizeof (name);
                                nbytes=recvfrom(sock,message,MAXMSG,0,
                                        (struct sockaddr*) & name,&size);
                                if (nbytes < 0)
                                {
                                        perror ("recfrom (server)");
                                        exit (EXIT_FAILURE);
                                }
                                fprintf (stderr, "Server: got message:
                                %s\n", message);
                                nbytes=sendto(sock,message,nbytes,0,
                                        (struct sockaddr*) & name,size);
                                if (nbytes < 0)
                                {
                                        perror ("sendto (server)");
                                        exit (EXIT_FAILURE);
                                }
                        }
                        }

Пример Чтения Датаграмм

Вот программа клиента, соответствующая серверу выше.

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

                #include 
                #include 
                #include 
                #include 
                #include 
                #include 
                #define SERVER  "/tmp/serversocket"
                #define CLIENT  "/tmp/mysocket"
                #define MAXMSG  512
                #define MESSAGE "Yow!!! Are we having fun yet?!?"
                int
                main (void)
                {
                        extern int make_named_socket (const
                char *name);
                        int sock;
                        char message[MAXMSG];
                        struct sockaddr_un name;
                        size_t size;
                        int nbytes;
                        sock = make_named_socket (CLIENT);
                        name.sun_family = AF_UNIX;
                        strcpy (name.sun_path, SERVER);
                        size = strlen (name.sun_path) + sizeof (name.sun_family);
                        nbytes = sendto (sock, MESSAGE,
        strlen (MESSAGE) + 1, 0, (struct sockaddr *) & name, size);
                        if (nbytes < 0)
                        {
                                perror ("sendto (client)");
                                exit (EXIT_FAILURE);
                        }
                nbytes =recvfrom(sock,message,MAXMSG,0,NULL,0);
                if (nbytes < 0)
                {
                        perror ("recfrom (client)");
                        exit (EXIT_FAILURE);
                }
                fprintf (stderr,"Client: got message: %s\n",message);
                        remove (CLIENT);
                        close (sock);
                }

Имейте в виду, что датаграмная связь сокетов ненадежна. В этом примере программа клиента ждет неопределенное время, если сообщение никогда не достигает сервера, или, если ответ сервера никогда не возвращается. Более автоматическое решение могло бы использовать select (см. Раздел 8.6 [Ждущий ввод - вывод]), чтобы установить период блокировки по времени для ответа, и в этом случае или снова послать сообщение, или выключить сокет и выйти.

11.10 Демон Inetd

Мы объяснили выше, как написать программу сервера, которая реализует собственное ожидание. Такой сервер должен уже выполниться для любого соединения с ним.

Другой способ обеспечивать обслуживание портов для Internet состоит в том, чтобы использовать в программе для ожидания демона inetd. Inetd - программа, которая выполняется все время и ждет (используя select) сообщения на заданном наборе портов. Когда она получает сообщение, она принимает соединение (если стиль сокета запрашивает соединение), и тогда запускает дочерний процесс, чтобы выполнить соответствующую программу сервера. Вы определяете порты и их программы в файле "/etc/inetd.conf".

Inetd Серверы

Написание программы сервера, которая будет выполнена inetd очень просто. Каждый раз когда кто-то запрашивает соединение с соответствующим портом, стартует новый процесс сервера. Соединение уже существует в это время; гнездо доступно как описатель стандартного ввода и как описатель стандартного вывода (описатели 0 и 1) в процессе сервера. Так что программа сервера может начинать читать и писать данные сразу же. Часто программа нуждается только в обычных средствах ввода-вывода; фактически, универсальная программа-фильтр, которая не знает ничего относительно сокетов, может работать как сервер потока байтов, запускаемая inetd.

Вы можете также использовать inetd для серверов, которые используют стили связи без установления логического соединения. Для этих серверов, inetd не пробует принять соединение, так как никакое соединение не возможно. Она только начинает программу сервера, которая может читать входящий датаграмный пакет из описателя 0. Программа сервера может обрабатывать один запрос и выходить, или читать большое количество запросов. Вы должны определить, который из этих двух методов использования сервера удобен Вам при конфигурации inetd.

Конфигурирование inetd

Файл "/etc/inetd.conf " сообщает inetd, какие порты ожидает какой сервер для обработки пакетов. Обычно каждый вход в файле - это строка, но Вы можете разбивать его на много строк, если все, кроме первой строки входа, начинаются с пропуска. Строки, которые начинаются с "*" являются комментариями.

Имеются два стандартных входа в "/etc/inetd.conf":

    ftp stream tcp nowait root /libexec/ftpd ftpd
    talk dgram udp wait root /libexec/talkd talkd

Вход имеет формат:

    service style protocol wait username program arguments

Поле service говорит, какое обслуживание обеспечивает эта программа. Это должно быть имя обслуживания, определенного в "/etc/services". Inetd использует обслуживание, чтобы решить какой порт слушать для этого входа.

style и protocol определяют стиль связи и протокол для использования ожидающего сокета. Стиль должен иметь имя стиля связи, преобразованного в строчные буквы и с удаленным "SOCK_", например, "stream" или "dgram". Протокол должен быть один из протоколов, перечисленных в "/etc/protocols". Типичные имена протокола - "tcp" для соединений потока байтов и "udp" для ненадежных датаграмм.

Поле wait должно быть, или "wait" или "nowait". "wait" используется, если стиль не требует установления логического соединения и обрабатывает многократные запросы. "nowait" используется, когда необходимо,чтобы inetd начинал новый процесс для каждого сообщения, или запроса, который приходит. Если используется соединение, то wait должен быть "nowait".

user - имя пользователя, под которым сервер должен выполняться. Inetd выполняется под пользователя root, так что она может устанавливать ID пользователей дочерних процессов произвольно. Лучше избегать использования "root" для пользователя; но некоторые серверы, типа Telnet и FTP, читают username и пароль самостоятельно. Эти серверы должны быть запущены под пользователя root изначально, так как они могут регистрировать потоки данных передаваемых по сети.

program вместе с аргументами определяет команду, для запуска сервера. Это должно быть абсолютное имя файла, определяющее исполняемый файл для выполнения. Аргументы состоят из любого числа отделенных пробелами слов, которые станут аргументами командной строки программы.

Первое слово в аргументах - нуль, который должен быть именем программы непосредственно (каталоги sans).

Если Вы редактируете "/etc/inetd.conf", то Вы можете указывать необходимость повторного чтения файла для inetd и сообщения нового содержимого, посылая inetd сигнал SIGHUP. Вы будете должны использовать ps, чтобы определить ID процесса inetd, поскольку оно не фиксировано.

11.11 Опции сокетов.

Этот раздел описывает, как читать или установить различные опции, которые изменяют поведение сокетов и их основных протоколов связи.

Когда Вы манипулируете опциями сокета, Вы должны определить, к какому уровню они относятся, то есть применяется ли опция к интерфейсу сокета, или к интерфейсу протокола связи низшего уровня.

Функции Опций сокета.

Имеются функции для исследования и изменения опций сокета. Они объявлены в "sys/socket.h".

       int getsockopt (int socket, int level, int optname, void *optval, size_t *optlen_ptr)

Функция getsockopt получает информацию относительно значения опции optname заданного уровня для указанного сокета.

Значение опции сохранено в буфере, на который указывает optval. Перед обращением, Вы должны обеспечить в * optlen_ptr размер этого буфера; по возвращении, он содержит число байтов информации, фактически сохраненной в буфере.

Большинство опций интерпретирует буфер optval как одиночное значение int.

Фактически возвращаемое значение getsockopt - 0 при успехе и -1 в случае неудачи. В переменной errno отражены следующие возможные причины:

EBADF

аргумент socket - не допустимый описатель файла.

ENOTSOCK

дескриптор socket - не сокет.

ENOPROTOOPT

Optname не имеет смысла для данного уровня.

    int setsockopt (int socket, int level, int optname, void *optval, size_t optlen)

Эта функция используется, чтобы установить опцию сокета optname заданного уровня для указанное сокета. Значение опции передано в буфере optval, который имеет размер optlen.

Возвращаемое значение и коды ошибки для setsockopt такие же как для getsockopt.

Опции уровня сокета.

    int SOL_SOCKET  (константа)

Используйте эту константу, как аргумент level для getsockopt или setsockopt, чтобы манипулировать опциями уровня сокета, описанными в этом разделе.

Вот таблица имен опций уровня сокета; все они определены в файле "sys/socket.h".

    SO_DEBUG

Эта опция переключает запись информации об отладке в основных модулях протокола. Значение имеет тип int; значение отличное от нуля означает "да".

    SO_REUSEADDR

Эта опция говорит bind (см. Раздел 11.3.2 [Установка Адреса]) разрешить многократное использование местных адресов для этого сокета. Если Вы пользуетесь этой опцией, Вы можете фактически иметь два сокета с тем же самым номером порта Internet. Необходимость в этой опции возникает, потому что что некоторые протоколы Internet с более высоким уровнем, такие FTP, требуют, чтобы Вы многократно использовали тот же самый номер сокета.

Значение имеет тип int; значение отличное от нуля означает "да".

    SO_KEEPALIVE

Эта опция указывает, должен ли основной протокол периодически передавать сообщения на соединенный сокет. Если адресат будет не в состоянии отвечать на эти сообщения, соединение рассматривается разорваным. Значение имеет тип int; значение отличное от нуля означает "да".

    SO_DONTROUTE

Эта опция контролирует при посылке сообщения обход нормальных средств посылки сообщений. Если она установлена, сообщения посылаются непосредственно сетевому интерфейсу. Значение имеет тип int; значение отличное от нуля означает "да".

    SO_LINGER

Эта опция определяет то, что должно случиться, в случае, когда сокет предоставляющий надежную выдачу все еще не передал сообщения, до закрытия; см. Раздел 11.7.2 [Закрытие сокета]. Значение имеет тип struct linger.

    struct linger  (тип данных)

Эта структура имеет следующие элементы:

    int l_onoff

Это поле интерпретируется как булевское. Оно отлично от нуля, если блокировка закрыта, пока данные не переданы, или период блокировки по времени не истек.

    int l_linger

Определяет период блокировки по времени, в секундах.

    SO_BROADCAST

Эта опция определяет могут ли датаграммы быть широковещательно переданы из сокета. Значение имеет тип int; значение отличное от нуля означает "да".

    SO_OOBINLINE

Если эта опция установлена, данные вне потока получаемые в сокет помещаются в нормальную входную очередь. Она разрешает читать их, используя read или recv без того, чтобы определить флаг MSG_OOB. См. Раздел 11.8.8 [Данные вне потока]. Значение имеет тип int; значение отличное от нуля означает "да".

    SO_SNDBUF

Эта опция получает или устанавливает размер буфера вывода. Значение size_t является его размером в байтах.

    SO_RCVBUF

Эта опция получает или устанавливает размер буфера ввода. Значение size_t является его размером в байтах.

    SO_STYLE
    SO_TYPE

Эта опция может использоваться только с getsockopt. Она используется, чтобы получить стиль связи сокета. SO_TYPE - историческое имя, а SO_STYLE - привилегированное имя в GNU. Значение имеет тип int, и обозначает стиль связи; см. Раздел 11.2 [Стили Связи].

    SO_ERROR

Эта опция может использоваться только с getsockopt. Она используется, чтобы сбросить состояние ошибки сокета. Значение int представляет собой предыдущее состояние ошибки.

11.12 База данных Сетей

Много систем приходят с базой данных, которая записывает список сетей, известных разработчику системы. Она обычно сохраняется или в файле "/etc/networks" или в блоке преобразования имен. Эта база данных полезна для маршрутизации программ типа route, но бесполезна для программ, которые просто связываются по сети. Функции предназначенные для обращения к этой базе данных описаны в "netdb.h ".

    struct netent                 (тип данных)

Этот тип данных используется, чтобы представить информацию относительно входов в базе данных сетей.

Он имеет следующие элементы:

    char *n_name

Это - "официальное" имя сети.

    char **n_aliases

Это альтернативные имена для сети, представляемые как вектор строк. Пустой указатель завершает массив.

    int n_addrtype

Это - тип сетевого номера; он всегда равно AF_INET для сетей Internet.

    unsigned long int n_net

Это сетевой номер. Сетевые числа представлены в главном порядке байтов; см. Раздел 11.5.5 [Порядок Байтов].

    struct netent * getnetbyname (const char *name)     (функция)

Getnetbyname функция возвращает информацию относительно сети именованной name. Она возвращает пустой указатель, если нет никакой сети.

    struct netent * getnetbyaddr (long net, int type)   (функция)

Функция getnetbyaddr возвращает информацию относительно сети указанного типа с номером net. Вы должны определить значение AF_INET для аргумента type для сети Internet.

    getnetbyaddr возвращает пустой указатель в случае отсутствия такой сети.

Вы можете также просматривать базу данных сетей, используя setnetent, getnetent, и endnetent. Будьте внимательным в использовании этих функций, потому что они не предназначены для повторного использования.

    void setnetent (int stayopen)

Эта функция открывает базу данных сетей.

Если аргумент stayopen является отличным от нуля, то она устанавливает флаг так, чтобы последующие обращения к getnetbyname или getnetbyaddr не закрыли базу данных. Это делается для большей эффективности, если Вы вызываете эти функции несколько раз, избегая повторного открытия базы данных для каждого обращения.

    struct netent * getnetent (void)

Эта функция возвращает следующий вход в базе данных сетей. Она возвращает пустой указатель, если не имеется больше входов.

    void endnetent (void)

Эта функция закрывает базу данных сетей.

12. Интерфейс Терминала низкого уровня

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

Большинство функций в этой главе использует на описатели файлов. См. Главу 8 [ввод-вывод низкого уровня] для получения более подробной информации, чем описатель файла является и как открыть описатель файла для устройства терминала.

12.1 Идентификация Терминалов

Функции, описанные в этой главе работают с файлами, которые соответствуют терминальному устройству. Используя функцию isatty, Вы можете выяснять, связан ли описатель файла с терминалом.

Прототипы и для isatty и ttyname объявлены в файле "unistd.h".

    int isatty (int filedes)  (функция)

Эта функция возвращает 1, если filedes - описатель файла, связанный с открытым терминальным устройством, и 0 в противном случае.

Если описатель файла связан с терминалом, Вы может получать связанное имя файла, используя функцию ttyname. См. также ctermid функцию, описанную в Разделе 24.7.1 [Идентификация Терминала].

    char * ttyname (int filedes)  (функция)

Если описатель файла filedes связан с терминальным устройством, то функция ttyname возвращает указатель на статически размещенную строку с нулевым символом в конце, содержащую имя файла терминала. Значение - пустой указатель, если описатель файла не связан с терминалом, или имя файла не может быть определено.

12.2 Очереди Ввода-вывода

Многие из функций в этом разделе обращаются к очередям ввода и вывода терминального устройства. Эти очереди определяют форму буферизации внутри ядра, которое независит от буферизации, выполненной потоками ввода-вывода (см. Главу 7 [ввод-вывод на Потоках]).

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

Размер входной очереди терминала описан параметрами _POSIX _MAX_INPUT и MAX_INPUT; см. Раздел 27.6 [Ограничения для Файлов]. Если управление потоком данных ввода допускается установкой бита режима ввода IXOFF (см. Раздел 12.4.4 [Входные Режимы]), драйвер терминала передает символы STOP и START на терминал, когда необходимо предохранить очередь от переполнения. Иначе, ввод можно потерять при сликшом быстром поступлении данных с терминала. (Это маловероятно, если Вы печатаете ввод вручную!)

Очередь вывода терминала подобна входной очереди, но для вывода она содержит символы, которые написаны процессами, но еще не передавались на терминал. Если управление потоком данных вывода допускается установкой бита режима ввода IXON (см. Раздел 12.4.4 [Входные Режимы]), то драйвер терминала удовлетворяет условиям символов STOP и STOP, посланных терминалом, чтобы остановиться и перезапустить передачу вывода.

Очистка входной очереди терминала означает отбрасывание любых символов, которые были получены, но еще не прочитаны. Аналогично, очистка очереди вывода терминала означает отбрасывание любых символов, которые написаны, но еще не передавались.

12.3 Два Стиля Ввода: каноническиий и неканонический.

Системы POSIX поддерживают два базисных режима ввода: каноническиий и неканоническиий.

В каноническом входном режиме ввод с терминала обрабатывается построчно, строка оканчивается символом перевода строки ("\n"), символами EOF, или EOL. Ввод не может закончиться, пока вся строка не напечатана пользователем.

В каноническиом входном режиме, операционная система обеспечивает входное редактирование: символы ERASE и KILL интерпретируются особо для выполнения операций редактирования внутри текущей строки текста. См. Раздел 12.4.9.1 [Редактирование Символов].

Константы _POSIX_MAX_CANON и MAX_CANON указывают максимальное число байтов, которые могут появляться в одиночной строке каноническиого ввода. См. Раздел 27.6 [Ограничения для Файлов].

В неканоническиом входном режиме обработки символы не сгруппированы в строки, и ERASE, и KILL не выполняются. Степень детализации, с которой байты читаются в неканоническиом входном режиме, управляется MIN и TIME. См. Раздел 12.4.10 [Неканоническиий Ввод].

Большинство программ использует каноническиий входной режим, потому что это дает пользователю способ редактировать входную строку. Обычной причиной для использования неканонического режима является необходимость программой принимать одиночно-символьные команды или предоставлять собственные средства редактирования.

Выбор каноническиого или неканоническиого ввода управляется флагом ICANON в элементе _lflag в struct termios. См. Раздел 12.4.7 [Автономные режимов].

12.4 Режимы Терминала

Этот раздел описывает различные атрибуты терминала, которые управляют вводом и выводом. Функции, структуры данных, и символические константы объявлены в файле " termios.h".

Типы Данных Режима Терминала

Вся коллекция атрибутов терминала сохранена в структуре типа struct termios. Эта структура используется функциями tcgetattr и tcsetattr, чтобы читать и установливать атрибуты.

    struct termios  (тип данных)

Это структура, которая записывает все атрибуты ввода-вывода терминала. Структура включает по крайней мере следующие элементы:

    tcflag_t c_iflag

Битовая маска, определяющая флаги для режимов ввода; см. Раздел 12.4.4 [Режимы Ввода].

    tcflag_t c_oflag

Битовая маска, определяющая флаги для режимов вывода; см. Раздел 12.4.5 [Режимы вывода].

    tcflag_t c_cflag

Битовая маска, определяющая флаги для режимов управления; см. Раздел 12.4.6 [Режимы Управления].

    tcflag_t c_lflag

Битовая маска, определяющая флаги для автономных режимов; см. Раздел 12.4.7 [Автономные режимы].

    cc_t c_cc[NCCS]

Массив, определяющий, символы связаные с различными функциями управления; см. Раздел 12.4.9 [Специальные Символы].

Структура struct termios также содержит элементы, которые кодируют скорости передачи ввода и вывода, но представление не преднано. См. Раздел 12.4.8 [Быстродействие Строки], для того, как исследовать и сохранять значения быстродействия.

Следующие разделы описывают подробности элементов структуры struct termios.

    tcflag_t      (тип данных)

Это тип integer unsigned, используемый, чтобы представить различные битовые маски для флагов терминала.

    cc_t                          (тип данных)

Это тип integer unsigned, используемый, чтобы представить символы, связанные с различными функциями управления терминала.

    int NCCS      (макрос)

Значение этой макрокоманды - число элементов в массиве c_cc.

Функции Режимов Терминала

    int tcgetattr (int filedes, struct termios *termios_p) (функция)

Эта функция используется, чтобы исследовать атрибуты терминального устройства с описателем файла filedes. Атрибуты возвращены в структуре на которую указывает termios_p.

В случае успеха tcgetattr возвращает 0. Возвращаемое значение -1 указывает ошибку. Следующие условия ошибки errno определены для этой функции:

EBADF

filedes аргумент - не допустимый описатель файла.

ENOTTY

filedes не связан с терминалом.

       int tcsetattr (int filedes, int when, const struct termios *termios_p) (функция)

Эта функция устанавливает атрибуты устройства терминала с описателем файла filedes. Новые атрибуты принимаются из заданной структуры.

Аргумент when определяет, как поступать с вводом и выводом, уже поставленным в очередь. Это может быть одно из следующих значений:

TCSANOW

Делать изменения немедленно.

TCSADRAIN Делать изменения после ожидания, пока весь поставленный в очередь вывод не написан.

Вы должны обычно использовать эту опцию при изменении параметров, которые воздействуют на вывод.

TCSAFLUSH

подобен TCSADRAIN, но отбрасывает любой поставленный в очередь ввод.

TCSASOFT

Это бит флага, который Вы можете добавлять к любому из вышеупомянутых вариантов. Значение должно запретить чередование состояния аппаратных средств терминала. Это BSD расширение; он не имеет никакого эффекта в не-BSD системах.

Если эта функция вызывается из фонового процесса на терминале управления, то все процессы в группе этого процесса посылается сигнал SIGTTOU, как будто процесс пробовал запись на терминал. Исключение ­ если процесс вызова непосредственно игнорирует или блокирует сигналы SIGTTOU См. Главу 24 [Управление заданиями].

При успехе tcsetattr возвращает 0. Возвращаемое значение -1 указывает ошибку. Следующие условия ошибки errno определены для этой функции:

EBADF

filedes аргумент - не допустимый описатель файла.

ENOTTY

filedes не связан с терминалом.

EINVAL

или значение аргумента when не допустимо, или ошибка с данными в аргументе termios_p .

Хотя tcgetattr и tcsetattr определяют устройство терминала описателем файла, атрибуты - сами устройства терминала непосредственно, а не описателя файла. Это означает, что эффект изменения атрибутов терминала постоянен; если другой процесс открывает файл терминала позже, он будет видеть измененные атрибуты.

Аналогично, если одиночный процесс имеет многократные или дублированные описатели файла для того же самого устройства терминала, замена атрибутов терминала воздействует на ввод и вывод для всех этих описателей файлов.

Установка Режимов Терминала Правильно

Когда Вы устанавливаете режимы терминала, Вы должны сначала вызвать tcgetattr, чтобы получить текущие режимы специфического устройства терминала, и изменять только те режимы, в которых Вы действительно заинтересованы. Для сохранения результата испольуйте tcsetattr.

Плохая практика просто инициализировать структуру struct termios для выбранного набора атрибутов и передавать ее непосредственно в tcsetattr. Ваша программа может быть выполнена через годы на системах, которые поддерживают элементы, не зарегистрированные в этом руководстве.

Более того различные терминальные устройства могут требовать различных установок режима. Так что Вы должны избегать слепого копирования атрибутов из одного терминального устройства в другое.

Вот пример того, как устанавливать один флаг (ISTRIP) в структуре struct termios при правильном сохранении всех других данных в структуре:

                int
                set_istrip (int desc, int value)
                {
                        struct termios settings;
                        int result;
                        result = tcgetattr (desc, &settings);
                        if (result < 0)
                        {
                                perror ("error in tcgetattr");
                                return 0;
                        }
                        settings.c_iflag &= ~ISTRIP;
                        if (value)
                                settings.c_iflag |= ISTRIP;
                        result = tcgetattr (desc, &settings);
                        if (result < 0)
                        {
                                perror ("error in tcgetattr");
                                return;
                        }
                        return 1;
                }

Режимы Ввода

Этот раздел описывает флаги атрибутов терминала, которые управляют аспектами низкого уровня входной обработки: обработка ошибок контроля четности, сигналы останова, управление потоком данных, символы и RET и LFD.

Все эти флаги - биты в c_iflag элементе структуры struct termios. Элемент - integer, и Вы изменяете флаги, использующие операторами &, | и ^. Не пробуйте определять все значение для c_iflag_instead, изменяйте только специфические флаги и оставляйте остаток нетронутым (см. Раздел 12.4.3 [Режимы Установки]).

INPCK если этот бит установлен, допускается входная проверка контроля четности. Если он не установлен, никакой проверки ошибок четности нет; символы просто передаются к приложению.

Если этот бит установлен, то при ошибки контроля четности ситуация зависит от того, установлены ли биты IGNPAR или PARMRK. Если никакой из этих битов не установлен, байт с ошибкой контроля четности, передается приложению как символ '\0'.

IGNPAR если этот бит установлен, любой байт с ошибкой четности игнорируется. Это полезно только, если также установлен INPCK.

PARMRK если этот бит установлен, входные байты с ошибкой четности или ошибкой синхронизации отмечены при передачи программе. Этот бит значим только, когда INPCK установлен, и IGNPAR не установлен.

Ошибочные байты отмечены двумя предшествующими байтами, 377 и 0. Таким образом, программа фактически читает три байта для одного ошибочного байта, полученного от терминала.

Если допустимый байт имеет значение 0377, и ISTRIP (см. ниже) не установлен, программа может путать его с префиксом, который отмечает ошибку контроля по четности. Так что допустимый байт 0377 передается программе как два байта, 0377 0377.

ISTRIP если этот бит установлен, допустимые входные байты урезаны до семи битов; иначе, все восемь битов доступны для программ.

IGNBRK если этот бит установлен, условия прерывания игнорируются.

Условие прерывания определено в контексте асинхронной последовательной передачи данных как ряд нулевых битов длиннее байта.

BRKINT если этот бит установлен, и IGNBRK не установлен, условие прерывания очищает ввод терминала и очереди вывода и поднимает сигнал SIGINT для группы приоритетного процесса, связанной с терминалом.

Если ни BRKINT ни IGNBRK не установлены, условие прерывания передается приложению как одиночный символ '\0', если PARMRK не установлен, или иначе как последовательность из трех символов "\377", "\0","\0".

IGNCR Если этот бит установлен, символы возврата каретки ("\r") отброшены на вводе. Отбрасывание возврата каретки может быть полезно на терминалах, которые посылают, и возврат каретки и перевод строки при нажатии клавишу RET.

ICRNL Если этот бит установлен, и IGNCR не установлен, символ возврата каретки (" \r ") передается к приложению как символ перевода строки (" \n ").

INLCR Если этот бит установлен, символ перевода строки ("\n") передан к приложению как символ возврата каретки ("\r").

IXOFF Если этот бит установлен, допускается start/stop контроль над вводом. Другими словами, компьютер посылает символы STOP и START по мере необходимости, чтобы предотвратить ввод от прибытия быстрее чем программы его читают. Идея состоит в том, что фактические аппаратные средства терминала, которые генерируют входные данные, отвечают на символ STOP, приостанавливая передачу, а на символ START, продолжая передачу. См. Раздел 12.4.9.4 [Start/Stop Символы].

IXON Если этот бит установлен, допускается start/stop контроль над выводом. Другими словами, если компьютер получает символ STOP, он приостанавливает вывод, пока символ START не получен.

В этом случае, символы STOP и START никогда не переданы прикладной программе. Если этот бит не установлен, то START и STOP, могут читаться как обычные символы. См. Раздел 12.4.9.4 [Start/Stop Символы].

IXANY если этот бит установлен, любой входной символ перезапускает вывод, когда вывод был приостановлен символом STOP. Иначе, только символ START перезапускает вывод.

IMAXBEL если этот бит установлен, то при заполнении буфера ввода терминала посылается символ BEL (код 007) на терминал (звенит звонок).

Режимы вывода

Этот раздел описывает флаги терминала и поля, которые управляют, как выводимые символы транслируются и дополняются для дисплея. Все они содержатся в c_oflag элементе struct termiosstructure.

C_oflag элемент непосредственно - integer, и Вы изменяете флаги и поля, используюя операторы &, |, и ^. Не пробуйте определять все значения для c_oflag , изменяйте только специфические флаги и оставляйте остаток нетронутым (см. Раздел 12.4.3 [Режимы Установки]).

       int OPOST  (макрос)

Если этот бит установлен, выходные данные обрабатываются некоторым неопределенным способом так, чтобы они отобразились соответственно на устройстве терминала. Это обычно включает отображение символа перевода строки ("\n") на пару перевод строки и возврат каретки.

Если этот бит не установлен, символы передаются как есть.

Следующие три бита возможности BSD, и они не имеют никакого эффекта на не-BSD системах. На всех системах, они эффективны только, если OPOST установлен.

    int ONLCR  (макрос)

Если этот бит установлен, преобразовывает символ перевода строки на выводе в пару символов: возврат каретки, сопровождаемый переводом строки.

    int OXTABS  (макрос)

Если этот бит установлен, преобразовывает cимволы табуляции на выводе в соответствующее число пробелов, чтобы эмулировать табулятор через каждые восемь столбцов.

    int ONOEOT  (макрос)

Если этот бит установлен, отбрасываются символы C-d (код 004) на выводе. Эти символы заставляют терминалы разъединиться.

Режимы Управления

Этот раздел описывает флаги терминала и поля, которые управляют параметрами, обычно связываемыми с асинхронной последовательной передачей данных. Эти флаги могут не иметь смысла для других видов портов терминала (типа псевдо-терминального сетевого соединения). Все они содержатся в элементе c_cflag структуры struct termios.

Элемент с_сflag непосредственно integer, и Вы изменяете флаги и поля, используюя операторы &, |, и ^. Не пробуйте определять все значения для c_cflag взамен, изменяйте только специфические флаги и оставляйте остаток нетронутым (см. Раздел 12.4.3 [Режимы Установки]).

CLOCAL если этот бит установлен, это указывает, что терминал соединен "локально", и что строки состояния модема должны игнорироваться.

Если этот бит не установлен и Вы вызываете open без O_NONBLOCK набора флагов, оpen блокируются, пока соединение модема не установлено.

Если этот бит не установлен, а модем обнаружен, то посылается сигнал SIGHUP группе процесса управления терминала. Обычно, это вызывает завершение процессов; см. Главу 21 [Обработка Сигнала]. Чтение из терминала после разъединения дает условие конца файла, а запиись вызывает ошибку EIO. Для очищения бита устройство терминала должно быть закрыто и вновь открыто.

HUPCL Если этот бит установлен, то использующие это терминальное устройство процессы по окончании или закрытии файла вызовут разъединение с модемом.

CREAD Если этот бит установлен, ввод может читаться из терминала. Иначе, поступающий ввод отбрасывается.

CSTOPB если этот бит установлен, используются два стоповых бита. Иначе, используется только один стоповый бит .

PARENB если этот бит установлен, допускается порождение и обнаружение бита контроля четности. См. Раздел 12.4.4 [Входные Режимы], для уточнения информации о том, как обрабатываются входные ошибки контроля четности .

Если этот бит не установлен, никакой бит контроля четности не добавлен при выводе символов, и входные символы не проверены по четности.

PARODD Этот бит полезен только, если PARENB установлен. Если PARODD установлен, используется проверка на нечетность, иначе используется проверка на четность.

Флаги режима управления также включают поле для числа битов на символ. Вы можете использовать CSIZE макрокоманду как маску, чтобы извлечь значение, примерно так:

    settings.c_cflag & CSIZE.

CSIZE Это маска для числа битов на символ.

CS5 Это определяет пять битов на байт.

CS6 Это определяет шесть битов на байт.

CS7 Это определяет семь битов на байт.

CS8 Это определяет восемь битов на байт.

CCTS_OFLOW Если этот бит установлен, он дает возможность управления потоком выводимых данных, основанного на CTS (RS232 протокол).

    CRTS_IFLOW

Если этот бит установлен, он дает возможность управления потоком вводимых данных, основанного на RTS (RS232 протокол).

MDMBUF если этот бит установлен, он предоставляет возможность курьерскому управлению потоком выводимых данных.

Автономные режимы

Этот раздел описывает флаги для элемента c_lflag структуры struct termios. Эти флаги управляют аспектами с более высоким уровнем ввода, чем флаги режимов, описанные в Разделе 12.4.4 [Входные Режимы], типа отображения на экране, сигналов, и выбора каноническиого или неканоническиого ввода.

Элемент с_lflag непосредственно - integer, и Вы изменяете флаги и поля, используюя операторы &, |, и ^. Не пробуйте определять все значения для c_lflag, а изменяйте только специфические флаги и оставляйте остаток нетронутым (см. Раздел 12.4.3 [Режимы Установки]).

ICANON Этот бит, устанавливает канонический режим обработки ввода. Иначе, ввод обработан в неканоническом режиме. См. Раздел 12.3 [Каноническиий или Нет].

ECHO если этот бит установлен, допускается отображение входных символов обратно на терминал.

ECHOE если этот бит установлен, происходит отображение стирания ввода символом ERASE, уничтожается последний символ в текущей строке. Иначе, уничтоженный символ переотображен, чтобы показать что случилось.

Этот бит управляет только поведением дисплея; ICANON бит управляет фактическим распознаванием символа ERASE и стиранием ввода.

ECHOK Этот бит дает возможность специальному отображению символа KILL. Имеются два способа, которыми это может быть выполнено. Лучший способ стирать всю строку. Худший способ перемещение в новую строку после отображения на экране символа KILL.

Если этот бит не установлен, символ KILL отображается, точно как это было бы, если не было символа KILL. Ввод предшестующий символу KILL не отображается.

ECHONL если этот бит установлен, и бит ICANON также установлен, то символ перевода строки ("\n") отображается на экране, даже если бит ECHO не установлен.

ISIG Этот бит управляет распознаванием символов INTR, QUIT, и SUSP. Функции, связанные с этими символами выполняются только, если этот бит установлен.

В каноническиом или неканоническиом входном режиме терминал может не реагировать на интерпретацию этих символов.

IEXTEN Этот бит подобен ISIG, но контролирует определенные реализацией специальные символы. Если он установлен, он мог бы отменять заданное по умолчанию поведение для ICANON и ISIG флагов автономного режима, и IXON и флагов режима ввода IXOFF.

NOFLSH Обычно, символы INTR, QUIT, и SUSP очищают очереди ввода и вывода для терминала. Если этот бит установлен, очереди, не очищаются.

TOSTOP если этот бит установлен, и система поддерживает управление заданиями, то фоновыми процессами генерируются SIGTTOU сигналы, при попытке записи на терминал. См. Раздел 24.4 [Доступ к Терминалу].

Следующие биты представляют собой расширения BSD; библиотека GNU определяет эти символы в любой системе, но использоваться они будут только в системах BSD.

ECHOKE на системах BSD этот бит выбирает между двумя альтернативными способами отображения символа KILL, когда ECHOK установлен. Если ECHOKE установлен, то символ KILL стирает целую экранную строку; иначе, символ KILL перемещается в следующую экранную строку. Установка ECHOKE не производит никакого эффекта, если бит ECHOK не установлен.

ECHOPRT этот бит дает возможность отображать символ ERASE заданным способом.

ECHOCTL если этот бит установлен, управляющие символы отображаются с "^" сопровождаемый соответствующим текстовым символом. Таким образом, control-A отображается на экране как "^A".

ALTWERASE Этот бит определяет сколько символов WERASE должен стереть. Символ WERASE стирает обратно к началу слова.

Если этот бит является неустановлен, то началом слова считается первый символ после символов пробел. Если бит установлен, то начало слова - алфавитно-цифровой символ или подчеркивание после символа, который не является одним из этих.

FLUSHO - бит, который переключается, когда пользователь печатает символ DISCARD. Если этот бит установлен то вывод не производится.

NOKERNINFO Установка этого бита отключает обработку символа STATUS.

PENDIN если бит установлен, то это означает, что имеется строка ввода, которая должна быть перпечатана.

Печать символа REPRINT устанавливает этот бит; бит остается установленным, пока перепечатывание не закончено. См. Раздел 12.4.9.2 [Редактирование в BSD].

Быстродействие Строки

Быстродействие строки терминала сообщает компьютеру как быстро читать и писать данные относительно терминала.

Если терминал соединен с реальной последовательной строкой, быстродействие терминала, которое Вы определяете фактически управляет строкой, если она не соответствует собственной концепции терминала относительно быстродействия, связь не работает.

Реальные последовательные порты воспринимают только некоторые стандартные скорости. Специфические аппаратные средства не могут поддерживать все стандартные скорости. При определении нулевой скорости "зависает " соединение телефонного вызова по номеру и выключаются сигналы управления модема.

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

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

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

    speed_t cfgetospeed (const struct termios *termios_p)  (функция)

Эта функция возвращает быстродействие строки вывода, сохраненное в структуре * termios_p.

    speed_t cfgetispeed (const struct termios *termios_p)  (функция)

Эта функция возвращает входное быстродействие строки, сохраненное в структуре * termios_p.

    int cfsetospeed (struct termios *termios_p, speed_t speed) (функция)

Эта функция сохраняет быстродействие в * termios_p как быстродействие вывода. Нормальное возвращаемое значение 0; значение -1 указывает ошибку. Если speed - не быстродействие, cfsetospeed возвращает -1.

    int cfsetispeed (struct termios *termios_p, speed_t speed) (функция)

Эта функция сохраняет быстродействие в * termios_p как входное быстродействие. Нормальное возвращаемое значение - 0; значение -1 указывает ошибку. Если speed - не быстродействие, cfsetospeed возвращает -1.

    int cfsetspeed (struct termios *termios_p, speed_t speed) (функция)

Эта функция сохраняет быстродействие в * termios_p, и как скорости вывода и как скорость ввода. Нормальное возвращаемое значение 0; значение -1 указывает ошибку. Если speed не быстродействие, cfsetspeed возвращает -1. Эта функция расширение 4.4 BSD.

    speed_t  (тип данных)

Speed_t тип данных integer unsigned, используемый, чтобы представить скорости строки.

Функции cfsetospeed и cfsetispeed выдают ошибки только для значений быстродействия, которые система просто не может обрабатывать. Если Вы определяете значение быстродействия, которое является в основном допустимым, то эти функции выполняются успешно. Но они не проверяют, что специфическое аппаратное устройство может фактически поддерживать заданное быстродействие, они не знают, для которого устройства Вы планируете устанавливать быстродействие. Если Вы используете tcsetattr, чтобы установить быстродействие специфического устройства в значение, которое оно не может обрабатывать, tcsetattr, возвращает -1.

Примечание Переносимости: В библиотеке GNU, функции выше принимают скорости, измеряемые в битах в секунду как ввод, и возвращает значения быстродействия, измеряемые в битах в секунду. Другие библиотеки требуют, чтобы скорости были обозначены специальными кодами. Для POSIX. 1 переносимости, Вы должны использовать один из следующих символов, чтобы представить быстродействие; их точные числовые значения зависят от системы, но каждое имя, имеет фиксированное значение: B110 замещает 110 бит\сек, B300 для 300 бит\сек, и так далее. Нет никакого переносимого способа представить любое быстродействие, но это единственые скорости, которые последовательные строки могут поддерживать.

                B0  B50  B75  B110  B134  B150  B200
                B300  B600  B1200  B1800  B2400  B4800
                B9600  B19200  B38400

BSD определяет два дополнительных символа быстродействия как побочные результаты исследования: EXTA - побочный результат исследования для B19200, и EXTB - побочный результат исследования для B38400. Эти побочные результаты исследования устаревшие.

    int cfmakeraw (struct termios *termios_p)  (функция)

Эта функция обеспечивает простой способ установить * termios_p для того, что традиционно вызвалось "необрабатываемый режим" в BSD. Она делает следующее:

       termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
                        |INLCR|IGNCR|ICRNL|IXON);
       termios_p->c_oflag &= ~OPOST;
       termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
       termios_p->c_cflag &= ~(CSIZE|PARENB);
       termios_p->c_cflag |= CS8;

Специальные Символы

В каноническиом вводе, драйвер терминала распознает ряд специальных символов, которые выполняют различные функции управления. Они включают символ ERASE (обычно DEL) для редактирования ввода, и другие символы редактирования. Символ INTR (обычно C-c) для посылки сигнала SIGINT, и другие сигнальные символы, могут быть доступны либо в каноническиом либо в неканоническиом входном режиме. Все эти символы описаны в этом разделе.

Специфические используемые символы определены в c_cc элементе struct termios структуры. Этот элемент массив; каждый элемент определяет символ для специфической роли. Каждый элемент имеет символическую константу, которая замещает индекс этого элемента например, INTR - индекс элемента, который определяет символ INTR, так что сохранение "=" в termios.c_cc [INTR] определяет "=" как символ INTR.

На некоторых системах Вы можете отключать специфическую специальную символьную функцию, определяя значение _POSIX_VDISABLE. Это значение неравно любому возможному символьному коду. См. Раздел 27.7 [Опции Файлов] для получения более подробной информации, о том как сообщить операционной системе, которую Вы используете, поддержку _POSIX_VDISABLE.

Символы для Входного Редактирования

Эти специальные символы активны только в каноническиом входном режиме. См. Раздел 12.3 [Каноническиий или Нет].

       int VEOF           (макрос)

Это индекс для символа EOF в специальном массиве управляющих символов. Termios.c_cc [VEOF] содержит символ непосредственно.

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

Обычно, символ EOF - C-d.

       int VEOL           (макрос)

Это индекс для символа EOL в специальном массиве управляющих символов. Termios.c_cc [VEOL] содержит символ непосредственно.

Символ EOL распознаваем только в каноническиом входном режиме. Он действует как признак конца строки, точно так же как символ перевода строки. Символ EOL не отбрасывается; он читается как последний символ во входной строке.

Вы не должны использовать символ EOL, чтобы заставить RET закончить строку. Просто установите ICRNL флаг. Фактически, это заданное по умолчанию состояние.

    int VERASE

Это индекс для символа ERASE в специальном массиве управляющих символов. Termios.c_cc [VERASE] содержит символ непосредственно.

Символ ERASE распознаваем только в каноническиом входном режиме. Когда пользователь печатает символ удаления, предыдущий печатаемый символ отбрасывается. (Если терминал генерирует многобайтовые символьные последовательности, может быть отброшен больше чем один байт ввода.) Символ ERASE непосредственно отбрасывается.

Обычно, символ ERASE - DEL.

    int VKILL  (макрос)

Это индекс для символа KILL в специальном массиве управляющих символов. Termios.c_cc [VKILL] содержит символ непосредственно.

Символ KILL распознаваем только в каноническиом входном режиме. Когда пользователь печатает этот символ, все содержимое текущей строки ввода отбрасывается. Сам символ непосредственно также отбрасывается.

Символ KILL - обычно C-u.

         12.4.9.2 BSD Расширения Редактирующих Символов

Эти специальные символы активны только в каноническиом входном режиме. См. Раздел 12.3 [Каноническиий или Нет]. Они являются расширениями BSD; библиотека GNU определяет символы на любой системе, но работают они только в системе BSD.

    int VEOL2  (макрос)

Это индекс для символа EOL2 в специальном массиве управляющих символов. Termios.c_cc [VEOL2] содержит символ непосредственно.

Символ EOL2 работает точно так же как символ EOL (см. выше), но это может быть другой символ. Таким образом, Вы можете определять два символа, чтобы завершить входную строку, устанавливая EOL как один из них и EOL2 как другой.

    int VWERASE  (макрос)

Это индекс для символа WERASE в специальном массиве управляющих символов. Termios.c_cc [VWERASE] содержит символ непосредственно.

Символ WERASE распознаваем только в каноническиом входном режиме. Он стирает слово предшествующего ввода.

    int VREPRINT  (макрос)

Это индекс для символа REPRINT в специальном массиве управляющих символов. Termios.c_cc [VREPRINT] содержит символ непосредственно.

Символ REPRINT распознаваем только в каноническом входном режиме. Он перепечатывает текущую входную строку.

    int VLNEXT  (макрос)

Это индекс для символа LNEXT в специальном массиве управляющих символов. Termios.c_cc [VLNEXT] содержит символ непосредственно.

Символ LNEXT распознаваем только, когда IEXTEN установлен. Он отключает редактирующее значение следующего печатающегося символа. Это аналог команды C-q в Emacs.

Символ LNEXT - обычно C-v.

Символы вызывающие Сигналы

Эти специальные символы могут быть активны или в каноническом или в неканоническом входном режиме, но только, когда флаг ISIG установлен (см. Раздел 12.4.7 [Автономные режимы]).

    int VINTR  (макрос)

Это индекс для символа INTR в специальном массиве управляющих символов. Termios.c_cc [VINTR] содержит символ непосредственно.

INTR символ (прерывания) вызывает сигнал SIGINT для всех процессов в приоритетной работе, связанной с терминалом. Символ INTR непосредственно отбрасывается. См. Главу 21 [Обработка Сигнала] , для получения более подробной информации.

Обычно, символ INTR - C-c.

    int VQUIT  (макрос)

Это индекс для символа QUIT в специальном массиве управляющих символов. Termios.c_cc [VQUIT] содержит символ непосредственно.

Символ QUIT вызывает сигнал SIGQUIT для всех процессов в приоритетной работе, связанной с терминалом. Символ QUIT непосредственно отбрасывается. См. Главу 21 [Обработка Сигнала], дл получения более подробной информации.

Обычно, символ QUIT - C-\.

    int VSUSP  (макрос)

Это - нижний индекс для символа SUSP в специальном массиве управляющих символов.

Termios.c_cc [VSUSP] содержит символ непосредственно.

SUSP (приостанавливающий) символ, распознается только, если реализация поддерживает управление заданиями (см. Главу 24 [Управление заданиями]). Он посылает сигнал SIGTSTP всем процессам в приоритетной работе, связанной с терминалом. Символ SUSP непосредственно отбрасывается. См. Главу 21 [Обработка Сигнала], для получения более подробной информации.

Обычно, символ SUSP - C-z.

Некоторые приложения отключают нормальную интерпретацию символа SUSP. Если ваша программа делает это, она должна обеспечить какой-нибудь другой механизм для пользователя, чтобы остановить работу. Когда пользователь вызывает этот механизм, программа должна послать сигнал SIGTSTP группе процесса, а не только процессу непосредственно. См. Раздел 21.6.2 [Передача сигналов для Другого Процесса].

    int VDSUSP  (макрос)

Это индекс для символа DSUSP в специальном массиве управляющих символов. Termios.c_cc [VDSUSP] содержит символ непосредственно.

DSUSP (приостанавливающий) символ распознается только, если реализация поддерживает управление заданиями (см. Главу 24 [Управление заданиями]). Он посылает сигнал SIGTSTP, подобно символу SUSP, но не сразу, а когда программа пробует читать его как ввод.

Не все системы с управлением заданиями поддерживают DSUSP; только системы BSD. См. Главу 21 [Обработка Сигнала], для получения более подробной информации.

Обычно, символ DSUSP - C-y.

Специальные Символы для Управления потоком данных

Эти специальные символы могут быть активны или в каноническиом или в неканоническиом входном режиме, но их использование управляется флагами IXON и IXOFF (см. Раздел 12.4.4 [Входные Режимы]).

    int VSTART

Это индекс для символа START в специальном массиве управляющих символов. Termios.c_cc [VSTART] содержит символ непосредственно.

Символ START используется, чтобы поддерживать режимы ввода IXON и IXOFF. Если IXON установлен, то получение символа START продолжает приостановленный вывод; символ START непосредственно отбрасывается. Если IXOFF установлен, то система может также передавать символы START на терминал.

Обычное значение для символа START - C-q. Вы не можете изменить это значение, т. к. аппаратные средства могут настаивать на использовании C-q независимо от того, что Вы определяете.

    int VSTOP  (макрос)

Это индекс для символа STOP в специальном массиве управляющих символов. Termios.c_cc [VSTOP] содержит символ непосредственно.

Символ STOP используется, чтобы поддерживать IXON и IXOFF режимы ввода. Если IXON установлен, то получение символа STOP заставляет риостановить вывод; символ STOP непосредственно отбрасывается. Если IXOFF установлен, система может также передавать символы STOP на терминал, предохранняя входную очередь от переполнения.

Обычное значение для символа STOP - C-s.

Другие Специальные Символы

Имеются два дополнительных специальных символа, которые являются значимыми на системах BSD.

    int VDISCARD  (макрос)

Это индекс для символа DISCARD в специальном массиве управляющих символов. Termios.c_cc [VDISCARD] содержит символ непосредственно.

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

    int VSTATUS  (макрос)

Это индекс для символа STATUS в специальном массиве управляющих символов. Termios.c_cc [VSTATUS] содержит символ непосредственно.

Символ STATUS должен распечатать сообщение состояния относительно того, как текущий процесс выполняется.

Символ STATUS распознаваем только в каноническом режиме. Это специфическое решение задачи, так как значение символа STATUS ничего не делает со вводом.

Неканоническиий Ввод

В неканоническом входном режиме, специальные символы редактирования типа ERASE и KILL игнорируются.

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

Неканоническиий режим предлагает специальные параметры называемые MIN и TIME для управления тем как долго ждать доступного ввода. Вы можете даже использовать их, чтобы избежать ожидания, чтобы возвратиться немедленно с любым вводом, или без ввода.

MIN и TIME сохранены в элементах c_cc массива, который является элементом struct termiosstructure. Каждый элемент этого массива имеет специфическую роль, и каждый элемент имеет символическую константу, которая замещает индекс этого элемента. VMIN и VMAX ­ имена для индексов TIME и MIN.

    int VMIN

Это индекс для MIN в c_cc массиве. Таким образом, в termios.c_cc [VMIN] значение MIN хранится непосредственно.

MIN значим только в неканоническиом входном режиме; он определяет минимальное число байтов, которые должны быть доступны во входной очереди для возвращения read.

    int VTIME  (макрос)

Это индекс для TIME в массиве c_cc. Таким образом, в termios.c_cc [VTIME] значение TIME представлено непосредственно.

TIME значим только в неканоническиом входном режиме; он определяет, как долго ждать ввод перед возвращением, в единицах по 0.1 секунды.

Значения MIN и TIME взаимодействуют, чтобы определить критерий для возвращения read; их точные значения зависят, от того какой из них является отличным от нуля. Имеются четыре возможных случая:

O И MIN и TIME - нули.

В этом случае, read всегда возвращается немедленно со столькими символами, сколько доступно в очереди, до запрошенного числа. Если никакой ввод не является немедленно доступным, read возвращает нуль.

O MIN - нуль, но TIME имеет значение отличное от нуля.

В этом случае, read ждет в течение времени TIME доступного ввода; доступность одиночного байта должна быть достаточна, чтобы удовлетворить запрос read. По возвращению функция принимает значение обозначающее количество доступных символов вплоть до запрошенного числа. Если никакой ввод не является доступным в заданное время, read возвращает нуль.

O TIME - нуль, но MIN имеет значение отличное от нуля.

В этом случае, read ждет, пока по крайней мере MIN байтов станут доступны в очереди. И возвращает количество доступных символов. Read может возвращать больше, чем MIN символов.

O И TIME и MIN отличны от нуля.

В этом случае, TIME определяет, как долго ждать следующего ввода после каждого входного символа. Read ждет, пока или MIN байтов прочитаны, или TIME истекает без дальнейшего ввода.

Read может не возвращать никакого ввода, если TIME истекает прежде, чем появится первый входной символ. Read может возвращать больше, чем MIN символов, если в очереди их больше чем MIN.

Что случается, если MIN - 50, и Вы запрашиваете прочитать только 10 байтов? Обычно, read ждет до 50 байтов в буфере (т. е. пока условие ожидания не удовлетварено), и тогда читает 10 из них, оставляя другие 40 буферизированными в операционной системе для последующего обращения read.

Примечание Переносимости: На некоторых системах, места MIN и TIME в массиве фактически такие же как у EOF и EOL. Это не вызывает никакой серьезной проблемы, потому что MIN и TIME используется только в неканоническиом вводе, а EOF и EOL используются только в каноническиом вводе, но это не совсем хорошо. Библиотека GNU резервирует отдельные места для них.

12.5 Функции управления Строкой

Эти функции выполняют разнообразные управляющие действия на терминальных устройствах. Что касается доступа к терминалу, они обрабатываются подобно выполнению вывода: если любая из этих функций используется фоновым процессом на управляемом терминале, то обычно всем процессам в группе процесса посланы сигналы SIGTTOU. Исключение, если вызывающий процесс непосредственно игнорирует или блокирует сигналы SIGTTOU, тогда операция выполняется, и никакой сигнал не посылается. См. Главу 24 [Управление заданиями].

    int tcsendbreak (int filedes, int duration)

Эта функция генерирует условие прерывания, передавая поток нулевых битов на терминал, связанный с описателем файла filedes. Продолжительность прерывания управляется аргументом duration. Если нуль, то продолжительность - между 0.25 и 0.5 секундами. Значение значения отличного от нуля зависит от операционной системы.

Эта функция ничего не делает, если терминал не асинхронный последовательный порт данных.

Возвращаемое значение обычно нуль. В случае ошибки, возвращается значение -1. Следующие условия ошибки errno определены для этой функции:

EBADF

filedes - не допустимый описатель файла.

ENOTTY

filedes не связан с устройством терминала.

    int tcdrain (int filedes)  (функция)

Tcdrain функция ждет, пока весь поставленный в очередь вывод на терминал filedes не будет передан.

Возвращаемое значение обычно нуль. В случае ошибки, возвращается значение -1. Следующие условия ошибки errno определены для этой функции:

EBADF

filedes - не допустимый описатель файла.

ENOTTY

filedes не связан с устройством терминала.

EINTR

операция была прервана сигналом. См. Раздел 21.5 [Прерванные Примитивы].

    int tcflush (int filedes, int queue)  (функция)

Эта функция используется, чтобы очистить очереди ввода и/или вывода, связанные с файлом терминала filedes. Аргумент queue определяет, какую очередь очищать, и может принимать одно из следующих значений:

TCIFLUSH очистить любые полученные, но еще не прочитанные, входные данные.

TCOFLUSH очистите любые выходные данные, которые написаны, но еще не переданный.

TCIOFLUSH Очистить, поставленный в очередь и ввод и вывод.

Возвращаемое значение обычно нуль. В случае ошибки, возвращаеися значение -1. Следующие условия ошибки errno определены для этой функции:

EBADF

filedes - не допустимый описатель файла.

ENOTTY

filedes не связан с устройством терминала.

EINVAL

плохое значение было обеспечено как аргумент queue.

tcflush не совсем удачное название для это функции, так как термин "поток" обычно используется для совершенно другой операции. К сожалению, имя tcflush исходит из POSIX, и мы не можем изменять его.

    int tcflow (int filedes, int action)  (функция)

Эта функция используется, чтобы выполнить операции в отношении XON/XOFF управления потоком данных на файле терминала, заданном filedes.

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

    TCOOFF Приостанавливают передачу вывода.
    TCOON Рестарт передачи вывода.
    TCIOFF Передача символа STOP.
    TCION Передача символа START.

Для получения более подробной информации о символах STOP и START, см. Раздел 12.4.9 [Специальные Символы].

Возвращаемое значение обычно нуль. В случае ошибки, возвращается значение -1. Следующие условия ошибки errno определены для этой функции:

EBADF

filedes - не допустимый описатель файла.

ENOTTY

filedes не связан с устройством терминала.

EINVAL

плохое значение был обеспечен как аргумент action.

12.6 Пример Неканонического Режима

Вот пример программ, который показывает как Вы можете устанавливать устройство терминала для чтения одиночных символов в неканоническиом входном режиме, без ECHO.

                #include 
                #include 
                #include 
                #include 
                struct termios saved_attributes;
                void
                reset_input_mode (void)
                {
                        tcsetattr (STDIN_FILENO, TCSANOW,
                &saved_attributes);
                }
                void
                set_input_mode (void)
                {
                        struct termios tattr;
                        char *name;
                        if (!isatty (STDIN_FILENO))
                                {
                                        fprintf (stderr,
                "Not a terminal.\n");
                                        exit (EXIT_FAILURE);
                                }
                        tcgetattr (STDIN_FILENO, &saved_attributes);
                        atexit (reset_input_mode);
                        tcgetattr (STDIN_FILENO, &tattr);
                        tattr.c_lflag &= ~(ICANON|ECHO);
                        tattr.c_cc[VMIN] = 1;
                        tattr.c_cc[VTIME] = 0;
                        tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
                }
                int
                main (void)
                {
                        char c;
                        set_input_mode ();
                        while (1)
                                {
                                        read (STDIN_FILENO, &c, 1);
                                        if (c == '\004')        /* C-d */
                                                break;
                                        else
                                                putchar (c);
                                }
                        return EXIT_SUCCESS;
                }

Эта программа осторожна, она восстанавливает первоначальные режимы терминала перед выходом или завершением с сигналом. Она использует функцию atexit (см. Раздел 22.3.3 [Очистка на Выходе]) чтобы удостовериться в установке режимов.

Оболочка, как предполагается, заботится о сбросе режимов терминала, когда процесс остановлен или продолжается; см. Главу 24 [Управление заданиями]. Но некоторые существующие оболочки фактически не делают это, так что Вы можете установить обработчики для сигналов управления заданиями, которые сбрасывают режимы терминала. Вышеупомянутый пример делает так.


Обсудить данную тему на нашем форуме "Все о Linux"