5. safe_rw, full_rw 函数
read 和write函数在读写第一个字符之前有可能被signal中断, safe_rw可以恢复被中断的read和write过程. 这个函数非常tricky, 它的名字safe_rw和rw其实都是宏定义, 条件编译可以将此函数编译成safe_read, safe_write两个函数.
size_t /* 原始的read()函数返回值是 ssize_t */
safe_rw (int fd, void const *buf, size_t count)
{
/* Work around a bug in Tru64 5.1. Attempting to read more than
INT_MAX bytes fails with errno == EINVAL. See
.
When decreasing COUNT, keep it block-aligned. */
enum { BUGGY_READ_MAXIMUM = INT_MAX & ~8191 };
for (;;)
{
ssize_t result = rw (fd, buf, count);
if (0 <= result)
return result;
else if (IS_EINTR (errno)) /* signal 中断 */
continue;
else if (errno == EINVAL && BUGGY_READ_MAXIMUM < count)
count = BUGGY_READ_MAXIMUM;
else /* 返回 (size_t) -1 */
return result;
}
}
read, write 读写过程中有可能被 signal 中断, full_rw 可以恢复读写过程, 直到读写到了指定数目的字节或到达文件结尾(EOF), 或者是读写出错. 返回当前已经读写了的字节数目. full_rw() 函数名也是宏定义的, 实际上实现了full_read() full_write().
/* Write(read) COUNT bytes at BUF to(from) descriptor FD, retrying if
interrupted or if a partial write(read) occurs. Return the number
of bytes transferred.
When writing, set errno if fewer than COUNT bytes are written.
When reading, if fewer than COUNT bytes are read, you must examine
errno to distinguish failure from EOF (errno == 0). */
size_t
full_rw (int fd, const void *buf, size_t count)
{
size_t total = 0;
const char *ptr = (const char *) buf;
while (count > 0)
{
size_t n_rw = safe_rw (fd, ptr, count);
if (n_rw == (size_t) -1) /* error */
break;
if (n_rw == 0) /* reach EOF */
{
errno = ZERO_BYTE_TRANSFER_ERRNO;
break;
}
total += n_rw;
ptr += n_rw;
count -= n_rw;
}
return total;
}
Tips : 查看lib目录下safe_read.c 和 safe_write.c文件可以看到这个函数怎样被展开成两个不同的函数的.
6. cat 函数, 处理格式化输出
simple_cat只是将输入原封不动的输出, 没有作任何处理, 所有与格式化输出有关的内容都放在了 cat 函数里面.
cat 函数的实现包含很多技巧. 例如使用一个哨兵'\n'来标记输入缓冲区的结尾. 另外使用了一个字符数组来统计行数, 使得不支持64位整型的系统也可以使用很大范围的数.
下面是这个行计数器的代码.
/* Position in 'line_buf' where printing starts. This will not change
unless the number of lines is larger than 999999. */
static char *line_num_print = line_buf + LINE_COUNTER_BUF_LEN - 8;
/* Position of the first digit in 'line_buf'. */
static char *line_num_start = line_buf + LINE_COUNTER_BUF_LEN - 3;
/* Position of the last digit in 'line_buf'. */
static char *line_num_end = line_buf + LINE_COUNTER_BUF_LEN - 3;
static void
next_line_num (void)
{
char *endp = line_num_end;
do
{
if ((*endp)++ < '9')
return;
*endp-- = '0';
}
while (endp >= line_num_start);
if (line_num_start > line_buf)
*--line_num_start = '1';
else
*line_buf = '>';
if (line_num_start < line_num_print)
line_num_print--;
}
理解这个函数的关键是搞懂newlines这个变量作用, cat格式化输出主要的操作判断换行和连续空行, newlines这个变量标记的是空行的数目, 值为0表示此时的inbuf的读取位置在一行的开头, 1表示有一个空行, -1 表示刚刚解析完一行, 准备进入下一行, 可以看到cat 函数最后的那个 while(true) 的两个break语句都将 newlines 置为 -1.
cat格式化输出的过程实际上就是逐一扫描输入缓冲数组的过程, 并在扫描的过程中将转化后的字符存入输出缓冲数组中.