Logo CitForum CITForum на CD Форумы Газета Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

24.05.2012

Google
WWW CITForum.ru

6.12. Простой интерпретатор команд.

Данный раздел просто приводит исходный текст простого интерпретатора команд. Функция match описана в главе "Текстовая обработка".

    /* Примитивный интерпретатор команд. Распознает построчно
     * команды вида: CMD ARG1 ... ARGn <FILE >FILE >>FILE >&FILE >>&FILE
     * Сборка: cc -U42 -DCWDONLY sh.c match.c pwd.c -o sh
     */

    #include <sys/types.h>/* определение типов, используемых системой */
    #include <stdio.h>      /* описание библиотеки ввода/вывода  */
    #include <signal.h>     /* описание сигналов                 */
    #include <fcntl.h>      /* определение O_RDONLY              */
    #include <errno.h>      /* коды системных ошибок             */
    #include <ctype.h>      /* макросы для работы с символами    */
    #include <dirent.h>     /* эмуляция файловой системы BSD 4.2 */
    #include <pwd.h>        /* работа с /etc/passwd              */
    #include <sys/wait.h>   /* описание формата wait()           */

    char cmd[256];          /* буфер для считывания команды */
    #define   MAXARGS 256   /* макс. количество аргументов  */
    char *arg[MAXARGS];     /* аргументы команды */
    char *fin, *fout;       /* имена для перенаправления ввода/вывода */
    int  rout;              /* флаги перенаправления вывода */

    char *firstfound;       /* имя найденной, но невыполняемой программы */
    #define LIM ':'         /* разделитель имен каталогов в path */
    extern char *malloc(), *getenv(), *strcpy(), *getwd();
    extern char *strchr(), *execat();
    extern void callshell(), printenv(), setenv(), dowait(), setcwd();
    extern struct passwd   *getpwuid();
            /* Предопределенные переменные */
    extern char **environ;  /* окружение: изначально смотрит на тот же
                             * массив, что и ev из main() */
    extern int errno;       /* код ошибки системного вызова   */

    char *strdup(s)char *s;
    { char *p; return(p=malloc(strlen(s)+1), strcpy(p,s)); }
            /* strcpy() возвращает свой первый аргумент */
    char *str3spl(s, p, q) char *s, *p, *q;
    { char *n = malloc(strlen(s)+strlen(p)+strlen(q)+1);
      strcpy(n, s); strcat(n, p); strcat(n, q); return n;
    }

    int cmps(s1, s2) char **s1, **s2;
    { return strcmp(*s1, *s2); }
    /* Перенаправить вывод */
    #define APPEND 0x01
    #define ERRTOO 0x02
    int output (name, append, err_too, created) char *name; int *created;
    {
        int     fd;
        *created = 0;     /* Создан ли файл ? */

        if( append ){                   /* >>file */
            /* Файл name существует? Пробуем открыть на запись */
            if((fd = open (name, O_WRONLY)) < 0) {
                if (errno == ENOENT) /* Файл еще не существовал */
                    goto CREATE;
                else
                    return 0;    /* Не имеем права писать в этот файл */
            }
            /* иначе fd == открытый файл, *created == 0 */
        }else{
    CREATE: /* Пытаемся создать (либо опустошить) файл "name" */
            if((fd = creat (name, 0666)) < 0 )
                        return 0;     /* Не могу создать файл  */
            else        *created = 1; /* Был создан новый файл */
        }
        if (append)
            lseek (fd, 0l, 2);      /* на конец файла */
     /* перенаправить стандартный вывод */
        dup2(fd, 1);
        if( err_too ) dup2(fd, 2);  /* err_too=1 для >& */
        close(fd); return 1;
    }

    /* Перенаправить ввод */
    int input (name) char *name;
    {
        int     fd;
        if((fd = open (name, O_RDONLY)) < 0 ) return 0;/* Не могу читать */
     /* перенаправить стандартный ввод */
        dup2(fd, 0); close(fd); return 1;
    }
    /* запуск команды */
    int cmdExec(progr, av, envp, inp, outp, outflg)
            char *progr;       /* имя программы */
            char **av;         /* список аргументов */
            char **envp;       /* окружение */
            char *inp, *outp;  /* файлы ввода-вывода (перенаправления) */
            int outflg;        /* режимы перенаправления вывода */
    {
            void (*del)(), (*quit)();
            int pid;
            int cr = 0;

            del = signal(SIGINT, SIG_IGN); quit = signal(SIGQUIT, SIG_IGN);
            if( ! (pid = fork())){     /* ветвление */
               /* порожденный процесс (сын) */
               signal(SIGINT, SIG_DFL); /* восстановить реакции */
               signal(SIGQUIT,SIG_DFL); /* по умолчанию         */
               /* getpid() выдает номер (идентификатор) данного процесса */
               printf( "Процесс pid=%d запущен\n", pid = getpid());

               /* Перенаправить ввод-вывод */
               if( inp ) if(!input( inp )){
                 fprintf(stderr, "Не могу <%s\n", inp ); goto Err;
               }
               if( outp )
                   if(!output (outp, outflg & APPEND, outflg & ERRTOO, &cr)){
                       fprintf(stderr, "Не могу >%s\n", outp ); goto Err;
                   }
     /* Заменить программу: при успехе
      * данная программа завершается, а вместо нее вызывается
      * функция main(ac, av, envp) программы, хранящейся в файле progr.
      * ac вычисляет система.
      */
               execvpe(progr, av, envp);

    Err:
      /* при неудаче печатаем причину и завершаем порожденный процесс */
               perror(firstfound ? firstfound: progr);
               /* Мы не делаем free(firstfound),firstfound = NULL
                * потому что данный процесс завершается (и тем ВСЯ его
                * память освобождается) :
                */
               if( cr && outp )  /* был создан новый файл     */
                   unlink(outp); /* но теперь он нам не нужен */

               exit(errno);
            }
            /* процесс - отец */

            /* Сейчас сигналы игнорируются, wait не может быть оборван
             * прерыванием с клавиатуры */
            dowait();       /* ожидать окончания сына */
            /* восстановить реакции на сигналы от клавиатуры */
            signal(SIGINT, del); signal(SIGQUIT, quit);
            return pid;     /* вернуть идентификатор сына */
    }
    /* Запуск программы с поиском по переменной среды PATH */
    int execvpe(progr, av, envp) char *progr, **av, **envp;
    {
            char *path, *cp;
            int try = 1;
            register eacces = 0;
            char fullpath[256];     /* полное имя программы */

            firstfound = NULL;
            if((path = getenv("PATH")) == NULL )
                path = ".:/bin:/usr/bin:/etc";
            /* имя: короткое или путь уже задан ? */
            cp = strchr(progr, '/') ? "" : path;
            do{     /* пробуем разные варианты */
                    cp = execat(cp, progr, fullpath);
            retry:
                fprintf(stderr, "пробуем \"%s\"\n", fullpath );
                    execve(fullpath, av, envp);
                    /* если программа запустилась, то на этом месте данный
                     * процесс заменился новой программой. Иначе - ошибка. */
                    switch( errno ){  /* какова причина неудачи ? */
                    case ENOEXEC:  /* это командный файл */
                            callshell(fullpath, av, envp);
                            return (-1);
                    case ETXTBSY:   /* файл записывается */
                            if( ++try > 5 ) return (-1);
                            sleep(try); goto retry;
                    case EACCES:    /* не имеете права */
                            if(firstfound == NULL)
                               firstfound = strdup(fullpath);
                            eacces++; break;
                    case ENOMEM:    /* программа не лезет в память */
                    case E2BIG:
                            return (-1);
                    }
            }while( cp );
            if( eacces ) errno = EACCES;
            return (-1);
    }

    /* Склейка очередной компоненты path и имени программы name */
    static char *execat(path, name, buf)
            register char *path, *name;
            char *buf;      /* где будет результат */
    {
            register char *s = buf;
            while(*path && *path != LIM )
                    *s++ = *path++;         /* имя каталога */
            if( s != buf ) *s++ = '/';
            while( *name )
                    *s++ = *name++;         /* имя программы */
            *s = '\0';
            return ( *path ? ++path /* пропустив LIM */ : NULL );
    }

    /* Запуск командного файла при помощи вызова интерпретатора */
    void callshell(progr, av, envp) char *progr, **av, **envp;
    {
            register i; char *sh; char *newav[MAXARGS+2];
            int fd; char first = 0;

            if((fd = open(progr, O_RDONLY)) < 0 )
                    sh = "/bin/sh";
            else{
                    read(fd, &first, 1); close(fd);
                    sh = (first == '#') ? "/bin/csh" : "/bin/sh";
            }
            newav[0] = "Shellscript"; newav[1] = progr;
            for(i=1; av[i]; i++)
                    newav[i+1] = av[i];
            newav[i+1] = NULL;
            printf( "Вызываем %s\n", sh );
            execve(sh, newav, envp);
    }

    /* Ожидать окончания всех процессов, выдать причины смерти. */
    void dowait(){
            int ws; int pid;

            while((pid = wait( &ws)) > 0 ){
              if( WIFEXITED(ws)){
                printf( "Процесс %d умер с кодом %d\n",
                                 pid,            WEXITSTATUS(ws));
              }else if( WIFSIGNALED(ws)){
                printf( "Процесс %d убит сигналом %d\n",
                                 pid,             WTERMSIG(ws));
                if(WCOREDUMP(ws)) printf( "Образовался core\n" );
                /* core - образ памяти процесса для отладчика adb */
              }else if( WIFSTOPPED(ws)){
                printf( "Процесс %d остановлен сигналом %d\n",
                                 pid,            WSTOPSIG(ws));
              }
           }
    }
    /* Расширение шаблонов имен. Это упрощенная версия, которая
     * расширяет имена только в текущем каталоге.
     */
    void glob(dir, args, indx, str /* что расширять */, quote )
         char *args[], *dir; int *indx; char *str;
         char quote; /* кавычки, в которые заключена строка str */
    {
            static char globchars[] = "*?[";
            char *p; char **start = &args[ *indx ];
            short nglobbed = 0;

            register struct dirent *dirbuf;
            DIR *fd; extern DIR *opendir();

            /* Затычка для отмены глоббинга: */
            if( *str == '\\' ){ str++;    goto noGlob; }

            /* Обработка переменных $NAME    */
            if( *str == '$' && quote != '\'' ){
                    char *s = getenv(str+1);
                    if( s ) str = s;
            }
            /* Анализ: требуется ли глоббинг */
            if( quote ) goto noGlob;
            for( p=str; *p; p++ )  /* Есть ли символы шаблона? */
                    if( strchr(globchars, *p))
                            goto doGlobbing;
    noGlob:
            args[ (*indx)++ ] = strdup(str);
            return;

    doGlobbing:
           if((fd = opendir (dir)) == NULL){
                  fprintf(stderr, "Can't read %s\n", dir); return;
           }
           while ((dirbuf = readdir (fd)) != NULL ) {
                  if (dirbuf->d_ino == 0) continue;
                  if (strcmp (dirbuf->d_name, ".") == 0 ||
                      strcmp (dirbuf->d_name, "..") == 0) continue;
                  if( match( dirbuf->d_name, str)){
                      args[ (*indx)++ ] = strdup(dirbuf->d_name);
                      nglobbed++;
                  }
           }
           closedir(fd);
           if( !nglobbed){
               printf( "%s: no match\n", str);
               goto noGlob;
           }else{   /* отсортировать */
               qsort(start, nglobbed, sizeof (char *), cmps);
           }
    }

    /* Разбор командной строки */
    int parse(s) register char *s;
    {
            int i; register char *p;
            char tmp[80];   /* очередной аргумент */
            char c;

            /* очистка старых аргументов */
            for(i=0; arg[i]; i++) free(arg[i]), arg[i] = NULL;
            if( fin  ) free(fin ), fin  = NULL;
            if( fout ) free(fout), fout = NULL;
            rout = 0;

            /* разбор строки */
            for( i=0 ;; ){
                    char quote = '\0';

                    /* пропуск пробелов - разделителей слов */
                    while((c = *s) && isspace(c)) s++;
                    if( !c ) break;
                    /* очередное слово */
                    p = tmp;
                    if(*s == '\'' || *s == '"' ){
                    /* аргумент в кавычках */
                            quote = *s++;  /* символ кавычки */
                            while((c = *s) != '\0' && c != quote){
                               if( c == '\\' ){ /* заэкранировано */
                                   c = *++s;
                                   if( !c ) break;
                               }
                               *p++ = c; ++s;
                            }
                            if(c == '\0')
                      fprintf(stderr, "Нет закрывающей кавычки %c\n", quote);
                            else s++; /* проигнорировать кавычку на конце */
                    } else
                            while((c = *s) && !isspace(c)){
                               if(c == '\\') /* заэкранировано */
                                    if( !(c = *++s))
                                         break /* while */;
                               *p++ = c; s++;
                            }
                    *p = '\0';
                    /* Проверить, не есть ли это перенаправление
                     * ввода/вывода. В отличие от sh и csh
                     * здесь надо писать >ФАЙЛ <ФАЙЛ
                     * >< вплотную к имени файла.
                     */
                    p = tmp;        /* очередное слово */
                    if( *p == '>'){ /* перенаправлен вывод */
                        p++;
                        if( fout ) free(fout), rout = 0; /* уже было */
                        if( *p == '>' ){ rout |= APPEND; p++; }
                        if( *p == '&' ){ rout |= ERRTOO; p++; }
                        if( !*p ){
                                fprintf(stderr, "Нет имени для >\n");
                                fout = NULL; rout = 0;
                        } else  fout = strdup(p);
                    } else if( *p == '<' ){  /* перенаправлен ввод */
                        p++;
                        if( fin ) free(fin);        /* уже было */
                        if( !*p ){
                                fprintf(stderr, "Нет имени для <\n");
                                fin = NULL;
                        } else  fin = strdup(p);
                    } else /* добавить имена к аргументам */
                                glob( ".", arg, &i, p, quote );
            }
            arg[i] = NULL; return i;
    }

    /* Установить имя пользователя */
    void setuser(){
            int uid = getuid();     /* номер пользователя, запустившего Шелл */
            char *user = "mr. Nobody";      /* имя пользователя     */
            char *home = "/tmp";            /* его домашний каталог */
            struct passwd *pp = getpwuid( uid );
            if( pp != NULL ){
                    if(pp->pw_name && *pp->pw_name ) user = pp->pw_name;
                    if(               *pp->pw_dir  ) home = pp->pw_dir;
            }
            setenv("USER", user); setenv("HOME", home);
    }

    void setcwd(){ /* Установить имя текущего каталога */
            char cwd[512];
            getwd(cwd); setenv( "CWD", cwd );
    }
    void main(ac, av, ev) char *av[], *ev[]; {
            int argc;              /* количество аргументов */
            char *prompt;          /* приглашение           */

            setuser(); setcwd();
            signal(SIGINT, SIG_IGN);
            setbuf(stdout, NULL);  /* отменить буферизацию */
            for(;;){
                 prompt = getenv( "prompt" ); /* setenv prompt -->\  */
                 printf( prompt ? prompt : "@ ");/* приглашение */
                 if( gets(cmd) == NULL /* at EOF  */ ) exit(0);
                 argc = parse(cmd);
                 if( !argc) continue;
                 if( !strcmp(arg[0], "exit" )) exit(0);

                 if( !strcmp(arg[0], "cd" )){
                     char *d = (argc==1) ? getenv("HOME"):arg[1];
                     if(chdir(d) < 0)
                        printf( "Не могу войти в %s\n", d );
                     else setcwd();
                     continue;
                 }

                 if( !strcmp(arg[0], "echo" )){
                     register i; FILE *fp;
                     if( fout ){
                        if((fp = fopen(fout, rout & APPEND ? "a":"w"))
                               == NULL) continue;
                     }  else fp = stdout;
                     for(i=1; i < argc; i++ )
                        fprintf( fp, "%s%s", arg[i], i == argc-1 ? "\n":"   ");
                     if( fp != stdout ) fclose(fp);
                     continue;
                 }

                 if( !strcmp(arg[0], "setenv" )){
                          if( argc == 1 ) printenv();
                     else if( argc == 2 ) setenv( arg[1], "" );
                     else                 setenv( arg[1], arg[2]);
                     continue;
                 }
                 cmdExec(arg[0], (char **) arg, environ, fin, fout, rout);
            }
    }

    /* -----------------------------------------------------------*/
    /* Отсортировать и напечатать окружение */
    void printenv(){
            char *e[40]; register i = 0; char *p, **q = e;

            do{
                 p = e[i] = environ[i]; i++;
            } while( p );

    #ifdef SORT
            qsort( e, --i /* сколько */, sizeof(char *), cmps);
    #endif
            while( *q )
                 printf( "%s\n", *q++ );
    }

    /* Сравнение имени переменной окружения с name */
    static char *envcmp(name, evstr) char *name, *evstr;
    {
            char *p; int code;
            if((p = strchr(evstr, '=')) == NULL ) return NULL; /* error ! */
            *p = '\0';      /* временно */
            code = strcmp(name, evstr);
            *p = '=';       /* восстановили */
            return code==0 ? p+1 : NULL;
    }

    /* Установить переменную окружения */
    void setenv( name, value ) char *name, *value;
    {
            static malloced = 0;    /* 1, если environ перемещен */
            char *s, **p, **newenv;
            int len, change_at = (-1), i;

            /* Есть ли переменная name в environ-е ? */
            for(p = environ; *p; p++ )
                    if(s = envcmp(name, *p)){       /* уже есть */
                            if((len = strlen(s)) >= strlen(value)){
                                /* достаточно места */
                                strcpy(s, value); return;
                            }
                            /* Если это новый environ ... */
                            if( malloced ){
                                free( *p ); *p = str3spl(name, "=", value);
                                return;
                            }
                            /* иначе создаем копию environ-а */
                            change_at = p - environ;        /* индекс */
                            break;
                    }
            /* Создаем копию environ-а. Если change_at == (-1), то
             * резервируем новую ячейку для еще не определенной переменной */
            for(p=environ, len=0; *p; p++, len++ );
            /* вычислили количество переменных */
            if( change_at < 0 ) len++;
            if((newenv = (char **) malloc( sizeof(char *) * (len+1)))
                    == (char **) NULL) return;
            for(i=0; i < len+1; i++ ) newenv[i] = NULL;     /* зачистка */
            /* Копируем старый environ в новый */
            if( !malloced ) /* исходный environ в стеке (дан системой) */
                 for(i=0; environ[i]; i++ ) newenv[i] = strdup(environ[i]);
            else for(i=0; environ[i]; i++ ) newenv[i] =        environ[i];
            /* Во втором случае строки уже были спасены, копируем ссылки */

            /* Изменяем, если надо: */
            if( change_at >= 0 ){
                    free( newenv[change_at] );
                    newenv[change_at] = str3spl(name, "=", value);
            } else {        /* добавить в конец новую переменную */
                    newenv[len-1] = str3spl(name, "=", value);
            }
            /* подменить environ */
            if( malloced ) free( environ );
            environ = newenv; malloced++;
            qsort( environ, len, sizeof(char *), cmps);
    }

    /* Допишите команды:
       unsetenv имя_переменной - удаляет переменную среды;
       exit N                  - завершает интерпретатор с
                                 кодом возврата N (это целое число);
    */

