Imported Robodoc.
[robodoc.git] / Source / util.c
1 // vi: ff=unix spell 
2 /*
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.
6
7 This file is part of ROBODoc
8
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.
13
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.
18
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/>.
21
22 */
23
24 /****h* ROBODoc/Utilities
25  * FUNCTION
26  *   Set of general purpose utility functions that are used
27  *   in more than one module.
28  *****
29  * $Id: util.c,v 1.57 2007/07/10 19:13:52 gumpu Exp $
30  */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdarg.h>             /* for RB_Say() */
39 #include <errno.h>
40 #include <time.h>
41 #include <assert.h>
42 #include <sys/stat.h>
43
44 #if defined (RB_MSVC)
45 /* empty */
46 #else
47 #include <unistd.h>
48 #endif
49
50 #include "robodoc.h"
51 #include "globals.h"
52 #include "links.h"
53 #include "headers.h"
54 #include "path.h"
55 #include "util.h"
56
57 #ifdef DMALLOC
58 #include <dmalloc.h>
59 #endif
60
61
62 static void         RB_Swap(
63     void **array,
64     int left,
65     int right );
66
67
68 /*===============================================================================*/
69
70
71 /****f* Utilities/ExpandTab
72  * FUNCTION
73  *   Expand the tabs in a line of text.
74  * SYNOPSIS
75  */
76
77 char               *ExpandTab(
78     char *line )
79 /*
80  * INPUTS
81  *   line -- the line to be expanded
82  *   tab_size    -- global.
83  * RETURN
84  *   pointer to the expanded line.
85  * NOTE
86  *   This function is not reentrant.
87  * SOURCE
88  */
89
90 {
91     char               *cur_char = line;
92     int                 n = 0;
93     int                 jump = 0;
94     char               *newLine = NULL;
95     int                 lineBufLen = 1;
96     int                 actual_tab = 0;
97
98     lineBufLen = strlen( line ) + 1;
99
100     if ( ( newLine = malloc( lineBufLen * sizeof( char ) ) ) == NULL )
101     {
102         RB_Panic( "Out of memory! ExpandTab()\n" );
103     }
104
105     for ( ; *cur_char; ++cur_char )
106     {
107         if ( *cur_char == '\t' )
108         {
109             int                 i;
110
111             // Seek to actual tab stop position in tabstop table
112             while ( ( tab_stops[actual_tab] <= n )
113                     && ( actual_tab < ( MAX_TABS - 1 ) ) )
114             {
115                 actual_tab++;
116             }
117
118             jump = tab_stops[actual_tab] - n;
119
120             // If jump gets somehow negative fix it...
121             if ( jump < 0 )
122             {
123                 jump = 1;
124             }
125
126             lineBufLen += jump;
127             if ( ( newLine = realloc( newLine, sizeof( char ) * lineBufLen ) )
128                  == NULL )
129             {
130                 RB_Panic( "Out of memory! ExpandTab()\n" );
131             }
132             for ( i = 0; i < jump; i++ )
133             {
134                 newLine[n] = ' ';
135                 ++n;
136             }
137         }
138         else
139         {
140             newLine[n] = *cur_char;
141             ++n;
142         }
143     }
144     newLine[n] = '\0';
145
146     return newLine;
147 }
148
149 /******/
150
151
152 /****f* Utilities/RB_Alloc_Header
153  * FUNCTION
154  *   allocate the struct RB_header
155  * SYNOPSIS
156  */
157
158 struct RB_header   *RB_Alloc_Header(
159     void )
160 /*
161  * RESULT
162  *   struct RB_header *      -- all attributes/pointers set to zero
163  * AUTHOR
164  *   Koessi
165  * SEE ALSO
166  *   RB_Free_Header()
167  * SOURCE
168  */
169
170 {
171     struct RB_header   *new_header;
172
173     if ( ( new_header = malloc( sizeof( struct RB_header ) ) ) != NULL )
174     {
175         memset( new_header, 0, sizeof( struct RB_header ) );
176     }
177     else
178     {
179         RB_Panic( "out of memory! [Alloc Header]\n" );
180     }
181     return ( new_header );
182 }
183
184 /********/
185
186
187 /****f* Utilities/RB_Free_Header
188  * NAME
189  *   RB_Free_Header             -- oop
190  * SYNOPSIS
191  */
192 void RB_Free_Header(
193     struct RB_header *header )
194 /*
195  * FUNCTION
196  *   free struct RB_header and associated strings
197  * INPUTS
198  *   struct RB_header *header -- this one
199  * AUTHOR
200  *   Koessi
201  * SEE ALSO
202  *   RB_Alloc_Header(), RB_Close_The_Shop()
203  * SOURCE
204  */
205
206 {
207     if ( header )
208     {
209         if ( header->function_name )
210         {
211             free( header->function_name );
212         }
213         if ( header->version )
214         {
215             free( header->version );
216         }
217         if ( header->name )
218         {
219             free( header->name );
220         }
221         if ( header->unique_name )
222         {
223             free( header->unique_name );
224         }
225         if ( header->lines )
226         {
227             int                 i;
228
229             for ( i = 0; i < header->no_lines; ++i )
230             {
231                 free( header->lines[i] );
232             }
233             free( header->lines );
234         }
235         free( header );
236     }
237 }
238
239 /************/
240
241
242 /****if* Utilities/RB_StrDup
243  * NAME
244  *   RB_StrDup
245  * SYNOPSIS
246  */
247
248 char               *RB_StrDup(
249     char *str )
250 /*
251  * FUNCTION
252  *   duplicate the given string.
253  * INPUTS
254  *   char *str               -- source
255  * RESULT
256  *   char *                  -- destination
257  * AUTHOR
258  *   Koessi
259  * SOURCE
260  */
261 {
262     char               *dupstr;
263     if ( ( dupstr =
264            malloc( ( strlen( str ) + 1 ) * sizeof( char ) ) ) != NULL )
265     {
266         strcpy( dupstr, str );
267     }
268     else
269     {
270         RB_Panic( "out of memory! [StrDup]\n" );
271     }
272     return ( dupstr );
273 }
274
275
276 char               *RB_StrDupLen(
277     char *str,
278     size_t length )
279 {
280     char               *new = malloc( length + 1 );
281
282     if ( new )
283     {
284         memcpy( new, str, length );
285         new[length] = 0;
286     }
287     else
288     {
289         RB_Panic( "out of memory! [StrDupLen]\n" );
290     }
291     return new;
292 }
293
294
295 /*** RB_StrDup ***/
296
297 /****f* Utilities/RB_Say [2.01]
298  * NAME
299  *   RB_Say                     -- varargs
300  * SYNOPSIS
301  */
302 void RB_Say(
303     char *format,
304     long mode,
305     ... )
306 /*
307  * FUNCTION
308  *   Say what's going on.  Goes to stdout.
309  * INPUTS
310  *   * char *format    -- formatstring
311  *   * long mode       -- SAY_INFO | SAY_DEBUG
312  *   * ...             -- parameters
313  * AUTHOR
314  *   Koessi
315  * HISTORY
316  *   22. Nov. 2005   - Multiple mode support (Michel Albert)
317  * SOURCE
318  */
319
320 {
321     va_list             ap;
322
323     if ( course_of_action.do_tell && debugmode & mode )
324     {
325         va_start( ap, mode );
326         printf( "%s: ", whoami );
327         vprintf( format, ap );
328         va_end( ap );
329     }
330 }
331
332 /*** RB_Say ***/
333
334
335 /*x**f* Analyser/RB_SetCurrentFile
336  * NAME
337  *   RB_SetCurrentFile
338  * FUNCTION
339  *   Set... (TODO Documentation) 
340  * INPUTS
341  *   * filename -- 
342  * SOURCE
343  */
344
345 void RB_SetCurrentFile(
346     char *filename )
347 {
348     current_file = filename;
349 }
350
351 /*******/
352
353
354 /****f* Analyser/RB_GetCurrentFile
355  * NAME
356  *   Get a copy of the name of the current file.
357  *   Allocates memory.
358  * SOURCE
359  */
360
361 char               *RB_GetCurrentFile(
362     void )
363 {
364     if ( current_file )
365     {
366         return RB_StrDup( current_file );
367     }
368     else
369     {
370         return NULL;
371     }
372 }
373
374 /*******/
375
376
377
378 /****f* Utilities/Path2Win32Path
379  * SUMMARY
380  *   Convert a path to a path in MS windows format.
381  ****
382  */
383
384 char               *Path_2_Win32Path(
385     char *path )
386 {
387     char               *cur_char = path;
388
389     for ( ; *cur_char; ++cur_char )
390     {
391         if ( *cur_char )
392         {
393             if ( *cur_char == '/' )
394             {
395                 *cur_char = '\\';
396             }
397         }
398     }
399     return path;
400 }
401
402 /****f* Utilities/RB_Panic [2.01]
403  * NAME
404  *   RB_Panic -- free resources and shut down
405  * SYNOPSIS
406  */
407
408 void RB_Panic(
409     char *format,
410     ... )
411 /*
412  * FUNCTION
413  *   Print error message.  Frees all resources used by robodoc.
414  *   Terminates program.  Output goes to stderr
415  * INPUTS
416  *   char *format            -- formatstring
417  *   ...                     -- parameters
418  * AUTHOR
419  *   Koessi
420  * SOURCE
421  */
422
423 {
424     va_list             ap;
425     char               *name;
426
427     va_start( ap, format );
428
429     name = RB_GetCurrentFile(  );
430
431 #if defined(RB_BCC)
432     if ( course_of_action.do_ms_errors )
433     {
434         Path_2_Win32Path( name );
435     }
436 #endif
437
438     if ( name )
439     {
440         char               *buffer_copy = RB_StrDup( myLine );
441
442         RB_StripCR( buffer_copy );
443         fprintf( stderr, "%s:\n%s(%d) : Error E1:\n", whoami, name,
444                  line_number );
445         fprintf( stderr, "   %s\n%s: ", whoami, buffer_copy );
446         free( buffer_copy );
447         free( name );
448     }
449     else
450     {
451         fprintf( stderr, "%s: ", whoami );
452     }
453     vfprintf( stderr, format, ap );
454     fprintf( stderr, "%s: closing down...\n", whoami );
455     va_end( ap );
456     RB_Close_The_Shop(  );
457     exit( EXIT_FAILURE );
458 }
459
460 /*** RB_Panic ***/
461
462
463 /****f* Analyser/RB_Warning_Full
464  * NAME
465  *   RB_Warning_Full
466  * FUNCTION
467  *   Print warning to stdout.
468  * INPUTS
469  *   * arg_filename    --
470  *   * arg_line_number --
471  *   * arg_format      --
472  *   * ...
473  * SOURCE
474  */
475
476 void RB_Warning_Full(
477     char *arg_filename,
478     int arg_line_number,
479     char *arg_format,
480     ... )
481 {
482     va_list             ap;
483
484     ++number_of_warnings;
485     va_start( ap, arg_format );
486     fprintf( stderr, "%s: Warning - %s:%d\n", whoami, arg_filename,
487              arg_line_number );
488     fprintf( stderr, "  " );
489     vfprintf( stderr, arg_format, ap );
490     va_end( ap );
491 }
492
493 /*******/
494
495
496 /****f* Analyser/RB_Warning
497  * NAME
498  *   RB_Warning
499  * FUNCTION
500  *   Print warning to stdout. (stderr better?)
501  * INPUTS
502  *   * format --
503  *   * ...    --
504  * SOURCE
505  */
506
507 void RB_Warning(
508     char *format,
509     ... )
510 {
511     static int          count = 1;
512     va_list             ap;
513     char               *name;
514
515     ++number_of_warnings;
516     va_start( ap, format );
517
518     name = RB_GetCurrentFile(  );
519
520 #if defined (RB_BCC)
521     if ( course_of_action.do_ms_errors )
522     {
523         Path_2_Win32Path( name );
524     }
525 #endif
526
527     if ( name )
528     {
529         fprintf( stderr, "%s:\n%s(%d) : Warning R%d:\n", whoami, name,
530                  line_number, count );
531         free( name );
532     }
533
534     fprintf( stderr, "  " );
535     vfprintf( stderr, format, ap );
536     va_end( ap );
537
538     ++count;
539 }
540
541 /*******/
542
543
544 /****f* Utilities/RB_Str_Case_Cmp
545  * FUNCTION
546  *   Compare two strings, regardless of the case of the characters.
547  * SYNOPSIS
548  */
549 int RB_Str_Case_Cmp(
550     char *s,
551     char *t )
552 /*
553  * RESULT
554  *    0  s == t
555  *   -1  s < t
556  *    1  s > t
557  * SOURCE
558  */
559 {
560     assert( s );
561     assert( t );
562     for ( ; tolower( *s ) == tolower( *t ); s++, t++ )
563     {
564         if ( *s == '\0' )
565         {
566             return 0;
567         }
568     }
569     return ( int ) ( tolower( *s ) - tolower( *t ) );
570 }
571
572 /*********/
573
574
575 /****f* Utilities/RB_TimeStamp
576  * NAME
577  *   RB_TimeStamp -- print a time stamp
578  * SOURCE
579  */
580
581 void RB_TimeStamp(
582     FILE *f )
583 {
584     time_t              ttp;
585     char                timeBuffer[255];
586
587     time( &ttp );
588     strftime( timeBuffer, 255, "%a %b %d %Y %H:%M:%S\n", localtime( &ttp ) );
589     fprintf( f, "%s", timeBuffer );
590 }
591
592 /******/
593
594
595 /****f* Utilities/RB_Skip_Whitespace
596  * SYNOPSIS
597  *   char * RB_Skip_Whitespace(char *buf)
598  * FUNCTION
599  *   Skip space and tab chars from the start *buf. This is needed when
600  *   searching for indented headers and items. 
601  * NOTES
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. 
605  * SEE ALSO
606  *   RB_Find_Marker, RB_Find_End_Marker, RB_Find_Item, RB_Generate_Item_Body
607  * SOURCE
608  */
609
610 char               *RB_Skip_Whitespace(
611     char *buf )
612 {
613     char               *c;
614
615     for ( c = buf; *c; c++ )
616     {
617         if ( utf8_isspace( *c ) )
618         {
619
620         }
621         else
622         {
623             return c;
624         }
625     }
626     return c;                   /* buf was null */
627 }
628
629 /*** RB_Skip_Whitespace ***/
630
631
632 /****f* Utilities/RB_FputcLatin1ToUtf8
633  * NAME
634  *   RB_FputcLatin1ToUtf8
635  * SYNOPSIS
636  *   void RB_FputcLatin1ToUtf8(FILE *fp, int c)
637  * BUGS
638  *   This wrongly assumes that input is always Latin-1.
639  * SOURCE
640  */
641
642 void RB_FputcLatin1ToUtf8(
643     FILE *fp,
644     int c )
645 {
646     if ( c < 0x80 )
647     {
648         if ( fputc( c, fp ) == EOF )
649             RB_Panic( "RB_FputcLatin1ToUtf8: write error" );
650     }
651     else
652     {
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" );
657     }
658 }
659
660 /*** RB_FputcLatin1ToUtf8 ***/
661
662
663 /****f* Utilities/RB_CopyFile
664  * NAME
665  *   RB_CopyFile -- copy a file to another file
666  * SYNOPSIS
667  *   void RB_CopyFile( char* sourceFileName, char* destinationFileName )
668  * RESULT
669  *   Program Exit if one of the specified files did not open.
670  * SOURCE
671  */
672
673 void RB_CopyFile(
674     char *sourceFileName,
675     char *destinationFileName )
676 {
677     FILE               *source;
678
679     source = fopen( sourceFileName, "r" );
680     if ( source )
681     {
682         FILE               *dest;
683
684         dest = fopen( destinationFileName, "w" );
685         if ( dest )
686         {
687             for ( ; fgets( line_buffer, MAX_LINE_LEN, source ); )
688             {
689                 fputs( line_buffer, dest );
690             }
691         }
692         else
693         {
694             fclose( source );
695             RB_Panic( "Can't open file %s for writing.\n",
696                       destinationFileName );
697         }
698     }
699     else
700     {
701         RB_Panic( "Can't open file %s for reading\n", sourceFileName );
702     }
703 }
704
705 /*****/
706
707
708 /****f* Utilities/RB_Match
709  * FUNCTION
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
714  *   character.
715  *
716  *   Examples:
717  *      "*aap"   matches "aapaapaapaap"
718  *      "?inux"  matches "linux"
719  *      "lin*ux" matches "linux"
720  *      "linux*" matches "linux"
721  * NOTES
722  *   This is a recursive function.
723  * SYNOPSIS
724  *   int RB_Match( char* target, char* wildcard_expression )
725  * INPUTS
726  *   o target -- the string to be matched agains the
727  *               wildcard_expression.
728  *   o wildcard_expression -- the wildcard expression
729  * RETURN VALUE
730  *   TRUE  -- the target matches the wildcard expression
731  *   FALSE -- it does not match.
732  * SOURCE
733  */
734
735 int RB_Match(
736     char *target,
737     char *wildcard_expression )
738 {
739     if ( ( *wildcard_expression == '\0' ) && ( *target == '\0' ) )
740     {
741         /* a match, since both strings are now "" */
742         return TRUE;
743     }
744     else if ( *wildcard_expression == '\0' )
745     {
746         /* we reached the end of the wildcard_expression,
747          * but not the end of the target, this is not
748          * a match.
749          */
750         return FALSE;
751     }
752     else if ( *target == '\0' )
753     {
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.
757          */
758         unsigned int        i;
759
760         for ( i = 0; i < strlen( wildcard_expression ); ++i )
761         {
762             if ( wildcard_expression[i] != '*' )
763             {
764                 return FALSE;
765             }
766         }
767         return TRUE;
768     }
769     else
770     {
771         /* There are wildcard_expression characters left
772          * and target characters left.
773          */
774         char                wildcard = wildcard_expression[0];
775
776         if ( wildcard == '?' )
777         {
778             /* Match a single character and see if the
779              * rest of the target matches.
780              */
781             return RB_Match( target + 1, wildcard_expression + 1 );
782         }
783         else if ( wildcard == '*' )
784         {
785             int                 match = FALSE;
786             int                 l = strlen( target );
787             int                 i;
788
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. )
792              */
793             for ( i = l; i >= 0; --i )
794             {
795                 if ( RB_Match( target + i, wildcard_expression + 1 ) )
796                 {
797                     match = TRUE;
798                     break;
799                 }
800             }
801             return match;
802         }
803         else
804         {
805             int                 l_w = strlen( wildcard_expression );
806             int                 l_t = strlen( target );
807
808             /* The minimum of the length of the wildcard_expression
809              * and target expression 
810              */
811             int                 l = ( l_w <= l_t ) ? l_w : l_t;
812             int                 i;
813
814             for ( i = 0; i < l; ++i )
815             {
816                 if ( ( wildcard_expression[i] != '*' ) &&
817                      ( wildcard_expression[i] != '?' ) )
818                 {
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.
823                      */
824 #ifdef IGNORE_CASE_FILENAMES
825                     if ( tolower( wildcard_expression[i] ) !=
826                          tolower( target[i] ) )
827 #else
828                     if ( wildcard_expression[i] != target[i] )
829 #endif
830                     {
831                         return FALSE;
832                     }
833                 }
834                 else
835                 {
836                     return RB_Match( target + i, wildcard_expression + i );
837                 }
838             }
839             /* The first l characters of the target and
840              * wildcard_expression matched, now see if the rest
841              * matches too.
842              */
843             return RB_Match( target + l, wildcard_expression + l );
844         }
845     }
846 }
847
848 /******/
849
850
851 /****f* Utilities/RB_Swap
852  * FUNCTION
853  *   Swap two elements in a array of pointers.  This function is used
854  *   by RB_QuickSort().
855  * SOURCE
856  */
857
858 static void RB_Swap(
859     void **array,
860     int left,
861     int right )
862 {
863     void               *p = array[left];
864
865     array[left] = array[right];
866     array[right] = p;
867 }
868
869 /*****/
870
871
872 /****f* Utilities/RB_QuickSort
873  * FUNCTION
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.
878  * INPUTS
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
884  *              point to.
885  * RESULT
886  *   array -- A sorted array of pointers.
887  *
888  * EXAMPLE
889  *   The following is an example program that shows
890  *   the use
891  *    #define TEST_SIZE 10
892  *
893  *    char* test[ TEST_SIZE ] = { "ape", "zebra", 
894  *       "duck", "goofbal", "dodo", "rabit", 
895  *       "crow", "cow", "pig", "goat" };
896  *
897  *    int string_compare( void* p1, void* p2 )
898  *    {
899  *       char *cp1 = p1;
900  *       char *cp2 = p2;
901  *       return strcmp( cp1, cp2 );
902  *    }
903  *
904  *    RB_QuickSort( test, 0, TEST_SIZE - 1, string_compare ); 
905  *
906  * SOURCE
907  */
908
909 void RB_QuickSort(
910     void **array,
911     int left,
912     int right,
913     TCompare f )
914 {
915     int                 i;
916     int                 last;
917
918     if ( left >= right )
919     {
920         return;
921     }
922
923     RB_Swap( array, left, ( left + right ) / 2 );
924     last = left;
925     for ( i = left + 1; i <= right; ++i )
926     {
927         if ( ( *f ) ( array[i], array[left] ) < 0 )
928         {
929             RB_Swap( array, ++last, i );
930         }
931     }
932     RB_Swap( array, left, last );
933     RB_QuickSort( array, left, last - 1, f );
934     RB_QuickSort( array, last + 1, right, f );
935 }
936
937 /*******/
938
939
940 /****f* Utilities/RB_StripCR
941  * FUNCTION
942  *   Strip carriage return (CR) from line.
943  * INPUTS
944  *   * line -- line string to process
945  * SOURCE
946  */
947
948 void RB_StripCR(
949     char *line )
950 {
951     char               *c;
952
953     for ( c = line; *c; ++c )
954     {
955         if ( *c == '\n' )
956         {
957             *c = '\0';
958         }
959     }
960 }
961
962 /*******/
963
964
965 /****f* Utilities/RB_ContainsNL
966  * FUNCTION
967  *   Check whether the provided line buffer contains
968  *   a new line (NL) character.
969  * INPUTS
970  *   * line -- line string to process
971  * RETURN VALUE
972  *   * TRUE  -- the line contains a NL character
973  *   * FALSE -- the line does not contain a NL character
974  * SOURCE
975  */
976
977 int RB_ContainsNL(
978     char *line )
979 {
980     int                 found = 0;
981
982     for ( ; *line != '\0'; ++line )
983     {
984         if ( *line == '\n' )
985         {
986             found = 1;
987         }
988     }
989     return found;
990 }
991
992 /*******/
993
994
995 /****f* Utilities/RB_FreeLineBuffer
996  * FUNCTION
997  *   Free the dynamically allocated line-buffer
998  * INPUTS
999  *   * works on the globals line_buffer, readChars, myLine
1000  * SOURCE
1001  */
1002
1003 void RB_FreeLineBuffer(
1004      )
1005 {
1006     *line_buffer = '\0';
1007     free( myLine );
1008     myLine = NULL;
1009     readChars = 0;
1010 }
1011
1012 /*******/
1013
1014 /****f* Utilities/CR_LF_Conversion
1015  * FUNCTION
1016  *   Fix CR/LF problems.
1017  *
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.
1021  *
1022  * INPUTS
1023  *   * line -- a line of text
1024  * RETURN VALUE
1025  *   * number of characters that were removed.
1026  * SOURCE
1027  */
1028
1029 static int CR_LF_Conversion( char* line )
1030 {
1031     int n = strlen( line );
1032
1033     if ( ( n > 1 ) && 
1034           ( ( ( line[ n - 2 ] == '\r' ) && ( line [ n - 1 ] == '\n' ) ) 
1035             ||
1036             ( ( line[ n - 2 ] == '\n' ) && ( line [ n - 1 ] == '\r' ) ) 
1037           )
1038        )
1039     {
1040         line[ n - 2 ] = '\n';
1041         line[ n - 1 ] = '\0';
1042         return 1;
1043     } else {
1044         /* No problem */
1045         return 0;
1046     }
1047 }
1048
1049 /******/
1050
1051
1052 /****f* Utilities/RB_ReadWholeLine
1053  * FUNCTION
1054  *   Read a line from the file using the provided buffer.
1055  * INPUTS
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
1059  * RETURN VALUE
1060  *   * returns a dynamically allocated buffer containing the complete line
1061  *     read
1062  * NOTES
1063  *   If the line did not end in a new line (NL) character one is added.
1064  * SOURCE
1065  */
1066
1067 char               *RB_ReadWholeLine(
1068     FILE *file,
1069     char *buf,
1070     int *arg_readChars )
1071 {
1072     int                 foundNL = 0;
1073     char               *line = NULL;
1074     int                 curLineLen = 1;
1075     int                 chunkLen = 0;
1076
1077     clearerr( file );
1078     while ( ( !feof( file ) ) && ( !foundNL ) )
1079     {
1080         *buf = '\0';
1081         /* read next chunk */
1082         fgets( buf, MAX_LINE_LEN, file );
1083         if ( ferror( file ) )
1084         {
1085             /* an error occurred */
1086             RB_Panic( "I/O error %d! RB_ReadWholeLine()", errno );
1087         }
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 )
1092         {
1093             /* we run out of memory */
1094             RB_Panic( "Out of memory! RB_ReadWholeLine()" );
1095         }
1096         /* append the chunk to our buffer */
1097         strcpy( ( line + curLineLen - chunkLen - 1 ), buf );
1098
1099         if ( RB_ContainsNL( buf ) )
1100         {
1101             /* we are done - a line was read */
1102             foundNL = 1;
1103         }
1104     }
1105
1106     if ( !foundNL )
1107     {
1108         /* last line has no NL - add one */
1109         ++curLineLen;
1110         if ( ( line = realloc( line, sizeof( char ) * curLineLen ) ) == NULL )
1111         {
1112             /* we run out of memory */
1113             RB_Panic( "Out of memory! RB_ReadWholeLine()" );
1114         }
1115         line[curLineLen - 2] = '\n';
1116         line[curLineLen - 1] = '\0';
1117     }
1118
1119     /* This fixes any cr/lf problems. */
1120     curLineLen -= CR_LF_Conversion( line );
1121
1122     *arg_readChars = curLineLen;
1123     *buf = '\0';
1124     return line;
1125 }
1126
1127 /*******/
1128
1129
1130 /****f* Utilities/Stat_Path
1131  * FUNCTION
1132  *   Check the given path against required type.
1133  *   d -- directory, f -- file, e -- exists
1134  * RETURN VALUE
1135  *   TRUE if path is of the given type, otherwise FALSE.
1136  * BUGS
1137  *   Should check if symbolic link points to a directory or to a file.
1138  * SOURCE
1139  */
1140
1141 int Stat_Path(
1142     char required,
1143     char *path )
1144 {
1145     struct stat         st;
1146     int                 res = FALSE;
1147
1148     if ( stat( path, &st ) < 0 )
1149     {
1150         if ( required == 'e' )
1151         {
1152             res = FALSE;
1153         }
1154         else
1155         {
1156             if ( ( strcmp( path, "./" ) == 0 ) && ( required == 'd' ) )
1157             {
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. */
1161                 res = TRUE;
1162             }
1163             else
1164             {
1165                 RB_Panic( "Stat_Path: can not stat '%s'\n", path );
1166             }
1167         }
1168     }
1169     else
1170     {
1171         switch ( ( ( st.st_mode ) & S_IFMT ) )
1172         {
1173         case S_IFDIR:
1174             if ( ( required == 'd' ) || ( required == 'e' ) )
1175             {
1176                 res = TRUE;
1177             }
1178             break;
1179         case S_IFREG:
1180             if ( ( required == 'f' ) || ( required == 'e' ) )
1181             {
1182                 res = TRUE;
1183             }
1184             break;
1185             /* TODO case S_IFLNK: chdir() */
1186         default:
1187             break;
1188         }                       /* end switch */
1189     }
1190     return res;
1191 }
1192
1193 /*******/
1194
1195
1196 /****f* Utilities/RB_malloc
1197  * FUNCTION
1198  *   like malloc, but exit if malloc failed
1199  * RETURN VALUE
1200  *   See malloc
1201  * SOURCE
1202  */
1203
1204 void               *RB_malloc(
1205     size_t bytes )
1206 {
1207     void               *tmp;
1208
1209     tmp = malloc( bytes );
1210
1211     if ( tmp == NULL )
1212     {
1213         RB_Panic( "Unable to malloc %d bytes", bytes );
1214     }
1215
1216     return tmp;
1217 }
1218
1219 /*******/
1220
1221
1222 /****v* Utilities/RB_crc32_table
1223  * FUNCTION
1224  *   CRC32 lookup table for crc32 functions
1225  * SOURCE
1226  */
1227 static unsigned long *RB_crc32_table;
1228
1229 /*******/
1230
1231
1232 /****f* Utilities/Make_crc32_table
1233  * FUNCTION
1234  *   Builds up crc32 lookup table for crc32 function.
1235  *   Call this before calling to RB_crc32()
1236  * SOURCE
1237  */
1238 #define CRC32_GENERATOR_POLYNOMIAL      0xEDB88320
1239
1240 void Make_crc32_table(
1241     void )
1242 {
1243     unsigned int        i, n, carry;
1244     unsigned long       tmp;
1245
1246     // Allocate space for CRC table
1247     RB_crc32_table = RB_malloc( 256 * sizeof( unsigned long ) );
1248
1249     for ( i = 0; i < 256; i++ )
1250     {
1251         tmp = i;
1252         for ( n = 0; n < 8; n++ )
1253         {
1254             carry = tmp & 1;
1255             tmp >>= 1;
1256             if ( carry )
1257             {
1258                 tmp ^= CRC32_GENERATOR_POLYNOMIAL;
1259             }
1260         }
1261         RB_crc32_table[i] = tmp;
1262     }
1263 }
1264
1265 /*******/
1266
1267
1268 /****f* Utilities/RB_crc32
1269  * FUNCTION
1270  *   calcualtes crc32 value for a given buffer
1271  * INPUTS
1272  *   - buf:   input buffer to calculate
1273  *   - len:   length of buffer
1274  *   - crc32: CRC32 init value
1275  * RETURN VALUE
1276  *   CRC32 value
1277  * SOURCE
1278  */
1279 unsigned long RB_crc32(
1280     unsigned char *buf,
1281     unsigned int len,
1282     unsigned long crc32 )
1283 {
1284     unsigned char       tmp;
1285
1286     crc32 ^= 0xffffffffL;
1287
1288     while ( len-- )
1289     {
1290         tmp = *( buf++ ) ^ crc32;
1291         crc32 >>= 8;
1292         crc32 ^= RB_crc32_table[tmp];
1293     }
1294
1295     return crc32 ^= 0xffffffffL;
1296 }
1297
1298 /*******/
1299
1300
1301 /****f* Utilities/cwd
1302  * FUNCTION
1303  *   Holds current working directory
1304  * SOURCE
1305  */
1306 static char        *cwd = NULL;
1307
1308 /*******/
1309
1310 /****f* Utilities/RB_Get_Saved_CWD
1311  * FUNCTION
1312  *   Changes back to saved working directory and frees cwd string
1313  * SOURCE
1314  */
1315 char               *RB_Get_Saved_CWD(
1316     void )
1317 {
1318     return cwd;
1319 }
1320
1321 /*******/
1322
1323 /****f* Utilities/RB_Change_Back_To_CWD
1324  * FUNCTION
1325  *   Changes back to saved working directory and frees cwd string
1326  * SOURCE
1327  */
1328 void RB_Change_Back_To_CWD(
1329     void )
1330 {
1331     if ( cwd != NULL )
1332     {
1333         chdir( cwd );
1334         free( cwd );
1335         cwd = NULL;
1336     }
1337 }
1338
1339 /*******/
1340
1341 /****f* Utilities/RB_Change_To_Docdir
1342  * FUNCTION
1343  *   Saves current working directory and then changes to document dir
1344  * SOURCE
1345  */
1346 void RB_Change_To_Docdir(
1347     struct RB_Document *document )
1348 {
1349     char                tmp[TEMP_BUF_SIZE];
1350
1351     // Just for sure
1352     RB_Change_Back_To_CWD(  );
1353
1354     // Save CWD
1355     getcwd( tmp, sizeof( tmp ) );
1356     cwd = RB_StrDup( tmp );
1357
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 )
1361     {
1362         chdir( document->docroot->name );
1363     }
1364     else if ( course_of_action.do_singledoc )
1365     {
1366         int                 len = strrchr( document->singledoc_name, '/' )
1367             - document->singledoc_name;
1368         strncpy( tmp, document->singledoc_name, len );
1369         tmp[len] = 0;
1370         chdir( tmp );
1371     }
1372 }
1373
1374 /*******/
1375
1376
1377 /****f* Utilities/Open_Pipe
1378  * FUNCTION
1379  *   Opens a pipe and returns its handler
1380  * SOURCE
1381  */
1382 FILE               *Open_Pipe(
1383     char *pipe_name )
1384 {
1385     FILE               *a_pipe;
1386
1387 #if defined(RB_MSVC)
1388     a_pipe = _popen( pipe_name, "w" );
1389 #else
1390     a_pipe = popen( pipe_name, "w" );
1391 #endif
1392
1393     if ( a_pipe == NULL )
1394     {
1395         RB_Panic( "Unable to open pipe to '%s'", pipe_name );
1396     }
1397     return a_pipe;
1398 }
1399
1400 /*******/
1401
1402
1403 /****f* Utilities/Close_Pipe
1404  * FUNCTION
1405  *   Closes a given pipe
1406  * SOURCE
1407  */
1408 void Close_Pipe(
1409     FILE *arg_pipe )
1410 {
1411     if ( arg_pipe != NULL )
1412     {
1413 #if defined(RB_MSVC)
1414         _pclose( arg_pipe );
1415 #else
1416         pclose( arg_pipe );
1417 #endif
1418     }
1419 }
1420
1421 /*******/
1422
1423
1424 /* TODO Move this to a separate file */
1425 /* TODO grow this into a whole set of utf8 routines. */
1426 /* TODO Documentation */
1427
1428 int utf8_isalnum(
1429     unsigned int arg_c )
1430 {
1431     return ( ( arg_c < 128 ) && isalnum( arg_c ) );
1432 }
1433
1434 int utf8_isalpha(
1435     unsigned int arg_c )
1436 {
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 ) ) );
1441 }
1442
1443 int utf8_iscntrl(
1444     unsigned int arg_c )
1445 {
1446     return ( ( arg_c < 128 ) && iscntrl( arg_c ) );
1447 }
1448
1449 int utf8_isdigit(
1450     unsigned int arg_c )
1451 {
1452     return ( ( arg_c < 128 ) && isdigit( arg_c ) );
1453 }
1454
1455 int utf8_isgraph(
1456     unsigned int arg_c )
1457 {
1458     return ( ( arg_c < 128 ) && isgraph( arg_c ) );
1459 }
1460
1461 int utf8_islower(
1462     unsigned int arg_c )
1463 {
1464     return ( ( arg_c < 128 ) && islower( arg_c ) );
1465 }
1466
1467 int utf8_isprint(
1468     unsigned int arg_c )
1469 {
1470     return ( ( arg_c < 128 ) && isprint( arg_c ) );
1471 }
1472
1473 int utf8_ispunct(
1474     unsigned int arg_c )
1475 {
1476     return ( ( arg_c < 128 ) && ispunct( arg_c ) );
1477 }
1478
1479 int utf8_isspace(
1480     unsigned int arg_c )
1481 {
1482     return ( ( arg_c < 128 ) && isspace( arg_c ) );
1483 }
1484
1485 int utf8_isupper(
1486     unsigned int arg_c )
1487 {
1488     return ( ( arg_c < 128 ) && isupper( arg_c ) );
1489 }
1490
1491 int utf8_isxdigit(
1492     unsigned int arg_c )
1493 {
1494     return ( ( arg_c < 128 ) && isxdigit( arg_c ) );
1495 }