Если аргумент name не содержит значение NULL, в нем должна находиться строка, идентифицирующая аргумент в параметре arg. Эта строка становится частью любого выводимого этими функциями сообщения об ошибке.
Аргумент flags предоставляет возможность управления работой функций getInt() и getLong(). Изначально они ожидают получения строк, содержащих десятичные целые числа со знаком. Путем логического сложения (|) в аргументе flags нескольких констант вида GN_*, определенных в листинге 3.5, можно выбрать иную основу для преобразования и ограничить диапазон чисел неотрицательными значениями или значениями больше нуля.
Листинг 3.5. Заголовочный файл для get_num.c
lib/get_num.h
#ifndef GET_NUM_H
#define GET_NUM_H
#define GN_NONNEG 01 /* Значение должно быть >= 0 */
#define GN_GT_0 02 /* Значение должно быть > 0 */
/* По умолчанию целые числа являются десятичными */
#define GN_ANY_BASE 0100 /* Можно использовать любое основание –
наподобие strtol(3) */
#define GN_BASE_8 0200 /* Значение выражено в виде восьмеричного числа */
#define GN_BASE_16 0400 /* Значение выражено в виде шестнадцатеричного числа */
long getLong(const char *arg, int flags, const char *name);
int getInt(const char *arg, int flags, const char *name);
#endif
lib/get_num.h
Реализации функций getInt() и getLong() показаны в листинге 3.6.
Хотя аргумент flags позволяет принудительно проверять диапазон допустимых значений, рассмотренный в основном тексте, в некоторых случаях в примерах программ такие проверки не запрашиваются, даже если этот запрос кажется вполне логичным. Отказ от проверки диапазона в подобных случаях позволяет не только проводить эксперименты с правильным использованием системных вызовов и вызовов библиотечных функций, но и наблюдать, что произойдет, если будут предоставлены недопустимые аргументы. В приложениях, созданных для реальной работы, обычно вводятся более строгие проверки аргументов командной строки.
Листинг 3.6. Функции для анализа числовых аргументов командной строки
lib/get_num.c
#include
#include
#include
#include
#include
#include "get_num.h"
static void
gnFail(const char *fname, const char *msg, const char *arg, const char *name)
{
fprintf(stderr, "%s error", fname);
if (name!= NULL)
fprintf(stderr, " (in %s)", name);
fprintf(stderr, ": %s\n", msg);
if (arg!= NULL && *arg!= '\0')
fprintf(stderr, " offending text: %s\n", arg);
exit(EXIT_FAILURE);
}
static long
getNum(const char *fname, const char *arg, int flags, const char *name)
{
long res;
char *endptr;
int base;
if (arg == NULL || *arg == '\0')
gnFail(fname, "null or empty string", arg, name);
base = (flags & GN_ANY_BASE)? 0: (flags & GN_BASE_8)? 8:
(flags & GN_BASE_16)? 16: 10;
errno = 0;
res = strtol(arg, &endptr, base);
if (errno!= 0)
gnFail(fname, "strtol() failed", arg, name);
if (*endptr!= '\0')
gnFail(fname, "nonnumeric characters", arg, name);
if ((flags & GN_NONNEG) && res < 0)
gnFail(fname, "negative value not allowed", arg, name);
if ((flags & GN_GT_0) && res <= 0)
gnFail(fname, "value must be > 0", arg, name);
return res;
}
long
getLong(const char *arg, int flags, const char *name)
{
return getNum("getLong", arg, flags, name);
}
int
getInt(const char *arg, int flags, const char *name)
{
long res;
res = getNum("getInt", arg, flags, name);
if (res > INT_MAX || res < INT_MIN)
gnFail("getInt", "integer out of range", arg, name);
return (int) res;
}
lib/get_num.c
В этом разделе мы рассмотрим, как создавать портируемые системные программы. В нем будут представлены макросы проверки возможностей и стандартные типы данных системы, определенные спецификацией SUSv3, а затем рассмотрены некоторые другие вопросы портируемости.
3.6.1. Макросы проверки возможностей
Поведение API системных вызовов и вызовов библиотечных функций регулируется различными стандартами (см. раздел 1.3). Одни стандарты определены организациями стандартизации, такими как Open Group (Single UNIX Specification), а другие — двумя исторически важными реализациями UNIX: BSD и System V, выпуск 4 (и объединенным System V Interface Definition).
Иногда при создании портируемого приложения могут понадобиться различные заголовочные файлы для предоставления только тех значений (констант, прототипов функций и т. д.), которые отвечают конкретному стандарту. Для этого определены несколько
Как один из вариантов, можно задать макрос в исходном коде программы до включения каких-либо заголовочных файлов:
#define _BSD_SOURCE 1
В качестве альтернативы можно воспользоваться ключом — D компилятора языка C:
$ cc — D_BSD_SOURCE prog.c