/* vi: ff=unix spell */
/****h* ROBODoc/Analyser
* NAME
* Analyser -- Functions to scan source and collect headers
* FUNCTION
* This module provides the functions to scan a sourcefile and
* collect all the headers.
*
*****
* $Id: analyser.c,v 1.72 2007/02/21 22:24:46 gumpu Exp $
*/
#include
* aaa * aaaa * * */ /****f* Analyser/Analyse_Preformatted * FUNCTION * Analyse preformatted text ... (TODO) * SYNPOPSIS */ static void Analyse_Preformatted( struct RB_Item *arg_item, int indent ) /* * INPUTS * * arg_item -- the item to be analysed. * * indent -- * SOURCE */ { int i; int in_list = FALSE; int new_indent = -1; int preformatted = FALSE; char *line = NULL; /* preformatted blocks */ if ( arg_item->no_lines > 0 ) { i = 0; /* Skip any pipe stuff */ for ( ; ( i < arg_item->no_lines ) && ( arg_item->lines[i]->kind == ITEM_LINE_PIPE ); ++i ) { /* Empty */ } line = arg_item->lines[i]->line; if ( ( !in_list ) && ( arg_item->lines[i]->format & RBILA_BEGIN_LIST ) ) { in_list = TRUE; } if ( ( in_list ) && ( arg_item->lines[i]->format & RBILA_END_LIST ) ) { in_list = FALSE; } for ( ++i; i < arg_item->no_lines; i++ ) { if ( arg_item->lines[i]->kind == ITEM_LINE_PIPE ) { if ( preformatted ) { arg_item->lines[i]->format |= RBILA_END_PRE; } for ( ; ( i < arg_item->no_lines ) && ( arg_item->lines[i]->kind == ITEM_LINE_PIPE ); ++i ) { /* Empty */ }; /* Every item ends with an ITEM_LINE_END, so: */ assert( i < arg_item->no_lines ); if ( preformatted ) { arg_item->lines[i]->format |= RBILA_BEGIN_PRE; } } line = arg_item->lines[i]->line; new_indent = Get_Indent( line ); if ( ( !in_list ) && ( arg_item->lines[i]->format & RBILA_BEGIN_LIST ) ) { in_list = TRUE; } if ( ( in_list ) && ( arg_item->lines[i]->format & RBILA_END_LIST ) ) { in_list = FALSE; } if ( !in_list ) { if ( ( new_indent > indent ) && !preformatted ) { preformatted = TRUE; arg_item->lines[i]->format |= RBILA_BEGIN_PRE; } else if ( ( new_indent <= indent ) && preformatted ) { preformatted = FALSE; arg_item->lines[i]->format |= RBILA_END_PRE; } else { /* An empty line */ } } else { /* We are in a list, do nothing */ } } } } /******/ /****f* Analyser/Analyse_Paragraphs * FUNCTION * Analyse paragraphs... (TODO) * SYNPOPSIS */ static void Analyse_Paragraphs( struct RB_Item *arg_item ) /* * INPUTS * * arg_item -- the item to be analysed. * SOURCE */ { int i; int in_par = FALSE; int in_list = FALSE; int in_pre = FALSE; int is_empty = FALSE; int prev_is_empty = FALSE; for ( i = 0; ( i < arg_item->no_lines ) && ( arg_item->lines[i]->kind == ITEM_LINE_PIPE ); ++i ) { /* Empty */ } assert( i < arg_item->no_lines ); if ( ( arg_item->lines[i]->format == 0 ) ) { arg_item->lines[i]->format |= RBILA_BEGIN_PARAGRAPH; in_par = TRUE; } for ( ; i < arg_item->no_lines; i++ ) { char *line = arg_item->lines[i]->line; prev_is_empty = is_empty; is_empty = Is_Empty_Line( line ); if ( arg_item->lines[i]->format & RBILA_BEGIN_LIST ) { in_list = TRUE; } if ( arg_item->lines[i]->format & RBILA_BEGIN_PRE ) { in_pre = TRUE; } if ( arg_item->lines[i]->format & RBILA_END_LIST ) { in_list = FALSE; } if ( arg_item->lines[i]->format & RBILA_END_PRE ) { in_pre = FALSE; } if ( in_par ) { if ( ( arg_item->lines[i]->format & RBILA_BEGIN_LIST ) || ( arg_item->lines[i]->format & RBILA_BEGIN_PRE ) || is_empty ) { in_par = FALSE; arg_item->lines[i]->format |= RBILA_END_PARAGRAPH; } } else { if ( ( arg_item->lines[i]->format & RBILA_END_LIST ) || ( arg_item->lines[i]->format & RBILA_END_PRE ) || ( !is_empty && prev_is_empty && !in_list && !in_pre ) ) { in_par = TRUE; arg_item->lines[i]->format |= RBILA_BEGIN_PARAGRAPH; } } } if ( in_par ) { arg_item->lines[arg_item->no_lines - 1]->format |= RBILA_END_PARAGRAPH; } } /******/ /****f* Analyser/Analyse_Indentation * FUNCTION * Figure out how text is indented. * SYNPOPSIS */ static int Analyse_Indentation( struct RB_Item *arg_item ) /* * INPUTS * * arg_item -- the item to be analysed. * SOURCE */ { int i; int indent = -1; assert( arg_item->no_lines > 0 ); for ( i = 0; i < arg_item->no_lines; ++i ) { if ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN ) { char *line = arg_item->lines[i]->line; if ( Is_Empty_Line( line ) ) { /* Empty */ indent = 0; } else { indent = Get_Indent( line ); break; } } } assert( indent >= 0 ); return indent; } /******/ /****f* Analyser/Analyse_Item_Format * FUNCTION * Try to determine the formatting of an item. * An empty line generates a new paragraph * Things that are indented more that the rest of the text * are preformatted. * Things that start with a '*' or '-' create list items * unless they are indented more that the rest of the text. * SYNPOPSIS */ static void Analyse_Item_Format( struct RB_Item *arg_item ) /* * INPUTS * * arg_item -- the item to be analysed. * SOURCE */ { // If item is not empty if ( arg_item->no_lines ) { // If it is a SOURCE item if ( Works_Like_SourceItem( arg_item->type ) ) { // Preformat it like a SOURCE item Preformat_All( arg_item, TRUE ); } // Check if we have to analyse this item else if ( ( course_of_action.do_nopre || Is_Format_Item( arg_item->type ) ) && !Is_Preformatted_Item( arg_item->type ) ) { // analyse item int indent = Analyse_Indentation( arg_item ); Analyse_List( arg_item, indent ); Analyse_Preformatted( arg_item, indent ); Analyse_Paragraphs( arg_item ); } // If none of above, preformat item else { // Preformat it Preformat_All( arg_item, FALSE ); } } // Item is empty else { // Do nothing } } /*****/ static int Trim_Empty_Item_Begin_Lines( struct RB_header *arg_header, struct RB_Item *arg_item, int current_index ) { char *c; if ( Works_Like_SourceItem( arg_item->type ) ) { /* We skip the first line after the source item, if * it an remark end marker -- such as '*)' */ c = arg_header->lines[current_index]; if ( RB_Is_Remark_End_Marker( c ) ) { c = RB_Skip_Remark_End_Marker( c ); c = RB_Skip_Whitespace( c ); if ( *c != '\0' ) { c = arg_header->lines[current_index]; RB_Warning( "text following a remark end marker:\n%s\n", c ); } ++current_index; } } if ( current_index > arg_item->end_index ) { /* item is empty */ } else { do { c = arg_header->lines[current_index]; c = RB_Skip_Whitespace( c ); if ( RB_Has_Remark_Marker( c ) ) { c = RB_Skip_Remark_Marker( c ); } c = RB_Skip_Whitespace( c ); if ( *c == '\0' ) { ++current_index; } } while ( ( *c == '\0' ) && ( current_index < arg_item->end_index ) ); } return current_index; } static int Trim_Empty_Item_End_Lines( struct RB_header *arg_header, struct RB_Item *arg_item, int current_index ) { char *c; if ( Works_Like_SourceItem( arg_item->type ) ) { c = arg_header->lines[current_index]; if ( RB_Is_Remark_Begin_Marker( c ) ) { c = RB_Skip_Remark_Begin_Marker( c ); c = RB_Skip_Whitespace( c ); if ( *c != '\0' ) { c = arg_header->lines[current_index]; RB_Warning( "text following a remark begin marker:\n%s\n", c ); } --current_index; } } do { c = arg_header->lines[current_index]; c = RB_Skip_Whitespace( c ); if ( RB_Has_Remark_Marker( c ) ) { c = RB_Skip_Remark_Marker( c ); } c = RB_Skip_Whitespace( c ); if ( *c == '\0' ) { --current_index; } } while ( ( *c == '\0' ) && ( current_index > arg_item->begin_index ) ); return current_index; } static void Trim_Empty_Item_Lines( struct RB_header *arg_header, struct RB_Item *arg_item ) { arg_item->no_lines = arg_item->end_index - arg_item->begin_index + 1; if ( arg_item->no_lines <= 1 ) { /* item is empty */ arg_item->no_lines = 0; } else { int current_index; /* trim all empty lines at the begin of an item */ /* we skip the first line because that contains the item name. */ current_index = arg_item->begin_index + 1; current_index = Trim_Empty_Item_Begin_Lines( arg_header, arg_item, current_index ); /* Is there anything left? */ if ( current_index <= arg_item->end_index ) { arg_item->begin_index = current_index; /* trim all the empty lines at the end of an item */ current_index = arg_item->end_index; current_index = Trim_Empty_Item_End_Lines( arg_header, arg_item, current_index ); if ( current_index >= arg_item->begin_index ) { arg_item->end_index = current_index; arg_item->no_lines = arg_item->end_index - arg_item->begin_index + 1; } else { /* item is empty */ arg_item->no_lines = 0; } } else { /* item is empty */ arg_item->no_lines = 0; } } } /* TODO This routine is way too long */ static void Copy_Lines_To_Item( struct RB_header *arg_header, struct RB_Item *arg_item ) { #if 0 printf( "%d\n%d\n%s\n%s\n", arg_item->begin_index, arg_item->end_index, arg_header->lines[arg_item->begin_index], arg_header->lines[arg_item->end_index] ); #endif Trim_Empty_Item_Lines( arg_header, arg_item ); if ( arg_item->no_lines > 0 ) { int i = 0; int j = 0; struct RB_Item_Line *itemline = NULL; int tool_active = 0; // Shows wether we are inside a tool body /* Allocate enough memory for all the lines, plus one * extra line */ ++arg_item->no_lines; arg_item->lines = calloc( arg_item->no_lines, sizeof( struct RB_Item_Line * ) ); if ( !arg_item->lines ) { RB_Panic( "Out of memory! %s\n", "Copy_Lines_To_Item" ); } /* And create an RB_Item_Line for each of them, and add * those to the RB_Item */ for ( i = 0; i < arg_item->no_lines - 1; ++i ) { char *c = arg_header->lines[arg_item->begin_index + i]; /* TODO should be a Create_ItemLine() */ itemline = malloc( sizeof( struct RB_Item_Line ) ); if ( !itemline ) { RB_Panic( "Out of memory! %s (2)\n", "Copy_Lines_To_Item" ); } c = ExpandTab( c ); c = RB_Skip_Whitespace( c ); // Lines with remark marker if ( RB_Has_Remark_Marker( c ) && !Works_Like_SourceItem( arg_item->type ) ) { char *c2, *c3; int pipe_mode; enum ItemLineKind item_kind; c = RB_Skip_Remark_Marker( c ); c2 = RB_Skip_Whitespace( c ); if ( *c2 ) { // Check wether a tool starts or ends if ( ( c3 = Is_Tool( c2, &item_kind, &tool_active ) ) ) { itemline->kind = item_kind; c = c3; } // If we have an active tool, copy the body lines else if ( tool_active ) { itemline->kind = ITEM_LINE_TOOL_BODY; c++; // Skip space after the remark marker } // Check for pipes else if ( ( c3 = Is_Pipe_Marker( c2, &pipe_mode ) ) ) { itemline->kind = ITEM_LINE_PIPE; itemline->pipe_mode = pipe_mode; c = c3; } // Plain Items ... else { itemline->kind = ITEM_LINE_PLAIN; } } // Empty lines with remark markers and active tool else if ( tool_active ) { itemline->kind = ITEM_LINE_TOOL_BODY; } // Plain empty lines with remark markers... else { itemline->kind = ITEM_LINE_PLAIN; } } else { itemline->kind = ITEM_LINE_RAW; /* The is raw code, so we do not want to have the * whitespace stripped of */ c = arg_header->lines[arg_item->begin_index + i]; c = ExpandTab( c ); } if ( ( !Works_Like_SourceItem( arg_item->type ) && ( itemline->kind != ITEM_LINE_RAW ) ) || Works_Like_SourceItem( arg_item->type ) ) { itemline->line = RB_StrDup( c ); itemline->format = 0; arg_item->lines[j] = itemline; ++j; } else { /* We dump the RAW item lines if we are not in a * source item. */ free( itemline ); } } if ( j > 0 ) { /* And one empty line to mark the end of an item and * to be able to store some additional formatting actions */ itemline = malloc( sizeof( struct RB_Item_Line ) ); if ( !itemline ) { RB_Panic( "Out of memory! %s (3)\n", "Copy_Lines_To_Item" ); } itemline->kind = ITEM_LINE_END; itemline->line = RB_StrDup( "" ); itemline->format = 0; arg_item->lines[j] = itemline; /* Store the real number of lines we copied */ assert( arg_item->no_lines >= ( j + 1 ) ); arg_item->no_lines = j + 1; } else { arg_item->no_lines = 0; free( arg_item->lines ); arg_item->lines = NULL; } } else { arg_item->no_lines = 0; arg_item->lines = NULL; } } /****f* Analyser/RB_Analyse_Items * FUNCTION * Locate the items in the header and create RB_Item structures for * them. * SYNPOPSIS */ static int Analyse_Items( struct RB_header *arg_header ) /* * SOURCE */ { int line_nr; enum ItemType item_type = NO_ITEM; struct RB_Item *new_item; struct RB_Item *cur_item; RB_Item_Lock_Reset( ); /* find the first item */ for ( line_nr = 0; line_nr < arg_header->no_lines; ++line_nr ) { item_type = RB_Is_ItemName( arg_header->lines[line_nr] ); if ( item_type != NO_ITEM ) { break; } } /* and all the others */ while ( ( item_type != NO_ITEM ) && ( line_nr < arg_header->no_lines ) ) { new_item = RB_Create_Item( item_type ); new_item->begin_index = line_nr; /* Add the item to the end of the list of items. */ if ( arg_header->items ) { for ( cur_item = arg_header->items; cur_item->next; cur_item = cur_item->next ) { /* Empty */ } cur_item->next = new_item; } else { arg_header->items = new_item; } /* Find the next item */ for ( ++line_nr; line_nr < arg_header->no_lines; ++line_nr ) { item_type = RB_Is_ItemName( arg_header->lines[line_nr] ); if ( item_type != NO_ITEM ) { break; } } /* This points to the last line in the item */ new_item->end_index = line_nr - 1; assert( new_item->end_index >= new_item->begin_index ); /* Now analyse and copy the lines */ Copy_Lines_To_Item( arg_header, new_item ); Analyse_Item_Format( new_item ); /* Handy for debugging wiki formatting * Dump_Item( new_item ); */ } return 0; } /******/ /****f* Analyser/ToBeAdded * FUNCTION * Test whether or not a header needs to be added to the * list of headers. This implements the options * --internal * and * --internalonly * SYNPOPSIS */ static int ToBeAdded( struct RB_Document *document, struct RB_header *header ) /* * INPUTS * o document -- a document (to determine the options) * o header -- a header * RESULT * TRUE -- Add header * FALSE -- Don't add header * SOURCE */ { int add = FALSE; if ( header->is_internal ) { if ( ( document->actions.do_include_internal ) || ( document->actions.do_internal_only ) ) { add = TRUE; } else { add = FALSE; } } else { if ( document->actions.do_internal_only ) { add = FALSE; } else { add = TRUE; } } return add; } /******/ /****f* Analyser/Grab_Header * FUNCTION * Grab a header from a source file, that is scan a source file * until the start of a header is found. Then search for the end * of a header and store all the lines in between. * SYNPOPSIS */ static struct RB_header *Grab_Header( FILE *sourcehandle, struct RB_Document *arg_document ) /* * INPUTS * o sourcehandle -- an opened source file. * OUTPUT * o sourcehandle -- will point to the line following the end marker. * RESULT * 0 if no header was found, or a pointer to a new header otherwise. * SOURCE */ { struct RB_header *new_header = NULL; int is_internal = 0; struct RB_HeaderType *header_type = NULL; int good_header = FALSE; int reuse = FALSE; do { good_header = FALSE; header_type = RB_Find_Marker( sourcehandle, &is_internal, reuse ); reuse = FALSE; if ( header_type ) { struct RB_header *duplicate_header = NULL; long previous_line = 0; new_header = RB_Alloc_Header( ); new_header->htype = header_type; new_header->is_internal = is_internal; if ( Find_Header_Name( sourcehandle, new_header ) ) { new_header->line_number = line_number; RB_Say( "found header [line %5d]: \"%s\"\n", SAY_DEBUG, line_number, new_header->name ); duplicate_header = RB_Document_Check_For_Duplicate( arg_document, new_header ); if ( duplicate_header ) { /* Duplicate headers do not crash the program so * we accept them. But we do warn the user. */ RB_Warning ( "A header with the name \"%s\" already exists.\n See %s(%d)\n", new_header->name, Get_Fullname( duplicate_header->owner->filename ), duplicate_header->line_number ); } if ( ( new_header->function_name = Function_Name( new_header->name ) ) == NULL ) { RB_Warning( "Can't determine the \"function\" name.\n" ); RB_Free_Header( new_header ); new_header = NULL; } else { if ( ( new_header->module_name = Module_Name( new_header->name ) ) == NULL ) { RB_Warning ( "Can't determine the \"module\" name.\n" ); RB_Free_Header( new_header ); new_header = NULL; } else { previous_line = line_number; if ( Find_End_Marker( sourcehandle, new_header ) == 0 ) { RB_Warning ( "found header on line %d with name \"%s\"\n" " but I can't find the end marker\n", previous_line, new_header->name ); /* Reuse the current line while finding the next * Marking using RB_Find_Marker() */ reuse = TRUE; RB_Free_Header( new_header ); new_header = NULL; } else { RB_Say( "found end header [line %5d]:\n", SAY_DEBUG, line_number ); /* Good header found, we can stop */ good_header = TRUE; } } } } else { RB_Warning( "found header marker but no name\n" ); RB_Free_Header( new_header ); new_header = NULL; } } else { /* end of the file */ good_header = TRUE; } } while ( !good_header ); return new_header; } /*******/ /****f* Analyser/Module_Name * FUNCTION * Get the module name from the header name. The header name will be * something like * * module/functionname. * * SYNPOPSIS */ static char *Module_Name( char *header_name ) /* * INPUTS * o header_name -- a pointer to a nul terminated string. * RESULT * Pointer to the modulename. You're responsible for freeing it. * SEE ALSO * Function_Name() * SOURCE */ { char *cur_char; char c; char *name = NULL; assert( header_name ); for ( cur_char = header_name; *cur_char && *cur_char != '/'; ++cur_char ); if ( *cur_char ) { c = *cur_char; *cur_char = '\0'; name = RB_StrDup( header_name ); *cur_char = c; } return name; } /******/ /****f* Analyser/Function_Name * FUNCTION * A header name is consists of two parts. The module name and * the function name. This returns a pointer to the function name. * The name "function name" is a bit obsolete. It is really the name * of any of objects that can be documented; classes, methods, * variables, functions, projects, etc. * SYNOPSIS */ static char *Function_Name( char *header_name ) /* * SOURCE */ { char *cur_char; char *name; name = NULL; if ( ( cur_char = header_name ) != NULL ) { for ( ; *cur_char != '\0'; ++cur_char ) { if ( '/' == *cur_char ) { ++cur_char; if ( *cur_char ) { name = cur_char; break; } } } } if ( name ) { return RB_StrDup( name ); } else { return ( name ); } } /*** Function_Name ***/ /****f* Analyser/RB_Find_Marker * NAME * RB_Find_Marker -- Search for header marker in document. * FUNCTION * Read document file line by line, and search each line for * any of the headers defined in the array header_markers (OR * if using the -rh switch, robo_head) * SYNOPSIS */ static struct RB_HeaderType *RB_Find_Marker( FILE *document, int *is_internal, int reuse_previous_line ) /* * INPUTS * document - pointer to the file to be searched. * the gobal buffer line_buffer. * OUTPUT * o document will point to the line after the line with * the header marker. * o is_internal will be TRUE if the header is an internal * header. * RESULT * o header type * BUGS * Bad use of feof(), fgets(). * SEE ALSO * Find_End_Marker * SOURCE */ { int found; char *cur_char; struct RB_HeaderType *header_type = 0; cur_char = NULL; found = FALSE; while ( !feof( document ) && !found ) { if ( reuse_previous_line ) { /* reuse line in the line_buffer */ reuse_previous_line = FALSE; } else { RB_FreeLineBuffer( ); myLine = RB_ReadWholeLine( document, line_buffer, &readChars ); } if ( !feof( document ) ) { line_number++; found = RB_Is_Begin_Marker( myLine, &cur_char ); if ( found ) { header_type = AnalyseHeaderType( &cur_char, is_internal ); RB_Say( "found header marker of type %s\n", SAY_DEBUG, header_type->indexName ); } } } return header_type; } /******** END RB_Find_Marker ******/ /****f* Analyser/AnalyseHeaderType * FUNCTION * Determine the type of the header. * SYNOPSIS */ struct RB_HeaderType *AnalyseHeaderType( char **cur_char, int *is_internal ) /* * INPUTS * o cur_char -- pointer to the header type character * OUTPUT * o is_internal -- indicates if it is an internal header or not.* * o cur_char -- points to the header type character * RESULT * o pointer to a RB_HeaderType * SOURCE */ { struct RB_HeaderType *headertype = 0; *is_internal = RB_IsInternalHeader( **cur_char ); if ( *is_internal ) { /* Skip the character */ ++( *cur_char ); } headertype = RB_FindHeaderType( **cur_char ); if ( !headertype ) { RB_Panic( "Undefined headertype (%c)\n", **cur_char ); } return headertype; } /*******/ /****f* Analyser/Find_End_Marker * FUNCTION * Scan and store all lines from a source file until * an end marker is found. * SYNOPSIS */ static int Find_End_Marker( FILE *document, struct RB_header *new_header ) /* * INPUTS * o document -- a pointer to an opened source file. * OUTPUT * o new_header -- the lines of source code will be added * here. * RESULT * o TRUE -- an end marker was found. * o FALSE -- no end marker was found while scanning the * source file. * SOURCE */ { int found = FALSE; unsigned int no_lines = 0; unsigned int max_no_lines = 10; char **lines = NULL; char **new_lines = NULL; char *dummy; lines = ( char ** ) calloc( max_no_lines, sizeof( char * ) ); if ( !lines ) { RB_Panic( "Out of memory! %s()\n", "Find_End_Marker" ); } while ( !feof( document ) ) { RB_FreeLineBuffer( ); myLine = RB_ReadWholeLine( document, line_buffer, &readChars ); ++line_number; /* global linecounter, koessi */ if ( RB_Is_Begin_Marker( myLine, &dummy ) ) { /* Bad... found a begin marker but was expecting to find an end marker. Panic... */ found = FALSE; return found; } else if ( RB_Is_End_Marker( myLine ) ) { RB_Say( "Found end marker \"%s\"", SAY_DEBUG, myLine ); found = TRUE; break; } else { unsigned int n; char *line; line = RB_StrDup( myLine ); n = strlen( line ); assert( n > 0 ); assert( line[n - 1] == '\n' ); /* Strip CR */ line[n - 1] = '\0'; lines[no_lines] = line; ++no_lines; if ( no_lines == max_no_lines ) { max_no_lines *= 2; new_lines = realloc( lines, max_no_lines * sizeof( char * ) ); if ( !new_lines ) { RB_Panic( "Out of memory! %s()\n", "Find_End_Marker" ); } lines = new_lines; } } } new_header->no_lines = no_lines; new_header->lines = lines; return found; } /******/ /* TODO Documentation */ static void Remove_Trailing_Asterics( char *line ) { int i = strlen( line ) - 1; for ( ; ( i > 0 ) && utf8_isspace( line[i] ); i-- ) { /* Empty */ } for ( ; ( i > 0 ) && ( line[i] == '*' ); i-- ) { line[i] = ' '; } } /****if* Utilities/RB_WordWithSpacesLen * SYNOPSIS */ static int RB_WordWithSpacesLen( char *str ) /* * FUNCTION * get the amount of bytes until next separator character or ignore character * or end of line * INPUTS * char *str -- the line * RESULT * int -- length of the next word or 0 * SEE ALSO * RB_Find_Header_Name() * SOURCE */ { int len; char c; for ( len = 0; ( ( c = *str ) != '\0' ) && ( c != '\n' ); ++str, ++len ) { // Look for header truncating characters if ( Find_Parameter_Char( &( configuration.header_separate_chars ), c ) != NULL || Find_Parameter_Char( &( configuration.header_ignore_chars ), c ) != NULL ) { // and exit loop if any found break; } } /* Don't count any of the trailing spaces. */ if ( len ) { --str; for ( ; utf8_isspace( *str ); --len, --str ) { /* empty */ } } return ( len ); } /*** RB_WordWithSpacesLen ***/ /* TODO Documentation */ static int Find_Header_Name( FILE *fh, struct RB_header *hdr ) { char *cur_char = myLine; char **names = NULL; int num = 0; Remove_Trailing_Asterics( cur_char ); skip_while( *cur_char != '*' ); skip_while( !utf8_isspace( *cur_char ) ); skip_while( utf8_isspace( *cur_char ) ); while ( *cur_char ) { int length = RB_WordWithSpacesLen( cur_char ); if ( length == 0 ) break; names = realloc( names, ( ++num ) * sizeof *names ); if ( !names ) { RB_Panic( "Out of memory! %s()\n", "Find_Header_Name" ); } names[num - 1] = RB_StrDupLen( cur_char, length ); /* printf("%c adding name = %s\n", num > 1 ? ' ' : '*', names[ num - 1 ] ); */ cur_char += length; if ( Find_Parameter_Char( &( configuration.header_separate_chars ), *cur_char ) ) { for ( cur_char++; utf8_isspace( *cur_char ); cur_char++ ); /* End of line reach, but comma encountered, more headernames follow on next line */ if ( *cur_char == 0 ) { /* Skip comment */ RB_FreeLineBuffer( ); myLine = RB_ReadWholeLine( fh, line_buffer, &readChars ); line_number++; for ( cur_char = myLine; *cur_char && !utf8_isalpha( *cur_char ); cur_char++ ); } } } hdr->names = names; hdr->no_names = num; hdr->name = num ? names[0] : NULL; return num; } /***** Find_Header_Name *****/