象征性的错误号为String [英] Symbolic errno to String

查看:140
本文介绍了象征性的错误号为String的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有一个命令行工具,将采取象征性的错误号,例如 EINVAL 并打印相应的字符串,无效参数

我想避免不必找到EINVAL是我的系统上值22,然后使用 $ PERROR 22

在理想情况下,我可以写的东西像


  

$ errorcommand EINVAL结果
  无效的参数结果
  $



解决方案

据我所知,还没有一个标准的工具,没有工作。在一个层面上,它不会特别难写一个 - 最凌乱的部分找到正确的文件来分析(是经常的,但绝不总是/usr/include/sys/errno.h中),然后取从该数据做名称数字的映射。我还没有发现使用枚举值,而不是价值观的#define一个系统,但它可能只是一个时间问题。这也是一个有争议的问题是否产生三重由令牌数量(EINTR等),令牌名(EINTR等)和错误消息(被中断的系统调用等),还是只使用数量和名称,它留给'字符串错误()供应信息。


就像我说的,它不是特别难。我已经有一个叫做错误号接受了纯数值并打印相应的错误信息的程序:

  $错误号1:10 20
1:不允许操作
2:没有这样的文件或目录
3:没有这样的过程
4:中断系统调用
5:输入/输出错误
6:设备未配置
7:参数列表太长
8:执行格式错误
9:坏的文件描述符
10:无子进程
20:不是一个目录
$

我写了一个Perl脚本和黑客程序处理符号错误号太:

  $错误号1:4 EINTR ENOTDIR
1(EPERM):操作不被允许
2(ENOENT):没有这样的文件或目录
3(ESRCH):没有这样的过程
4(EINTR):中断系统调用
EINTR(4):中断系统调用
ENOTDIR(20):不是一个目录
$

它不处理符号的错误号范围(读者练习)。

generrno.pl

 #!的/ usr / bin中/ perl的-w

#@(#)$编号:generrno.pl,V 1.1 2010/02/07 18点39分18秒jleffler精通jleffler $

#生成错误号常数表根据给定文件(S)使用严格的;我的%SYMLIST;
我的$ maxsymlen = 0;
我的$ maxmsglen = 0;而(小于&GT)
{
    接下来,除非M%^ \\ S *#\\ S *定义\\ S +(E [A-Z0-9a-Z] +)\\ S +(\\ d +)\\ S * / \\ * \\ S *([A-ZA-Z ] * \\ S)\\ S * \\ * /%;
    $ SYMLIST {$ 1美元} = {数=> $ 2,消息=> $ 3};
    $ maxsymlen =长度($ 1),如果长度($ 1)> $ maxsymlen;
    $ maxmsglen =长度($ 3)如果长度($ 3)> $ maxmsglen;
}我的$ FORMAT = sprintf的{%% - %DS %% - %DS %% - 5S %% - %DS} \\ n,$ maxsymlen + 3,$ maxsymlen + 1,$ maxmsglen + 2;我的foreach $键(排序键%SYMLIST)
{
    我的名字$ = {QQ$键};
    我的$符号= QQ {$键,};
    我的$数= QQ {$ SYMLIST {$关键} - > {}号,};
    我的$消息= {QQ$ SYMLIST {$关键} - > {}消息};    printf的格式$,$名称,$符号,$号码,$消息;
}

errno.c

  / *
