Annotation of libwww/Library/src/HTNDir.c, revision 2.9
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.9 ! frystyk 6: ** @(#) $Id: HTNDir.c,v 2.8 1996/04/12 17:48:05 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 */
! 840: if (dir->cache) HTNewsCache_after(dir->request, 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