Пример 30

/* /bin/cc -M2 -Ml -DMATCHONLY -LARGE dosfs.c match.c -o dosfs
 * Копирование файлов с дискеты, записанной в MS DOS, в UNIX.
 * Предполагается, что ваша UNIX-машина имеет соответствующий драйвер
 * для чтения дискет, сформатированных на IBM PC.
 * match.c - файл, содержащий текст функции match().
 */
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

extern char *malloc();  /* выделитель памяти */
extern char *strrchr(); /* поиск последнего вхождения буквы */
extern long lseek();
void readBoot(), readFAT(), readRootDir(), main(), line(), getFile(),
     doDirectory(), mkname(), enterDir(), countFree(), traceclu();

int fd;         /* дескриптор файла - дисковода */

FILE *mapfp;     /* файл трассировки  */
int trace = 0;   /* трассировка пока выключена */
int ask = 1;     /* спрашивать ли подтверждение на перезапись файлов */
int dironly = 0; /* 1: только показывать имена, файлы не скидывать   */

typedef unsigned char  uchar;
/*typedef unsigned short ushort; Есть в sys/types.h */

/* Формат сектора загрузки */
struct boot {
        char jmp[3];      /* команда jmp */
        char label[8];    /* название системы */
        char bfs[2];      /* размер boot-сектора */
        uchar sectorsPerCluster; /* число секторов в кластере */
        char fatoff[2];   /* смещение до начала FAT */
        uchar copies;     /* число копий FAT  */
        char dirsize[2];  /* число записей в корневом каталоге */
        char sectors[2];  /* размер дискеты в секторах */
        uchar desc;       /* описатель типа дискеты */
        char FATsize[2];  /* размер FAT в секторах */
        char sectorsPerTrack[2]; /* число секторов на трек */
        char sides[2];    /* число сторон (1, 2) */
        char hidden[2];   /* число спрятанных секторов */
} *boot;

#define SECTOR 512      /* Размер сектора в байтах   */
int CLU;        /* Размер кластера в байтах          */
int SPC;        /* Размер кластера в секторах        */
int SECT;       /* Число секторов на дискете         */
long capacity;  /* емкость дискеты в байтах          */
ushort MAXCLU;  /* максимальный номер кластера + 1   */

int NDIR;       /* Число слотов в корневом каталоге  */
int DIRSIZE;    /* Длина корневого каталога в байтах */
int ENTRperCLUSTER;  /* Количество слотов в одном кластере каталога */

int SPF;        /* Размер FAT в секторах             */
int FATSIZE;    /* Размер FAT в байтах               */
int FATSTART;   /* Смещение до FAT в байтах          */
int NFAT;       /* Количество копий FAT              */

uchar DESC;     /* Описатель типа дискеты            */
int DATACLU;    /* Начало области данных (номер физич. кластера) */
int bit16 = 0;  /* 1 если FAT использует 16-битные поля, а не 12 */

/* Преобразование char[] в integer */
#define INT(s)  ( * (short *)s)
#define LONG(s) ( * (long  *)s)

/* Формат одной записи каталога. */
struct dir{
        char name[8];   /* имя файла            */
        char ext[3];    /* расширение (суффикс) */
        uchar attrib;   /* атрибуты файла       */
        char unused[10];
        char creat_time[2];     /* время создания */
        char creat_date[2];     /* дата создания  */
        char firstCluster[2];   /* начальный кластер */
        char size[4];           /* размер в байтах */
};
#define isdir(attr)     (attr & 0x10)     /* Является ли каталогом ? */
#define islabel(attr)   (attr & 0x08)     /* Метка тома ?            */

#define eq(s1, s2)      (!strcmp(s1, s2)) /* сравнение строк на ==   */

struct dir *droot;  /* Содержимое корневого каталога */
char *FAT1;         /* File Allocation Table, копия 1 */
char *FAT2;         /*                        копия 2 */
char cwd[256] = "";     /* Текущий каталог в DOS. "" - корневой  */
char *root = "/tmp";    /* Каталог в UNIX, куда копируются файлы */

char *pattern = NULL;   /* шаблон базового имени */
char *dirpattern;       /* каталог (не шаблон)   */

char newname[256];      /* буфер дла генерации имен */
char cluster[4098];     /* буфер для чтения кластера */

