Annotation of libwww/Library/src/HTNDir.c, revision 2.10
2.1 frystyk 1: /* HTNDir.c
2: ** GENERIC NEWS LISTINGS
3: **
4: ** (c) COPYRIGHT MIT 1995.
5: ** Please first read the full copyright statement in the file COPYRIGH.
2.10 ! frystyk 6: ** @(#) $Id: HTNDir.c,v 2.9 1996/08/12 16:43:28 frystyk Exp $
2.1 frystyk 7: **
8: ** Creates listings for all kind of News output.
9: **
10: ** Authors:
11: ** HF Henrik Frystyk, MIT, <frystyk@w3.org>
2.9 frystyk 12: ** MP Maciej Puzio <puzio@zodiac1.mimuw.edu.pl>
2.1 frystyk 13: ** History:
14: ** Oct 95 HFN written
2.9 frystyk 15: ** Mar 96 MP modified heavily
2.1 frystyk 16: */
17:
18: /* Library include files */
2.7 frystyk 19: #include "sysdep.h"
2.9 frystyk 20: #include "WWWUtil.h"
21: #include "WWWCore.h"
22: #include "WWWHTML.h"
2.1 frystyk 23: #include "HTNews.h"
2.9 frystyk 24: #include "HTNewsLs.h"
2.1 frystyk 25: #include "HTNDir.h" /* Implemented here */
26:
27: /* Macros and other defines */
28: #define PUTC(c) (*target->isa->put_character)(target, c)
29: #define PUTS(s) (*target->isa->put_string)(target, s)
30: #define START(e) (*target->isa->start_element)(target, e, 0, 0)
31: #define END(e) (*target->isa->end_element)(target, e)
32: #define FREE_TARGET (*target->isa->_free)(target)
33:
34: #define DEFAULT_MAXW 80 /* Default line width */
35:
36: /* Type definitions and global variables etc. local to this module */
37: struct _HTStructured {
2.7 frystyk 38: const HTStructuredClass * isa;
2.1 frystyk 39: /* ... */
40: };
41:
2.9 frystyk 42: struct _HTNewsNode {
43: int index;
44: char * name;
45: char * subject;
46: char * from;
47: time_t date;
48: int refs; /* Number of references */
49: BOOL is_tmplate;
50:
51: /* Added by MP: */
52: HTList* refNames; /* referee names list */
53: HTList* refObjects; /* referee objects list */
54: HTNewsNode * refParent; /* direct parent (referee) */
55: HTNewsNode * lastChild; /* last child (referer) */
56: BOOL show; /* show this node on the list? */
57: BOOL fake; /* create anchor with this node? */
58: int refChildren; /* number of referers */
59: int refLevel; /* reference level - to speed sorting */
60: int minRefIndex; /* minimal index in reference subtree */
61: int maxRefIndex; /* maximal index in reference subtree */
62: time_t minRefDate; /* minimal date in reference subtree */
63: time_t maxRefDate; /* maximal date in reference subtree */
64: };
65:
2.1 frystyk 66: struct _HTNewsDir {
67: HTStructured * target;
68: HTRequest * request;
2.9 frystyk 69: HTNewsDirKey key; /* Key for sorting */
70: char * name; /* Name of the newsgroup(s) Added by MP */
71: char * tmplate;
72: HTNewsNode * tmplate_node;
73: int lastLevel; /* Last printed ref level Added by MP. */
2.1 frystyk 74: HTArray * array; /* Array for sorted listings */
2.9 frystyk 75: HTArray * cache; /* Only created on request */
2.1 frystyk 76: };
77:
2.9 frystyk 78: PRIVATE int MaxLineW = DEFAULT_MAXW;
79:
80: /* Forward references - added by MP. */
81: PRIVATE void HTNewsDir_addLevelTags (HTNewsDir* dir, int level);
82: PRIVATE HTNewsNode* HTNewsDir_addFakeElement (HTNewsDir* dir, char* subject,
83: char* name);
84: PRIVATE HTNewsNode* HTNewsDir_findNodeNamed (HTNewsDir* dir, char* name);
85:
86: #define FNWS_ANY 0x0000
87: /* Date */
88: #define FNWS_MIN 0x0001
89: #define FNWS_MAX 0x0002
90: /* Fake/not-fake */
91: #define FNWS_ONLYFAKE 0x0010
92: #define FNWS_NOTFAKE 0x0020
93: /* Fake or not-fake with references */
94: #define FNWS_NOTORPHAN 0x0040
95: PRIVATE HTNewsNode* HTNewsDir_findNodeWithSubject (HTNewsDir* dir,
96: char* subject, int which, HTNewsNode* avoidNode);
2.1 frystyk 97:
98:
99: /* ------------------------------------------------------------------------- */
100: /* NODE MANAGEMENT */
101: /* ------------------------------------------------------------------------- */
102:
2.9 frystyk 103: PRIVATE HTNewsNode * HTNewsNode_new (int index, char * subject, char * from,
104: time_t date, char * name,
105: int refs, HTList * refNames)
2.1 frystyk 106: {
2.9 frystyk 107: HTNewsNode * node;
2.5 frystyk 108: if ((node = (HTNewsNode *) HT_CALLOC(1, sizeof(HTNewsNode))) == NULL)
109: HT_OUTOFMEM("HTNewsNode_new");
2.9 frystyk 110: if (name) StrAllocCopy(node->name, name);
111: if (subject) {
112: StrAllocCopy(node->subject, subject);
113: node->subject = HTStrip(node->subject);
114: }
115: if (from) StrAllocCopy(node->from, from);
116: node->index = index;
117: node->date = date;
118: node->refs = refs;
119: node->refNames = refNames;
120: node->show = (name != NULL);
121: node->fake = (name == NULL);
122: node->minRefIndex = index;
123: node->maxRefIndex = index;
124: node->minRefDate = date;
125: node->maxRefDate = date;
2.1 frystyk 126: return node;
127: }
128:
2.9 frystyk 129: PRIVATE BOOL HTNewsNode_delete (HTNewsNode * node, BOOL cache)
2.1 frystyk 130: {
131: if (node) {
2.9 frystyk 132: if (!cache || node->is_tmplate) HT_FREE(node->name);
2.5 frystyk 133: HT_FREE(node->subject);
134: HT_FREE(node->from);
2.9 frystyk 135: if (node->refNames) {
136: HTList * cur = node->refNames;
137: char * pres;
138: while ((pres = (char *) HTList_nextObject(cur))) HT_FREE(pres);
139: HTList_delete(node->refNames);
140: }
141: if (node->refObjects) HTList_delete(node->refObjects);
2.5 frystyk 142: HT_FREE(node);
2.1 frystyk 143: return YES;
144: }
145: return NO;
146: }
147:
2.9 frystyk 148: /* Added by MP. */
149: PRIVATE BOOL HTNewsNode_isAncestor (HTNewsNode* node, HTNewsNode* refered)
150: {
151: HTNewsNode* p;
152: for (p = refered; p; p = p->refParent)
153: if (p == node) return YES;
154: return NO;
155: }
156:
157: /* Added by MP. */
158: PRIVATE BOOL HTNewsNode_linkRef (HTNewsNode* node, HTNewsNode* referer)
159: {
160: if (node && referer) {
161: node->refChildren++;
162: node->lastChild = referer;
163: node->minRefIndex = (node->minRefIndex != 0 ?
164: HTMIN(node->minRefIndex, referer->index) :
165: referer->index);
166: node->maxRefIndex = (node->maxRefIndex != 0 ?
167: HTMAX(node->maxRefIndex, referer->index) :
168: referer->index);
169: node->minRefDate = (node->minRefDate != 0 ?
170: HTMIN(node->minRefDate, referer->date) :
171: referer->date);
172: node->maxRefDate = (node->maxRefDate != 0 ?
173: HTMAX(node->maxRefDate, referer->date) :
174: referer->date);
175: referer->refParent = node;
176: return YES;
177: }
178: return NO;
179: }
180:
181: /* Added by MP. */
182: PRIVATE int HTNewsNode_refLevel (HTNewsNode* node)
183: {
184: int cnt = 0;
185: HTNewsNode* p;
186: for (p = node->refParent; p && p->show; p = p->refParent)
187: cnt++;
188: return cnt;
189: }
190:
191: /* Added by MP. */
192: /* Returns index field for non-fake nodes, minimal or maximal subtreee */
193: /* index for fake nodes. */
194: PRIVATE time_t HTNewsNode_getIndex (HTNewsNode* node, BOOL minimal)
195: {
196: if (node->index != 0)
197: return node->index;
198: else if (minimal)
199: return node->minRefIndex;
200: else
201: return node->maxRefIndex;
202: }
203:
204: /* Added by MP. */
205: /* Returns date field for non-fake nodes, minimal or maximal subtreee */
206: /* date for fake nodes. */
207: PRIVATE time_t HTNewsNode_getDate (HTNewsNode* node, BOOL minimal)
208: {
209: if (node->date != 0)
210: return node->date;
211: else if (minimal)
212: return node->minRefDate;
213: else
214: return node->maxRefDate;
215: }
216:
217: /* Helper function - added by MP. */
218: PRIVATE char* UnReSubject (char* subject)
219: {
220: if (strlen(subject) >= 3 && strncasecomp(subject, "re:", 3) == 0)
221: {
222: char* p = subject + 3; /* "Re:XXX" */
223: if (*p == ' ') p ++; /* "Re: XXX" */
224: return p;
225: }
226: return subject;
227: }
228:
229: /* Added by MP. */
230: PRIVATE void HTNewsNode_setRefInfo_pass1 (HTNewsDir* dir, HTNewsNode* node)
231: {
232: HTList* ptr = node->refNames;
233: char* name = NULL;
234: if (node->fake)
235: return;
236: if (ptr != NULL)
237: name = (char*) HTList_nextObject(ptr);
238: while (ptr != NULL)
239: {
240: HTNewsNode* parent;
241: parent = HTNewsDir_findNodeNamed(dir, name);
242: if (parent)
243: {
244: if (!node->refObjects) node->refObjects = HTList_new();
245: HTList_addObject (node->refObjects, parent);
246: }
247: name = (char*) HTList_nextObject(ptr);
248: }
249: }
250:
251: /* Added by MP. */
252: PRIVATE void HTNewsNode_setRefInfo_pass2 (HTNewsDir* dir, HTNewsNode* node)
253: {
254: HTNewsNode* maxParent = NULL;
255: HTList* ptr = node->refObjects;
256: HTNewsNode* parent = NULL;
257: if (node->fake)
258: return;
259: if (ptr != NULL)
260: parent = (HTNewsNode*) HTList_nextObject(ptr);
261: while (ptr != NULL)
262: {
263: if (!maxParent || maxParent->date < parent->date)
264: maxParent = parent;
265: parent = (HTNewsNode*) HTList_nextObject(ptr);
266: }
267: if (maxParent)
268: {
269: if (!HTNewsNode_isAncestor(node, maxParent)) /* better be careful */
270: HTNewsNode_linkRef (maxParent, node);
271: }
272: else
273: {
274: char* refSubject;
275: BOOL re;
276:
277: /* Here is the only place we REALLY have to check for circular */
278: /* references. It is normally possible that a node refers to */
279: /* orphan node and both have the same subject. In this situation */
280: /* we can't make the orphan to refer to it's child. Without checking */
281: /* for circular references this is likely to happen here. */
282:
283: refSubject = UnReSubject(node->subject);
284: re = (strcasecomp(refSubject, node->subject) != 0);
285: if (re)
286: parent = HTNewsDir_findNodeWithSubject(dir, refSubject,
287: FNWS_MIN | FNWS_NOTFAKE, node);
288: if (!parent || HTNewsNode_isAncestor(node, parent))
289: parent = HTNewsDir_findNodeWithSubject(dir, refSubject,
290: FNWS_MIN | FNWS_ONLYFAKE, node);
291: if (!parent && re)
292: {
293: parent = HTNewsDir_findNodeWithSubject(dir, node->subject,
294: FNWS_MIN | FNWS_ONLYFAKE, node);
295: }
296:
297: if (!parent) parent = HTNewsDir_addFakeElement (dir, refSubject, NULL);
298: if (parent) {
299: HTNewsNode_linkRef (parent, node);
300: if (parent->refChildren > 1) /* Multi-children fake node visible */
301: parent->show = YES;
302: }
303: }
304: }
305:
306: /*
307: ** Added by MP.
308: */
309: #if 0
310: PRIVATE void HTNewsNode_setRefInfo_pass3 (HTNewsDir* dir, HTNewsNode* node)
311: {
312: HTNewsNode* parent = NULL;
313: char* refSubject;
314: BOOL re;
315:
316: if (node->fake || node->refParent)
317: return; /* This is only for nodes not handled in pass 2 */
318:
319: /* Here is the only place we REALLY have to check for circular */
320: /* references. It is normally possible that a node refers to */
321: /* orphan node and both have the same subject. In this situation */
322: /* we can't make the orphan to refer to it's child. Without checking */
323: /* for circular references this is likely to happen here. */
324:
325: refSubject = UnReSubject(node->subject);
326: re = (strcasecomp(refSubject, node->subject) != 0);
327: if (re)
328: parent = HTNewsDir_findNodeWithSubject(dir, refSubject,
329: FNWS_MIN | FNWS_NOTFAKE, node);
330: if (!parent || HTNewsNode_isAncestor(node, parent))
331: parent = HTNewsDir_findNodeWithSubject(dir, refSubject,
332: FNWS_MIN | FNWS_ONLYFAKE, node);
333: if (!parent && re)
334: {
335: parent = HTNewsDir_findNodeWithSubject(dir, node->subject,
336: FNWS_MIN | FNWS_ONLYFAKE, node);
337: }
338:
339: if (!parent) parent = HTNewsDir_addFakeElement (dir, refSubject, NULL);
340: if (parent) {
341: HTNewsNode_linkRef (parent, node);
342: if (parent->refChildren > 1) /* multi-children fake node visible */
343: parent->show = YES;
344: }
345: }
346: #else
347: PRIVATE void HTNewsNode_setRefInfo_pass3 (HTNewsDir* dir, HTNewsNode* node)
348: {
349: if (!node->fake)
350: node->refLevel = HTNewsNode_refLevel(node);
351: }
352: #endif
353:
354: /* Added by MP. */
355: PRIVATE int HTNewsNode_compareRefThread (HTNewsNode* node1, HTNewsNode* node2)
356: {
357: int level1 = node1->refLevel;
358: int level2 = node2->refLevel;
359: int level = HTMAX(level1, level2);
360: int i;
361: HTNewsNode* parent1;
362: HTNewsNode* parent2;
363: int diff = 0;
364: for (i = level; i >= 0; i--)
365: {
366: parent1 = (i < level1 ? parent1->refParent : node1);
367: parent2 = (i < level2 ? parent2->refParent : node2);
368: if (parent1 == parent2)
369: return diff; /* related messages (in same subtree) */
370: else
371: {
372: time_t date1 = (i > level1 ? 0 : HTNewsNode_getDate(parent1, YES));
373: time_t date2 = (i > level2 ? 0 : HTNewsNode_getDate(parent2, YES));
374: diff = date1 - date2;
375: if (diff == 0)
376: {
377: int idx1 = (i > level1 ? 0 : HTNewsNode_getIndex(parent1, YES));
378: int idx2 = (i > level2 ? 0 : HTNewsNode_getIndex(parent2, YES));
379: diff = idx1 - idx2;
380: }
381: }
382: }
383: return diff; /* completely unrelated messages */
384: }
385:
2.1 frystyk 386: /*
387: ** Output an element in HTML
388: ** Returns YES if OK, else NO
389: */
2.9 frystyk 390: PRIVATE BOOL HTNewsNode_print (HTNewsDir * dir, HTNewsNode * node)
2.1 frystyk 391: {
2.9 frystyk 392: if (node && node->show) {
393: HTStructured *target = dir->target;
394: char * escaped;
395:
396: HTNewsDir_addLevelTags (dir, node->refLevel); /* Added by MP. */
397: START(HTML_LI);
398:
399: /* Start the anchor and put the subject as anchor text */
400: /* Changed by MP to allow nodes without names */
401: if (!node->fake && node->name && node->subject) {
402: escaped = HTEscape(node->name, URL_XPALPHAS);
403: HTStartAnchor(target, NULL, escaped);
404: }
405: if (node->subject) PUTS(node->subject);
406: if (!node->fake && node->name && node->subject) {
407: END(HTML_A);
408: HT_FREE(escaped);
409: }
2.1 frystyk 410:
2.9 frystyk 411: /* From field */
412: if (node->from) {
413: PUTS (" by "); /* Changed by MP. */
414: PUTS(node->from);
415: }
2.1 frystyk 416:
2.9 frystyk 417: /* In group listing, put number of groups in the set; added by MP. */
418: if (node->name && strrchr(node->name, '*')) {
419: char buf[16];
420: sprintf (buf, " (%d groups)", node->refChildren);
421: PUTS (buf);
422: }
2.1 frystyk 423: }
424: return YES;
425: }
426:
427: /* ------------------------------------------------------------------------- */
428: /* DIRECTORY MANAGEMENT */
429: /* ------------------------------------------------------------------------- */
430:
431: /* HTNewsDir_setWidth
432: ** ------------------
433: ** The module automatically ajusts the width of the directory listing as
434: ** a function of the file name. The width can flows dynamically between
435: ** an upper and a lower limit.
436: */
437: PUBLIC BOOL HTNewsDir_setWidth (int max_width)
438: {
439: MaxLineW = (max_width > 0) ? max_width : DEFAULT_MAXW;
440: return YES;
441: }
442:
443: /* HTNewsDir_new
444: ** ----------
445: ** Creates a structured stream object and sets up the initial HTML stuff
446: ** Returns the newsdir object if OK, else NULL
447: */
2.7 frystyk 448: PUBLIC HTNewsDir * HTNewsDir_new (HTRequest * request, const char * title,
2.9 frystyk 449: HTNewsDirKey key, BOOL cache)
2.1 frystyk 450: {
451: HTNewsDir *dir;
452: if (!request) return NULL;
453:
454: /* Create object */
2.5 frystyk 455: if ((dir = (HTNewsDir *) HT_CALLOC(1, sizeof (HTNewsDir))) == NULL)
456: HT_OUTOFMEM("HTNewsDir_new");
2.1 frystyk 457: dir->target = HTMLGenerator(request, NULL, WWW_HTML,
458: HTRequest_outputFormat(request),
459: HTRequest_outputStream(request));
2.4 frystyk 460: HTAnchor_setFormat(HTRequest_anchor(request), WWW_HTML);
2.1 frystyk 461: dir->request = request;
462: dir->key = key;
2.9 frystyk 463: dir->lastLevel = -1; /* Added by MP. */
464:
465: /* Get the newsgroup(s) name; added by MP. */
466: {
467: char* url = HTAnchor_physical(HTRequest_anchor(request));
468: char* p = url+strlen(url);
469: while (p > url && p[-1] != ':' && p[-1] != '/' && p[-1] != '\\')
470: p--;
471: StrAllocCopy (dir->name, p);
472: }
473:
2.3 frystyk 474: if (key != HT_NDK_NONE) { /* Thread is unsorted */
2.1 frystyk 475: int total = HTNews_maxArticles();
476: dir->array = HTArray_new(total > 0 ? total : 128);
477: }
478:
2.9 frystyk 479: /* If we are asked to prepare a cache entry then create the cache array */
480: if (cache) {
481: int total = HTNews_maxArticles();
482: dir->cache = HTArray_new(total > 0 ? total : 128);
483: }
484:
2.1 frystyk 485: /* Start the HTML stuff */
486: {
487: HTStructured *target = dir->target;
2.7 frystyk 488: const char *msg = title ? title : "News Listing";
2.1 frystyk 489: START(HTML_HTML);
490: START(HTML_HEAD);
491: START(HTML_TITLE);
492: PUTS(msg);
493: END(HTML_TITLE);
494: END(HTML_HEAD);
495: START(HTML_BODY);
496: START(HTML_H1);
497: PUTS(msg);
498: END(HTML_H1);
499: }
500: return dir;
501: }
502:
503: /* HTNewsDir_addElement
504: ** --------------------
2.3 frystyk 505: ** This function accepts a news line. Everything except dir and name can
2.1 frystyk 506: ** can be 0 or NULL.
2.9 frystyk 507: ** Returns new node pointer if OK, else NULL
508: ** Changed by MP: reference list added.
509: ** Note: Unlike other parameters, refNames is not copied, but assigned, so
510: ** it has to contain copies of message names, not the originals.
511: */
512: PUBLIC HTNewsNode* HTNewsDir_addElement (HTNewsDir * dir, int index,
513: char * subject, char * from,
514: time_t date, char * name,
515: int refs, HTList * refNames)
516: {
517: if (dir && name) {
518: HTNewsNode * node = HTNewsNode_new(index, subject, from,
519: date, name, refs, refNames);
520: if (dir->key == HT_NDK_NONE) {
521: HTNewsNode_print(dir, node);
522: HTNewsNode_delete(node, (dir->cache!=NULL));
523: } else
524: HTArray_addObject(dir->array, (void *) node);
525: return node;
526: }
527: return NULL;
528: }
529:
530: /* Helper function - added by MP. */
531: PRIVATE HTNewsNode* HTNewsDir_addFakeElement (HTNewsDir * dir,
532: char * subject, char * name)
533: {
534: HTNewsNode * node =
535: HTNewsDir_addElement(dir, 0, subject, NULL, 0, name, 0, NULL);
536: if (node) {
537: node->show = NO;
538: node->fake = YES;
539: }
540: return node;
541: }
542:
543: /* Helper function - added by MP. */
544: PUBLIC HTNewsNode * HTNewsDir_addGroupElement (HTNewsDir * dir, char * group,
545: BOOL tmplate)
546: {
547: HTNewsNode * node = NULL;
548: if (dir && group) {
549: if (HTNewsDir_belongsToSet(dir, group))
550: node=HTNewsDir_addElement (dir, 0, group, NULL, 0, group, 0, NULL);
551:
552: /* If we are building a cache object then add the entry */
553: if (dir->cache && !tmplate) {
554: char * name = node ? node->name : NULL;
555: if (!name) StrAllocCopy(name, group);
556: HTArray_addObject(dir->cache, name);
557: }
558: }
559: return node;
560: }
561:
562: /* Added by MP. */
563: PUBLIC BOOL HTNewsDir_belongsToSet (HTNewsDir* dir, char* group)
564: {
565: char* p;
566: if (!dir->name || !*(dir->name))
567: return YES;
568: p = strrchr(dir->name, '*');
569: if (!p)
570: return strcasecomp(group, dir->name) == 0;
571: else
572: {
573: int len = p - dir->name;
574: return strncasecomp(group, dir->name, len) == 0;
575: }
576: }
577:
578:
579: /* Added by MP. */
580: PRIVATE void HTNewsDir_addLevelTags (HTNewsDir* dir, int level)
581: {
582: HTStructured *target = dir->target;
583: int i = level;
584: while (i > dir->lastLevel)
585: {
586: START(HTML_UL);
587: i--;
588: }
589: while (i < dir->lastLevel)
590: {
591: END(HTML_UL);
592: i++;
593: }
594: dir->lastLevel = level;
595: }
596:
597: /* Added by MP. */
598: PRIVATE HTNewsNode* HTNewsDir_findNodeNamed (HTNewsDir* dir, char* name)
599: {
600: int i;
601: for (i = 0; i < HTArray_size(dir->array); i++)
602: {
603: HTNewsNode* node = (HTNewsNode*)(HTArray_data(dir->array)[i]);
604: if (node->name && strcasecomp(node->name, name) == 0)
605: return node;
606: }
607: return NULL;
608: }
609:
610: /* Added by MP. */
611: PRIVATE HTNewsNode* HTNewsDir_findNodeWithSubject (HTNewsDir* dir,
612: char* subject, int which, HTNewsNode* avoidNode)
613: {
614: int i;
615: int whichDate = (which & FNWS_MIN ? -1 : (which & FNWS_MAX ? 1 : 0));
616: HTNewsNode* foundNode = NULL;
617: for (i = 0; i < HTArray_size(dir->array); i++)
618: {
619: HTNewsNode* node = (HTNewsNode*)(HTArray_data(dir->array)[i]);
620: if (!(which & FNWS_ONLYFAKE && !node->fake) &&
621: !(which & FNWS_NOTFAKE && node->fake) &&
622: !(which & FNWS_NOTORPHAN && !node->fake && !node->refNames) &&
623: node != avoidNode && node->subject &&
624: strcasecomp(node->subject, subject) == 0)
625: {
626: if (which == FNWS_ANY)
627: return node;
628: else if (!foundNode || (node->date != 0 &&
629: (node->date - foundNode->date) * (long)whichDate > 0))
630: foundNode = node;
631: }
632: }
633: return foundNode;
634: }
635:
636: /* Added by MP. */
637: PRIVATE void HTNewsDir_setRefInfo (HTNewsDir* dir)
638: {
639: /* Array grows when fake elements are added. */
640: /* We don't want to set reference info for fake elements. */
641: int size = HTArray_size(dir->array);
642: int i;
643: for (i = 0; i < size; i++)
644: HTNewsNode_setRefInfo_pass1 (dir, (HTNewsNode*)(HTArray_data(dir->array)[i]));
645: for (i = 0; i < size; i++)
646: HTNewsNode_setRefInfo_pass2 (dir, (HTNewsNode*)(HTArray_data(dir->array)[i]));
647: for (i = 0; i < size; i++)
648: HTNewsNode_setRefInfo_pass3 (dir, (HTNewsNode*)(HTArray_data(dir->array)[i]));
649: }
650:
651: PRIVATE void make_template (HTNewsDir * dir, HTNewsNode * node)
652: {
653: HT_FREE(dir->tmplate);
654: if ((dir->tmplate = (char *) HT_MALLOC(strlen(node->name) + 3)) == NULL)
655: HT_OUTOFMEM("HTNewsNode_setGroupInfo");
656: {
657: char * p1 = dir->name;
658: char * p2 = dir->tmplate;
659: strcpy(p2, node->name);
660: while (*p1 && *p2 && *p1 == *p2) p1++, p2++;
661: while (*p2 && *p2 != '.') p2++;
662: if (*p2) {
663: strcpy(p2, ".*");
664: dir->tmplate_node=HTNewsDir_addGroupElement(dir, dir->tmplate,YES);
665: dir->tmplate_node->is_tmplate = YES;
666: } else {
667: HT_FREE(dir->tmplate);
668: dir->tmplate_node = node;
669: }
670: dir->tmplate_node->show = YES;
671: }
672: }
673:
674: /*
675: ** Runs through a sorted list of news groups and identifies the group
676: ** hierarchy. Template groups are added to the list, for example as
677: ** "alt.*"
2.1 frystyk 678: */
2.9 frystyk 679: PRIVATE void HTNewsDir_setGroupInfo (HTNewsDir * dir)
680: {
681: HTArray * array = dir->array;
682: HTNewsNode * node;
683: int cur_size = HTArray_size(array);
684: int cnt;
685:
686: /*
687: ** If we don't have a template to test against then create one
688: ** A template can be something like "alt.*" for example
689: */
690: for (cnt=0; cnt<cur_size; cnt++) {
691: node = (HTNewsNode *) HTArray_data(array)[cnt];
692:
693: /*
694: ** Make a template if we don't have any
695: */
696: if (!dir->tmplate) make_template(dir, node);
697:
698: /*
699: ** Now, if we do have a template then test the node name against
700: ** it to see if we have this group already or it is a new group
701: ** at this level in the hierarchy
702: */
703: if (dir->tmplate) {
704: if (HTStrCaseMatch(dir->tmplate, node->name) == NULL) {
705: make_template(dir, node);
706: } else {
707: HTNewsNode * tmp_node = dir->tmplate_node;
708:
709: /* Should we show this element in the list? */
710: if (tmp_node->lastChild) {
711: tmp_node->lastChild->show = NO;
712: node->show = NO;
713: }
714: }
715: HTNewsNode_linkRef(dir->tmplate_node, node);
2.1 frystyk 716: }
2.9 frystyk 717: }
2.1 frystyk 718: }
719:
2.7 frystyk 720: PRIVATE int NDirIndexSort (const void *a, const void *b)
2.3 frystyk 721: {
722: int aa = (*((HTNewsNode **)a))->index;
723: int bb = (*((HTNewsNode **)b))->index;
724: return aa-bb;
725: }
726:
2.7 frystyk 727: PRIVATE int NDirSubjectSort (const void *a, const void *b)
2.1 frystyk 728: {
2.3 frystyk 729: char *aa = (*((HTNewsNode **)a))->subject;
730: char *bb = (*((HTNewsNode **)b))->subject;
2.9 frystyk 731: return strcasecomp(aa?aa:"", bb?bb:"");
2.1 frystyk 732: }
733:
2.7 frystyk 734: PRIVATE int NDirFromSort (const void *a, const void *b)
2.1 frystyk 735: {
2.9 frystyk 736: char *aa = (*((HTNewsNode **)a))->from;
737: char *bb = (*((HTNewsNode **)b))->from;
738: return strcasecomp(aa?aa:"", bb?bb:"");
2.3 frystyk 739: }
740:
2.7 frystyk 741: PRIVATE int NDirDateSort (const void *a, const void *b)
2.3 frystyk 742: {
743: time_t aa = (*((HTNewsNode **)a))->date;
744: time_t bb = (*((HTNewsNode **)b))->date;
745: return bb-aa;
746: }
747:
2.7 frystyk 748: PRIVATE int NDirGroupSort (const void *a, const void *b)
2.3 frystyk 749: {
750: char *aa = (*((HTNewsNode **)a))->name;
751: char *bb = (*((HTNewsNode **)b))->name;
752: while (*aa && *bb && TOLOWER(*aa)==TOLOWER(*bb)) aa++, bb++;
753: return (*aa=='.' && *bb) ? -1 : (*aa && *bb=='.') ?
754: 1 : TOLOWER(*aa)-TOLOWER(*bb);
2.1 frystyk 755: }
756:
2.9 frystyk 757: /* Added by MP. */
758: PRIVATE int NDirRefThreadSort (const void* a, const void* b)
759: {
760: HTNewsNode* aa = *((HTNewsNode**)a);
761: HTNewsNode* bb = *((HTNewsNode**)b);
762: return HTNewsNode_compareRefThread(aa,bb);
763: }
764:
2.1 frystyk 765: /* HTNewsDir_free
766: ** --------------
767: ** If we are sorting then do the sorting and put out the list,
768: ** else just append the end of the list.
769: */
770: PUBLIC BOOL HTNewsDir_free (HTNewsDir * dir)
771: {
772: if (!dir) return NO;
2.3 frystyk 773: if (dir->key != HT_NDK_NONE) {
2.9 frystyk 774: HTArray * array = dir->array;
775: HTArray * cache = NULL;
776: HTComparer * comp = NULL;
777:
778: /*
779: ** Find a suitable sort key for this listing. The sort function
780: ** depends on the type of new listing we have received.
781: */
782: if (dir->key == HT_NDK_INDEX) /* Sort by Message Number */
783: comp = NDirIndexSort;
784: else if (dir->key == HT_NDK_DATE) /* Sort by Date */
785: comp = NDirDateSort;
786: else if (dir->key == HT_NDK_SUBJECT) /* Sort after Subject */
787: comp = NDirSubjectSort;
788: else if (dir->key == HT_NDK_FROM) /* Sort after From */
789: comp = NDirFromSort;
790: else if (dir->key == HT_NDK_GROUP) { /* Sort as group hierarchi */
2.3 frystyk 791: comp = NDirGroupSort;
2.9 frystyk 792: } else if (dir->key == HT_NDK_REFTHREAD) { /* Added by MP. */
793: HTNewsDir_setRefInfo (dir);
794: comp = NDirRefThreadSort;
795: } else {
796: if (STREAM_TRACE) HTTrace("NewsListing. Invalid sortkey\n");
797: return NO;
798: }
799:
800: /*
801: ** Now sort the array of news items that we have read with the sort
802: ** function defined by the sort key above.
803: */
804: HTArray_sort(array, comp);
805:
806: /*
807: ** If we are showing a group listing then run through the list and
808: ** identify group hierarchy. We have to sort the thing again in order
809: ** to get the new template groups included
810: */
811: if (dir->key == HT_NDK_GROUP) {
812: HTNewsDir_setGroupInfo(dir);
813: HTArray_sort(array, comp);
2.3 frystyk 814: }
2.9 frystyk 815:
816: /*
817: ** After we have sorted the listing, we can write out the result and
818: ** free the array.
819: */
820: {
821: void ** data;
822: HTNewsNode *node = (HTNewsNode *) HTArray_firstObject(array, data);
823: while (node) {
2.3 frystyk 824: HTNewsNode_print(dir, node);
2.9 frystyk 825:
826: /*
827: ** Create a special array for the cache containing the group
828: ** names only and no templates
829: */
830: if (dir->key == HT_NDK_GROUP && !node->is_tmplate)
831: HTArray_addObject(cache, node->name);
832:
833: HTNewsNode_delete(node, (dir->cache!=NULL));
2.3 frystyk 834: node = (HTNewsNode *) HTArray_nextObject(array, data);
2.9 frystyk 835: }
836: HTArray_delete(array);
2.1 frystyk 837: }
2.9 frystyk 838:
839: /* Update the cache */
2.10 ! frystyk 840: if (dir->cache) HTNewsCache_after(dir->request, NULL, dir->cache, 0);
2.1 frystyk 841: }
842:
843: /* Put out the end of the HTML stuff */
844: {
845: HTStructured *target = dir->target;
2.9 frystyk 846: /* END(HTML_UL); */
847: HTNewsDir_addLevelTags (dir, -1);
2.1 frystyk 848: START(HTML_HR);
849: END(HTML_BODY);
850: END(HTML_HTML);
851: FREE_TARGET;
852: }
2.9 frystyk 853:
854: /* Clean up the dir object */
855: HT_FREE(dir->name);
856: HT_FREE(dir->tmplate);
2.5 frystyk 857: HT_FREE(dir);
2.1 frystyk 858: return YES;
859: }
Webmaster