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

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

Webmaster