/* Чтение n байт по адресу s */
Read(fd, s, n) char *s;
{
        int nn = read(fd, s, n);
        if(nn != n ){
                fprintf(stderr, "Ошибка чтения: %d вместо %d\n", nn, n);
                perror( "read" ); exit(1);
        }
        return nn;
}

/* Позиционирование головок */
long Lseek(fd, off, how) long off;
{
        long offf;
        if((offf = lseek(fd, off, how)) < 0){
                fprintf(stderr, "Ошибка lseek(%ld,%d)\n", off, how);
        }
        return offf;
}

/* Отведение памяти и ее зачистка */
char *Malloc(n) unsigned n;{
        char *ptr = malloc(n);
        register unsigned i;
        if( !ptr){
                fprintf(stderr, "Не могу malloc(%u)\n", n ); exit(2);
        }
        for(i=0; i < n ; i++ ) ptr[i] = 0;
        /* Можно было бы использовать ptr = calloc(1,n); эта функция
         * как раз отводит и очищает память */
        return ptr;
}

/* Нарисовать горизонтальную черту */
void line(c) char c;{
        register i;
        for(i=0; i < 78; i++) putchar(c);
        putchar('\n');
}

/* Обработка псевдо-имен устройств. Используются имена для XENIX */
char *drive(name) char *name;
{
        if( eq(name, "360")) return "/dev/fd048ds9";
        if( eq(name, "720")) return "/dev/fd096ds9";
        if( eq(name, "1.2")) return "/dev/fd096ds15";
        return name;
}

/* Создать каталог */
char command[512];      /* буфер дла формирования команд */
mkdir(name, mode) char *name;
{
   int retcode;    struct stat st;

   if( stat(name, &st) >= 0 &&
       (st.st_mode & S_IFMT) == S_IFDIR ) return 0; /* уже есть */
   sprintf(command, "mkdir \"%s\"", name );
   retcode = system(command); /* выполнить команду, записанную в command */
   chmod(name, mode & 0777);  /* установить коды доступа */
   return retcode;            /* 0 - успешно */
}

/* Открыть файл, создавая (если надо) недостаюшие каталоги */
FILE *fmdopen(name, mode)
        char *name, *mode;
{
        extern errno;   char *s;    FILE *fp;
        if( fp = fopen(name, mode)) return fp;  /* OK */
        /* иначе файл не смог создаться */
        /* if( errno != ENOENT ) return NULL; /* из-за недостатка прав */
        /* Пробуем создать все каталоги по пути к файлу */
        if((s = strrchr(name, '/' )) == NULL ) return NULL;
        *s = '\0'; md(name); *s = '/';
        return fopen(name, mode);
}

/* Рекурсивный mkdir */
md(path)        char *path;
{       struct stat st; char *s; int code;
        if( !*path) return 0;     /* корневой каталог "/" */
        if( stat(path, &st) >= 0 ){     /* существует */
            if((st.st_mode & S_IFMT) == S_IFDIR) return 0; /* OK */
            printf( "%s - не каталог\n", path ); return 1; /* FAIL */
        }
        if( s = strrchr(path, '/')){
            *s = '\0'; code = md(path); *s = '/';
            if( code ) return code;     /* Облом */
        }
        sprintf(command, "mkdir \"%s\"", path );
        return system(command); /* 0 если OK */
}

/* Сконструировать имя файла в стиле UNIX.
 * В MS DOS все буквы в именах - большие */
void mkname( res, n, e ) char *res, *n, *e;
{ /* res - результат, n - имя, e - суффикс */
        register i; char *start = res;

        if( n[0] == 0x05 ) n[0] = 0xE5;  /* подставной символ */
        for(i=0; i < 8 && n[i] && n[i] != ' ' ; i++)
                *res++ = n[i];
        if( e[0] != ' ')
                *res++ = '.';
        for(i=0; i < 3 && e[i] && e[i] != ' ' ; i++)
                *res++ = e[i];
        *res = '\0';

        while( *start ){
                if( isalpha(*start) && isupper(*start))
                        *start = tolower(*start);
                start++;
        }
}
/* ------------------------------------------------------- */
/* Получить запись из FAT для кластера clu */
ushort numCluster(clu) ushort clu;
{       ushort n;

        if( clu >= MAXCLU )
          printf( "Слишком большой номер кластера %03X >= %03X\n",
                                                  clu,    MAXCLU );
        if( bit16 ){    /* 16 бит на номер кластера */
                n = INT( &FAT1[ 2*clu ]);
                n &= 0xFFFF;
                return n;
        } /* иначе 12 бит на номер кластера */
        n = clu + clu/2 ;
        n = INT( &FAT1[n] );
        if( clu % 2 ){  /* нечетный */
                n >>= 4;
        }
        n &= 0xFFF;
        return n;
}

