1 /*****************************************************************************/
2 /*									     */
3 /*				  FILECOLL.CC				     */
4 /*									     */
5 /* (C) 1995-97	Ullrich von Bassewitz					     */
6 /*		Wacholderweg 14						     */
7 /*		D-70597 Stuttgart					     */
8 /* EMail:	uz@ibb.schwaben.com					     */
9 /*									     */
10 /*****************************************************************************/
11 
12 
13 
14 // $Id$
15 //
16 // $Log$
17 //
18 //
19 
20 
21 
22 // This module produces warnings about unreachable code under DOS and OS/2.
23 // You can safely ignore this warnings.
24 
25 
26 
27 #include <errno.h>
28 #ifdef __WATCOMC__
29 #  include <direct.h>
30 #else
31 #  include <sys/types.h>
32 #  include <dirent.h>
33 #endif
34 
35 #include "streamid.h"
36 #include "filesys.h"
37 #include "filepath.h"
38 #include "filecoll.h"
39 
40 
41 
42 // Register the classes
43 LINK (FileInfo, ID_FileInfo);
44 LINK (FileInfoColl, ID_FileInfoColl);
45 
46 
47 
48 /*****************************************************************************/
49 /*			Explicit template instantiation			     */
50 /*****************************************************************************/
51 
52 
53 
54 #ifdef EXPLICIT_TEMPLATES
55 template class Collection<FileInfo>;
56 template class SortedCollection<FileInfo, String>;
57 #endif
58 
59 
60 
61 /*****************************************************************************/
62 /*				class FileInfo				     */
63 /*****************************************************************************/
64 
65 
66 
Init()67 void FileInfo::Init ()
68 // Initialize - is called from the constructors
69 {
70     // Try to stat the file
71     struct stat Buf;
72     if (stat (Name.GetStr (), &Buf) != 0) {
73 	Error = errno;
74 	return;
75     }
76 
77     // Reset the error code and transfer the information to the object
78     Error	= 0;
79     Dev		= Buf.st_dev;
80     Inode	= Buf.st_ino;
81     Mode	= Buf.st_mode;
82     LinkCount	= Buf.st_nlink;
83     UID		= Buf.st_uid;
84     GID		= Buf.st_gid;
85     RDev	= Buf.st_rdev;
86     Size	= Buf.st_size;
87     ATime	= Buf.st_atime;
88     MTime	= Buf.st_mtime;
89     CTime	= Buf.st_ctime;
90 }
91 
92 
93 
FileInfo(const FileInfo & FI)94 FileInfo::FileInfo (const FileInfo& FI):
95     Name (FI.Name),
96     Error (FI.Error),
97     Dev (FI.Dev),
98     Inode (FI.Inode),
99     Mode (FI.Mode),
100     LinkCount (FI.LinkCount),
101     UID (FI.UID),
102     GID (FI.GID),
103     RDev (FI.RDev),
104     Size (FI.Size),
105     ATime (FI.ATime),
106     MTime (FI.MTime),
107     CTime (FI.CTime)
108 {
109 }
110 
111 
112 
operator =(const FileInfo & rhs)113 FileInfo& FileInfo::operator = (const FileInfo& rhs)
114 // Assignment operator
115 {
116     // Beware! Ignore FI = FI
117     if (&rhs != this) {
118 	Name	  = rhs.Name;
119 	Error	  = rhs.Error;
120 	Dev	  = rhs.Dev;
121 	Inode	  = rhs.Inode;
122 	Mode	  = rhs.Mode;
123 	LinkCount = rhs.LinkCount;
124 	UID	  = rhs.UID;
125 	GID	  = rhs.GID;
126 	RDev	  = rhs.RDev;
127 	Size	  = rhs.Size;
128 	ATime	  = rhs.ATime;
129 	MTime	  = rhs.MTime;
130 	CTime	  = rhs.CTime;
131     }
132     return *this;
133 }
134 
135 
136 
Load(Stream & S)137 void FileInfo::Load (Stream& S)
138 {
139     S >> Name >> Error >> Dev >> Inode >> Mode >> LinkCount >> UID >> GID
140       >> RDev >> Size >> ATime >> MTime >> CTime;
141 }
142 
143 
144 
Store(Stream & S) const145 void FileInfo::Store (Stream& S) const
146 {
147     S << Name << Error << Dev << Inode << Mode << LinkCount << UID << GID
148       << RDev << Size << ATime << MTime << CTime;
149 }
150 
151 
152 
StreamableID() const153 u16 FileInfo::StreamableID () const
154 {
155     return ID_FileInfo;
156 }
157 
158 
159 
Build()160 Streamable* FileInfo::Build ()
161 {
162     return new FileInfo (Empty);
163 }
164 
165 
166 
PermStr() const167 String FileInfo::PermStr () const
168 // Create a file permission string from the mode
169 {
170     char S [11];
171 
172     // Calling this function with errors is invalid
173     PRECONDITION (Error == 0);
174 
175     // Set file type
176     if (S_ISREG (Mode)) {
177 	S [0] = '-';
178     } else if (S_ISDIR (Mode)) {
179 	S [0] = 'd';
180 #ifdef UNIXLIKE_OS
181     } else if (S_ISLNK (Mode)) {
182 	S [0] = 'l';
183     } else if (S_ISFIFO (Mode)) {
184 	S [0] = 'f';
185     } else if (S_ISSOCK (Mode)) {
186 	S [0] = 's';
187     } else if (S_ISBLK (Mode)) {
188 	S [0] = 'b';
189 #endif
190     } else if (S_ISCHR (Mode)) {
191 	S [0] = 'c';
192     }
193 
194     S [1] = (Mode & S_IRUSR) ? 'r' : '-';
195     S [2] = (Mode & S_IWUSR) ? 'w' : '-';
196     S [3] = (Mode & S_ISUID) ? 's' : (Mode & S_IXUSR) ? 'x' : '-';
197     S [4] = (Mode & S_IRGRP) ? 'r' : '-';
198     S [5] = (Mode & S_IWGRP) ? 'w' : '-';
199     S [6] = (Mode & S_ISGID) ? 's' : (Mode & S_IXGRP) ? 'x' : '-';
200     S [7] = (Mode & S_IROTH) ? 'r' : '-';
201     S [8] = (Mode & S_IWOTH) ? 'w' : '-';
202     S [9] = (Mode & S_ISVTX) ? 't' : (Mode & S_IXOTH) ? 'x' : '-';
203     S [10] = '\0';
204 
205     // return the result
206     return String (S);
207 }
208 
209 
210 
211 /*****************************************************************************/
212 /*			      class FileInfoColl			     */
213 /*****************************************************************************/
214 
215 
216 
ReadFiles(String Dir,const String & FileSpec,unsigned ModeAnd,unsigned ModeXor)217 int FileInfoColl::ReadFiles (String Dir,
218 			     const String& FileSpec,
219 			     unsigned ModeAnd,
220 			     unsigned ModeXor)
221 // Create a collection of files in the given directory. Files not matching
222 // FileSpec are not inserted. The mode of all files found are xor'ed with
223 // ModeXor, than and'ed with ModeAnd. If the result is zero, the file is
224 // not inserted into the collection. This is a convienient way to choose
225 // files by mode.
226 {
227     // Delete the old contents of the collection
228     DeleteAll ();
229 
230     // If the directory is empty, use the current dir
231     if (Dir.IsEmpty ()) {
232 	Dir = GetCurrentDir ();
233     }
234 
235     // Delete a trailing path separator
236     DelPathSep (Dir);
237 
238     // Remember if the file system, the directory resides on, preserves the
239     // case of the names
240     int PreservesCase = FileSysPreservesCase (Dir);
241 
242     // Open the directory stream
243     DIR* D;
244 #if (defined (DOS) && (__BCPLUSPLUS__==0x310)) || (defined (DOS32) && defined (__GO32__))
245     // BC++ 3.1 for DOS and djgpp both have a buggy declaration for opendir():
246     // the parameter is not declared as const. I'll assume that the function
247     // will not change the name and cast away the constness (*sigh*)
248     if ((D = opendir ((char*) Dir.GetStr ())) == NULL) {
249 #else
250     if ((D = opendir (Dir.GetStr ())) == NULL) {
251 #endif
252 	// Invalid directory
253 	return errno;
254     }
255 
256     // Ok, work with Dir is done, add the path separator again
257     AddPathSep (Dir);
258 
259     // Read one name after the other
260     int ErrorCode = 0;
261     dirent* Entry;
262     while ((Entry = readdir (D)) != NULL) {
263 	if (Match (Entry->d_name, FileSpec) != 0) {
264 	    // File name matches the given filespec
265 	    FileInfo* FI = new FileInfo (Dir + Entry->d_name);
266 	    if (FI->Error != 0) {
267 		// An error occured
268 		ErrorCode = FI->Error;
269 		delete FI;
270 	    } else if (((FI->Mode ^ ModeXor) & ModeAnd) == 0) {
271 		// We are not interested in this file
272 		delete FI;
273 	    } else {
274 		// Ok, we want that file. If the file system does not preserve
275 		// the case of names, convert the file name to lower case
276 		if (!PreservesCase) {
277 		    FI->Name.ToLower ();
278 		}
279 		Insert (FI);
280 	    }
281 	}
282     }
283 
284     // Close the directory stream
285     closedir (D);
286 
287     // return the error code
288     return ErrorCode;
289 }
290 
291 
292 
293 int FileInfoColl::ReadFiles (String Dir)
294 // Create a collection of files in the given directory inserting all of the
295 // files in this directory regardless of mode and filetype.
296 {
297     // Delete the old contents of the collection
298     DeleteAll ();
299 
300     // If the directory is empty, use the current dir
301     if (Dir.IsEmpty ()) {
302 	Dir = GetCurrentDir ();
303     }
304 
305     // Make the given directory clean and absolute
306     Dir = CleanPath (Dir);
307 
308     // Delete a trailing path separator
309     DelPathSep (Dir);
310 
311     // Remember if the file system, the directory resides on, preserves the
312     // case of the names
313     int PreservesCase = FileSysPreservesCase (Dir);
314 
315     // Open the directory stream
316     DIR* D;
317 #if (defined (DOS) && (__BCPLUSPLUS__==0x310)) || (defined (DOS32) && defined (__GO32__))
318     // BC++ 3.1 for DOS and djgpp both have a buggy declaration for opendir():
319     // the parameter is not declared as const. I'll assume that the function
320     // will not change the name and cast away the constness (*sigh*)
321     if ((D = opendir ((char*) Dir.GetStr ())) == NULL) {
322 #else
323     if ((D = opendir (Dir.GetStr ())) == NULL) {
324 #endif
325 	// Invalid directory
326 	return errno;
327     }
328 
329 #if defined (DOSLIKE_OS)
330     // Check and remember if we are in the root and first level dirs. This
331     // will be needed for tweaking DOS & OS/2 later.
332     int RootLevel = Dir.Len () == 3;	// Since Dir is clean, this is ok
333     String Root, N;
334     FSplit (Dir, Root, N);
335     int SecondLevel = Root.Len () == 3 && N.Len () > 0;
336 #endif
337 
338     // Ok, work with Dir is done, add the path separator again
339     AddPathSep (Dir);
340 
341     // Read one name after the other
342     int ErrorCode = 0;
343     dirent* Entry;
344     while ((Entry = readdir (D)) != NULL) {
345 
346 #if defined (DOSLIKE_OS)
347 	// On OS/2 systems, the root directory of HPFS drives contains a ".."
348 	// directory that points back to the root directory. Calling stat
349 	// with such a name fails. So check for this before creating a new
350 	// FileInfo entry.
351 	// Another problem under DOS & OS/2 are the entries ".." in second
352 	// level directories. Calling stat with an argument of "C:\X\.."
353 	// will fail, but "C:\" (which is the correct name) is ok, since it
354 	// is handled in a special way by the stat function (so one can say
355 	// that this is a stat bug, stat should do also a special handling
356 	// on names like C:\X\..). To enable the special handling by stat,
357 	// use the name of the root directory in this case, but do not
358 	// forget to set the correct name.
359 	FileInfo* FI;
360 	if (strcmp (Entry->d_name, "..") == 0) {
361 	    if (RootLevel) {
362 		// This is really the root dir, ignore the ".." entry
363 		continue;
364 	    }
365 	    if (SecondLevel) {
366 		FI = new FileInfo (Root);
367 		FI->Name = "..";	// Use the correct name!
368 	    } else {
369 		// Not a special directory
370 		FI = new FileInfo (Dir + Entry->d_name);
371 	    }
372 	} else {
373 	    // Normal file, stat it
374 	    FI = new FileInfo (Dir + Entry->d_name);
375 	}
376 #else
377 	// This is the way to do it under a _real_ operating system
378 	FileInfo* FI = new FileInfo (Dir + Entry->d_name);
379 #endif
380 
381 	// If the file has no error, insert it
382 	if (FI->Error != 0) {
383 	    // An error occured
384 	    ErrorCode = FI->Error;
385 	    delete FI;
386 	} else {
387 	    // Ok, we want that file. If the file system does not preserve
388 	    // the case of names, convert the file name to lower case
389 	    if (!PreservesCase) {
390 		FI->Name.ToLower ();
391 	    }
392 	    Insert (FI);
393 	}
394     }
395 
396     // Close the directory stream
397     closedir (D);
398 
399     // return the error code
400     return ErrorCode;
401 }
402 
403 
404 
405 int FileInfoColl::Compare (const String* Key1, const String* Key2)
406 {
407     return ::Compare (*Key1, *Key2);
408 }
409 
410 
411 
412 const String* FileInfoColl::KeyOf (const FileInfo* Item)
413 {
414     return &Item->Name;
415 }
416 
417 
418 
419 u16 FileInfoColl::StreamableID () const
420 {
421     return ID_FileInfoColl;
422 }
423 
424 
425 
426