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