Хрестоматия по программированию на Си в Unix


Работа с файлами. Хрестоматия по программированию на Си в Unix - стр. 4


#include <stdio.h>

void message(char *s){ FILE *fptty = fopen("/dev/tty", "w"); fprintf(fptty, "%s\n", s); fclose (fptty); } main(){ message("Tear down the wall!"); }

Это устройство доступно и для записи (на экран) и для чтения (с клавиатуры).

Файлы устройств нечувствительны к флагу открытия

O_TRUNC - он не имеет для них смысла и просто игнорируется. Поэтому невозможно случайно уничтожить файл-устройство (к примеру /dev/tty) вызовом

fd=creat("/dev/tty", 0644);

Файлы-устройства создаются вызовом mknod, а уничтожаются обычным unlink-ом. Более подробно про это - в главе "Взаимодействие с UNIX".

Эмуляция основ библиотеки STDIO, по мотивам 4.2 BSD.

#include <fcntl.h>

#define BUFSIZ 512 /* стандартный размер буфера */ #define _NFILE 20 #define EOF (-1) /* признак конца файла */ #define NULL ((char *) 0) #define IOREAD 0x0001 /* для чтения */ #define IOWRT 0x0002 /* для записи */ #define IORW 0x0004 /* для чтения и записи */ #define IONBF 0x0008 /* не буферизован */ #define IOTTY 0x0010 /* вывод на терминал */ #define IOALLOC 0x0020 /* выделен буфер malloc-ом */ #define IOEOF 0x0040 /* достигнут конец файла */ #define IOERR 0x0080 /* ошибка чтения/записи */ extern char *ttyname(); char *tname = ttyname(fd);

Она выдаст строку, подобную "/dev/tty01". Если fd не связан с терминалом - она вернет

