/* * main.c * * This program error checks the OAR PDL and converts it into * It works by reading the input file into a linked list of lines * and then performing sweeps on that list until all formatting is * complete. * * FEATURES: * + rudimentary statistics * + forgiveness features * + output in Texinfo format * + output in a form that a sibling MS-Word VBA program can format * * CHECKS PERFORMED: * + unable to open file * + unexpected end of file * + line should have a colon * + basic text (to right or below) associated with keyword * improperly placed * + an "incomplete marker" is still in place * + missing keywords within a subsection * + duplicated keywords withing a subsection * + subsections in correct order * + section header indicates no subsections and there are subsections * + section header indicates subsections and there are no subsections * + inconsistent spacing in RAW text. This tends to be 1st line with * text is indented further than a subsequent line. * + missing components on line (incomplete) * + invalid repitition of a subsection * + type keyword validated for class, type, and spacing between * + both members and range are present * + neither members and range are present * + enumerated types and attributes have members * + non-enumerated types and attributes have ranges. * + Object name and end object have the same name * + booleans in attribute section list both true and false * + task synchronization keyword is checked. There must be a valid * type of synchronization primitive and a description when expected. * + sections in correct order * * INTERNAL ERRORS: * + attempting to reformat an already formatted line * + end of processing reached and no formatting assigned to line * * CHECKS NOT PERFORMED: * * TODO: * * IDEAS NOT IMPLEMENTED: * + smarter reporting of sections not in the right order * + task which uses multiple synchronization types * + improved error messages * + no introductions inserted to sections * + recognize special "symbols" like infinity, +-, etc. * * QUESTIONS: * + "what do I know" is actually a table and should not be reformatted. * * COPYRIGHT (c) 1997. * On-Line Applications Research Corporation (OAR). * All rights reserved. * * $Id$ */ #include #include #include #include #include #include /* XXX -- just for testing -- these should be set by options */ char DocsNextNode[256] = ""; char DocsPreviousNode[256] = ""; char DocsUpNode[256] = ""; extern int optind; /* Why is this not in ? */ extern char *optarg; /* Why is this not in ? */ #ifndef NAME_MAX #define NAME_MAX 14 /* Why is the one in limits.h not showing up? */ #endif #define INIT_DATA #define EXTERN #include "base.h" FILE *OutFile; /************************************************************************* ************************************************************************* ***** DATA TYPES AND CONSTANT TABLES ***** ************************************************************************* *************************************************************************/ /* * Usage Information */ char *Usage_Strings[] = { "\n", "usage: cmd [-vti?] [-p previous_node] [-u up_node] files ...\n", "\n", "EOF" }; /* * The page separator is not really a keyword and will be purged before * it is seen elsewhere. */ #define PAGE_SEPARATOR "#PAGE" /* * Section Delimiter Keywords */ #define MAXIMUM_KEYWORD_LENGTH 32 /* * Level indicates where in the format the delimiter is allowed to occur. * 1 indicates a major section divider (e.g. "ATTRIBUTE DESCRIPTIONS:"). * 2 indicates a subsection (e.g. "ATTRIBUTE:"). * 3 indicates a heading (e.g. "DESCRIPTION:"). */ #define TEXT 0 #define SECTION 1 #define SUBSECTION 2 #define HEADING 3 typedef enum { UNUSED, /* dummy 0 slot */ OBJECT, /* sections */ ATTRIBUTE_DESCRIPTIONS, ASSOCIATION_DESCRIPTIONS, ABSTRACT_TYPE_DESCRIPTIONS, DATA_ITEM_DESCRIPTIONS, METHOD_DESCRIPTIONS, TASK_DESCRIPTIONS, END_OBJECT, ATTRIBUTE, /* subsections */ ASSOCIATION, ABSTRACT_TYPE, DATA_ITEM, METHOD, TASK, DESCRIPTION, /* headings */ COPYRIGHT, PORTING, THEORY_OF_OPERATION, DERIVATION, DEPENDENCIES, NOTES, TYPE, RANGE, MEMBERS, UNITS, SCALE_FACTOR, DEFAULT, TOLERANCE, REQUIREMENTS, REFERENCES, VISIBILITY, ASSOCIATED_WITH, MULTIPLICITY, INPUTS, OUTPUTS, PDL, SYNCHRONIZATION, TIMING, RAW_EXAMPLE, /* our own */ } Keyword_indices_t; #define KEYWORD_FIRST OBJECT #define KEYWORD_LAST TIMING /* * Line Management Structure */ typedef enum { NO_EXTRA_FORMATTING_INFO, RAW_OUTPUT, PARAGRAPH_OUTPUT, BULLET_OUTPUT, } ExtraFormat_info_t; typedef struct { Chain_Node Node; Keyword_indices_t keyword; /* unused is unknown/undecided */ ExtraFormat_info_t format; int number; char Contents[ PARAGRAPH_SIZE ]; } Line_Control; typedef enum { RT_NO_CHECK, /* don't do this at all */ RT_FORBIDDEN, /* no text to right allowed */ RT_OPTIONAL, /* text to right optional -- none below */ RT_NONE, /* text to right is "none" or nothing -- none below */ RT_REQUIRED, /* text to right required -- none below */ RT_BELOW, /* text to right forbidden -- text below required */ RT_NONE_OR_BELOW, /* text to right is "none" OR there is text below */ RT_MAYBE_BELOW, /* text to right required -- text below optional */ RT_EITHER, /* text to right OR below */ RT_BOTH /* text to right AND below */ } Keywords_text_mode_t; typedef enum { BL_FORBIDDEN, /* text below forbidden */ BL_FORMATTED, /* text below is to be formatted as paragraphs */ BL_RAW, /* text below is to be unprocessed by this program */ } Keywords_text_below_t; typedef int (*Keyword_validater_t)( Line_Control * ); typedef struct { char Name[ MAXIMUM_KEYWORD_LENGTH ]; int level; Keywords_text_mode_t text_mode; Keywords_text_below_t text_below_mode; Keyword_validater_t keyword_validation_routine; } Keyword_info_t; /* * Keyword Validation Routines */ int Validate_visibility( Line_Control *line ); int Validate_synchronization( Line_Control *line ); Keyword_info_t Keywords[] = { { "unused", 0, 0, 0, NULL }, /* so 0 can be invalid */ { "OBJECT:", SECTION, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "ATTRIBUTE DESCRIPTIONS:", SECTION, RT_NONE, BL_FORBIDDEN, NULL }, { "ASSOCIATION DESCRIPTIONS:", SECTION, RT_NONE, BL_FORBIDDEN, NULL }, { "ABSTRACT TYPE DESCRIPTIONS:", SECTION, RT_NONE, BL_FORBIDDEN, NULL }, { "DATA ITEM DESCRIPTIONS:", SECTION, RT_NONE, BL_FORBIDDEN, NULL }, { "METHOD DESCRIPTIONS:", SECTION, RT_NONE, BL_FORBIDDEN, NULL }, { "TASK DESCRIPTIONS:", SECTION, RT_NONE, BL_FORBIDDEN, NULL }, { "ENDOBJECT:", SECTION, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "ATTRIBUTE:", SUBSECTION, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "ASSOCIATION:", SUBSECTION, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "ABSTRACT TYPE:", SUBSECTION, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "DATA ITEM:", SUBSECTION, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "METHOD:", SUBSECTION, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "TASK:", SUBSECTION, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "DESCRIPTION:", HEADING, RT_BELOW, BL_FORMATTED, NULL }, { "COPYRIGHT:", HEADING, RT_BELOW, BL_FORMATTED, NULL }, { "PORTING:", HEADING, RT_BELOW, BL_FORMATTED, NULL }, { "THEORY OF OPERATION:", HEADING, RT_BELOW, BL_FORMATTED, NULL }, { "DERIVATION:", HEADING, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "DEPENDENCIES:", HEADING, RT_BELOW, BL_FORMATTED, NULL }, { "NOTES:", HEADING, RT_BELOW, BL_FORMATTED, NULL }, { "TYPE:", HEADING, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "RANGE:", HEADING, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "MEMBERS:", HEADING, RT_BELOW, BL_RAW, NULL }, { "UNITS:", HEADING, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "SCALE FACTOR:", HEADING, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "DEFAULT:", HEADING, RT_EITHER, BL_RAW, NULL }, { "TOLERANCE:", HEADING, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "REQUIREMENTS:", HEADING, RT_BELOW, BL_RAW, NULL }, { "REFERENCES:", HEADING, RT_BELOW, BL_RAW, NULL }, { "VISIBILITY:", HEADING, RT_REQUIRED, BL_FORBIDDEN, Validate_visibility }, { "ASSOCIATED WITH:", HEADING, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "MULTIPLICITY:", HEADING, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "INPUTS:", HEADING, RT_NONE_OR_BELOW, BL_RAW, NULL }, { "OUTPUTS:", HEADING, RT_NONE_OR_BELOW, BL_RAW, NULL }, { "PDL:", HEADING, RT_BELOW, BL_RAW, NULL }, { "SYNCHRONIZATION:", HEADING, RT_MAYBE_BELOW, BL_RAW, Validate_synchronization }, { "TIMING:", HEADING, RT_REQUIRED, BL_FORBIDDEN, NULL }, { "", TEXT, RT_NO_CHECK, BL_RAW, NULL } }; #define NUMBER_OF_KEYWORDS \ ( sizeof( Keywords ) / sizeof( Keyword_info_t ) ) - 2 /* * Section Descriptions */ #define MAXIMUM_ELEMENTS 16 typedef struct { Keyword_indices_t keyword; boolean is_required; } Element_info_t; typedef struct Section_info_struct_t Section_info_t; typedef (*Section_validater_t)( Section_info_t *, Line_Control *, /* start */ Line_Control * /* next_section */ ); struct Section_info_struct_t { boolean repeats; Keyword_indices_t This_section; Keyword_indices_t Next_section; Section_validater_t section_validation_routine; Element_info_t Description[MAXIMUM_ELEMENTS]; }; int Validate_object( Section_info_t *section, Line_Control *start, Line_Control *next_section ); int Validate_attribute( Section_info_t *section, Line_Control *start, Line_Control *next_section ); int Validate_abstract_type( Section_info_t *section, Line_Control *start, Line_Control *next_section ); Section_info_t ObjectSection = { FALSE, /* subsections repeat */ OBJECT, /* this section */ ATTRIBUTE_DESCRIPTIONS, /* next section */ Validate_object, /* validation routine */ { { OBJECT, TRUE }, /* 0 */ { DESCRIPTION, TRUE }, /* 1 */ { COPYRIGHT, FALSE }, /* 2 */ { PORTING, FALSE }, /* 3 */ { THEORY_OF_OPERATION, TRUE }, /* 4 */ { DERIVATION, FALSE }, /* 5 */ { DEPENDENCIES, FALSE }, /* 6 */ { REQUIREMENTS, FALSE }, /* 7 */ { REFERENCES, FALSE }, /* 8 */ { NOTES, FALSE }, /* 9 */ { UNUSED, FALSE }, /* 10 */ { UNUSED, FALSE }, /* 11 */ { UNUSED, FALSE }, /* 12 */ { UNUSED, FALSE }, /* 13 */ { UNUSED, FALSE }, /* 14 */ { UNUSED, FALSE }, /* 15 */ } }; Section_info_t AttributeSection = { TRUE, /* subsections repeat */ ATTRIBUTE_DESCRIPTIONS, /* this section */ ASSOCIATION_DESCRIPTIONS, /* next section */ Validate_attribute, /* validation routine */ { { ATTRIBUTE, TRUE }, /* 0 */ { DESCRIPTION, TRUE }, /* 1 */ { TYPE, TRUE }, /* 2 */ { MEMBERS, FALSE }, /* 3 */ { RANGE, FALSE }, /* 4 */ { UNITS, FALSE }, /* 5 */ { SCALE_FACTOR, FALSE }, /* 6 */ { DEFAULT, FALSE }, /* 7 */ { TOLERANCE, FALSE }, /* 8 */ { REQUIREMENTS, FALSE }, /* 9 */ { REFERENCES, FALSE }, /* 10 */ { NOTES, FALSE }, /* 11 */ { UNUSED, FALSE }, /* 12 */ { UNUSED, FALSE }, /* 13 */ { UNUSED, FALSE }, /* 14 */ { UNUSED, FALSE }, /* 15 */ } }; Section_info_t AssociationSection = { TRUE, /* subsections repeat */ ASSOCIATION_DESCRIPTIONS, /* this section */ ABSTRACT_TYPE_DESCRIPTIONS, /* next section */ NULL, /* validation routine */ { { ASSOCIATION, TRUE }, /* 0 */ { DESCRIPTION, TRUE }, /* 1 */ { VISIBILITY, TRUE }, /* 2 */ { ASSOCIATED_WITH, TRUE }, /* 3 */ { MULTIPLICITY, TRUE }, /* 4 */ { REQUIREMENTS, FALSE }, /* 5 */ { REFERENCES, FALSE }, /* 6 */ { NOTES, FALSE }, /* 7 */ { UNUSED, FALSE }, /* 8 */ { UNUSED, FALSE }, /* 9 */ { UNUSED, FALSE }, /* 10 */ { UNUSED, FALSE }, /* 11 */ { UNUSED, FALSE }, /* 12 */ { UNUSED, FALSE }, /* 13 */ { UNUSED, FALSE }, /* 14 */ { UNUSED, FALSE }, /* 15 */ } }; Section_info_t AbstractTypeSection = { TRUE, /* subsections repeat */ ABSTRACT_TYPE_DESCRIPTIONS, /* this section */ DATA_ITEM_DESCRIPTIONS, /* next section */ Validate_abstract_type, /* validation routine */ { { ABSTRACT_TYPE, TRUE }, /* 0 */ { DESCRIPTION, TRUE }, /* 1 */ { VISIBILITY, TRUE }, /* 2 */ { DERIVATION, TRUE }, /* 3 */ { MEMBERS, FALSE }, /* 4 */ { RANGE, FALSE }, /* 5 */ { UNITS, FALSE }, /* 6 */ { SCALE_FACTOR, FALSE }, /* 7 */ { DEFAULT, FALSE }, /* 8 */ { TOLERANCE, FALSE }, /* 9 */ { REQUIREMENTS, FALSE }, /* 10 */ { REFERENCES, FALSE }, /* 11 */ { NOTES, FALSE }, /* 12 */ { UNUSED, FALSE }, /* 13 */ { UNUSED, FALSE }, /* 14 */ { UNUSED, FALSE }, /* 15 */ } }; Section_info_t DataItemSection = { TRUE, /* subsections repeat */ DATA_ITEM_DESCRIPTIONS, /* this section */ METHOD_DESCRIPTIONS, /* next section */ NULL, /* validation routine */ { { DATA_ITEM, TRUE }, /* 0 */ { DESCRIPTION, TRUE }, /* 1 */ { TYPE, TRUE }, /* 2 */ { UNITS, FALSE }, /* 3 */ { SCALE_FACTOR, FALSE }, /* 4 */ { DEFAULT, FALSE }, /* 5 */ { TOLERANCE, FALSE }, /* 6 */ { NOTES, FALSE }, /* 7 */ { UNUSED, FALSE }, /* 8 */ { UNUSED, FALSE }, /* 9 */ { UNUSED, FALSE }, /* 10 */ { UNUSED, FALSE }, /* 11 */ { UNUSED, FALSE }, /* 12 */ { UNUSED, FALSE }, /* 13 */ { UNUSED, FALSE }, /* 14 */ { UNUSED, FALSE }, /* 15 */ } }; Section_info_t MethodSection = { TRUE, /* subsections repeat */ METHOD_DESCRIPTIONS, /* this section */ TASK_DESCRIPTIONS, /* next section */ NULL, /* validation routine */ { { METHOD, TRUE }, /* 0 */ { DESCRIPTION, TRUE }, /* 1 */ { VISIBILITY, TRUE }, /* 2 */ { INPUTS, TRUE }, /* 3 */ { OUTPUTS, TRUE }, /* 4 */ { REQUIREMENTS, FALSE }, /* 5 */ { REFERENCES, FALSE }, /* 6 */ { NOTES, FALSE }, /* 7 */ { PDL, TRUE }, /* 8 */ { UNUSED, FALSE }, /* 9 */ { UNUSED, FALSE }, /* 10 */ { UNUSED, FALSE }, /* 11 */ { UNUSED, FALSE }, /* 12 */ { UNUSED, FALSE }, /* 13 */ { UNUSED, FALSE }, /* 14 */ { UNUSED, FALSE }, /* 15 */ } }; Section_info_t TaskSection = { TRUE, /* subsections repeat */ TASK_DESCRIPTIONS, /* this section */ END_OBJECT, /* next section */ NULL, /* validation routine */ { { METHOD, TRUE }, /* 0 */ { DESCRIPTION, TRUE }, /* 1 */ { VISIBILITY, TRUE }, /* 2 */ { INPUTS, TRUE }, /* 3 */ { SYNCHRONIZATION, TRUE }, /* 4 */ { TIMING, TRUE }, /* 5 */ { REQUIREMENTS, FALSE }, /* 6 */ { REFERENCES, FALSE }, /* 7 */ { NOTES, FALSE }, /* 8 */ { PDL, TRUE }, /* 9 */ { UNUSED, FALSE }, /* 10 */ { UNUSED, FALSE }, /* 11 */ { UNUSED, FALSE }, /* 12 */ { UNUSED, FALSE }, /* 13 */ { UNUSED, FALSE }, /* 14 */ { UNUSED, FALSE }, /* 15 */ } }; Section_info_t *Sections[] = { &ObjectSection, &AttributeSection, &AssociationSection, &AbstractTypeSection, &DataItemSection, &MethodSection, &TaskSection }; /* * exit_application */ void exit_application( int status ) { fprintf( stderr, "*** Error encountered ***\n" ); /* fprintf( stderr, "*** Error encountered on line %d ***\n", CurrentLine ); */ fclose( OutFile ); exit( 1 ); } /************************************************************************* ************************************************************************* ***** LINE MANIPULATION ROUTINES ***** ************************************************************************* *************************************************************************/ /* * PrintLine */ void PrintLine( Line_Control *line ) { assert( line ); if ( line->number == -1 ) fprintf( stderr, " " ); else fprintf( stderr, "%5d", line->number ); #if 0 fprintf( stderr, "%s\n", line->Contents ); #else /* * Include some debugging information */ fprintf( stderr, "<%d,%d>:%s\n", line->keyword, line->format, line->Contents ); #endif } Chain_Control Line_Pool; /* * FillLinePool */ void FillLinePool( void ) { void *pool; #define LINE_POOL_FILL_COUNT 100 pool = malloc( sizeof( Line_Control ) * LINE_POOL_FILL_COUNT ); assert( pool ); _Chain_Initialize( &Line_Pool, pool, LINE_POOL_FILL_COUNT, sizeof( Line_Control ) ); } /* * AllocateLine */ Line_Control *AllocateLine( void ) { Line_Control *new_line; new_line = (Line_Control *) _Chain_Get( &Line_Pool ); if ( !new_line ) { FillLinePool(); new_line = (Line_Control *) _Chain_Get( &Line_Pool ); assert( new_line ); } /* * This is commented out because although it is helpful during debug, * it consumes a significant percentage of the program's execution time. */ memset( new_line->Contents, '\0', sizeof( new_line->Contents ) ); new_line->number = -1; new_line->keyword = UNUSED; new_line->format = NO_EXTRA_FORMATTING_INFO; new_line->Node.next = NULL; new_line->Node.previous = NULL; return new_line; } /* * FreeLine */ void FreeLine( Line_Control *line ) { fflush( stdout ); _Chain_Append( &Line_Pool, &line->Node ); } /* * DeleteLine */ Line_Control *DeleteLine( Line_Control *line ) { Line_Control *next; next = (Line_Control *)line->Node.next; _Chain_Extract( &line->Node ); FreeLine( line ); return next; } /* * PrintSurroundingLines */ void PrintSurroundingLines( Line_Control *line, int backward, int forward ) { int i; int real_backward; Line_Control *local; for ( local=line, real_backward=0, i=1 ; i<=backward ; i++, real_backward++ ) { if ( &local->Node == Lines.first ) break; local = (Line_Control *) local->Node.previous; } for ( i=1 ; i<=real_backward ; i++ ) { PrintLine( local ); local = (Line_Control *) local->Node.next; } PrintLine( local ); for ( i=1 ; i<=forward ; i++ ) { local = (Line_Control *) local->Node.next; if ( _Chain_Is_last( &local->Node ) ) break; PrintLine( local ); } } /* * SetLineFormat */ void SetLineFormat( Line_Control *line, ExtraFormat_info_t format ) { if ( line->format != NO_EXTRA_FORMATTING_INFO ) { fprintf( stderr, "Line %d is already formatted\n", line->number ); PrintLine( line ); assert( FALSE ); } line->format = format; } /* * LineCopyFromRight */ void LineCopyFromRight( Line_Control *line, char *dest ) { char *p; for ( p=line->Contents ; *p != ':' ; p++ ) ; p++; /* skip the ':' */ for ( ; isspace( *p ) ; p++ ) ; strcpy( dest, p ); } /* * LineCopySectionName */ void LineCopySectionName( Line_Control *line, char *dest ) { char *p; char *d; p = line->Contents; d = dest; if ( *p == '@' ) { /* skip texinfo command */ while ( !isspace( *p++ ) ) ; } for ( ; *p != ':' ; ) *d++ = *p++; *d = '\0'; } /* * FormatParagraph */ Line_Control *FormatParagraph( Line_Control *starting ) { Chain_Node *insert_after; Line_Control *to_add; Line_Control *new_line; char Paragraph[ PARAGRAPH_SIZE ]; int starting_line; char *src; char *dest; boolean do_loop; boolean is_bullet; int length; int i; length = 0; starting_line = starting->number; insert_after = starting->Node.previous; to_add = starting; dest = Paragraph; is_bullet = FALSE; for ( ; ; ) { src = to_add->Contents; for ( ; *src && isspace( *src ) ; src++ ); /* skip leading spaces */ if ( !strncmp( src, "@table", 6 ) ) { to_add = DeleteLine( to_add ); src = to_add->Contents; for ( i=4 ; *src && isspace( *src ) && i-- ; src++ ); /* skip leading spaces */ for ( ; ; ) { if ( _Chain_Is_last( &to_add->Node ) ) { fprintf( stderr, "Missing end table for table starting at line %d\n", starting_line ); exit_application( 1 ); } if ( !strncmp( src, "@end table", 10 ) ) { to_add = DeleteLine( to_add ); return to_add; } strcpy( Paragraph, src ); strcpy( to_add->Contents, Paragraph ); SetLineFormat( to_add, PARAGRAPH_OUTPUT ); to_add->keyword = RAW_EXAMPLE; to_add = (Line_Control *) to_add->Node.next; src = to_add->Contents; for ( i=4 ; *src && isspace( *src ) && i-- ; src++ ); /* skip leading spaces */ } } if ( *src == '+' ) { if ( is_bullet == TRUE ) break; is_bullet = TRUE; for ( src++ ; isspace(*src) ; src++ ) ; } do_loop = TRUE; while ( *src && do_loop == TRUE ) { if ( isspace( *src ) ) { /* convert multiple spaces to 1 */ if ( *(dest-1) == '.' ) { /* two spaces after period */ *dest++ = ' '; length++; } assert( length < PARAGRAPH_SIZE ); *dest++ = ' '; length++; assert( length < PARAGRAPH_SIZE ); for ( ; isspace( *src ) ; src++ ) if ( !*src ) { do_loop = FALSE; break; } continue; } else { *dest++ = *src; length++; assert( length < PARAGRAPH_SIZE ); src++; } } to_add = DeleteLine( to_add ); if ( _Chain_Is_last( &to_add->Node ) ) break; if ( !strlen( to_add->Contents ) ) break; if ( to_add->keyword ) break; if ( !isspace( *(dest-1) ) ) { if ( *(dest-1) == '.' ) { *dest++ = ' '; length++; assert( length < PARAGRAPH_SIZE ); } *dest++ = ' '; length++; assert( length < PARAGRAPH_SIZE ); } } new_line = AllocateLine(); SetLineFormat( new_line, (is_bullet) ? BULLET_OUTPUT : PARAGRAPH_OUTPUT ); *dest = '\0'; strcpy( new_line->Contents, Paragraph ); new_line->number = starting_line; _Chain_Insert( insert_after, &new_line->Node ); return new_line; } /* * FormatMultipleParagraphs */ Line_Control *FormatMultipleParagraphs( Line_Control *starting ) { Line_Control *line = starting; if ( _Chain_Is_last( &line->Node ) ) { fprintf( stderr, "end of file reached when expecting text\n" ); exit_application( 0 ); } if ( line->keyword ) { line = (Line_Control *) line->Node.next; if ( _Chain_Is_last( &line->Node ) ) { fprintf( stderr, "end of file reached when expecting text\n" ); exit_application( 0 ); } } for ( ;; ) { assert( line ); if ( _Chain_Is_last( &line->Node ) ) break; if ( line->keyword ) break; if ( !strlen( line->Contents ) ) { line = DeleteLine( line ); continue; } line = FormatParagraph( line ); line = (Line_Control *) line->Node.next; } } /************************************************************************* ************************************************************************* ***** END OF LINE MANIPULATION ROUTINES ***** ************************************************************************* *************************************************************************/ /* * CountKeywordOccurrences */ int CountKeywordOccurrences( Keyword_indices_t keyword ) { int count = 0; Line_Control *line; for ( line = (Line_Control *) Lines.first ; !_Chain_Is_last( &line->Node ) ; line = (Line_Control *) line->Node.next ) { if ( line->keyword == keyword ) count += 1; } return count; } /* * main */ int main( int argc, char **argv ) { int c; int index; boolean single_file_mode; OutFile = stdout; Verbose = FALSE; Statistics = FALSE; OutputWord = FALSE; IncompletesAreErrors = TRUE; InsertTBDs = FALSE; while ((c = getopt(argc, argv, "istvw?n:p:u:")) != EOF) { switch (c) { case 'i': IncompletesAreErrors = FALSE; break; case 't': InsertTBDs = TRUE; break; case 'v': Verbose = TRUE; break; case 'n': strcpy( DocsNextNode, optarg ); break; case 'p': strcpy( DocsPreviousNode, optarg ); break; case 'u': strcpy( DocsUpNode, optarg ); break; case 's': Statistics = TRUE; break; case 'w': OutputWord = TRUE; break; case '?': usage(); return 0; } } if ( Verbose ) { fprintf( stderr, "Arguments successfully parsed\n" ); fprintf( stderr, "Next Node = (%s)\n", DocsNextNode ); fprintf( stderr, "Previous Node = (%s)\n", DocsPreviousNode ); fprintf( stderr, "Up Node = (%s)\n", DocsUpNode ); fprintf( stderr, "Output Format = (%s)\n", (OutputWord) ? "Word" : "Texi" ); } FillLinePool(); for ( index=optind ; index < argc ; index++ ) { ProcessFile( argv[ index ], NULL ); } if ( Verbose ) fprintf( stderr, "Exitting\n" ); return 0; } /* * ProcessFile */ void ProcessFile( char *inname, char *outname ) { char out[ 256 ]; int index; int out_index; /* * Automatically generate the output file name. */ if ( outname == NULL ) { out_index = 0; for( index=0 ; inname[index] && inname[index] != '.' ; index++ ) { /* if ( inname[ index ] != '_' ) */ out[ out_index++ ] = inname[ index ]; } if ( OutputWord ) { out[ out_index++ ] = '.'; out[ out_index++ ] = 't'; out[ out_index++ ] = 'x'; out[ out_index++ ] = 't'; } else { out[ out_index++ ] = '.'; out[ out_index++ ] = 't'; out[ out_index++ ] = 'e'; out[ out_index++ ] = 'x'; out[ out_index++ ] = 'i'; } out[ out_index ] = '\0'; } /* * Read the file into our internal data structure */ if ( Verbose ) printf( "Processing (%s) -> (%s)\n", inname, out ); ReadFileIntoChain( inname ); if ( Verbose ) fprintf( stderr, "-------->FILE READ IN\n" ); /* * Remove any spaces before the keyword and mark each keyword line as * such. Also remove extra white space at the end of lines. */ StripBlanks(); if ( Verbose ) fprintf( stderr, "-------->BLANKS BEFORE KEYWORDS STRIPPED\n" ); /* * Count the number of each type of thing */ NumberOfAttributes = CountKeywordOccurrences( ATTRIBUTE ); NumberOfAssociations = CountKeywordOccurrences( ASSOCIATION ); NumberOfAbstractTypes = CountKeywordOccurrences( ABSTRACT_TYPE ); NumberOfDataItems = CountKeywordOccurrences( DATA_ITEM ); NumberOfMethods = CountKeywordOccurrences( METHOD ); NumberOfTasks = CountKeywordOccurrences( TASK ); if ( Verbose || Statistics ) { fprintf( stderr, "NUMBER OF ATTRIBUTES = %d\n", NumberOfAttributes ); fprintf( stderr, "NUMBER OF ASSOCIATIONS = %d\n", NumberOfAssociations ); fprintf( stderr, "NUMBER OF ABSTRACT TYPES = %d\n", NumberOfAbstractTypes); fprintf( stderr, "NUMBER OF DATA ITEMS = %d\n", NumberOfDataItems ); fprintf( stderr, "NUMBER OF METHODS = %d\n", NumberOfMethods ); fprintf( stderr, "NUMBER OF TASKS = %d\n", NumberOfTasks ); } /* * 1. Merge paragraphs entered as multiple lines into a single node * on the chain. * 2. Clean up "colon lines". */ if ( MergeText() == -1 ) exit_application( 1 ); if ( Verbose ) fprintf( stderr, "-------->PARAGRAPHS MERGED\n" ); RemovePagebreaks(); /* * Remove extraneous white space */ if ( Verbose ) fprintf( stderr, "-------->PAGEBREAKS REMOVED\n" ); /* * At this point, the only unmarked lines should be empty or errors. * This deletes empty unmarked lines and flags the others as errors. */ if ( RemoveExtraBlankLines() == -1 ) exit_application( 1 ); if ( Verbose ) fprintf( stderr, "-------->EXTRA BLANK LINES REMOVED\n" ); /* * Now report which lines do not appear to be complete */ if ( CheckForIncomplete() == -1 ) exit_application( 1 ); if ( Verbose ) fprintf( stderr, "-------->CHECKED FOR INCOMPLETE KEYWORDS\n" ); /* * Check that all the required sections are present. */ if ( CheckOutline() == -1 ) exit_application( 1 ); if ( Verbose ) fprintf( stderr, "-------->CHECKED FOR HIGH LEVEL SECTION PROBLEMS\n" ); /* * Check for problems in each section. */ if ( CheckSections() == -1 ) exit_application( 1 ); if ( Verbose ) fprintf( stderr, "-------->CHECKED FOR INTERNAL SECTION PROBLEMS\n" ); /* * Look for Internal Consistencies */ if ( Verbose ) fprintf( stderr, "-------->LOOKING FOR ERRORS\n" ); LookForInternalInconsistencies(); /* * Formatting the file */ if ( OutputWord ) { FormatToWord(); if ( Verbose ) fprintf( stderr, "-------->FILE FORMATTED TO WORD\n" ); } else { FormatToTexinfo(); if ( Verbose ) fprintf( stderr, "-------->FILE FORMATTED TO TEXINFO\n" ); } /* * Print the file */ PrintFile( out ); if ( Verbose ) fprintf( stderr, "-------->FILE PRINTED\n" ); /* * Clean Up */ ReleaseFile(); if ( Verbose ) fprintf( stderr, "-------->FILE RELEASED\n" ); } /* * LookForInternalInconsistencies * * This routine looks for lines with no formatting information and * lines with too much formatting information. */ void LookForInternalInconsistencies( void ) { Line_Control *line; int i; int errors = 0; char *src; for ( line = (Line_Control *) Lines.first ; !_Chain_Is_last( &line->Node ) ; line = (Line_Control *)line->Node.next ) { if ( !line->keyword && line->format == NO_EXTRA_FORMATTING_INFO ) { fprintf( stderr, "\nLine %d has no keyword or formatting:\n", line->number ); PrintSurroundingLines( line, 3, 3 ); errors++; } } if ( errors ) exit_application( 1 ); } /* * usage */ void usage( void ) { int index; for ( index=0 ; strcmp( Usage_Strings[ index ], "EOF" ) ; index++ ) fprintf( stderr, Usage_Strings[ index ] ); } /* * ReadFileIntoChain */ void ReadFileIntoChain( char *inname ) { FILE *InFile; int line_count; int max_length; char *line; char Buffer[ BUFFER_SIZE ]; Line_Control *new_line; InFile = fopen( inname, "r" ); if ( !InFile ) { fprintf( stderr, "Unable to open (%s)\n", inname ); exit( 1 ); } assert( InFile ); max_length = 0; line_count = 0; _Chain_Initialize_empty( &Lines ); for ( ;; ) { line = fgets( Buffer, BUFFER_SIZE, InFile ); if ( !line ) break; Buffer[ strlen( Buffer ) - 1 ] = '\0'; new_line = AllocateLine(); strcpy( new_line->Contents, Buffer ); new_line->number = ++line_count; _Chain_Append( &Lines, &new_line->Node ); } fclose( InFile ); } /* * StripBlanks */ void StripBlanks( void ) { Line_Control *line; Keyword_indices_t index; int indentation; int length; Keyword_info_t *key; for ( line = (Line_Control *) Lines.first ; !_Chain_Is_last( &line->Node ) ; line = (Line_Control *) line->Node.next ) { /* * Strip white space from the end of each line */ length = strlen( line->Contents ); while ( isspace( line->Contents[ --length ] ) ) line->Contents[ length ] = '\0'; /* * Strip white space from the front of each keyword line. */ for ( index=KEYWORD_FIRST ; index <= KEYWORD_LAST ; index++ ) { key = &Keywords[ index ]; switch ( key->level ) { case SECTION: indentation = 0; break; case SUBSECTION: indentation = 0; break; case HEADING: indentation = 2; break; default: assert( FALSE ); } if ( !strncmp( &line->Contents[ indentation ], key->Name, strlen( key->Name ) ) ) { if ( indentation ) strcpy( line->Contents, &line->Contents[ indentation ] ); line->keyword = index; break; } } } } /* * CrunchRight */ boolean CrunchRight( Line_Control *line ) { char Buffer[ PARAGRAPH_SIZE ]; char *src; char *dest; boolean is_text_to_right = FALSE; src = line->Contents; dest = Buffer; while( *src != ':' ) { if ( !*src ) { fprintf( stderr, "Line %d should have had a colon\n", line->number ); exit_application( 1 ); } *dest++ = *src++; } *dest++ = *src++; /* this is the ':' */ if ( *src ) { *dest++ = ' '; while ( *src && isspace( *src ) ) src++; if ( *src ) is_text_to_right = TRUE; while ( *src ) *dest++ = *src++; } *dest = '\0'; strcpy( line->Contents, Buffer ); return is_text_to_right; } /* * CrunchBelow */ void CrunchBelow( Line_Control *start_line, Keywords_text_below_t text_below_mode ) { Line_Control *line; int i; int leading_white; int length; switch ( text_below_mode ) { case BL_FORBIDDEN: assert( FALSE ); case BL_FORMATTED: (void) FormatMultipleParagraphs( start_line ); break; case BL_RAW: for ( line = (Line_Control *) start_line->Node.next; !_Chain_Is_last( &line->Node ) ; ) { if ( strlen( line->Contents ) ) break; line = DeleteLine( line ); } for ( i=0 ; isspace( line->Contents[ i ] ) ; i++ ) ; leading_white = i; for ( ; !_Chain_Is_last( &line->Node ) ; ) { assert( line ); if ( line->keyword ) break; assert( !_Chain_Is_last( &line->Node ) ); length = strlen( line->Contents ); if ( length ) { if ( length < leading_white ) { fprintf( stderr, "\nLine %d %s:\n", line->number, "has inconsistent spacing -- is too short -- see 1st line of text" ); PrintSurroundingLines( line, 0, 0 ); fprintf( stderr, "\n" ); } for ( i=0 ; i < leading_white ; i++ ) { if ( !isspace( line->Contents[ i ] ) ) { fprintf( stderr, "\nLine %d %s:\n", line->number, "has inconsistent spacing -- see 1st line of text" ); PrintSurroundingLines( line, 0, 0 ); fprintf( stderr, "\n" ); break; } } strcpy( line->Contents, &line->Contents[ leading_white ] ); SetLineFormat( line, RAW_OUTPUT ); line = (Line_Control *) line->Node.next; } else { SetLineFormat( line, RAW_OUTPUT ); /* line = DeleteLine( line ); */ line = (Line_Control *) line->Node.next; } } break; } } /* * MergeText */ /* XXX expand this and address the error numbers */ char *Format_Errors[] = { /* unused */ "no formatting error", /* RT_NO_CHECK */ "why am I here", /* RT_FORBIDDEN */ "no text allowed to right or below", /* RT_OPTIONAL */ "text to right optional -- none below", /* RT_NONE */ "text to right is \"none\" or nothing -- none below", /* RT_REQUIRED */ "text to right required -- none below", /* RT_BELOW */ "text to right forbidden -- text below required", /* RT_NONE_OR_BELOW */ "text to right is \"none\" OR there is text below", /* RT_MAYBE_BELOW */ "text to right required -- text below optional", /* RT_EITHER */ "text to right OR below", /* RT_BOTH */ "text to right AND below" }; int MergeText( void ) { Line_Control *line; Line_Control *next; Line_Control *new_line; Keyword_info_t *key; boolean is_text_to_right; boolean is_text_below; boolean add_text_to_right; boolean add_text_below; int errors = 0; int error_code = 0; for ( line = (Line_Control *) Lines.first ; !_Chain_Is_last( &line->Node ) ; ) { if ( error_code ) { fprintf( stderr, "\nLine %d %s:\n", line->number, Format_Errors[ error_code ] ); PrintSurroundingLines( line, 0, 3 ); fprintf( stderr, "\n" ); error_code = 0; line = (Line_Control *) line->Node.next; } key = &Keywords[ line->keyword ]; if ( !line->keyword || key->text_mode == RT_NO_CHECK ) { line = (Line_Control *) line->Node.next; continue; } /* * Figure out where the text is for this keyword. It is a pretty * ugly thing to look ahead for text below because of intermediate * blank lines which we are not allowed to remove. */ add_text_to_right = FALSE; add_text_below = FALSE; is_text_to_right = CrunchRight( line ); is_text_below = FALSE; for ( next = (Line_Control *) line->Node.next ; !_Chain_Is_last( &next->Node ) ; next = (Line_Control *) next->Node.next ) { if ( next->keyword ) break; if ( strlen( next->Contents ) ) { is_text_below = TRUE; break; } } switch ( key->text_mode ) { case RT_NO_CHECK: /* no requirements */ break; case RT_FORBIDDEN: /* no text to right or below allowed */ if ( is_text_to_right || is_text_below ) { error_code = 1; errors++; continue; } break; case RT_OPTIONAL: /* text to right optional -- none below */ if ( is_text_below ) { error_code = 2; errors++; continue; } break; case RT_NONE: /* text to right is "none" or nothing -- none below */ if ( (is_text_to_right && !strstr( line->Contents, "none" )) || is_text_below ) { error_code = 3; errors++; continue; } break; case RT_REQUIRED: /* text to right required -- none below */ if ( is_text_below ) { error_code = 4; errors++; continue; } if ( !is_text_to_right ) { if ( !InsertTBDs ) { error_code = 4; errors++; continue; } else add_text_to_right = TRUE; } break; case RT_BELOW: /* text to right forbidden -- text below required */ if ( is_text_to_right ) { error_code = 5; errors++; continue; } if ( !is_text_below ) { if ( !InsertTBDs ) { error_code = 5; errors++; continue; } else add_text_below = TRUE; } break; case RT_NONE_OR_BELOW: /* text to right is "none" OR text below */ if ( is_text_to_right ) { if ( strstr( line->Contents, "none" ) ) break; error_code = 6; errors++; continue; } if ( !is_text_below ) { if ( !InsertTBDs ) { error_code = 6; errors++; continue; } else add_text_to_right = TRUE; } break; /* text to right required -- text below optional */ case RT_MAYBE_BELOW: if ( !is_text_to_right ) { error_code = 7; errors++; continue; } break; case RT_EITHER: /* text to right OR below */ if ( !(is_text_to_right || is_text_below) ) { if ( !InsertTBDs ) { error_code = 8; errors++; continue; } else add_text_to_right = TRUE; } break; case RT_BOTH: /* text to right AND below */ if ( !(is_text_to_right && is_text_below) ) { if ( !InsertTBDs ) { error_code = 9; errors++; continue; } else { add_text_to_right = TRUE; add_text_below = TRUE; } } break; } if ( add_text_to_right ) strcat( line->Contents, " TBD" ); if ( add_text_below ) { new_line = AllocateLine(); strcpy( new_line->Contents, "TBD" ); _Chain_Insert( &line->Node, &new_line->Node ); is_text_below = TRUE; } if ( is_text_below ) CrunchBelow( line, key->text_below_mode ); line = (Line_Control *) line->Node.next; } return (errors) ? -1 : 0; } /* * CheckForIncomplete */ char *IncompleteMarkers[] = { "??", "xxx", "XXX", "xyz", "XYZ" }; int CheckForIncomplete() { Line_Control *line; int i; int errors = 0; for ( line = (Line_Control *) Lines.first ; !_Chain_Is_last( &line->Node ) ; line = (Line_Control *) line->Node.next ) { for ( i=0 ; i < NUMBER_ELEMENTS( IncompleteMarkers ) ; i++ ) { if ( !strstr( line->Contents, IncompleteMarkers[ i ] ) ) continue; fprintf( stderr, "\nLine %d %s:\n", line->number, "appears to be incomplete" ); PrintSurroundingLines( line, 0, 0 ); fprintf( stderr, "\n" ); if ( IncompletesAreErrors ) errors++; break; } } return ( errors ) ? -1 : 0; } /* * CheckOutline * * Insure all section headers are present. * Check sections which say "none", really have subsections. * Check sections which have subsections, don't say none. */ int CheckOutline( void ) { Line_Control *line; int count; int errors = 0; void *none_present; boolean KeywordsPresent[ KEYWORD_LAST + 1 ]; Keyword_indices_t keyword; for ( keyword=0 ; keyword <= KEYWORD_LAST ; keyword++ ) KeywordsPresent[ keyword ] = FALSE; for ( line = (Line_Control *) Lines.first ; !_Chain_Is_last( &line->Node ) ; line = (Line_Control *) line->Node.next ) { KeywordsPresent[ line->keyword ] = TRUE; switch ( line->keyword ) { case ATTRIBUTE_DESCRIPTIONS: count = NumberOfAttributes; break; case ASSOCIATION_DESCRIPTIONS: count = NumberOfAssociations; break; case ABSTRACT_TYPE_DESCRIPTIONS: count = NumberOfAbstractTypes; break; case DATA_ITEM_DESCRIPTIONS: count = NumberOfDataItems; break; case METHOD_DESCRIPTIONS: count = NumberOfMethods; break; case TASK_DESCRIPTIONS: count = NumberOfTasks; break; default: count = -1; break; } if ( count == -1 ) continue; none_present = strstr( line->Contents, "none" ); /* valid cases are (none_present && !count) AND (!none_present && count) */ if ( none_present && count ) { fprintf( stderr, "\nLine %d : %s\n", line->number, "section header says \"none\" and there are subsections" ); PrintSurroundingLines( line, 0, 0 ); fprintf( stderr, "\n" ); errors++; } if ( !none_present && !count ) { fprintf( stderr, "\nLine %d : %s\n", line->number, "section header does not say \"none\" and there are no subsections" ); PrintSurroundingLines( line, 0, 0 ); fprintf( stderr, "\n" ); errors++; } } for ( keyword=0 ; keyword <= KEYWORD_LAST ; keyword++ ) { if ( Keywords[ keyword ].level != SECTION ) continue; if ( !KeywordsPresent[ keyword ] ) { fprintf( stderr, "Section (%s) is missing\n", Keywords[ keyword ].Name ); errors++; } } return (errors) ? -1 : 0; } /* * ReportMissingKeywords */ int ReportMissingKeywords( Section_info_t *section, Line_Control *line, int elements_present[ MAXIMUM_ELEMENTS ] ) { int i; int errors = 0; int last_found; #ifdef SECTION_DEBUG PrintLine( line ); for ( i=0 ; i < MAXIMUM_ELEMENTS ; i++ ) fprintf( stderr, "%d ", elements_present[ i ] ); fprintf( stderr, "\n" ); #endif /* * Check for missing sections */ for ( i=0 ; i < MAXIMUM_ELEMENTS ; i++ ) { if ( section->Description[ i ].keyword == UNUSED ) break; #ifdef SECTION_DEBUG fprintf( stderr, "%d %d %s\n", section->Description[ i ].is_required, elements_present[ i ], Keywords[ section->Description[ i ].keyword ].Name ); #endif if ( section->Description[ i ].is_required && elements_present[i] == -1 ) { fprintf( stderr, "Section starting at line %d is missing the %s keyword\n", line->number, Keywords[ section->Description[ i ].keyword ].Name ); errors++; } } last_found = -1; for ( i=0 ; i < MAXIMUM_ELEMENTS ; i++ ) { if ( section->Description[ i ].keyword == UNUSED ) break; if ( elements_present[i] == -1 ) continue; if ( elements_present[i] > last_found ) { last_found = elements_present[i]; continue; } fprintf( stderr, "Keywords in the section starting at line %d are in the wrong order\n", line->number ); errors++; break; } return errors; } /* * ScanForNextSection */ Line_Control *ScanForNextSection( Line_Control *start_line, Section_info_t *section ) { Line_Control *line; Element_info_t *Elements; line = start_line; Elements = section->Description; for ( ;; ) { line = (Line_Control *) line->Node.next; if ( line->keyword == END_OBJECT ) break; assert( line ); assert( !_Chain_Is_last( &line->Node ) ); if ( Keywords[ line->keyword ].level == SECTION && section->Next_section != line->keyword ) { fprintf( stderr, "Sections appear to be out of order at line %d\n", start_line->number ); return NULL; } if ( section->Next_section == line->keyword ) /* next section */ break; if ( Elements[ 0 ].keyword == line->keyword ) /* repeating subsection */ break; } return line; } /* * CheckSections */ int CheckSections( void ) { Line_Control *line; Line_Control *scan_line; Section_info_t *section; Keyword_info_t *key; Element_info_t *Elements; int elements_present[ MAXIMUM_ELEMENTS ]; boolean stay_in_subsection; int i; int errors; int subsection; int element; int keyword_count; Line_Control *next_section; errors = 0; line = (Line_Control *) Lines.first; for ( i=0 ; i < NUMBER_ELEMENTS( Sections ) ; i++ ) { section = Sections[ i ]; Elements = section->Description; subsection = 0; if ( strstr( line->Contents, "none" ) ) { next_section = ScanForNextSection( line, section ); if ( !next_section ) { errors++; goto end_object; } line = next_section; if ( line->keyword == END_OBJECT ) goto end_object; continue; } while ( Elements[ 0 ].keyword != line->keyword ) { if ( line->keyword == END_OBJECT ) goto end_object; if( !line || _Chain_Is_last( &line->Node ) ) { fprintf( stderr, "out of sync -- aborting\n" ); errors++; goto end_object; } line = (Line_Control *) line->Node.next; } repeating_subsection: #ifdef SECTION_DEBUG fprintf( stderr, "Checking Section %d Subsection %d\n", i, subsection ); #endif assert( line ); assert( !_Chain_Is_last( &line->Node ) ); /* * Per Subsection Initialization */ subsection++; keyword_count = 0; next_section = ScanForNextSection( line, section ); if ( !next_section ) { errors++; goto end_object; } for ( element=0 ; element < MAXIMUM_ELEMENTS ; element++ ) elements_present[ element ] = -1; /* * Do a subsection */ /* validate each keyword */ for ( scan_line = line ; ; ) { if ( !scan_line->keyword ) { scan_line = (Line_Control *) scan_line->Node.next; continue; } if ( scan_line == next_section ) break; if ( *Keywords[ scan_line->keyword ].keyword_validation_routine ) { if ( (*Keywords[ scan_line->keyword ].keyword_validation_routine )( scan_line ) ) errors++; } scan_line = (Line_Control *) scan_line->Node.next; } /* scan subsection for keywords */ for ( scan_line = line ; ; ) { if ( !scan_line->keyword ) { scan_line = (Line_Control *) scan_line->Node.next; continue; } if ( scan_line == next_section ) break; for ( element=0 ; element < MAXIMUM_ELEMENTS ; element++ ) { if ( scan_line->keyword == Elements[ element ].keyword ) { if ( elements_present[ element ] != -1 ) { fprintf( stderr, "Section starting at line %d has the %s keyword more than once\n", line->number, Keywords[ Elements[ i ].keyword ].Name ); errors++; } #ifdef SECTION_DEBUG fprintf( stderr, "FOUND %s %d %d\n", Keywords[ Elements[ element ].keyword ].Name, element, keyword_count ); #endif elements_present[ element ] = keyword_count++; break; } if ( Elements[ element ].keyword == UNUSED ) break; } scan_line = (Line_Control *) scan_line->Node.next; } errors += ReportMissingKeywords( section, line, elements_present ); /* * Validate the section as a whole */ if ( section->section_validation_routine ) { if ( (*section->section_validation_routine)(section, line, next_section) ) errors++; } scan_for_next_section: /* * Scan forward until next subsection or next section */ #ifdef SECTION_DEBUG fprintf( stderr, "End of Subsection\n" ); #endif line = next_section; if ( line->keyword == END_OBJECT ) { goto end_object; } if ( Elements[ 0 ].keyword == line->keyword ) { /* repeating subsection */ if ( !section->repeats ) { fprintf( stderr, "Invalid repeating subsection starting at line %d\n", line->number ); errors++; goto end_object; } goto repeating_subsection; } } end_object: return (errors) ? -1 : 0; } /* * RemovePagebreaks */ void RemovePagebreaks() { Line_Control *line; for ( line = (Line_Control *) Lines.first ; !_Chain_Is_last( &line->Node ) ; ) { if ( !strcmp( line->Contents, PAGE_SEPARATOR ) ) line = DeleteLine( line ); else line = (Line_Control *) line->Node.next; } } /* * strIsAllSpace */ boolean strIsAllSpace( char *s ) { char *p; for ( p = s ; *p ; p++ ) if ( !isspace( *p ) ) return FALSE; return TRUE; } /* * RemoveExtraBlankLines * * NOTE: Be careful not remove to remove white space in raw text. */ int RemoveExtraBlankLines() { Line_Control *line; Line_Control *previous; int errors; errors = 0; for ( line = (Line_Control *) Lines.first ; !_Chain_Is_last( &line->Node ) ; line = (Line_Control *) line->Node.next ) { if ( line->keyword || line->format ) continue; if ( !strlen( line->Contents ) ) { line = DeleteLine( line ); line = (Line_Control *) line->Node.previous; continue; } fprintf( stderr, "\nLine %d is not associated with a keyword:\n", line->number ); PrintSurroundingLines( line, 0, 0 ); fprintf( stderr, "\n" ); errors++; } return ( errors ) ? -1 : 0; } /* * strCopyToColon */ void strCopyToColon( char *src, char *dest ) { char *s = src; char *d = dest; for ( ; *s && *s != ':' ; ) *d++ = *s++; *d ='\0'; } /* * BuildTexinfoNodes */ void BuildTexinfoNodes( void ) { Line_Control *line; Line_Control *new_line; Line_Control *next_node; char Buffer[ BUFFER_SIZE ]; char ObjectName[ BUFFER_SIZE ]; char NodeName[ BUFFER_SIZE ]; char NextNode[ BUFFER_SIZE ]; char NextNodeName[ BUFFER_SIZE ]; char PreviousNodeName[ BUFFER_SIZE ]; char UpNodeName[ BUFFER_SIZE ]; char SectionName[ BUFFER_SIZE ]; char MenuBuffer[ BUFFER_SIZE ]; Line_Control *menu_insert_point; Line_Control *node_line; boolean next_found; int menu_items; strcpy( PreviousNodeName, DocsPreviousNode ); for ( line = (Line_Control *) Lines.first ; !_Chain_Is_last( &line->Node ) ; line = (Line_Control *) line->Node.next ) { menu_insert_point = (Line_Control *) line->Node.next; switch ( Keywords[ line->keyword ].level ) { case TEXT: case HEADING: break; case SECTION: if ( line->keyword == END_OBJECT ) goto bottom; if ( line->keyword == OBJECT ) { LineCopyFromRight( line, Buffer ); sprintf( ObjectName, "%s Object", Buffer ); strcpy( NodeName, ObjectName ); strcpy( UpNodeName, DocsUpNode ); } else { LineCopySectionName( line, Buffer ); sprintf( NodeName, "%s %s", ObjectName, Buffer ); strcpy( UpNodeName, ObjectName ); } strtoInitialCaps( NULL, NodeName ); strtoInitialCaps( NULL, UpNodeName ); strcpy( SectionName, NodeName ); /* * Go ahead and put it on the chain in the right order (ahead of * the menu) and we can fill it in later (after the menu is built). */ new_line = AllocateLine(); strcpy( new_line->Contents, "@ifinfo" ); _Chain_Insert( line->Node.previous, &new_line->Node ); node_line = AllocateLine(); _Chain_Insert( line->Node.previous, &node_line->Node ); new_line = AllocateLine(); strcpy( new_line->Contents, "@end ifinfo" ); _Chain_Insert( line->Node.previous, &new_line->Node ); menu_items = 0; if ( line->keyword == OBJECT ) { next_node = (Line_Control *) line->Node.next; next_found = FALSE; for ( ; ; ) { if ( next_node->keyword == END_OBJECT ) break; if ( Keywords[ next_node->keyword ].level == SECTION ) { LineCopySectionName( next_node, Buffer ); strtoInitialCaps( NULL, Buffer ); if ( !next_found ) { next_found = TRUE; sprintf( NextNodeName, "%s %s", ObjectName, Buffer ); } if ( menu_items == 0 ) { new_line = AllocateLine(); strcpy( new_line->Contents, "@ifinfo" ); _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node ); new_line = AllocateLine(); strcpy( new_line->Contents, "@menu" ); _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node ); } menu_items++; new_line = AllocateLine(); sprintf( new_line->Contents, "* %s %s::", ObjectName, Buffer ); _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node); } next_node = (Line_Control *) next_node->Node.next; } } else { next_node = (Line_Control *) line->Node.next; next_found = FALSE; for ( ; ; ) { if ( Keywords[ next_node->keyword ].level == SECTION ) { if ( !next_found ) { if ( next_node->keyword == END_OBJECT ) { strcpy( NextNodeName, DocsNextNode ); } else { LineCopySectionName( next_node, Buffer ); sprintf( NextNodeName, "%s %s", ObjectName, Buffer ); strtoInitialCaps( NULL, NextNodeName ); } next_found = TRUE; } break; } else if ( Keywords[ next_node->keyword ].level == SUBSECTION ) { LineCopySectionName( next_node, Buffer ); strtoInitialCaps( NULL, Buffer ); sprintf( MenuBuffer, "%s %s - ", ObjectName, Buffer ); LineCopyFromRight( next_node, Buffer ); strcat( MenuBuffer, Buffer ); if ( menu_items == 0 ) { new_line = AllocateLine(); strcpy( new_line->Contents, "@ifinfo" ); _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node ); new_line = AllocateLine(); strcpy( new_line->Contents, "@menu" ); _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node ); } menu_items++; new_line = AllocateLine(); sprintf( new_line->Contents, "* %s::", MenuBuffer ); _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node ); if ( !next_found ) { next_found = TRUE; strcpy( NextNodeName, MenuBuffer ); } } next_node = (Line_Control *) next_node->Node.next; } } if ( menu_items ) { new_line = AllocateLine(); strcpy( new_line->Contents, "@end menu" ); _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node ); new_line = AllocateLine(); strcpy( new_line->Contents, "@end ifinfo" ); _Chain_Insert( menu_insert_point->Node.previous, &new_line->Node ); } #if 0 fprintf( stderr, "@node %s, %s, %s, %s\n", NodeName, NextNodeName, PreviousNodeName, UpNodeName ); #endif /* node_line was previously inserted */ sprintf( node_line->Contents, "@node %s, %s, %s, %s", NodeName, NextNodeName, PreviousNodeName, UpNodeName ); strcpy( PreviousNodeName, NodeName ); break; case SUBSECTION: strcpy( UpNodeName, SectionName ); LineCopySectionName( line, Buffer ); strtoInitialCaps( NULL, Buffer ); sprintf( NodeName, "%s %s - ", ObjectName, Buffer ); LineCopyFromRight( line, Buffer ); strcat( NodeName, Buffer ); new_line = AllocateLine(); strcpy( new_line->Contents, "@ifinfo" ); _Chain_Insert( line->Node.previous, &new_line->Node ); next_node = (Line_Control *) line->Node.next; for ( ; ; ) { if ( Keywords[ next_node->keyword ].level == SECTION ) { if ( next_node->keyword == END_OBJECT ) { strcpy( NextNodeName, DocsNextNode ); } else { LineCopySectionName( next_node, Buffer ); sprintf( NextNodeName, "%s %s", ObjectName, Buffer ); strtoInitialCaps( NULL, NextNodeName ); } break; } else if ( Keywords[ next_node->keyword ].level == SUBSECTION ) { LineCopySectionName( next_node, Buffer ); strtoInitialCaps( NULL, Buffer ); sprintf( NextNodeName, "%s %s - ", ObjectName, Buffer ); LineCopyFromRight( next_node, Buffer ); strcat( NextNodeName, Buffer ); break; } next_node = (Line_Control *) next_node->Node.next; } #if 0 fprintf( stderr, "@node %s, %s, %s, %s\n", NodeName, NextNodeName, PreviousNodeName, UpNodeName ); #endif new_line = AllocateLine(); sprintf( new_line->Contents, "@node %s, %s, %s, %s", NodeName, NextNodeName, PreviousNodeName, UpNodeName ); _Chain_Insert( line->Node.previous, &new_line->Node ); new_line = AllocateLine(); strcpy( new_line->Contents, "@end ifinfo" ); _Chain_Insert( line->Node.previous, &new_line->Node ); strcpy( PreviousNodeName, NodeName ); break; } } bottom: } /* * FormatToTexinfo */ char *Texinfo_Headers[] = { "\\input texinfo @c -*-texinfo-*-", "@c %**start of header", "@setfilename ", "@settitle ", "@paragraphindent 0", "@c %**end of header", "", "@c", "@c COPYRIGHT (c) 1996.", "@c On-Line Applications Research Corporation (OAR).", "@c All rights reserved.", "@c", "@c This file is automatically generated. DO NOT EDIT!!", "@c", "", "@c This prevents a black box from being printed on overfull lines.", "@c The alternative is to rework a sentence to avoid this problem.", "@finalout", "", "@tex", "\\global\\parindent 0in", "\\global\\chapheadingskip = 15pt plus 4pt minus 2pt", "\\global\\secheadingskip = 12pt plus 4pt minus 2pt", "\\global\\subsecheadingskip = 9pt plus 4pt minus 2pt", "", "@ifclear smallbook", "\\global\\parskip 6pt plus 1pt", "@end ifclear", "@end tex", "@setchapternewpage odd", "@ifinfo", "@top ", "@node Top, (dir), (dir), (dir)", "@end ifinfo", "@c ", "@c This is the end of the header block", "@c " }; void FormatToTexinfo( void ) { Line_Control *line; Line_Control *new_line; char Buffer[ PARAGRAPH_SIZE ]; int i; char ChapterTitle[ PARAGRAPH_SIZE ]; char InfoFile[ PARAGRAPH_SIZE ]; char *p; boolean new_section; boolean in_bullets; for ( line = (Line_Control *) Lines.first ; !_Chain_Is_last( &line->Node ) ; ) { switch ( line->keyword ) { case UNUSED: line = (Line_Control *) line->Node.next; break; case OBJECT: LineCopyFromRight( line, ChapterTitle ); strcpy( InfoFile, ChapterTitle ); for ( p=InfoFile ; *p ; *p++ ) /* turn this into a file name */ if ( isspace( *p ) ) *p = '_'; sprintf( Buffer, "@chapter %s", line->Contents ); strcpy( line->Contents, Buffer ); line = (Line_Control *) line->Node.next; break; case ATTRIBUTE_DESCRIPTIONS: case ASSOCIATION_DESCRIPTIONS: case ABSTRACT_TYPE_DESCRIPTIONS: case DATA_ITEM_DESCRIPTIONS: case METHOD_DESCRIPTIONS: case TASK_DESCRIPTIONS: sprintf( Buffer, "@section %s", line->Contents ); strcpy( line->Contents, Buffer ); new_line = AllocateLine(); strcpy( new_line->Contents, "@page" ); _Chain_Insert( line->Node.previous, &new_line->Node ); line = (Line_Control *) line->Node.next; new_section = TRUE; break; case END_OBJECT: line->Contents[ 0 ] = '\0'; goto bottom; case ATTRIBUTE: case ASSOCIATION: case ABSTRACT_TYPE: case DATA_ITEM: if ( !new_section ) { new_line = AllocateLine(); strcpy( new_line->Contents, "@need 4000" ); _Chain_Insert( line->Node.previous, &new_line->Node ); } new_section = FALSE; sprintf( Buffer, "@subsection %s", line->Contents ); strcpy( line->Contents, Buffer ); line = (Line_Control *) line->Node.next; break; case METHOD: case TASK: if ( !new_section ) { new_line = AllocateLine(); strcpy( new_line->Contents, "@page" ); _Chain_Insert( line->Node.previous, &new_line->Node ); } new_section = FALSE; sprintf( Buffer, "@subsection %s", line->Contents ); strcpy( line->Contents, Buffer ); line = (Line_Control *) line->Node.next; break; case DESCRIPTION: case COPYRIGHT: case PORTING: case THEORY_OF_OPERATION: case DEPENDENCIES: case NOTES: sprintf( Buffer, "@subheading %s\n", line->Contents ); strcpy( line->Contents, Buffer ); line = (Line_Control *) line->Node.next; /* now take care of the paragraphs which are here */ in_bullets = FALSE; do { line = (Line_Control *) line->Node.next; if ( line->format == BULLET_OUTPUT ) { if ( !in_bullets ) { in_bullets = TRUE; new_line = AllocateLine(); _Chain_Insert( line->Node.previous, &new_line->Node ); new_line = AllocateLine(); strcpy( new_line->Contents, "@itemize @bullet" ); _Chain_Insert( line->Node.previous, &new_line->Node ); } sprintf( Buffer, "@item %s\n", line->Contents ); strcpy( line->Contents, Buffer ); } else if ( in_bullets ) { in_bullets = FALSE; new_line = AllocateLine(); strcpy( new_line->Contents, "@end itemize" ); _Chain_Insert( line->Node.previous, &new_line->Node ); new_line = AllocateLine(); _Chain_Insert( line->Node.previous, &new_line->Node ); } else { new_line = AllocateLine(); _Chain_Insert( line->Node.previous, &new_line->Node ); } } while ( !line->keyword ); break; case DERIVATION: case TYPE: case RANGE: case UNITS: case SCALE_FACTOR: case TOLERANCE: case VISIBILITY: case ASSOCIATED_WITH: case MULTIPLICITY: case TIMING: sprintf( Buffer, "@subheading %s\n", line->Contents ); strcpy( line->Contents, Buffer ); line = (Line_Control *) line->Node.next; break; case MEMBERS: case DEFAULT: case REQUIREMENTS: case REFERENCES: case INPUTS: case OUTPUTS: case PDL: case SYNCHRONIZATION: sprintf( Buffer, "@subheading %s\n", line->Contents ); strcpy( line->Contents, Buffer ); /* now take care of the raw text which is here */ new_line = AllocateLine(); strcpy( new_line->Contents, "@example" ); _Chain_Insert( &line->Node, &new_line->Node ); do { line = (Line_Control *) line->Node.next; if ( !strlen( line->Contents ) ) { new_line = AllocateLine(); strcpy( new_line->Contents, "@end example" ); _Chain_Insert( line->Node.previous, &new_line->Node ); new_line = AllocateLine(); strcpy( new_line->Contents, "@example" ); _Chain_Insert( &line->Node, &new_line->Node ); } } while ( !line->keyword ); new_line = AllocateLine(); strcpy( new_line->Contents, "@end example" ); _Chain_Insert( line->Node.previous, &new_line->Node ); /* at this point line points to the next keyword */ break; case RAW_EXAMPLE: /* now take care of the raw text which is here */ new_line = AllocateLine(); strcpy( new_line->Contents, "@example" ); _Chain_Insert( line->Node.previous, &new_line->Node ); while ( line->keyword == RAW_EXAMPLE ) { line = (Line_Control *) line->Node.next; } new_line = AllocateLine(); strcpy( new_line->Contents, "@end example" ); _Chain_Insert( line->Node.previous, &new_line->Node ); /* at this point line points to the next keyword */ break; } } bottom: #if 0 for ( i=NUMBER_ELEMENTS( Texinfo_Headers ) - 1 ; i >= 0 ; i-- ) { new_line = AllocateLine(); strcpy( new_line->Contents, Texinfo_Headers[ i ] ); if ( !strcmp( "@setfilename ", new_line->Contents ) ) strcat( new_line->Contents, ChapterTitle ); else if ( !strcmp( "@settitle ", new_line->Contents ) ) strcat( new_line->Contents, InfoFile ); else if ( !strcmp( "@top ", new_line->Contents ) ) strcat( new_line->Contents, InfoFile ); _Chain_Insert( _Chain_Head( &Lines ), &new_line->Node ); } /* * Remove the special end of object string. No one wants to see * it in the printed output and the node is already marked "END_OBJECT". */ ((Line_Control *)Lines.last)->Contents[ 0 ] = '\0'; new_line = AllocateLine(); strcpy( new_line->Contents, "@bye" ); _Chain_Append( &Lines, &new_line->Node ); #endif if ( Verbose ) fprintf( stderr, "-------->INSERTING TEXINFO MENUS\n" ); BuildTexinfoNodes(); } /* * FormatToWord */ void FormatToWord( void ) { Line_Control *line; Line_Control *new_line; char Buffer[ PARAGRAPH_SIZE ]; int i; int length; char ChapterTitle[ PARAGRAPH_SIZE ]; char InfoFile[ PARAGRAPH_SIZE ]; char *p; boolean new_section; boolean in_bullets; for ( line = (Line_Control *) Lines.first ; !_Chain_Is_last( &line->Node ) ; ) { switch ( line->keyword ) { case UNUSED: line = (Line_Control *) line->Node.next; break; case OBJECT: LineCopyFromRight( line, ChapterTitle ); strcpy( InfoFile, ChapterTitle ); for ( p=InfoFile ; *p ; *p++ ) /* turn this into a file name */ if ( isspace( *p ) ) *p = '_'; sprintf( Buffer, "@Chapter = %s", line->Contents ); strcpy( line->Contents, Buffer ); line = (Line_Control *) line->Node.next; break; case ATTRIBUTE_DESCRIPTIONS: case ASSOCIATION_DESCRIPTIONS: case ABSTRACT_TYPE_DESCRIPTIONS: case DATA_ITEM_DESCRIPTIONS: case METHOD_DESCRIPTIONS: case TASK_DESCRIPTIONS: sprintf( Buffer, "@Section = %s", line->Contents ); strcpy( line->Contents, Buffer ); new_line = AllocateLine(); strcpy( new_line->Contents, "@Page" ); _Chain_Insert( line->Node.previous, &new_line->Node ); line = (Line_Control *) line->Node.next; new_section = TRUE; break; case END_OBJECT: line->Contents[ 0 ] = '\0'; goto bottom; case ATTRIBUTE: case ASSOCIATION: case ABSTRACT_TYPE: case DATA_ITEM: if ( !new_section ) { /* * Do something with the style to keep subsection * contents together */ ; } new_section = FALSE; sprintf( Buffer, "@Subsection = %s", line->Contents ); strcpy( line->Contents, Buffer ); line = (Line_Control *) line->Node.next; break; case METHOD: case TASK: if ( !new_section ) { new_line = AllocateLine(); strcpy( new_line->Contents, "@Page" ); _Chain_Insert( line->Node.previous, &new_line->Node ); } new_section = FALSE; sprintf( Buffer, "@Subsection = %s", line->Contents ); strcpy( line->Contents, Buffer ); line = (Line_Control *) line->Node.next; break; case DESCRIPTION: case COPYRIGHT: case PORTING: case THEORY_OF_OPERATION: case DEPENDENCIES: case NOTES: sprintf( Buffer, "@Subheading = %s\n", line->Contents ); strcpy( line->Contents, Buffer ); line = (Line_Control *) line->Node.next; /* now take care of the paragraphs which are here */ in_bullets = FALSE; do { line = (Line_Control *) line->Node.next; if ( line->format == BULLET_OUTPUT ) { if ( !in_bullets ) { in_bullets = TRUE; new_line = AllocateLine(); _Chain_Insert( line->Node.previous, &new_line->Node ); } sprintf( Buffer, "@Bullet = %s\n", line->Contents ); strcpy( line->Contents, Buffer ); } else if ( in_bullets ) { in_bullets = FALSE; new_line = AllocateLine(); _Chain_Insert( line->Node.previous, &new_line->Node ); } else { new_line = AllocateLine(); _Chain_Insert( line->Node.previous, &new_line->Node ); } } while ( !line->keyword ); break; case DERIVATION: case TYPE: case RANGE: case UNITS: case SCALE_FACTOR: case TOLERANCE: case VISIBILITY: case ASSOCIATED_WITH: case MULTIPLICITY: case TIMING: sprintf( Buffer, "@Subheading = %s\n", line->Contents ); strcpy( line->Contents, Buffer ); line = (Line_Control *) line->Node.next; break; case MEMBERS: case DEFAULT: case REQUIREMENTS: case REFERENCES: case INPUTS: case OUTPUTS: case PDL: case SYNCHRONIZATION: sprintf( Buffer, "@Subheading = %s\n", line->Contents ); strcpy( line->Contents, Buffer ); line = (Line_Control *) line->Node.next; /* now take care of the raw text which is here */ #if 0 while ( !line->keyword ) { sprintf( Buffer, "@Example = %s\n", line->Contents ); strcpy( line->Contents, Buffer ); line = (Line_Control *) line->Node.next; } /* at this point line points to the next keyword */ #endif /* now take care of the raw text which is here */ new_line = AllocateLine(); _Chain_Insert( line->Node.previous, &new_line->Node ); strcpy( new_line->Contents, "@Example = " ); while ( !line->keyword ) { if ( strlen( line->Contents ) ) { new_line->keyword = line->keyword; new_line->format = line->format; length = strlen(new_line->Contents); if ( (length + strlen(line->Contents) + 12) > PARAGRAPH_SIZE ) { fprintf( stderr, "Output line too long at %d\n", line->number ); exit_application( 1 ); } strcat( new_line->Contents, line->Contents ); strcat( new_line->Contents, "<@ManualCR>" ); line = DeleteLine( line ); } else { line = (Line_Control *) line->Node.next; new_line = AllocateLine(); _Chain_Insert( line->Node.previous, &new_line->Node ); strcpy( new_line->Contents, "@Example = " ); } } /* at this point line points to the next keyword */ break; case RAW_EXAMPLE: /* now take care of the raw text which is here */ new_line = AllocateLine(); _Chain_Insert( line->Node.previous, &new_line->Node ); strcpy( new_line->Contents, "@Example = " ); while ( line->keyword == RAW_EXAMPLE ) { if ( strlen( line->Contents ) ) { new_line->keyword = line->keyword; new_line->format = line->format; length = strlen(new_line->Contents); if ( (length + strlen(line->Contents) + 12) > PARAGRAPH_SIZE ) { fprintf( stderr, "Output line too long at %d\n", line->number ); exit_application( 1 ); } strcat( new_line->Contents, line->Contents ); strcat( new_line->Contents, "<@ManualCR>" ); line = DeleteLine( line ); } else { line = (Line_Control *) line->Node.next; new_line = AllocateLine(); _Chain_Insert( line->Node.previous, &new_line->Node ); strcpy( new_line->Contents, "@Example = " ); } } /* at this point line points to the next keyword */ break; } } bottom: } /* * PrintFile */ void PrintFile( char *out ) { Line_Control *line; OutFile = fopen( out, "w+" ); if ( !OutFile ) { fprintf( stderr, "Unable to open (%s) for output\n", out ); exit_application( 1 ); } assert( OutFile ); for ( line = (Line_Control *) Lines.first ; !_Chain_Is_last( &line->Node ) ; line = (Line_Control *) line->Node.next ) { fprintf( OutFile, "%s\n", line->Contents ); /* fprintf( OutFile, "(%d,%d)%s\n", line->keyword, line->format, line->Contents ); */ } } /* * DumpList */ void DumpList( Chain_Control *the_list ) { Line_Control *line; fprintf( stderr, "---> Dumping list (%p)\n", the_list ); for ( line = (Line_Control *) the_list->first ; !_Chain_Is_last( &line->Node ) ; line = (Line_Control *) line->Node.next ) { fprintf( stderr, "%s\n", line->Contents ); } } /* * ReleaseFile */ void ReleaseFile() { Line_Control *line; Line_Control *next; for ( line = (Line_Control *) Lines.first ; !_Chain_Is_last( &line->Node ) ; ) { next = (Line_Control *) line->Node.next; line = next; } } /* * strtoInitialCaps */ void strtoInitialCaps( char *dest, char *src ) { char *source = src; char *destination = dest; if ( !dest ) return; strcpy( dest, src ); #if 0 source = src; destination = (dest) ? dest : src; while ( *source ) { while ( isspace( *source ) ) *destination++ = *source++; if ( !*source ) break; *destination++ = toupper( *source++ ); for ( ; *source && !isspace( *source ) ; source++ ) *destination++ = tolower( *source ); if ( !*source ) break; } *destination = '\0'; #endif } /* * Validate_visibility */ char *Valid_visibilities[] = { "public", "private" }; int Validate_visibility( Line_Control *line ) { char *s; char *d; char Buffer[ BUFFER_SIZE ]; char Visibility[ BUFFER_SIZE ]; int i; boolean found; int errors = 0; LineCopyFromRight( line, Buffer ); memset( Visibility, '\0', sizeof( Visibility ) ); s = Buffer; for ( d=Visibility ; ; s++, d++ ) { *d = *s; if ( !*s || isspace(*s) ) break; } *d = '\0'; if ( isspace(*s) ) { fprintf( stderr, "Unexpected white space on line %d -- are there multiple words?\n", line->number ); errors++; } /* * Check out the type part of this keyword */ for ( found=FALSE, i=0 ; inumber ); errors++; } } return (errors) ? -1 : 0; } /* * Validate_synchronization */ char *Valid_synchronization[] = { "delay", "event", "mutex", "semaphore", "message", "signal", "period" }; boolean Valid_synchronization_text_below[] = { FALSE, /* delay */ TRUE, /* event */ TRUE, /* mutex */ TRUE, /* semaphore */ TRUE, /* message */ TRUE, /* signal */ FALSE /* period */ }; int Validate_synchronization( Line_Control *line ) { char *s; char *d; char Buffer[ BUFFER_SIZE ]; char Synchronization[ BUFFER_SIZE ]; int i; boolean found; int errors = 0; LineCopyFromRight( line, Buffer ); memset( Synchronization, '\0', sizeof( Synchronization ) ); s = Buffer; for ( d=Synchronization ; ; s++, d++ ) { *d = *s; if ( !*s || isspace(*s) ) break; } *d = '\0'; if ( isspace(*s) ) { fprintf( stderr, "Unexpected white space on line %d -- invalid use of multiple words\n", line->number ); errors++; } /* * Check out the type part of this keyword */ for ( found=FALSE, i=0 ; inumber ); errors++; } if ( line->keyword && !Valid_synchronization_text_below[ i ] ) { fprintf( stderr, "Expected text below synchronization type (%s) on line %d\n", Synchronization, line->number ); errors++; } return (errors) ? -1 : 0; } /* * Validate_abstract_type * * presence of range or members but not both */ int Validate_abstract_type( Section_info_t *section, Line_Control *start, Line_Control *next_section ) { boolean range_found = FALSE; boolean members_found = FALSE; boolean enumerated_found = FALSE; boolean no_range_or_members = FALSE; boolean true_found = FALSE; boolean false_found = FALSE; Line_Control *line; int errors = 0; for ( line = start; line != next_section ; line = (Line_Control *) line->Node.next ) { if ( line->keyword == RANGE ) range_found = TRUE; else if ( line->keyword == MEMBERS ) { members_found = TRUE; } else if ( line->keyword == TYPE ) { if ( strstr( line->Contents, "enumerated" ) || strstr( line->Contents, "structure" ) ) enumerated_found = TRUE; } else if ( line->keyword == DERIVATION ) { if ( strstr( line->Contents, "enumerated" ) || strstr( line->Contents, "structure" ) ) { enumerated_found = TRUE; } else if ( strstr( line->Contents, "handle" ) || strstr( line->Contents, "array" ) || strstr( line->Contents, "string" ) ) { no_range_or_members = TRUE; } } } if ( no_range_or_members ) { /* This code does not like: ABSTRACT TYPE: Times DESCRIPTION: This type specifies mode elapsed times. VISIBILITY: public DERIVATION: array of Elapsed_Seconds of Timeutil RANGE: COMM - ENGAGE of Current_mode_t if ( range_found ) { fprintf( stderr, "Range should not be specified in subsection starting at line %d\n", start->number ); errors++; } */ if ( members_found ) { fprintf( stderr, "Members should not be specified in subsection starting at line %d\n", start->number ); errors++; } } else { if ( !range_found && !members_found ) { fprintf( stderr, "Neither range nor members keyword present in " "subsection starting at line %d\n", start->number ); errors++; } if ( !enumerated_found && !range_found ) { fprintf( stderr, "Type does not have range specified in " "subsection starting at line %d\n", start->number ); errors++; } } if ( !InsertTBDs ) { if ( range_found && members_found ) { fprintf( stderr, "Both range and members keyword present in " "subsection starting at line %d\n", start->number ); errors++; } } if ( enumerated_found && !members_found ) { fprintf( stderr, "Enumerated type without list of members in " "subsection starting at line %d\n", start->number ); errors++; } return (errors) ? -1 : 0; } /* * Validate_attribute * * presence of range or members but not both */ int Validate_attribute( Section_info_t *section, Line_Control *start, Line_Control *next_section ) { boolean range_found = FALSE; boolean members_found = FALSE; boolean enumerated_found = FALSE; boolean boolean_found = FALSE; boolean true_found = FALSE; boolean false_found = FALSE; Line_Control *line; int errors = 0; for ( line = start; line != next_section ; line = (Line_Control *) line->Node.next ) { if ( line->keyword == RANGE ) range_found = TRUE; else if ( line->keyword == MEMBERS ) { members_found = TRUE; if ( boolean_found == TRUE ) { line = (Line_Control *) line->Node.next; while ( !_Chain_Is_last( &line->Node ) ) { if ( line->keyword ) break; if ( strstr( line->Contents, "FALSE" ) ) false_found = TRUE; else if ( strstr( line->Contents, "TRUE" ) ) true_found = TRUE; line = (Line_Control *) line->Node.next; } line = (Line_Control *) line->Node.previous; } } else if ( line->keyword == TYPE || line->keyword == DERIVATION ) { if ( strstr( line->Contents, "enumerated" ) ) enumerated_found = TRUE; else if ( strstr( line->Contents, "boolean" ) ) { enumerated_found = TRUE; boolean_found = TRUE; } } } if ( !range_found && !members_found ) { fprintf( stderr, "Neither range nor members keyword present in " "subsection starting at line %d\n", start->number ); errors++; } if ( !InsertTBDs ) { if ( range_found && members_found ) { fprintf( stderr, "Both range and members keyword present in " "subsection starting at line %d\n", start->number ); errors++; } } if ( enumerated_found && !members_found ) { fprintf( stderr, "Enumerated type without list of members in " "subsection starting at line %d\n", start->number ); errors++; } if ( !enumerated_found && !range_found ) { fprintf( stderr, "Type does not have range specified in " "subsection starting at line %d\n", start->number ); errors++; } if ( boolean_found && !true_found ) { fprintf( stderr, "Boolean without a TRUE case specified in " "subsection starting at line %d\n", start->number ); errors++; } if ( boolean_found && !false_found ) { fprintf( stderr, "Boolean without a FALSE case specified in " "subsection starting at line %d\n", start->number ); errors++; } return (errors) ? -1 : 0; } /* * Validate_object * * presence of range or members but not both */ int Validate_object( Section_info_t *section, Line_Control *start, Line_Control *next_section ) { char ObjectName[ BUFFER_SIZE ]; char EndObjectName[ BUFFER_SIZE ]; Line_Control *line; int errors = 0; LineCopyFromRight( start, ObjectName ); for ( line = start; !_Chain_Is_last( &line->Node ) ; line = (Line_Control *) line->Node.next ) { if ( line->keyword == END_OBJECT ) { LineCopyFromRight( line, EndObjectName ); break; } } if ( strcmp( ObjectName, EndObjectName ) ) { fprintf( stderr, "Object and End Object names do not match\n" ); errors++; } return (errors) ? -1 : 0; }