Chinaunix
标题:
求教一段纠结的函数代码
[打印本页]
作者:
superwujc
时间:
2012-12-18 17:55
标题:
求教一段纠结的函数代码
本帖最后由 superwujc 于 2012-12-18 17:57 编辑
下列代码有些又臭又长,摘自《The Linux Programming Interface》第4章,Page84,文件名为seek_io.c;我把它拼凑到了一起,没办法,小弟水平太菜,不知道怎样发问
先说一下代码功能:验证Linux系统调用open(),read(),write()和lseek(),第一个命令行参数指定将要被打开的文件,其余的参数指定将要在文件上执行的IO操作,每个操作包含一个 字母+数值 的形式
s表示偏移量,r表示从当前偏移量读取并以文本形式打印,R表示从当前偏移量读取并以十六进制形式打印,这3个参数都后跟表示字节长度的数值,w表示写入字符,后跟指定的字符串
如,seek_io myfile wxyz s1 r2 表示打开(或新建)myfile文件,在当前偏移量写入字符xyz,从文件开始处设置1字节的偏移量后,读取2个字节
由于里面细节有些复杂,说一下具体疑问:
在main()中执行到if (-1 == fd)判断文件是否打开或创建成功时,调用了errExit()函数,而errExit()函数又调用了outputError()函数,但是想不通这个outputError()函数是干什么用的,为什么直接调用先前定义的Boolean类型,而且是直接使用TRUE
烦请哪位大神,帮忙解释一下if (-1 == fd)时,调用errExit()函数和outputError()函数处理的具体思路和具体步骤吗?不胜感激!!!
头文件和函数声明部分
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include "ename.c.inc"
#include <ctype.h> //declaration for character test & map function
#define GN_NONNEG 01 /* Value must be >= 0 */
#define GN_GT_0 02 /* Value must be > 0 */
#define GN_ANY_BASE 0100 /* Can use any base - like strtol(3) */
#define GN_BASE_8 0200 /* Value is expressed in octal */
#define GN_BASE_16 0400 /* Value is expressed in hexadecimal */
typedef enum { FALSE, TRUE } Boolean;
void usageErr(const char *format, ...);
void errExit(const char *format, ...);
void cmdLineErr(const char *format, ...);
static void terminate(Boolean useExit3);
static void outputError(Boolean useErr, int err, Boolean flushStdout,
const char *format, va_list ap);
static void gnFail(const char *fname, const char *msg, const char *arg, const char *name);
long getLong(const char *arg, int flags, const char *name);
static long getNum(const char *fname, const char *arg, int flags, const char *name);
复制代码
ename.c.inc部分
static char *ename[] = {
/* 0 */ "",
/* 1 */ "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO",
/* 7 */ "E2BIG", "ENOEXEC", "EBADF", "ECHILD",
/* 11 */ "EAGAIN/EWOULDBLOCK", "ENOMEM", "EACCES", "EFAULT",
/* 15 */ "ENOTBLK", "EBUSY", "EEXIST", "EXDEV", "ENODEV",
/* 20 */ "ENOTDIR", "EISDIR", "EINVAL", "ENFILE", "EMFILE",
/* 25 */ "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE",
/* 30 */ "EROFS", "EMLINK", "EPIPE", "EDOM", "ERANGE",
/* 35 */ "EDEADLK/EDEADLOCK", "ENAMETOOLONG", "ENOLCK", "ENOSYS",
/* 39 */ "ENOTEMPTY", "ELOOP", "", "ENOMSG", "EIDRM", "ECHRNG",
/* 45 */ "EL2NSYNC", "EL3HLT", "EL3RST", "ELNRNG", "EUNATCH",
/* 50 */ "ENOCSI", "EL2HLT", "EBADE", "EBADR", "EXFULL", "ENOANO",
/* 56 */ "EBADRQC", "EBADSLT", "", "EBFONT", "ENOSTR", "ENODATA",
/* 62 */ "ETIME", "ENOSR", "ENONET", "ENOPKG", "EREMOTE",
/* 67 */ "ENOLINK", "EADV", "ESRMNT", "ECOMM", "EPROTO",
/* 72 */ "EMULTIHOP", "EDOTDOT", "EBADMSG", "EOVERFLOW",
/* 76 */ "ENOTUNIQ", "EBADFD", "EREMCHG", "ELIBACC", "ELIBBAD",
/* 81 */ "ELIBSCN", "ELIBMAX", "ELIBEXEC", "EILSEQ", "ERESTART",
/* 86 */ "ESTRPIPE", "EUSERS", "ENOTSOCK", "EDESTADDRREQ",
/* 90 */ "EMSGSIZE", "EPROTOTYPE", "ENOPROTOOPT",
/* 93 */ "EPROTONOSUPPORT", "ESOCKTNOSUPPORT",
/* 95 */ "EOPNOTSUPP/ENOTSUP", "EPFNOSUPPORT", "EAFNOSUPPORT",
/* 98 */ "EADDRINUSE", "EADDRNOTAVAIL", "ENETDOWN", "ENETUNREACH",
/* 102 */ "ENETRESET", "ECONNABORTED", "ECONNRESET", "ENOBUFS",
/* 106 */ "EISCONN", "ENOTCONN", "ESHUTDOWN", "ETOOMANYREFS",
/* 110 */ "ETIMEDOUT", "ECONNREFUSED", "EHOSTDOWN", "EHOSTUNREACH",
/* 114 */ "EALREADY", "EINPROGRESS", "ESTALE", "EUCLEAN",
/* 118 */ "ENOTNAM", "ENAVAIL", "EISNAM", "EREMOTEIO", "EDQUOT",
/* 123 */ "ENOMEDIUM", "EMEDIUMTYPE", "ECANCELED", "ENOKEY",
/* 127 */ "EKEYEXPIRED", "EKEYREVOKED", "EKEYREJECTED",
/* 130 */ "EOWNERDEAD", "ENOTRECOVERABLE", "ERFKILL"
};
#define MAX_ENAME 132
复制代码
main部分
int main (int argc, char *argv[])
{
size_t len;
off_t offset;
int fd, ap, j;
char *buf;
ssize_t numRead, numWritten;
if (argc < 3 || 0 == strcmp(argv[1], "--help"))
{
usageErr ("%s file {r<length>|R<length>|w<string>|s<offset>}...\n", argv[0]);//only two arguments
}
fd = open (argv[1], O_RDWR | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);//rw-rw-rw
if (-1 == fd)
{
errExit ("open");
}
for (ap = 2; ap < argc; ap++)
{
switch (argv[ap][0])
{
case 'r':
case 'R':
len = getLong (&argv[ap][1], GN_ANY_BASE, argv[ap]);
buf = malloc (len);
if (NULL == buf)
{
errExit ("malloc");
}
numRead = read (fd, buf, len);
if (-1 == numRead)
{
errExit ("read");
}
if (0 == numRead)
{
printf ("%s: end-of-file\n", argv[ap]);
} else
{
printf ("%s: ", argv[ap]);
for (j = 0; j < numRead; j++)
{
if ('r' == argv[ap][0])
{
printf ("%c", isprint((unsigned char) buf[j]) ?
buf[j] : '?');
} else
{
printf ("%02x ", (unsigned int) buf[j]);
}
}
printf ("\n");
}
free (buf);
break;
case 'w':
numWritten = write (fd, &argv[ap][1], strlen (&argv[ap][1]));
if (-1 == numWritten)
{
errExit ("write");
}
printf ("%s: wrote %ld bytes\n", argv[ap], (long) numWritten);
break;
case 's':
offset = getLong (&argv[ap][1], GN_ANY_BASE, argv[ap]);
if (-1 == lseek (fd, offset, SEEK_SET))
{
errExit ("lseek");
}
printf ("%s: seek succeeded\n", argv[ap]);
break;
default:
cmdLineErr ("Argument must start with [rRws]: %s\n", argv[ap]);
}
}
exit (EXIT_SUCCESS);
}
复制代码
其他函数部分
void usageErr(const char *format, ...)
{
va_list argList;
fflush(stdout); /* Flush any pending stdout */
fprintf(stderr, "Usage: ");
va_start(argList, format);
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr); /* In case stderr is not line-buffered */
exit(EXIT_FAILURE);
}
void errExit(const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errno, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
void terminate(Boolean useExit3)
{
char *s;
/* Dump core if EF_DUMPCORE environment variable is defined and
is a nonempty string; otherwise call exit(3) or _exit(2),
depending on the value of 'useExit3'. */
s = getenv("EF_DUMPCORE");
if (s != NULL && *s != '\0')
abort();
else if (useExit3)
exit(EXIT_FAILURE);
else
_exit(EXIT_FAILURE);
}
void cmdLineErr(const char *format, ...)
{
va_list argList;
fflush(stdout); /* Flush any pending stdout */
fprintf(stderr, "Command-line usage error: ");
va_start(argList, format);
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr); /* In case stderr is not line-buffered */
exit(EXIT_FAILURE);
}
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;
}
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);
}
void outputError(Boolean useErr, int err, Boolean flushStdout,
const char *format, va_list ap)
{
#define BUF_SIZE 500
char buf[BUF_SIZE], userMsg[BUF_SIZE], errText[BUF_SIZE];
vsnprintf(userMsg, BUF_SIZE, format, ap);
if (useErr)
snprintf(errText, BUF_SIZE, " [%s %s]",
(err > 0 && err <= MAX_ENAME) ?
ename[err] : "?UNKNOWN?", strerror(err));
else
snprintf(errText, BUF_SIZE, ":");
snprintf(buf, BUF_SIZE, "ERROR%s %s\n", errText, userMsg);
if (flushStdout)
fflush(stdout); /* Flush any pending stdout */
fputs(buf, stderr);
fflush(stderr); /* In case stderr is not line-buffered */
}
long getLong(const char *arg, int flags, const char *name)
{
return getNum("getLong", arg, flags, name);
}
复制代码
作者:
sqfasd
时间:
2012-12-18 18:30
userMsg,是你调用errExit穿进去的字符串
errText是是标准库内部产生的错误,通过strerror(err)获取,err是标准库定义的全局变量errno传过来的,useErr用来标识是否打印这个错误
最后把这两个错误信息合并打印
有点不太理解这段
if (flushStdout)
fflush(stdout); /* Flush any pending stdout */
下面反正是要fflush的,为什么还要多调用一次fflush,这是出于什么考虑,有人能解答一下吗?
作者:
crazy_snail
时间:
2012-12-18 20:19
useErr 设置为 TRUE 的话, 输出详细的错误信息—— 使用ename数组吧errno翻译为字符串,并用strerror获得原因。 然后再附加上userMsg, 否则的话只有userMsg。
这里作者直接用TRUE可能是想让errExit输出详细的错误信息。
最后fflush标准输出和标准错误输出
欢迎光临 Chinaunix (http://bbs.chinaunix.net/)
Powered by Discuz! X3.2