extern char *malloc(); extern long lseek(); typedef unsigned char uchar; uchar sibuf[BUFSIZ], sobuf[BUFSIZ]; typedef struct _iobuf { int cnt; /* счетчик */ uchar *ptr, *base; /* указатель в буфер и на его начало */ int bufsiz, flag, file; /* размер буфера, флаги, дескриптор */ } FILE; FILE iob[_NFILE] = { { 0, NULL, NULL, 0, IOREAD, 0 }, { 0, NULL, NULL, 0, IOWRT|IOTTY, 1 }, { 0, NULL, NULL, 0, IOWRT|IONBF, 2 }, }; #define stdin (&iob[0]) #define stdout (&iob[1]) #define stderr (&iob[2]) #define putchar(c) putc((c), stdout) #define getchar() getc(stdin) #define fileno(fp) ((fp)->file) #define feof(fp) (((fp)->flag & IOEOF) != 0) #define ferror(fp) (((fp)->flag & IOERR) != 0) #define clearerr(fp) ((void) ((fp)->flag &= ~(IOERR | IOEOF))) #define getc(fp) (--(fp)->cnt < 0 ? \ filbuf(fp) : (int) *(fp)->ptr++) #define putc(x, fp) (--(fp)->cnt < 0 ? \ flsbuf((uchar) (x), (fp)) : \ (int) (*(fp)->ptr++ = (uchar) (x))) int fputc(int c, FILE *fp){ return putc(c, fp); } int fgetc( FILE *fp){ return getc(fp); } /* Открытие файла */ FILE *fopen(char *name, char *how){ register FILE *fp; register i, rw; for(fp = iob, i=0; i < _NFILE; i++, fp++) if(fp->flag == 0) goto found; return NULL; /* нет свободного слота */ found: rw = how[1] == '+'; if(*how == 'r'){ if((fp->file = open(name, rw ? O_RDWR:O_RDONLY)) < 0) return NULL; fp->flag = IOREAD; } else { if((fp->file = open(name, (rw ? O_RDWR:O_WRONLY)| O_CREAT | (*how == 'a' ? O_APPEND : O_TRUNC), 0666 )) < 0) return NULL; fp->flag = IOWRT; } if(rw) fp->flag = IORW; fp->bufsiz = fp->cnt = 0; fp->base = fp->ptr = NULL; return fp; } /* Принудительный сброс буфера */ void fflush(FILE *fp){ uchar *base; int full= 0; if((fp->flag & (IONBF|IOWRT)) == IOWRT && (base = fp->base) != NULL && (full=fp->ptr - base) > 0){ fp->ptr = base; fp->cnt = fp->bufsiz; if(write(fileno(fp), base, full) != full) fp->flag |= IOERR; } } /* Закрытие файла */ void fclose(FILE *fp){ if((fp->flag & (IOREAD|IOWRT|IORW)) == 0 ) return; fflush(fp); close(fileno(fp)); if(fp->flag & IOALLOC) free(fp->base); fp->base = fp->ptr = NULL; fp->cnt = fp->bufsiz = fp->flag = 0; fp->file = (-1); } /* Закрытие файлов при exit()-е */ void _cleanup(){ register i; for(i=0; i < _NFILE; i++) fclose(iob + i); } /* Завершить текущий процесс */ void exit(uchar code){ _cleanup(); _exit(code); /* Собственно системный вызов */ } /* Прочесть очередной буфер из файла */ int filbuf(FILE *fp){ static uchar smallbuf[_NFILE]; if(fp->flag & IORW){ if(fp->flag & IOWRT){ fflush(fp); fp->flag &= ~IOWRT; } fp->flag |= IOREAD; /* операция чтения */ } if((fp->flag & IOREAD) == 0 feof(fp)) return EOF; while( fp->base == NULL ) /* отвести буфер */ if( fp->flag & IONBF ){ /* небуферизованный */ fp->base = &smallbuf[fileno(fp)]; fp->bufsiz = sizeof(uchar); } else if( fp == stdin ){ /* статический буфер */ fp->base = sibuf; fp->bufsiz = sizeof(sibuf); } else if((fp->base = malloc(fp->bufsiz = BUFSIZ)) == NULL) fp->flag |= IONBF; /* не будем буферизовать */ else fp->flag |= IOALLOC; /* буфер выделен */ if( fp == stdin && (stdout->flag & IOTTY)) fflush(stdout); fp->ptr = fp->base; /* сбросить на начало буфера */ if((fp->cnt = read(fileno(fp), fp->base, fp->bufsiz)) == 0 ){ fp->flag |= IOEOF; if(fp->flag & IORW) fp->flag &= ~IOREAD; return EOF; } else if( fp->cnt < 0 ){ fp->flag |= IOERR; fp->cnt = 0; return EOF; } return getc(fp); } /* Вытолкнуть очередной буфер в файл */ int flsbuf(int c, FILE *fp){ uchar *base; int full, cret = c; if( fp->flag & IORW ){ fp->flag &= ~(IOEOF|IOREAD); fp->flag |= IOWRT; /* операция записи */ } if((fp->flag & IOWRT) == 0) return EOF; tryAgain: if(fp->flag & IONBF){ /* не буферизован */ if(write(fileno(fp), &c, 1) != 1) { fp->flag |= IOERR; cret=EOF; } fp->cnt = 0; } else { /* канал буферизован */ if((base = fp->base) == NULL){ /* буфера еще нет */ if(fp == stdout){ if(isatty(fileno(stdout))) fp->flag |= IOTTY; else fp->flag &= ~IOTTY; fp->base = fp->ptr = sobuf; /* статический буфер */ fp->bufsiz = sizeof(sobuf); goto tryAgain; } if((base = fp->base = malloc(fp->bufsiz = BUFSIZ))== NULL){ fp->bufsiz = 0; fp->flag |= IONBF; goto tryAgain; } else fp->flag |= IOALLOC; } else if ((full = fp->ptr - base) > 0) if(write(fileno(fp), fp->ptr = base, full) != full) { fp->flag |= IOERR; cret = EOF; } fp->cnt = fp->bufsiz - 1; *base++ = c; fp->ptr = base; } return cret; } /* Вернуть символ в буфер */ int ungetc(int c, FILE *fp){ if(c == EOF fp->flag & IONBF fp->base == NULL) return EOF; if((fp->flag & IOREAD)==0 fp->ptr <= fp->base) if(fp->ptr == fp->base && fp->cnt == 0) fp->ptr++; else return EOF; fp->cnt++; return(* --fp->ptr = c); } /* Изменить размер буфера */ void setbuffer(FILE *fp, uchar *buf, int size){ fflush(fp); if(fp->base && (fp->flag & IOALLOC)) free(fp->base); fp->flag &= ~(IOALLOC|IONBF); if((fp->base = fp->ptr = buf) == NULL){ fp->flag |= IONBF; fp->bufsiz = 0; } else fp->bufsiz = size; fp->cnt = 0; } /* "Перемотать" файл в начало */ void rewind(FILE *fp){ fflush(fp); lseek(fileno(fp), 0L, 0); fp->cnt = 0; fp->ptr = fp->base; clearerr(fp); if(fp->flag & IORW) fp->flag &= ~(IOREAD|IOWRT); } /* Позиционирование указателя чтения/записи */ #ifdef COMMENT base ptr случай IOREAD | |<----cnt---->| 0L |б у |ф е р | |=======######@@@@@@@@@@@@@@======== файл file | |<-p->|<-dl-->| |<----pos---->| | | |<----offset(new)-->| | |<----RWptr---------------->| где pos = RWptr - cnt; // указатель с поправкой offset = pos + p = RWptr - cnt + p = lseek(file,0L,1) - cnt + p отсюда: (для SEEK_SET) p = offset+cnt-lseek(file,0L,1); или (для SEEK_CUR) dl = RWptr - offset = p - cnt lseek(file, dl, 1); Условие, что указатель можно сдвинуть просто в буфере: if( cnt > 0 && p <= cnt && base <= ptr + p ){ ptr += p; cnt -= p; } #endif /*COMMENT*/ int fseek(FILE *fp, long offset, int whence){ register resync, c; long p = (-1); clearerr(fp); if( fp->flag & (IOWRT|IORW)){ fflush(fp); if(fp->flag & IORW){ fp->cnt = 0; fp->ptr = fp->base; fp->flag &= ~IOWRT; } p = lseek(fileno(fp), offset, whence); } else if( fp->flag & IOREAD ){ if(whence < 2 && fp->base && !(fp->flag & IONBF)){ c = fp->cnt; p = offset; if(whence == 0) /* SEEK_SET */ p += c - lseek(fileno(fp), 0L, 1); else offset -= c; if(!(fp->flag & IORW) && c > 0 && p <= c && p >= fp->base - fp->ptr ){ fp->ptr += (int) p; fp->cnt -= (int) p; return 0; /* done */ } resync = offset & 01; } else resync = 0; if(fp->flag & IORW){ fp->ptr = fp->base; fp->flag &= ~IOREAD; resync = 0; } p = lseek(fileno(fp), offset-resync, whence); fp->cnt = 0; /* вынудить filbuf(); */ if(resync) getc(fp); } return (p== -1 ? -1 : 0); } /* Узнать текущую позицию указателя */ long ftell(FILE *fp){ long tres; register adjust; if(fp->cnt < 0) fp->cnt = 0; if(fp->flag & IOREAD) adjust = -(fp->cnt); else if(fp->flag & (IOWRT|IORW)){ adjust = 0; if(fp->flag & IOWRT && fp->base && !(fp->flag & IONBF)) /* буферизован */ adjust = fp->ptr - fp->base; } else return (-1L); if((tres = lseek(fileno(fp), 0L, 1)) < 0) return tres; return (tres + adjust); }

* Заметим еще, что если дескриптор fd связан с терминалом, то можно узнать полное имя этого устройства вызовом стандартной функции

** Ссылка на управляющий терминал процесса хранится в u-area каждого процесса: u_ttyp, u_ttyd, поэтому ядро в состоянии определить какой настоящий терминал следует открыть для вас. Если разные процессы открывают /dev/tty, они могут открыть в итоге разные терминалы, т.е. одно имя приводит к разным устройствам! Смотри главу про UNIX.

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

| |




- Начало -  - Назад -  



Книжный магазин