File: /~heha/hsn/dos/treeinfo.zip/TREEINFO.TXT

/*
Aus J:\USER\HENNI\NEU

Aufbau der TREEINFO.NCD
- 5 Bytes ASCIIZ "PNCI", wird NICHT geprüft!
- 2 Bytes Anzahl (n) der Verzeichnisse (INTEL-Notation)
- 2 Bytes simple Prüfsumme über diese 7 Header-Bytes, 1. Prüfsumme
- n 16-Byte-Verzeichnisblöcke, beginnend mit "\", mit je folgendem Aufbau:
  - 13 Byte ASCIIZ Verzeichnisname (mit Punkt; Slack undefiniert, jedoch
    ist das 13. Byte stets Null)
  - 1 Byte Tiefe des Verzeichnisses, Root=0
  - 1 Byte Flag (0 oder 1): Vorhergehender Eintrag 1 Level höher
  - 1 Byte Flag (0 oder 1): Weitere Einträge desselben Levels folgen
(Diese Blöcke sind selbstverständlich tree-gerecht geordnet, d.h. beim
 Erstellen eines tief unten liegenden Vrz's wird ggf. mitten in der Datei
 Platz gemacht, um dort den Block einzufügen)
- 2 Bytes simple Prüfsumme (*nur*) über die n Verzeichnisblöcke, 2. Prüfsumme
*/

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <io.h>
#include <ctype.h>
#include <string.h>

/* Compilerunterschiede mit Makros erschlagen */
#ifdef __TURBOC__
#include        <dir.h>
#define FF_STRUCT       struct ffblk
#define FF_NAME(ff)     ff.ff_name
#define FF_ATTRIB(ff)   ff.ff_attrib
#else   /* Zortech C */
#define FF_STRUCT       struct FIND
#define FF_NAME(ff)     ff.name
#define FF_ATTRIB(ff)   ff.attribute
#endif

/* Grafische Verbindungselemente für Codepage 437 */
#define HSTRICH         0xC4    /* - */
#define VSTRICH         0xB3    /* | */
#define WINKEL          0xC0    /* \ */
#define VERBINDUNG      0xC3    /* + */

/* Makro zum Test auf Unterverzeichnisse */
#define ISDIR(ff)       ((FF_ATTRIB(ff) & FA_DIREC) && (FF_NAME(ff)[0] != '.'))

unsigned        NumDirs;        /* Anzahl Verzeichnisse */
char            TreeInfo[] = { " :\\TREEINFO.NCD" };
                                /* Laufwerk wird im Hauptprogramm eingetragen */
unsigned        CheckSum;       /* Prüfsumme */

/* Kopfinformation von TREEINFO.NCD */
struct HEADER
{       char            ident[5]; /* "PNCI\x0" */
        unsigned        numdir;   /* Anzahl 'DIRINFO' Einträge */
        unsigned        checksum; /* Checksumme über 'ident' und 'numdir' */
} header;

#if sizeof(header) != 9
#error Byteweise Ausrichtung benutzen
#endif

/* Verzeichnisinformation von TREEINFO.NCD */
struct DIRINFO
{       char    name[13], 
                deep, 
                isfirst, 
                isnotlast;
} dirinfo;

/* Struktur zum Aufbau eines Verzeichnisbaumes im Hauptspeicher */
struct DIRTREE 
{       char            *name;
        int             deep;
        struct DIRTREE  *next,  /* Zeiger auf Verzeichnisnamen im selben Verzeichnis */
                        *sub,   /* Zeiger auf Unterverzeichnis dieses Verzeichnisses */
                        *father; /* Zeiger auf übergeordnetes Verzeichnis -
                                wurde eingeführt, um von einer beliebigen Stelle aus
                                den kompletten Verzeichnisnamen einfach bilden zu
                                können, z.B. für Verzeichniswechseln */
} root;