/* Узнать следующий кластер файла. 0 если последний */
ushort nextCluster(clu) ushort clu;
{
        clu = numCluster(clu);
        if( clu >= (bit16 ? 0xFFF8 : 0xFF8 ))
                return 0;       /* EOF */
        return clu;
}

/* Прочесть кластер и сохранить его в файле и буфере */
getCluster(clu, fp, size, buffer)
        ushort clu;     /* логический кластер (2..) */
        FILE *fp;       /* файл для спасения  */
        long size;      /* осталось дописать  */
        char *buffer;   /* буфер для кластера */
{
        long offset;
        int rd, howmuchtoread;

        if( size <= 0L ){
                printf( "CLUSTER %03X лишний\n", clu ); exit(3);
        }
        /* Вычислить смещение. Кластеры нумеруются начиная с #2 */
        offset = (clu - 2 + DATACLU) * (long) CLU;
        Lseek(fd, offset, 0);

        /* Сколько байт прочесть ? */
        howmuchtoread = (size > CLU) ? CLU : size;
        rd = Read(fd, buffer, howmuchtoread);
        if( fp != NULL )
            fwrite(buffer, 1, rd, fp);
        return ( rd < 0 ) ? 0 : rd;
}
/* ----------------------------------------------------------------- *      dosfs -rPATH    файлы скидываются в каталог PATH, а не в /tmp
 *      dosfs ... "шаблон"    сбрасываются только файлы с подходящими
 *        именами, например:
 *              dosfs 1.2  "/*.c"        *.c из корня дискеты
 *              dosfs 1.2  "/dir1/*.c"   *.c из каталога /dir1
 *              dosfs 1.2  "*.c"         *.c из всех каталогов
 *      dosfs -d        только просмотр каталогов, без сброса файлов
 *      Пример: dosfs -qr. 360
 */
void main(argc, argv) char *argv[];
{
        if( argc < 2 ) goto usage;
        if( *argv[1] == '-' ){  /* разбор ключей */
            char *keys = &argv[1][1];
            while(*keys){
               switch(*keys){
               case 't':  /* включить трассировку */
                   trace++;
                   if((mapfp = fopen( ".Map", "w" )) == NULL )
                       trace = 0;
                   break;
               case 'q':  /* без запросов (quiet) */
                   ask = 0; break;
               case 'r':  /* переназначить root */
                   root = keys+1; goto breakwhile;
               case 'd':  /* dosfs -d == команда dir */
                   dironly++; break;
               }
               keys++;
            }
        breakwhile:
            argc--; argv++;
        }
        if( argc < 2 ) goto usage;
        if( pattern = argv[2] ){   /* может быть NULL */
                char *s = strrchr(pattern, '/');
                if(s){  /*      PATH/PATTERN                */
                    dirpattern  = pattern;       /* PATH    */
                    *s = '\0';    pattern = s+1; /* PATTERN */
                }else{  /*      просто PATTERN              */
                    dirpattern = NULL;
                }
        }
        setbuf(stdout, NULL);   /* отменить буферизацию */
        readBoot(drive(argv[1]));
        readFAT();
        countFree();
        readRootDir();
        exit(0);
usage:
        printf( "Вызов:  dosfs  [-dqtrDIR]  устройство [\"шаблон\"]\n" );
        exit(4);
}

/* Прочесть boot-sector, вычислить разные параметры дискеты */
void readBoot(dsk) char *dsk;
{
        char BOOT[SECTOR];
        int skips, sides;

        if((fd = open( dsk, O_RDONLY)) < 0 ){
                fprintf(stderr, "Не могу читать %s\n", dsk); exit(5);
        }
        /* нулевой сектор дискеты - boot */
        Read(fd, BOOT, SECTOR);
        boot = (struct boot *) BOOT;

        line('-');
        printf( "Сформатировано \"%8.8s\"\n", boot->label );
        printf( "Размер boot-сектора %d байт\n", INT(boot->bfs));
        printf( "Кластер содержит %d секторов\n",
                SPC = boot->sectorsPerCluster );
        printf( "Дискета содержит %d секторов ",
                SECT = INT(boot->sectors));
        capacity = SECT * (long) SECTOR;
        printf( "(%ld KB)\n", capacity / 1024L );
        printf( "На треке %d секторов\n", INT(boot->sectorsPerTrack));
        sides = INT(boot->sides);
        printf( "Диск имеет %d сторон%c\n\n", sides, sides==1? 'у':'ы');

        printf( "Смещение до FAT %d сектор\n",
                skips = INT(boot->fatoff));
        printf( "Имеется %d копии FAT\n", NFAT = boot->copies );
        printf( "FAT занимает %d секторов\n\n", SPF = INT(boot->FATsize));

        printf( "Корневой каталог содержит %d записей\n\n",
                NDIR = INT(boot->dirsize));

        printf( "Описатель дискеты = %02X\t(", DESC = boot->desc );
        switch( DESC ){
        case 0xFF: printf( "double sided, 8 sectors per track" ); break;
        case 0xFE: printf( "single sided, 8 sectors per track" ); break;
        case 0xFD: printf( "double sided, 9 sectors per track" ); break;
        case 0xFC: printf( "single sided, 9 sectors per track" ); break;
        case 0xF9: printf( "double sided, 15 sectors per track"); break;
        case 0xF8: printf( "Winchester" ); bit16++; break;
        default:   printf( "неизвестный тип" ); break;
        }
        printf( ")\n");
        printf( "На диске %d спрятанных секторов\n", INT(boot->hidden));

        /* Вычислить характеристики */
        CLU      = SECTOR * SPC;   /* размер кластера в байтах */
        FATSIZE  = SECTOR * SPF;   /* длина FAT в байтах       */
        FATSTART = SECTOR * skips; /* смещение в байтах до FAT */
        /* длина корневого каталога в байтах */
        DIRSIZE  = NDIR   * sizeof(struct dir);
        /* физический номер первого кластера данных */
        DATACLU  = ((long) FATSTART +
                    (long) FATSIZE * NFAT +
                    (long) DIRSIZE ) / CLU;
        printf( "Первый кластер данных (физ.) = %d\n", DATACLU );
        /* число записей каталога в кластере */
        ENTRperCLUSTER = CLU / sizeof(struct dir);

        /* число секторов для данных */
        MAXCLU = (SECT - DATACLU * SPC);
        /* число кластеров для данных */
        MAXCLU = MAXCLU / SPC;
        /* логические номера кластеров идут с #2 */
        MAXCLU += 2;
}

/* Прочесть File Allocation Table (таблицу размещения файлов) */
void readFAT(){
        register int i;

        FAT1 = Malloc(FATSIZE);

        Lseek(fd, (long) FATSTART, 0);
        Read(fd, FAT1, FATSIZE);
        if(NFAT > 1){
                FAT2 = Malloc(FATSIZE);
                Read(fd, FAT2, FATSIZE);

                /* Сравнить копии FAT */
                for(i=0; i < FATSIZE; i++ )
                        if(FAT1[i] != FAT2[i]){
                           printf( "копии FAT различаются в %d/%d\n",
                                    i, FATSIZE );
                           break;
                        }
                free( FAT2 );
        }
        if( DESC != FAT1[0] )
            printf( "У FAT другой описатель: %02X\n", FAT1[0] & 0xFF );
}

/* Прочесть корневой каталог дискеты.
 * Он расположен сразу же после копий FAT
 */
void readRootDir(){
        if( DIRSIZE % SECTOR )
                printf( "Размер каталога не кратен сектору\n" );
        Lseek(fd, (long)FATSTART + (long)FATSIZE * NFAT, 0);
        droot = (struct dir *) Malloc(DIRSIZE);
        Read(fd, droot, DIRSIZE );
        /* NDIR должно быть 112 для 360K и 720K
         *                  224 для 1.2 Mb
         */
        if( !dironly ) mkdir( root, 0755 );
        line('-');
        doDirectory(0, NDIR, droot);
}

/* Обработать каталог (напечатать, спасти файлы, обойти подкаталоги) */
#define PRINT  \
  for(j=0; j < level; j++ ) printf( "  " ); /* отступ */                \
  printf( "%02d\t%s/%-14s   %12ld   %s\n",                              \
           strt + i,                                                    \
                 cwd,                                                   \
                    basename,                                           \
                            size,                                       \
                                    isdir(dd[i].attrib) ?    "<DIR>"  : \
                                    islabel(dd[i].attrib) ?  "<LAB>"  : "" )

void doDirectory(strt, entries, dd)
        struct dir dd[];
{
        register i, j;
        char basename[40];
        static int level = 0;
        int need_to_get;        /* надо ли сбрасывать */

        /* line('-'); */
        for(i=0; i < entries; i++ ){
           uchar c; long size;

           if((c = *dd[i].name) == 0xE5 || !c)
                   continue;        /* файл стерт (дыра) */
           mkname(basename, dd[i].name, dd[i].ext);
           size = LONG(dd[i].size); /* размер файла */

           /* проверить шаблон имени, если нужно */
           if( !pattern          || /* pattern задан и */
               (   (!dirpattern  || eq(cwd, dirpattern)) &&
                   match(basename, pattern)
               )
           ){  PRINT; need_to_get = !dironly; }
           else       need_to_get = 0;

           if(isdir(dd[i].attrib)){
               /* себя и родителя проигнорировать */
              if( eq(basename, "." ) || eq(basename, ".."))
                   continue;
               level++; /* У каталогов почему-то size == 0 */
                enterDir( basename, INT(dd[i].firstCluster), need_to_get);
               level--;
           } else if( islabel(dd[i].attrib)){
               printf( "Volume label:%11.11s\n", dd[i].name );
           } else if( need_to_get )
               getFile ( basename, INT(dd[i].firstCluster), size);
        }
        /* line('#'); */
}

/* Прочесть файл в UNIX-ную файловую систему */
void getFile(name, clu, size)
        char *name;     /* имя файла */
        ushort clu;     /* начальный кластер */
        long size;      /* размер */
{
        FILE *fp;       /* файл куда сохранять */
        struct stat st;
        ushort nclu = 0;/* порядковый номер кластера */

        sprintf(newname, "%s%s/%s", root, cwd, name );

        if( ask && stat(newname, &st) >= 0 ){
                char answer[30];
                fprintf(stderr, "%s уже существует, перезаписать? ",
                                 newname);
                gets(answer);
                if( *answer != 'y' ) return;
                fprintf( stderr, "\tOK\n" );
        }
        if((fp = fmdopen( newname, "w" )) == NULL){
                printf( "Не могу создать %s\n", newname );
                return;
        }
        if( trace ) fprintf( mapfp, "\n%s/%s:", cwd, name );

        while( clu ){
                if( trace ) traceclu(nclu++, clu);
                size -= getCluster(clu, fp, size, cluster);
                clu = nextCluster(clu);
        }
        fclose(fp);
}

/* Обработать подкаталог */
void enterDir(name, clu, create)
        char *name;     /* имя */
        ushort clu;     /* начальный кластер */
{
        char *tail, *myCluster;
        struct dir *dsub;
        ushort nclu;
        int nentries;   /* число записей в каталоге */

        /* Коррекция cwd */
        tail = cwd + strlen(cwd);
        *tail = '/'; strcpy(tail+1, name);

        if( create ){   /* создать */
            sprintf( newname, "%s%s", root, cwd );
            mkdir  ( newname, 0755);
        }
        if( trace ) fprintf( mapfp, "\nDIR %s:", cwd);

        myCluster = Malloc( sizeof cluster );
        dsub = (struct dir *) myCluster;

        nentries = nclu = 0;
        while( clu ){
                if( trace ) traceclu(nclu++, clu);
                /* Прочесть очередной кластер каталога */
                getCluster(clu, NULL,(long) CLU, myCluster);
                /* Обработать имена в этом кластере */
                doDirectory(nentries, ENTRperCLUSTER, dsub);
                nentries += ENTRperCLUSTER;
                /* Взять следующий кластер */
                clu = nextCluster(clu);
        }
        *tail = '\0';   free(myCluster);
}

