/*
* 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 <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 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 <stdlib.h>? */
extern char *optarg; /* Why is this not in <stdlib.h>? */
#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 ; i<NUMBER_ELEMENTS(Valid_visibilities) ; i++ ) {
if ( !strcmp( Valid_visibilities[ i ], Visibility ) ) {
found = TRUE;
break;
}
}
if ( !found ) {
if ( !(InsertTBDs && !strcmp( Visibility, "TBD" )) ) {
fprintf(
stderr,
"Invalid visibility type (%s) on line %d\n",
Visibility,
line->number
);
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 ; i<NUMBER_ELEMENTS(Valid_synchronization) ; i++ ) {
if ( !strcmp( Valid_synchronization[ i ], Synchronization ) ) {
found = TRUE;
break;
}
}
if ( !found ) {
fprintf(
stderr,
"Invalid synchronization type (%s) on line %d\n",
Synchronization,
line->number
);
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;
}