/* Funktion zum Testen auf eine gültige TREEINFO.NCD */
FILE *validate(void)
{       FILE                    *fp;
        unsigned                i, checksum;
        unsigned char           *p;
        
        if ((fp = fopen(TreeInfo, "rb")) != NULL)       
        if (fread(&header, 1, sizeof(header), fp) == sizeof(header))
        if (strcmp(header.ident, "PNCI") == 0)
        if (filelength(fileno(fp)) >> 4 == header.numdir)
        {
                p = (void*)&header;
                for (i=checksum=0; i<7; i++, p++) checksum += *p;
                if (checksum == header.checksum) 
                {       NumDirs = header.numdir;
                        return fp;
                }
        }
        if (fp) fclose(fp);
        return NULL;
}

void readsubdir(FILE *fp, struct DIRTREE *pFather, int deep)
{       struct DIRTREE  *pCurrent, *pNext;

        deep++;
        pCurrent = malloc(sizeof(struct DIRTREE));
        pFather->sub = pCurrent;

        while (pCurrent && (dirinfo.deep == deep))
        {       pCurrent->name = strdup(dirinfo.name);
                pCurrent->deep = dirinfo.deep;
                pCurrent->next = pCurrent->sub = NULL;
                pCurrent->father = pFather;

                if (fread(&dirinfo, 1,  sizeof(dirinfo), fp) == sizeof(dirinfo))
                {       if (dirinfo.deep > deep) readsubdir(fp, pCurrent, deep);
                        if (dirinfo.deep == deep)
                        {       pNext = malloc(sizeof(struct DIRTREE));
                                pCurrent->next = pNext;
                                pCurrent = pNext;
                        }
                }
                else dirinfo.deep = -1;
        }
}

int readinfo(FILE *fp)
{       int deep;       

        /* Wurzelverzeichnis einlesen */
        if (fread(&dirinfo, 1, sizeof(dirinfo), fp) != sizeof(dirinfo)) return 1;
        root.next = root.father = root.sub = NULL;
        root.name = strdup(dirinfo.name);
        root.deep = dirinfo.deep;

        if (fread(&dirinfo, 1, sizeof(dirinfo), fp) == sizeof(dirinfo))
                readsubdir(fp, &root, 0);
        fclose(fp);
        return 0;
}

void writesubs(FILE *fp, struct DIRTREE *pDT)
{       int             i;
        unsigned char   *p;

        while (pDT)
        {
                strcpy(dirinfo.name, pDT->name);
                for (i=strlen(pDT->name); i < 13; i++) dirinfo.name[i] = 0;
                dirinfo.deep = pDT->deep;
                dirinfo.isnotlast = pDT->next != NULL;
                dirinfo.isfirst = pDT->father ? pDT->father->sub == pDT: 0;
                if (fwrite(&dirinfo, 1, 16, fp) != 16) 
                {       fprintf(stderr, "error writing %s (disk full ?)\n", TreeInfo);
                        return;
                }
                p = (void*)&dirinfo;
                for (i=0; i < 16; i++, p++) CheckSum += *p;
                if (pDT->sub) writesubs(fp, pDT->sub);
                pDT = pDT->next;
        }       
}

void writeinfo(void)
{       unsigned        i;
        unsigned char   *p;
        FILE            *fp;

        strcpy(header.ident, "PNCI");
        header.numdir = NumDirs;
        p = (void*)&header;
        for (i = header.checksum = 0; i<7; i++, p++) header.checksum += *p;
        fp = fopen(TreeInfo, "wb");
        if (!fp) 
        {       fprintf(stderr, "error opening %s\n", TreeInfo);
                return;
        }
        if (fwrite(&header, 1, sizeof(header), fp) != sizeof(header))
        {       fprintf(stderr, "error writing %s (disk full ?)\n", TreeInfo);
                return;
        }
        CheckSum = 0;
        writesubs(fp, &root);
        fwrite(&CheckSum, 1, sizeof(unsigned), fp);
        fclose(fp);     
}