@(#)文件:$ RCSfile:errno.c,V $
@(#)版本:$修订:$ 2.2
@(#)上次修改:$日期:2010/02/07 19点22分37秒$
@(#)目的:与errno值或名称打印消息
@(#)作者:J-莱弗勒
@(#)版权所有:(C)JLSS 2003,2005,2008,2010
* // *制表位= 4 * /#定义MAIN_PROGRAM/ *需要O / S特定的消息以及POSIX消息* /
//#如果__STDC_VERSION__> = 199901L
//#定义_XOPEN_SOURCE 600
//#其他
//#定义_XOPEN_SOURCE 500
//#ENDIF / * * __STDC_VERSION__ /#包括LT&;&文件ctype.h GT;
#包括LT&;&errno.h中GT;
#包括LT&;&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&string.h中GT;
#包括LT&;&unistd.h中GT; / * getopt的()在MacOS X 10.2 * /
#包括stderr.h
#包括range.htypedef结构err_info
{
    为const char * errsym; / *错误符号 - EINTR* /
    INT的差错编号; / *错误号 - EINTR * /
    INT errdef; / *错误定义 - 4 * /
    为const char * ERRMSG; / *错误信息 - 被中断的系统调用* /
} err_info;/ *
**使用生成generrno.h:
** perl的generrno.pl /usr/include/sys/errno.h中> generrno.h
**注:列表必须在符号名的字母顺序排序
* /
静态常量err_info err_msgs [] =
{
#包括generrno.h
};静态为const char usestr [] =[-qv] [ - ] LO [:喜] ...;DIM的#define(X)(sizeof的(X)/ sizeof的(*(X)))静态常量err_info * err_nums [DIM(err_msgs)];皮棉的#ifndef
/ * prevent过分进取,从消除ID字符串优化* /
为const char jlss_id_errno_c [] =@(#)$编号:errno.c,V 2.2 2010/02/07 19时22分37秒jleffler精通$;
#ENDIF / *皮棉* /静态INT cmp_err_number(常量无效* V1,常量无效* V2)
{
    INT E1 =(*((常量err_info * const的*)V1)) - >的差错编号;
    INT E2 =(*((常量err_info * const的*)V2)) - >的差错编号;
    返回(E1 - E2);
}静态无效map_numbers(无效)
{
    INT I;    对于(i = 0; I< D​​IM(err_msgs);我++)
        err_nums [I] =安培; err_msgs [I]
    的qsort(err_nums,DIM(err_nums)的sizeof(* err_nums),cmp_err_number);
}静态为const char * err_symbol(INT NUM)
{
    为const char *符号=<&未知GT;
    err_info lookfor = {0,NUM,0,0};
    err_info * lookptr =安培; lookfor;
    常量err_info **发现= bsearch(安培; lookptr,err_nums,DIM(err_nums)的sizeof(* err_nums),cmp_err_number);
    如果(找到!= 0)
        符号=(*找到) - > errsym;
    返回(符号);
}静态INT cmp_err_symbol(常量无效* V1,常量无效* V2)
{
    为const char * S1 =((常量err_info *)V1) - GT; errsym;
    为const char * S2 =((常量err_info *)V2) - GT; errsym;
    返回(的strcmp(S1,S2));
}静态INT pr_string_errno(为const char *阿根廷,INT qflag)
{
    INT ESTAT = EXIT_SUCCESS;
    err_info lookfor = {精氨酸,0,0,0};
    常量err_info *发现= bsearch(安培; lookfor,err_msgs,DIM(err_msgs)的sizeof(* err_msgs),cmp_err_symbol);
    如果(找到== 0)
    {
        err_remark(无法识别的符号%s \\ n,ARG);
        ESTAT = EXIT_FAILURE;
    }
    否则,如果(qflag == 0)
        的printf(%S(%D):%S \\ n,阿根廷,发现 - >的差错编号,发现 - > ERRMSG);
    返回(ESTAT);
}静态INT pr_number_errno(为const char *阿根廷,INT qflag)
{
    INT ESTAT = EXIT_SUCCESS;
    罗长;
    长喜;
    为const char * ENDP;
    长味精;    ENDP = numeric_range(阿根廷,和放大器;不料,&安培;喜);
    如果(ENDP == ARG)
        err_remark(指定(%S)无效的范围 - 应该是LO [:喜] \\ n,ARG);
    否则,如果(* ENDP!='\\ 0')
        err_remark(非数字字符(%C)范围后,'%s'的\\ n
                    (?isprint判断((无符号字符)* ENDP)* ENDP:'?'),ARG);
    其他
    {
        对于(味精= L​​O;味精< =喜;味精++)
        {
            字符* msgtxt =字符串错误(MSG);
            如果(msgtxt == 0)
            {
                err_remark(没有消息的错误号=%LD \\ N,味精);
                ESTAT = EXIT_FAILURE;
            }
            否则,如果(qflag == 0)
                的printf(%LD(%S):%S \\ n,味精,err_symbol(MSG),msgtxt);
        }
    }
    返回(ESTAT);
}静态INT pr_errno(字符*阿根廷,INT qflag)
{
    INT ESTAT;
    如果(因而isalpha(* ARG))
        ESTAT = pr_string_errno(阿根廷,qflag);
    其他
        ESTAT = pr_number_errno(阿根廷,qflag);
    返回(ESTAT);
}INT主(INT ARGC,字符** argv的)
{
    INT I;
    INT选择;
    INT nstat;
    INT ESTAT = EXIT_SUCCESS;
    INT qflag = 0;
    INT nflag = 0;    err_setarg0(的argv [0]);    map_numbers();    而((选择= getopt的(ARGC,ARGV,qV0:1:2:3:4:5:6:7:8:9:))= EOF)
    {
        开关(OPT)
        {
        案例'Q':
            qflag = 1;
            打破;
        案例'V':
            err_version(ERRNO,$修订:$ 2.2($日期:2010/02/07 19点22分37秒$));
            打破;        情况下0:
        情况1':
        案2:
        案3:
        案4:
        案5:
        案6:
        案7:
        案例'8':
        案例'9':
            / * getopt的()可能是不适合这个工作的工具! * /
            nstat = pr_errno(OPTARG-2,qflag);
            如果(ESTAT == EXIT_SUCCESS)
                ESTAT = nstat;
            nflag = 1;
            打破;        默认:
            err_usage(usestr);
            打破;
        }
    }    如果(OPTIND> = ARGC和放大器;&安培; nflag == 0)
        err_usage(usestr);    对于(i = OPTIND; I< ARGC,我++)
    {
        nstat = pr_errno(的argv [I],qflag);
        如果(ESTAT == EXIT_SUCCESS)
            ESTAT = nstat;
    }    返回(ESTAT);
}


在code需要一些配套文件 - stderr.h,range.h,range2.c和stderrmin.c(该stderr.c我通常使用,这对处理系统日志额外花俏的简化版本和写入文件,而不是描述符文件指针)。

stderr.h

  / *
@(#)文件:$ RCSfile:stderr.h,V $
@(#)版本:$修订:$ 9.2
@(#)上次修改:$日期:2009/03/06 6时52分26秒$
@(#)用途:用于标准错误函数头文件
@(#)作者:J-莱弗勒
@(#)版权所有:(C)JLSS 1989-93,1996-99,2003,2005-09
@(#)产品:产品:
* /的#ifndef STDERR_H
#定义STDERR_H#IFDEF MAIN_PROGRAM
皮棉的#ifndef
/ * prevent过分进取,从消除ID字符串优化* /
为const char jlss_id_stderr_h [] =@(#)$编号:stderr.h,V 9.2 2009/03/06 6时52分26秒jleffler精通$;
#ENDIF / *皮棉* /
#万一下列块#ifdef HAVE_CONFIG_H
的#includeconfig.h中
#ENDIF / * * HAVE_CONFIG_H /#包括LT&;&stdio.h中GT;
#包括LT&;&STDARG.H GT;#IFDEF __GNUC__
#定义PRINTFLIKE(N,M)__attribute __((格式(printf的,N,M)))
#定义noreturn的()__attribute __((noreturn))的
#其他
#定义PRINTFLIKE(N,M)/ *如果只* /
#定义noreturn的()/ *如果只* /
#ENDIF / * * __GNUC__ // * - 定义为错误处理* /枚举{ERR_STAT = 1}; / *默认退出状态* /枚举{ERR_DEFAULT = 0×0000}; / *默认标志* /
枚举{ERR_NOFLUSH = 0×0001}; / *不要冲洗打开的文件* /
枚举{ERR_EXIT = 0x0004单元}; / *退出 - 不返回* /
枚举{ERR_ABORT = 0×0008}; / *中止 - 不返回* /
枚举{ERR_STAMP = 0×0020}; / *时间戳信息* /
枚举{ERR_NOARG0 =×0040}; / *不打印为arg0 preFIX * /
枚举{ERR_PID = 0x0080}; / *包括PID = NNNNN信息* /
枚举{ERR_ERRNO = 0100}; / *包括系统错误* /#IFDEF USE_STDERR_SYSLOG
/ *相关使用syslog定义* /
枚举{ERR_LOG_EMERG = 0x01000}; / *系统不可用* /
枚举{ERR_LOG_ALERT = 0x02000}; / *必须立即采取行动* /
枚举{ERR_LOG_CRIT = 0x04000}; / *临界条件* /
枚举{ERR_LOG_ERR = 0x08000}; / *错误条件* /
枚举{ERR_LOG_WARNING = 0x10000的}; / *警告状态* /
枚举{ERR_LOG_NOTICE =地址0x20000}; / *正常的,但意义状态* /
枚举{ERR_LOG_INFO = 0x40000}; /* 情报的 */
枚举{ERR_LOG_DEBUG = 0x80000}; / *调试级消息* /
枚举{ERR_LOG_LEVEL_HI = ERR_LOG_EMERG | ERR_LOG_ALERT | ERR_LOG_CRIT | ERR_LOG_ERR};
枚举{ERR_LOG_LEVEL_LO = ERR_LOG_WARNING | ERR_LOG_NOTICE | ERR_LOG_INFO | ERR_LOG_DEBUG};
枚举{ERR_LOG_LEVEL = ERR_LOG_LEVEL_HI | ERR_LOG_LEVEL_LO};
#ENDIF / * * USE_STDERR_SYSLOG // * - 标志的标准组合* /枚举{ERR_REM = ERR_DEFAULT};
枚举{ERR_ERR = ERR_EXIT};
枚举{ERR_ABT = ERR_ABORT};
枚举{ERR_LOG = ERR_STAMP | ERR_PID};
枚举{ERR_SYSREM = ERR_REM | ERR_ERRNO};
枚举{ERR_SYSERR = ERR_ERR | ERR_ERRNO};/ * - 的argv的最大记录长度[0];额外被截断* /枚举{ERR_MAXLEN_ARGV0 = 63};/ * - 全球定义* /EXTERN为const char err_format1 []; / *%S \\ N - 一个字符串参数* /
EXTERN为const char err_format2 []; / *%s%S \\ N - 两个字符串参数* /EXTERN为const char * err_getarg0(无效);
EXTERN无效err_setarg0(为const char * argv0);的extern FILE * err_stderr(FILE * FP);
EXTERN为const char * err_rcs_string(为const char * S,字符*缓冲区,为size_t buflen);EXTERN无效err_abort(为const char *格式,...)PRINTFLIKE(1,2)不返回的();
EXTERN无效err_error(为const char *格式,...)PRINTFLIKE(1,2)不返回的();
EXTERN无效err_error1(为const char * S1)不返回的();
EXTERN无效err_error2(为const char * S1,为const char * S2)不返回的();
EXTERN无效err_help(为const char * use_str,为const char * hlp_str)不返回的();
EXTERN无效err_helplist(为const char * use_str,为const char * const的* help_list)不返回的();
EXTERN无效err_internal(为const char *功能,为const char * MSG)不返回的();
EXTERN无效err_logmsg(FILE * FP,诠释旗帜,INT ESTAT,为const char *格式,...)PRINTFLIKE(4,5);
EXTERN无效err_print(INT标志,INT ESTAT,为const char *格式,va_list的参数);
EXTERN无效err_printversion(为const char *程序,为const char * verinfo);
EXTERN无效err_remark(为const char *格式,...)PRINTFLIKE(1,2);
EXTERN无效err_remark1(为const char * S1);
EXTERN无效err_remark2(为const char * S1,为const char * S2);
EXTERN无效err_report(INT标志,INT ESTAT,为const char *格式,...)PRINTFLIKE(3,4);
EXTERN无效err_syserr(为const char *格式,...)PRINTFLIKE(1,2)不返回的();
EXTERN无效err_syserr1(为const char * S1)不返回的();
EXTERN无效err_syserr2(为const char * S1,为const char * S2)不返回的();
EXTERN无效err_sysrem(为const char *格式,...)PRINTFLIKE(1,2);
EXTERN无效err_sysrem1(为const char * S1);
EXTERN无效err_sysrem2(为const char * S1,为const char * S2);
EXTERN无效err_usage(为const char * usestr)不返回的();
EXTERN无效err_version(为const char *程序,为const char * verinfo)不返回的();EXTERN INT err_getlogopts(无效); / *获取默认日志选项* /
EXTERN INT err_setlogopts(INT new_opts); / *设置默认日志选项* /#IFDEF USE_STDERR_FILEDESC
EXTERN INT err_use_fd(INT FD); / *使用文件描述符* /
#ENDIF / * * USE_STDERR_FILEDESC /
#IFDEF USE_STDERR_SYSLOG
/ *在有疑问的情况下,使用两个logopts和设备零* /
EXTERN INT err_use_syslog(INT logopts,INT设施); / *配置/使用的syslog()* /
#ENDIF / * * USE_STDERR_SYSLOG // *
** JL 2003-07-31:安全说明。
**问:鉴于err_remark(ABC \\ n)和err_remark1(ABC)
**产生相同的输出,什么时候应该使用err_remark1()
**代替err_remark()?
**回答1:琐事 - 当你不能把换行符的字符串中。
**答2:安全 - 当参数包含用户输入和可能,
**因此,包含转换说明等。code在
** err_remark()不会(也不能)验证是否具有
**通过正确的参数在转换说明
**格式字符串。
**答3:惯性 - 迁移使用此言code时,()。
**
**泛化:当您使用具有'为const char *格式功能
**在上面的原型,确保你的code是全面负责的
**格式字符串,以避免安全漏洞。不允许用户
**提供弦,除非你严格地检查它事先。
* /#ENDIF / * * STDERR_H /

range.h

  / *
@(#)文件:$ RCSfile:range.h,V $
@(#)版本:$修订:$ 1.8
@(#)上次修改:$日期:2008年2月11日7时39分36秒$
@(#)目的:范围分析函数声明
@(#)作者:J-莱弗勒
@(#)版权所有:(C)JLSS 1997,2005,2007-08
@(#)产品:产品:
* // *制表位= 4 * /的#ifndef RANGE_H
#定义RANGE_H#IFDEF MAIN_PROGRAM
皮棉的#ifndef
/ * prevent过分进取,从消除ID字符串优化* /
为const char jlss_id_range_h [] =@(#)$编号:range.h,V 1.8 2008年2月11日7时39分36秒jleffler精通$;
#ENDIF / *皮棉* /
#ENDIF / * * MAIN_PROGRAM // *
** parse_range():解析非负数的范围内。
**
**给定一个字符串,parse_range()返回对应低和高值
**由字符串指定的范围。例如:
**输入:低高
** 23 23 23
** 23-25​​ 23 25
** 23- 23 0
** -23 0 23
**比其他任何定界符 - 之前或多项终止后''
**扫描,但逗号被跳过。返回指针指向字符后
**最后一个字符解析(其可以是或可以不是'\\ 0')如果成功。
**否则,返回null。
**
**习惯用法:
**
**为const char * PTR = source_string;
**为const char * NXT;
**而((NXT = parse_range(PTR,和放大器;不料,&安培;!喜))= 0)
** {
**如果(NXT == PTR)
** err_error(无效的字符串范围(%S)\\ n,source_string);
** use_range(LO,HI);
** PTR = NXT;
**}
* /
EXTERN为const char * parse_range(为const char * str中,长*卤味,长*喜);/ *
** numeric_range():解析号码,正或负的范围内。
**
**输入:低高
** 23 23 23
** -23 -23 -23
** 23:25 23 25
** 23..25 23 25
** -23 ..- 25 -25 -23
** -23..25 -23 25
** 23 ..- 25 -25 23
**返回指针'\\ 0'的字符串结尾,如果确定,将* LO和*您好,
**和担保* LO< = *您好。
**否则,返回指针开始的字符串,并且不设置* LO或*您好。
**
**习惯用法:
**
**为const char * PTR = source_string;
**为const char * NXT;
**而((NXT = numeric_range(PTR,和放大器;不料,&安培;!喜))= 0)
** {
**如果(NXT == PTR)
** err_error(无效的字符串范围(%S)\\ n,source_string);
** use_range(LO,HI);
** PTR = NXT;
**}
* /
EXTERN为const char * numeric_range(为const char * str中,长*卤味,长*喜);#ENDIF / * * RANGE_H /

range2.c

  / *
@(#)文件:$ RCSfile:range2.c,V $
@(#)版本:$修订:$ 1.8
@(#)上次修改:$日期:2008年2月11日8时44分50秒$
@(#)目的:德code字符串转换成整数的范围。
@(#)作者:J-莱弗勒
@(#)版权所有:(C)JLSS 1997,2002,2005,2007-08
@(#)产品:产品:
* // *制表位= 4 * // *
**解析号码范围,处理正值和负值,
**并以冒号或双点分离的范围。
**
**输入:低高
** 23 23 23
** -23 -23 -23
** 23:25 23 25
** 23..25 23 25
** -23 ..- 25 -25 -23
** -23..25 -23 25
** 23 ..- 25 -25 23
** -23 .. + 25 -23 25
**任何其他分隔符数后(或号码之前)终止
**输入。注意:一个领导冒号(或点)是不是一个有效范围。如果
**有格式错误,返回指针指向
**字符串的开始(哎哟喜不变)。如果有
**没有错误,那么返回的指针指向ASCII NUL在
**字符串的末尾。
* /#包括range.h
#包括LT&;&stdlib.h中GT;皮棉的#ifndef
/ * prevent过分进取,从消除ID字符串优化* /
为const char jlss_id_range2_c [] =@(#)$编号:range2.c,V 1.8 2008年2月11日8点44分50秒jleffler精通$;
#ENDIF / *皮棉* // *
**解析数字范围。
**返回指针尾随'\\ 0',如果OK,否则指针输入字符串
* /
为const char * numeric_range(为const char * str中,长*卤味,长*喜)
{
    为const char * S = str中;
    字符*吨;
    长升;
    长H;    L = strtol将(S,amp; T公司,10);
    如果(* T =='\\ 0')
    {
        / *一个号码* /
        * LO = *您好= 1;
        返回(T);
    }    如果(* T ==':')
        T + = 1;
    否则,如果(T [0] ==放'。';&放大器;'。'T [1] ==)
        T + = 2;
    其他
    {
        / *格式错误* /
        返程(STR);
    }    H =与strtol(T,& T公司,10);
    如果(* T!='\\ 0')
    {
        / *格式错误* /
        返程(STR);
    }    如果(H< L)
    {
        长X = ​​H;
        H =升;
        L = X;
    }    * LO =升;
    * HI = H;    返回(T);
}#IFDEF TEST
#包括LT&;&stdio.h中GT;
#包括stderr.hINT主(INT ARGC,字符** argv的)
{
    INT I;
    罗长;
    长喜;
    为const char * T;
    为const char * S;    err_setarg0(的argv [0]);
    如果(的argc&下; = 1)
        err_usage(范围[...]);
    对于(i = 1; I< ARGC,我++)
    {
        T =的argv [I]
        如果(T = 0&安培;!&放大器; * T ='\\ 0'!)
        {
            的printf(解析:15S%(地址=为0x%08lX),T(无符号长)T);
            fflush(标准输出);
            S = numeric_range(T,&安培;不料,&安培;喜);
            的printf(范围:%2LD - >%2LD(地址=为0x%08lX;尾=<<%S>>)\\ n,罗喜(无符号长)S,S);
            fflush(标准输出);
        }
    }
    返回(0);
}
#ENDIF / * TEST * /

stderrmin.c

这是400行,而不是约700线。是的,这是矫枉过正对这一计划的;我并不只在这个程序中使用它。

  / *
@(#)文件:$ RCSfile:stderrmin.c,V $
@(#)版本:$修订:$ 9.6
@(#)上次修改:$日期:2009/02/26 20时27分38秒$
@(#)用途:最小执行错误报告例程
@(#)作者:J-莱弗勒
@(#)版权所有:(C)JLSS 1988-91,1996-99,2001,2003,2005-09
@(#)产品:产品:
* // *制表位= 4 * /和#undef STDERR_EXTENDED
#包括stderr.h#包括LT&;&ASSERT.H GT;
#包括LT&;&文件ctype.h GT;
#包括LT&;&errno.h中GT;
#包括LT&;&string.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&STDARG.H GT;
#包括LT&;&time.h中GT;#IFDEF HAVE_UNISTD_H
#包括LT&;&unistd.h中GT;
#其他
EXTERN INT GETPID(无效);
#ENDIF / * * HAVE_UNISTD_H /枚举{MAX_MSGLEN = 2048};/ *全球格式化字符串* /
为const char err_format1 [] =%S \\ n;
为const char err_format2 [] =%s%S \\ n;静态为const char def_format [] =%Y-%M-%D%H:%M:%S;
静态为const char * tm_format = def_format;
静态字符将arg0 [ERR_MAXLEN_ARGV0 + 1] =** **未定义;/ *允许的默认错误标志* /
枚举{ERR_LOGOPTS = ERR_NOFLUSH | ERR_EXIT | ERR_ABORT | ERR_STAMP |
                     ERR_NOARG0 | ERR_PID | ERR_ERRNO};
静态INT err_flags = 0; / *默认错误标志(ERR_STAMP,ERR_PID等)* /
静态文件* errout = 0;/ *
** ERR _ ??? _打印()函数被系统命名,并且都是静态的。
**
ERR_ ** [EV] [CRX] [FN] _print():
** - 电子需要省略参数
** - v需要va_list的参数
** - Ç有条件退出
** - R的收益(不退出)
** - X退出(不归)
** - ˚F接受文件指针
** - N无文件指针(使用errout)
**
**注:没有回报和printf一样只能附声明,没有定义。
* /静态无效err_vxf_print(FILE * FP,诠释旗帜,INT ESTAT,为const char *格式,va_list的参数)
                noreturn的();
静态无效err_vxn_print(INT标志,INT ESTAT,为const char *格式,va_list的参数)
                noreturn的();
静态无效err_exn_print(INT标志,INT ESTAT,为const char *格式,...)
                不返回的()PRINTFLIKE(3,4);
静态无效err_terminate(INT标志,INT ESTAT)不返回的();皮棉的#ifndef
/ * prevent过分进取,从消除ID字符串优化* /
为const char jlss_id_stderrmin_c [] =@(#)$编号:stderrmin.c,V 9.6 2009/02/26 20时27分38秒jleffler精通$;
#ENDIF / *皮棉* // *
**设置默认日志选项,返回旧值。
**设置ERR_EXIT和ERR_ABORT是允许的,但不建议使用。
* /
INT err_setlogopts(INT new_opts)
{
    INT old_opts = err_flags;
    err_flags = new_opts&安培; ERR_LOGOPTS;
    返回(old_opts);
}/ *返回默认日志选项* /
INT err_getlogopts(无效)
{
    回报(err_flags);
}/ *更改标准错误的定义,在旧的报告太* /
/ *注意:使用err_stderr((FILE *)0)只报告当前的'标准错误'* /
FILE *(err_stderr)(FILE * newerr)
{
    FILE *老;    如果(errout == 0)
        errout =标准错误;
    老= errout;
    如果(newerr!= 0)
        errout = newerr;
    返回(旧);
}/ *命令返回存储基本名* /
为const char *(err_getarg0)(无效)
{
    回报(为arg0);
}/ *命令的基本名称储存不包​​括尾随斜线* /
无效(err_setarg0)(为const char * argv0)
{
    / *忽略的三种病理检查节目名称 - NULL,/和* /
    如果(argv0 = 0&安培;!&放大器; * argv0 ='\\ 0'和;!&安培;!(* argv0 ='/'|| *(argv0 + 1)='\\ 0'))
    {
        为const char * CP;
        为size_t为nbytes = sizeof的(arg0中) - 1;        如果((CP = strrchr(argv0,'/'))== 0)
        {
            / *文件的基名只* /
            CP = argv0;
        }
        否则,如果(*(CP + 1)!='\\ 0')
        {
            / *包含斜杠而不是结尾的斜杠定期路径* /
            CP ++;
        }
        其他
        {
            / *跳到了向后斜杠结尾* /
            为const char * EP = CP;
            而(EP> argv0和放大器;&放大器; * EP =='/')
                ep--;
            / *向后跳过非斜线* /
            CP = EP;
            而(CP> argv0和放大器;&安培;!* CP ='/')
                cp--;
            断言(EP> = CP);
            CP ++;
            为nbytes =(为size_t)(EP - CP)+ 1;
            如果(为nbytes>的sizeof(为arg0) - 1)
                为nbytes = sizeof的(arg0中) - 1;
        }
        函数strncpy(为arg0,CP,为nbytes);
        将arg0 [返回nbytes] ='\\ 0';
    }
}为const char *(err_rcs_string)(为const char * S2,字符*缓冲区,为size_t buflen)
{
    为const char * SRC = S2;
    字符* DST =缓冲;
    字符*结束=缓冲区+ buflen - 1;    / *
    **烦RCS!我们可能已经被赋予类似:
    **$修订:$ 9.6($日期:2009/02/26 20时27分38秒$)
    **我们只希望发出7.5(2001年8月11日6点25分48秒)。
    **跳过$和组件之间的:复制到$,
    **重复是必要的。而我们就来测试溢出!
    **还与关键字的形式未展开($关键字$)工作。
    **决不SCCS需要这个!
    * /
    而(* SRC ='\\ 0'和;!&安培; DST<结束)
    {
        而(* SRC ='\\ 0'和;!&安培;!* SRC =$)
        {
            * DST ++ = * SRC ++;
            如果(DST> =结束)
                打破;
        }
        如果(* SRC = =$)
            SRC ++;
        而(* SRC ='\\ 0'和;&放大器; * SRC =!:!&放大器;&放大器; * SRC =$)
            SRC ++;
        如果(* SRC =='\\ 0')
            打破;
        如果(* SRC = =$)
        {
            / *未展开关键字$关键字$符号* /
            SRC ++;
            继续;
        }
        如果(* SRC ==':')
            SRC ++;
        如果(* SRC =='')
            SRC ++;
        而(* SRC ='\\ 0'和;!&安培;!* SRC =$)
        {
            / *地图/在2009/02/15冲* /
            / *启发式 - 地图斜杠用阿拉伯数字包围破折号* /
            字符C = * SRC ++;
            如果(C =='/'和;&放大器; ISDIGIT(* SRC)及&放大器; ISDIGIT(*(SRC-2)))
                C =' - ';
            * DST ++ = C;
            如果(DST> =结束)
                打破;
        }
        如果(* SRC = =$)
        {
            如果(*(DST-1)=='')
                dst--;
            SRC ++;
        }
    }
    * DST ='\\ 0';
    返回(缓冲);
}/ *格式,现在(使用ISO8601格式)时间字符串* /
/ *允许与tm_format未来可设置时间格式* /
静态的char * err_time(字符*缓冲区,为size_t buflen)
{
    现在time_t的;
    结构TM * TP;    现在时间=((time_t的*)0);
    TP =本地时间(安培,现在);
    的strftime(缓冲,buflen,tm_format,TP);
    返回(缓冲);
}/ *最根本的(灵活)错误信息打印程序 - 总是返回* /
静态的

Is there a command-line tool that will take a symbolic errno such as EINVAL and print the corresponding string, Invalid argument?

I would like to avoid having to find that EINVAL is value 22 on my system and then using$ perror 22.

Ideally I could write something like

$ errorcommand EINVAL
Invalid argument
$

解决方案

AFAIK, there isn't a standard tool that does the job. At one level, it wouldn't be particularly hard to write one - the messiest parts are finding the correct file to parse (is is often, but by no means always, /usr/include/sys/errno.h) and then taking the data from that to do the mapping of names to numbers. I have not found a system that uses enum values rather than #define values, but it is probably only a matter of time. It is also a moot point whether to generate a triple consisting of token number (EINTR, etc), token name ("EINTR", etc) and error message ("Interrupted system call", etc), or whether to use just the number and name and leave it to 'strerror()' to supply the message.


As I said, it isn't particularly hard. I already had a program called 'errno' that accepted pure numeric values and printed the corresponding error messages:

$ errno 1:10 20
1: Operation not permitted
2: No such file or directory
3: No such process
4: Interrupted system call
5: Input/output error
6: Device not configured
7: Argument list too long
8: Exec format error
9: Bad file descriptor
10: No child processes
20: Not a directory
$

I've written a Perl script and hacked the program to handle symbolic error numbers too:

$ errno 1:4 EINTR ENOTDIR
1 (EPERM): Operation not permitted
2 (ENOENT): No such file or directory
3 (ESRCH): No such process
4 (EINTR): Interrupted system call
EINTR (4): Interrupted system call
ENOTDIR (20): Not a directory
$

It does not handle ranges of symbolic error numbers (exercise for the reader).

generrno.pl

#!/usr/bin/perl -w
#
# @(#)$Id: generrno.pl,v 1.1 2010/02/07 18:39:18 jleffler Exp jleffler $
#
# Generate table of error number constants from given file(s)

use strict;

my %symlist;
my $maxsymlen = 0;
my $maxmsglen = 0;

while (<>)
{
    next unless m%^\s*#\s*define\s+(E[A-Z0-9a-z]+)\s+(\d+)\s*/\*\s*([A-Za-z].*\S)\s*\*/%;
    $symlist{$1} = { number => $2, message => $3 };
    $maxsymlen = length($1) if length($1) > $maxsymlen;
    $maxmsglen = length($3) if length($3) > $maxmsglen;
}

my $format = sprintf "    {   %%-%ds %%-%ds %%-5s   %%-%ds },\n", $maxsymlen + 3, $maxsymlen + 1, $maxmsglen + 2;

foreach my $key (sort keys %symlist)
{
    my $name    = qq{"$key",};
    my $symbol  = qq{$key,};
    my $number  = qq{$symlist{$key}->{number},};
    my $message = qq{"$symlist{$key}->{message}"};

    printf $format, $name, $symbol, $number, $message;
}

errno.c

/*
@(#)File:           $RCSfile: errno.c,v $
@(#)Version:        $Revision: 2.2 $
@(#)Last changed:   $Date: 2010/02/07 19:22:37 $
@(#)Purpose:        Print messages corresponding to errno values or name
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 2003,2005,2008,2010
*/

/*TABSTOP=4*/

#define MAIN_PROGRAM

/* Need O/S specific messages as well as POSIX messages */
//#if __STDC_VERSION__ >= 199901L
//#define _XOPEN_SOURCE 600
//#else
//#define _XOPEN_SOURCE 500
//#endif /* __STDC_VERSION__ */

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> /* getopt() on MacOS X 10.2 */
#include "stderr.h"
#include "range.h"

typedef struct err_info
{
    const char *errsym;     /* Error symbol - "EINTR" */
    int         errnum;     /* Error number - EINTR   */
    int         errdef;     /* Error define - 4       */
    const char *errmsg;     /* Error message - Interrupted system call */
} err_info;

/*
** Generate generrno.h using:
**     perl generrno.pl /usr/include/sys/errno.h > generrno.h 
** NB: list must be sorted alphabetically on symbol name
*/
static const err_info err_msgs[] =
{
#include "generrno.h"
};

static const char usestr[] = "[-qV] [--] lo[:hi] ...";

#define DIM(x)  (sizeof(x)/sizeof(*(x)))

static const err_info *err_nums[DIM(err_msgs)];

#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
const char jlss_id_errno_c[] = "@(#)$Id: errno.c,v 2.2 2010/02/07 19:22:37 jleffler Exp $";
#endif /* lint */

static int cmp_err_number(const void *v1, const void *v2)
{
    int e1 = (*((const err_info * const *)v1))->errnum;
    int e2 = (*((const err_info * const *)v2))->errnum;
    return(e1 - e2);
}

static void map_numbers(void)
{
    int i;

    for (i = 0; i < DIM(err_msgs); i++)
        err_nums[i] = &err_msgs[i];
    qsort(err_nums, DIM(err_nums), sizeof(*err_nums), cmp_err_number);
}

static const char *err_symbol(int num)
{
    const char *sym = "<UNKNOWN>";
    err_info    lookfor = { 0, num, 0, 0 };
    err_info   *lookptr = &lookfor;
    const err_info **found = bsearch(&lookptr, err_nums, DIM(err_nums), sizeof(*err_nums), cmp_err_number);
    if (found != 0)
        sym = (*found)->errsym;
    return(sym);
}

static int cmp_err_symbol(const void *v1, const void *v2)
{
    const char *s1 = ((const err_info *)v1)->errsym;
    const char *s2 = ((const err_info *)v2)->errsym;
    return(strcmp(s1, s2));
}

static int pr_string_errno(const char *arg, int qflag)
{
    int estat = EXIT_SUCCESS;
    err_info lookfor = { arg, 0, 0, 0 };
    const err_info *found = bsearch(&lookfor, err_msgs, DIM(err_msgs), sizeof(*err_msgs), cmp_err_symbol);
    if (found == 0)
    {
        err_remark("unrecognized symbol %s\n", arg);
        estat = EXIT_FAILURE;
    }
    else if (qflag == 0)
        printf("%s (%d): %s\n", arg, found->errnum, found->errmsg);
    return(estat);
}

static int pr_number_errno(const char *arg, int qflag)
{
    int estat = EXIT_SUCCESS;
    long lo;
    long hi;
    const char *endp;
    long msg;

    endp = numeric_range(arg, &lo, &hi);
    if (endp == arg)
        err_remark("Invalid range specified (%s) - should be lo[:hi]\n", arg);
    else if (*endp != '\0')
        err_remark("Non-numeric character (%c) after range '%s'\n",
                    (isprint((unsigned char)*endp) ? *endp : '?'), arg);
    else
    {
        for (msg = lo; msg <= hi; msg++)
        {
            char *msgtxt = strerror(msg);
            if (msgtxt == 0)
            {
                err_remark("no message for errno = %ld\n", msg);
                estat = EXIT_FAILURE;
            }
            else if (qflag == 0)
                printf("%ld (%s): %s\n", msg, err_symbol(msg), msgtxt);
        }
    }
    return(estat);
}

static int pr_errno(char *arg, int qflag)
{
    int estat;
    if (isalpha(*arg))
        estat = pr_string_errno(arg, qflag);
    else
        estat = pr_number_errno(arg, qflag);
    return(estat);
}

int main(int argc, char **argv)
{
    int i;
    int opt;
    int nstat;
    int estat = EXIT_SUCCESS;
    int qflag = 0;
    int nflag = 0;

    err_setarg0(argv[0]);

    map_numbers();

    while ((opt = getopt(argc, argv, "qV0:1:2:3:4:5:6:7:8:9:")) != EOF)
    {
        switch (opt)
        {
        case 'q':
            qflag = 1;
            break;
        case 'V':
            err_version("ERRNO", "$Revision: 2.2 $ ($Date: 2010/02/07 19:22:37 $)");
            break;

        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            /* GETOPT() is probably not the right tool for this job! */
            nstat = pr_errno(optarg-2, qflag);
            if (estat == EXIT_SUCCESS)
                estat = nstat;
            nflag = 1;
            break;

        default:
            err_usage(usestr);
            break;
        }
    }

    if (optind >= argc && nflag == 0)
        err_usage(usestr);

    for (i = optind; i < argc; i++)
    {
        nstat = pr_errno(argv[i], qflag);
        if (estat == EXIT_SUCCESS)
            estat = nstat;
    }

    return(estat);
}


The code needs some supporting files - stderr.h, range.h, range2.c and stderrmin.c (a simpler version of the stderr.c I normally use, which has extra bells and whistles for handling syslog and writing to file descriptors instead of file pointers.).

stderr.h

/*
@(#)File:           $RCSfile: stderr.h,v $
@(#)Version:        $Revision: 9.2 $
@(#)Last changed:   $Date: 2009/03/06 06:52:26 $
@(#)Purpose:        Header file for standard error functions
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 1989-93,1996-99,2003,2005-09
@(#)Product:        :PRODUCT:
*/

#ifndef STDERR_H
#define STDERR_H

#ifdef MAIN_PROGRAM
#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
const char jlss_id_stderr_h[] = "@(#)$Id: stderr.h,v 9.2 2009/03/06 06:52:26 jleffler Exp $";
#endif /* lint */
#endif

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <stdio.h>
#include <stdarg.h>

#ifdef __GNUC__
#define PRINTFLIKE(n,m) __attribute__((format(printf,n,m)))
#define NORETURN()      __attribute__((noreturn))
#else
#define PRINTFLIKE(n,m) /* If only */
#define NORETURN()      /* If only */
#endif /* __GNUC__ */

/* -- Definitions for error handling */

enum { ERR_STAT    = 1 };           /* Default exit status     */

enum { ERR_DEFAULT = 0x0000 };      /* Default flag             */
enum { ERR_NOFLUSH = 0x0001 };      /* Do not flush open files  */
enum { ERR_EXIT    = 0x0004 };      /* Exit  -- do not return   */
enum { ERR_ABORT   = 0x0008 };      /* Abort -- do not return   */
enum { ERR_STAMP   = 0x0020 };      /* Timestamp messages       */
enum { ERR_NOARG0  = 0x0040 };      /* Do not print arg0 prefix */
enum { ERR_PID     = 0x0080 };      /* Include pid=nnnnn info   */
enum { ERR_ERRNO   = 0x0100 };      /* Include system error     */

#ifdef USE_STDERR_SYSLOG
/* Definitions related to using syslog */
enum { ERR_LOG_EMERG    = 0x01000 };    /* system is unusable */
enum { ERR_LOG_ALERT    = 0x02000 };    /* action must be taken immediately */
enum { ERR_LOG_CRIT     = 0x04000 };    /* critical conditions */
enum { ERR_LOG_ERR      = 0x08000 };    /* error conditions */
enum { ERR_LOG_WARNING  = 0x10000 };    /* warning conditions */
enum { ERR_LOG_NOTICE   = 0x20000 };    /* normal but signification condition */
enum { ERR_LOG_INFO     = 0x40000 };    /* informational */
enum { ERR_LOG_DEBUG    = 0x80000 };    /* debug-level messages */
enum { ERR_LOG_LEVEL_HI = ERR_LOG_EMERG|ERR_LOG_ALERT|ERR_LOG_CRIT|ERR_LOG_ERR };
enum { ERR_LOG_LEVEL_LO = ERR_LOG_WARNING|ERR_LOG_NOTICE|ERR_LOG_INFO|ERR_LOG_DEBUG };
enum { ERR_LOG_LEVEL    = ERR_LOG_LEVEL_HI|ERR_LOG_LEVEL_LO };
#endif /* USE_STDERR_SYSLOG */

/* -- Standard combinations of flags */

enum { ERR_REM    = ERR_DEFAULT       };
enum { ERR_ERR    = ERR_EXIT          };
enum { ERR_ABT    = ERR_ABORT         };
enum { ERR_LOG    = ERR_STAMP|ERR_PID };
enum { ERR_SYSREM = ERR_REM|ERR_ERRNO };
enum { ERR_SYSERR = ERR_ERR|ERR_ERRNO };

/* -- Maximum recorded length of argv[0]; extra is truncated */

enum { ERR_MAXLEN_ARGV0 = 63 };

/* -- Global definitions */

extern const char  err_format1[];    /* "%s\n"    - for one string argument */
extern const char  err_format2[];    /* "%s %s\n" - for two string arguments */

extern const char *err_getarg0(void);
extern void        err_setarg0(const char *argv0);

extern FILE       *err_stderr(FILE *fp);
extern const char *err_rcs_string(const char *s, char *buffer, size_t buflen);

extern void err_abort(const char *format, ...) PRINTFLIKE(1,2) NORETURN();
extern void err_error(const char *format, ...) PRINTFLIKE(1,2) NORETURN();
extern void err_error1(const char *s1) NORETURN();
extern void err_error2(const char *s1, const char *s2) NORETURN();
extern void err_help(const char *use_str, const char *hlp_str) NORETURN();
extern void err_helplist(const char *use_str, const char * const *help_list) NORETURN();
extern void err_internal(const char *function, const char *msg) NORETURN();
extern void err_logmsg(FILE *fp, int flags, int estat, const char *format, ...) PRINTFLIKE(4,5);
extern void err_print(int flags, int estat, const char *format, va_list args);
extern void err_printversion(const char *program, const char *verinfo);
extern void err_remark(const char *format, ...) PRINTFLIKE(1,2);
extern void err_remark1(const char *s1);
extern void err_remark2(const char *s1, const char *s2);
extern void err_report(int flags, int estat, const char *format, ...) PRINTFLIKE(3,4);
extern void err_syserr(const char *format, ...) PRINTFLIKE(1,2) NORETURN();
extern void err_syserr1(const char *s1) NORETURN();
extern void err_syserr2(const char *s1, const char *s2) NORETURN();
extern void err_sysrem(const char *format, ...) PRINTFLIKE(1,2);
extern void err_sysrem1(const char *s1);
extern void err_sysrem2(const char *s1, const char *s2);
extern void err_usage(const char *usestr) NORETURN();
extern void err_version(const char *program, const char *verinfo) NORETURN();

extern int  err_getlogopts(void);           /* Get default log options */
extern int  err_setlogopts(int new_opts);   /* Set default log options */

#ifdef USE_STDERR_FILEDESC
extern int  err_use_fd(int fd);             /* Use file descriptor */
#endif /* USE_STDERR_FILEDESC */
#ifdef USE_STDERR_SYSLOG
/* In case of doubt, use zero for both logopts and facility */
extern int  err_use_syslog(int logopts, int facility);  /* Configure/use syslog() */
#endif /* USE_STDERR_SYSLOG */

/*
** JL 2003-07-31: Security Note.
** Question: given that err_remark("abc\n") and err_remark1("abc")
**           produce the same output, when should you use err_remark1()
**           instead of err_remark()?
** Answer 1: trivia - when you can't put the newline in the string.
** Answer 2: security - when the argument contains user input and could,
**           therefore, contain conversion specifiers, etc.  The code in
**           err_remark() does not (and cannot) verify that you have
**           passed correct arguments for the conversion specifiers in
**           the format string.
** Answer 3: inertia - when migrating code that uses remark().
**
** Generalizing: when you use a function that has 'const char *format'
** in the prototype above, make sure your code is fully in charge of the
** format string to avoid security lapses.  Do not allow the user to
** provide that string unless you stringently check it beforehand.
*/

#endif /* STDERR_H */

range.h

/*
@(#)File:           $RCSfile: range.h,v $
@(#)Version:        $Revision: 1.8 $
@(#)Last changed:   $Date: 2008/02/11 07:39:36 $
@(#)Purpose:        Declaration of range parsing functions
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 1997,2005,2007-08
@(#)Product:        :PRODUCT:
*/

/*TABSTOP=4*/

#ifndef RANGE_H
#define RANGE_H

#ifdef MAIN_PROGRAM
#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
const char jlss_id_range_h[] = "@(#)$Id: range.h,v 1.8 2008/02/11 07:39:36 jleffler Exp $";
#endif /* lint */
#endif /* MAIN_PROGRAM */

/*
** parse_range(): parse range of non-negative numbers.
**
** Given a string, parse_range() returns the lo and hi values corresponding
** to the range specified by the string.  For example:
**      Input:          Low             High
**      23              23              23
**      23-25           23              25
**      23-             23              0
**      -23             0               23
** Any delimiter other than '-' before or after a number terminates the
** scan, but commas are skipped.  Returns pointer to character after
** last character parsed (which may or may not be '\0') if successful.
** Otherwise, returns null.
**
** Idiomatic use:
**
**  const char *ptr = source_string;
**  const char *nxt;
**  while ((nxt = parse_range(ptr, &lo, &hi)) != 0)
**  {
**      if (nxt == ptr)
**          err_error("invalid range string (%s)\n", source_string);
**      use_range(lo, hi);
**      ptr = nxt;
**  }
*/
extern const char *parse_range(const char *str, long *lo, long *hi);

/*
** numeric_range(): parse range of numbers, positive or negative.
**
**  Input:      Low     High
**  23          23      23
**  -23        -23     -23
**  23:25       23      25
**  23..25      23      25
**  -23..-25   -25     -23
**  -23..25    -23      25
**  23..-25    -25      23
**  Returns pointer to '\0' at end of string if OK, sets *lo and *hi,
**  and guarantees *lo <= *hi.
**  Otherwise, returns pointer to start of string and does not set *lo or *hi.
**
** Idiomatic use:
**
**  const char *ptr = source_string;
**  const char *nxt;
**  while ((nxt = numeric_range(ptr, &lo, &hi)) != 0)
**  {
**      if (nxt == ptr)
**          err_error("invalid range string (%s)\n", source_string);
**      use_range(lo, hi);
**      ptr = nxt;
**  }
*/
extern const char *numeric_range(const char *str, long *lo, long *hi);

#endif /* RANGE_H */

range2.c

/*
@(#)File:           $RCSfile: range2.c,v $
@(#)Version:        $Revision: 1.8 $
@(#)Last changed:   $Date: 2008/02/11 08:44:50 $
@(#)Purpose:        Decode string into range of integers.
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 1997,2002,2005,2007-08
@(#)Product:        :PRODUCT:
*/

/*TABSTOP=4*/

/*
**  Parse number ranges, dealing with positive and negative values,
**  and ranges separated by either colon or double-dot.
**
**  Input:      Low     High
**  23          23      23
**  -23        -23     -23
**  23:25       23      25
**  23..25      23      25
**  -23..-25   -25     -23
**  -23..25    -23      25
**  23..-25    -25      23
**  -23..+25   -23      25
**  Any other delimiter after number (or before number) terminates
**  input.  NB: a leading colon (or dot) is not a valid range.  If
**  there is a format error, the returned pointer points to the
**  start of the string (and lo and hi are unchanged).  If there is
**  no error, then the returned pointer points to the ASCII NUL at
**  the end of the string.
*/

#include "range.h"
#include <stdlib.h>

#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
const char jlss_id_range2_c[] = "@(#)$Id: range2.c,v 1.8 2008/02/11 08:44:50 jleffler Exp $";
#endif /* lint */

/*
** Parse numeric range.
** Return pointer to trailing '\0' if OK, else pointer to input string
*/
const char *numeric_range(const char *str, long *lo, long *hi)
{
    const char *s = str;
    char       *t;
    long        l;
    long        h;

    l = strtol(s, &t, 10);
    if (*t == '\0')
    {
        /* Just one number */
        *lo = *hi = l;
        return(t);
    }

    if (*t == ':')
        t += 1;
    else if (t[0] == '.' && t[1] == '.')
        t += 2;
    else
    {
        /* Format error */
        return(str);
    }

    h = strtol(t, &t, 10);
    if (*t != '\0')
    {
        /* Format error */
        return(str);
    }

    if (h < l)
    {
        long x = h;
        h = l;
        l = x;
    }

    *lo = l;
    *hi = h;

    return(t);
}

#ifdef TEST
#include <stdio.h>
#include "stderr.h"

int main(int argc, char **argv)
{
    int         i;
    long        lo;
    long        hi;
    const char *t;
    const char *s;

    err_setarg0(argv[0]);
    if (argc <= 1)
        err_usage("range [...]");
    for (i = 1; i < argc; i++)
    {
        t = argv[i];
        if (t != 0 && *t != '\0')
        {
            printf("Parse: %15s (addr = 0x%08lX) ", t, (unsigned long)t);
            fflush(stdout);
            s = numeric_range(t, &lo, &hi);
            printf("Range: %2ld -> %2ld (addr = 0x%08lX; trailer = <<%s>>)\n", lo, hi, (unsigned long)s, s);
            fflush(stdout);
        }
    }
    return(0);
}
#endif /* TEST */

stderrmin.c

This is about 400 lines, instead of about 700 lines. Yes, it is overkill for this program; I don't use it only in this program.

/*
@(#)File:           $RCSfile: stderrmin.c,v $
@(#)Version:        $Revision: 9.6 $
@(#)Last changed:   $Date: 2009/03/02 20:27:38 $
@(#)Purpose:        Minimal implementation of error reporting routines
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 1988-91,1996-99,2001,2003,2005-09
@(#)Product:        :PRODUCT:
*/

/*TABSTOP=4*/

#undef STDERR_EXTENDED
#include "stderr.h"

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#else
extern int getpid(void);
#endif /* HAVE_UNISTD_H */

enum { MAX_MSGLEN = 2048 };

/* Global format strings */
const char err_format1[] = "%s\n";
const char err_format2[] = "%s %s\n";

static const char  def_format[] = "%Y-%m-%d %H:%M:%S";
static const char *tm_format = def_format;
static char        arg0[ERR_MAXLEN_ARGV0+1] = "**undefined**";

/* Permitted default error flags */
enum { ERR_LOGOPTS = ERR_NOFLUSH | ERR_EXIT | ERR_ABORT | ERR_STAMP |
                     ERR_NOARG0  | ERR_PID  | ERR_ERRNO };
static int   err_flags = 0;     /* Default error flags (ERR_STAMP, ERR_PID, etc) */
static FILE *errout    = 0;

/*
** err_???_print() functions are named systematically, and are all static.
**
** err_[ev][crx][fn]_print():
** --   e   takes ellipsis argument
** --   v   takes va_list argument
** --   c   conditionally exits
** --   r   returns (no exit)
** --   x   exits (no return)
** --   f   takes file pointer
** --   n   no file pointer (use errout)
**
** NB: no-return and printf-like can only be attached to declarations, not definitions.
*/

static void err_vxf_print(FILE *fp, int flags, int estat, const char *format, va_list args)
                NORETURN();
static void err_vxn_print(int flags, int estat, const char *format, va_list args)
                NORETURN();
static void err_exn_print(int flags, int estat, const char *format, ...)
                NORETURN() PRINTFLIKE(3,4);
static void err_terminate(int flags, int estat) NORETURN();

#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
const char jlss_id_stderrmin_c[] = "@(#)$Id: stderrmin.c,v 9.6 2009/03/02 20:27:38 jleffler Exp $";
#endif /* lint */

/*
** Set default log options, returning old value.
** Setting ERR_EXIT and ERR_ABORT is permitted but not recommended.
*/
int err_setlogopts(int new_opts)
{
    int old_opts = err_flags;
    err_flags = new_opts & ERR_LOGOPTS;
    return(old_opts);
}

/* Return default log options */
int err_getlogopts(void)
{
    return(err_flags);
}

/* Change the definition of 'stderr', reporting on the old one too */
/* NB: using err_stderr((FILE *)0) simply reports the current 'stderr' */
FILE *(err_stderr)(FILE *newerr)
{
    FILE *old;

    if (errout == 0)
        errout = stderr;
    old = errout;
    if (newerr != 0)
        errout = newerr;
    return(old);
}

/* Return stored basename of command */
const char *(err_getarg0)(void)
{
    return(arg0);
}

/* Store basename of command, excluding trailing slashes */
void (err_setarg0)(const char *argv0)
{
    /* Ignore three pathological program names -- NULL, "/" and "" */
    if (argv0 != 0 && *argv0 != '\0' && (*argv0 != '/' || *(argv0 + 1) != '\0'))
    {
        const char *cp;
        size_t nbytes = sizeof(arg0) - 1;

        if ((cp = strrchr(argv0, '/')) == 0)
        {
            /* Basename of file only */
            cp = argv0;
        }
        else if (*(cp + 1) != '\0')
        {
            /* Regular pathname containing slashes but not trailing slashes */
            cp++;
        }
        else
        {
            /* Skip backwards over trailing slashes */
            const char *ep = cp;
            while (ep > argv0 && *ep == '/')
                ep--;
            /* Skip backwards over non-slashes */
            cp = ep;
            while (cp > argv0 && *cp != '/')
                cp--;
            assert(ep >= cp);
            cp++;
            nbytes = (size_t)(ep - cp) + 1;
            if (nbytes > sizeof(arg0) - 1)
                nbytes = sizeof(arg0) - 1;
        }
        strncpy(arg0, cp, nbytes);
        arg0[nbytes] = '\0';
    }
}

const char *(err_rcs_string)(const char *s2, char *buffer, size_t buflen)
{
    const char *src = s2;
    char *dst = buffer;
    char *end = buffer + buflen - 1;

    /*
    ** Bother RCS!  We've probably been given something like:
    ** "$Revision: 9.6 $ ($Date: 2009/03/02 20:27:38 $)"
    ** We only want to emit "7.5 (2001/08/11 06:25:48)".
    ** Skip the components between '$' and ': ', copy up to ' $',
    ** repeating as necessary.  And we have to test for overflow!
    ** Also work with the unexpanded forms of keywords ($Keyword$).
    ** Never needed this with SCCS!
    */
    while (*src != '\0' && dst < end)
    {
        while (*src != '\0' && *src != '$')
        {
            *dst++ = *src++;
            if (dst >= end)
                break;
        }
        if (*src == '$')
            src++;
        while (*src != '\0' && *src != ':' && *src != '$')
            src++;
        if (*src == '\0')
            break;
        if (*src == '$')
        {
            /* Unexpanded keyword '$Keyword$' notation */
            src++;
            continue;
        }
        if (*src == ':')
            src++;
        if (*src == ' ')
            src++;
        while (*src != '\0' && *src != '$')
        {
            /* Map / in 2009/02/15 to dash */
            /* Heuristic - maps slashes surrounded by digits to dashes */
            char c = *src++;
            if (c == '/' && isdigit(*src) && isdigit(*(src-2)))
                c = '-';
            *dst++ = c;
            if (dst >= end)
                break;
        }
        if (*src == '$')
        {
            if (*(dst-1) == ' ')
                dst--;
            src++;
        }
    }
    *dst = '\0';
    return(buffer);
}

/* Format a time string for now (using ISO8601 format) */
/* Allow for future settable time format with tm_format */
static char *err_time(char *buffer, size_t buflen)
{
    time_t  now;
    struct tm *tp;

    now = time((time_t *)0);
    tp = localtime(&now);
    strftime(buffer, buflen, tm_format, tp);
    return(buffer);
}

/* Most fundamental (and flexible) error message printing routine - always returns */
static

这篇关于象征性的错误号为String的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