home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / amiga / comms / network / grn1src.lha / amode.c next >
Encoding:
C/C++ Source or Header  |  1992-07-05  |  62.9 KB  |  1,708 lines

  1. #include        "defs.h"
  2. #include        "memalloc.h"
  3. #include        "headers.h"
  4.  
  5. #define DISABLE_GADGETS 1
  6.  
  7. // #define PROCDEBUG
  8.  
  9. #ifdef PROCDEBUG
  10. #define D(x)    printf x
  11. #else
  12. #define D(x)
  13. #endif
  14.  
  15. extern char     *NewsGroupToFileName (char *newsgroup);
  16. extern void     AppendSignature (int outfd);
  17. extern void     AppendNewsGroupSignature (int outfd);
  18. extern void     DisableWindow (void);
  19. extern void     EnableWindow (void);
  20.  
  21. static LIST     msgList;
  22.  
  23. ART             *currentArticle;
  24. char            *ArticleName (ART *ap); /* FORWARD */
  25.  
  26. /*
  27.     Parameters to Mail()
  28. */
  29. #define MAIL_MAIL       0
  30. #define MAIL_REPLY      1
  31. #define MAIL_FORWARD    2       // much like a MAIL_REPLY
  32.  
  33. /************************************************************************/
  34.  
  35. /*
  36.  * As each gadget in the GadDefs array above is created, it is assigned a UserID
  37.  * field value, incremented from zero.  The following defines are useful for switch
  38.  * statements, etc.  Enum would also work.  Be careful, if you add new gadgets in
  39.  * the middle of the initializations above, because you will need to renumber these
  40.  * defines.
  41.  */
  42. static enum GADGET_IDS {
  43.         ARTLIST_ID,
  44.         HIDEHEADERS_ID,
  45.         HIDEREAD_ID,
  46.         SORT_ID,
  47.         QUIT_ID,
  48.         PREV_ID,
  49.         NEXT_ID,
  50.         NEXTUNREAD_ID,
  51.         NEXTTHREAD_ID,
  52.         CATCHUP_ID,
  53.         PREVGROUP_ID,
  54.         NEXTGROUP_ID,
  55.         FROM_ID,
  56.         ORGANIZATION_ID,
  57.         DATE_ID,
  58.         SUBJECT_ID,
  59.         MSGLIST_ID,
  60.         STATUS_ID,
  61.         SAVE_ID,
  62.         REPLY_ID,
  63.         FORWARD_ID,
  64.         FOLLOWUP_ID,
  65.         POST_ID,
  66.         PRINT_ID,
  67.         MAXGADGET,
  68. };
  69.  
  70. static GADGET   *gadgetArray[MAXGADGET];
  71.  
  72. static char     *sortLabels[] = { "Number", "From", "Subject", 0 };
  73.  
  74. static int      viewHeight;             // height of MSGLIST listview in character lines
  75. static int      viewTop = 0;            // top line of MSGLIST view (for GTLV_Top pagedown
  76. static int      messageLines = 0;       // number of lines in message
  77. static int      idcmp_shifted = 0;      // if you really want to know, see the
  78.                                         // 2.0 "Includes" RKM, page 347 right
  79.                                         // after IDCMP_VANILLAKEY, and remember
  80.                                         // that we set RAWKEY | VANILLAKEY
  81.  
  82. /*
  83.  * Misc tags arrays
  84.  */
  85. static TAGS     nullTags[] = { GT_Underscore, '_', TAG_DONE,0 };
  86. static TAGS     disableTags[] = { GT_Underscore, '_', GA_Disabled,TRUE, TAG_DONE,0 };
  87. static TAGS     enableTags[] = { GT_Underscore, '_', GA_Disabled,FALSE, TAG_DONE,0 };
  88.  
  89. static TAGS     sortTags[] = {
  90.         GT_Underscore, '_',
  91.         GTCY_Labels, &sortLabels[0],
  92.         GTCY_Active, 0,
  93.         TAG_DONE, 0,
  94. };
  95.  
  96. static TAGS     artListTags[] = {
  97.         GT_Underscore, '_',
  98.         GTLV_Top, 0,
  99.         GTLV_Labels, 0,
  100.         GTLV_ReadOnly, FALSE,
  101.         GTLV_ScrollWidth, 16,
  102. #ifdef MYKE_REMOVED_THIS
  103.         GTLV_ShowSelected, NULL,
  104. #endif
  105.         GTBB_Recessed, FALSE,
  106.         TAG_DONE, 0,
  107. };
  108.  
  109. static TAGS     msgListTags[] = {       // LISTVIEW_KIND
  110.         GT_Underscore, '_',
  111.         GTLV_Top, 0,
  112.         GTLV_Labels, &msgList,
  113.         GTLV_ReadOnly, TRUE,
  114.         GTLV_ScrollWidth, 16,
  115.         GTBB_Recessed, TRUE,
  116.         TAG_DONE, 0,
  117. };
  118.  
  119. static char     *nullText = "";
  120. static TAGS     textTags[] = {
  121.         GT_Underscore, '_',
  122.         GTTX_Text, (ULONG)"",
  123.         GTTX_CopyText, TRUE,
  124.         GTTX_Border, TRUE,
  125.         TAG_DONE, 0,
  126. };
  127.  
  128. /*
  129.  * The Array of Gadget definitions.  To create a new gadget, add an initializer
  130.  * here, and create the necessary tags...
  131.  */
  132. static GADDEF   gadDefs[] = {
  133.         artListTags, LISTVIEW_KIND,0,0,0,0, NG_HIGHLABEL, "",
  134.         nullTags, CHECKBOX_KIND, 0,0,0,0, NG_HIGHLABEL, "Hide Headers:",
  135.         disableTags, CHECKBOX_KIND, 0,0,0,0, NG_HIGHLABEL, "Hide Read Messages:",
  136.         sortTags, CYCLE_KIND, 0,0,0,0, NG_HIGHLABEL, "Sort By:",
  137.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "_Groups",
  138.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "_prev",
  139.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "_next",
  140.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "_unread",
  141.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "_thread",
  142.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "_Catch up",
  143.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "_Prev Group",
  144.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "_Next Group",
  145.         textTags, TEXT_KIND, 0,0,0,0, NG_HIGHLABEL, "From:",
  146.         textTags, TEXT_KIND, 0,0,0,0, NG_HIGHLABEL, "Org:",
  147.         textTags, TEXT_KIND, 0,0,0,0, NG_HIGHLABEL, "Date:",
  148.         textTags, TEXT_KIND, 0,0,0,0, NG_HIGHLABEL, "Subj:",
  149.         msgListTags, LISTVIEW_KIND, 0,0,0,0, NG_HIGHLABEL, "",
  150.         textTags, TEXT_KIND, 0,0,0,0, NG_HIGHLABEL, "",
  151.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "_Save",
  152.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "_Reply",
  153.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "Forward",
  154.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "_FollowUp",
  155.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "Post",
  156.         enableTags, BUTTON_KIND, 0,0,0,0, NG_HIGHLABEL, "Print",
  157.         0,0,0,0,0,0,0,0,
  158. };
  159.  
  160. static GADGET   *gList = 0;
  161.  
  162. static void     LayoutGadgets (FONT *font, short width, short height,short top)
  163. {
  164.         short   i, x,y, fh, lh;
  165.         RPORT   rPort, *rp = &rPort;
  166.  
  167.         InitRastPort (rp);
  168.         SetFont (rp, font);
  169.         y = top + INTERHEIGHT / 2;
  170.  
  171.         lh = windowHeight / 50;
  172.         if (lh > 10)
  173.                 lh = 10;
  174.         fh = rp->TxHeight + 2;
  175.         gadDefs [ARTLIST_ID].left   = INTERWIDTH;
  176.         gadDefs [ARTLIST_ID].top    = y;
  177.         gadDefs [ARTLIST_ID].width  = width - INTERWIDTH - INTERWIDTH;
  178.         gadDefs [ARTLIST_ID].height = (lh * fh) + INTERHEIGHT;
  179.         y += (lh * fh) + INTERHEIGHT + 2;
  180.  
  181.         fh = topazRP->TxHeight+2;
  182.         x = INTERWIDTH;
  183.         gadDefs [HIDEHEADERS_ID].left = x + TextLen (topazRP, gadDefs [HIDEHEADERS_ID].text) + 20;
  184.         gadDefs [HIDEHEADERS_ID].top = y;
  185.         gadDefs [HIDEHEADERS_ID].width = 12;
  186.         gadDefs [HIDEHEADERS_ID].height = fh + 2;
  187.         x = gadDefs [HIDEHEADERS_ID].left + gadDefs [HIDEHEADERS_ID].width + INTERWIDTH;
  188.  
  189.         gadDefs [HIDEREAD_ID].left = x + TextLen (topazRP, gadDefs [HIDEREAD_ID].text) + 20;
  190.         gadDefs [HIDEREAD_ID].top = y;
  191.         gadDefs [HIDEREAD_ID].width = 12;
  192.         gadDefs [HIDEREAD_ID].height = fh + 2;
  193.         x = gadDefs [HIDEREAD_ID].left + gadDefs [HIDEREAD_ID].width + INTERWIDTH;
  194.  
  195.         gadDefs [SORT_ID].left = x + TextLen (topazRP, gadDefs [SORT_ID].text) + 20;
  196.         gadDefs [SORT_ID].top = y;
  197.         gadDefs [SORT_ID].width = TextLen (topazRP, "Subject") + 24;
  198.         gadDefs [SORT_ID].height = fh + 2;
  199.         x = gadDefs [SORT_ID].left + gadDefs [SORT_ID].width + INTERWIDTH;
  200.  
  201.         y += fh + 4;
  202.  
  203.         x = INTERWIDTH;
  204.         for (i = QUIT_ID; i <= NEXTGROUP_ID; i++) {
  205.                 gadDefs [i].left = x;
  206.                 gadDefs [i].top = y;
  207.                 gadDefs [i].width = TextLen (topazRP, gadDefs [i].text) + 4;
  208.                 gadDefs [i].height = fh + 4;
  209.                 x += gadDefs [i].width + INTERWIDTH;
  210.         }
  211.  
  212.         y += fh + 4 + INTERHEIGHT;
  213.  
  214.         for (i = FROM_ID; i <= SUBJECT_ID; i++) {
  215.                 gadDefs [i].left = INTERWIDTH + 48;
  216.                 gadDefs [i].top = y;
  217.                 gadDefs [i].width = width - 48 - INTERWIDTH - INTERWIDTH;
  218.                 gadDefs [i].height = fh + 2;
  219.                 y += fh + INTERHEIGHT;
  220.         }
  221.  
  222.         fh = rp->TxHeight + 2;
  223.         gadDefs [MSGLIST_ID].left   = INTERWIDTH;
  224.         gadDefs [MSGLIST_ID].top    = y;
  225.         gadDefs [MSGLIST_ID].width  = width - INTERWIDTH - INTERWIDTH;
  226.         gadDefs [MSGLIST_ID].height = height - y - fh - fh - INTERHEIGHT - INTERHEIGHT;
  227.         y += gadDefs [MSGLIST_ID].height;
  228.         viewHeight = gadDefs [MSGLIST_ID].height / (fh - 2);    // MBS-FIX
  229.  
  230.         fh = topazRP->TxHeight + 2;
  231.         // viewHeight = gadDefs [MSGLIST_ID].height / (fh - 2);    MBS-FIX
  232.         gadDefs [STATUS_ID].left = INTERWIDTH;
  233.         gadDefs [STATUS_ID].top = y;
  234.         gadDefs [STATUS_ID].width = width - INTERWIDTH - INTERWIDTH;
  235.         gadDefs [STATUS_ID].height = fh + 2;
  236.         y += fh + 4;
  237.  
  238.         x = INTERWIDTH;
  239.         for (i = SAVE_ID; i <= PRINT_ID; i++) {
  240.                 gadDefs [i].left = x;
  241.                 gadDefs [i].top = y;
  242.                 gadDefs [i].width = TextLen (topazRP, gadDefs [i].text) + 4;
  243.                 gadDefs [i].height = fh+2;
  244.                 x += gadDefs [i].width + INTERWIDTH;
  245.         }
  246.         y += fh + 4;
  247.         return;
  248. }
  249.  
  250. /************************************************************************/
  251.  
  252. void    CloseArticles (void)
  253. {
  254.         D (("CloseArticles(): enter\n"));
  255.  
  256.         if (!gList)
  257.                 return;
  258.         if (RemoveGList (mainWindow, gList, -1) == -1) {
  259.                 D (("CloseArticles(): exit: RemoveGList() failed\n"));
  260.                 return;
  261.         }
  262.         FreeGadgets (gList);
  263.         gList = 0;
  264.         ClearWindow ();
  265.         RefreshGList (mainWindow->FirstGadget, mainWindow, 0, -1);
  266.         GT_RefreshWindow (mainWindow, NULL);
  267.         D (("CloseArticles(): exit\n"));
  268.         return;
  269. }
  270.  
  271. /************************************************************************/
  272.  
  273. GLIST   *NextGroup (void)
  274. {
  275.         GLIST   *gp = currentGroup;
  276.  
  277.         D (("NextGroup(): enter\n"));
  278.  
  279.         do {
  280.                 if (groupList.lh_TailPred == (NODE *)gp)
  281.                         gp = (GLIST *)groupList.lh_Head;
  282.                 else
  283.                         gp = (GLIST *)gp->node.ln_Succ;
  284.         } while (!gp->articles && gp != currentGroup);
  285.  
  286.         D (("NextGroup(): exit\n"));
  287.  
  288.         return gp;
  289. }
  290.  
  291. GLIST   *PrevGroup (void)
  292. {
  293.         GLIST   *gp = currentGroup;
  294.  
  295.         D (("PrevGroup(): enter\n"));
  296.  
  297.         do {
  298.                 if (groupList.lh_Head == (NODE *)gp)
  299.                         gp = (GLIST *)groupList.lh_TailPred;
  300.                 else
  301.                         gp = (GLIST *)gp->node.ln_Pred;
  302.         } while (!gp->articles && gp != currentGroup);
  303.  
  304.         D (("PrevGroup(): exit\n"));
  305.  
  306.         return gp;
  307. }
  308.  
  309. void    SortArticles (void)
  310. {
  311.         D (("SortArticles(): enter\n"));
  312.  
  313.         switch (currentGroup->sortActive) {
  314.                 case 0:
  315.                         SortArticlesByNumber (¤tGroup->artList);
  316.                         break;
  317.                 case 1:
  318.                         SortArticlesByFrom (¤tGroup->artList);
  319.                         break;
  320.                 case 2:
  321.                         SortArticlesBySubject (¤tGroup->artList);
  322.                         break;
  323.         }
  324.  
  325.         D (("SortArticles(): exit\n"));
  326.         return;
  327. }
  328.  
  329. void    Save (void)
  330. {
  331.         char    *temp;                  // I/O buffer to disk
  332.         char    *filename;              // filename to save article to
  333.         char    *ptr;                   // fully qualified article filename
  334.         int     fd;                     // article fd
  335.         int     outfd;                  // output file fd
  336.         int     len;
  337.         ULONG   size, count = 0, pct;
  338.  
  339.         D (("Save(): enter\n"));
  340.  
  341.         // first, have user specify filename to save the article to
  342.         if (!AslRequest (saveReq, NULL))
  343.                 return;
  344.         len = strlen (saveReq->rf_Dir) + strlen (saveReq->rf_File) + 10;
  345.         if (!(filename = malloc (len))) {
  346.                 t_printf (mainWindow, "Can't malloc %d bytes for filename", len);
  347.                 D (("Save(): memory failure, exit\n"));
  348.                 return;
  349.         }
  350.         strcpy (filename, saveReq->rf_Dir);
  351.         AddPart (filename, saveReq->rf_File, len);
  352.  
  353.         // find the article, and see how big it is
  354.         ptr = ArticleName (currentArticle);
  355.         if ((fd = open (ptr, O_RDONLY)) < 0) {
  356.                 t_printf (mainWindow, "Can't open '%s' for input\n", ptr);
  357.                 D (("Save(): open input failure, exit\n"));
  358.                 return;
  359.         }
  360.         size = lseek (fd, 0, 2);
  361.         lseek (fd, 0, 0);
  362.  
  363.         // create output file (APPEND to it if it already exists)
  364.         if ((outfd = open (filename, O_WRONLY | O_APPEND | O_CREAT)) < 0) {
  365.                 t_printf (mainWindow, "Can't open '%s' for output\n", ptr);
  366.                 close (fd);
  367.                 free (filename);
  368.                 D (("Save(): open output failure, exit\n"));
  369.                 return;
  370.         }
  371.  
  372.         // get memory for I/O buffer
  373.         if (!(temp = malloc (512))) {
  374.                 t_printf (mainWindow, "Can't malloc 512 bytes for I/O");
  375.                 close (outfd);
  376.                 close (fd);
  377.                 free (filename);
  378.                 D (("Save(): get mem failure, exit\n"));
  379.                 return;
  380.         }
  381.  
  382.         // save the article
  383.         while ((len = read (fd, temp, 512)) > 0) {
  384.                 count += len;
  385.                 pct = 100 * count / size;
  386.                 if (GuageRequest (pct, "GRn - Please Wait...",
  387.                                         "       Saving        ", "Abort"))
  388.                         break;
  389.                 // FIXME - check for errors on write() [DISK full, etc.]
  390.                 write (outfd, temp, len);
  391.         }
  392.         GuageRequest (1000, "GRn - Please Wait...",
  393.                             "       Saving        ", "Abort");
  394.         free (temp);
  395.         free (filename);
  396.         close (fd);
  397.         close (outfd);
  398.         D (("Save(): exit\n"));
  399.         return;
  400. }
  401.  
  402. void    Print (void)
  403. {
  404.         char    *temp;
  405.         char    *ptr;
  406.         int     fd;
  407.         int     outfd;
  408.         int     len;
  409.         ULONG   size, count = 0, pct;
  410.  
  411.         D (("Print(): enter\n"));
  412.         if (!(temp = malloc (256))) {
  413.                 t_printf ("Can't malloc 256 bytes to print");
  414.                 D (("Print(): get mem failure, exit\n"));
  415.                 return;
  416.         }
  417.         ptr = ArticleName (currentArticle);
  418.  
  419.         if ((fd = open (ptr, O_READ)) < 0) {
  420.                 t_printf (mainWindow, "Can't open '%s'", ptr);
  421.                 free (temp);
  422.                 D (("Print(): open input failure, exit\n"));
  423.                 return;
  424.         }
  425.         size = lseek (fd, 0, 2);
  426.         lseek (fd, 0, 0);
  427.         if ((outfd = open ("prt:", O_WRONLY)) < 0) {
  428.                 t_printf (mainWindow, "Can't open printer");
  429.                 free (temp);
  430.                 close (fd);
  431.                 D (("Print(): open output failure, exit\n"));
  432.                 return;
  433.         }
  434.         while ((len = read (fd, temp, 256)) > 0) {
  435.                 count += len;
  436.                 pct = 100 * count / size;
  437.                 // FIXME - check for output errors
  438.                 write (outfd, temp, len);
  439.                 if (GuageRequest (pct, "GRn - Please Wait...",
  440.                                         "      Printing      ", "Abort")) break;
  441.         }
  442.         GuageRequest (1000, "GRn - Please Wait...",
  443.                             "      Printing      ", "Abort");
  444.         close (fd);
  445.         // output formfeed
  446.         *temp = 12;
  447.         write (outfd, temp, 1);
  448.         close (outfd);
  449.         free (temp);
  450.         D (("Print(): exit\n"));
  451.         return;
  452. }
  453.  
  454. #ifndef SLOW
  455. // in our unending quest for more speed on a puny 68000 processor....
  456. // I wish DICE supported __inline
  457. #define Store_Text(xbuf, xlen)                                          \
  458.         {                                                               \
  459.                 xbuf [xlen] = '\0';                                     \
  460.                 AddTail (&msgList, AllocNODE (xbuf, xlen));             \
  461.                 messageLines++;                                         \
  462.         }
  463. #else
  464. void Store_Text (char *buf, int len)
  465. {
  466.         NODE *np;
  467.  
  468.         buf [len] = '\0';
  469.         np = AllocNODE (buf, len);
  470.         AddTail (&msgList, np);
  471.         messageLines++;
  472.  
  473.         return;
  474. }
  475. #endif
  476.  
  477. int OutputFile (int fd, int doing_headers)
  478. {
  479. #define output(x)       { Store_Text (x, outlen); pout = outbuf; outlen = 0; }
  480.  
  481.         static char temp [512], outbuf [512];
  482.         register char *pout;
  483.         register char *p;
  484.         register int len;
  485.         register int outlen;
  486.         register int last_was_newline;
  487.  
  488.         // on entry, we assume that the file is positioned appropriately
  489.  
  490.         D (("OutputFile(): enter\n"));
  491.  
  492.         pout = outbuf;
  493.         outlen = 0;
  494.         last_was_newline = 0;
  495.         while ((len = read (fd, temp, 512)) > 0) {
  496.                 p = temp;
  497.  
  498.                 while (len > 0) {
  499.                         len--;
  500.                         if (*p == '\t') {
  501.                                 p++;
  502.                                 // a tab should consume a minimum of one space
  503.                                 do {
  504.                                         *pout++ = ' ';
  505.                                         outlen++;
  506.                                 } while ((outlen < wrapCol) && (outlen % 8));
  507.                                 last_was_newline = 0;
  508.                                 continue;
  509.                         }
  510.                         if (*p == '\n') {
  511.                                 if (last_was_newline && doing_headers)
  512.                                         goto done;
  513.                                 last_was_newline = 1;
  514.                                 p++;
  515.                                 if (outlen == 0) {
  516.                                         *pout = ' ';
  517.                                         outlen++;
  518.                                 }
  519.                                 output (outbuf);
  520.                                 continue;
  521.                         }
  522.                         last_was_newline = 0;
  523.                         *pout++ = *p++;
  524.                         outlen++;
  525.                         if ((outlen >= wrapCol) && (*p != '\n')) {
  526.                                 *pout = '\\';
  527.                                 outlen++;
  528.                                 output (outbuf);
  529.                         }
  530.                 }
  531.         }
  532.         if (outlen)
  533.                 output (outbuf);
  534. done:
  535.         D (("OutputFile(): exit\n"));
  536.  
  537.         return 0;
  538. #undef output
  539. }
  540.  
  541. void    ShowMessage (ART *ap)
  542. {
  543.         NODE            *np;
  544.         GLIST           *gp;
  545.         BOOL            flag = !0;
  546.         int             fd;
  547.         static char     artStatus [256];
  548.         static char     blank [2];
  549.  
  550.         D (("ShowMessage(): enter\n"));
  551.  
  552.         blank [0] = ' ';
  553.         messageLines = 0;
  554.         if (currentArticle == ap)
  555.                 flag = 0;
  556.         currentArticle = ap;
  557.         GT_SetGadgetAttrs (gadgetArray [MSGLIST_ID], mainWindow, NULL,
  558.                            GTLV_Labels, ~0, TAG_DONE);
  559.         FreeListNode (&msgList);
  560.         NewList (&msgList);
  561.  
  562.         Organization = NULL;
  563.         Date = NULL;
  564.         X_NewsSoftware = NULL;
  565.  
  566.         fd = ScanAndLoadHeaders (ArticleName (ap), HEADER_ORGANIZATION | HEADER_X_NEWSSOFTWARE | HEADER_DATE, OPT_DONT_CLOSE);
  567.         if (fd < 0) {
  568.                 t_printf (mainWindow, "Can't open %s", ArticleName (ap));
  569.                 D (("ShowMessage(): open input failure, exit\n"));
  570.                 return;
  571.         }
  572.  
  573.         if (currentGroup->hideHeaders) {
  574.                 if (X_NewsSoftware) {
  575.                         // display X-NewsSoftware header only
  576.                         X_NewsSoftware -= 16; // strlen ("X-NewsSoftware: ")
  577.                         Store_Text (X_NewsSoftware, strlen (X_NewsSoftware));
  578.                         X_NewsSoftware += 16;
  579.                         Store_Text (blank, 1);
  580.                 }
  581.         }
  582.         else {
  583.                 // Display ALL header lines
  584.                 lseek (fd, 0, 0);
  585.                 OutputFile (fd, 1);
  586.                 Store_Text (blank, 1);
  587.         }
  588.         // point file to immediately after the last header
  589.         if (header_len <= 0) {
  590.                 t_printf (mainWindow, "Internal error ScanHeaders()");
  591.                 close (fd);
  592.                 D (("ShowMessage(): ScanHeaders() error, exit\n"));
  593.                 return;
  594.         }
  595.         lseek (fd, header_len + 1, 0);
  596.  
  597.         // Display the file.
  598.         OutputFile (fd, 0);
  599.  
  600.         close (fd);
  601.  
  602.         if (ap->state == UNREAD) {
  603.                 treeDirty = !0;
  604.                 if (currentGroup->unread)
  605.                         currentGroup->unread--;
  606.         }
  607.         ap->state = READ;
  608.         sprintf (ap->node.ln_Name, "%s %6d %-32.32s %.64s",
  609.                  ap->state?"  READ ":"UNREAD ",
  610.                  ap->filenum,
  611.                  ap->from,
  612.                  ap->subject
  613.         );
  614.         GT_SetGadgetAttrs (gadgetArray [MSGLIST_ID], mainWindow, NULL,
  615.                            GTLV_Labels, &msgList, TAG_DONE);
  616.         if (flag) {
  617.                 GT_SetGadgetAttrs (gadgetArray [MSGLIST_ID], mainWindow, NULL,
  618.                                    GTLV_Top, 0, TAG_DONE);
  619.                 viewTop = 0;
  620.         }
  621.         gp = NextGroup ();
  622.         sprintf (artStatus, " *** Article %6d (%d remaining) Next Group is %s",
  623.                  ap->filenum,
  624.                  currentGroup->unread,
  625.                  currentGroup != gp ? gp->groupName : "No More"
  626.         );
  627.  
  628.         GT_SetGadgetAttrs (gadgetArray [STATUS_ID], mainWindow, NULL,
  629.                            GTTX_Text, artStatus, TAG_DONE);
  630.  
  631.         GT_SetGadgetAttrs (gadgetArray [FROM_ID], mainWindow, NULL,
  632.                            GTTX_Text, ap->from, TAG_DONE);
  633.  
  634.         GT_SetGadgetAttrs (gadgetArray [ORGANIZATION_ID], mainWindow, NULL,
  635.                            GTTX_Text, Organization, TAG_DONE);
  636.  
  637.         GT_SetGadgetAttrs (gadgetArray [DATE_ID], mainWindow, NULL,
  638.                            GTTX_Text, Date, TAG_DONE);
  639.  
  640.         GT_SetGadgetAttrs (gadgetArray [SUBJECT_ID], mainWindow, NULL,
  641.                            GTTX_Text, ap->subject, TAG_DONE);
  642.  
  643.         GT_SetGadgetAttrs (gadgetArray [ARTLIST_ID], mainWindow, NULL,
  644.                            GTLV_Top,  ap->index, TAG_DONE);
  645.  
  646.         GT_RefreshWindow (mainWindow, NULL);
  647.  
  648.         D (("ShowMessage(): exit\n"));
  649.         return;
  650. }
  651.  
  652. void    NextArticle (void)
  653. {
  654.         D (("NextArticle(): enter\n"));
  655.  
  656.         if (currentGroup->artList.lh_TailPred == (NODE *) currentArticle)
  657.                 return;
  658.         ShowMessage ((ART *) currentArticle->node.ln_Succ);
  659.  
  660.         D (("NextArticle(): exit\n"));
  661.         return;
  662. }
  663.  
  664. void    PrevArticle (void)
  665. {
  666.         D (("PrevArticle(): enter\n"));
  667.  
  668.         if (currentGroup->artList.lh_Head == (NODE *) currentArticle)
  669.                 return;
  670.         ShowMessage ((ART *) currentArticle->node.ln_Pred);
  671.  
  672.         D (("PrevArticle(): exit\n"));
  673.         return;
  674. }
  675.  
  676. void    Post (BOOL followupFlag)
  677. {
  678.         char    temp [512], work [512];
  679.         char    *ptr;               // temporary
  680.         char    *p;                 // temporary
  681.         char    *pout;              // output pointer to work
  682.         char    *fname;             // name of the temporary output file
  683.         int     fd;                 // the input file (currentArticle)
  684.         int     outfd;              // the article we are building
  685.         int     len;                // input read length
  686.         int     outlen;             // output write length
  687.  
  688.         D (("Post(): enter\n"));
  689.  
  690.         Newsgroups = From = Message_ID = References = Subject =
  691.                 Distribution = Followup_To = NULL;
  692.         fd = -1;
  693.  
  694.         if (followupFlag && !currentArticle) {
  695.                 t_printf (mainWindow, "I shouldn't be here");
  696.                 D (("Post(): why am I here, exit\n"));
  697.                 return;
  698.         }
  699.  
  700.         if (currentArticle) {
  701.                 ptr = ArticleName (currentArticle);
  702.                 fd = ScanAndLoadHeaders (ptr,
  703.                                 (HEADER_NEWSGROUPS |
  704.                                  HEADER_FROM |
  705.                                  HEADER_MESSAGE_ID |
  706.                                  HEADER_REFERENCES |
  707.                                  HEADER_SUBJECT |
  708.                                  HEADER_FOLLOWUP_TO |
  709.                                  HEADER_DISTRIBUTION),
  710.                                 OPT_DONT_CLOSE);
  711.  
  712.                 if (fd < 0) {
  713.                         t_printf (mainWindow, "Can't open %s for input", ptr);
  714.                         D (("Post(): open input failure, exit\n"));
  715.                         return;
  716.                 }
  717.         }
  718.  
  719.         ptr = TempName ();
  720.         fname = malloc (strlen (ptr) + 1);
  721.         if (!fname) {
  722.                 t_printf (mainWindow, "Can't get memory for filename");
  723.                 if (fd >= 0)
  724.                         close (fd);
  725.  
  726.                 D (("Post(): get mem failure, exit\n"));
  727.                 return;
  728.         }
  729.         strcpy (fname, ptr);
  730.  
  731.         if ((outfd = creat (ptr, 0660)) < 0) {
  732.                 t_printf (mainWindow, "Can't open %s for output", ptr);
  733.                 free (fname);
  734.                 if (fd >= 0)
  735.                         close (fd);
  736.  
  737.                 D (("Post(): open output failure, exit\n"));
  738.                 return;
  739.         }
  740.  
  741.         if (followupFlag)
  742.                 if (Followup_To && *Followup_To) {
  743.                         // FIXME: do Followup-To: poster logic
  744.                         p = Followup_To;
  745.                 }
  746.                 else
  747.                         if (Newsgroups && *Newsgroups)
  748.                                 p = Newsgroups;
  749.                         else
  750.                                 p = currentGroup->groupName;
  751.         else
  752.                 p = currentGroup->groupName;
  753.  
  754.         OutputStrings (outfd, "Newsgroups: ", p, "\n", NULL);
  755.  
  756.         OutputStrings (outfd, "Subject: ", NULL);
  757.         if (followupFlag) {
  758.                 if (strncmp (Subject, "Re:", 3) != 0)
  759.                         OutputStrings (outfd, "Re: ", NULL);
  760.  
  761.                 OutputStrings (outfd, Subject, NULL);
  762.         }
  763.         OutputStrings (outfd, "\n", NULL);
  764.  
  765.         OutputStrings (outfd, "Distribution: ", NULL);
  766.         if (Distribution)
  767.                 OutputStrings (outfd, Distribution, NULL);
  768.         else
  769.                 OutputStrings (outfd, "world", NULL);
  770.  
  771.         OutputStrings (outfd, "\n\n", NULL);
  772.  
  773.         if (followupFlag) {
  774.                 OutputStrings (outfd, "In article ",
  775.                                 Message_ID ? Message_ID : "???",
  776.                                 " ",
  777.                                 From ? From : "???",
  778.                                 " writes:\n",
  779.                                 NULL);
  780.  
  781.                 // point file to immediately after the last header
  782.                 if (header_len <= 0) {
  783.                         t_printf (mainWindow, "Internal error from ScanHeaders()");
  784.                         close (fd);
  785.                         remove (fname);
  786.                         free (fname);
  787.  
  788.                         D (("Post(): ScanHeaders() error, exit\n"));
  789.                         return;
  790.                 }
  791.                 lseek (fd, header_len + 1, 0);
  792.  
  793.                 // This is the same algorithm as from OutputFile(), we just
  794.                 // don't worry about wrapCol (instead we use 500 chars as a
  795.                 // limit). And we don't do tab expansion.
  796.  
  797.                 // This is EXACTLY the same algorithm as in Mail().
  798.  
  799.                 // FIXME: The prefix "> " should be an option.
  800.  
  801. #define output(x) { *pout = '\0'; OutputStrings (outfd, "> ", work, "\n", NULL); outlen = 0; pout = work; *pout = '\0'; }
  802.  
  803.                 pout = work;
  804.                 *pout = '\0';
  805.                 outlen = 0;
  806.                 while ((len = read (fd, temp, 512)) > 0) {
  807.                         p = temp;
  808.  
  809.                         while (len > 0) {
  810.                                 len--;
  811.                                 // tab expansion was here....
  812.                                 if (*p == '\n') {
  813.                                         p++;
  814.                                         output (work);
  815.                                         continue;
  816.                                 }
  817.                                 *pout++ = *p++;
  818.                                 outlen++;
  819.                                 if ((outlen >= 500) && (*p != '\n')) {
  820.                                         *pout++ = '\\';
  821.                                         outlen++;
  822.                                         output (work);
  823.                                 }
  824.                         }
  825.                 }
  826.                 if (outlen)
  827.                         output (work);
  828. #undef output
  829.         }
  830.  
  831.         if (fd >= 0)
  832.                 close (fd);
  833.         OutputStrings (outfd, "\n-- \n", NULL);
  834.  
  835.         AppendNewsGroupSignature (outfd);
  836.         close (outfd);
  837.  
  838.         PostNews (followupFlag, fname);
  839.         free (fname);
  840.  
  841.         D (("Post(): exit\n"));
  842.         return;
  843. }
  844.  
  845. void    Mail (BOOL replyFlag)
  846. {
  847.         char    temp [512], work [512];
  848.         char    *ptr;
  849.         char    *p;
  850.         char    *fname;
  851.         char    *pout;
  852.         int     len;
  853.         int     outlen;
  854.         int     fd;
  855.         int     outfd;
  856.         int     rslt;
  857.         int     rfd;
  858.         int     i;
  859.  
  860.         D (("Mail(): enter\n"));
  861.  
  862.         Path = From = Reply_To = Message_ID = Subject = NULL;
  863.         fd = -1;
  864.  
  865.         if (replyFlag && !currentArticle) {
  866.                 t_printf (mainWindow, "I shouldn't be here....");
  867.  
  868.                 D (("Mail(): exit, why am I here?\n"));
  869.                 return;
  870.         }
  871.         if (currentArticle) {
  872.                 fd = ScanAndLoadHeaders (p = ArticleName (currentArticle),
  873.                                  (HEADER_FROM |
  874.                                   HEADER_REPLY_TO |
  875.                                   HEADER_PATH |
  876.                                   HEADER_MESSAGE_ID |
  877.                                   HEADER_SUBJECT),
  878.                                  OPT_DONT_CLOSE);
  879.                 if (fd < 0) {
  880.                         t_printf (mainWindow, "Can't open article %s", p);
  881.  
  882.                         D (("Mail(): open input error\n"));
  883.                         return;
  884.                 }
  885.         }
  886.  
  887.         fname = TempName ();
  888.         if ((outfd = creat (fname, 0660)) < 0) {
  889.                 t_printf (mainWindow, "Can't open %s for output", fname);
  890.                 if (fd >= 0)
  891.                         close (fd);
  892.  
  893.                 D (("Mail(): open output error, exit\n"));
  894.                 return;
  895.         }
  896.         OutputStrings (outfd, "To: ", NULL);
  897.         if (replyFlag == MAIL_REPLY)
  898.                 if (Reply_To)
  899.                         OutputStrings (outfd, Reply_To, NULL);
  900.                 else
  901.                         if (From)
  902.                                 OutputStrings (outfd, From, NULL);
  903.                         else
  904.                                 OutputStrings (outfd, Path, NULL);
  905.         OutputStrings (outfd, "\n", NULL);
  906.  
  907.         OutputStrings (outfd, "Subject: ", NULL);
  908.         if (replyFlag && Subject) {
  909.                 if (strncmp (Subject, "Re: ", 4) != 0)
  910.                         OutputStrings (outfd, "Re: ", NULL);
  911.  
  912.                 OutputStrings (outfd, Subject, NULL);
  913.  
  914.                 if (replyFlag == MAIL_FORWARD) {
  915.                         if (p = strrchr (Subject, '(')) {
  916.                                 if (strncmp (p, "(fwd)", 5) != 0)
  917.                                         OutputStrings (outfd, " (fwd)", NULL);
  918.                         }
  919.                         else
  920.                                 OutputStrings (outfd, " (fwd)", NULL);
  921.                 }
  922.         }
  923.         OutputStrings (outfd, "\n", NULL);
  924.  
  925.         if (Message_ID)
  926.                 OutputStrings (outfd, "References: ", Message_ID, "\n", NULL);
  927.  
  928.         OutputStrings (outfd, "X-NewsSoftware: ", GRN_VERSION, "\n", NULL);
  929.  
  930.         ApplyMailHeaders (outfd);  // also writes the "empty line"
  931.  
  932.         if (replyFlag == MAIL_REPLY) {
  933.                 i = TwoGadgetRequest ("GRn - Reply to Article",
  934.                                       "Include original text in Reply?",
  935.                                       "_Yes", "_No");
  936.         }
  937.         else
  938.                 i = 0;
  939.  
  940.         if ((i && replyFlag == MAIL_REPLY) || replyFlag == MAIL_FORWARD) {
  941.                 if (replyFlag == MAIL_FORWARD)
  942.                         OutputStrings (outfd, "Originally From: ", From, NULL);
  943.                 else {
  944.                         OutputStrings (outfd, "In article ",
  945.                                 Message_ID ? Message_ID : "???",
  946.                                 " ",
  947.                                 NULL);
  948.  
  949.                         if (From && *From)
  950.                                 ptr = From;
  951.                         else
  952.                                 ptr = "some unknown individual";
  953.  
  954.                         OutputStrings (outfd, ptr, " writes:\n", NULL);
  955.                 }
  956.  
  957.                 // point file to immediately after the last header
  958.                 if (header_len <= 0) {
  959.                         t_printf (mainWindow, "Internal error from ScanHeaders()");
  960.                         close (fd);
  961.                         close (outfd);
  962.                         remove (fname);
  963.  
  964.                         D (("Mail(): ScanHeaders() error, exit\n"));
  965.                         return;
  966.                 }
  967.                 lseek (fd, header_len + 1, 0);
  968.  
  969.                 // This is the same algorithm as from OutputFile(), we just
  970.                 // don't worry about wrapCol (instead we use 500 chars as a
  971.                 // limit). And we don't do tab expansion.
  972.  
  973.                 // This is EXACTLY the same algorithm as in Post().
  974.  
  975.                 // FIXME: The prefix "> " should be an option.
  976.  
  977. #define output(x)   {                                                       \
  978.                     *pout = '\0';                                           \
  979.                     if (replyFlag == MAIL_REPLY)                            \
  980.                             OutputStrings (outfd, "> ", work, "\n", NULL);  \
  981.                     else                                                    \
  982.                             if (strncmp (work, "From ", 5) == 0)            \
  983.                                     OutputStrings (outfd, ">", work, "\n", NULL);   \
  984.                             else                                            \
  985.                                     OutputStrings (outfd, work, "\n", NULL);        \
  986.                     outlen = 0;                                             \
  987.                     pout = work;                                            \
  988.                     *pout = '\0';                                           \
  989.                     }
  990.  
  991.                 pout = work;
  992.                 *pout = '\0';
  993.                 outlen = 0;
  994.                 while ((len = read (fd, temp, 512)) > 0) {
  995.                         p = temp;
  996.  
  997.                         while (len > 0) {
  998.                                 len--;
  999.                                 if (*p == '\n') {
  1000.                                         p++;
  1001.                                         output (work);
  1002.                                         continue;
  1003.                                 }
  1004.                                 *pout++ = *p++;
  1005.                                 outlen++;
  1006.                                 if ((outlen >= 500) && (*p != '\n')) {
  1007.                                         *pout++ = '\\';
  1008.                                         outlen++;
  1009.                                         output (work);
  1010.                                 }
  1011.                         }
  1012.                 }
  1013.                 if (outlen)
  1014.                         output (work);
  1015. #undef output
  1016.                 if (replyFlag == MAIL_FORWARD)
  1017.                         OutputStrings (outfd, "---------- End of Forwarded Article ----------\n", NULL);
  1018.  
  1019.         }
  1020.  
  1021.         if (fd >= 0)
  1022.                 close (fd);
  1023.         OutputStrings (outfd, "\n-- \n", NULL);
  1024.  
  1025.         AppendSignature (outfd);
  1026.         close (outfd);
  1027.  
  1028.         SendMail (fname);
  1029.  
  1030.         D (("Mail(): exit\n"));
  1031.         return;
  1032. }
  1033.  
  1034. /************************************************************************/
  1035.  
  1036. BOOL    ParseThread (char *filename, char *refs, char *msgid)
  1037. {
  1038. #if 1
  1039.         int fd;
  1040.         register char *ps;
  1041.  
  1042.         References = Message_ID = NULL;
  1043.         fd = ScanAndLoadHeaders (filename, HEADER_REFERENCES | HEADER_MESSAGE_ID, 0);
  1044.         if (fd < 0)
  1045.                 return 0;
  1046.  
  1047.         // FIXME - this only gets the first line of the References header
  1048.         if (refs) {
  1049.                 *refs = '\0';
  1050.                 if (References) {
  1051.                         ps = References;
  1052.                         while (*ps == ' ')
  1053.                                 ps++;
  1054.                         strcpy (refs, ps);
  1055.                 }
  1056.         }
  1057.  
  1058.         if (msgid) {
  1059.                 *msgid = '\0';
  1060.                 if (Message_ID) {
  1061.                         ps = Message_ID;
  1062.                         while (*ps == ' ')
  1063.                                 ps++;
  1064.                         strcpy (msgid, ps);
  1065.                 }
  1066.         }
  1067. #else
  1068.         char    temp[512], *ps;
  1069.         FILE    *fp;
  1070.  
  1071.         fp = fopen(filename, "r");
  1072.         if (!fp) return 0;
  1073.         if (refs) *refs = 0;
  1074.         if (msgid) *msgid = 0;
  1075.         while (fgets(temp, 512, fp)) {
  1076.                 temp[strlen(temp)-1] = '\0';
  1077.                 if (!strlen(temp)) break;
  1078.                 // FIXME - this only gets the first line of the References:
  1079.                 // header. The header MAY span multiple lines per RFC.
  1080.                 if (!strnicmp(temp, "References:", 11)) {
  1081.                         ps = &temp[11];
  1082.                         while (*ps == ' ') ps++;
  1083.                         strcpy(refs, ps);
  1084.                 }
  1085.                 else if (!strnicmp(temp, "Message-ID:", 11)) {
  1086.                         ps = &temp[11];
  1087.                         while (*ps == ' ') ps++;
  1088.                         strcpy(msgid, ps);
  1089.                 }
  1090.         }
  1091.         fclose(fp);
  1092. #endif
  1093.         return 1;
  1094. }
  1095.  
  1096. char    *ArticleName (ART *ap)
  1097. {
  1098.         static char temp1 [256];
  1099.  
  1100.         D (("ArticleName(): enter\n"));
  1101.         strcpy (temp1, uunews);
  1102.         AddPart (temp1, NewsGroupToFileName (currentGroup->groupName), 256);
  1103.         AddPart (temp1, itoa (ap->filenum), 256);
  1104.  
  1105.         D (("ArticleName(): exit\n"));
  1106.         return temp1;
  1107. }
  1108.  
  1109. BOOL    RefCmp (char *ref1, char *ref2)
  1110. {
  1111.         static char    r1 [256], r2 [256];
  1112.         register char  *pd;
  1113.         register char  *ps;
  1114.  
  1115.         while (*ref1) {
  1116.                 // get a reference from ref1 into r1
  1117.                 pd = r1;
  1118.                 while (*ref1 && *ref1 != '<')
  1119.                         ref1++;
  1120.                 if (*ref1 == '\0')
  1121.                         return 0;
  1122.                 while (*ref1 && *ref1 != '>')
  1123.                         *pd++ = *ref1++;
  1124.                 if (*ref1 == '>')
  1125.                         *pd++ = *ref1++;
  1126.                 *pd = '\0';
  1127.                 // now compare against all refs in ref2
  1128.                 ps = ref2;
  1129.                 while (*ref2) {
  1130.                         // get a reference from ref2 into r2
  1131.                         pd = r2;
  1132.                         while (*ps && *ps != '<')
  1133.                                 ps++;
  1134.                         if (*ps == '\0')
  1135.                                 return 0;
  1136.                         while (*ps && *ps != '>')
  1137.                                 *pd++ = *ps++;
  1138.                         if (*ps == '>')
  1139.                                 *pd++ = *ps++;
  1140.                         *pd = '\0';
  1141.                         if (strcmp (r1, r2) == 0)  // was stricmp
  1142.                                 return !0;
  1143.                 }
  1144.         }
  1145.         return 0;
  1146. }
  1147.  
  1148. BOOL    NextThread (void)
  1149. {
  1150.         ART     *ap;
  1151.         static char    myrefs[512], myid[256], refs[512], msgid[256], subj[256];
  1152.         register char *ps;
  1153.  
  1154.         ps = currentArticle->subject;
  1155.         while (*ps == ' ')
  1156.                 ps++;
  1157.         if (strncmp(ps, "Re:", 3) == 0)     // was strnicmp
  1158.                 ps += 3;
  1159.         while (*ps == ' ')
  1160.                 ps++;
  1161.         strcpy (subj, ps);
  1162.         if (!ParseThread (ArticleName (currentArticle), myrefs, myid))
  1163.                 return 0;
  1164.         ap = currentArticle;
  1165.         do {
  1166.                 if (currentGroup->artList.lh_TailPred == (NODE *) ap)
  1167.                         ap = (ART *) currentGroup->artList.lh_Head;
  1168.                 else
  1169.                         ap = (ART *) ap->node.ln_Succ;
  1170.                 if (ap->state == UNREAD) {
  1171.                         if (!ParseThread (ArticleName (ap), refs, msgid))
  1172.                                 continue;
  1173.                         ps = ap->subject;
  1174.                         while (*ps == ' ')
  1175.                                 ps++;
  1176.                         if (strncmp(ps, "Re:", 3) == 0)     // was strnicmp
  1177.                                 ps += 3;
  1178.                         while (*ps == ' ')
  1179.                                 ps++;
  1180.                         if (RefCmp (myid, refs) || RefCmp (myrefs, refs) ||
  1181.                             RefCmp (msgid, myrefs) || strcmp (subj, ps) == 0) {
  1182.                                                // was stricmp
  1183.                                 ShowMessage (ap);
  1184.                                 return !0;
  1185.                         }
  1186.                 }
  1187.         } while (ap != currentArticle);
  1188.         return 0;
  1189. }
  1190.  
  1191. /************************************************************************/
  1192.  
  1193. /*
  1194.  * void ProcessGadget(id, code);
  1195.  * UWORD        id;             gadgetID of gadget to process
  1196.  * UWORD        code;           IntuiMessage Code
  1197.  *
  1198.  * Synopsis:
  1199.  *      Process IDCMP message for GadTools gadget.  Simply prints an appropriate
  1200.  *      string into the event listview, unless it is palette related (handles
  1201.  *      palette appropriately).
  1202.  */
  1203. static void     ProcessGadget (UWORD id, UWORD code)
  1204. {
  1205. /*      GADGET  *gad = gadgetArray[id]; */
  1206.         ART     *ap;
  1207.         GLIST   *start_gp;
  1208.         GLIST   *gp;
  1209.  
  1210.         D (("ProcessGadget(): enter, Article Mode\n"));
  1211.  
  1212.         idcmp_shifted = 0;  // if non-zero, someone is screwing with us....
  1213.                             // they've hit shift, then clicked on a mouse,
  1214.                             // without hitting any characters....
  1215.  
  1216. #if DISABLE_GADGETS
  1217.         DisableWindow();
  1218. #endif
  1219.         switch (id) {
  1220.                 case HIDEHEADERS_ID:
  1221.                         currentGroup->hideHeaders = !currentGroup->hideHeaders;
  1222.                         treeDirty = 1;
  1223.                         GT_SetGadgetAttrs (gadgetArray [HIDEHEADERS_ID], mainWindow, NULL,
  1224.                                 GTCB_Checked, currentGroup->hideHeaders ? TRUE : FALSE,
  1225.                                 TAG_DONE
  1226.                         );
  1227.                         ShowMessage (currentArticle);
  1228.                         break;
  1229.                 case HIDEREAD_ID:
  1230.                         currentGroup->hideRead = !currentGroup->hideRead;
  1231.                         treeDirty = 1;
  1232.                         GT_SetGadgetAttrs (gadgetArray [HIDEREAD_ID], mainWindow, NULL,
  1233.                                 GTCB_Checked, currentGroup->hideRead ? TRUE : FALSE,
  1234.                                 TAG_DONE
  1235.                         );
  1236.                         break;
  1237.                 case NEXTGROUP_ID:
  1238.                         gp = NextGroup ();
  1239.                         if (gp != currentGroup) {
  1240.                                 // this circuitous code finds the next non-empty
  1241.                                 // group if one exists, and just goes to the
  1242.                                 // next group if one doesn't.
  1243.                                 start_gp = currentGroup;
  1244.                                 while (gp && gp != start_gp && gp->unread == 0) {
  1245.                                         gp = NextGroup ();
  1246.                                         currentGroup = gp;
  1247.                                 }
  1248.                                 if (!gp)
  1249.                                         gp = start_gp;
  1250.                                 if (gp->unread == 0 && gp == start_gp) {
  1251.                                         currentGroup = gp;
  1252.                                         gp = NextGroup ();
  1253.                                 }
  1254.                                 mode = NEXTGROUPS_MODE;
  1255.                                 currentGroup = gp;
  1256.                         }
  1257.                         return;  // note: no EnableWindow()!!!
  1258.                 case PREVGROUP_ID:
  1259.                         gp = PrevGroup ();
  1260.                         if (gp != currentGroup) {
  1261.                                 mode = PREVGROUPS_MODE;
  1262.                                 currentGroup = gp;
  1263.                         }
  1264.                         return;  // note: no EnableWindow()!!!
  1265.                 case QUIT_ID:
  1266.                         mode = GROUPS_MODE;
  1267.                         return;  // note: no EnableWindow()!!!
  1268.                 case SORT_ID:
  1269.                         currentGroup->sortActive = code;
  1270.                         treeDirty = !0;
  1271.                         GT_SetGadgetAttrs (gadgetArray [ARTLIST_ID], mainWindow, NULL,
  1272.                                            GTLV_Labels, ~0, TAG_DONE);
  1273.                         SortArticles ();
  1274.                         GT_SetGadgetAttrs (gadgetArray [ARTLIST_ID], mainWindow, NULL,
  1275.                                            GTLV_Labels, ¤tGroup->artList, TAG_DONE);
  1276.                         ShowMessage (currentArticle);
  1277.                         break;
  1278.                 case ARTLIST_ID:
  1279.                         ap = (ART *) FindListItem (¤tGroup->artList, code);
  1280.                         if (!ap)
  1281.                                 printf ("Error: Can't FindListItem(%d)\n", code);
  1282.                         else {
  1283.                                 ShowMessage (ap);
  1284.                         }
  1285.                         break;
  1286.                 case PREV_ID:
  1287.                         PrevArticle ();
  1288.                         break;
  1289.                 case NEXT_ID:
  1290.                         NextArticle ();
  1291.                         break;
  1292.                 case NEXTUNREAD_ID:
  1293.                         ap = currentArticle;
  1294.                         do {
  1295.                                 if (currentGroup->artList.lh_TailPred == (NODE *)ap)
  1296.                                         ap = (ART *)currentGroup->artList.lh_Head;
  1297.                                 else
  1298.                                         ap = (ART *)ap->node.ln_Succ;
  1299.                                 if (ap->state == UNREAD) {
  1300.                                         ShowMessage (ap);
  1301. #if DISABLE_GADGETS
  1302.                                         EnableWindow ();
  1303. #endif
  1304.                                         return;
  1305.                                 }
  1306.                         } while (ap != currentArticle);
  1307.                         break;
  1308.                 case NEXTTHREAD_ID:
  1309.                         if (!NextThread ()) {
  1310.                                 ap = currentArticle;
  1311.                                 do {
  1312.                                         if (currentGroup->artList.lh_TailPred == (NODE *)ap)
  1313.                                                 ap = (ART *)currentGroup->artList.lh_Head;
  1314.                                         else
  1315.                                                 ap = (ART *)ap->node.ln_Succ;
  1316.                                         if (ap->state == UNREAD) {
  1317.                                                 ShowMessage (ap);
  1318. #if DISABLE_GADGETS
  1319.                                                 EnableWindow ();
  1320. #endif
  1321.                                                 return;
  1322.                                         }
  1323.                                 } while (ap != currentArticle);
  1324.                         }
  1325.                         break;
  1326.                 case CATCHUP_ID:
  1327.                         Catchup ();
  1328.                         mode = NEXTGROUPS_MODE;
  1329.                         return;  // note: no EnableWindow()!!!
  1330.                 case REPLY_ID:
  1331.                         Mail (MAIL_REPLY);
  1332.                         break;
  1333.                 case FOLLOWUP_ID:
  1334.                         Post (TRUE);
  1335.                         break;
  1336.                 case POST_ID:
  1337.                         Post (FALSE);
  1338.                         break;
  1339.                 case FORWARD_ID:
  1340.                         Mail (MAIL_FORWARD);
  1341.                         break;
  1342.                 case PRINT_ID:
  1343.                         Print ();
  1344.                         break;
  1345.                 case SAVE_ID:
  1346.                         Save ();
  1347.                         break;
  1348.                 default:
  1349.                         t_printf (mainWindow, "ProcessGadget - id = %d", id); break;
  1350.         }
  1351. #if DISABLE_GADGETS
  1352.         EnableWindow ();
  1353. #endif
  1354.  
  1355.         D (("ProcessGadget(): exit, Article Mode\n"));
  1356.  
  1357.         return;
  1358. }
  1359.  
  1360. static void     IDCMPFunc (IMSG *m)
  1361. {
  1362.         GLIST   *gp;
  1363.         GLIST   *start_gp;
  1364.         ART     *ap;
  1365.  
  1366.         D (("IDCMPFunc(): enter, Article Mode, m->Class = 0x%x, m->Code = 0x%x\n", m->Class, m->Code));
  1367.  
  1368. #if DISABLE_GADGETS
  1369.         DisableWindow();
  1370. #endif
  1371.         switch (m->Class) {
  1372.                 default:
  1373.                         idcmp_shifted = 0;
  1374.                         break;
  1375.  
  1376.                 case IDCMP_CLOSEWINDOW:
  1377.                         idcmp_shifted = 0;
  1378.                         mode = ABORT_MODE;
  1379.                         D (("IDCMPFunc(): exit 1\n"));
  1380.                         return;  // note: no EnableWindow()!!!
  1381.  
  1382.                 case IDCMP_VANILLAKEY:
  1383.                         idcmp_shifted = 0;
  1384.                         switch (m->Code) {
  1385.  
  1386.                                 case 0x0d:
  1387.                                         // ENTER (numeric keypad)
  1388.                                         // RETURN (main keypad)
  1389.                                         viewTop++;
  1390.                                         if (viewTop > (messageLines - viewHeight))
  1391.                                                 viewTop = messageLines -
  1392.                                                           viewHeight;
  1393.                                         if (viewTop < 0)
  1394.                                                 viewTop = 0;
  1395.                                         GT_SetGadgetAttrs (gadgetArray [MSGLIST_ID], mainWindow, NULL,
  1396.                                                 GTLV_Top, viewTop, TAG_DONE);
  1397.                                         break;
  1398.  
  1399.                                 case 'F':
  1400.                                         Post (TRUE);
  1401.                                         break;
  1402.  
  1403.                                 case 'R':
  1404.                                         Mail (MAIL_REPLY);
  1405.                                         break;
  1406.  
  1407.                                 case 'n':
  1408.                                         NextArticle ();
  1409.                                         break;
  1410.  
  1411.                                 case 'b':
  1412.                                 case 'p':
  1413.                                         PrevArticle ();
  1414.                                         break;
  1415.  
  1416.                                 case 'C':
  1417.                                         Catchup ();
  1418.                                         mode = NEXTGROUPS_MODE;
  1419.                                         D (("IDCMPFunc(): exit 2\n"));
  1420.                                         return;  // note: no EnableWindow()!!!
  1421.  
  1422.                                 case 'N':
  1423.                                         gp = NextGroup ();
  1424.                                         if (gp == currentGroup)
  1425.                                                 break;
  1426.                                         start_gp = currentGroup;
  1427.                                         while (gp && gp != start_gp && gp->unread == 0) {
  1428.                                                 gp = NextGroup ();
  1429.                                                 currentGroup = gp;
  1430.                                         }
  1431.                                         if (!gp)
  1432.                                                 gp = start_gp;
  1433.                                         if (gp->unread == 0 && gp == start_gp) {
  1434.                                                 currentGroup = start_gp;
  1435.                                                 gp = NextGroup ();
  1436.                                         }
  1437.                                         mode = NEXTGROUPS_MODE;
  1438.                                         currentGroup = gp;
  1439.                                         D (("IDCMPFunc(): exit 3\n"));
  1440.                                         return;  // note: no EnableWindow()!!!
  1441.  
  1442.                                 case 'P':
  1443.                                         gp = PrevGroup ();
  1444.                                         if (gp == currentGroup)
  1445.                                                 break;
  1446.                                         mode = PREVGROUPS_MODE;
  1447.                                         currentGroup = gp;
  1448.                                         D (("IDCMPFunc(): exit 4\n"));
  1449.                                         return;  // note: no EnableWindow()!!!
  1450.  
  1451.                                 case ' ':
  1452.                                         viewTop += viewHeight - 1;
  1453.                                         if (viewTop > messageLines-viewHeight)
  1454.                                                 viewTop = messageLines - viewHeight;
  1455.                                         if (viewTop < 0)
  1456.                                                 viewTop = 0;
  1457.                                         GT_SetGadgetAttrs (gadgetArray [MSGLIST_ID], mainWindow, NULL,
  1458.                                                 GTLV_Top, viewTop, TAG_DONE);
  1459.                                         break;
  1460.  
  1461.                                 case 'G':
  1462.                                         mode = GROUPS_MODE;
  1463.                                         D (("IDCMPFunc(): exit 5\n"));
  1464.                                         return;     // note: no EnableWindow()!!!
  1465.  
  1466.                                 case 'S':
  1467.                                         Save ();
  1468.                                         break;
  1469.  
  1470.                                 case 't':
  1471.                                         if (!NextThread ()) {
  1472.                                                 ap = currentArticle;
  1473.                                                 do {
  1474.                                                         if (currentGroup->artList.lh_TailPred == (NODE *)ap)
  1475.                                                                 ap = (ART *)currentGroup->artList.lh_Head;
  1476.                                                         else
  1477.                                                                 ap = (ART *)ap->node.ln_Succ;
  1478.                                                         if (ap->state == UNREAD) {
  1479.                                                                 ShowMessage (ap);
  1480. #if DISABLE_GADGETS
  1481.                                                                 EnableWindow ();
  1482. #endif
  1483.                                                                 return;
  1484.                                                         }
  1485.                                                 } while (ap != currentArticle);
  1486.                                         }
  1487.                                         break;
  1488.  
  1489.                                 case 'u':
  1490.                                         ap = currentArticle;
  1491.                                         do {
  1492.                                                 if (currentGroup->artList.lh_TailPred == (NODE *)ap)
  1493.                                                         ap = (ART *)currentGroup->artList.lh_Head;
  1494.                                                 else
  1495.                                                         ap = (ART *)ap->node.ln_Succ;
  1496.                                                 if (ap->state == UNREAD) {
  1497.                                                         ShowMessage (ap);
  1498. #if DISABLE_GADGETS
  1499.                                                         EnableWindow();
  1500. #endif
  1501.                                                         return;
  1502.                                                 }
  1503.                                         } while (ap != currentArticle);
  1504.                                         break;
  1505.                         }
  1506.                         break;
  1507.  
  1508.                 case IDCMP_RAWKEY:
  1509.                         switch (m->Code) {
  1510.                                 case 0x4c:  // up arrow key
  1511.                                         if (idcmp_shifted)
  1512.                                                 viewTop = 0;
  1513.                                         else {
  1514.                                                 viewTop--;
  1515.                                                 if (viewTop < 0)
  1516.                                                         viewTop = 0;
  1517.                                         }
  1518.                                         GT_SetGadgetAttrs (gadgetArray [MSGLIST_ID], mainWindow, NULL,
  1519.                                                 GTLV_Top, viewTop, TAG_DONE);
  1520.                                         idcmp_shifted = 0;
  1521.                                         break;
  1522. #if 0
  1523.                                 // These never make it here. They are "cooked"
  1524.                                 // and returned as vanilla-keys (newline)
  1525.                                 case 0x43:  // enter key (numeric keypad)
  1526.                                 case 0x44:  // return key (main keypad)
  1527.                                         idcmp_shifted = 0;
  1528.                                         // fall-through on purpose
  1529. #endif
  1530.                                 case 0x4d:  // down arrow key
  1531.                                         if (idcmp_shifted)
  1532.                                                 viewTop = messageLines -
  1533.                                                           viewHeight;
  1534.                                         else {
  1535.                                                 viewTop++;
  1536.                                                 if (viewTop > (messageLines - viewHeight))
  1537.                                                         viewTop = messageLines -
  1538.                                                                   viewHeight;
  1539.                                                 if (viewTop < 0)
  1540.                                                         viewTop = 0;
  1541.                                         }
  1542.                                         GT_SetGadgetAttrs (gadgetArray [MSGLIST_ID], mainWindow, NULL,
  1543.                                                 GTLV_Top, viewTop, TAG_DONE);
  1544.                                         idcmp_shifted = 0;
  1545.                                         break;
  1546.                                 case 0x60:
  1547.                                 case 0x61:
  1548.                                         D (("IDCMPFunc(): Shift in\n"));
  1549.                                         idcmp_shifted = 1;
  1550.                                         break;
  1551. #if 0
  1552.                                 // These never happen. They get "cooked".
  1553.                                 case 0xe0:
  1554.                                 case 0xe1:
  1555.                                         // Tthis will never happen, since we set
  1556.                                         // RAWKEY | VANILLAKEY. See the Includes
  1557.                                         // RKM (2.0), page 347, right after the
  1558.                                         // discussion on IDCMP_VANILLAKEY.
  1559.                                         D (("IDCMPFunc(): Shift out\n"));
  1560.                                         idcmp_shifted = 0;
  1561.                                         break;
  1562. #endif
  1563.                                 default:
  1564.                                         idcmp_shifted = 0;
  1565.                                         D (("IDCMPFunc(): m->Code = 0x%x\n", m->Code & 0xff));
  1566.                         }
  1567.                         break;
  1568.  
  1569.         }
  1570. #if DISABLE_GADGETS
  1571.         EnableWindow();
  1572. #endif
  1573.  
  1574.         D (("IDCMPFunc(): exit, Article Mode\n"));
  1575.         return;
  1576. }
  1577.  
  1578. void    ArticlesWindow (void)
  1579. {
  1580.         ART     *ap;
  1581.         UWORD   n = 0;
  1582.  
  1583.         D (("ArticlesWindow(): enter\n"));
  1584.  
  1585.         if (!currentGroup || currentGroup->articles == 0) {
  1586.                 mode = GROUPS_MODE;
  1587.  
  1588.                 D (("ArticlesWindow(): no group, exit\n"));
  1589.                 return;
  1590.         }
  1591.  
  1592. #if DISABLE_GADGETS
  1593.         DisableWindow ();
  1594. #endif
  1595.         NewList (&msgList);
  1596.         LayoutGadgets (defaultFont, prefWidth, windowHeight, screenTop);
  1597.         gList = CreateGadgets (gadgetArray, gadDefs);
  1598.         AddGList (mainWindow, gList, -1, -1, 0);
  1599.         RefreshGList (gList, mainWindow, 0, -1);
  1600.  
  1601.         while (1) {
  1602.                 // stay in here while we get Next Group and/or Prev Group click
  1603.  
  1604.                 t_printf (mainWindow, "GRn - %s", currentGroup->groupName);
  1605.  
  1606.                 // FIX-ME: do we need to do this every time we enter a group?
  1607.                 // ANSWER -- this is a net win if the user only enters some newsgroups,
  1608.                 // and/or only enters a newsgroup a single time. If he goes back, he
  1609.                 // pays a price. Don't change right now.
  1610.  
  1611.                 GT_SetGadgetAttrs (gadgetArray [ARTLIST_ID], mainWindow, NULL,
  1612.                                 GTLV_Labels, ~0, TAG_DONE);
  1613.  
  1614.                 GT_SetGadgetAttrs (gadgetArray [MSGLIST_ID], mainWindow, NULL,
  1615.                                 GTLV_Labels, ~0, TAG_DONE);
  1616.  
  1617.                 GT_RefreshWindow (mainWindow, NULL);
  1618.  
  1619.                 for (ap = (ART *) currentGroup->artList.lh_Head;
  1620.                      ap->node.ln_Succ;
  1621.                      ap = (ART *) ap->node.ln_Succ) {
  1622.  
  1623.                         sprintf (ap->node.ln_Name, "%s %6d %-32.32s %s",
  1624.                                  ap->state?"  READ ":"UNREAD ",
  1625.                                  ap->filenum,
  1626.                                  ap->from,
  1627.                                  ap->subject
  1628.                         );
  1629.                 }
  1630.                 // FIX-ME -- sort EVERY time? why?
  1631.                 // ANSWER: see the above.
  1632.                 SortArticles ();
  1633.  
  1634.                 GT_SetGadgetAttrs (gadgetArray [ARTLIST_ID], mainWindow, NULL,
  1635.                                 GTLV_Labels, ¤tGroup->artList, TAG_DONE);
  1636.                 GT_SetGadgetAttrs (gadgetArray [SORT_ID], mainWindow, NULL,
  1637.                                 GTCY_Active, currentGroup->sortActive, TAG_DONE);
  1638.  
  1639.                 if (currentGroup->unread == 0) {        // show last READ message
  1640. #ifdef MICHAEL_REMOVED_THIS
  1641.                         // this is only need if the section MYKE_REMOVED_THIS below
  1642.                         // is re-enabled
  1643.                         for (ap=(ART *)currentGroup->artList.lh_Head;
  1644.                              ap->node.ln_Succ;
  1645.                              ap = (ART *)ap->node.ln_Succ) {
  1646.  
  1647.                                 n++;
  1648.                         }
  1649. #endif
  1650.                         ShowMessage ((ART *) currentGroup->artList.lh_TailPred);
  1651.                 }
  1652.                 else {  // show first unread message
  1653.                         for (ap=(ART *)currentGroup->artList.lh_Head;
  1654.                              ap->node.ln_Succ;
  1655.                              ap = (ART *)ap->node.ln_Succ) {
  1656.  
  1657.                                 if (ap->state == UNREAD)
  1658.                                         break;
  1659. #ifdef MICHAEL_REMOVED_THIS
  1660.                                 n++;
  1661. #endif
  1662.                         }
  1663.                         ShowMessage (ap);
  1664.                 }
  1665. #ifdef MYKE_REMOVED_THIS
  1666.                 GT_SetGadgetAttrs (gadgetArray [ARTLIST_ID], mainWindow, NULL,
  1667.                         GTLV_Selected, n,
  1668.                         GTLV_Top, n,
  1669.                         TAG_DONE
  1670.                 );
  1671. #endif
  1672.  
  1673.                 GT_SetGadgetAttrs (gadgetArray [HIDEHEADERS_ID], mainWindow, NULL,
  1674.                         GTCB_Checked, currentGroup->hideHeaders ? TRUE : FALSE,
  1675.                         TAG_DONE
  1676.                 );
  1677.                 GT_SetGadgetAttrs (gadgetArray [HIDEREAD_ID], mainWindow, NULL,
  1678.                         GTCB_Checked, currentGroup->hideRead ? TRUE : FALSE,
  1679.                         TAG_DONE
  1680.                 );
  1681. #if DISABLE_GADGETS
  1682.                 EnableWindow();
  1683. #endif
  1684.                 while (mode == ARTICLES_MODE) {
  1685.                         WaitPort (mainWindow->UserPort);
  1686.                         EventHandler (mainWindow, ProcessGadget, IDCMPFunc, NULL);
  1687.                 }
  1688.  
  1689.                 FreeListNode (&msgList);
  1690.  
  1691.                 if ((mode != NEXTGROUPS_MODE) && (mode != PREVGROUPS_MODE)) {
  1692.                         CloseArticles ();
  1693. #if DISABLE_GADGETS
  1694.                         EnableWindow ();
  1695. #endif
  1696.                         D (("ArticlesWindow(): new mode, exit\n"));
  1697.                         return;
  1698.                 }
  1699.                 // so, we did a next group or a prev group. Do it again.
  1700.                 mode = ARTICLES_MODE;
  1701.                 NewList (&msgList);
  1702.                 // loop...
  1703.         } // ... while (1) ...
  1704.         /* NOTREACHED */
  1705.         D (("ArticlesWindow(): exit, how the hell did we get here?\n"));
  1706.         return;
  1707. }
  1708.