BSD版 cat メモ

/*-
 * Copyright (c) 1989, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Kevin Fall.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
//ここまでBSDのライセンス
//以下、プリプロセッサで外された走行しないコードが多数。
//本来なら、 lint の定義の有無で分岐するが、 #if 0 があるので常に無視される
#if 0
#ifndef lint
static char const copyright[] =
"@(#) Copyright (c) 1989, 1993\n\
	The Regents of the University of California.  All rights reserved.\n";
#endif /* not lint */
#endif
 
#ifndef lint
#if 0
static char sccsid[] = "@(#)cat.c	8.2 (Berkeley) 4/27/95";
#endif
#endif /* not lint */
//ここまで無視

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
 
#include <sys/param.h>
#include <sys/stat.h>
//UDOM:Unix Domain Socket
//POSIXシステムでプロセス間通信に使うソケット
#ifndef NO_UDOM_SUPPORT
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#endif
 
#include <ctype.h>
#include <err.h>
#include <fcntl.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stddef.h>
 
int bflag, eflag, nflag, sflag, tflag, vflag;
int rval;
const char *filename;
 
static void usage(void);
static void scanfiles(char *argv[], int cooked);
static void cook_cat(FILE *);
static void raw_cat(int);
 
#ifndef NO_UDOM_SUPPORT
static int udom_open(const char *path, int flags);
#endif
 
/* Memory strategy threshold, in pages: if physmem is larger then this, use a 
 * large buffer */
#define PHYSPAGES_THRESHOLD (32*1024)
 
/* Maximum buffer size in bytes - do not allow it to grow larger than this */
#define BUFSIZE_MAX (2*1024*1024)
 
/* Small (default) buffer size in bytes. It's inefficient for this to be
 * smaller than MAXPHYS */
#define BUFSIZE_SMALL (MAXPHYS)
 
int
main(int argc, char *argv[])
{
    int ch;
    //ロケールの設定
    setlocale(LC_CTYPE, "");
    //オプションの解釈 
    while ((ch = getopt(argc, argv, "benstuv")) != -1)
        switch (ch) {
        case 'b':
            bflag = nflag = 1;    /* -b implies -n */
            break;
        case 'e':
            eflag = vflag = 1;    /* -e implies -v */
            break;
        case 'n':
            nflag = 1;
            break;
        case 's':
            sflag = 1;
            break;
        case 't':
            tflag = vflag = 1;    /* -t implies -v */
            break;
        case 'u':
            setbuf(stdout, NULL);
            break;
        case 'v':
            vflag = 1;
            break;
        default:
        // 無いオプションがあったら用途を表示
            usage();
        }
    // 次のoption
    argv += optind;
 
    // オプションの設定が歩かないかで、scanflilesの第2引数が決まる。
    if (bflag || eflag || nflag || sflag || tflag || vflag)
        scanfiles(argv, 1);
    else
        scanfiles(argv, 0);
    // 標準出力をクローズ 
    if (fclose(stdout))
        err(1, "stdout");
    exit(rval);
    /* NOTREACHED */
}
 
static void
usage(void)
{
    fprintf(stderr, "usage: cat [-benstuv] [file ...]\n");
    exit(1);
    /* NOTREACHED */
}
 
static void
scanfiles(char *argv[], int cooked)
{
    int i = 0;
    char *path;
    FILE *fp;
 
    // 初回 or 引数がなくなるまで
	while ((path = argv[i]) != NULL || i == 0) {
        int fd;
 
        if (path == NULL || strcmp(path, "-") == 0) {
            filename = "stdin";
            fd = STDIN_FILENO;
        } else {
            filename = path;
            fd = open(path, O_RDONLY);
#ifndef NO_UDOM_SUPPORT
            if (fd < 0 && errno == EOPNOTSUPP)
                fd = udom_open(path, O_RDONLY);
#endif
        }
        if (fd < 0) {
            warn("%s", path);
            rval = 1;
        } else if (cooked) {
            //オプションがある場合の処理
        	if (fd == STDIN_FILENO)
        	//標準入力の場合
                cook_cat(stdin);
            else {
            //ファイルの場合
                fp = fdopen(fd, "r");
                cook_cat(fp);
                fclose(fp);
            }
        } else {
        	//オプションなしの場合
            raw_cat(fd);
            if (fd != STDIN_FILENO)
                close(fd);
        }
        if (path == NULL)
            break;
        ++i;
    }
}
 
