home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume27 / mthreads / part01 / mt-write.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-20  |  9.5 KB  |  395 lines

  1. /* $Id: mt-write.c,v 3.0 1990/10/01 00:14:05 davison Trn $
  2. */
  3. /* The authors make no claims as to the fitness or correctness of this software
  4.  * for any use whatsoever, and it is provided as is. Any use of this software
  5.  * is at the user's own risk. 
  6.  */
  7.  
  8. #include "EXTERN.h"
  9. #include "common.h"
  10. #include "thread.h"
  11. #include "mthreads.h"
  12.  
  13. static FILE *fp_out;
  14. static int seq;
  15. static int article_seq;
  16.  
  17. static int failure;
  18.  
  19. void write_subjects(), write_authors(), write_roots(), write_ids();
  20. void write_articles(), write_thread(), write_item();
  21. void enumerate_articles(), enumerate_thread();
  22. void free_leftovers();
  23. int ensure_path _((char *));
  24.  
  25. /* Write out all the data in a packed format that is easy for our newsreader
  26. ** to use.  We free things as we go, when we don't need them any longer.  If
  27. ** we encounter any write errors, the write_item routine sets a failure flag
  28. ** to halt our writing of the file, but we keep on plugging away to free
  29. ** everything up.
  30. */
  31. int
  32. write_data(filename)
  33. char *filename;
  34. {
  35.     if (filename == Nullch) {
  36.     failure = 2;    /* A NULL filename indicates just free the data */
  37.     } else if ((fp_out = fopen(filename, FOPEN_WB)) == Nullfp) {
  38.     if (ensure_path(filename)) {
  39.         if ((fp_out = fopen(filename, FOPEN_WB)) == Nullfp) {
  40.         log_error("Unable to create file: `%s'.\n", filename);
  41.         failure = 2;
  42.         }
  43.     } else {
  44.         log_error("Unable to create path: `%s'.\n", filename);
  45.         failure = 2;
  46.     }
  47.     } else {
  48.     failure = 0;
  49. #ifdef SETBUFFER
  50.     setbuffer(fp_out, rwbuf, RWBUFSIZ);
  51. #else
  52. # ifdef SETVBUF
  53.     setvbuf(fp_out, rwbuf, _IOFBF, RWBUFSIZ);
  54. # endif
  55. #endif
  56.     }
  57.  
  58.     /* If there's no roots, there's no data.  Leave the file with no length. */
  59.     if (!failure && total.last < total.first) {
  60.     failure = -1;
  61.     }
  62.  
  63.     write_item(&total, sizeof (TOTAL));
  64.  
  65.     enumerate_articles();
  66.  
  67.     write_authors();
  68.     write_subjects();
  69.     write_roots();
  70.     write_articles();
  71.     write_ids();
  72.     free_leftovers();
  73.     bzero(&total, sizeof (TOTAL));
  74.  
  75.     if (failure != 2) {
  76.     fclose(fp_out);
  77.     }
  78.     if (failure == 1) {
  79.     log_error("Write failed!  Removing `%s'.\n", filename);
  80.     unlink(filename);
  81.     }
  82.     return failure <= 0;
  83. }
  84.  
  85. /* Recursively descend the article tree, enumerating the articles as we go.
  86. ** This way we can output the article sequence numbers into the data file.
  87. */
  88. void
  89. enumerate_articles()
  90. {
  91.     register ROOT *root;
  92.  
  93.     seq = article_seq = 0;
  94.  
  95.     for (root = root_root; root; root = root->link) {
  96.     root->seq = seq++;
  97.     if (!root->articles) {
  98.         log_error("** No articles on this root??\n");
  99.         continue;
  100.     }
  101.     enumerate_thread(root->articles);
  102.     }
  103.     if (seq != total.root) {
  104.     log_error("** Wrote %d roots instead of %d **\n", seq, total.root);
  105.     }
  106.     if (article_seq != total.article) {
  107.     log_error("** Wrote %d articles instead of %d **\n", article_seq, total.article);
  108.     }
  109. }
  110.  
  111. /* Recursive routine for above-mentioned enumeration. */
  112. void
  113. enumerate_thread(article)
  114. ARTICLE *article;
  115. {
  116.     while (article) {
  117.     article->seq = article_seq++;
  118.     if (article->children) {
  119.         enumerate_thread(article->children);
  120.     }
  121.     article = article->siblings;
  122.     }
  123. }
  124.  
  125. #define write_and_free(str_ptr)    /* Comment for makedepend to     \
  126.                     ** ignore the backslash above */ \
  127. {\
  128.     register int len = strlen(str_ptr) + 1;\
  129.     write_item(str_ptr, len);\
  130.     free(str_ptr);\
  131.     string_offset += len;\
  132. }
  133.  
  134. MEM_SIZE string_offset;
  135.  
  136. /* Write out the author information:  first the use-counts, then the
  137. ** name strings all packed together.
  138. */
  139. void
  140. write_authors()
  141. {
  142.     register AUTHOR *author;
  143.  
  144.     seq = 0;
  145.     for (author = author_root; author; author = author->link) {
  146.     write_item(&author->count, sizeof (WORD));
  147.     author->seq = seq++;
  148.     }
  149.     if (seq != total.author) {
  150.     log_error("** Wrote %d authors instead of %d **\n",
  151.         seq, total.author);
  152.     }
  153.  
  154.     string_offset = 0;
  155.  
  156.     for (author = author_root; author; author = author->link) {
  157.     write_and_free(author->name);
  158.     }
  159. }
  160.  
  161. /* Write out the subject information: first the packed string data, then
  162. ** the use-counts.  The order is important -- it is the order required
  163. ** by the roots for their subject structures.
  164. */
  165. void
  166. write_subjects()
  167. {
  168.     register ROOT *root;
  169.     register SUBJECT *subject;
  170.  
  171.     for (root = root_root; root; root = root->link) {
  172.     for (subject = root->subjects; subject; subject = subject->link) {
  173.         write_and_free(subject->str);
  174.     }
  175.     }
  176.     if (string_offset != total.string1) {
  177.     log_error("** Author/subject strings were %ld bytes instead of %ld **\n",
  178.         string_offset, total.string1);
  179.     }
  180.  
  181.     seq = 0;
  182.     for (root = root_root; root; root = root->link) {
  183.     for (subject = root->subjects; subject; subject = subject->link) {
  184.         write_item(&subject->count, sizeof (WORD));
  185.         subject->seq = seq++;
  186.     }
  187.     }
  188.     if (seq != total.subject) {
  189.     log_error("** Wrote %d subjects instead of %d **\n",
  190.         seq, total.subject);
  191.     }
  192. }
  193.  
  194. /* Write the roots in a packed format.  Interpret the pointers into
  195. ** sequence numbers as we go.
  196. */
  197. void
  198. write_roots()
  199. {
  200.     register ROOT *root;
  201.  
  202.     for (root = root_root; root; root = root->link) {
  203.     p_root.articles = root->articles->seq;
  204.     p_root.root_num = root->root_num;
  205.     p_root.thread_cnt = root->thread_cnt;
  206.     p_root.subject_cnt = root->subject_cnt;
  207.     write_item(&p_root, sizeof (PACKED_ROOT));
  208.     }
  209. }
  210.  
  211. #define rel_article(article, rseq)    ((article)? (article)->seq - (rseq) : 0)
  212. #define valid_seq(ptr)        ((ptr)? (ptr)->seq : -1)
  213.  
  214. /* Write all the articles in the same order that we sequenced them. */
  215. void
  216. write_articles()
  217. {
  218.     register ROOT *root;
  219.  
  220.     for (root = root_root; root; root = root->link) {
  221.     write_thread(root->articles);
  222.     }
  223. }
  224.  
  225. /* Recursive routine to write the articles in thread order.  We depend on
  226. ** the fact that our first child is the very next article written (if we
  227. ** have children).
  228. */
  229. void
  230. write_thread(article)
  231. register ARTICLE *article;
  232. {
  233.     while (article) {
  234.     p_article.num = article->num;
  235.     p_article.date = article->date;
  236.     p_article.subject = valid_seq(article->subject);
  237.     p_article.author = valid_seq(article->author);
  238.     p_article.flags = article->flags;
  239.     p_article.child_cnt = article->child_cnt;
  240.     p_article.parent = rel_article(article->parent, article->seq);
  241.     p_article.siblings = rel_article(article->siblings, article->seq);
  242.     p_article.root = article->root->seq;
  243.     write_item(&p_article, sizeof (PACKED_ARTICLE));
  244.     if (article->children) {
  245.         write_thread(article->children);
  246.     }
  247.     article = article->siblings;
  248.     }
  249. }
  250.  
  251. WORD minus_one = -1;
  252.  
  253. /* Write the message-id strings:  each domain name (not including the
  254. ** ".unknown." domain) followed by all of its associated unique ids.
  255. ** Then output the article sequence numbers they belong to.  This stuff
  256. ** is last because the newsreader doesn't need to read it.
  257. */
  258. void
  259. write_ids()
  260. {
  261.     register DOMAIN *domain;
  262.     register ARTICLE *id;
  263.     register DOMAIN *next_domain;
  264.     register ARTICLE *next_id;
  265.  
  266.     string_offset = 0;
  267.  
  268.     for (domain = &unk_domain; domain; domain = domain->link) {
  269.     if (domain != &unk_domain) {
  270.         write_and_free(domain->name);
  271.         if (!domain->ids) {
  272.         log_error("** Empty domain name!! **\n");
  273.         }
  274.     }
  275.     for (id = domain->ids; id; id = id->id_link) {
  276.         write_and_free(id->id);
  277.     }
  278.     }
  279.     if (string_offset != total.string2) {
  280.     log_error("** Message-id strings were %ld bytes (%ld) **\n",
  281.         string_offset, total.string2);
  282.     }
  283.     for (domain = &unk_domain; domain; domain = next_domain) {
  284.     next_domain = domain->link;
  285.     for (id = domain->ids; id; id = next_id) {
  286.         next_id = id->id_link;
  287.         write_item(&id->seq, sizeof (WORD));
  288.         free(id);
  289.     }
  290.     write_item(&minus_one, sizeof (WORD));
  291.     if (domain != &unk_domain) {
  292.         free(domain);
  293.     }
  294.     }
  295.     unk_domain.ids = Nullart;
  296.     unk_domain.link = Null(DOMAIN*);
  297. }
  298.  
  299. /* Free everything that's left to free.
  300. */
  301. void
  302. free_leftovers()
  303. {
  304.     register ROOT *root, *next_root;
  305.     register SUBJECT *subj, *next_subj;
  306.     register AUTHOR *author, *next_author;
  307.  
  308.     for (root = root_root; root; root = next_root) {
  309.     next_root = root->link;
  310.     for (subj = root->subjects; subj; subj = next_subj) {
  311.         next_subj = subj->link;
  312.         free(subj);
  313.     }
  314.     free(root);
  315.     }
  316.     for (author = author_root; author; author = next_author) {
  317.     next_author = author->link;
  318.     free(author);
  319.     }
  320.     root_root = Null(ROOT*);
  321.     author_root = Null(AUTHOR*);
  322. }
  323.  
  324. /* This routine will check to be sure that the required path exists for
  325. ** the data file, and if not it will attempt to create it.
  326. */
  327. int
  328. ensure_path(filename)
  329. register char *filename;
  330. {
  331.     int status, pid, w;
  332.     char tmpbuf[1024];
  333. #ifdef MAKEDIR
  334.     register char *cp, *last;
  335.     register char *tbptr = tmpbuf+5;
  336.  
  337.     if (!(last = rindex(filename, '/'))) {    /* find filename portion */
  338.     return 1;                /* no path, we're fine */
  339.     }
  340.     *last = '\0';                /* truncate path at filename */
  341.     strcpy(tmpbuf, "mkdir");
  342.  
  343.     for (cp = last;;) {
  344.     if (stat(filename, &filestat) >= 0 && (filestat.st_mode & S_IFDIR)) {
  345.         *cp = '/';
  346.         break;
  347.     }
  348.     if (!(cp = rindex(filename, '/'))) {/* find something that exists */
  349.         break;
  350.     }
  351.     *cp = '\0';
  352.     }
  353.     
  354.     for (cp = filename; cp <= last; cp++) {
  355.     if (!*cp) {
  356.         sprintf(tbptr, " %s", filename);
  357.         tbptr += strlen(tbptr);        /* set up for mkdir call */
  358.         *cp = '/';
  359.     }
  360.     }
  361.     if (tbptr == tmpbuf+5) {
  362.     return 1;
  363.     }
  364. #else
  365.     sprintf(tmpbuf,"%s %s %d", file_exp(DIRMAKER), filename, 1);
  366. #endif
  367.  
  368.     if ((pid = vfork()) == 0) {
  369.     execl(SH, SH, "-c", tmpbuf, Nullch);
  370.     _exit(127);
  371.     }
  372.     while ((w = wait(&status)) != pid && w != -1) {
  373.     ;
  374.     }
  375.     if (w == -1) {
  376.     status = -1;
  377.     }
  378.     return !status;
  379. }
  380.  
  381. /* A simple routine to output some data only if we haven't failed any
  382. ** previous writes.
  383. */
  384. void
  385. write_item(buff, len)
  386. char *buff;
  387. int len;
  388. {
  389.     if (!failure) {
  390.     if (fwrite(buff, 1, len, fp_out) < len) {
  391.         failure = 1;
  392.     }
  393.     }
  394. }
  395.