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

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

Webmaster