Annotation of libwww/Library/src/HTDir.c, revision 2.19

2.1       frystyk     1: /*                                                                  HTDir.c
                      2: **     DIRECTORY BROWSING
                      3: **
                      4: **     (c) COPYRIGHT MIT 1995.
                      5: **     Please first read the full copyright statement in the file COPYRIGH.
2.19    ! frystyk     6: **     @(#) $Id: HTDir.c,v 2.18 1996/08/24 18:09:51 frystyk Exp $
2.1       frystyk     7: **
                      8: **     This is unix-specific code in general
                      9: **     The module is intended for use in HTFile.c and HTFTP.c where
                     10: **     it replaces the old directory browsing routine.
                     11: **     The module is only compiled if GOT_READ_DIR is defined
                     12: **
                     13: ** Authors:
2.5       frystyk    14: **             HF      Henrik Frystyk, MIT, <frystyk@w3.org>
2.1       frystyk    15: ** History:
                     16: **        Sep 95  HFN  written
                     17: **
                     18: ** Note:
                     19: **     It could be a HTML table instead
                     20: */
                     21: 
                     22: /* Library include files */
2.13      frystyk    23: #include "sysdep.h"
2.18      frystyk    24: #include "WWWUtil.h"
                     25: #include "WWWCore.h"
                     26: #include "WWWHTML.h"
2.1       frystyk    27: #include "HTIcons.h"
                     28: #include "HTDescpt.h"
                     29: #include "HTDir.h"                                      /* Implemented here */
                     30: 
                     31: /* Macros and other defines */
                     32: #define PUTC(c)                (*target->isa->put_character)(target, c)
                     33: #define PUTS(s)                (*target->isa->put_string)(target, s)
                     34: #define START(e)       (*target->isa->start_element)(target, e, 0, 0)
                     35: #define END(e)         (*target->isa->end_element)(target, e)
                     36: #define FREE_TARGET    (*target->isa->_free)(target)
                     37: 
                     38: #define DEFAULT_MINFW  15
