From ae68ff085724dd35d60151bd153e80b8b0776873 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Tue, 27 May 1997 12:40:11 +0000 Subject: Initial revision --- doc/tools/bmenu/main.c | 985 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 985 insertions(+) create mode 100644 doc/tools/bmenu/main.c (limited to 'doc/tools/bmenu/main.c') diff --git a/doc/tools/bmenu/main.c b/doc/tools/bmenu/main.c new file mode 100644 index 0000000000..1a9a309831 --- /dev/null +++ b/doc/tools/bmenu/main.c @@ -0,0 +1,985 @@ +/* + * main.c + * + * This program takes a texinfo file without node and menu commands, + * build those commands and inserts them. + * + * 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. After the program is run, there is still a little + * clean up to be performed by hand. The following have to be fixed + * by hand: + * + previous of the first node + * + next of the last node + */ + +#include +#include +#include +#include +#include +#include + +/* XXX -- just for testing -- these should be set by options */ +char DocsNextNode[] = ""; +char DocsPreviousNode[] = "Top"; +char DocsUpNode[] = "Top"; + +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 = stdout; + +/************************************************************************* + ************************************************************************* + ***** DATA TYPES AND CONSTANT TABLES ***** + ************************************************************************* + *************************************************************************/ +/* + * Usage Information + */ + +char *Usage_Strings[] = { + "\n", + "usage: cmd [-bv] 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 */ + KEYWORD_CHAPTER, + KEYWORD_CHAPHEADING, + KEYWORD_SECTION, + KEYWORD_SUBSECTION, + KEYWORD_OTHER, + KEYWORD_END + +} Keyword_indices_t; + +#define KEYWORD_FIRST KEYOWRD_CHAPTER +#define KEYWORD_LAST KEYWORD_END + +/* + * Line Management Structure + */ + +typedef enum { + NO_EXTRA_FORMATTING_INFO, + RAW_OUTPUT, + PARAGRAPH_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_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_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 (*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_info_t Keywords[] = { + { "unused", + 0, 0, 0, NULL }, /* so 0 can be invalid */ + { "@chapter", SECTION, RT_FORBIDDEN, BL_FORBIDDEN, NULL }, + { "@chapheading",SECTION, RT_FORBIDDEN, BL_FORBIDDEN, NULL }, + { "@section", SECTION, RT_FORBIDDEN, BL_FORBIDDEN, NULL }, + { "@subsection", SUBSECTION, RT_FORBIDDEN, BL_FORBIDDEN, NULL }, + { "", HEADING, RT_FORBIDDEN, BL_FORBIDDEN, NULL }, + { "END OF FILE", SECTION, RT_FORBIDDEN, BL_FORBIDDEN, NULL } +}; + +#define NUMBER_OF_KEYWORDS \ + ( sizeof( Keywords ) / sizeof( Keyword_info_t ) ) - 2 + +/* + * exit_application + */ + +void exit_application( + int status +) +{ + fprintf( stderr, "*** Error encountered ***\n" ); +/* + fprintf( stderr, "*** Error encountered on line %d ***\n", CurrentLine ); +*/ + fclose( OutFile ); + exit( status ); +} + +/************************************************************************* + ************************************************************************* + ***** 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'; +} + +/************************************************************************* + ************************************************************************* + ***** END OF LINE MANIPULATION ROUTINES ***** + ************************************************************************* + *************************************************************************/ + +/* + * main + */ + +int main( + int argc, + char **argv +) +{ + int c; + int index; + boolean single_file_mode; + + Verbose = FALSE; + + while ((c = getopt(argc, argv, "bv")) != EOF) { + switch (c) { + case 'v': + Verbose = TRUE; + break; + case '?': + usage(); + return 0; + } + } + + if ( Verbose ) + fprintf( stderr, "Arguments successfully parsed\n" ); + + 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; + + /* + * Automatically generate the output file name. + */ + + if ( outname == NULL ) { + for( index=0 ; inname[index] && inname[index] != '.' ; index++ ) { + out[ index ] = inname[ index ]; + } + + out[ index++ ] = '.'; + out[ index++ ] = 't'; + out[ index++ ] = 'x'; + out[ index++ ] = 't'; + 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" ); + + + 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" ); +} + +/* + * 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; + + 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'; + + if ( strstr( line->Contents, "@chapter" ) ) + line->keyword = KEYWORD_CHAPTER; + else if ( strstr( line->Contents, "@chapheading" ) ) + line->keyword = KEYWORD_CHAPHEADING; + else if ( strstr( line->Contents, "@section" ) ) + line->keyword = KEYWORD_SECTION; + else if ( strstr( line->Contents, "@subsection" ) ) + line->keyword = KEYWORD_SUBSECTION; + else + line->keyword = KEYWORD_OTHER; + + } + line = AllocateLine(); + line->keyword = KEYWORD_END; + _Chain_Append( &Lines, &line->Node ); +} + +/* + * strIsAllSpace + */ + +boolean strIsAllSpace( + char *s +) +{ + char *p; + + for ( p = s ; *p ; p++ ) + if ( !isspace( *p ) ) + return FALSE; + + return TRUE; +} + +/* + * BuildTexinfoNodes + */ + +void BuildTexinfoNodes( void ) +{ + Line_Control *line; + Line_Control *new_line; + Line_Control *next_node; + char Buffer[ BUFFER_SIZE ]; + char ChapterName[ 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 *node_insert_point; + 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 == KEYWORD_END ) + goto bottom; + + if ( line->keyword == KEYWORD_CHAPTER || + line->keyword == KEYWORD_CHAPHEADING ) { + LineCopyFromRight( line, ChapterName ); + strcpy( UpNodeName, DocsUpNode ); + strcpy( NodeName, ChapterName ); + } else { + LineCopySectionName( line, Buffer ); + sprintf( NodeName, "%s %s", ChapterName, Buffer ); + strcpy( UpNodeName, ChapterName ); + } + 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 == KEYWORD_CHAPTER || line->keyword == KEYWORD_CHAPHEADING ) { + next_node = (Line_Control *) line->Node.next; + next_found = FALSE; + for ( ; ; ) { + if ( next_node->keyword == KEYWORD_END ) + break; + if ( Keywords[ next_node->keyword ].level == SECTION ) { + LineCopySectionName( next_node, Buffer ); + if ( !next_found ) { + next_found = TRUE; + sprintf( NextNodeName, "%s %s", ChapterName, 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::", ChapterName, 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 == KEYWORD_END ) { + strcpy( NextNodeName, DocsNextNode ); + } else { + LineCopySectionName( next_node, Buffer ); + sprintf( NextNodeName, "%s %s", ChapterName, Buffer ); + } + next_found = TRUE; + } + break; + } else if ( Keywords[ next_node->keyword ].level == SUBSECTION ) { + LineCopySectionName( next_node, MenuBuffer ); /* has next node */ + + 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 ); + + LineCopyFromRight( line, NodeName ); + + 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 == KEYWORD_END ) { + strcpy( NextNodeName, DocsNextNode ); + } else { + LineCopySectionName( next_node, Buffer ); + sprintf( NextNodeName, "%s %s", ChapterName, Buffer ); + } + break; + } else if ( Keywords[ next_node->keyword ].level == SUBSECTION ) { + LineCopyFromRight( next_node, NextNodeName ); + 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 + */ + +void FormatToTexinfo( void ) +{ + if ( Verbose ) + fprintf( stderr, "-------->INSERTING TEXINFO MENUS\n" ); + + BuildTexinfoNodes(); +} + +/* + * 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; + + 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'; +} -- cgit v1.2.3