home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume23 / trn / part07 / mt-write.c next >
Encoding:
C/C++ Source or Header  |  1991-08-22  |  9.3 KB  |  377 lines

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