Personal tools
You are here: Home オペレーティングシステム論 演習 2006年度 第2回 exsh-2.c
Document Actions

exsh-2.c

by 管理者 last modified 2006-07-27 01:48

Click here to get the file

Size 4.4 kB - File type text/x-csrc

File contents

/* 
 * $Id: exsh-2.c,v 1.30 2006/05/31 10:35:38 yamamoto Exp yamamoto $
 * 
 * (C) Copyright:       S.Yamamoto  2006
 *                      This file is a product of the project Exsh.
 */

#include <setjmp.h>             /* For setjmp() and longjmp(). */
#include <signal.h>             /* For sigaction(). */
#include <stdarg.h>             /* For variable argument lists. */
#include <stdio.h>
#include <stdlib.h>             /* For EXIT_SUCCESS. */
#include <string.h>             /* For strtok(). */
#include <sys/types.h>          /* For pid_t. */
#include <sys/wait.h>           /* For waitpid(). */
#include <unistd.h>             /* For execl() family. */

#ifndef FALSE
#define FALSE           0
#define TRUE            1
#endif

#define MAX_INPUT       81      /* Including a terminator. */

/* Including a command name, 8 arguments and a NULL terminator. */
#define ARG_MAX         (1 + 8 +1)

#define JMP_SIGINT      1       /* Return value from longjmp(). */

static int          debug_mode = FALSE;
static char         input[MAX_INPUT];
static sigjmp_buf   jmp;

static void set_signal_handler(void);

static void parse_input(char x[MAX_INPUT], char *a[ARG_MAX]);
static void read_command(char *a[ARG_MAX]);

static void handler_sigint(int x);

static void debug(const char *format, ...);

int main(int argc, char *argv[])
{
    int         status;
    pid_t       pid;

    /* Initialize. */ 
    if (2 <= argc && !strcmp(argv[1], "-d")) {
        debug_mode = TRUE;
    }
    set_signal_handler();

    debug("getpid() %d.\n", getpid());
    while (TRUE) {
        char    *args[ARG_MAX];
        int     ret_setjmp;

        ret_setjmp = sigsetjmp(jmp, 1);
        debug("getpid() %d, setjmp(): %d.\n", getpid(), ret_setjmp);
        if (ret_setjmp != 0) {          /* longjmp() occurred. */
            fputs("\n", stderr);
            continue;
        }

        read_command(args);
        if (args[0] == NULL) {
            continue;
        }

        if ((pid = fork()) < 0) {       /* Error. */
            perror(NULL);
        } else if (pid == 0) {          /* Child. */
            int     retval;
            
            debug("Child: getpid() %d, fork() %d.\n", getpid(), pid);
            retval = execvp(args[0], args);
            perror(args[0]);
            exit(EXIT_FAILURE);
        } else {                        /* Parent. */
            debug("Parent: getpid() %d, fork() %d.\n", getpid(), pid);
            waitpid(pid, &status, 0);
        }
    }

    exit (EXIT_SUCCESS);
}

/*
 * シグナルハンドラを設定する.
 */

static void set_signal_handler(void)
{
    static struct sigaction    action;

    action.sa_handler = handler_sigint;         /* シグナルハンドラ. */

    /* ハンドラ内でSIGINTをブロックする. */
    sigemptyset(&action.sa_mask);               /* マスクのクリア. */  
    sigaddset(&action.sa_mask, SIGINT);

    action.sa_flags = SA_RESTART;               /* BSDとの互換性(おまじない). */

    sigaction(SIGINT, &action, NULL);           /* SIGINTに設定.*/
}

static void read_command(char *a[ARG_MAX])
{
    if (isatty(fileno(stdin))) {        /* Only in case of terminal. */
        fputs("> ", stderr);
    }

    gets(input);
    if (feof(stdin)) {
        exit(EXIT_SUCCESS);
    }
    debug("\"%s\".\n", input);

    if (!strcmp(input, "exit")) {
        exit (EXIT_SUCCESS);
    }
    parse_input(input, a);
}

/*
 * Return value:
 * a[0]: command name.
 * a[1] ... a[n]: arguments followed by a NULL terminator.
 *
 * If input is a empty string, a[0] is NULL.
 */

static void parse_input(char x[MAX_INPUT], char *a[ARG_MAX])
{
    int     i = 0;

    a[i] = strtok(x, " ");
    debug("a[%d] \"%s\".\n", i, a[i]);
    i++;
    while (a[i - 1] != NULL) {
        if (ARG_MAX <= i) {
            fprintf(stderr, "Too many arguments.\n");
            a[0] = NULL;
            break;
        }
        a[i] = strtok(NULL, " ");
        debug("a[%d] \"%s\".\n", i, a[i]);
        i++;
    };
}

/*
 * シグナルハンドラ.
 */

static void handler_sigint(int x)
{
    debug("Catch signal SIGINT.\n");
    siglongjmp(jmp, JMP_SIGINT);
}

static void debug(const char *format, ...)
{
    va_list     args;
    
    if (!debug_mode) {
        return;
    }

    fflush(stdout);
    fflush(stderr);

    fputs("Debug: ", stderr);
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);

    fflush(stderr);
}