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