© Copyright А. Богатырев, 1992-95
Си в UNIX

Назад | Содержание | Вперед

Подписка на новости CITForum.ru

Новые публикации:

19 мая

  • Прозрачный механизм удаленного обслуживания системных вызовов

  • Система моделирования Grid: реализация и возможности применения

    Газета:

    Майкл Стоунбрейкер:

  • Ошибки в системах баз данных, согласованность "в конечном счете" и теорема CAP

  • Дискуссия по поводу "NoSQL" не имеет никакого отношения к SQL

    29 апреля

  • Материалы конференции "Корпоративные Базы Данных-2010"

  • Разные облики технологии баз данных (отчет о конференции)

    14 апреля

  • MapReduce: внутри, снаружи или сбоку от параллельных СУБД?

  • Научные вызовы технологиям СУБД

    Обзоры журнала Computer:

    31 марта

  • Рационализация согласованности в "облаках": не платите за то, что вам не требуется

  • Взаимные блокировки в Oracle

  • Архитектура среды тестирования на основе моделей, построенная на базе компонентных технологий

  • Объектное представление XML-документов

    Газета:

  • Microsoft для российских разработчиков: практика с элементами фундаментальности

    10 марта

  • HadoopDB: архитектурный гибрид технологий MapReduce и СУБД для аналитических рабочих нагрузок

  • Классификация OLAP-систем вида xOLAP

  • BGP. Три внешних канала. Балансировка исходящего и входящего трафиков

    Газета:

  • Что мы знаем об iPhone 4G?

    17 февраля

  • MapReduce и параллельные СУБД: друзья или враги?

  • Объектно-ориентированное программирование в ограничениях: новый подход на основе декларативных языков моделирования данных

  • Системологический подход к декомпозиции в объектно-ориентированном анализе и проектировании программного обеспечения

    Газета:

  • Эволюция Wine

    3 февраля

  • Дом на песке

  • Реальное переосмысление "формальных методов"

  • Интервью с Найджелом Пендзом

    Газета:

  • iPad. Первый взгляд на долгожданный планшет от Apple

  • Я не верю в iPad

    20 января

  • SQL/MapReduce: практический подход к поддержке самоописываемых, полиморфных и параллелизуемых функций, определяемых пользователями

  • Данные на лету: как технология потокового SQL помогает преодолеть кризис

    Обзоры журнала Computer:

    2 декабря

  • Сергей Кузнецов. Год эпохи перемен в технологии баз данных

    18 ноября

  • Генерация тестовых программ для подсистемы управления памятью микропроцессора

  • Сравнительный анализ современных технологий разработки тестов для моделей аппаратного обеспечения

    Все публикации >>>


  • IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

    Информация для рекламодателей PR-акции, размещение рекламы — тел. +7 495 6608306, ICQ 232284597 Пресс-релизы — pr@citforum.ru
    Послать комментарий
    Информация для авторов

    Редакция раздаёт котят!

    Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
    Copyright © 1997-2000 CIT, © 2001-2009 CIT Forum
    Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...