2.4       frystyk    39: #define DEFAULT_MAXFW  25
2.1       frystyk    40: 
                     41: /* Type definitions and global variables etc. local to this module */
                     42: 
                     43: struct _HTStructured {
2.13      frystyk    44:     const HTStructuredClass *  isa;
2.1       frystyk    45:     /* ... */
                     46: };
                     47: 
                     48: struct _HTDir {
                     49:     HTStructured *     target;
                     50:     HTRequest *                request;
                     51:     HTArray *          array;                  /* Array for sorted listings */
                     52:     char *             fnbuf;                           /* File name buffer */
                     53:     char *             lnbuf;                               /* Rest of line */
                     54:     char *             base;                             /* base url is any */
                     55:     HTDirShow          show;                     /* What do we want to show */
                     56:     HTDirKey           key;                              /* Key for sorting */
                     57:     int                        size;                             /* Number of files */
                     58:     int                curfw;               /* Max file name length in list */
                     59: };
                     60: 
                     61: typedef struct _HTDirNode {
                     62:     char *     fname;
                     63:     char *     date;
                     64:     char *     size;
                     65:     char *     note;
                     66:     HTFileMode mode;
                     67: } HTDirNode;
                     68: 
                     69: typedef enum _HTShowLength {                        /* Width of each collumn */
                     70:     HT_DLEN_SIZE  = 6,
                     71:     HT_DLEN_DATE  = 15,
                     72:     HT_DLEN_SPACE = 1,
                     73:     HT_DLEN_DES          = 25
                     74: } HTShowLength;
                     75: 
                     76: PRIVATE int MinFileW = DEFAULT_MINFW;
                     77: PRIVATE int MaxFileW = DEFAULT_MAXFW;
                     78: 
                     79: /* ------------------------------------------------------------------------- */
                     80: /*                             LINE JUSTIFICATION                           */
                     81: /* ------------------------------------------------------------------------- */
                     82: 
                     83: /*
                     84: **     Left-justifies str_in into str_out expecting str_out having size length
                     85: **     l is number of chars. No 0 termination, rest of line filled with ' '
                     86: */
                     87: PRIVATE void LeftStr (char **outstr, char * instr, int l)
                     88: {
                     89:     char *out = *outstr;
                     90:     while (l-- > 0 && *instr && (*out++ = *instr++));
                     91:     while (l-- > 0) *out++ = ' ';
                     92:     *outstr = out;
                     93: }
                     94: 
                     95: /*
                     96: **     Like LeftStr(), but result is right-justified.
                     97: **     l is number of chars. No 0 termination
                     98: */
                     99: PRIVATE void RightStr (char **outstr, char * instr, int l)
                    100: {
                    101:     char *start = *outstr+l-strlen(instr);
                    102:     char *out = *outstr;
                    103:     while (out<start) *out++ = ' ';
                    104:     while (*instr && (*out++ = *instr++));
                    105:     *outstr = out;
                    106: }
                    107: 
                    108: /* ------------------------------------------------------------------------- */
                    109: /*                             NODE  MANAGEMENT                             */
                    110: /* ------------------------------------------------------------------------- */
                    111: 
                    112: /*
                    113: **     Create a sort key node
                    114: */
                    115: PRIVATE HTDirNode * HTDirNode_new (void)
                    116: {
                    117:     HTDirNode *node;
2.10      frystyk   118:     if ((node = (HTDirNode *) HT_CALLOC(1, sizeof(HTDirNode))) == NULL)
                    119:         HT_OUTOFMEM("HTDirNode_new");
2.1       frystyk   120:     return node;
                    121: }
                    122: 
                    123: /*
                    124: **     Free a sort key node
                    125: */
                    126: PRIVATE BOOL HTDirNode_free (HTDirNode *node)
                    127: {
                    128:     if (node) {
2.10      frystyk   129:        HT_FREE(node->fname);
                    130:        HT_FREE(node->date);
                    131:        HT_FREE(node->size);
                    132:        HT_FREE(node->note);
                    133:        HT_FREE(node);
2.1       frystyk   134:        return YES;
                    135:     }
                    136:     return NO;
                    137: }
                    138: 
                    139: /*
2.17      frystyk   140: **  Escape a filename and add a '/' if it's a directory
                    141: */
                    142: PRIVATE char * expand_name (char * name, HTFileMode mode)
                    143: {
                    144:     char * escaped = HTEscape(name, URL_XPALPHAS);
                    145:     if (mode == HT_IS_DIR)
                    146:        if (*(name+strlen(name)-1) != '/') StrAllocCat(escaped, "/");
                    147:     return escaped;
                    148: }
                    149: 
                    150: /*
2.1       frystyk   151: **     Output an element in HTML
                    152: **     Returns YES if OK, else NO
                    153: */
                    154: PRIVATE BOOL HTDirNode_print (HTDir *dir, HTDirNode *node)
                    155: {
                    156:     char *tp = NULL;
                    157:     HTStructured *target = dir->target;
                    158:     if (dir->show & HT_DS_ICON) {
                    159:        HTFormat format = NULL;
                    160:        HTEncoding encoding = NULL;
                    161:        double q=1.0;
2.15      frystyk   162:        HTIconNode * icon;
2.1       frystyk   163:        if (node->mode == HT_IS_FILE)
2.14      frystyk   164:            HTBind_getFormat(node->fname, &format, &encoding, NULL, NULL, &q);
2.15      frystyk   165:        icon = HTIcon_find(node->mode, format, encoding);
2.1       frystyk   166: 
                    167:        /* Are we having a hot or a cold icon? */
                    168:        if (!(dir->show & HT_DS_HOTI)) {
2.9       frystyk   169:            if (icon) {
2.15      frystyk   170:                char * alt = HTIcon_alternative(icon, YES);
                    171:                HTMLPutImg(target, HTIcon_url(icon), alt, NULL);
                    172:                HT_FREE(alt);
2.9       frystyk   173:                PUTC(' ');
                    174:            }
2.1       frystyk   175:        }
                    176: 
                    177:        /* Start the anchor element */
                    178:        if (dir->base) {
2.17      frystyk   179:            char *escaped = expand_name(node->fname, node->mode);
2.10      frystyk   180:            char *full;
2.12      frystyk   181:            if ((full = (char *) HT_MALLOC(strlen(escaped)+strlen(dir->base)+1)) == NULL)
2.10      frystyk   182:                HT_OUTOFMEM("HTDirNode_print");
2.1       frystyk   183:            strcpy(full, dir->base);
                    184:            strcat(full, escaped);
                    185:            HTStartAnchor(target, NULL, full);
2.10      frystyk   186:            HT_FREE(escaped);
                    187:            HT_FREE(full);
2.1       frystyk   188:        } else {
2.17      frystyk   189:            char *escaped = expand_name(node->fname, node->mode);
2.1       frystyk   190:            HTStartAnchor(target, NULL, escaped);
2.10      frystyk   191:            HT_FREE(escaped);
2.1       frystyk   192:        }
                    193: 
                    194:        if (dir->show & HT_DS_HOTI) {
2.15      frystyk   195:            char * alt = HTIcon_alternative(icon, YES);
                    196:            HTMLPutImg(target, HTIcon_url(icon), alt, NULL);
2.1       frystyk   197:            PUTC(' ');
                    198:        }
                    199:     } else {
                    200:        if (dir->base) {
2.17      frystyk   201:            char *escaped = expand_name(node->fname, node->mode);
2.10      frystyk   202:            char *full;
2.12      frystyk   203:            if ((full = (char *) HT_MALLOC(strlen(escaped)+strlen(dir->base)+1)) == NULL)
2.10      frystyk   204:                HT_OUTOFMEM("HTDirNode_print");
2.1       frystyk   205:            strcpy(full, dir->base);
                    206:            strcat(full, escaped);
                    207:            HTStartAnchor(target, NULL, escaped);
2.10      frystyk   208:            HT_FREE(escaped);
                    209:            HT_FREE(full);
2.1       frystyk   210:        } else {
                    211:            char *escaped = HTEscape(node->fname, URL_XPALPHAS);
                    212:            HTStartAnchor(target, NULL, escaped);
2.10      frystyk   213:            HT_FREE(escaped);
2.1       frystyk   214:        }
                    215:     }
                    216:     
                    217:     /* Insert the anchor text and end anchor */
                    218:     {
                    219:        char *in = node->fname;
                    220:        char *out = dir->fnbuf;
                    221:        int l = dir->curfw;
                    222:        while (l-- > 0 && *in && (*out++ = *in++));
                    223:        if (*in)
                    224:            *(out-1) = '>';
                    225:        else if (node->mode == HT_IS_DIR) {
                    226:            *out++ = '/';
                    227:            l--;
                    228:        }
                    229:        *out = '\0';
                    230:        PUTS(dir->fnbuf);
                    231:        END(HTML_A);
                    232:        out = dir->fnbuf;
                    233:        while (l-- >= 0) *out++ = ' ';
                    234:        LeftStr(&out, " ", HT_DLEN_SPACE);
                    235:        *out = '\0';
                    236:        PUTS(dir->fnbuf);
                    237:     }
                    238: 
                    239:     /* Print the rest of it */
                    240:     tp = dir->lnbuf;
                    241:     if (node->date) {
                    242:        RightStr(&tp, node->date, HT_DLEN_DATE);
                    243:        LeftStr(&tp, " ", HT_DLEN_SPACE);
                    244:     }
                    245:     if (node->size) {
                    246:        RightStr(&tp, node->size, HT_DLEN_SIZE);
                    247:        LeftStr(&tp, " ", HT_DLEN_SPACE);
                    248:     }
                    249:     if (node->note) {
                    250:        LeftStr(&tp, node->note, HT_DLEN_DES);
                    251:        LeftStr(&tp, " ", HT_DLEN_SPACE);
                    252:     }
                    253:     *tp = '\0';
                    254:     PUTS(dir->lnbuf);
                    255:     PUTC('\n');
                    256:     return YES;
                    257: }
                    258: 
                    259: /* ------------------------------------------------------------------------- */
                    260: /*                             DIRECTORY MANAGEMENT                         */
                    261: /* ------------------------------------------------------------------------- */
                    262: 
                    263: /*     HTDir_headLine
                    264: **     --------------
                    265: **     Puts out the header line of the list itself
                    266: **     Returns YES if OK, else NO
                    267: */
                    268: PRIVATE BOOL HTDir_headLine (HTDir *dir)
                    269: {
                    270:     if (dir) {
                    271:        char *tp;
                    272:        HTStructured *target = dir->target;
                    273:        START(HTML_PRE);
                    274:        if (dir->show & HT_DS_ICON) {
2.15      frystyk   275:            HTIconNode * icon = HTIcon_find(HT_IS_BLANK, NULL, NULL);
2.9       frystyk   276:            if (icon) {
2.15      frystyk   277:                char * alt = HTIcon_alternative(icon, NO);
                    278:                HTMLPutImg(target, HTIcon_url(icon), alt, NULL);
                    279:                HT_FREE(alt);
2.9       frystyk   280:                PUTC(' ');
                    281:            }
2.1       frystyk   282:        }
                    283: 
                    284:        tp = dir->fnbuf;
                    285:        LeftStr(&tp, "Name", dir->curfw);
                    286:        LeftStr(&tp, " ", HT_DLEN_SPACE);
                    287:        *tp = '\0';
                    288:        PUTS(dir->fnbuf);
                    289: 
                    290:        tp = dir->lnbuf;
                    291:        if (dir->show & HT_DS_DATE) {
2.2       frystyk   292:            LeftStr(&tp, "Last Modified", HT_DLEN_DATE);
2.1       frystyk   293:            LeftStr(&tp, " ", HT_DLEN_SPACE);
                    294:        }
                    295:        if (dir->show & HT_DS_SIZE) {
                    296:            RightStr(&tp, "Size", HT_DLEN_SIZE);
                    297:            LeftStr(&tp, " ", HT_DLEN_SPACE);
                    298:        }
                    299:        if (dir->show & HT_DS_DES) {
                    300:            LeftStr(&tp, "Description", HT_DLEN_DATE);
                    301:            LeftStr(&tp, " ", HT_DLEN_SPACE);
                    302:        }
                    303:        *tp = '\0';
                    304:        PUTS(dir->lnbuf);
                    305:        START(HTML_HR);
                    306:        PUTC('\n');
                    307:        return YES;
                    308:     }
                    309:     return NO;
                    310: }
                    311: 
                    312: /*     HTDir_setWidth
                    313: **     --------------
                    314: **     The module automatically ajusts the width of the directory listing as
                    315: **     a function of the file name. The width can flows dynamically between
                    316: **     an upper and a lower limit.
                    317: */
                    318: PUBLIC BOOL HTDir_setWidth (int minfile, int maxfile)
                    319: {
                    320:     MinFileW = (minfile>=0) ? minfile : 0;
                    321:     MaxFileW = (maxfile>minfile) ? maxfile : minfile+1;
                    322:     return YES;
                    323: }
                    324: 
                    325: /*     HTDir_new
                    326: **     ---------
                    327: **     Creates a structured stream object and sets up the initial HTML stuff
                    328: **     Returns the dir object if OK, else NULL
                    329: */
                    330: PUBLIC HTDir * HTDir_new (HTRequest * request, HTDirShow show, HTDirKey key)
                    331: {    
                    332:     HTDir *dir;
                    333:     char *title = NULL;
                    334:     if (!request) return NULL;
                    335: 
                    336:     /* Create object */
2.10      frystyk   337:     if ((dir = (HTDir *) HT_CALLOC(1, sizeof (HTDir))) == NULL ||
                    338:        (dir->fnbuf = (char *) HT_MALLOC(MaxFileW+HT_DLEN_SPACE)) == NULL)
                    339:        HT_OUTOFMEM("HTDir_new");
2.1       frystyk   340:     dir->target = HTMLGenerator(request, NULL, WWW_HTML,
                    341:                               HTRequest_outputFormat(request),
                    342:                               HTRequest_outputStream(request));
2.16      frystyk   343:     HTRequest_setOutputConnected(request, YES);
2.7       frystyk   344:     HTAnchor_setFormat(HTRequest_anchor(request), WWW_HTML);
2.1       frystyk   345:     dir->request = request;
                    346:     dir->show = show;
                    347:     dir->key = key;
                    348:     if (key==HT_DK_NONE)
                    349:        dir->curfw = MaxFileW;
                    350:     else {
                    351:        dir->curfw = MinFileW;
                    352:        dir->array = HTArray_new(256);
                    353:     }
2.16      frystyk   354: 
                    355:     /* We're all OK */
                    356:     HTRequest_addError(request, ERR_INFO, NO, HTERR_OK, NULL, 0, "HTDir_new");
2.1       frystyk   357: 
                    358:     /* Find the length of the fields */
                    359:     {
                    360:        int len = HT_DLEN_SPACE+1;
                    361:        if (show & HT_DS_SIZE) len += (HT_DLEN_SIZE+HT_DLEN_SPACE);
                    362:        if (show & HT_DS_DATE) len += (HT_DLEN_DATE+HT_DLEN_SPACE);
                    363:        if (show & HT_DS_DES) len += HT_DLEN_DES;
2.10      frystyk   364:        if ((dir->lnbuf = (char *) HT_MALLOC(len)) == NULL)
                    365:            HT_OUTOFMEM("HTDir_new");
2.1       frystyk   366:     }
                    367: 
                    368:     /* Find the title and the base URL */
                    369:     {
                    370:        char *addr = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
                    371:        char *path = HTParse(addr, "", PARSE_PATH+PARSE_PUNCTUATION);
                    372:        char *ptr;
                    373:        if ((ptr = strchr(path, ';')) || (ptr = strchr(path, '?')))
                    374:            *ptr = '\0';
                    375:        StrAllocCopy(title, path);
                    376:        HTUnEscape(title);                                          /* Title */
                    377:        if((ptr=strrchr(path, '/')) && (ptr<path+strlen(path)-1 || ptr==path)){
                    378:            StrAllocCopy(dir->base, ++ptr);
                    379:            StrAllocCat(dir->base, "/");
                    380:        }
2.19    ! frystyk   381:        if (PROT_TRACE)
        !           382:            HTTrace("HTDir_new... base is `%s\'\n", dir->base ? dir->base : "");
2.10      frystyk   383:        HT_FREE(addr);
                    384:        HT_FREE(path);
2.1       frystyk   385:     }
                    386: 
                    387:     /* Start the HTML stuff */
                    388:     {
                    389:        HTStructured *target = dir->target;
                    390:        START(HTML_HTML);
                    391:        START(HTML_HEAD);
                    392:        START(HTML_TITLE);
                    393:        PUTS("Current index is ");
                    394:        PUTS(title);
                    395:        END(HTML_TITLE);
                    396:        END(HTML_HEAD);
                    397:        START(HTML_BODY);
                    398:        START(HTML_H1);
                    399:        PUTS("Index of ");
                    400:        PUTS(title);
                    401:        END(HTML_H1);
                    402:     }
2.10      frystyk   403:     HT_FREE(title);
2.1       frystyk   404:     return dir;
                    405: }
                    406: 
                    407: /*     HTDir_addElement
                    408: **     ---------------
                    409: **     This function accepts a directory line. "data" and "size", and
                    410: **     "description" can all be NULL
                    411: **     Returns YES if OK, else NO
                    412: */
                    413: PUBLIC BOOL HTDir_addElement (HTDir *dir, char *name, char *date, char *size,
                    414:                              HTFileMode mode)
                    415: {
                    416:     HTDirNode *node = HTDirNode_new();
                    417:     if (!dir || !name) return NO;
2.17      frystyk   418:     if ((node->fname = (char *) HT_MALLOC(strlen(name) + 2)) == NULL)
                    419:        HT_OUTOFMEM("HTDir_addElement");
                    420:     strcpy(node->fname, name);                                 /* Mandatory */
2.1       frystyk   421:     if (dir->show & HT_DS_DATE && date) StrAllocCopy(node->date, date);
                    422:     if (dir->show & HT_DS_SIZE && size) StrAllocCopy(node->size, size);
                    423:     if (dir->show & HT_DS_DES) {
                    424: #if 0
                    425: 
                    426:        /* FIND DESCRIPTION */
                    427: 
                    428: #endif
                    429:     }
2.17      frystyk   430: 
                    431: 
                    432:     /* Set the mode of the file */
2.1       frystyk   433:     node->mode = mode;
2.17      frystyk   434: 
                    435:     /* Should we display now or later? */
2.1       frystyk   436:     if (dir->key == HT_DK_NONE) {
                    437:        if (!dir->size++) HTDir_headLine(dir);
                    438:        HTDirNode_print(dir, node);
                    439:        HTDirNode_free(node);
                    440:     } else {
                    441:        int slen = strlen(name);
                    442:        if (slen > dir->curfw)
                    443:            dir->curfw = slen < MaxFileW ? slen : MaxFileW;
                    444:        HTArray_addObject(dir->array, (void *) node);
                    445:     }
                    446:     return YES;
                    447: }
                    448: 