/* Подсчет свободных и плохих кластеров. */
void countFree(){
        int isFree = 0;       /* свободные кластеры */
        int isBad  = 0;       /* сбойные кластеры   */
        int isReserved = 0;   /* спрятанные кластеры */

        register ushort n = 0;
        register ushort clu;  /* текущий анализируемый кластер */
        int nline = 300;

        if( trace ) fprintf(mapfp, "\t\tFAT chart\n");
        for(clu=0; clu < MAXCLU; clu++){
                if( clu >= 2 ){
                    n = numCluster(clu);
                    if( n == 0 ) isFree++;
                    if( n == (bit16 ? 0xFFF7 : 0xFF7)) isBad++;
                    if( n >= (bit16 ? 0xFFF0 : 0xFF0 ) &&
                        n <  (bit16 ? 0xFFF7 : 0xFF7 )) isReserved++;
                }
                if( trace ){
                  if( nline >= 8){
                        nline = 0; fprintf( mapfp, "\n%03X:\t", clu );
                  } else  nline++;
                  fprintf( mapfp, "%03X ", n );
                }
        }
        line('=');
        printf( "Свободно %ld, испорчено %ld, резерв %d кластеров\n",
                      (long)isFree * CLU,  /* в байтах */
                               (long)isBad * CLU,   isReserved );
}

void traceclu(nclu, clu) ushort nclu, clu;
{
        if( nclu % 16 == 0 )
            fprintf( mapfp, "\n\t" );
        fprintf( mapfp, "%03X ", clu );
}

#ifdef LOCAL_MALLOC
/*
Обратите внимание, что в этой программе память отводится malloc()
и освобождается free() по принципу стека (LIFO).
Мы могли бы переопределить стандартные функции malloc() и free(),
заставив их работать со статической памятью! (Если мы напишем
свою функцию с именем, как у стандартной, то будет использоваться
НАША функция).
*/
static char allocArena[32 * 1024];
static char *top = allocArena;
char *malloc(n){        char *ptr;
        /* округлить до целого числа слов */  /* деление с остатком */
        /* число int-ов: */  n = (n + (sizeof(int)-1)) / sizeof(int);
        /* число char-ов:*/  n *= sizeof(int);
        ptr = top; top += n; return ptr;
}
free(ptr) char *ptr; { top = ptr; }
#endif /*LOCAL_MALLOC*/

 
        /*      Пример 31      */
/* Интроспективная программа: печатает сама себя */

#include <stdio.h>
char *text[] = {
        "#include <stdio.h>",
        "char *text[] = {",
        "        NULL};",
        "/* Программа, печатающая свой собственный текст */",
        "main(){ int i;",
        "  puts(text[0]); puts(text[1]);",
        "  for(i=0; text[i]; i++) putq(text[i]);",
        "  for(i=2; text[i]; i++) puts(text[i]);",
        "}",
        "putq(s) char *s; {",
        "  printf(\"\\t\\\"\");",
        "  while(*s){",
        "    if(*s == '\"')       printf(\"\\\\\\\"\");",
        "    else if(*s == '\\\\') printf(\"\\\\\\\\\");",
        "    else putchar(*s);",
        "    s++;",
        "  }",
        "  printf(\"\\\",\\n\");",
        "}",
        NULL};
/* Программа, печатающая свой собственный текст */
main(){ int i;
  puts(text[0]); puts(text[1]);
  for(i=0; text[i]; i++) putq(text[i]);
  for(i=2; text[i]; i++) puts(text[i]);
}
putq(s) char *s; {
  printf("\t\"");
  while(*s){
    if(*s == '"')       printf("\\\"");
    else if(*s == '\\') printf("\\\\");
    else putchar(*s);
    s++;
  }
  printf("\",\n");
}
 
        /*      Пример 32     */
/* C beautify: программа cb.c, форматирующая исходный
 * текст программы на Си. Текст взят из дистрибутива UNIX */
#include <stdio.h>
#include <stdlib.h>

#define gets    getlex
#define puts    putlex

        /* прототипы */
void main(int argc, char *argv[]);
void ptabs( void );
int getch( void );
void puts( void );
int lookup( char *tab[] );
int gets( void );
void gotelse( void );
int getnl( void );
void comment( void );

int     slevel[10];
int     clevel  = 0;
int     spflg[20][10];
int     sind [20][10];
int     siflev[10];
int     sifflg[10];
int     iflev   = 0;
int     ifflg   = -1;
int     level   = 0;
int     ind[10] = { 0,0,0,0,0,0,0,0,0,0 };
int     eflg    = 0;
int     paren   = 0;
int     pflg[10] = { 0,0,0,0,0,0,0,0,0,0 };
char    lchar;
char    pchar;
int     aflg    = 0;
int     ct;
int     stabs[20][10];
int     qflg    = 0;
char    *wif[] = { "if",NULL};
char    *welse[] = { "else", NULL};
char    *wfor[] =  { "for" , NULL};
char    *wds[] =   { "case","default", NULL};
int     j       = 0;
char    string[200];
char    cc;
int     sflg    = 1;
int     peek    = -1;
int     tabs    = 0;
int     lastchar;
int     c;

