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