1 /*
2 Copyright (C) 1994-2007  Frans Slothouber, Jacco van Weert, Petteri Kettunen,
3 Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
4 Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
5 
6 This file is part of ROBODoc
7 
8 ROBODoc is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 /****h* ROBODoc/Document
24  * FUNCTION
25  *   This module contains functions to manipulate the central data
26  *   structure (RB_Document) that contains information about the
27  *   source files, and documentation files, and headers.
28  *
29  *   The name is a bit confusing because it sort of implies that
30  *   it contains the documentation extracted from the sourcefiles.
31  *
32  *   For each run a RB_Document structure is created, it is filled
33  *   by the analyser and directory module and then used by the
34  *   generator module to create the documentation.
35  * MODIFICATION HISTORY
36  *   * ????-??-??   Frans Slothouber  V1.0
37  *   * 2003-02-03   Frans Slothouber  Refactoring
38  *   * 2003-10-30   David White       Removed unistd.h for Borland
39  *******
40  */
41 
42 #include <assert.h>
43 #include <stdlib.h>
44 #include <ctype.h>
45 
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <unistd.h>
49 
50 #include "robodoc.h"
51 #include "document.h"
52 #include "part.h"
53 #include "path.h"
54 #include "directory.h"
55 #include "headers.h"
56 #include "links.h"
57 #include "util.h"
58 #include <string.h>
59 #include "generator.h"
60 #include "file.h"
61 #include "globals.h"
62 
63 
64 #ifdef DMALLOC
65 #include <dmalloc.h>
66 #endif
67 
68 
69 /****f* Document/RB_Document_Add_Part
70  * FUNCTION
71  *   Add a new part to the document.
72  * SYNOPSIS
73  */
RB_Document_Add_Part(struct RB_Document * document,struct RB_Part * part)74 void RB_Document_Add_Part(
75     struct RB_Document *document,
76     struct RB_Part *part )
77 /*
78  * INPUTS
79  *   o document  -- the document the part is to be added to.
80  *   o part      -- the part to be added
81  * SOURCE
82  */
83 {
84     part->next = document->parts;
85     document->parts = part;
86 }
87 
88 /*****/
89 
90 
91 /* TODO Documentation */
RB_Free_RB_Document_Parts(struct RB_Document * document)92 void RB_Free_RB_Document_Parts(
93     struct RB_Document *document )
94 {
95     if ( document->parts )
96     {
97         struct RB_Part     *a_part = NULL;
98         struct RB_Part     *a_part2 = NULL;
99 
100         for ( a_part = document->parts; a_part; a_part = a_part2 )
101         {
102             a_part2 = a_part->next;
103             RB_Free_RB_Part( a_part );
104         }
105     }
106     document->parts = NULL;
107 }
108 
109 
110 /* TODO Documentation */
RB_Free_RB_Document(struct RB_Document * document)111 void RB_Free_RB_Document(
112     struct RB_Document *document )
113 {
114     RB_Free_RB_Document_Parts( document );
115     if ( document->headers )
116     {
117         unsigned long       i;
118 
119         for ( i = 0; i < document->no_headers; ++i )
120         {
121             RB_Free_Header( document->headers[i] );
122 
123         }
124         free( document->headers );
125     }
126     free( document );
127 }
128 
129 /****f* Document/RB_Document_Create_Parts
130  * FUNCTION
131  *   Create all the parts of a document based on the sourcefiles in
132  *   the source tree.  This creates a new RB_Part for each file in
133  *   the source tree.
134  * INPUTS
135  *    o document -- the document for which the parts are generated.
136  * SOURCE
137  */
138 
RB_Document_Create_Parts(struct RB_Document * document)139 void RB_Document_Create_Parts(
140     struct RB_Document *document )
141 {
142     struct RB_Filename *i_file = NULL;
143 
144     assert( document );
145     assert( document->srctree );
146 
147     for ( i_file = document->srctree->first; i_file; i_file = i_file->next )
148     {
149         struct RB_Part     *rbpart;
150 
151         rbpart = RB_Get_RB_Part(  );
152         RB_Part_Add_Source( rbpart, i_file );
153         RB_Document_Add_Part( document, rbpart );
154     }
155 }
156 
157 /*******/
158 
159 
160 /****f* Document/RB_Fill_Header_Filename
161  * FUNCTION
162  *   Fill the file_name attribute of all headers based either on the
163  *   part or the singledoc name.   The file_name tells in which file
164  *   the documentation for the header is to be stored.
165  * SYNOPSIS
166  */
RB_Fill_Header_Filename(struct RB_Document * document)167 void RB_Fill_Header_Filename(
168     struct RB_Document *document )
169 /*
170  * SOURCE
171  */
172 {
173     struct RB_Part     *i_part;
174 
175     RB_Say( "Computing file_name attribute for all headers.\n", SAY_DEBUG );
176     for ( i_part = document->parts; i_part; i_part = i_part->next )
177     {
178         struct RB_header   *i_header;
179 
180         for ( i_header = i_part->headers;
181               i_header; i_header = i_header->next )
182         {
183             if ( document->actions.do_singledoc )
184             {
185                 i_header->file_name = document->singledoc_name;
186             }
187             else if ( document->actions.do_multidoc )
188             {
189                 i_header->file_name = RB_Get_FullDocname( i_part->filename );
190             }
191             else if ( document->actions.do_singlefile )
192             {
193                 i_header->file_name = document->singledoc_name;
194             }
195             else
196             {
197                 assert( 0 );
198             }
199         }
200     }
201 }
202 
203 /******/
204 
205 
206 /****f* Document/RB_Document_Determine_DocFilePaths
207  * FUNCTION
208  *   Determine the path of each of the documentation files based on
209  *   the path of the source file and the documentation root path and
210  *   the source root path.
211  * SYNOPSIS
212  */
RB_Document_Determine_DocFilePaths(struct RB_Document * document)213 void RB_Document_Determine_DocFilePaths(
214     struct RB_Document *document )
215 /*
216  * EXAMPLE
217  *   srcpath = ./test/mysrc/sub1/sub2
218  *   srcroot = ./test/mysrc/
219  *   docroot = ./test/mydoc/
220  *     ==>
221  *   docpath = ./test/mydoc/sub1/sub2
222  * SOURCE
223  */
224 {
225     struct RB_Path     *path;
226     int                 docroot_length;
227     int                 srcroot_length;
228     int                 length;
229 
230     assert( document->srctree );
231     assert( document->srcroot );
232     assert( document->docroot );
233 
234     docroot_length = strlen( document->docroot->name );
235     srcroot_length = strlen( document->srcroot->name );
236 
237     for ( path = document->srctree->first_path; path; path = path->next )
238     {
239         char               *name;
240         char               *new_name;
241         char               *tail;
242 
243         name = path->name;
244         length = strlen( name );
245         assert( length >= srcroot_length );
246         tail = name + srcroot_length;
247         new_name = calloc( docroot_length +
248                            ( length - srcroot_length ) + 1, sizeof( char ) );
249         assert( new_name );
250         strcat( new_name, document->docroot->name );
251         if ( document->actions.do_no_subdirectories )
252         {
253             /* No subdirectory */
254         }
255         else
256         {
257             strcat( new_name, tail );
258         }
259         path->docname = new_name;
260     }
261 }
262 
263 /******/
264 
265 
266 /****f* Document/RB_Document_Create_DocFilePaths
267  * FUNCTION
268  *   This function creates the whole document directory
269  *   tree.  It tests if the directories exist and if they
270  *   do not the directory is created.
271  * SYNOPSIS
272  */
RB_Document_Create_DocFilePaths(struct RB_Document * document)273 void RB_Document_Create_DocFilePaths(
274     struct RB_Document *document )
275 /*
276  * INPUTS
277  *   o document -- the document for which the tree is created.
278  * SOURCE
279  */
280 {
281     struct RB_Path     *path;
282 
283     for ( path = document->srctree->first_path; path; path = path->next )
284     {
285         char               *pathname = NULL;
286         char               *c2 = NULL;
287 
288         RB_Say( "Trying to create directory %s\n", SAY_INFO, path->docname );
289         /* Don't want to modify the docname in the path
290            structure. So we make a copy that we later
291            destroy. */
292 
293         pathname = RB_StrDup( path->docname );
294         for ( c2 = pathname + 1;        /* We skip the leading '/' */
295               *c2; ++c2 )
296         {
297             if ( *c2 == '/' )
298             {
299                 struct stat         dirstat;
300 
301                 *c2 = '\0';     /* Replace the '/' with a '\0'. */
302                 /* We now have one of the paths leading up to the
303                    total path. Test if it exists. */
304                 if ( stat( pathname, &dirstat ) == 0 )
305                 {
306                     /* Path exists. */
307                 }
308                 else if ( ( strlen( pathname ) == 2 ) &&
309                           ( utf8_isalpha( pathname[0] ) ) &&
310                           ( pathname[1] == ':' ) )
311                 {
312                     /* Is is a drive indicator, ( A: B: C: etc )
313                      * stat fails on this, but we should not
314                      * create either, so we do nothing.
315                      */
316                 }
317                 else
318                 {
319                     int                 result;
320 
321 #if defined(__MINGW32__)
322                     result = mkdir( pathname );
323 #else
324                     result = mkdir( pathname, 0770 );
325 #endif
326                     if ( result == 0 )
327                     {
328                         /* Path was created. */
329                     }
330                     else
331                     {
332                         perror( NULL );
333                         RB_Panic( "Can't create directory %s\n", pathname );
334                     }
335                 }
336                 /* Put the '/' back in it's place. */
337                 *c2 = '/';
338             }
339         }
340         free( pathname );
341     }
342 }
343 
344 /*******/
345 
346 
347 /* TODO Documentation */
348 
349 /*x**f*
350  * FUNCTION
351  *   Compare two header types for sorting.
352  * RESULT
353  *   -1  h1 <  h2
354  *    0  h1 == h2
355  *    1  h1 >  h2
356  * SOURCE
357  */
358 
RB_CompareHeaders(void * h1,void * h2)359 int RB_CompareHeaders(
360     void *h1,
361     void *h2 )
362 {
363     struct RB_header   *header_1 = h1;
364     struct RB_header   *header_2 = h2;
365 
366     /*  Check for priorities */
367     if ( header_1->htype->priority > header_2->htype->priority )
368     {
369         /*  Header 1 has higher priority */
370         return -1;
371     }
372     else if ( header_1->htype->priority < header_2->htype->priority )
373     {
374         /*  Header 2 has higher priority */
375         return 1;
376     }
377     else
378     {
379         /*  Priorities are equal */
380         /*  Check if we sort on full name or just the function name */
381         if ( course_of_action.do_sectionnameonly )
382         {
383             /*  Do not include parent name in sorting if they are not displayed */
384             return RB_Str_Case_Cmp( header_1->function_name,
385                                     header_2->function_name );
386         }
387         else
388         {
389             /*  Sort on full name ( module/function ) */
390             return RB_Str_Case_Cmp( header_1->name, header_2->name );
391         }
392     }
393 }
394 
395 /*****/
396 
397 /* TODO Documentation */
RB_Document_Sort_Headers(struct RB_Document * document)398 void RB_Document_Sort_Headers(
399     struct RB_Document *document )
400 {
401     struct RB_Part     *i_part;
402     unsigned long       part_count = 0;
403 
404     RB_Say( "Sorting headers per part (file)\n", SAY_INFO );
405     for ( i_part = document->parts; i_part; i_part = i_part->next )
406     {
407         struct RB_header   *i_header;
408 
409         /* Count the number of headers */
410         for ( part_count = 0, i_header = i_part->headers;
411               i_header; i_header = i_header->next )
412         {
413             part_count++;
414         }
415 
416         if ( part_count )
417         {
418             /* Sort them */
419             struct RB_header  **temp_headers =
420                 calloc( part_count, sizeof( struct RB_header * ) );
421             unsigned int        i = 0;
422 
423             i_header = i_part->headers;
424             for ( i = 0; i < part_count; ++i )
425             {
426                 assert( i_header );
427                 temp_headers[i] = i_header;
428                 i_header = i_header->next;
429             }
430             RB_QuickSort( ( void ** ) temp_headers, 0, part_count - 1,
431                           RB_CompareHeaders );
432             i_part->headers = temp_headers[0];
433             i_part->headers->next = NULL;
434             i_header = temp_headers[0];
435             for ( i = 1; i < part_count; ++i )
436             {
437                 assert( i_header );
438                 i_header->next = temp_headers[i];
439                 i_header = i_header->next;
440             }
441             temp_headers[part_count - 1]->next = NULL;
442             free( temp_headers );
443         }
444     }
445     RB_Say( "Sorting all headers\n", SAY_INFO );
446     RB_QuickSort( ( void ** ) document->headers, 0, document->no_headers - 1,
447                   RB_CompareHeaders );
448 }
449 
450 
451 /****f* Document/RB_Document_Collect_Headers
452  * FUNCTION
453  *   Create a table of pointers to all headers.  This is done to
454  *   have easy access to all heades without having to scan all
455  *   RB_Parts.
456  * INPUTS
457  *   o document -- the document for which the table is created.
458  * OUTPUT
459  *   o document->headers
460  *   o document->no_headers
461  * SOURCE
462  */
463 
RB_Document_Collect_Headers(struct RB_Document * document)464 void RB_Document_Collect_Headers(
465     struct RB_Document *document )
466 {
467     struct RB_Part     *i_part;
468     struct RB_header  **headers;        /* Pointer to an array of pointers RB_headers. */
469     unsigned long       count = 0;
470     unsigned long       part_count = 0;
471     unsigned long       i = 0;
472 
473     RB_Say( "Collecting all headers in a single table\n", SAY_INFO );
474     for ( i_part = document->parts; i_part; i_part = i_part->next )
475     {
476         struct RB_header   *i_header;
477 
478         /* Count the number of headers */
479         for ( part_count = 0, i_header = i_part->headers;
480               i_header; i_header = i_header->next )
481         {
482             part_count++;
483         }
484         /* Compute the total count */
485         count += part_count;
486     }
487     headers =
488         ( struct RB_header ** ) calloc( count, sizeof( struct RB_header * ) );
489     for ( i_part = document->parts; i_part; i_part = i_part->next )
490     {
491         struct RB_header   *i_header;
492 
493         for ( i_header = i_part->headers;
494               i_header; i_header = i_header->next )
495         {
496             headers[i] = i_header;
497             i++;
498         }
499     }
500     document->headers = headers;
501     document->no_headers = count;
502 }
503 
504 /*******/
505 
506 /* TODO Documentation */
507 
RB_Document_Check_For_Duplicate(struct RB_Document * arg_document,struct RB_header * hdr)508 struct RB_header   *RB_Document_Check_For_Duplicate(
509     struct RB_Document *arg_document,
510     struct RB_header *hdr )
511 {
512     struct RB_Part     *i_part;
513 
514     for ( i_part = arg_document->parts; i_part; i_part = i_part->next )
515     {
516         struct RB_header   *i_header;
517 
518         for ( i_header = i_part->headers; i_header;
519               i_header = i_header->next )
520         {
521             int                 i;
522 
523             if ( hdr == i_header )
524                 continue;
525 
526             for ( i = 0; i < hdr->no_names; i++ )
527                 if ( strcmp( hdr->names[i], i_header->name ) == 0 )
528                     return i_header;
529         }
530     }
531     return NULL;
532 }
533 
534 
535 /*  TODO Documentation
536  If A is called   qqqq/ffff and B is called  ffff/zzzz then A is the
537  parent of B
538 */
539 
RB_Document_Link_Headers(struct RB_Document * document)540 void RB_Document_Link_Headers(
541     struct RB_Document *document )
542 {
543     unsigned long       i;
544     unsigned long       j;
545     struct RB_header   *parent;
546     struct RB_header   *child;
547     char               *parent_name;
548     char               *child_name;
549 
550     RB_Say( "Linking all %d headers.\n", SAY_INFO, document->no_headers );
551     for ( i = 0; i < document->no_headers; i++ )
552     {
553         parent = ( document->headers )[i];
554         parent_name = parent->function_name;
555         for ( j = 0; j < document->no_headers; j++ )
556         {
557             if ( i != j )
558             {
559                 child = ( document->headers )[j];
560                 child_name = child->module_name;
561                 if ( strcmp( child_name, parent_name ) == 0 )
562                 {
563                     child->parent = parent;
564                 }
565             }
566         }
567     }
568 }
569 
570 
571 /****f* Document/RB_Loop_Check
572  * FUNCTION
573  *   This function checks for loops in the headersr; that is
574  *   by mistake the following relation between headers
575  *   can be specified.
576  *     y.parent -> x.parent -> z.parent -+
577  *     ^                                 |
578  *     |---------------------------------+
579  *   This functions detects these loops, prints a warning
580  *   and breaks the loop.
581  *
582  *   If left unbroken ROBODoc can hang.
583  * SYNOPSIS
584  */
585 
RB_Loop_Check(struct RB_Document * document)586 void RB_Loop_Check(
587     struct RB_Document *document
588         )
589 
590  /* INPUTS
591   *   document -- document to be checked for loops.
592   *
593   * SOURCE
594   */
595 {
596     int i;
597 
598     RB_Say( "Check all %d headers for loops.\n", SAY_INFO, document->no_headers );
599     for ( i = 0; i < document->no_headers; i++ )
600     {
601         struct RB_header   *parent;
602         struct RB_header   *grant_parent;
603         parent = ( document->headers )[i];
604         for ( grant_parent = parent->parent;
605               grant_parent && ( parent != grant_parent );
606               grant_parent = grant_parent->parent ) {
607             /* Empty */
608         }
609         if ( parent == grant_parent ) {
610             /* Problem, from a parent I can get
611              * back to the parent itself. This means
612              * there is a loop. Warn the user about this.
613              */
614 
615              RB_Warning( "Warning: Header %s/%s eventually points back to itself.\n",
616                    parent->module_name, parent->function_name  );
617              /* TODO print whole linkage line */
618              for ( grant_parent = parent->parent;
619                    grant_parent && ( parent != grant_parent );
620                    grant_parent = grant_parent->parent ) {
621                     /* Empty */
622                  RB_Warning("--> %s/%s\n",
623                    grant_parent->module_name, grant_parent->function_name  );
624              }
625              parent->parent = NULL;
626              RB_Warning( "Breaking the linkage for %s/%s\n",
627                    parent->module_name, parent->function_name  );
628         }
629     }
630 }
631 
632 /*******/
633 
634 
635 /* TODO Documentation */
RB_Document_Split_Parts(struct RB_Document * document)636 void RB_Document_Split_Parts(
637     struct RB_Document *document )
638 {
639     struct RB_Part     *i_part = NULL;
640     struct RB_Part    **new_parts = NULL;
641     int                 new_number_of_parts = 0;
642     int                 n = 0;
643     int                 i;
644 
645     /* split eacht part in several parts. One for each header
646      * in the original part.
647      */
648     for ( i_part = document->parts; i_part; i_part = i_part->next )
649     {
650         struct RB_header   *i_header;
651 
652         for ( i_header = i_part->headers;
653               i_header; i_header = i_header->next )
654         {
655             ++new_number_of_parts;
656         }
657     }
658 
659     new_parts = calloc( new_number_of_parts, sizeof( struct RB_Part * ) );
660 
661     if ( new_parts )
662     {
663         /* Create new parts */
664 
665         RB_Say( "Splitting parts based on headers.\n", SAY_DEBUG );
666         for ( i_part = document->parts; i_part; i_part = i_part->next )
667         {
668             struct RB_header   *i_header;
669             struct RB_header   *i_next_header;
670 
671             for ( i_header = i_part->headers;
672                   i_header; i_header = i_next_header )
673             {
674                 struct RB_Part     *new_part;
675 
676                 i_next_header = i_header->next;
677 
678                 RB_Say( "Creating new part.\n", SAY_DEBUG );
679                 new_part = RB_Get_RB_Part(  );
680                 RB_Part_Add_Source( new_part,
681                                     RB_Copy_RB_Filename( RB_Part_Get_Source
682                                                          ( i_part ) ) );
683                 /* remove header from i_part and add to new_part.
684                  */
685                 RB_Part_Add_Header( new_part, i_header );
686                 assert( n < new_number_of_parts );
687                 new_parts[n] = new_part;
688                 ++n;
689             }
690             i_part->headers = NULL;
691             i_part->last_header = NULL;
692         }
693         /* Remove old part from document */
694         RB_Free_RB_Document_Parts( document );
695         /* Add new parts to document */
696         for ( i = 0; i < n; ++i )
697         {
698             RB_Document_Add_Part( document, new_parts[i] );
699         }
700         /* clean-up temp array */
701         free( new_parts );
702     }
703     else
704     {
705         RB_Panic( "Out of memory! RB_Document_Split_Parts()" );
706     }
707 }
708 
709 /* TODO Documentation */
RB_Document_Determine_DocFileNames(struct RB_Document * document)710 void RB_Document_Determine_DocFileNames(
711     struct RB_Document *document )
712 {
713     struct RB_Filename *filename;
714     unsigned int        length = 0;
715     char               *name;
716     char               *c;
717 
718     struct RB_Part     *part;
719 
720     assert( document->actions.do_multidoc );
721 
722     for ( part = document->parts; part; part = part->next )
723     {
724 
725         filename = part->filename;
726         /* turn  mysource.c  into  mysource_c.html
727            First find the total length. */
728         length = strlen( filename->name );
729         /* add one for the '.' */
730         ++length;
731 
732         if ( document->actions.do_one_file_per_header )
733         {
734             struct RB_header   *i_header = part->headers;
735 
736             assert( i_header );
737             /* add the name of the header to the filename */
738             /* We make this twice as long because some of the
739              * characters in the file are escaped to 2 hexadecimal
740              * digits.
741              */
742             length += 2 * strlen( i_header->name );
743         }
744 
745         length += RB_Get_Len_Extension( document->extension );
746         /* plus one for the '\0' */
747         ++length;
748         name = ( char * ) calloc( length, sizeof( char ) );
749         assert( name );
750         strcat( name, filename->name );
751         for ( c = name; *c != '\0'; c++ )
752         {
753             if ( *c == '.' )
754             {
755                 *c = '_';
756             }
757         }
758 
759         if ( document->actions.do_one_file_per_header )
760         {
761             unsigned int        i;
762             struct RB_header   *i_header = part->headers;
763 
764             assert( i_header );
765             /* add the name of the header to the filename */
766             for ( i = 0; i < strlen( i_header->name ); ++i )
767             {
768                 if ( utf8_isalnum( i_header->name[i] ) )
769                 {
770                     sprintf( c, "%c", i_header->name[i] );
771                     c++;
772                 }
773                 else
774                 {
775                     sprintf( c, "%2X", i_header->name[i] );
776                     c++;
777                     c++;
778                 }
779             }
780         }
781         RB_Add_Extension( document->extension, name );
782 
783         RB_Say( "Filename for part is %s\n", SAY_DEBUG, name );
784         part->filename->docname = name;
785     }
786 }
787 
788 /****f* Document/RB_Open_SingleDocumentation
789  * FUNCTION
790  *   Open the file that will contain the documentation in
791  *   case we create a single document.
792  * SYNOPSIS
793  */
RB_Open_SingleDocumentation(struct RB_Document * document)794 FILE               *RB_Open_SingleDocumentation(
795     struct RB_Document *document )
796 /*
797  * RESULT
798  *   An opened file.
799  * SOURCE
800  */
801 {
802     FILE               *file;
803     static char        *default_name = "singledoc";
804     char               *name = NULL;
805     size_t              size = 0;
806 
807     if ( document->singledoc_name )
808     {
809         size += strlen( document->singledoc_name );
810     }
811     else
812     {
813         size += strlen( default_name );
814     }
815     size++;                     /* and the '\0'; */
816     size += RB_Get_Len_Extension( document->extension );
817 
818     name = ( char * ) calloc( size, sizeof( char ) );
819     assert( name );
820     if ( document->singledoc_name )
821     {
822         strcat( name, document->singledoc_name );
823     }
824     else
825     {
826         strcat( name, default_name );
827     }
828     RB_Add_Extension( document->extension, name );
829 
830     file = fopen( name, "w" );
831     if ( file )
832     {
833         /* File opened  */
834     }
835     else
836     {
837         RB_Panic( "Can't open %s\n", name );
838     }
839     free( name );
840     return file;
841 }
842 
843 /****/
844 
845 /****f* Document/RB_Get_RB_Document
846  * FUNCTION
847  *   Allocate and initialize an RB_Document structure.
848  * SYNOPSIS
849  */
RB_Get_RB_Document(void)850 struct RB_Document *RB_Get_RB_Document(
851     void )
852 /*
853  * RESULT
854  *   An initialized document structure.
855  * SOURCE
856  */
857 {
858     struct RB_Document *document = 0;
859     document =
860         ( struct RB_Document * ) malloc( sizeof( struct RB_Document ) );
861     if ( document )
862     {
863         document->cur_part = NULL;
864         document->parts = NULL;
865         document->links = NULL;
866         document->headers = NULL;
867         document->doctype = UNKNOWN;
868         document->actions = No_Actions();
869         document->srctree = NULL;
870         document->srcroot = NULL;
871         document->docroot = NULL;
872         document->singledoc_name = NULL;
873         document->no_headers = 0;
874         document->charset = NULL;
875         document->extension = NULL;
876         document->first_section_level = 1;
877         document->doctype_name = NULL;
878         document->doctype_location = NULL;
879     }
880     else
881     {
882         RB_Panic( "out of memory" );
883     }
884     return document;
885 }
886 
887 /*******/
888