void main(int argc, char *argv[])
{
        if( argc > 1 ){
                if( freopen( argv[1], "r", stdin ) == NULL ){
                        fprintf(stderr, "Can't open %s\n", argv[1] );
                        exit(1);
                }
        }
        if( argc > 2 ){
                if( freopen( argv[2], "w", stdout ) == NULL ){
                        fprintf(stderr, "Can't create %s\n", argv[2] );
                        exit(1);
                }
        }
        while((c = getch()) != EOF){
                switch(c){
                case ' ':
                case '\t':
                        if(lookup(welse) == 1){
                                gotelse();
                                if(sflg == 0 || j > 0) string[j++] = c;
                                puts();
                                sflg = 0;
                                if(getnl() == 1){
                                        puts();
                                        printf("\n");
                                        sflg = 1;
                                        pflg[level]++;
                                        tabs++;
                                }
                                continue;
                        }
                        if(sflg == 0 || j > 0) string[j++] = c;
                        continue;
                case '\n':
                        if((eflg = lookup(welse)) == 1) gotelse();
                        puts();
                        printf("\n");
                        sflg = 1;
                        if(eflg == 1){
                                pflg[level]++;
                                tabs++;
                        }
                        else
                                if(pchar == lchar)
                                        aflg = 1;
                        continue;
                case '{':
                        if(lookup(welse) == 1) gotelse();
                        siflev[clevel] = iflev;
                        sifflg[clevel] = ifflg;
                        iflev = ifflg = 0;
                        clevel++;
                        if(sflg == 1 && pflg[level] != 0){
                                pflg[level]--;
                                tabs--;
                        }
                        string[j++] = c;
                        puts(); getnl(); puts(); printf("\n");
                        tabs++;
                        sflg = 1;
                        if(pflg[level] > 0){
                                ind[level] = 1;
                                level++;
                                slevel[level] = clevel;
                        }
                        continue;
                case '}':
                        clevel--;
                        if((iflev = siflev[clevel]-1) < 0) iflev = 0;
                        ifflg = sifflg[clevel];
                        if(pflg[level] >0 && ind[level] == 0){
                                tabs -= pflg[level];
                                pflg[level] = 0;
                        }
                        puts();
                        tabs--;
                        ptabs();
                        if((peek = getch()) == ';'){
                                printf("%c;", c);
                                peek = -1;
                        }
                        else printf("%c", c);
                        getnl(); puts(); printf("\n");
                        sflg = 1;
                        if(clevel < slevel[level])if(level > 0) level--;
                        if(ind[level] != 0){
                                tabs -= pflg[level];
                                pflg[level] = 0;
                                ind[level] = 0;
                        }
                        continue;
                case '"':
                case '\'':
                        string[j++] = c;
                        while((cc = getch()) != c){
                                string[j++] = cc;
                                if(cc == '\\'){
                                        string[j++] = getch();
                                }
                                if(cc == '\n'){
                                        puts();
                                        sflg = 1;
                                }
                        }
                        string[j++] = cc;
                        if(getnl() == 1){
                                lchar = cc;
                                peek = '\n';
                        }
                        continue;
                case ';':
                        string[j++] = c;
                        puts();
                        if(pflg[level] > 0 && ind[level] == 0){
                                tabs -= pflg[level];
                                pflg[level] = 0;
                        }
                        getnl(); puts(); printf("\n");
                        sflg = 1;
                        if(iflev > 0)
                                if(ifflg == 1){
                                        iflev--; ifflg = 0;
                                }
                                else iflev = 0;
                        continue;
                case '\\':
                        string[j++] = c;
                        string[j++] = getch();
                        continue;
                case '?':
                        qflg = 1;
                        string[j++] = c;
                        continue;
                case ':':
                        string[j++] = c;
                        if(qflg == 1){
                                qflg = 0;
                                continue;
                        }
                        if(lookup(wds) == 0){
                                sflg = 0;
                                puts();
                        }
                        else{
                                tabs--; puts(); tabs++;
                        }
                        if((peek = getch()) == ';'){
                                printf(";");
                                peek = -1;
                        }
                        getnl(); puts(); printf("\n");
                        sflg = 1;
                        continue;
                case '/':
                        string[j++] = c;
                        if((peek = getch()) != '*') continue;
                        string[j++] = peek;
                        peek = -1;
                        comment();
                        continue;
                case ')':
                        paren--;
                        string[j++] = c;
                        puts();
                        if(getnl() == 1){
                                peek = '\n';
                                if(paren != 0) aflg = 1;
                                else if(tabs > 0){
                                        pflg[level]++;
                                        tabs++;
                                        ind[level] = 0;
                                }
                        }
                        continue;
                case '#':
                        string[j++] = c;
                        while((cc = getch()) != '\n') string[j++] = cc;
                        string[j++] = cc;
                        sflg = 0;
                        puts();
                        sflg = 1;
                        continue;
                case '(':
                        string[j++] = c;
                        paren++;
                        if(lookup(wfor) == 1){
                                while((c = gets()) != ';');
                                ct=0;
cont:
                                while((c = gets()) != ')'){
                                        if(c == '(') ct++;
                                }
                                if(ct != 0){
                                        ct--; goto cont;
                                }
                                paren--;
                                puts();
                                if(getnl() == 1){
                                        peek = '\n';
                                        pflg[level]++;
                                        tabs++;
                                        ind[level] = 0;
                                }
                                continue;
                        }
                        if(lookup(wif) == 1){
                                puts();
                                stabs[clevel][iflev] = tabs;
                                spflg[clevel][iflev] = pflg[level];
                                sind[clevel][iflev]  = ind[level];
                                iflev++;
                                ifflg = 1;
                        }
                        continue;
                default:
                        string[j++] = c;
                        if(c != ',') lchar = c;
                }
        }
}

