home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994…tember: Reference Library / Dev.CD Sep 94.toast / Periodicals / develop / develop Issue 6 / develop 6 code / TCP / NewsWatcher / NewsWatcher 2.0d15 source / source / article.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-30  |  9.1 KB  |  390 lines  |  [TEXT/KAHL]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     article.c
  4.  
  5.     This module handles the creation of article windows.
  6.     
  7.     Portions copyright © 1990, Apple Computer.
  8.     Portions copyright © 1993, Northwestern University.
  9.  
  10. ----------------------------------------------------------------------------*/
  11.  
  12. #include <string.h>
  13. #include <stdio.h>
  14.  
  15. #include "glob.h"
  16. #include "article.h"
  17. #include "child.h"
  18. #include "close.h"
  19. #include "dlgutil.h"
  20. #include "header.h"
  21. #include "mark.h"
  22. #include "nntp.h"
  23. #include "open.h"
  24. #include "resize.h"
  25. #include "scroll.h"
  26. #include "util.h"
  27.  
  28.  
  29. /*    CalcSectionBreaks calculates section breaks for an article 
  30.     larger than 32K. */
  31.  
  32. static void CalcSectionBreaks (WindowPtr wind)
  33. {
  34.     TWindow **info;
  35.     Handle text;
  36.     long **breaks;
  37.     long len;
  38.     short numSections;
  39.     char *p,*textEnd,*qStart,*qEnd,*q,*r,*qCR,*qCRCR;
  40.  
  41.     info = (TWindow**)GetWRefCon(wind);
  42.     text = (**info).fullText;
  43.     len = GetHandleSize(text);
  44.     breaks = (long**)MyNewHandle((len/25000 + 2)<<2);
  45.     p = *text;
  46.     textEnd = *text + len;
  47.     numSections = 0;
  48.     while (true) {
  49.         (*breaks)[numSections] = p - *text;
  50.         qStart = p+31999;
  51.         if (qStart >= textEnd) break;
  52.         qCR = qCRCR = qEnd = p+25010;
  53.         for (q = qStart; q > qEnd; q--) {
  54.             if (*q == CR) {
  55.                 qCR = q;
  56.                 if (*(q-1) == CR) {
  57.                     qCRCR = q;
  58.                     for (r = q-2; *r == '-' && r > qEnd; r--);
  59.                     if (*r == CR) break;
  60.                 }
  61.             }
  62.         }
  63.         if (q <= qEnd) q = qCRCR;
  64.         if (q <= qEnd) q = qCR;
  65.         if (q <= qEnd) q = qStart;
  66.         p = q+1;
  67.         numSections++;
  68.     }
  69.     (*breaks)[++numSections] = len;
  70.     (**info).numSections = numSections;
  71.     (**info).curSection = 0;
  72.     (**info).sectionBreaks = breaks;
  73.     MakeHScroller(wind,numSections-1);
  74. }
  75.  
  76. /*    OpenArticle gets the text of an article, given the newsgroup,
  77.     and article number or message-id.  The article is fetched
  78.     and placed in a new window created by the procedure call.
  79.  
  80.     Returns as function result:
  81.         0 if article retrieved and window opened, or existing open window brought to front.
  82.         1 if article does not exist on server.
  83.         2 if some other error.
  84. */
  85.  
  86. static short OpenArticle (char *newsGroup, char *number, char *title,
  87.     WindowPtr parent, EWindowKind kind)
  88. {
  89.     Handle text;
  90.     long length;
  91.     WindowPtr wind;
  92.     TWindow **newInfo;
  93.     GrafPtr savePort;
  94.     TEHandle theTE;
  95.     Point firstOffset;
  96.     OSErr err = noErr;
  97.     short result;
  98.     long offset;
  99.     Handle msgId;
  100.     char subject[250];
  101.     CStr255 miscTitle;
  102.     CStr255 wTitle;
  103.     WindowPtr w;
  104.     short i;
  105.     
  106.     result = GetArticle(newsGroup,number,&text,&length);
  107.     if (result != 0) return result;
  108.  
  109.     SetPt(&firstOffset,0,0);
  110.     GetPort(&savePort);
  111.         
  112.     SetPort(parent == nil ? FrontWindow() : parent);    
  113.     LocalToGlobal(&firstOffset);
  114.     SetPort(savePort);
  115.  
  116.     if (kind == kMiscArticle) {
  117.         FindHeader(text, "Subject:", subject, sizeof(subject));
  118.         i = 1;
  119.         while (true) {
  120.             sprintf(miscTitle, "%d.%s", i, subject); 
  121.             w = FrontWindow();
  122.             while (w != nil) {
  123.                 GetWTitle(w, (StringPtr)wTitle);
  124.                 p2cstr((StringPtr)wTitle);
  125.                 if (strcmp(wTitle, miscTitle) == 0) break;
  126.                 w = (WindowPtr)((WindowPeek)w)->nextWindow;
  127.             }
  128.             if (w == nil) break;
  129.             i++;
  130.         }
  131.         c2pstr(miscTitle);
  132.         wind = MakeNewWindow(kind, firstOffset, (StringPtr)miscTitle);
  133.     } else {
  134.         c2pstr(title);
  135.         wind = MakeNewWindow(kind, firstOffset, (StringPtr)title);
  136.         p2cstr((StringPtr)title);
  137.     }
  138.  
  139.     newInfo = (TWindow**) GetWRefCon(wind);
  140.     (**newInfo).parentWindow = parent;
  141.     (**newInfo).fullText = text;
  142.     
  143.     if (kind == kMiscArticle) {
  144.         PtrToHand(number, &msgId, strlen(number)+1);
  145.         (**newInfo).msgId = msgId;
  146.     }
  147.     
  148.     if (parent != nil) AddChild(parent, wind);
  149.     SetPort(wind);
  150.     
  151.     if (length > kMaxLength) {
  152.         CalcSectionBreaks(wind);
  153.         length = (*(**newInfo).sectionBreaks)[1];
  154.     }
  155.     theTE = (**newInfo).theTE;
  156.     
  157.     (**newInfo).headerShown = gPrefs.showHeaders;
  158.     
  159.     offset = gPrefs.showHeaders ? 0 : FindBody(text);
  160.     HLock(text);
  161.     TESetText(*text+offset, length-offset, theTE);
  162.     HUnlock(text);
  163.     
  164.     TESetSelect(0,0,theTE);
  165.     if (!DoZoom(wind,inZoomOut)) {
  166.         result = 2;
  167.         goto exit;
  168.     }
  169.     AdjustScrollBar(wind);
  170.     ShowWindow(wind);
  171.     SetPort(savePort);
  172.     return 0;
  173.  
  174. exit:
  175.     
  176.     DoCloseWindow(wind);
  177.     SetPort(savePort);
  178.     return result;
  179. }
  180.  
  181.  
  182. /*    OpenSubjectCell opens one article for a cell in a subject list window.
  183.     
  184.     Entry:    wind = pointer to subject list window.
  185.             theCell = the cell in the subject list window to be opened.
  186.             threadOrdinal = ordinal of article within thread to
  187.                 be opened.
  188.             
  189.     Exit:    function result = 
  190.                 0 if article window opened.
  191.                 1 if article no longer on server.
  192.                 2 if some other error.
  193. */
  194.  
  195. short OpenSubjectCell (WindowPtr wind, Cell theCell, short threadOrdinal)
  196. {
  197.     TWindow **info,**childInfo;
  198.     ListHandle theList;
  199.     TSubject **subjectArray, subject;
  200.     CStr255 newsGroup, numStr;
  201.     char title[512];
  202.     short cellData, cellDataLen;
  203.     WindowPtr child;
  204.     short result;
  205.     Handle strings;
  206.     
  207.     info = (TWindow**) GetWRefCon(wind);
  208.     theList = (**info).theList;
  209.     subjectArray = (**info).subjectArray;
  210.     strings = (**info).strings;
  211.     
  212.     cellDataLen = 2;
  213.     LGetCell(&cellData, &cellDataLen ,theCell, theList);
  214.     while (threadOrdinal-- > 1) 
  215.         cellData = (*subjectArray)[cellData].nextInThread;
  216.     
  217.     if ((child = FindChildByCellData(wind, cellData)) != nil) {
  218.         SelectWindow(child);
  219.         return 0;
  220.     }
  221.  
  222.     subject = (*subjectArray)[cellData];
  223.     
  224.     GetWTitle(wind, (StringPtr)newsGroup);
  225.     p2cstr((StringPtr)newsGroup);
  226.     
  227.     *title = '√';
  228.     strcpy(title+1, *strings + subject.subjectOffset);
  229.     if (subject.threadLength > 1) {
  230.         NumToString(subject.threadOrdinal, (StringPtr)numStr);
  231.         p2cstr((StringPtr)numStr);
  232.         strcat(title, " - ");
  233.         strcat(title, numStr);
  234.     }
  235.     title[255] = 0;
  236.     
  237.     NumToString(subject.number, (StringPtr)numStr);
  238.     p2cstr((StringPtr)numStr);
  239.     
  240.     result = OpenArticle(newsGroup,numStr,title,wind,kArticle);
  241.     if (result != 0) return result;
  242.     
  243.     child = FrontWindow();
  244.     childInfo = (TWindow**)GetWRefCon(child);
  245.     (**childInfo).parentSubject = cellData;
  246.     MarkArticle(FrontWindow(),true);
  247.  
  248.     return 0;
  249. }
  250.  
  251.  
  252. /*  CheckMiscArticleAlreadyOpen checks to see if a miscellaneous article
  253.     window is already open. If it is already open, it is brought to the front
  254.     and the function returns true. If not, the function returns false.
  255. */
  256.  
  257. static Boolean CheckMiscArticleAlreadyOpen (char *msgId)
  258. {
  259.     WindowPtr wind;
  260.     TWindow **info;
  261.     
  262.     wind = FrontWindow();
  263.     while (wind != nil) {
  264.         if (IsAppWindow(wind)) {
  265.             info = (TWindow**)GetWRefCon(wind);
  266.             if ((**info).kind == kMiscArticle) {
  267.                 if (strcmp(msgId, *(**info).msgId) == 0) {
  268.                     SelectWindow(wind);
  269.                     return true;
  270.                 }
  271.             }
  272.         }
  273.         wind = (WindowPtr)(((WindowPeek)wind)->nextWindow);
  274.     }
  275.     return false;
  276. }
  277.  
  278.  
  279. /*    OpenReferences opens up all articles which are referred to in an article 
  280.     or message.  The "References:" field is used to determine which articles 
  281.     should be fetched.
  282. */
  283.  
  284. void OpenReferences (WindowPtr wind)
  285. {
  286.     TWindow **info;
  287.     long startReference,endReference;
  288.     CStr255 msgId;
  289.     char refs[4096];
  290.     short totalRefs=0, totalOpenedRefs=0;
  291.     CStr255 msg;
  292.     Handle text;
  293.     EWindowKind kind;
  294.     short msgIdLen;
  295.     
  296.     info = (TWindow**)GetWRefCon(wind);
  297.     kind = (**info).kind;
  298.     
  299.     if (kind == kArticle || kind == kMiscArticle) {
  300.         text = (**info).fullText;
  301.     } else if ((**info).headerShown) {
  302.         text = (Handle)TEGetText((**info).theTE);
  303.     } else {
  304.         text = (**info).headerText;
  305.     }
  306.  
  307.     if(!FindHeader(text,"References:",refs,sizeof(refs)))
  308.     {
  309.         SysBeep(1);
  310.         return;
  311.     }
  312.     endReference = strlen(refs);
  313.     while (endReference > 0) {
  314.         while(refs[endReference] != '>' && endReference >= 0) endReference--;
  315.         startReference = endReference;
  316.         while(refs[startReference] != '<' && startReference >= 0) 
  317.             startReference--;
  318.         msgIdLen = endReference - startReference + 1;
  319.         strncpy(msgId, &refs[startReference], msgIdLen);
  320.         msgId[msgIdLen] = 0;
  321.         totalRefs++;
  322.         if (CheckMiscArticleAlreadyOpen(msgId)) {
  323.             totalOpenedRefs++;
  324.         } else {
  325.             switch (OpenArticle(nil,msgId,"",nil,kMiscArticle)) {
  326.                 case 0:
  327.                     totalOpenedRefs++;
  328.                     break;
  329.                 case 1:
  330.                     break;
  331.                 case 2:
  332.                     return;
  333.             }
  334.         }
  335.         endReference = startReference;
  336.     }
  337.     if (totalOpenedRefs < totalRefs) {
  338.         if (totalOpenedRefs == 0) {
  339.             strcpy(msg,"None of the referenced articles could be opened");
  340.         } else {
  341.             strcpy(msg,"Some of the referenced articles could not be opened");
  342.         }
  343.         strcat(msg,", because they no longer exist on the news server.");
  344.         ErrorMessage(msg);
  345.     }
  346. }
  347.  
  348.  
  349. /*    OpenSelectedReference opens article corresponding to selected message ID.
  350. */
  351.  
  352. void OpenSelectedReference (WindowPtr wind)
  353. {
  354.     TWindow **info;
  355.     TEHandle te;
  356.     Handle theText;
  357.     CStr255 msgId;
  358.     short selStart, selEnd;
  359.     char *start,*end;
  360.     short teLen;
  361.         
  362.     info = (TWindow**)GetWRefCon(wind);
  363.     
  364.     te = (**info).theTE;
  365.     teLen = (**te).teLength;
  366.     theText = (Handle) TEGetText(te);
  367.     selStart = (*te)->selStart;
  368.     selEnd = (*te)->selEnd;
  369.     
  370.     HLock(theText);
  371.     
  372.     start = *theText + selStart;
  373.     end = *theText + selEnd - 1;
  374.     while (start >= *theText && *start != '<' && *start != CR) start--;
  375.     while (end < *theText + teLen && *end != '>' && *end != CR) end++;
  376.     if (*start != '<' || *end != '>' || end-start > 254) {
  377.         HUnlock(theText);
  378.         SysBeep(1);
  379.         return;
  380.     }
  381.     strncpy(msgId, start, end-start+1);
  382.     msgId[end-start+1] = 0;
  383.  
  384.     HUnlock(theText);
  385.     
  386.     if (CheckMiscArticleAlreadyOpen(msgId)) return;
  387.     if (OpenArticle(nil, msgId, "", nil, kMiscArticle) == 1) 
  388.         ErrorMessage("The referenced article no longer exists on the news server.");
  389. }
  390.