2 Copyright (C) 1994-2007 Frans Slothouber, Jacco van Weert, Petteri Kettunen,
3 Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
4 Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
6 This file is part of ROBODoc
8 ROBODoc is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 /****h* ROBODoc/Directory
25 * This module contains function to scan a directory tree and to
26 * create a RB_Directory structure. Most of the OS dependend parts
27 * of ROBODoc can be found in this module..
29 * This module currently works under:
30 * o Cygwin http://cygwin.com
32 * o VC++ under Windows NT
33 * o MINGW http://www.mingw.org/
39 * $Id: directory.c,v 1.41 2007/07/10 19:13:51 gumpu Exp $
45 #include "directory.h"
48 #include "roboconfig.h"
50 #if defined (RB_MSVC) || defined(__MINGW32__)
53 # include <sys/types.h>
57 # if defined _DIRENT_HAVE_D_TYPE
60 # include <sys/stat.h>
63 /* no dirent in strict ansi !!! */
76 /****v* Directory/content_buffer
78 * Temporary buffer file file content and filenames.
82 #define RB_CBUFFERSIZE 8191
83 char content_buffer[RB_CBUFFERSIZE + 1];
89 static int RB_Is_PathCharacter(
93 #if defined (RB_MSVC) || defined(__MINGW32__)
97 /****f* Directory/RB_FileType
99 * Determine the type of the file. This function is used for all
100 * compilers except VC++.
102 * If _DIRENT_HAVE_D_TYPE is defined we can find the filetype
103 * directly in the a_direntry. If not we have to stat the file to
107 T_RB_FileType RB_FileType(
109 struct dirent *a_direntry )
112 * o arg_pathname -- the path leading up to this file
113 * o a_direntry -- a directory entry read by readdir()
115 * The type of the file.
119 T_RB_FileType file_type = RB_FT_UNKNOWN;
121 #if defined _DIRENT_HAVE_D_TYPE
122 if ( a_direntry->d_type == DT_REG )
124 file_type = RB_FT_FILE;
126 else if ( a_direntry->d_type == DT_DIR )
128 file_type = RB_FT_DIRECTORY;
132 file_type = RB_FT_UNKNOWN;
135 if ( file_type == RB_FT_UNKNOWN )
137 char *file_name = a_direntry->d_name;
138 struct stat filestat;
141 /* Either we do not have d_type, or it gives
142 * no information, so we try it a different
145 content_buffer[0] = '\0';
146 strcat( content_buffer, arg_pathname );
147 if ( content_buffer[strlen( content_buffer ) - 1] != '/' )
149 strcat( content_buffer, "/" );
151 strcat( content_buffer, file_name );
152 result = stat( content_buffer, &filestat );
155 if ( S_ISREG( filestat.st_mode ) )
157 file_type = RB_FT_FILE;
159 else if ( S_ISDIR( filestat.st_mode ) )
161 file_type = RB_FT_DIRECTORY;
165 file_type = RB_FT_UNKNOWN;
170 /* Some error occurred while statting the file */
177 /****f* Directory/RB_Directory_Insert_RB_Path
181 void RB_Directory_Insert_RB_Path(
182 struct RB_Directory *arg_rb_directory,
183 struct RB_Path *arg_rb_path )
186 * Insert a RB_Path into a RB_Directory.
187 * The RB_Path is added at the beginning of the list.
191 arg_rb_path->next = arg_rb_directory->first_path;
192 arg_rb_directory->first_path = arg_rb_path;
197 /****f* Directory/RB_Directory_Insert_RB_File
200 void RB_Directory_Insert_RB_Filename(
201 struct RB_Directory *arg_rb_directory,
202 struct RB_Filename *arg_rb_filename )
205 * Insert an RB_File structure into a RB_Directory structure.
206 * The RB_File is added at the end of the list.
210 if ( arg_rb_directory->last == 0 )
212 arg_rb_directory->first = arg_rb_filename;
213 arg_rb_filename->next = 0;
214 arg_rb_directory->last = arg_rb_filename;
218 arg_rb_directory->last->next = arg_rb_filename;
219 arg_rb_filename->next = 0;
220 arg_rb_directory->last = arg_rb_filename;
226 /****f* Directory/RB_Free_RB_Directory
228 * Free all the memory use by the RB_Directory structure.
231 void RB_Free_RB_Directory(
232 struct RB_Directory *arg_directory )
235 * o arg_directory -- the thing to be freed.
239 struct RB_Filename *rb_filename;
240 struct RB_Filename *rb_filename2;
241 struct RB_Path *rb_path;
242 struct RB_Path *rb_path2;
244 /* TODO Not complete... check for leaks. */
245 rb_filename = arg_directory->first;
247 while ( rb_filename )
249 rb_filename2 = rb_filename;
250 rb_filename = rb_filename->next;
251 RB_Free_RB_Filename( rb_filename2 );
254 rb_path = arg_directory->first_path;
259 rb_path = rb_path->next;
260 RB_Free_RB_Path( rb_path2 );
263 free( arg_directory );
269 /****f* Directory/RB_Get_RB_Directory
271 * RB_Get_RB_Directory -- get a RB_Directory structure
273 * Returns a RB_Directory structure to the give directory,
274 * specified by the path.
275 * This structure can then be uses to walk through all the
276 * files in the directory and it's subdirectories.
279 struct RB_Directory *RB_Get_RB_Directory(
280 char *arg_rootpath_name,
281 char *arg_docroot_name )
284 * arg_rootpath -- the name a the directory to get,
285 * a nul terminated string.
286 * arg_docroot_name -- the name of the directory the documentation
287 * file are stored in. This directory is
288 * skipped while scanning for sourcefiles.
291 * A freshly allocated RB_Directory filled with source files.
295 struct RB_Directory *rb_directory;
296 struct RB_Path *doc_path = NULL;
299 ( struct RB_Directory * ) malloc( sizeof( struct RB_Directory ) );
300 rb_directory->first = 0;
301 rb_directory->last = 0;
302 rb_directory->first_path = RB_Get_RB_Path( arg_rootpath_name );
304 if ( arg_docroot_name )
306 doc_path = RB_Get_RB_Path( arg_docroot_name );
309 RB_Fill_Directory( rb_directory, rb_directory->first_path, doc_path );
310 if ( ( RB_Number_Of_Filenames( rb_directory ) > 0 ) &&
311 ( RB_Number_Of_Paths( rb_directory ) > 0 ) )
313 RB_SortDirectory( rb_directory );
318 RB_Panic( "No files found! (Or all were filtered out)\n" );
327 /****f* Directory/RB_Get_RB_SingleFileDirectory
329 * RB_Get_RB_SingleFileDirectory -- get a RB_Directory structure
332 struct RB_Directory *RB_Get_RB_SingleFileDirectory(
336 * Returns a RB_Directory structure to the give directory,
337 * specified by the path that contains only a single file.
338 * This is used for the --singlefile option.
340 * o filename -- a filename. This may include the path.
342 * a freshly allocated RB_Directory that contains only
347 struct RB_Directory *rb_directory;
348 struct RB_Path *path;
349 char *pathname = NULL;
350 char *filename = NULL;
352 assert( arg_fullpath );
354 pathname = RB_Get_PathName( arg_fullpath );
355 filename = RB_Get_FileName( arg_fullpath );
359 path = RB_Get_RB_Path( pathname );
363 /* no directory was specified so we use
364 * the current directory
366 path = RB_Get_RB_Path( "./" );
370 ( struct RB_Directory * ) malloc( sizeof( struct RB_Directory ) );
371 rb_directory->first = 0;
372 rb_directory->last = 0;
374 rb_directory->first_path = path;
376 RB_Directory_Insert_RB_Filename( rb_directory,
377 RB_Get_RB_Filename( filename, path ) );
385 /****f* Directory/RB_Fill_Directory
387 * RB_Fill_Directory -- fill a RB_Directory structure
390 void RB_Fill_Directory(
391 struct RB_Directory *arg_rb_directory,
392 struct RB_Path *arg_path,
393 struct RB_Path *arg_doc_path )
396 * Walks through all the files in the directory pointed to
397 * by arg_path and adds all the files to arg_rb_directory.
398 * This is a recursive function.
400 * o arg_rb_directory -- the result.
401 * o arg_path -- the current path that is scanned.
402 * o arg_doc_path -- the path to the documentation files.
404 * a RB_Directory structure filled with all sourcefiles and
405 * subdirectories in arg_path.
407 * This a is a recursive function.
412 #if defined (RB_MSVC) || defined(__MINGW32__)
413 struct _finddata_t c_file;
415 char *wildcard = NULL;
418 RB_Say( "Scanning %s\n", SAY_INFO, arg_path->name );
419 len = strlen( arg_path->name ) + strlen( "*.*" ) + 1;
420 wildcard = ( char * ) malloc( len );
423 strcat( wildcard, arg_path->name );
424 strcat( wildcard, "*.*" );
425 if ( ( hFile = _findfirst( wildcard, &c_file ) ) == -1L )
427 RB_Panic( "No files found!\n" );
435 if ( ( c_file.attrib & _A_SUBDIR ) )
437 /* It's a directory */
438 if ( ( strcmp( ".", c_file.name ) == 0 )
439 || ( strcmp( "..", c_file.name ) == 0 ) )
441 /* Don't recurse into . or ..
442 because that will result in an infinite
447 if ( RB_To_Be_Skipped( c_file.name ) )
449 /* User asked this directory to be skipped */
453 if ( course_of_action.do_nodesc )
455 /* Don't descent into the subdirectories */
459 struct RB_Path *rb_path =
460 RB_Get_RB_Path2( arg_path->name,
464 && strcmp( rb_path->name,
465 arg_doc_path->name ) )
468 rb_path->parent = arg_path;
469 RB_Directory_Insert_RB_Path( arg_rb_directory,
471 RB_Fill_Directory( arg_rb_directory, rb_path,
476 RB_Say( "skipping %s\n", SAY_INFO,
485 if ( RB_Is_Source_File( arg_path, c_file.name ) )
487 RB_Directory_Insert_RB_Filename( arg_rb_directory,
494 /* It's not a sourcefile so we skip it. */
497 /* Find the rest of the *.* files */
498 found = ( _findnext( hFile, &c_file ) == 0 );
504 struct dirent *a_direntry;
507 RB_Say( "Scanning %s\n", SAY_INFO, arg_path->name );
508 a_dirstream = opendir( arg_path->name );
512 T_RB_FileType file_type;
514 for ( a_direntry = readdir( a_dirstream );
515 a_direntry; a_direntry = readdir( a_dirstream ) )
517 file_type = RB_FileType( arg_path->name, a_direntry );
518 if ( file_type == RB_FT_FILE )
520 /* It is a regular file. See if it is a sourcefile. */
521 if ( RB_Is_Source_File( arg_path, a_direntry->d_name ) )
523 /* It is, so we add it to the directory tree */
524 RB_Directory_Insert_RB_Filename( arg_rb_directory,
526 ( a_direntry->d_name,
531 /* It's not a sourcefile so we skip it. */
534 else if ( file_type == RB_FT_DIRECTORY )
536 if ( ( strcmp( ".", a_direntry->d_name ) == 0 ) ||
537 ( strcmp( "..", a_direntry->d_name ) == 0 ) )
539 /* Don't recurse into . or ..
540 because that will result in an infinite */
544 if ( RB_To_Be_Skipped( a_direntry->d_name ) )
546 /* User asked this directory to be skipped */
550 if ( course_of_action.do_nodesc )
552 /* Don't descent into the subdirectories */
556 struct RB_Path *rb_path =
557 RB_Get_RB_Path2( arg_path->name,
558 a_direntry->d_name );
560 rb_path->parent = arg_path;
562 && strcmp( rb_path->name,
563 arg_doc_path->name ) )
566 RB_Directory_Insert_RB_Path( arg_rb_directory,
568 RB_Fill_Directory( arg_rb_directory, rb_path,
573 RB_Say( "skipping %s\n", SAY_INFO,
582 /* Not a file and also not a directory */
586 closedir( a_dirstream );
592 /****f* Directory/RB_Is_Source_File
594 * RB_Is_Source_File -- Is a file a sourcefile?
597 int RB_Is_Source_File(
598 struct RB_Path *path,
602 * This functions examines the content of a file to
603 * see whether or not it is a sourcefile.
605 * Currently it checks if there are no nul characters
606 * in the first 8191 characters of the file.
612 if ( RB_Not_Accepted( filename ) )
614 /* File needs to be skipped based on it's name */
619 unsigned int size = 0;
621 /* Compute the length of the filename including
623 size += strlen( path->name );
625 size += strlen( filename );
626 if ( size < RB_CBUFFERSIZE )
630 /* We use the content_buffer buffer temporarily to
631 store the filename. */
632 content_buffer[0] = '\0';
633 strcat( content_buffer, path->name );
634 strcat( content_buffer, filename );
635 if ( ( file = fopen( content_buffer, "rb" ) ) )
639 fread( content_buffer, sizeof( char ), RB_CBUFFERSIZE,
645 for ( c = content_buffer; no_read; --no_read, c++ )
656 /* A file with only 9 characters can not
657 contain any source plus documentation. */
664 /* The file could not be opened. */
670 /* The filename is longer than 8191 characters,
671 that's way too long... so we skip it. */
681 /****f* Directory/RB_To_Be_Skipped
683 * Test if a file should not be included in the list of source files
684 * that are scanned for documentation.
686 * This test is done based on the wildcard expressions specified
687 * in configuration.ignore_files.
690 int RB_To_Be_Skipped(
694 * o filename -- the name of the file
701 for ( i = 0; i < configuration.ignore_files.number; ++i )
703 if ( RB_Match( filename, configuration.ignore_files.names[i] ) )
715 /****f* Directory/RB_Not_Accepted
717 * Test if a file should be skipped,
718 * because it does not match a pattern in "accept files:"
720 * This test is done based on the wildcard expressions specified
721 * in configuration.accept_files.
728 * o filename -- the name of the file
735 skip = RB_To_Be_Skipped( filename );
737 if ( !skip && configuration.accept_files.number > 0 )
740 for ( i = 0; i < configuration.accept_files.number; ++i )
742 if ( RB_Match( filename, configuration.accept_files.names[i] ) )
744 RB_Say( "accept >%s< with >%s<\n", SAY_INFO, filename,
745 configuration.accept_files.names[i] );
757 /****f* Directory/RB_Get_FileName
759 * RB_Get_PathName -- extract the file name
762 char *RB_Get_FileName(
766 * Given a full path to a file, that is a filename, or a filename
767 * prefixed with a pathname, return the filename.
769 * "./filename" returns "filename"
770 * "/home/et/filename" returns "filename"
771 * "filename" return "filename"
773 * arg_fullpath -- a full path to a file, with or without a path.
776 * 0 -- The full path did not contain a filename
777 * pointer to the filename -- otherwise.
779 * You are responsible for deallocating it.
788 assert( arg_fullpath );
790 n = strlen( arg_fullpath );
792 /* Try and find a path character ( ':' or '/' ) */
793 for ( found = FALSE, i = 0; i < n; ++i )
795 if ( RB_Is_PathCharacter( arg_fullpath[i] ) )
804 /* The full path does not contain a pathname,
805 * so we can return the arg_fullpath as
808 result = RB_StrDup( arg_fullpath );
812 /* The full path does contain a pathname,
813 * strip this and return the remainder
816 for ( i = n - 1; i > 0; --i )
818 if ( RB_Is_PathCharacter( arg_fullpath[i] ) )
820 assert( i < ( n - 1 ) );
821 result = RB_StrDup( &( arg_fullpath[i + 1] ) );
834 /****f* Directory/RB_Get_PathName
836 * RB_Get_PathName -- extract the path name
839 char *RB_Get_PathName(
843 * Given a full path to a file, that is a filename, or a filename
844 * prefixed with a pathname, return the pathname.
846 * "./filename" returns "./"
847 * "/home/et/filename" returns "/home/et/"
848 * "filename" return ""
850 * arg_fullpath -- a full path to a file, with or without a path.
853 * 0 -- The full path did not contain a path
854 * pointer to the pathname -- otherwise.
856 * You are responsible for deallocating it.
865 assert( arg_fullpath );
867 n = strlen( arg_fullpath );
869 /* Try and find a path character ( ':' or '/' ) */
870 for ( found = FALSE, i = 0; i < n; ++i )
872 if ( RB_Is_PathCharacter( arg_fullpath[i] ) )
881 /* Copy the whole file name and then
882 replace the character after the
883 first path character found
884 counting from the back with a '\0'
886 result = RB_StrDup( arg_fullpath );
888 for ( i = n - 1; i > 0; --i )
890 if ( RB_Is_PathCharacter( result[i] ) )
892 assert( i < ( n - 1 ) );
893 result[i + 1] = '\0';
905 /****f* Directory/RB_Is_PathCharacter
907 * Test if a character is part of the group of
908 * characters that you would normally find in
912 static int RB_Is_PathCharacter(
916 * c -- the character to be tested.
918 * TRUE -- it is a path character.
919 * FALSE -- it is not.
923 return ( ( c == ':' ) || ( c == '/' ) );
929 /* Sort the files and paths */
931 /* TODO FS Document */
932 unsigned int RB_Number_Of_Filenames(
933 struct RB_Directory *arg_rb_directory )
935 unsigned int number_of_filenames = 0;
936 struct RB_Filename *rb_filename = NULL;
938 for ( rb_filename = arg_rb_directory->first;
940 ++number_of_filenames, rb_filename = rb_filename->next )
944 return number_of_filenames;
948 /* TODO FS Document */
949 unsigned int RB_Number_Of_Paths(
950 struct RB_Directory *arg_rb_directory )
952 unsigned int number_of_paths = 0;
953 struct RB_Path *rb_path = NULL;
955 for ( rb_path = arg_rb_directory->first_path;
956 rb_path; ++number_of_paths, rb_path = rb_path->next )
960 return number_of_paths;
963 /* TODO FS Document */
968 struct RB_Path *path_1 = p1;
969 struct RB_Path *path_2 = p2;
971 return RB_Str_Case_Cmp( path_1->name, path_2->name );
974 /* TODO FS Document */
975 int RB_Filename_Compare(
979 struct RB_Filename *filename_1 = p1;
980 struct RB_Filename *filename_2 = p2;
982 return RB_Str_Case_Cmp( filename_1->name, filename_2->name );
986 /* TODO FS Document */
987 void RB_SortDirectory(
988 struct RB_Directory *arg_rb_directory )
990 unsigned int number_of_filenames =
991 RB_Number_Of_Filenames( arg_rb_directory );
992 unsigned int number_of_paths =
993 RB_Number_Of_Paths( arg_rb_directory );
995 struct RB_Path *rb_path = NULL;
996 struct RB_Filename *rb_filename = NULL;
997 struct RB_Path **paths = NULL;
998 struct RB_Filename **filenames = NULL;
1000 assert( number_of_filenames > 0 );
1001 assert( number_of_paths > 0 );
1002 paths = calloc( number_of_paths, sizeof( struct RB_Path * ) );
1003 filenames = calloc( number_of_filenames, sizeof( struct RB_Filename * ) );
1006 RB_Say( "Sorting Directory\n", SAY_INFO );
1007 for ( i = 0, rb_path = arg_rb_directory->first_path;
1008 rb_path; rb_path = rb_path->next )
1010 assert( i < number_of_paths );
1015 for ( i = 0, rb_filename = arg_rb_directory->first;
1016 rb_filename; rb_filename = rb_filename->next )
1018 assert( i < number_of_filenames );
1019 filenames[i] = rb_filename;
1023 RB_QuickSort( ( void ** ) paths, 0, number_of_paths - 1,
1025 RB_QuickSort( ( void ** ) filenames, 0, number_of_filenames - 1,
1026 RB_Filename_Compare );
1028 for ( i = 0; i < number_of_paths - 1; ++i )
1030 paths[i]->next = paths[i + 1];
1032 paths[number_of_paths - 1]->next = NULL;
1033 arg_rb_directory->first_path = paths[0];
1035 for ( i = 0; i < number_of_filenames - 1; ++i )
1037 filenames[i]->next = filenames[i + 1];
1039 filenames[number_of_filenames - 1]->next = NULL;
1040 arg_rb_directory->first = filenames[0];
1041 arg_rb_directory->last = filenames[number_of_filenames - 1];