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