C++ CSS HTML Java JavaScript MySQL Oracle PERL PHP SQL Unix VBScript XHTML XML Сети
6.12. Простой интерпретатор команд.
 
6.12.  Простой интерпретатор команд.

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

	<FILE >FILE >>FILE >&FILE >>&FILE
	* Сборка: cc -U42 -DCWDONLY sh.c match.c pwd.c -o sh
	*/

	<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;	/* флаги перенаправления вывода */

	 *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;	/* Создан ли файл ? */

	>>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 */
	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];	/* полное имя программы */

	 */
	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 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 */
	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;
	}


	 */
	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 (это целое число);
	*/

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

Главная