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