//整形つき表示関数
static void
cook_cat(FILE *fp)
{
    int ch, gobble, line, prev;
 
    /* Reset EOF condition on stdin. */
    if (fp == stdin && feof(stdin))
        clearerr(stdin);
 
    line = gobble = 0;
    for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
    	//空行を処理するために、一文字取って解釈
    	//第3フィールドの、prev=chが肝
        if (prev == '\n') {
        	//連続した空行の場合に走行
            if (sflag) {
            	// 連続した空行を圧縮
                if (ch == '\n') {
                    if (gobble)
                        continue;
                    gobble = 1;
                } else
                    gobble = 0;
            }
            if (nflag && (!bflag || ch != '\n')) {
                // 空行も含めて行数表示
            	(void)fprintf(stdout, "%6d\t", ++line);
                if (ferror(stdout))
                    break;
            }
        }
        if (ch == '\n') {
            if (eflag && putchar('$') == EOF)
        	// 行末に$を表示する
                break;
        } else if (ch == '\t') {
            if (tflag) {
            	// Tab文字を可視化
                if (putchar('^') == EOF || putchar('I') == EOF)
                    break;
                continue;
            }
        } else if (vflag) {
            if (!isascii(ch) && !isprint(ch)) {
            	//アスキー文字かつ表示可能なことを確認する
                if (putchar('M') == EOF || putchar('-') == EOF)
                    break;
                ch = toascii(ch);
            }
            if (iscntrl(ch)) {
            	//制御文字?
                if (putchar('^') == EOF ||
                    putchar(ch == '\177' ? '?' :
                    ch | 0100) == EOF)
                    break;
                continue;
            }
        }
        if (putchar(ch) == EOF)
            break;
    }
    if (ferror(fp)) {
        warn("%s", filename);
        rval = 1;
        clearerr(fp);
    }
    if (ferror(stdout))
        err(1, "stdout");
}
 
 //ただ表示するだけ
static void
raw_cat(int rfd)
{
    int off, wfd;
    ssize_t nr, nw;
    static size_t bsize;
    static char *buf = NULL;
    struct stat sbuf;
 
    wfd = fileno(stdout);
    if (buf == NULL) {
        if (fstat(wfd, &sbuf))
            err(1, "%s", filename);
        if (S_ISREG(sbuf.st_mode)) {
            /* If there's plenty of RAM, use a large copy buffer */
            if (sysconf(_SC_PHYS_PAGES) > PHYSPAGES_THRESHOLD)
                bsize = MIN(BUFSIZE_MAX, MAXPHYS*8);
            else
                bsize = BUFSIZE_SMALL;
        } else
            bsize = MAX(sbuf.st_blksize, 
                    (blksize_t)sysconf(_SC_PAGESIZE));
        if ((buf = malloc(bsize)) == NULL)
            err(1, "malloc() failure of IO buffer");
    }
    while ((nr = read(rfd, buf, bsize)) > 0)
        for (off = 0; nr; nr -= nw, off += nw)
            if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
                err(1, "stdout");
    if (nr < 0) {
        warn("%s", filename);
        rval = 1;
    }
}
 
#ifndef NO_UDOM_SUPPORT
 
static int
udom_open(const char *path, int flags)
{
    struct sockaddr_un sou;
    int fd;
    unsigned int len;
 
    bzero(&sou, sizeof(sou));
 
    /*
	 * Construct the address and attempt to connect
	 */
    fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd >= 0) {
        sou.sun_family = AF_UNIX;
        if ((len = strlcpy(sou.sun_path, path,
            sizeof(sou.sun_path))) >= sizeof(sou.sun_path)) {
            errno = ENAMETOOLONG;
            return (-1);
        }
        len = offsetof(struct sockaddr_un, sun_path[len+1]);
 
        if (connect(fd, (void *)&sou, len) < 0) {
            close(fd);
            fd = -1;
        }
    }
 
    /*
	 * handle the open flags by shutting down appropriate directions
	 */
    if (fd >= 0) {
        switch(flags & O_ACCMODE) {
        case O_RDONLY:
            if (shutdown(fd, SHUT_WR) == -1)
                warn(NULL);
            break;
        case O_WRONLY:
            if (shutdown(fd, SHUT_RD) == -1)
                warn(NULL);
            break;
        default:
            break;
        }
    }
    return(fd);
}
 
#endif