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