2.13      frystyk   449: PRIVATE int DirSort (const void *a, const void *b)
2.1       frystyk   450: {
2.8       frystyk   451: #if 0
2.1       frystyk   452:     HTDirNode *aa = *(HTDirNode **) a;
                    453:     HTDirNode *bb = *(HTDirNode **) b;
                    454:     return strcmp(aa->fname, bb->fname);
2.8       frystyk   455: #else
2.15      frystyk   456:     return strcmp((*((HTDirNode **) a))->fname,
                    457:                  (*((HTDirNode **) b))->fname);
2.1       frystyk   458: #endif
                    459: }
                    460: 
2.13      frystyk   461: PRIVATE int DirCaseSort (const void *a, const void *b)
2.1       frystyk   462: {
2.8       frystyk   463: #if 0
2.1       frystyk   464:     HTDirNode *aa = *(HTDirNode **) a;
                    465:     HTDirNode *bb = *(HTDirNode **) b;
                    466:     return strcasecomp(aa->fname, bb->fname);
2.8       frystyk   467: #else
2.15      frystyk   468:     return strcasecomp((*((HTDirNode **) a))->fname,
                    469:                       (*((HTDirNode **) b))->fname);
2.1       frystyk   470: #endif
                    471: }
                    472: 
                    473: /*     HTDir_free
                    474: **     ----------
                    475: **     If we are sorting then do the sorting and put out the list,
                    476: **     else just append the end of the list.
                    477: */
                    478: PUBLIC BOOL HTDir_free (HTDir * dir)
                    479: {
                    480:     if (!dir) return NO;
                    481:     if (dir->key != HT_DK_NONE) {
                    482:        HTArray *array = dir->array;
                    483:        void **data;
                    484:        HTDirNode *node;
                    485:        HTDir_headLine(dir);    
                    486:        HTArray_sort(array, (dir->key==HT_DK_CINS ? DirCaseSort : DirSort));
                    487:        node = (HTDirNode *) HTArray_firstObject(array, data);
                    488:        while (node) {
                    489:            HTDirNode_print(dir, node);
                    490:            HTDirNode_free(node);
                    491:            node = (HTDirNode *) HTArray_nextObject(array, data);
                    492:        }
                    493:        dir->size = HTArray_size(array);
                    494:        HTArray_delete(array);  
                    495:     }
                    496: 
                    497:     /* Put out the end of the HTML stuff */
                    498:     {
                    499:        HTStructured *target = dir->target;
                    500:        START(HTML_HR);
                    501:        if (!dir->size)
                    502:            PUTS("Empty directory");
                    503:        else if (dir->size == 1)
                    504:            PUTS("1 File");
                    505:        else {
                    506:            char buffer[20];
                    507:            sprintf(buffer, "%u files", dir->size);
                    508:            PUTS(buffer);
                    509:        }
                    510:        END(HTML_PRE);
                    511:        END(HTML_BODY);
                    512:        END(HTML_HTML);
                    513:        FREE_TARGET;
                    514:     }
                    515: 
2.10      frystyk   516:     HT_FREE(dir->fnbuf);
                    517:     HT_FREE(dir->lnbuf);
                    518:     HT_FREE(dir->base);
                    519:     HT_FREE(dir);
2.1       frystyk   520:     return YES;
                    521: }

Webmaster