void deleteEntry(struct DIRTREE *pDT)
{       struct DIRTREE	*tmp;

	while (pDT)
	{
        	pDT->father = NULL;             
	       	if (pDT->name) 
	        {       free(pDT->name); 
        	        pDT->name = NULL; 
	        }
        	if (pDT->sub)
	        {       deleteEntry(pDT->sub); 
        	        free(pDT->sub);
                	pDT->sub = NULL;
	        }
		tmp = pDT;
		pDT = pDT->next;
		free(tmp);
        }
}

void subdir(char *path, struct DIRTREE *dir, int deep)
{       char                    *pp;
        int                     done;
        FF_STRUCT               ff;
        struct  DIRTREE         *dp;

        pp = path + strlen(path);       /* auf die abschließende 0  */
        if (*(pp-1) != '\\')
        {       *pp     = '\\';
                *(++pp) = 0;
        }

        deep++ ;
        strcpy(pp, "*.*");
        done = findfirst(path, &ff, 0x3F);
        while (!done && !ISDIR(ff))
        {       done = findnext(&ff);
        }
        if (done) dir->sub = NULL;
        else
        {       dir->sub = dp = malloc(sizeof(struct DIRTREE));
                while (!done && dp)
                {
                        dp->name = strdup(FF_NAME(ff));
                        NumDirs++;
                        dp->father = dir;
                        dp->deep = deep;
                        strcpy(pp, FF_NAME(ff));
                        subdir(path, dp, deep);
                        done  = findnext(&ff);
                        while (!done && !ISDIR(ff))
                        {       done = findnext(&ff);
                        }
                        if (done) dp->next = NULL;
                        else 
                        {       dp->next = malloc(sizeof(struct DIRTREE));
                                dp = dp->next;
                        }
                }
        }        
}

void scandirs(void)
{       char    path[129];

        deleteEntry(root.sub);
	if (root.name) free(root.name);
        root.name = strdup("\\");
        strcpy(path, root.name);
        root.father = root.next = NULL;
        root.deep = 0;
        NumDirs = 1;
        subdir(path, &root, 0);
}

void printdir(struct DIRTREE *pDT, long lines)
{       int i;

        while (pDT)
        {       for (i=1; i<pDT->deep; i++) 
                {       if (lines & (1L << i)) putchar(VSTRICH);
                        else putchar(' ');
                        putchar(' ');
                }
                if (pDT->deep)
                {
                        if (pDT->next == NULL) putchar(WINKEL);
                        else putchar(VERBINDUNG);
                        putchar(HSTRICH);
                }
                puts(pDT->name);
                printdir(pDT->sub, pDT->next ? lines | (1L << pDT->deep): lines);
                pDT = pDT->next;
        }
}

main(int argc, char **argv)
{       FILE    *fp;
        char    Read, Show, Drive;
        int     i;

        Read = Show = Drive = 0;
        if (argc > 1) for (i=1; i<argc; i++)
        {       if ((argv[i][0] == '/') || (argv[i][0] == '-'))
                {       switch (toupper(argv[i][1]))
                        {       case 'R': Read = 1; break;
                                case 'S': Show = 1; break;
                                case 'H':
                                case '?': 
                                printf( "TREEINFO [/r] [/s] [<drive>:]\n"
                                        " /r - reread,\n"
                                        " /s - show    the directory tree"
                                        " of specified drive\n");
                                return 0;

                                default:
                                printf("using: TREEINFO [/r] [/s] [<drive>:]\n");
                                return 0;
                        }
                }
                else if (argv[i][1] == ':') Drive = toupper(argv[i][0]) - '@';
        }
        else
        {
                printf("using: TREEINFO [/r] [/s] [<drive>:]\n");
                return 0;
        }

        if (Drive == 0) Drive = bdos(0x19,0,0) + 1;
        TreeInfo[0] = Drive + '@';

        if (!Read)
        {       fp = validate();
                Read =  fp ?  readinfo(fp): 1;
        }
        if (Read) 
        {       scandirs();
                writeinfo();
        }
        if (Show) printdir(&root, 0);
        return 0;
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded