3 Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
4 Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
5 Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
7 This file is part of ROBODoc
9 ROBODoc is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 /****h* ROBODoc/Utilities
26 * Set of general purpose utility functions that are used
27 * in more than one module.
29 * $Id: util.c,v 1.57 2007/07/10 19:13:52 gumpu Exp $
38 #include <stdarg.h> /* for RB_Say() */
68 /*===============================================================================*/
71 /****f* Utilities/ExpandTab
73 * Expand the tabs in a line of text.
81 * line -- the line to be expanded
84 * pointer to the expanded line.
86 * This function is not reentrant.
91 char *cur_char = line;
98 lineBufLen = strlen( line ) + 1;
100 if ( ( newLine = malloc( lineBufLen * sizeof( char ) ) ) == NULL )
102 RB_Panic( "Out of memory! ExpandTab()\n" );
105 for ( ; *cur_char; ++cur_char )
107 if ( *cur_char == '\t' )
111 // Seek to actual tab stop position in tabstop table
112 while ( ( tab_stops[actual_tab] <= n )
113 && ( actual_tab < ( MAX_TABS - 1 ) ) )
118 jump = tab_stops[actual_tab] - n;
120 // If jump gets somehow negative fix it...
127 if ( ( newLine = realloc( newLine, sizeof( char ) * lineBufLen ) )
130 RB_Panic( "Out of memory! ExpandTab()\n" );
132 for ( i = 0; i < jump; i++ )
140 newLine[n] = *cur_char;
152 /****f* Utilities/RB_Alloc_Header
154 * allocate the struct RB_header
158 struct RB_header *RB_Alloc_Header(
162 * struct RB_header * -- all attributes/pointers set to zero
171 struct RB_header *new_header;
173 if ( ( new_header = malloc( sizeof( struct RB_header ) ) ) != NULL )
175 memset( new_header, 0, sizeof( struct RB_header ) );
179 RB_Panic( "out of memory! [Alloc Header]\n" );
181 return ( new_header );
187 /****f* Utilities/RB_Free_Header
189 * RB_Free_Header -- oop
193 struct RB_header *header )
196 * free struct RB_header and associated strings
198 * struct RB_header *header -- this one
202 * RB_Alloc_Header(), RB_Close_The_Shop()
209 if ( header->function_name )
211 free( header->function_name );
213 if ( header->version )
215 free( header->version );
219 free( header->name );
221 if ( header->unique_name )
223 free( header->unique_name );
229 for ( i = 0; i < header->no_lines; ++i )
231 free( header->lines[i] );
233 free( header->lines );
242 /****if* Utilities/RB_StrDup
252 * duplicate the given string.
254 * char *str -- source
256 * char * -- destination
264 malloc( ( strlen( str ) + 1 ) * sizeof( char ) ) ) != NULL )
266 strcpy( dupstr, str );
270 RB_Panic( "out of memory! [StrDup]\n" );
280 char *new = malloc( length + 1 );
284 memcpy( new, str, length );
289 RB_Panic( "out of memory! [StrDupLen]\n" );
297 /****f* Utilities/RB_Say [2.01]
308 * Say what's going on. Goes to stdout.
310 * * char *format -- formatstring
311 * * long mode -- SAY_INFO | SAY_DEBUG
312 * * ... -- parameters
316 * 22. Nov. 2005 - Multiple mode support (Michel Albert)
323 if ( course_of_action.do_tell && debugmode & mode )
325 va_start( ap, mode );
326 printf( "%s: ", whoami );
327 vprintf( format, ap );
335 /*x**f* Analyser/RB_SetCurrentFile
339 * Set... (TODO Documentation)
345 void RB_SetCurrentFile(
348 current_file = filename;
354 /****f* Analyser/RB_GetCurrentFile
356 * Get a copy of the name of the current file.
361 char *RB_GetCurrentFile(
366 return RB_StrDup( current_file );
378 /****f* Utilities/Path2Win32Path
380 * Convert a path to a path in MS windows format.
384 char *Path_2_Win32Path(
387 char *cur_char = path;
389 for ( ; *cur_char; ++cur_char )
393 if ( *cur_char == '/' )
402 /****f* Utilities/RB_Panic [2.01]
404 * RB_Panic -- free resources and shut down
413 * Print error message. Frees all resources used by robodoc.
414 * Terminates program. Output goes to stderr
416 * char *format -- formatstring
427 va_start( ap, format );
429 name = RB_GetCurrentFile( );
432 if ( course_of_action.do_ms_errors )
434 Path_2_Win32Path( name );
440 char *buffer_copy = RB_StrDup( myLine );
442 RB_StripCR( buffer_copy );
443 fprintf( stderr, "%s:\n%s(%d) : Error E1:\n", whoami, name,
445 fprintf( stderr, " %s\n%s: ", whoami, buffer_copy );
451 fprintf( stderr, "%s: ", whoami );
453 vfprintf( stderr, format, ap );
454 fprintf( stderr, "%s: closing down...\n", whoami );
456 RB_Close_The_Shop( );
457 exit( EXIT_FAILURE );
463 /****f* Analyser/RB_Warning_Full
467 * Print warning to stdout.
470 * * arg_line_number --
476 void RB_Warning_Full(
484 ++number_of_warnings;
485 va_start( ap, arg_format );
486 fprintf( stderr, "%s: Warning - %s:%d\n", whoami, arg_filename,
488 fprintf( stderr, " " );
489 vfprintf( stderr, arg_format, ap );
496 /****f* Analyser/RB_Warning
500 * Print warning to stdout. (stderr better?)
511 static int count = 1;
515 ++number_of_warnings;
516 va_start( ap, format );
518 name = RB_GetCurrentFile( );
521 if ( course_of_action.do_ms_errors )
523 Path_2_Win32Path( name );
529 fprintf( stderr, "%s:\n%s(%d) : Warning R%d:\n", whoami, name,
530 line_number, count );
534 fprintf( stderr, " " );
535 vfprintf( stderr, format, ap );
544 /****f* Utilities/RB_Str_Case_Cmp
546 * Compare two strings, regardless of the case of the characters.
562 for ( ; tolower( *s ) == tolower( *t ); s++, t++ )
569 return ( int ) ( tolower( *s ) - tolower( *t ) );
575 /****f* Utilities/RB_TimeStamp
577 * RB_TimeStamp -- print a time stamp
585 char timeBuffer[255];
588 strftime( timeBuffer, 255, "%a %b %d %Y %H:%M:%S\n", localtime( &ttp ) );
589 fprintf( f, "%s", timeBuffer );
595 /****f* Utilities/RB_Skip_Whitespace
597 * char * RB_Skip_Whitespace(char *buf)
599 * Skip space and tab chars from the start *buf. This is needed when
600 * searching for indented headers and items.
602 * We should extract some info about indentation level and save it to
603 * global variable in order to write out source items (that originate from
604 * indented headers) neatly.
606 * RB_Find_Marker, RB_Find_End_Marker, RB_Find_Item, RB_Generate_Item_Body
610 char *RB_Skip_Whitespace(
615 for ( c = buf; *c; c++ )
617 if ( utf8_isspace( *c ) )
626 return c; /* buf was null */
629 /*** RB_Skip_Whitespace ***/
632 /****f* Utilities/RB_FputcLatin1ToUtf8
634 * RB_FputcLatin1ToUtf8
636 * void RB_FputcLatin1ToUtf8(FILE *fp, int c)
638 * This wrongly assumes that input is always Latin-1.
642 void RB_FputcLatin1ToUtf8(
648 if ( fputc( c, fp ) == EOF )
649 RB_Panic( "RB_FputcLatin1ToUtf8: write error" );
653 if ( fputc( ( 0xC0 | ( c >> 6 ) ), fp ) == EOF )
654 RB_Panic( "RB_FputcLatin1ToUtf8: write error" );
655 if ( fputc( ( 0x80 | ( c & 0x3F ) ), fp ) == EOF )
656 RB_Panic( "RB_FputcLatin1ToUtf8: write error" );
660 /*** RB_FputcLatin1ToUtf8 ***/
663 /****f* Utilities/RB_CopyFile
665 * RB_CopyFile -- copy a file to another file
667 * void RB_CopyFile( char* sourceFileName, char* destinationFileName )
669 * Program Exit if one of the specified files did not open.
674 char *sourceFileName,
675 char *destinationFileName )
679 source = fopen( sourceFileName, "r" );
684 dest = fopen( destinationFileName, "w" );
687 for ( ; fgets( line_buffer, MAX_LINE_LEN, source ); )
689 fputs( line_buffer, dest );
695 RB_Panic( "Can't open file %s for writing.\n",
696 destinationFileName );
701 RB_Panic( "Can't open file %s for reading\n", sourceFileName );
708 /****f* Utilities/RB_Match
710 * See if a wildcard expression matches a target string. The wildcard
711 * expression can consists of any literal character and the two
712 * wildcards characters '*' and '?'. '*' matches the longest string
713 * of zero or more characters that fit. '?' matches any single
717 * "*aap" matches "aapaapaapaap"
718 * "?inux" matches "linux"
719 * "lin*ux" matches "linux"
720 * "linux*" matches "linux"
722 * This is a recursive function.
724 * int RB_Match( char* target, char* wildcard_expression )
726 * o target -- the string to be matched agains the
727 * wildcard_expression.
728 * o wildcard_expression -- the wildcard expression
730 * TRUE -- the target matches the wildcard expression
731 * FALSE -- it does not match.
737 char *wildcard_expression )
739 if ( ( *wildcard_expression == '\0' ) && ( *target == '\0' ) )
741 /* a match, since both strings are now "" */
744 else if ( *wildcard_expression == '\0' )
746 /* we reached the end of the wildcard_expression,
747 * but not the end of the target, this is not
752 else if ( *target == '\0' )
754 /* we reached the end of the target but not the end of the
755 * wildcard_expression. Only if the whole wildcard_expression
756 * consists of * we have a match.
760 for ( i = 0; i < strlen( wildcard_expression ); ++i )
762 if ( wildcard_expression[i] != '*' )
771 /* There are wildcard_expression characters left
772 * and target characters left.
774 char wildcard = wildcard_expression[0];
776 if ( wildcard == '?' )
778 /* Match a single character and see if the
779 * rest of the target matches.
781 return RB_Match( target + 1, wildcard_expression + 1 );
783 else if ( wildcard == '*' )
786 int l = strlen( target );
789 /* First try to match all of the target string, and
790 * then work back to the begin of the target string.
791 * ( including the "" string. )
793 for ( i = l; i >= 0; --i )
795 if ( RB_Match( target + i, wildcard_expression + 1 ) )
805 int l_w = strlen( wildcard_expression );
806 int l_t = strlen( target );
808 /* The minimum of the length of the wildcard_expression
809 * and target expression
811 int l = ( l_w <= l_t ) ? l_w : l_t;
814 for ( i = 0; i < l; ++i )
816 if ( ( wildcard_expression[i] != '*' ) &&
817 ( wildcard_expression[i] != '?' ) )
819 /* Some OS-es are not case-sensitive when it comes
820 * to file names, and consider Readme to be equal
821 * to README. On these OS-es it can be handy if
822 * robodoc is also not case-sensitive.
824 #ifdef IGNORE_CASE_FILENAMES
825 if ( tolower( wildcard_expression[i] ) !=
826 tolower( target[i] ) )
828 if ( wildcard_expression[i] != target[i] )
836 return RB_Match( target + i, wildcard_expression + i );
839 /* The first l characters of the target and
840 * wildcard_expression matched, now see if the rest
843 return RB_Match( target + l, wildcard_expression + l );
851 /****f* Utilities/RB_Swap
853 * Swap two elements in a array of pointers. This function is used
863 void *p = array[left];
865 array[left] = array[right];
872 /****f* Utilities/RB_QuickSort
874 * Sort an array of pointers according to the lexical order
875 * of the elements the pointers point to.
876 * This is based on the quicksort routine in
877 * "The C programming language" by B Kerninghan en D Ritchie.
879 * * array -- the array of pointers.
880 * * left -- the most left element in the array.
881 * * right -- the most right element in the array.
882 * * f -- pointer to a function that can compare
883 * the objects two elements of the array
886 * array -- A sorted array of pointers.
889 * The following is an example program that shows
891 * #define TEST_SIZE 10
893 * char* test[ TEST_SIZE ] = { "ape", "zebra",
894 * "duck", "goofbal", "dodo", "rabit",
895 * "crow", "cow", "pig", "goat" };
897 * int string_compare( void* p1, void* p2 )
901 * return strcmp( cp1, cp2 );
904 * RB_QuickSort( test, 0, TEST_SIZE - 1, string_compare );
923 RB_Swap( array, left, ( left + right ) / 2 );
925 for ( i = left + 1; i <= right; ++i )
927 if ( ( *f ) ( array[i], array[left] ) < 0 )
929 RB_Swap( array, ++last, i );
932 RB_Swap( array, left, last );
933 RB_QuickSort( array, left, last - 1, f );
934 RB_QuickSort( array, last + 1, right, f );
940 /****f* Utilities/RB_StripCR
942 * Strip carriage return (CR) from line.
944 * * line -- line string to process
953 for ( c = line; *c; ++c )
965 /****f* Utilities/RB_ContainsNL
967 * Check whether the provided line buffer contains
968 * a new line (NL) character.
970 * * line -- line string to process
972 * * TRUE -- the line contains a NL character
973 * * FALSE -- the line does not contain a NL character
982 for ( ; *line != '\0'; ++line )
995 /****f* Utilities/RB_FreeLineBuffer
997 * Free the dynamically allocated line-buffer
999 * * works on the globals line_buffer, readChars, myLine
1003 void RB_FreeLineBuffer(
1006 *line_buffer = '\0';
1014 /****f* Utilities/CR_LF_Conversion
1016 * Fix CR/LF problems.
1018 * If ROBODoc reads a text file that was created on another OS
1019 * line-endings might not be what ROBODoc expects of the current OS.
1020 * This function tries to detect and fix this.
1023 * * line -- a line of text
1025 * * number of characters that were removed.
1029 static int CR_LF_Conversion( char* line )
1031 int n = strlen( line );
1034 ( ( ( line[ n - 2 ] == '\r' ) && ( line [ n - 1 ] == '\n' ) )
1036 ( ( line[ n - 2 ] == '\n' ) && ( line [ n - 1 ] == '\r' ) )
1040 line[ n - 2 ] = '\n';
1041 line[ n - 1 ] = '\0';
1052 /****f* Utilities/RB_ReadWholeLine
1054 * Read a line from the file using the provided buffer.
1056 * * file -- file to read from
1057 * * buf -- buffer of length MAX_LINE_LEN to read chunks of the line to
1058 * * arg_readChars -- reference to the variable to store the read characters in
1060 * * returns a dynamically allocated buffer containing the complete line
1063 * If the line did not end in a new line (NL) character one is added.
1067 char *RB_ReadWholeLine(
1070 int *arg_readChars )
1078 while ( ( !feof( file ) ) && ( !foundNL ) )
1081 /* read next chunk */
1082 fgets( buf, MAX_LINE_LEN, file );
1083 if ( ferror( file ) )
1085 /* an error occurred */
1086 RB_Panic( "I/O error %d! RB_ReadWholeLine()", errno );
1088 chunkLen = strlen( buf );
1089 curLineLen += chunkLen;
1090 /* make room for the chunk in our buffer */
1091 if ( ( line = realloc( line, sizeof( char ) * curLineLen ) ) == NULL )
1093 /* we run out of memory */
1094 RB_Panic( "Out of memory! RB_ReadWholeLine()" );
1096 /* append the chunk to our buffer */
1097 strcpy( ( line + curLineLen - chunkLen - 1 ), buf );
1099 if ( RB_ContainsNL( buf ) )
1101 /* we are done - a line was read */
1108 /* last line has no NL - add one */
1110 if ( ( line = realloc( line, sizeof( char ) * curLineLen ) ) == NULL )
1112 /* we run out of memory */
1113 RB_Panic( "Out of memory! RB_ReadWholeLine()" );
1115 line[curLineLen - 2] = '\n';
1116 line[curLineLen - 1] = '\0';
1119 /* This fixes any cr/lf problems. */
1120 curLineLen -= CR_LF_Conversion( line );
1122 *arg_readChars = curLineLen;
1130 /****f* Utilities/Stat_Path
1132 * Check the given path against required type.
1133 * d -- directory, f -- file, e -- exists
1135 * TRUE if path is of the given type, otherwise FALSE.
1137 * Should check if symbolic link points to a directory or to a file.
1148 if ( stat( path, &st ) < 0 )
1150 if ( required == 'e' )
1156 if ( ( strcmp( path, "./" ) == 0 ) && ( required == 'd' ) )
1158 /* This fixes a bug in Mingw, where ./ can not be
1159 stat-ed under windows2000 and above,
1160 we just assume that ./ always exists. */
1165 RB_Panic( "Stat_Path: can not stat '%s'\n", path );
1171 switch ( ( ( st.st_mode ) & S_IFMT ) )
1174 if ( ( required == 'd' ) || ( required == 'e' ) )
1180 if ( ( required == 'f' ) || ( required == 'e' ) )
1185 /* TODO case S_IFLNK: chdir() */
1196 /****f* Utilities/RB_malloc
1198 * like malloc, but exit if malloc failed
1209 tmp = malloc( bytes );
1213 RB_Panic( "Unable to malloc %d bytes", bytes );
1222 /****v* Utilities/RB_crc32_table
1224 * CRC32 lookup table for crc32 functions
1227 static unsigned long *RB_crc32_table;
1232 /****f* Utilities/Make_crc32_table
1234 * Builds up crc32 lookup table for crc32 function.
1235 * Call this before calling to RB_crc32()
1238 #define CRC32_GENERATOR_POLYNOMIAL 0xEDB88320
1240 void Make_crc32_table(
1243 unsigned int i, n, carry;
1246 // Allocate space for CRC table
1247 RB_crc32_table = RB_malloc( 256 * sizeof( unsigned long ) );
1249 for ( i = 0; i < 256; i++ )
1252 for ( n = 0; n < 8; n++ )
1258 tmp ^= CRC32_GENERATOR_POLYNOMIAL;
1261 RB_crc32_table[i] = tmp;
1268 /****f* Utilities/RB_crc32
1270 * calcualtes crc32 value for a given buffer
1272 * - buf: input buffer to calculate
1273 * - len: length of buffer
1274 * - crc32: CRC32 init value
1279 unsigned long RB_crc32(
1282 unsigned long crc32 )
1286 crc32 ^= 0xffffffffL;
1290 tmp = *( buf++ ) ^ crc32;
1292 crc32 ^= RB_crc32_table[tmp];
1295 return crc32 ^= 0xffffffffL;
1301 /****f* Utilities/cwd
1303 * Holds current working directory
1306 static char *cwd = NULL;
1310 /****f* Utilities/RB_Get_Saved_CWD
1312 * Changes back to saved working directory and frees cwd string
1315 char *RB_Get_Saved_CWD(
1323 /****f* Utilities/RB_Change_Back_To_CWD
1325 * Changes back to saved working directory and frees cwd string
1328 void RB_Change_Back_To_CWD(
1341 /****f* Utilities/RB_Change_To_Docdir
1343 * Saves current working directory and then changes to document dir
1346 void RB_Change_To_Docdir(
1347 struct RB_Document *document )
1349 char tmp[TEMP_BUF_SIZE];
1352 RB_Change_Back_To_CWD( );
1355 getcwd( tmp, sizeof( tmp ) );
1356 cwd = RB_StrDup( tmp );
1358 // Get the name of doc directory
1359 // I think this is not the right way to do it. Anyone help plz?
1360 if ( course_of_action.do_multidoc )
1362 chdir( document->docroot->name );
1364 else if ( course_of_action.do_singledoc )
1366 int len = strrchr( document->singledoc_name, '/' )
1367 - document->singledoc_name;
1368 strncpy( tmp, document->singledoc_name, len );
1377 /****f* Utilities/Open_Pipe
1379 * Opens a pipe and returns its handler
1387 #if defined(RB_MSVC)
1388 a_pipe = _popen( pipe_name, "w" );
1390 a_pipe = popen( pipe_name, "w" );
1393 if ( a_pipe == NULL )
1395 RB_Panic( "Unable to open pipe to '%s'", pipe_name );
1403 /****f* Utilities/Close_Pipe
1405 * Closes a given pipe
1411 if ( arg_pipe != NULL )
1413 #if defined(RB_MSVC)
1414 _pclose( arg_pipe );
1424 /* TODO Move this to a separate file */
1425 /* TODO grow this into a whole set of utf8 routines. */
1426 /* TODO Documentation */
1429 unsigned int arg_c )
1431 return ( ( arg_c < 128 ) && isalnum( arg_c ) );
1435 unsigned int arg_c )
1437 /* We assume here that all non ascii characters
1438 * are alphabetic characters. */
1439 return ( ( arg_c >= 128 ) ||
1440 ( arg_c < 128 && isalpha( arg_c ) ) );
1444 unsigned int arg_c )
1446 return ( ( arg_c < 128 ) && iscntrl( arg_c ) );
1450 unsigned int arg_c )
1452 return ( ( arg_c < 128 ) && isdigit( arg_c ) );
1456 unsigned int arg_c )
1458 return ( ( arg_c < 128 ) && isgraph( arg_c ) );
1462 unsigned int arg_c )
1464 return ( ( arg_c < 128 ) && islower( arg_c ) );
1468 unsigned int arg_c )
1470 return ( ( arg_c < 128 ) && isprint( arg_c ) );
1474 unsigned int arg_c )
1476 return ( ( arg_c < 128 ) && ispunct( arg_c ) );
1480 unsigned int arg_c )
1482 return ( ( arg_c < 128 ) && isspace( arg_c ) );
1486 unsigned int arg_c )
1488 return ( ( arg_c < 128 ) && isupper( arg_c ) );
1492 unsigned int arg_c )
1494 return ( ( arg_c < 128 ) && isxdigit( arg_c ) );