strcpy (pathname, ROOTDIR);
else {
strcpy (pathname, pnptr);
if (chdir (pnptr) < 0) {
GETWDERR ("getwd: can't change back to .");
return (NULL);
}
}
return (pathname);
fail:
chdir (prepend (CURDIR, pnptr));
return (NULL);
}
#endif
А. Богатырев, 1992-95 - 257 - Си в UNIX
/*
* prepend() tacks a directory name onto the front of a pathname.
*/
static char *prepend (
register char *dirname, /* что добавлять */
register char *pathname /* к чему добавлять */
) {
register int i; /* длина имени каталога */
for (i = 0; *dirname != '\0'; i++, dirname++)
continue;
if ((pathsize += i) < MAXPATHLEN)
while (i-- > 0)
*--pathname = *--dirname;
return (pathname);
}
#ifndef CWDONLY
void main(){
char buffer[MAXPATHLEN+1];
char *cwd = getwd(buffer);
printf( "%s%s\n", cwd ? "": "ERROR:", buffer);
}
#endif
6.10.2. Напишите функцию canon(), канонизирующую имя файла, т.е. превращающую его в
полное имя (от корневого каталога), не содержащее компонент "." и "..", а также лиш-
них символов слэш '/'. Пусть, к примеру, текущий рабочий каталог есть /usr/abs/C-
book. Тогда функция преобразует
. -> /usr/abs/C-book
.. -> /usr/abs
../.. -> /usr
////.. -> /
/aa -> /aa
/aa/../bb -> /bb
cc//dd/../ee -> /usr/abs/C-book/cc/ee
../a/b/./d -> /usr/abs/a/b/d
Ответ:
#include
/* слэш, разделитель компонент пути */
#define SLASH '/'
extern char *strchr (char *, char),
*strrchr(char *, char);
struct savech{ char *s, c; };
#define SAVE(sv, str) (sv).s = (str); (sv).c = *(str)
#define RESTORE(sv) if((sv).s) *(sv).s = (sv).c
/* Это структура для использования в таком контексте:
void main(){
char *d = "hello"; struct savech ss;
SAVE(ss, d+3); *(d+3) = '\0'; printf("%s\n", d);
RESTORE(ss); printf("%s\n", d);
}
*/
/* ОТСЕЧЬ ПОСЛЕДНЮЮ КОМПОНЕНТУ ПУТИ */
struct savech parentdir(char *path){
char *last = strrchr( path, SLASH );
А. Богатырев, 1992-95 - 258 - Си в UNIX
char *first = strchr ( path, SLASH );
struct savech sp; sp.s = NULL; sp.c = '\0';
if( last == NULL ) return sp; /* не полное имя */
if( last[1] == '\0' ) return sp; /* корневой каталог */
if( last == first ) /* единственный слэш: /DIR */
last++;
sp.s = last; sp.c = *last; *last = '\0';
return sp;
}
#define isfullpath(s) (*s == SLASH)
/* КАНОНИЗИРОВАТЬ ИМЯ ФАЙЛА */
void canon(
char *where, /* куда поместить ответ */
char *cwd, /* полное имя текущего каталога */
char *path /* исходное имя для канонизации */
){ char *s, *slash;
/* Сформировать имя каталога - точки отсчета */
if( isfullpath(path)){
s = strchr(path, SLASH); /* @ */
strncpy(where, path, s - path + 1);
where[s - path + 1] = '\0';
/* или даже просто strcpy(where, "/"); */
path = s+1; /* остаток пути без '/' в начале */
} else strcpy(where, cwd);
/* Покомпонентный просмотр пути */
do{ if(slash = strchr(path, SLASH)) *slash = '\0';
/* теперь path содержит очередную компоненту пути */
if(*path == '\0' || !strcmp(path, ".")) ;
/* то просто проигнорировать "." и лишние "///" */
else if( !strcmp(path, ".."))
(void) parentdir(where);
else{ int len = strlen(where);
/* добавить в конец разделяющий слэш */
if( where[len-1] != SLASH ){
where[len] = SLASH;
where[len+1] = '\0';
}
strcat( where+len, path );
/* +len чисто для ускорения поиска
* конца строки внутри strcat(); */
}
if(slash){ *slash = SLASH; /* восстановить */
path = slash + 1;
}
} while (slash != NULL);
}
char cwd[256], input[256], output[256];
void main(){
/* Узнать полное имя текущего каталога.
* getcwd() - стандартная функция, вызывающая
* через popen() команду pwd (и потому медленная).
*/
getcwd(cwd, sizeof cwd);
while( gets(input)){
canon(output, cwd, input);
printf("%-20s -> %s\n", input, output);
}
}
А. Богатырев, 1992-95 - 259 - Си в UNIX
В этом примере (изначально писавшемся для MS DOS) есть "странное" место, помеченное
/*@*/. Дело в том, что в DOS функция isfullpath была способна распознавать имена фай-
лов вроде C:\aaa\bbb, которые не обязательно начинаются со слэша.
6.11. Мультиплексирование ввода-вывода.
Данная глава посвящена системному вызову select, который, однако, мы предостав-
ляем вам исследовать самостоятельно. Его роль такова: он позволяет опрашивать нес-
колько дескрипторов открытых файлов (или устройств) и как только в файле появляется
новая информация - сообщать об этом нашей программе. Обычно это бывает связано с
дескрипторами, ведущими к сетевым устройствам.
6.11.1.
/* Пример использования вызова select() для мультиплексирования
* нескольких каналов ввода. Этот вызов можно также использовать
* для получения таймаута.
* Вызов: войти на терминалах tty01 tty02 и набрать на каждом
* sleep 30000
* затем на tty00 сказать select /dev/tty01 /dev/tty02
* и вводить что-либо на терминалах tty01 и tty02
* Сборка: cc select.c -o select -lsocket
*/
#include
#include
#include /* fd_set, FD_SET, e.t.c. */
#include /* NOFILE */
#include
#include
#include /* для FIONREAD */
#define max(a,b) ((a) > (b) ? (a) : (b))
char buf[512]; /* буфер чтения */
int fdin, fdout; /* дескрипторы каналов stdin, stdout */
int nready; /* число готовых каналов */
int nopen; /* число открытых каналов */
int maxfd = 0; /* максимальный дескриптор */
int nfds; /* сколько первых дескрипторов проверять */
int f; /* текущий дескриптор */
fd_set set, rset; /* маски */
/* таблица открытых нами файлов */
struct _fds {
int fd; /* дескриптор */
char name[30]; /* имя файла */
} fds[ NOFILE ] = { /* NOFILE - макс. число открытых файлов на процесс */
{ 0, "stdin" }, { 1, "stdout" }, { 2, "stderr" }
/* все остальное - нули */
};
struct timeval timeout, rtimeout;
/* выдать имя файла по дескриптору */
char *N( int fd ){
register i;
for(i=0; i < NOFILE; i++)
if(fds[i].fd == fd ) return fds[i].name;
return "???";
}
А. Богатырев, 1992-95 - 260 - Си в UNIX
void main( int ac, char **av ){
nopen = 3; /* stdin, stdout, stderr */
for( f = 3; f < NOFILE; f++ ) fds[f].fd = (-1);
fdin = fileno(stdin); fdout = fileno(stdout);
setbuf(stdout, NULL); /* отмена буферизации */
FD_ZERO(&set); /* очистка маски */
for(f=1; f < ac; f++ )
if((fds[nopen].fd = open(av[f], O_RDONLY)) < 0 ){
fprintf(stderr, "Can't read %s\n", av[f] );
continue;
} else {
FD_SET(fds[nopen].fd, &set ); /* учесть в маске */
maxfd = max(maxfd, fds[nopen].fd );
strncpy(fds[nopen].name, av[f], sizeof(fds[0].name) - 1);
nopen++;
}
if( nopen == 3 ){
fprintf(stderr, "Nothing is opened\n");
exit(1);
}
FD_SET(fdin, &set); /* учесть stdin */
maxfd = max(maxfd, fdin );
nopen -= 2; /* stdout и stderr не участвуют в select */
timeout.tv_sec = 10; /* секунд */
timeout.tv_usec = 0; /* миллисекунд */
/* nfds - это КОЛИЧЕСТВО первых дескрипторов, которые надо
* просматривать. Здесь можно использовать
* nfds = NOFILE; (кол-во ВСЕХ дескрипторов )
* или nfds = maxfd+1; (кол-во = номер последнего+1)
* ( +1 т.к. нумерация fd идет с номера 0, а количество - с 1).
*/
nfds = maxfd + 1;
while( nopen ){
rset = set; rtimeout = timeout; /* копируем, т.к. изменятся */
/* опрашивать можно FIFO-файлы, терминалы, pty, socket-ы, stream-ы */
nready = select( nfds, &rset, NULL, NULL, &rtimeout );
/* Если вместо &rtimeout написать NULL, то ожидание будет
* бесконечным (пока не собьют сигналом)
*/
if( nready <= 0 ){ /* ничего не поступило */
fprintf(stderr, "Timed out, nopen=%d\n", nopen);
continue;
}
А. Богатырев, 1992-95 - 261 - Си в UNIX
/* опрос готовых дескрипторов */
for(f=0; f < nfds; f++ )
if( FD_ISSET(f, &rset)){ /* дескриптор f готов */
int n;
/* Вызов FIONREAD позволяет запросить
* число байт готовых к передаче
* через дескриптор.
*/
if(ioctl(f, FIONREAD, &n) < 0)
perror("FIONREAD");
else printf("%s have %d bytes.\n", N(f), n);
if((n = read(f, buf, sizeof buf)) <= 0 ){
eof:
FD_CLR(f, &set); /* исключить */
close(f); nopen--;
fprintf(stderr, "EOF in %s\n", N(f));
} else {
fprintf(stderr, "\n%d bytes from %s:\n", n, N(f));
write(fdout, buf, n);
if( n == 4 && !strncmp(buf, "end\n", 4))
/* ncmp, т.к. buf может не оканчиваться \0 */
goto eof;
}
}
}
exit(0);
}
6.11.2. В качестве самостоятельной работы предлагаем вам пример программы, ведущей
протокол сеанса работы. Информацию о псевдотерминалах изучите самостоятельно.
А. Богатырев, 1992-95 - 262 - Си в UNIX
/*
* script.c
* Программа получения трассировки работы других программ.
* Используется системный вызов опроса готовности каналов