void ptabs( void ){
        int i;
        for(i=0; i < tabs; i++) printf("\t");
}

int getch( void ){
        if(peek < 0 && lastchar != ' ' && lastchar != '\t')
           pchar = lastchar;
        lastchar = (peek<0) ? getc(stdin) : peek;
        peek = -1;
        return(lastchar);
}

void puts( void ){
        if(j > 0){
                if(sflg != 0){
                        ptabs();
                        sflg = 0;
                        if(aflg == 1){
                                aflg = 0;
                                if(tabs > 0) printf("    ");
                        }
                }
                string[j] = '\0';
                printf("%s",string);
                j = 0;
        }
        else{
                if(sflg != 0){
                        sflg = 0; aflg = 0;
                }
        }
}

int lookup( char *tab[] )
{
        char r;
        int l,kk,k,i;
        if(j < 1) return(0);
        kk=0;
        while(string[kk] == ' ') kk++;
        for(i=0; tab[i] != 0; i++){
                l=0;
                for(k=kk;(r = tab[i][l++]) == string[k] && r != '\0';k++);
                if(r == '\0' &&
                   (string[k] < 'a' || string[k] > 'z' || k >= j))
                      return(1);
        }
        return(0);
}

int gets( void ){
        char ch;
beg:
        if((ch = string[j++] = getch()) == '\\'){
                string[j++] = getch();
                goto beg;
        }
        if(ch == '\'' || ch == '"'){
                while((cc = string[j++] = getch()) != ch)
                     if(cc == '\\') string[j++] = getch();
                goto beg;
        }
        if(ch == '\n'){
                puts();
                aflg = 1;
                goto beg;
        }
        else return(ch);
}

void gotelse( void ){
        tabs = stabs[clevel][iflev];
        pflg[level] = spflg[clevel][iflev];
        ind[level]  = sind [clevel][iflev];
        ifflg = 1;
}

int getnl( void ){
        while((peek = getch()) == '\t' || peek == ' '){
                string[j++] = peek;
                peek = -1;
        }
        if((peek = getch()) == '/'){
                peek = -1;
                if((peek = getch()) == '*'){
                        string[j++] = '/';
                        string[j++] = '*';
                        peek = -1;
                        comment();
                }
                else string[j++] = '/';
        }
        if((peek = getch()) == '\n'){
                peek = -1;
                return(1);
        }
        return(0);
}

void comment( void ){
rep:
        while((c = string[j++] = getch()) != '*')
                if(c == '\n'){
                        puts();
                        sflg = 1;
                }
gotstar:
        if((c = string[j++] = getch()) != '/'){
                if(c == '*') goto gotstar;
                goto rep;
        }
}

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

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

Hosted by uCoz