1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: filesrch.c 1194 2015-12-26 19:08:47Z wesleyjohnson $
5 //
6 // Portions Copyright (C) 1998-2015 by DooM Legacy Team.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
12 //
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.
17 //
18 // DESCRIPTION:
19 // Search directories, in depth, for a filename.
20 //
21 //-----------------------------------------------------------------------------
22
23
24 #include <stdio.h>
25 #include <dirent.h>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <stdlib.h>
31
32 #include "d_netfil.h"
33
34 #define MAX_SRCHPATH (MAX_WADPATH * 2)
35
36 //
37 // sys_filesearch:
38 //
39 // filename : the filename to be found
40 // wantedmd5sum : NULL for no md5 check
41 // completepath : when not NULL, return the full path and name
42 // must be a buffer of MAX_WADPATH
43 // maxsearchdepth : dir depth, when 0 only search given directory
44 // return FS_NOTFOUND
45 // FS_MD5SUMBAD
46 // FS_FOUND
47
sys_filesearch(const char * filename,const char * startpath,const byte * wantedmd5sum,int maxsearchdepth,char * completepath)48 filestatus_e sys_filesearch( const char * filename, const char * startpath,
49 const byte * wantedmd5sum, int maxsearchdepth,
50 /*OUT*/ char * completepath )
51 {
52 filestatus_e retval = FS_NOTFOUND;
53 DIR ** dirhandle_stack; // (malloc)
54 DIR * dirhandle;
55 int * index_stack; // each directory in the searchpath (malloc)
56 int depth=0;
57 struct dirent *dent;
58 struct stat fstat;
59 int cur_index, remspace; // searchpath
60 char searchpath[MAX_SRCHPATH];
61
62 if( maxsearchdepth < 1 )
63 maxsearchdepth = 0;
64 dirhandle_stack = (DIR**) malloc( (maxsearchdepth+1) * sizeof( DIR*));
65 if( dirhandle_stack == NULL ) goto error1_exit;
66 index_stack = (int*) malloc( (maxsearchdepth+1) * sizeof(int));
67 if( index_stack == NULL ) goto error2_exit;
68
69 strncpy( searchpath, startpath, MAX_SRCHPATH-1 );
70 searchpath[MAX_SRCHPATH-1] = '\0';
71 cur_index = strlen( searchpath) + 1;
72
73 dirhandle = opendir( searchpath);
74 if( dirhandle == NULL ) goto error3_exit;
75
76 // Initial stack
77 index_stack[0] = cur_index;
78 dirhandle_stack[0] = dirhandle;
79
80 if(searchpath[cur_index-2] != '/')
81 {
82 searchpath[cur_index-1] = '/';
83 searchpath[cur_index] = 0;
84 }
85 else
86 {
87 cur_index--;
88 }
89
90 for(;;)
91 {
92 searchpath[cur_index]=0;
93 dent = readdir( dirhandle ); // next dir entry
94 if( !dent) // done with dir
95 {
96 closedir( dirhandle );
97 // Pop stack to previous directory.
98 if( depth == 0 ) break; // done
99 cur_index = index_stack[--depth];
100 dirhandle = dirhandle_stack[depth];
101 continue;
102 }
103 if( dent->d_name[0]=='.' )
104 {
105 // ignore the "." and ".." entries, we don't want to scan uptree
106 if( dent->d_name[1]=='\0' ) continue;
107 if( dent->d_name[1]=='.' && dent->d_name[2]=='\0' ) continue;
108 }
109
110 // Form file name for stat.
111 remspace = (MAX_SRCHPATH - 1) - cur_index;
112 strncpy(&searchpath[cur_index], dent->d_name, remspace);
113
114 if( stat(searchpath,&fstat) < 0) // do we want to follow symlinks? if not: change it to lstat
115 {
116 // was the file (re)moved? can't stat it
117 continue;
118 }
119
120 if( S_ISDIR(fstat.st_mode) )
121 {
122 if( depth >= maxsearchdepth ) continue; // depth limited
123
124 dirhandle = opendir(searchpath);
125 if( dirhandle == NULL )
126 {
127 // can't open it... maybe no read-permissions
128 // go back to previous dir
129 cur_index = index_stack[depth];
130 dirhandle = dirhandle_stack[depth];
131 continue;
132 }
133
134 // Push new directory to stack.
135 cur_index = strlen(searchpath) + 1;
136 index_stack[++depth] = cur_index;
137 dirhandle_stack[depth] = dirhandle;
138
139 searchpath[cur_index-1]='/';
140 searchpath[cur_index]=0;
141 }
142 else if ( strcasecmp(filename, dent->d_name) == 0 )
143 {
144 // Found the filename.
145 retval = checkfile_md5(searchpath, wantedmd5sum);
146 if( retval != FS_FOUND ) continue;
147
148 // FS_FOUND, Return the found file.
149 if(completepath)
150 {
151 strncpy(completepath, searchpath, MAX_WADPATH-1);
152 completepath[MAX_WADPATH-1] = '\0';
153 }
154 // Close the open directories.
155 for(; depth >= 0; closedir(dirhandle_stack[depth--]));
156 break;
157 }
158 }
159
160 error3_exit:
161 free(index_stack);
162 error2_exit:
163 free(dirhandle_stack);
164 error1_exit:
165
166 return retval;
167 }
168