home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / trn_12.zip / src / mt-write.c < prev    next >
C/C++ Source or Header  |  1993-12-04  |  11KB  |  421 lines

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