home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / util / editor / dig12.cpt / Application / Browser.c next >
Encoding:
C/C++ Source or Header  |  1992-03-10  |  10.2 KB  |  399 lines

  1. /*
  2.  * Browser.c
  3.  *
  4.  * 9/5/91
  5.  * Manuel A. Perez
  6.  *
  7.  * 9/5/91 Added a simple mac front end to the skim-digest program
  8.  * from sumex.
  9.  *
  10.  * 9/6/91 Liked it so much, that I decided this was worth making
  11.  * a TCL application out of it.
  12.  *
  13.  * 2/29/92 Reorganized the parser.  More error checks.  It now
  14.  * detects Date, From, and Subject.  All three of them are kept
  15.  * in the directory structure.  Also, this file can be tested
  16.  * without the full application.
  17.  *
  18.  * 3/9/92 Fixed problem with BuildHeadMessage.
  19.  * Based on: skim-digest.c (stored in info-mac)
  20.  * version 1.0, 28 Feb 91, by Mike Gleason, NCEMRSoft.
  21.  *
  22.  */
  23.  
  24.  
  25. #include <stdio.h>
  26. #include <string.h>                // str-stuff
  27. #include "Browser.h"
  28. #include <Exceptions.h>
  29.  
  30. // Constants
  31. #define KEY "From:"
  32. #define KEYLEN 5
  33.  
  34. // Prototypes
  35. int select_index_line(void);
  36. int line_type(char *line);
  37. void copystr(register char *to, register char *from);
  38. Boolean find_first_header(FILE *fp);
  39. Boolean find_next_header(FILE *fp, fpos_t *last_blank_line);
  40. void get_article_info(BrowserItemPtr p);
  41. Boolean BuildHeadMessage(BrowserDir *dir);
  42.  
  43. // Uncomment the next line to test this program without TCL.
  44. //#define MAIN
  45.  
  46. #ifdef MAIN
  47. #define REPORT_ERROR(MSG)        fprintf(stderr, MSG)
  48. #define REPORT_F_NOT_OPENED(FNAME)         fprintf(stderr, "skim-digest: \"%s\" could not be opened.\n", FNAME)
  49. #define REPORT_EOF() fprintf(stderr, "skim-digest: Unexpected EOF\n")
  50. #define REPORT_MEMORY_ALOC() fprintf(stderr, "skim-digest: error allocating memory\n")
  51. #define REPORT_IMPROPER_FMT()    fprintf(stderr, "skim-digest: improper file format\n")
  52. #else
  53.  
  54. #define REPORT_ERROR(MSG)            FailMemError()
  55. #define REPORT_F_NOT_OPENED(FNAME)    Failure(1000, SpecifyMsg(1025, 1))
  56. #define REPORT_EOF()                 Failure(1001, SpecifyMsg(1025, 2))
  57. #define REPORT_MEMORY_ALOC()         FailMemError()
  58. #define REPORT_IMPROPER_FMT()        Failure(1000, SpecifyMsg(1025, 3))
  59.  
  60. #endif
  61.  
  62. #ifdef MAIN
  63. main()
  64. {
  65. BrowserDir    dir;
  66. SFTypeList typeList;
  67. SFReply theReply;
  68. Point at = {40, 40};
  69. BrowserItemPtr list, p, q;
  70.  
  71.  
  72.     typeList[0] = 'TEXT';
  73.     SFGetFile(at, "\pGet File", NULL, 1, typeList, NULL, &theReply);
  74.     while (theReply.good) {
  75.         PtoCstr(theReply.fName);
  76.         strcpy(dir.fname, (char *)&theReply.fName);
  77.         CtoPstr(theReply.fName);
  78.         dir.vRefNum = theReply.vRefNum;
  79.     
  80.         if (BuildBrowserIndex(&dir)) {
  81.  
  82.             p = dir.topItem;
  83.             while (p) {
  84.                 printf("Date: [%s]\n", p->date);
  85.                 printf("From: [%s]\n", p->from);
  86.                 printf("Subject: [%s]\n", p->subject);
  87.                 printf("\n\n");
  88.                 p = p->next;
  89.             }
  90.             //fclose(dir->fp);
  91.         }
  92.         SFGetFile(at, "\pGet File", NULL, 1, typeList, NULL, &theReply);
  93.     }
  94. }
  95. #endif
  96.  
  97. /*----------------------------------------------------------------------------*/
  98. Boolean equalstr(register char *s, register char *t, int n)
  99. {
  100. register int i;
  101.  
  102.     for (i = 0; i < n; i++, s++, t++)
  103.         if (*s != *t)
  104.             return false;
  105.         else if (*s == '\0' || *t == '\0')
  106.             return false;
  107.  
  108.     return true;
  109. }   /* equalstr */
  110.  
  111. /*----------------------------------------------------------------------------*/
  112. int select_index_line(void)
  113. {
  114.     return line_type("From:");
  115. }
  116.  
  117. /*----------------------------------------------------------------------------*/
  118. int line_type(char *line)
  119. {
  120. register int i = 1;
  121. Str255 string;
  122.  
  123.     do {
  124.         GetIndString(string, 132, i);
  125.         if (string[0] && equalstr((char *)&string[1], line, string[0]))
  126.             return i;
  127.     } while (string[0] != 0);
  128.     return 0;
  129. }
  130.  
  131. /*----------------------------------------------------------------------------*/
  132. void copystr(register char *to, register char *from)
  133. {
  134.  
  135.     if (!(to && from)) return;
  136.  
  137.     // advance string pointer until ':'
  138.     for (; *from && *from != ':'; from++)
  139.         ;/* nothing */
  140.  
  141.     // skip over white spaces
  142.     if (*from)
  143.         for (from++; *from && *from == ' '; from++)
  144.             ;/* nothing */
  145.  
  146.     strcpy(to, from);
  147.     if (*from)
  148.         to[strlen(to) - 1] = '\0';        // remove nl at end of line
  149. }
  150.  
  151. /*----------------------------------------------------------------------------*/
  152. /*
  153.  * The format of the file has:
  154.  *
  155.  * A message from the Info-mac administrator at the top,
  156.  * Followed by messages with the following format:
  157.  *
  158.  *    <blank line>
  159.  *    Date:
  160.  *    From:
  161.  *    Subject:        << this line seems optional
  162.  *    <blank line>
  163.  *    <message>        << will include blank lines
  164.  *    <blank line>
  165.  *    ------            << dash line separates the messages
  166.  *
  167.  * But, we can only be garanteed that the 'From' appears at the
  168.  * beginning of the line only when it is part of the header.
  169.  *
  170.  * So to parse the header we will do the following:
  171.  *  1) Search for a line beginning with From, while
  172.  *       remembering the last blank line seen.
  173.  *    2) Once a From line is found, look ahead for the
  174.  *       next blank line.
  175.  *    3) The text between the first blank line, and the
  176.  *       second blank line contains the header for the
  177.  *       mail message.  Search this chunck of text to
  178.  *       get the Date, From, and Subject.
  179.  *
  180.  */
  181.  
  182. // Look from the beginning of the file and determine if the
  183. // first few lines contains what looks like a header for this
  184. // type of file. (Looking at the previous comments, the first
  185. // header should not have the leading blank lines).
  186.  
  187. Boolean find_first_header(FILE *fp)
  188. {
  189. char buffer[200];
  190.  
  191.     fseek(fp, 0, SEEK_SET);        // set file position
  192.     while (fgets(buffer, sizeof(buffer), fp)) {
  193.  
  194.         if (equalstr(buffer, "\n", 1))    // found an empty line
  195.             return false;
  196.  
  197.         else if (equalstr(buffer, KEY, KEYLEN))
  198.             // found a From: before a blank line
  199.             return true;
  200.     }
  201.  
  202.     return false;
  203. }
  204.  
  205. // Look ahead on the file for the beginning of the next
  206. // message file.  Return the file position of where the
  207. // next message begins.
  208. Boolean find_next_header(FILE *fp, fpos_t *last_blank_line)
  209. {
  210. fpos_t next_message;
  211. char buffer[200];
  212. Boolean found_blank_line;
  213. Boolean empty_line;
  214. Boolean found_header;
  215.  
  216.     fgetpos(fp, &next_message);        // save file position
  217.     *last_blank_line = next_message;
  218.  
  219.     found_blank_line = false;
  220.     found_header = false;
  221.     while (fgets(buffer, sizeof(buffer), fp)) {
  222.  
  223.         empty_line = equalstr(buffer, "\n", 1);
  224.         if (!found_blank_line && empty_line) {    // found an empty line
  225.             *last_blank_line = next_message;
  226.             found_blank_line = true;
  227.         }
  228.  
  229.         else if (found_blank_line && equalstr(buffer, KEY, KEYLEN)) {
  230.             // found a From: after a blank line and before another
  231.             // blank line
  232.             found_header = true;
  233.             break;            // get out
  234.         }
  235.  
  236.         else if (found_blank_line && empty_line) {
  237.             *last_blank_line = next_message;
  238.             found_blank_line = true;
  239.         }
  240.  
  241.         fgetpos(fp, &next_message);
  242.     }
  243.  
  244.     return found_header;
  245. }
  246.  
  247. /*----------------------------------------------------------------------------*/
  248. void get_article_info(BrowserItemPtr p)
  249. {
  250. char  buffer[128];
  251. fpos_t start;
  252.  
  253.     fgetpos(p->fp, &start);
  254.  
  255.     p->from[0] = p->date[0] = p->subject[0] = '\0';
  256.  
  257.     // skip over blank lines
  258.     do {
  259.         fgets(buffer, sizeof(buffer), p->fp);
  260.     } while(equalstr(buffer, "\n", 1));
  261.  
  262.     // process lines until another blank line comes along
  263.     while (!equalstr(buffer, "\n", 1))  {
  264.         if (equalstr(buffer, "From:", 5))
  265.             copystr(p->from, buffer);                // Copy From:
  266.         else if (equalstr(buffer, "Date:", 5))
  267.             copystr(p->date, buffer);                // Copy Date:
  268.         else if (equalstr(buffer, "Subject:", 8))
  269.             copystr(p->subject, buffer);            // Copy Subject:
  270.         fgets(buffer, sizeof(buffer), p->fp);
  271.     }
  272.  
  273.     if (p->from[0] == '\0')        strcpy(p->from, "-");
  274.     if (p->date[0] == '\0')        strcpy(p->date, "-");
  275.     if (p->subject[0] == '\0')     strcpy(p->subject, "-");
  276. }
  277.  
  278. /*----------------------------------------------------------------------------*/
  279. Boolean BuildBrowserIndex(BrowserDir *dir)
  280. {
  281. char  buffer[128];
  282. short articleNum = 0;
  283. BrowserItemPtr p, q;
  284. fpos_t this_article;
  285. fpos_t next_article;
  286. short saveVol;
  287.  
  288.     GetVol(NULL, &saveVol);
  289.     SetVol(NULL, dir->vRefNum);
  290.     if (!(dir->fp = fopen(dir->fname, "r"))) {
  291.         SetVol(NULL, saveVol);
  292.         REPORT_F_NOT_OPENED(dir->fname);
  293.         return false;
  294.     }
  295.     SetVol(NULL, saveVol);
  296.  
  297.     dir->topItem = NULL;    // initialize head pointer
  298.  
  299.     if (!BuildHeadMessage(dir))    // if error, return
  300.         return false;
  301.  
  302.     q = p = dir->topItem;    // initialize temp pointers (q, p)
  303.     articleNum = 1;            // we already have one article
  304.  
  305.     // Find the beginning of the next article as an indicator
  306.     // of the end of this one. 'find_next_header' returns TRUE
  307.     // if it finds the beginning of the next header
  308.     this_article = dir->topItem->endAt;
  309.     fsetpos(dir->fp, &this_article);
  310.     while (find_next_header(dir->fp, &next_article)) {
  311.  
  312.         // found new one; store new information
  313.         if (q = Allocate(BrowserItem)) {
  314.  
  315.             // keep file pointer at every node, duplication of effort
  316.             q->fp = dir->fp;    // but worth it (see CDisplayText)
  317.  
  318.             // Set header to the beginning of this article, then
  319.             // get the header information, and find the end of it
  320.             fsetpos(dir->fp, &this_article);            // start
  321.             get_article_info(q);    // store Date, From and Subject
  322.             find_next_header(dir->fp, &next_article);
  323.  
  324.             q->startAt = this_article;
  325.             q->endAt = next_article;
  326.             q->next = NULL;
  327.  
  328.             // set info for previous node
  329.             if (p) {
  330.                 p->endAt = q->startAt;    // ends at beginning of current one
  331.                 p->next = q;            // set sequential link
  332.             }
  333.  
  334.             // copy pointer over
  335.             p = q;
  336.  
  337.             // increment article count
  338.             articleNum++;
  339.             this_article = next_article;
  340.             fsetpos(dir->fp, &next_article);
  341.         }
  342.  
  343.         else {
  344.             REPORT_ERROR("skim-digest: error allocating memory\n");
  345.             return false;
  346.         }
  347.     }
  348.  
  349.     // when done, set the end of the link
  350.     if (p) {
  351.         p->endAt = next_article;
  352.         p->next = NULL;
  353.     }
  354.     dir->numArticles = articleNum;
  355.  
  356.     return true;
  357. }  /* BuildBrowserIndex */
  358.  
  359. /*----------------------------------------------------------------------------*/
  360. Boolean BuildHeadMessage(BrowserDir *dir)
  361. {
  362. char  buffer[128];
  363. fpos_t start;
  364. fpos_t end;
  365. Boolean found_new_line = false;
  366.  
  367.     // Go through the digest until a line KEY follows a blank line
  368.     fseek(dir->fp, 0, SEEK_SET);    // set file position
  369.     fgetpos(dir->fp, &start);        // save file position
  370.     if (!find_first_header(dir->fp)) {
  371.         REPORT_IMPROPER_FMT();
  372.         return false;
  373.     }
  374.     else if (dir->topItem = Allocate(BrowserItem)) {    
  375.         dir->topItem->fp = dir->fp;    // keep file pointer at every node,
  376.             // duplication of effort, but worth it (see CDisplayText)
  377.  
  378.         fsetpos(dir->fp, &start);    // set file position
  379.         find_next_header(dir->fp, &end);    // get end of article
  380.  
  381.         fsetpos(dir->fp, &start);    // set file position
  382.         get_article_info(dir->topItem);        // get article info
  383.  
  384.         dir->topItem->startAt = start;
  385.         dir->topItem->endAt = end;
  386.         dir->topItem->next = NULL;
  387.         
  388.         // leave pointer at the end of the header
  389.         fsetpos(dir->fp, &end);
  390.         return true;
  391.     }
  392.     else {
  393.         REPORT_MEMORY_ALOC();
  394.         return false;
  395.     }
  396.  
  397. }  /* BuildHeadMessage */
  398.  
  399.