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