less

`Curses`端末制御ライブラリを用いた"less"コマンド
主に"sl"コマンドなどに用いられるライブラリです.
コンパイルする際は, 適当な名前のディレクトリを作り, その中に"Makefile",
を移動し, "src", "include"ディレクトリを作成し,
"src"にCソースファイル, "include"にヘッダーファイルを作成してください.

"curses"ライブラリを用いたのは初めてで他のサイトを見ながら作成してみました.
ファイヤープロジェクト
上のサイトに"curses"の使い方が書いてあります.

 #Makefile
CC = gcc
FLAGS = -g -Wall -lcurses
SOURCE = main.c size.c
OBJC = main.o size.o
PROJ = less
INCLUDE = -I include

vpath %.c src/
vpath %.h include/

$(PROJ):$(OBJC)
	$(CC) $(FLAGS) $^ -o $@

main.o:$(SOURCE)
	$(CC) $(INCLUDE) $^ -c $(FLAGS)
 /* main.c */
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <message.h>

#define UP       1
#define DOWN     2
#define RIGHT    3
#define LEFT     4
#define LINE_TAB 6
#define LINE_TOP 7
#define LINE_END 8
#define QUIT     9

extern long line_count(FILE *fp);
extern int getkey(void);
void close_fd(void);

FILE *fp = NULL;

int main(int argc, char **argv)
{
    int term_w   = 0;
    int pad_line = 0;
    int pad_w    = 0;
    int pad_y;
    int pad_x;
    char *buff;
    int ch;
    int answer = 1;

    WINDOW *pad;

    if(argc < 2) {
        print_help();
        print_version();
        exit(EXIT_FAILURE);
    }

    if((fp = fopen(argv[1], "r")) == NULL) {
        perror("fopen(3)");
        exit(EXIT_FAILURE);
    }

    atexit(close_fd);

    while(feof(fp) == 0) {
        term_w = line_count(fp);
        pad_line++;
        if(term_w > pad_w)
            pad_w = term_w;
    }

    if(initscr() == NULL) {
        perror("initscr(3)");
        exit(EXIT_FAILURE);
    }

    cbreak();
    noecho();

    scrollok(stdscr, FALSE);

    pad = newpad(pad_line, ++pad_w);

    fseek(fp, 0, SEEK_SET);
    buff = (char *)malloc(sizeof(char) * pad_w);

    for(pad_y = 0; pad_y < pad_line; pad_y++) {
        fgets(buff, pad_w, fp);
        mvwprintw(pad, pad_y, 0, "%s", buff);
    }
    free(buff);
    refresh();
    prefresh(pad, 0, 0, 0, 0, LINES - 1, COLS -1);
    pad_y = 0;
    pad_x = 0;

    curs_set(0);

    while(answer == 1) {
        ch = getkey();

        switch(ch) {
            case DOWN:
                if(pad_y < pad_line - LINES)
                    ++pad_y;
                break;
            case UP:
                if(pad_y == 0) 
                    pad_y += 0;
                else if(pad_y != 0)
                    --pad_y;
                break;
            case LINE_TAB:
                if(pad_y < pad_line - LINES - 20)
                    pad_y += 20;
                break;
            case QUIT:
                answer = 0;
                break;
            case LINE_TOP:
                pad_y = 0;
                break;
            case LINE_END:
                pad_y = pad_line - LINES;
                break;
        }
        prefresh(pad, pad_y, pad_x, 0, 0, LINES - 1, COLS - 1);
    }

    exit(EXIT_SUCCESS);
}

void close_fd(void) {
    endwin();
    fclose(fp);
}
 /* size.c */
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define UP       1
#define DOWN     2
#define RIGHT    3
#define LEFT     4
#define LINE_TAB 6
#define LINE_TOP 7
#define LINE_END 8
#define QUIT     9

long line_count(FILE *fp) {
    long read_line = 0;
    int ch;

    while((ch = fgetc(fp)) != '\n' && ch != EOF) 
        read_line++;


    return read_line;
}

int getkey(void) {
    char ch;
    struct termios tty, save;

    if(ioctl(0, TCGETS, &tty) == EOF) {
        perror("ioctl(2)");
        exit(EXIT_FAILURE);
    }
    save = tty;
    tty.c_cc[VMIN] = 1;
    tty.c_lflag &= ~ECHO;

    if(ioctl(0, TCSETS, &tty) == EOF) {
        perror("ioctl(2)");
        exit(EXIT_FAILURE);
    }

    if(read(0, &ch, 1) == EOF) {
        perror("read(2)");
        exit(EXIT_FAILURE);
    }

    if(ch == 'k')
        return UP;
    else if(ch == 'j')
        return DOWN;
    else if(ch == 'q') {
        ioctl(0, TCSETS, &save);
        return QUIT;
    }
    else if(ch == 'd')
        return LINE_TAB;
    else if(ch == 'G')
        return LINE_END;
    else if(ch == 'g') {
        if(read(0, &ch, 1) == EOF) {
            perror("read(2)");
            exit(EXIT_FAILURE);
        }
        if(ch == 'g')
            return LINE_TOP;
        else
            return 0;
    }

    return EOF;
}
 /* message.h */
void print_help(void) {
    printf("Usage: ./less [Filename]\n");
}

void print_version(void) {
    printf("Version: 1.0.0\n");
}