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