move fd duplication closer to the gz/bz2 open calls
[ntk/apt.git] / apt-pkg / contrib / fileutl.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
7da2b375 3// $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
578bfd0a
AL
4/* ######################################################################
5
6 File Utilities
7
8 CopyFile - Buffered copy of a single file
9 GetLock - dpkg compatible lock file manipulation (fcntl)
10
614adaa0
MV
11 Most of this source is placed in the Public Domain, do with it what
12 you will
7da2b375 13 It was originally written by Jason Gunthorpe <jgg@debian.org>.
a3a03f5d 14 FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
578bfd0a 15
614adaa0
MV
16 The exception is RunScripts() it is under the GPLv2
17
578bfd0a
AL
18 ##################################################################### */
19 /*}}}*/
20// Include Files /*{{{*/
ea542140
DK
21#include <config.h>
22
094a497d 23#include <apt-pkg/fileutl.h>
1cd1c398 24#include <apt-pkg/strutl.h>
094a497d 25#include <apt-pkg/error.h>
b2e465d6 26#include <apt-pkg/sptr.h>
468720c5 27#include <apt-pkg/aptconfiguration.h>
75ef8f14 28#include <apt-pkg/configuration.h>
453b82a3 29#include <apt-pkg/macros.h>
b2e465d6 30
453b82a3
DK
31#include <ctype.h>
32#include <stdarg.h>
33#include <stddef.h>
34#include <sys/select.h>
35#include <time.h>
36#include <string>
37#include <vector>
152ab79e 38#include <cstdlib>
4f333a8b 39#include <cstring>
3010fb0e 40#include <cstdio>
4d055c05 41#include <iostream>
578bfd0a 42#include <unistd.h>
2c206aa4 43#include <fcntl.h>
578bfd0a 44#include <sys/stat.h>
cc2313b7 45#include <sys/time.h>
1ae93c94 46#include <sys/wait.h>
46e39c8e 47#include <dirent.h>
54676e1a 48#include <signal.h>
65a1e968 49#include <errno.h>
488011fa
MV
50#include <glob.h>
51
75ef8f14 52#include <set>
46e39c8e 53#include <algorithm>
2cae0ccb 54
7efb8c8e
DK
55#ifdef HAVE_ZLIB
56 #include <zlib.h>
699b209e 57#endif
c4997486
DK
58#ifdef HAVE_BZ2
59 #include <bzlib.h>
60#endif
032bd56f 61
2a79d5b5 62#ifdef WORDS_BIGENDIAN
2cae0ccb
DK
63#include <inttypes.h>
64#endif
ea542140
DK
65
66#include <apti18n.h>
578bfd0a
AL
67 /*}}}*/
68
4d055c05
AL
69using namespace std;
70
032bd56f
DK
71class FileFdPrivate {
72 public:
7efb8c8e 73#ifdef HAVE_ZLIB
032bd56f 74 gzFile gz;
699b209e
DK
75#else
76 void* gz;
c4997486
DK
77#endif
78#ifdef HAVE_BZ2
79 BZFILE* bz2;
80#else
81 void* bz2;
699b209e 82#endif
561f860a 83 int compressed_fd;
699b209e
DK
84 pid_t compressor_pid;
85 bool pipe;
86 APT::Configuration::Compressor compressor;
52b47296 87 unsigned int openmode;
1abbc47c 88 unsigned long long seekpos;
c4997486
DK
89 FileFdPrivate() : gz(NULL), bz2(NULL),
90 compressed_fd(-1), compressor_pid(-1), pipe(false),
1abbc47c 91 openmode(0), seekpos(0) {};
500400fe
DK
92 bool CloseDown(std::string const &FileName)
93 {
94 bool Res = true;
95#ifdef HAVE_ZLIB
96 if (gz != NULL) {
97 int const e = gzclose(gz);
98 gz = NULL;
99 // gzdclose() on empty files always fails with "buffer error" here, ignore that
100 if (e != 0 && e != Z_BUF_ERROR)
101 Res &= _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
102 }
103#endif
104#ifdef HAVE_BZ2
105 if (bz2 != NULL) {
106 BZ2_bzclose(bz2);
107 bz2 = NULL;
108 }
109#endif
110 if (compressor_pid > 0)
111 ExecWait(compressor_pid, "FileFdCompressor", true);
112 compressor_pid = -1;
113
114 return Res;
115 }
116 ~FileFdPrivate() { CloseDown(""); }
032bd56f
DK
117};
118
614adaa0
MV
119// RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
120// ---------------------------------------------------------------------
121/* */
122bool RunScripts(const char *Cnf)
123{
124 Configuration::Item const *Opts = _config->Tree(Cnf);
125 if (Opts == 0 || Opts->Child == 0)
126 return true;
127 Opts = Opts->Child;
128
129 // Fork for running the system calls
130 pid_t Child = ExecFork();
131
132 // This is the child
133 if (Child == 0)
134 {
cfba4f69
MV
135 if (_config->FindDir("DPkg::Chroot-Directory","/") != "/")
136 {
137 std::cerr << "Chrooting into "
138 << _config->FindDir("DPkg::Chroot-Directory")
139 << std::endl;
140 if (chroot(_config->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
141 _exit(100);
142 }
143
614adaa0
MV
144 if (chdir("/tmp/") != 0)
145 _exit(100);
146
147 unsigned int Count = 1;
148 for (; Opts != 0; Opts = Opts->Next, Count++)
149 {
150 if (Opts->Value.empty() == true)
151 continue;
152
153 if (system(Opts->Value.c_str()) != 0)
154 _exit(100+Count);
155 }
156 _exit(0);
157 }
158
159 // Wait for the child
160 int Status = 0;
161 while (waitpid(Child,&Status,0) != Child)
162 {
163 if (errno == EINTR)
164 continue;
165 return _error->Errno("waitpid","Couldn't wait for subprocess");
166 }
167
168 // Restore sig int/quit
169 signal(SIGQUIT,SIG_DFL);
170 signal(SIGINT,SIG_DFL);
171
172 // Check for an error code.
173 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
174 {
175 unsigned int Count = WEXITSTATUS(Status);
176 if (Count > 100)
177 {
178 Count -= 100;
179 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
180 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
181 }
182
183 return _error->Error("Sub-process returned an error code");
184 }
185
186 return true;
187}
188 /*}}}*/
189
578bfd0a
AL
190// CopyFile - Buffered copy of a file /*{{{*/
191// ---------------------------------------------------------------------
192/* The caller is expected to set things so that failure causes erasure */
8b89e57f 193bool CopyFile(FileFd &From,FileFd &To)
578bfd0a 194{
2128d3fc
DK
195 if (From.IsOpen() == false || To.IsOpen() == false ||
196 From.Failed() == true || To.Failed() == true)
578bfd0a
AL
197 return false;
198
199 // Buffered copy between fds
b2e465d6 200 SPtrArray<unsigned char> Buf = new unsigned char[64000];
650faab0 201 unsigned long long Size = From.Size();
b0db36b1 202 while (Size != 0)
578bfd0a 203 {
650faab0 204 unsigned long long ToRead = Size;
b0db36b1
AL
205 if (Size > 64000)
206 ToRead = 64000;
207
4a6d5862 208 if (From.Read(Buf,ToRead) == false ||
b0db36b1 209 To.Write(Buf,ToRead) == false)
578bfd0a 210 return false;
b0db36b1
AL
211
212 Size -= ToRead;
578bfd0a
AL
213 }
214
578bfd0a
AL
215 return true;
216}
217 /*}}}*/
218// GetLock - Gets a lock file /*{{{*/
219// ---------------------------------------------------------------------
220/* This will create an empty file of the given name and lock it. Once this
221 is done all other calls to GetLock in any other process will fail with
222 -1. The return result is the fd of the file, the call should call
223 close at some time. */
224int GetLock(string File,bool Errors)
225{
f659b39a
OS
226 // GetLock() is used in aptitude on directories with public-write access
227 // Use O_NOFOLLOW here to prevent symlink traversal attacks
228 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
578bfd0a
AL
229 if (FD < 0)
230 {
1e3f4083 231 // Read only .. can't have locking problems there.
b2e465d6
AL
232 if (errno == EROFS)
233 {
234 _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
235 return dup(0); // Need something for the caller to close
236 }
237
578bfd0a 238 if (Errors == true)
b2e465d6
AL
239 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
240
241 // Feh.. We do this to distinguish the lock vs open case..
242 errno = EPERM;
578bfd0a
AL
243 return -1;
244 }
b2e465d6
AL
245 SetCloseExec(FD,true);
246
1e3f4083 247 // Acquire a write lock
578bfd0a 248 struct flock fl;
c71bc556
AL
249 fl.l_type = F_WRLCK;
250 fl.l_whence = SEEK_SET;
251 fl.l_start = 0;
252 fl.l_len = 0;
578bfd0a
AL
253 if (fcntl(FD,F_SETLK,&fl) == -1)
254 {
3d165906
MV
255 // always close to not leak resources
256 int Tmp = errno;
257 close(FD);
258 errno = Tmp;
259
d89df07a
AL
260 if (errno == ENOLCK)
261 {
b2e465d6
AL
262 _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
263 return dup(0); // Need something for the caller to close
3d165906
MV
264 }
265
578bfd0a 266 if (Errors == true)
b2e465d6
AL
267 _error->Errno("open",_("Could not get lock %s"),File.c_str());
268
578bfd0a
AL
269 return -1;
270 }
271
272 return FD;
273}
274 /*}}}*/
275// FileExists - Check if a file exists /*{{{*/
276// ---------------------------------------------------------------------
36f1098a 277/* Beware: Directories are also files! */
578bfd0a
AL
278bool FileExists(string File)
279{
280 struct stat Buf;
281 if (stat(File.c_str(),&Buf) != 0)
282 return false;
283 return true;
284}
285 /*}}}*/
36f1098a
DK
286// RealFileExists - Check if a file exists and if it is really a file /*{{{*/
287// ---------------------------------------------------------------------
288/* */
289bool RealFileExists(string File)
290{
291 struct stat Buf;
292 if (stat(File.c_str(),&Buf) != 0)
293 return false;
294 return ((Buf.st_mode & S_IFREG) != 0);
295}
296 /*}}}*/
1cd1c398
DK
297// DirectoryExists - Check if a directory exists and is really one /*{{{*/
298// ---------------------------------------------------------------------
299/* */
300bool DirectoryExists(string const &Path)
301{
302 struct stat Buf;
303 if (stat(Path.c_str(),&Buf) != 0)
304 return false;
305 return ((Buf.st_mode & S_IFDIR) != 0);
306}
307 /*}}}*/
308// CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
309// ---------------------------------------------------------------------
310/* This method will create all directories needed for path in good old
311 mkdir -p style but refuses to do this if Parent is not a prefix of
312 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
313 so it will create apt/archives if /var/cache exists - on the other
314 hand if the parent is /var/lib the creation will fail as this path
315 is not a parent of the path to be generated. */
316bool CreateDirectory(string const &Parent, string const &Path)
317{
318 if (Parent.empty() == true || Path.empty() == true)
319 return false;
320
321 if (DirectoryExists(Path) == true)
322 return true;
323
324 if (DirectoryExists(Parent) == false)
325 return false;
326
327 // we are not going to create directories "into the blue"
9ce3cfc9 328 if (Path.compare(0, Parent.length(), Parent) != 0)
1cd1c398
DK
329 return false;
330
331 vector<string> const dirs = VectorizeString(Path.substr(Parent.size()), '/');
332 string progress = Parent;
333 for (vector<string>::const_iterator d = dirs.begin(); d != dirs.end(); ++d)
334 {
335 if (d->empty() == true)
336 continue;
337
338 progress.append("/").append(*d);
339 if (DirectoryExists(progress) == true)
340 continue;
341
342 if (mkdir(progress.c_str(), 0755) != 0)
343 return false;
344 }
345 return true;
346}
347 /*}}}*/
7753e468 348// CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
b29c3712
DK
349// ---------------------------------------------------------------------
350/* a small wrapper around CreateDirectory to check if it exists and to
351 remove the trailing "/apt/" from the parent directory if needed */
7753e468 352bool CreateAPTDirectoryIfNeeded(string const &Parent, string const &Path)
b29c3712
DK
353{
354 if (DirectoryExists(Path) == true)
355 return true;
356
357 size_t const len = Parent.size();
358 if (len > 5 && Parent.find("/apt/", len - 6, 5) == len - 5)
359 {
360 if (CreateDirectory(Parent.substr(0,len-5), Path) == true)
361 return true;
362 }
363 else if (CreateDirectory(Parent, Path) == true)
364 return true;
365
366 return false;
367}
368 /*}}}*/
46e39c8e
MV
369// GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
370// ---------------------------------------------------------------------
371/* If an extension is given only files with this extension are included
372 in the returned vector, otherwise every "normal" file is included. */
b39c1859
MV
373std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
374 bool const &SortList, bool const &AllowNoExt)
375{
376 std::vector<string> ext;
377 ext.reserve(2);
378 if (Ext.empty() == false)
379 ext.push_back(Ext);
380 if (AllowNoExt == true && ext.empty() == false)
381 ext.push_back("");
382 return GetListOfFilesInDir(Dir, ext, SortList);
383}
384std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
385 bool const &SortList)
386{
387 // Attention debuggers: need to be set with the environment config file!
388 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
389 if (Debug == true)
390 {
391 std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
392 if (Ext.empty() == true)
393 std::clog << "\tNO extension" << std::endl;
394 else
395 for (std::vector<string>::const_iterator e = Ext.begin();
396 e != Ext.end(); ++e)
397 std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
398 }
399
46e39c8e 400 std::vector<string> List;
36f1098a 401
69c2ecbd 402 if (DirectoryExists(Dir) == false)
36f1098a
DK
403 {
404 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
405 return List;
406 }
407
1408e219 408 Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently");
46e39c8e
MV
409 DIR *D = opendir(Dir.c_str());
410 if (D == 0)
411 {
412 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
413 return List;
414 }
415
416 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
417 {
b39c1859 418 // skip "hidden" files
46e39c8e
MV
419 if (Ent->d_name[0] == '.')
420 continue;
421
491058e3
DK
422 // Make sure it is a file and not something else
423 string const File = flCombine(Dir,Ent->d_name);
424#ifdef _DIRENT_HAVE_D_TYPE
425 if (Ent->d_type != DT_REG)
426#endif
427 {
69c2ecbd 428 if (RealFileExists(File) == false)
491058e3 429 {
84e254d6
DK
430 // do not show ignoration warnings for directories
431 if (
432#ifdef _DIRENT_HAVE_D_TYPE
433 Ent->d_type == DT_DIR ||
434#endif
69c2ecbd 435 DirectoryExists(File) == true)
84e254d6 436 continue;
491058e3
DK
437 if (SilentIgnore.Match(Ent->d_name) == false)
438 _error->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent->d_name, Dir.c_str());
439 continue;
440 }
441 }
442
b39c1859
MV
443 // check for accepted extension:
444 // no extension given -> periods are bad as hell!
445 // extensions given -> "" extension allows no extension
446 if (Ext.empty() == false)
447 {
448 string d_ext = flExtension(Ent->d_name);
449 if (d_ext == Ent->d_name) // no extension
450 {
451 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
452 {
453 if (Debug == true)
454 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
5edc3966 455 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 456 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
457 continue;
458 }
459 }
460 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
461 {
462 if (Debug == true)
463 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
1408e219 464 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 465 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
466 continue;
467 }
468 }
46e39c8e 469
b39c1859 470 // Skip bad filenames ala run-parts
46e39c8e
MV
471 const char *C = Ent->d_name;
472 for (; *C != 0; ++C)
473 if (isalpha(*C) == 0 && isdigit(*C) == 0
9d39208a 474 && *C != '_' && *C != '-' && *C != ':') {
b39c1859
MV
475 // no required extension -> dot is a bad character
476 if (*C == '.' && Ext.empty() == false)
477 continue;
46e39c8e 478 break;
b39c1859 479 }
46e39c8e 480
b39c1859 481 // we don't reach the end of the name -> bad character included
46e39c8e 482 if (*C != 0)
b39c1859
MV
483 {
484 if (Debug == true)
485 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
486 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
487 continue;
488 }
489
fbb2c7e0
DK
490 // skip filenames which end with a period. These are never valid
491 if (*(C - 1) == '.')
492 {
493 if (Debug == true)
494 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
495 continue;
496 }
497
498 if (Debug == true)
499 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
500 List.push_back(File);
501 }
502 closedir(D);
503
504 if (SortList == true)
505 std::sort(List.begin(),List.end());
506 return List;
507}
508std::vector<string> GetListOfFilesInDir(string const &Dir, bool SortList)
509{
510 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
511 if (Debug == true)
512 std::clog << "Accept in " << Dir << " all regular files" << std::endl;
513
514 std::vector<string> List;
515
69c2ecbd 516 if (DirectoryExists(Dir) == false)
fbb2c7e0
DK
517 {
518 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
519 return List;
520 }
521
522 DIR *D = opendir(Dir.c_str());
523 if (D == 0)
524 {
525 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
526 return List;
527 }
528
529 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
530 {
531 // skip "hidden" files
532 if (Ent->d_name[0] == '.')
533 continue;
534
535 // Make sure it is a file and not something else
536 string const File = flCombine(Dir,Ent->d_name);
537#ifdef _DIRENT_HAVE_D_TYPE
538 if (Ent->d_type != DT_REG)
539#endif
540 {
69c2ecbd 541 if (RealFileExists(File) == false)
fbb2c7e0
DK
542 {
543 if (Debug == true)
544 std::clog << "Bad file: " << Ent->d_name << " → it is not a real file" << std::endl;
545 continue;
546 }
547 }
548
549 // Skip bad filenames ala run-parts
550 const char *C = Ent->d_name;
551 for (; *C != 0; ++C)
552 if (isalpha(*C) == 0 && isdigit(*C) == 0
553 && *C != '_' && *C != '-' && *C != '.')
554 break;
555
556 // we don't reach the end of the name -> bad character included
557 if (*C != 0)
558 {
559 if (Debug == true)
560 std::clog << "Bad file: " << Ent->d_name << " → bad character »" << *C << "« in filename" << std::endl;
561 continue;
562 }
563
b39c1859
MV
564 // skip filenames which end with a period. These are never valid
565 if (*(C - 1) == '.')
566 {
567 if (Debug == true)
568 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
46e39c8e 569 continue;
b39c1859 570 }
46e39c8e 571
b39c1859
MV
572 if (Debug == true)
573 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
46e39c8e
MV
574 List.push_back(File);
575 }
576 closedir(D);
577
578 if (SortList == true)
579 std::sort(List.begin(),List.end());
580 return List;
581}
582 /*}}}*/
578bfd0a
AL
583// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
584// ---------------------------------------------------------------------
585/* We return / on failure. */
586string SafeGetCWD()
587{
588 // Stash the current dir.
589 char S[300];
590 S[0] = 0;
7f25bdff 591 if (getcwd(S,sizeof(S)-2) == 0)
578bfd0a 592 return "/";
7f25bdff
AL
593 unsigned int Len = strlen(S);
594 S[Len] = '/';
595 S[Len+1] = 0;
578bfd0a
AL
596 return S;
597}
598 /*}}}*/
2ec858bc
MV
599// GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
600// ---------------------------------------------------------------------
601/* We return / on failure. */
602time_t GetModificationTime(string const &Path)
603{
604 struct stat St;
605 if (stat(Path.c_str(), &St) < 0)
606 return -1;
607 return St.st_mtime;
608}
609 /*}}}*/
8ce4327b
AL
610// flNotDir - Strip the directory from the filename /*{{{*/
611// ---------------------------------------------------------------------
612/* */
613string flNotDir(string File)
614{
615 string::size_type Res = File.rfind('/');
616 if (Res == string::npos)
617 return File;
618 Res++;
619 return string(File,Res,Res - File.length());
620}
621 /*}}}*/
d38b7b3d
AL
622// flNotFile - Strip the file from the directory name /*{{{*/
623// ---------------------------------------------------------------------
171c45bc 624/* Result ends in a / */
d38b7b3d
AL
625string flNotFile(string File)
626{
627 string::size_type Res = File.rfind('/');
628 if (Res == string::npos)
171c45bc 629 return "./";
d38b7b3d
AL
630 Res++;
631 return string(File,0,Res);
632}
633 /*}}}*/
b2e465d6
AL
634// flExtension - Return the extension for the file /*{{{*/
635// ---------------------------------------------------------------------
636/* */
637string flExtension(string File)
638{
639 string::size_type Res = File.rfind('.');
640 if (Res == string::npos)
641 return File;
642 Res++;
643 return string(File,Res,Res - File.length());
644}
645 /*}}}*/
421c8d10
AL
646// flNoLink - If file is a symlink then deref it /*{{{*/
647// ---------------------------------------------------------------------
648/* If the name is not a link then the returned path is the input. */
649string flNoLink(string File)
650{
651 struct stat St;
652 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
653 return File;
654 if (stat(File.c_str(),&St) != 0)
655 return File;
656
657 /* Loop resolving the link. There is no need to limit the number of
658 loops because the stat call above ensures that the symlink is not
659 circular */
660 char Buffer[1024];
661 string NFile = File;
662 while (1)
663 {
664 // Read the link
3286ad13 665 ssize_t Res;
421c8d10 666 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
3286ad13 667 (size_t)Res >= sizeof(Buffer))
421c8d10
AL
668 return File;
669
670 // Append or replace the previous path
671 Buffer[Res] = 0;
672 if (Buffer[0] == '/')
673 NFile = Buffer;
674 else
675 NFile = flNotFile(NFile) + Buffer;
676
677 // See if we are done
678 if (lstat(NFile.c_str(),&St) != 0)
679 return File;
680 if (S_ISLNK(St.st_mode) == 0)
681 return NFile;
682 }
683}
684 /*}}}*/
b2e465d6
AL
685// flCombine - Combine a file and a directory /*{{{*/
686// ---------------------------------------------------------------------
687/* If the file is an absolute path then it is just returned, otherwise
688 the directory is pre-pended to it. */
689string flCombine(string Dir,string File)
690{
691 if (File.empty() == true)
692 return string();
693
694 if (File[0] == '/' || Dir.empty() == true)
695 return File;
696 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
697 return File;
698 if (Dir[Dir.length()-1] == '/')
699 return Dir + File;
700 return Dir + '/' + File;
701}
702 /*}}}*/
3b5421b4
AL
703// SetCloseExec - Set the close on exec flag /*{{{*/
704// ---------------------------------------------------------------------
705/* */
706void SetCloseExec(int Fd,bool Close)
707{
708 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
709 {
710 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
711 exit(100);
712 }
713}
714 /*}}}*/
715// SetNonBlock - Set the nonblocking flag /*{{{*/
716// ---------------------------------------------------------------------
717/* */
718void SetNonBlock(int Fd,bool Block)
719{
0a8a80e5
AL
720 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
721 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
722 {
723 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
724 exit(100);
725 }
726}
727 /*}}}*/
728// WaitFd - Wait for a FD to become readable /*{{{*/
729// ---------------------------------------------------------------------
b2e465d6 730/* This waits for a FD to become readable using select. It is useful for
6d5dd02a
AL
731 applications making use of non-blocking sockets. The timeout is
732 in seconds. */
1084d58a 733bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
734{
735 fd_set Set;
cc2313b7 736 struct timeval tv;
3b5421b4
AL
737 FD_ZERO(&Set);
738 FD_SET(Fd,&Set);
6d5dd02a
AL
739 tv.tv_sec = timeout;
740 tv.tv_usec = 0;
1084d58a 741 if (write == true)
b0db36b1
AL
742 {
743 int Res;
744 do
745 {
746 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
747 }
748 while (Res < 0 && errno == EINTR);
749
750 if (Res <= 0)
751 return false;
1084d58a
AL
752 }
753 else
754 {
b0db36b1
AL
755 int Res;
756 do
757 {
758 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
759 }
760 while (Res < 0 && errno == EINTR);
761
762 if (Res <= 0)
763 return false;
cc2313b7 764 }
1084d58a 765
3b5421b4
AL
766 return true;
767}
768 /*}}}*/
96ae6de5 769// MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
54676e1a 770// ---------------------------------------------------------------------
96ae6de5
MV
771/* This is used to merge the APT::Keep-Fds with the provided KeepFDs
772 * set.
773 */
774void MergeKeepFdsFromConfiguration(std::set<int> &KeepFDs)
e45c4617 775{
e45c4617
MV
776 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
777 if (Opts != 0 && Opts->Child != 0)
778 {
779 Opts = Opts->Child;
780 for (; Opts != 0; Opts = Opts->Next)
781 {
782 if (Opts->Value.empty() == true)
783 continue;
784 int fd = atoi(Opts->Value.c_str());
785 KeepFDs.insert(fd);
786 }
787 }
96ae6de5
MV
788}
789 /*}}}*/
790// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
791// ---------------------------------------------------------------------
792/* This is used if you want to cleanse the environment for the forked
793 child, it fixes up the important signals and nukes all of the fds,
794 otherwise acts like normal fork. */
795pid_t ExecFork()
796{
797 set<int> KeepFDs;
798 // we need to merge the Keep-Fds as external tools like
799 // debconf-apt-progress use it
800 MergeKeepFdsFromConfiguration(KeepFDs);
e45c4617
MV
801 return ExecFork(KeepFDs);
802}
803
804pid_t ExecFork(std::set<int> KeepFDs)
54676e1a
AL
805{
806 // Fork off the process
807 pid_t Process = fork();
808 if (Process < 0)
809 {
810 cerr << "FATAL -> Failed to fork." << endl;
811 exit(100);
812 }
813
814 // Spawn the subprocess
815 if (Process == 0)
816 {
817 // Setup the signals
818 signal(SIGPIPE,SIG_DFL);
819 signal(SIGQUIT,SIG_DFL);
820 signal(SIGINT,SIG_DFL);
821 signal(SIGWINCH,SIG_DFL);
822 signal(SIGCONT,SIG_DFL);
823 signal(SIGTSTP,SIG_DFL);
75ef8f14 824
54676e1a 825 // Close all of our FDs - just in case
61f954bf 826 for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++)
75ef8f14
MV
827 {
828 if(KeepFDs.find(K) == KeepFDs.end())
007dc9e0 829 fcntl(K,F_SETFD,FD_CLOEXEC);
75ef8f14 830 }
54676e1a
AL
831 }
832
833 return Process;
834}
835 /*}}}*/
ddc1d8d0
AL
836// ExecWait - Fancy waitpid /*{{{*/
837// ---------------------------------------------------------------------
2c9a72d1 838/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
839 generated. Otherwise a failed subprocess will generate a proper descriptive
840 message */
3826564e 841bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
842{
843 if (Pid <= 1)
844 return true;
845
846 // Wait and collect the error code
847 int Status;
848 while (waitpid(Pid,&Status,0) != Pid)
849 {
850 if (errno == EINTR)
851 continue;
852
853 if (Reap == true)
854 return false;
855
db0db9fe 856 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
857 }
858
859
860 // Check for an error code.
861 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
862 {
863 if (Reap == true)
864 return false;
ab7f4d7c 865 if (WIFSIGNALED(Status) != 0)
40e7fe0e 866 {
ab7f4d7c
MV
867 if( WTERMSIG(Status) == SIGSEGV)
868 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
869 else
870 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 871 }
ddc1d8d0
AL
872
873 if (WIFEXITED(Status) != 0)
b2e465d6 874 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 875
b2e465d6 876 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
877 }
878
879 return true;
880}
881 /*}}}*/
578bfd0a 882
13d87e2e 883// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
884// ---------------------------------------------------------------------
885/* The most commonly used open mode combinations are given with Mode */
52b47296 886bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const Perms)
578bfd0a 887{
257e8d66
DK
888 if (Mode == ReadOnlyGzip)
889 return Open(FileName, ReadOnly, Gzip, Perms);
257e8d66 890
468720c5 891 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
ae635e3c 892 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
257e8d66 893
468720c5
DK
894 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
895 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
896 if (Compress == Auto)
897 {
468720c5
DK
898 for (; compressor != compressors.end(); ++compressor)
899 {
e788a834 900 std::string file = FileName + compressor->Extension;
468720c5
DK
901 if (FileExists(file) == false)
902 continue;
903 FileName = file;
468720c5
DK
904 break;
905 }
906 }
907 else if (Compress == Extension)
908 {
52b47296
DK
909 std::string::size_type const found = FileName.find_last_of('.');
910 std::string ext;
911 if (found != std::string::npos)
912 {
913 ext = FileName.substr(found);
914 if (ext == ".new" || ext == ".bak")
915 {
916 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
917 if (found2 != std::string::npos)
918 ext = FileName.substr(found2, found - found2);
919 else
920 ext.clear();
921 }
922 }
aee1aac6
DK
923 for (; compressor != compressors.end(); ++compressor)
924 if (ext == compressor->Extension)
925 break;
926 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
927 if (compressor == compressors.end())
928 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
929 if (compressor->Name == ".")
468720c5 930 break;
468720c5 931 }
aee1aac6 932 else
468720c5
DK
933 {
934 std::string name;
935 switch (Compress)
936 {
aee1aac6 937 case None: name = "."; break;
468720c5
DK
938 case Gzip: name = "gzip"; break;
939 case Bzip2: name = "bzip2"; break;
940 case Lzma: name = "lzma"; break;
941 case Xz: name = "xz"; break;
aee1aac6
DK
942 case Auto:
943 case Extension:
52b47296 944 // Unreachable
ae635e3c 945 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
468720c5
DK
946 }
947 for (; compressor != compressors.end(); ++compressor)
948 if (compressor->Name == name)
949 break;
aee1aac6 950 if (compressor == compressors.end())
ae635e3c 951 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
468720c5
DK
952 }
953
aee1aac6 954 if (compressor == compressors.end())
ae635e3c 955 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
aee1aac6
DK
956 return Open(FileName, Mode, *compressor, Perms);
957}
52b47296 958bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const Perms)
aee1aac6
DK
959{
960 Close();
aee1aac6
DK
961 Flags = AutoClose;
962
963 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
ae635e3c 964 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
aee1aac6 965 if ((Mode & ReadWrite) == 0)
ae635e3c 966 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
468720c5 967
257e8d66
DK
968 if ((Mode & Atomic) == Atomic)
969 {
970 Flags |= Replace;
257e8d66
DK
971 }
972 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
973 {
974 // for atomic, this will be done by rename in Close()
975 unlink(FileName.c_str());
976 }
977 if ((Mode & Empty) == Empty)
578bfd0a 978 {
257e8d66
DK
979 struct stat Buf;
980 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
981 unlink(FileName.c_str());
982 }
c4fc2fd7 983
561f860a
DK
984 int fileflags = 0;
985 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
986 if_FLAGGED_SET(ReadWrite, O_RDWR);
987 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
988 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
4a9db827 989
561f860a
DK
990 if_FLAGGED_SET(Create, O_CREAT);
991 if_FLAGGED_SET(Empty, O_TRUNC);
992 if_FLAGGED_SET(Exclusive, O_EXCL);
561f860a 993 #undef if_FLAGGED_SET
52b47296 994
7335eebe
AGM
995 if ((Mode & Atomic) == Atomic)
996 {
997 char *name = strdup((FileName + ".XXXXXX").c_str());
998
dc545c0b 999 if((iFd = mkstemp(name)) == -1)
7335eebe
AGM
1000 {
1001 free(name);
98b69f9d 1002 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
7335eebe
AGM
1003 }
1004
1005 TemporaryFileName = string(name);
7335eebe 1006 free(name);
dc545c0b
DK
1007
1008 if(Perms != 600 && fchmod(iFd, Perms) == -1)
1009 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
7335eebe 1010 }
468720c5 1011 else
561f860a 1012 iFd = open(FileName.c_str(), fileflags, Perms);
468720c5 1013
b711c01e 1014 this->FileName = FileName;
561f860a
DK
1015 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
1016 {
468720c5 1017 if (iFd != -1)
fc81e8f2 1018 {
561f860a
DK
1019 close (iFd);
1020 iFd = -1;
fc81e8f2 1021 }
ae635e3c 1022 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
257e8d66 1023 }
578bfd0a 1024
13d87e2e
AL
1025 SetCloseExec(iFd,true);
1026 return true;
578bfd0a 1027}
257e8d66
DK
1028 /*}}}*/
1029// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1030// ---------------------------------------------------------------------
1031/* */
52b47296 1032bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
aee1aac6
DK
1033{
1034 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1035 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1036 std::string name;
bce778a3
MV
1037
1038 // compat with the old API
1039 if (Mode == ReadOnlyGzip && Compress == None)
1040 Compress = Gzip;
1041
aee1aac6
DK
1042 switch (Compress)
1043 {
1044 case None: name = "."; break;
1045 case Gzip: name = "gzip"; break;
1046 case Bzip2: name = "bzip2"; break;
1047 case Lzma: name = "lzma"; break;
1048 case Xz: name = "xz"; break;
1049 case Auto:
1050 case Extension:
f97bb523
DK
1051 if (AutoClose == true && Fd != -1)
1052 close(Fd);
ae635e3c 1053 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
aee1aac6
DK
1054 }
1055 for (; compressor != compressors.end(); ++compressor)
1056 if (compressor->Name == name)
1057 break;
1058 if (compressor == compressors.end())
f97bb523
DK
1059 {
1060 if (AutoClose == true && Fd != -1)
1061 close(Fd);
ae635e3c 1062 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
f97bb523 1063 }
aee1aac6
DK
1064 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
1065}
52b47296 1066bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
144c0969
JAK
1067{
1068 Close();
1069 Flags = (AutoClose) ? FileFd::AutoClose : 0;
84baaae9 1070 iFd = Fd;
b711c01e 1071 this->FileName = "";
84baaae9 1072 if (OpenInternDescriptor(Mode, compressor) == false)
468720c5 1073 {
f97bb523 1074 if (iFd != -1 && (
84baaae9 1075 (Flags & Compressed) == Compressed ||
f97bb523
DK
1076 AutoClose == true))
1077 {
468720c5 1078 close (iFd);
f97bb523
DK
1079 iFd = -1;
1080 }
1081 return FileFdError(_("Could not open file descriptor %d"), Fd);
144c0969 1082 }
144c0969 1083 return true;
468720c5 1084}
52b47296 1085bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
468720c5 1086{
84baaae9
DK
1087 if (iFd == -1)
1088 return false;
ff477ee1
DK
1089 if (compressor.Name == "." || compressor.Binary.empty() == true)
1090 return true;
1091
ba667cf7
DK
1092 if (d == NULL)
1093 {
1094 d = new FileFdPrivate();
1095 d->openmode = Mode;
1096 d->compressor = compressor;
84baaae9
DK
1097 if (AutoClose == false && (
1098#ifdef HAVE_ZLIB
1099 compressor.Name == "gzip" ||
1100#endif
1101#ifdef HAVE_BZ2
1102 compressor.Name == "bzip2" ||
1103#endif
1104 false))
1105 {
1106 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1107 int const internFd = dup(iFd);
1108 if (internFd == -1)
1109 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
1110 iFd = internFd;
1111 }
ba667cf7 1112 }
ff477ee1 1113
7efb8c8e 1114#ifdef HAVE_ZLIB
ff477ee1 1115 if (compressor.Name == "gzip")
468720c5 1116 {
adbd7eb4
DK
1117 if (d->gz != NULL)
1118 {
1119 gzclose(d->gz);
1120 d->gz = NULL;
1121 }
468720c5 1122 if ((Mode & ReadWrite) == ReadWrite)
032bd56f 1123 d->gz = gzdopen(iFd, "r+");
468720c5 1124 else if ((Mode & WriteOnly) == WriteOnly)
032bd56f 1125 d->gz = gzdopen(iFd, "w");
468720c5 1126 else
c4997486 1127 d->gz = gzdopen(iFd, "r");
032bd56f 1128 if (d->gz == NULL)
468720c5 1129 return false;
032bd56f 1130 Flags |= Compressed;
561f860a 1131 return true;
468720c5 1132 }
699b209e 1133#endif
c4997486 1134#ifdef HAVE_BZ2
ff477ee1 1135 if (compressor.Name == "bzip2")
c4997486 1136 {
adbd7eb4
DK
1137 if (d->bz2 != NULL)
1138 {
1139 BZ2_bzclose(d->bz2);
1140 d->bz2 = NULL;
1141 }
c4997486
DK
1142 if ((Mode & ReadWrite) == ReadWrite)
1143 d->bz2 = BZ2_bzdopen(iFd, "r+");
1144 else if ((Mode & WriteOnly) == WriteOnly)
1145 d->bz2 = BZ2_bzdopen(iFd, "w");
1146 else
1147 d->bz2 = BZ2_bzdopen(iFd, "r");
1148 if (d->bz2 == NULL)
1149 return false;
1150 Flags |= Compressed;
1151 return true;
1152 }
1153#endif
1154
adbd7eb4
DK
1155 // collect zombies here in case we reopen
1156 if (d->compressor_pid > 0)
1157 ExecWait(d->compressor_pid, "FileFdCompressor", true);
561f860a
DK
1158
1159 if ((Mode & ReadWrite) == ReadWrite)
ae635e3c 1160 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
561f860a
DK
1161
1162 bool const Comp = (Mode & WriteOnly) == WriteOnly;
561f860a
DK
1163 if (Comp == false)
1164 {
adbd7eb4 1165 // Handle 'decompression' of empty files
561f860a
DK
1166 struct stat Buf;
1167 fstat(iFd, &Buf);
1168 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1169 return true;
1170
1171 // We don't need the file open - instead let the compressor open it
1172 // as he properly knows better how to efficiently read from 'his' file
1173 if (FileName.empty() == false)
afb093cd 1174 {
561f860a 1175 close(iFd);
afb093cd
DK
1176 iFd = -1;
1177 }
561f860a
DK
1178 }
1179
1180 // Create a data pipe
1181 int Pipe[2] = {-1,-1};
1182 if (pipe(Pipe) != 0)
ae635e3c 1183 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
561f860a
DK
1184 for (int J = 0; J != 2; J++)
1185 SetCloseExec(Pipe[J],true);
1186
1187 d->compressed_fd = iFd;
1188 d->pipe = true;
1189
1190 if (Comp == true)
1191 iFd = Pipe[1];
1192 else
1193 iFd = Pipe[0];
1194
1195 // The child..
1196 d->compressor_pid = ExecFork();
1197 if (d->compressor_pid == 0)
1198 {
1199 if (Comp == true)
1200 {
1201 dup2(d->compressed_fd,STDOUT_FILENO);
1202 dup2(Pipe[0],STDIN_FILENO);
1203 }
1204 else
1205 {
1206 if (FileName.empty() == true)
1207 dup2(d->compressed_fd,STDIN_FILENO);
1208 dup2(Pipe[1],STDOUT_FILENO);
1209 }
0b4895d3
DK
1210 int const nullfd = open("/dev/null", O_WRONLY);
1211 if (nullfd != -1)
1212 {
1213 dup2(nullfd,STDERR_FILENO);
1214 close(nullfd);
1215 }
561f860a
DK
1216
1217 SetCloseExec(STDOUT_FILENO,false);
1218 SetCloseExec(STDIN_FILENO,false);
1219
1220 std::vector<char const*> Args;
1221 Args.push_back(compressor.Binary.c_str());
1222 std::vector<std::string> const * const addArgs =
1223 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1224 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1225 a != addArgs->end(); ++a)
1226 Args.push_back(a->c_str());
1227 if (Comp == false && FileName.empty() == false)
1228 {
1229 Args.push_back("--stdout");
1230 if (TemporaryFileName.empty() == false)
1231 Args.push_back(TemporaryFileName.c_str());
1232 else
1233 Args.push_back(FileName.c_str());
1234 }
1235 Args.push_back(NULL);
1236
1237 execvp(Args[0],(char **)&Args[0]);
1238 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1239 _exit(100);
1240 }
1241 if (Comp == true)
1242 close(Pipe[0]);
468720c5 1243 else
561f860a 1244 close(Pipe[1]);
561f860a 1245
468720c5 1246 return true;
144c0969 1247}
578bfd0a 1248 /*}}}*/
8e06abb2 1249// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
1250// ---------------------------------------------------------------------
1251/* If the proper modes are selected then we close the Fd and possibly
1252 unlink the file on error. */
8e06abb2 1253FileFd::~FileFd()
578bfd0a
AL
1254{
1255 Close();
500400fe 1256 if (d != NULL)
500400fe 1257 d->CloseDown(FileName);
96ab3c6f
MV
1258 delete d;
1259 d = NULL;
578bfd0a
AL
1260}
1261 /*}}}*/
8e06abb2 1262// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 1263// ---------------------------------------------------------------------
1e3f4083 1264/* We are careful to handle interruption by a signal while reading
b0db36b1 1265 gracefully. */
650faab0 1266bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
578bfd0a 1267{
3286ad13 1268 ssize_t Res;
b0db36b1 1269 errno = 0;
f604cf55
AL
1270 if (Actual != 0)
1271 *Actual = 0;
699b209e 1272 *((char *)To) = '\0';
b0db36b1 1273 do
578bfd0a 1274 {
7efb8c8e 1275#ifdef HAVE_ZLIB
ff477ee1 1276 if (d != NULL && d->gz != NULL)
c4997486
DK
1277 Res = gzread(d->gz,To,Size);
1278 else
1279#endif
1280#ifdef HAVE_BZ2
ff477ee1 1281 if (d != NULL && d->bz2 != NULL)
c4997486 1282 Res = BZ2_bzread(d->bz2,To,Size);
a3a03f5d 1283 else
699b209e 1284#endif
a3a03f5d 1285 Res = read(iFd,To,Size);
b711c01e 1286
b0db36b1
AL
1287 if (Res < 0)
1288 {
b711c01e
DK
1289 if (errno == EINTR)
1290 continue;
7efb8c8e 1291#ifdef HAVE_ZLIB
ff477ee1 1292 if (d != NULL && d->gz != NULL)
b711c01e
DK
1293 {
1294 int err;
1295 char const * const errmsg = gzerror(d->gz, &err);
1296 if (err != Z_ERRNO)
ae635e3c 1297 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
b711c01e 1298 }
c4997486
DK
1299#endif
1300#ifdef HAVE_BZ2
ff477ee1 1301 if (d != NULL && d->bz2 != NULL)
c4997486
DK
1302 {
1303 int err;
1304 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1305 if (err != BZ_IO_ERROR)
ae635e3c 1306 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
c4997486 1307 }
b711c01e 1308#endif
ae635e3c 1309 return FileFdErrno("read",_("Read error"));
b0db36b1 1310 }
578bfd0a 1311
b0db36b1
AL
1312 To = (char *)To + Res;
1313 Size -= Res;
ff477ee1
DK
1314 if (d != NULL)
1315 d->seekpos += Res;
f604cf55
AL
1316 if (Actual != 0)
1317 *Actual += Res;
b0db36b1
AL
1318 }
1319 while (Res > 0 && Size > 0);
1320
1321 if (Size == 0)
1322 return true;
1323
ddc1d8d0 1324 // Eof handling
f604cf55 1325 if (Actual != 0)
ddc1d8d0
AL
1326 {
1327 Flags |= HitEof;
1328 return true;
1329 }
ae635e3c
DK
1330
1331 return FileFdError(_("read, still have %llu to read but none left"), Size);
578bfd0a
AL
1332}
1333 /*}}}*/
032bd56f
DK
1334// FileFd::ReadLine - Read a complete line from the file /*{{{*/
1335// ---------------------------------------------------------------------
1336/* Beware: This method can be quiet slow for big buffers on UNcompressed
1337 files because of the naive implementation! */
1338char* FileFd::ReadLine(char *To, unsigned long long const Size)
1339{
699b209e 1340 *To = '\0';
7efb8c8e 1341#ifdef HAVE_ZLIB
ff477ee1 1342 if (d != NULL && d->gz != NULL)
032bd56f 1343 return gzgets(d->gz, To, Size);
699b209e 1344#endif
032bd56f
DK
1345
1346 unsigned long long read = 0;
40468850
DK
1347 while ((Size - 1) != read)
1348 {
1349 unsigned long long done = 0;
1350 if (Read(To + read, 1, &done) == false)
1351 return NULL;
1352 if (done == 0)
1353 break;
1354 if (To[read++] == '\n')
1355 break;
1356 }
1357 if (read == 0)
032bd56f 1358 return NULL;
40468850 1359 To[read] = '\0';
032bd56f
DK
1360 return To;
1361}
1362 /*}}}*/
8e06abb2 1363// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
1364// ---------------------------------------------------------------------
1365/* */
650faab0 1366bool FileFd::Write(const void *From,unsigned long long Size)
578bfd0a 1367{
3286ad13 1368 ssize_t Res;
b0db36b1
AL
1369 errno = 0;
1370 do
578bfd0a 1371 {
7efb8c8e 1372#ifdef HAVE_ZLIB
ff477ee1 1373 if (d != NULL && d->gz != NULL)
032bd56f 1374 Res = gzwrite(d->gz,From,Size);
a3a03f5d 1375 else
c4997486
DK
1376#endif
1377#ifdef HAVE_BZ2
ff477ee1 1378 if (d != NULL && d->bz2 != NULL)
c4997486
DK
1379 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
1380 else
699b209e 1381#endif
a3a03f5d 1382 Res = write(iFd,From,Size);
b0db36b1
AL
1383 if (Res < 0 && errno == EINTR)
1384 continue;
1385 if (Res < 0)
1386 {
c4997486 1387#ifdef HAVE_ZLIB
ff477ee1 1388 if (d != NULL && d->gz != NULL)
c4997486
DK
1389 {
1390 int err;
1391 char const * const errmsg = gzerror(d->gz, &err);
1392 if (err != Z_ERRNO)
ae635e3c 1393 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486
DK
1394 }
1395#endif
1396#ifdef HAVE_BZ2
ff477ee1 1397 if (d != NULL && d->bz2 != NULL)
c4997486
DK
1398 {
1399 int err;
1400 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1401 if (err != BZ_IO_ERROR)
ae635e3c 1402 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486
DK
1403 }
1404#endif
ae635e3c 1405 return FileFdErrno("write",_("Write error"));
b0db36b1
AL
1406 }
1407
cf4ff3b7 1408 From = (char const *)From + Res;
b0db36b1 1409 Size -= Res;
ff477ee1
DK
1410 if (d != NULL)
1411 d->seekpos += Res;
578bfd0a 1412 }
b0db36b1 1413 while (Res > 0 && Size > 0);
578bfd0a 1414
b0db36b1
AL
1415 if (Size == 0)
1416 return true;
ae635e3c
DK
1417
1418 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
d68d65ad
DK
1419}
1420bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1421{
3286ad13 1422 ssize_t Res;
d68d65ad
DK
1423 errno = 0;
1424 do
1425 {
1426 Res = write(Fd,From,Size);
1427 if (Res < 0 && errno == EINTR)
1428 continue;
1429 if (Res < 0)
1430 return _error->Errno("write",_("Write error"));
1431
cf4ff3b7 1432 From = (char const *)From + Res;
d68d65ad
DK
1433 Size -= Res;
1434 }
1435 while (Res > 0 && Size > 0);
1436
1437 if (Size == 0)
1438 return true;
1439
1440 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
578bfd0a
AL
1441}
1442 /*}}}*/
8e06abb2 1443// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
1444// ---------------------------------------------------------------------
1445/* */
650faab0 1446bool FileFd::Seek(unsigned long long To)
578bfd0a 1447{
ff477ee1 1448 if (d != NULL && (d->pipe == true
c4997486 1449#ifdef HAVE_BZ2
ff477ee1 1450 || d->bz2 != NULL
c4997486 1451#endif
ff477ee1 1452 ))
699b209e 1453 {
1abbc47c
DK
1454 // Our poor man seeking in pipes is costly, so try to avoid it
1455 unsigned long long seekpos = Tell();
1456 if (seekpos == To)
1457 return true;
1458 else if (seekpos < To)
1459 return Skip(To - seekpos);
1460
561f860a 1461 if ((d->openmode & ReadOnly) != ReadOnly)
ae635e3c 1462 return FileFdError("Reopen is only implemented for read-only files!");
c4997486 1463#ifdef HAVE_BZ2
e3b402f4
MV
1464 if (d->bz2 != NULL)
1465 {
1466 BZ2_bzclose(d->bz2);
1467 d->bz2 = NULL;
1468 }
c4997486 1469#endif
afb093cd
DK
1470 if (iFd != -1)
1471 close(iFd);
1472 iFd = -1;
561f860a
DK
1473 if (TemporaryFileName.empty() == false)
1474 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1475 else if (FileName.empty() == false)
1476 iFd = open(FileName.c_str(), O_RDONLY);
1477 else
6fd947bd
DK
1478 {
1479 if (d->compressed_fd > 0)
1480 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1481 iFd = d->compressed_fd;
500400fe 1482 if (iFd < 0)
ae635e3c 1483 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
6fd947bd 1484 }
561f860a
DK
1485
1486 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
ae635e3c 1487 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
561f860a
DK
1488
1489 if (To != 0)
1490 return Skip(To);
1abbc47c
DK
1491
1492 d->seekpos = To;
561f860a 1493 return true;
699b209e 1494 }
3286ad13 1495 off_t res;
7efb8c8e 1496#ifdef HAVE_ZLIB
ff477ee1 1497 if (d != NULL && d->gz)
032bd56f 1498 res = gzseek(d->gz,To,SEEK_SET);
a3a03f5d 1499 else
699b209e 1500#endif
a3a03f5d 1501 res = lseek(iFd,To,SEEK_SET);
3286ad13 1502 if (res != (off_t)To)
ae635e3c 1503 return FileFdError("Unable to seek to %llu", To);
1abbc47c 1504
ff477ee1
DK
1505 if (d != NULL)
1506 d->seekpos = To;
727f18af
AL
1507 return true;
1508}
1509 /*}}}*/
1510// FileFd::Skip - Seek in the file /*{{{*/
1511// ---------------------------------------------------------------------
1512/* */
650faab0 1513bool FileFd::Skip(unsigned long long Over)
727f18af 1514{
ff477ee1 1515 if (d != NULL && (d->pipe == true
c4997486 1516#ifdef HAVE_BZ2
ff477ee1 1517 || d->bz2 != NULL
c4997486 1518#endif
ff477ee1 1519 ))
40468850
DK
1520 {
1521 d->seekpos += Over;
1522 char buffer[1024];
1523 while (Over != 0)
1524 {
1525 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1526 if (Read(buffer, toread) == false)
ae635e3c 1527 return FileFdError("Unable to seek ahead %llu",Over);
40468850
DK
1528 Over -= toread;
1529 }
1530 return true;
1531 }
1532
3286ad13 1533 off_t res;
7efb8c8e 1534#ifdef HAVE_ZLIB
ff477ee1 1535 if (d != NULL && d->gz != NULL)
032bd56f 1536 res = gzseek(d->gz,Over,SEEK_CUR);
a3a03f5d 1537 else
699b209e 1538#endif
a3a03f5d 1539 res = lseek(iFd,Over,SEEK_CUR);
1540 if (res < 0)
ae635e3c 1541 return FileFdError("Unable to seek ahead %llu",Over);
ff477ee1
DK
1542 if (d != NULL)
1543 d->seekpos = res;
1abbc47c 1544
6d5dd02a
AL
1545 return true;
1546}
1547 /*}}}*/
1548// FileFd::Truncate - Truncate the file /*{{{*/
1549// ---------------------------------------------------------------------
1550/* */
650faab0 1551bool FileFd::Truncate(unsigned long long To)
6d5dd02a 1552{
ad5051ef
DK
1553 // truncating /dev/null is always successful - as we get an error otherwise
1554 if (To == 0 && FileName == "/dev/null")
1555 return true;
c4997486 1556#if defined HAVE_ZLIB || defined HAVE_BZ2
ff477ee1 1557 if (d != NULL && (d->gz != NULL || d->bz2 != NULL))
ae635e3c 1558 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
c4997486 1559#endif
6d5dd02a 1560 if (ftruncate(iFd,To) != 0)
ae635e3c
DK
1561 return FileFdError("Unable to truncate to %llu",To);
1562
578bfd0a
AL
1563 return true;
1564}
1565 /*}}}*/
7f25bdff
AL
1566// FileFd::Tell - Current seek position /*{{{*/
1567// ---------------------------------------------------------------------
1568/* */
650faab0 1569unsigned long long FileFd::Tell()
7f25bdff 1570{
1abbc47c
DK
1571 // In theory, we could just return seekpos here always instead of
1572 // seeking around, but not all users of FileFd use always Seek() and co
1573 // so d->seekpos isn't always true and we can just use it as a hint if
1574 // we have nothing else, but not always as an authority…
ff477ee1 1575 if (d != NULL && (d->pipe == true
c4997486 1576#ifdef HAVE_BZ2
ff477ee1 1577 || d->bz2 != NULL
c4997486 1578#endif
ff477ee1 1579 ))
1abbc47c
DK
1580 return d->seekpos;
1581
a3a03f5d 1582 off_t Res;
7efb8c8e 1583#ifdef HAVE_ZLIB
ff477ee1 1584 if (d != NULL && d->gz != NULL)
032bd56f 1585 Res = gztell(d->gz);
a3a03f5d 1586 else
699b209e 1587#endif
a3a03f5d 1588 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff 1589 if (Res == (off_t)-1)
ae635e3c 1590 FileFdErrno("lseek","Failed to determine the current file position");
ff477ee1
DK
1591 if (d != NULL)
1592 d->seekpos = Res;
7f25bdff
AL
1593 return Res;
1594}
1595 /*}}}*/
8190b07a 1596static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
578bfd0a 1597{
6008b79a
DK
1598 bool ispipe = (d != NULL && d->pipe == true);
1599 if (ispipe == false)
1600 {
1601 if (fstat(iFd,&Buf) != 0)
8190b07a
DK
1602 // higher-level code will generate more meaningful messages,
1603 // even translated this would be meaningless for users
1604 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
6008b79a
DK
1605 ispipe = S_ISFIFO(Buf.st_mode);
1606 }
699b209e
DK
1607
1608 // for compressor pipes st_size is undefined and at 'best' zero
6008b79a 1609 if (ispipe == true)
699b209e
DK
1610 {
1611 // we set it here, too, as we get the info here for free
1612 // in theory the Open-methods should take care of it already
ff477ee1
DK
1613 if (d != NULL)
1614 d->pipe = true;
699b209e 1615 if (stat(FileName.c_str(), &Buf) != 0)
8190b07a
DK
1616 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1617 }
1618 return true;
1619}
1620 /*}}}*/
1621// FileFd::FileSize - Return the size of the file /*{{{*/
1622unsigned long long FileFd::FileSize()
1623{
1624 struct stat Buf;
1625 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1626 {
1627 Flags |= Fail;
1628 return 0;
699b209e 1629 }
4260fd39
DK
1630 return Buf.st_size;
1631}
1632 /*}}}*/
8190b07a
DK
1633// FileFd::ModificationTime - Return the time of last touch /*{{{*/
1634time_t FileFd::ModificationTime()
1635{
1636 struct stat Buf;
1637 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1638 {
1639 Flags |= Fail;
1640 return 0;
1641 }
1642 return Buf.st_mtime;
1643}
1644 /*}}}*/
4260fd39
DK
1645// FileFd::Size - Return the size of the content in the file /*{{{*/
1646// ---------------------------------------------------------------------
1647/* */
650faab0 1648unsigned long long FileFd::Size()
4260fd39 1649{
650faab0 1650 unsigned long long size = FileSize();
44dc669e 1651
699b209e
DK
1652 // for compressor pipes st_size is undefined and at 'best' zero,
1653 // so we 'read' the content and 'seek' back - see there
ff477ee1 1654 if (d != NULL && (d->pipe == true
c4997486 1655#ifdef HAVE_BZ2
ff477ee1 1656 || (d->bz2 && size > 0)
c4997486 1657#endif
ff477ee1 1658 ))
699b209e 1659 {
1abbc47c 1660 unsigned long long const oldSeek = Tell();
699b209e
DK
1661 char ignore[1000];
1662 unsigned long long read = 0;
1663 do {
638593bd
DK
1664 if (Read(ignore, sizeof(ignore), &read) == false)
1665 {
1666 Seek(oldSeek);
1667 return 0;
1668 }
699b209e 1669 } while(read != 0);
1abbc47c
DK
1670 size = Tell();
1671 Seek(oldSeek);
699b209e 1672 }
7efb8c8e 1673#ifdef HAVE_ZLIB
44dc669e 1674 // only check gzsize if we are actually a gzip file, just checking for
032bd56f 1675 // "gz" is not sufficient as uncompressed files could be opened with
44dc669e 1676 // gzopen in "direct" mode as well
ff477ee1 1677 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
9c182afa 1678 {
65c72a4b 1679 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
9c182afa
MP
1680 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1681 * this ourselves; the original (uncompressed) file size is the last 32
1682 * bits of the file */
650faab0 1683 // FIXME: Size for gz-files is limited by 32bit… no largefile support
9c182afa 1684 if (lseek(iFd, -4, SEEK_END) < 0)
638593bd
DK
1685 {
1686 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1687 return 0;
1688 }
1689 size = 0;
9c182afa 1690 if (read(iFd, &size, 4) != 4)
638593bd
DK
1691 {
1692 FileFdErrno("read","Unable to read original size of gzipped file");
1693 return 0;
1694 }
f330c0f3
DK
1695
1696#ifdef WORDS_BIGENDIAN
2cae0ccb
DK
1697 uint32_t tmp_size = size;
1698 uint8_t const * const p = (uint8_t const * const) &tmp_size;
1699 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
1700 size = tmp_size;
f330c0f3 1701#endif
9c182afa 1702
65c72a4b 1703 if (lseek(iFd, oldPos, SEEK_SET) < 0)
638593bd
DK
1704 {
1705 FileFdErrno("lseek","Unable to seek in gzipped file");
1706 return 0;
1707 }
65c72a4b 1708
9c182afa
MP
1709 return size;
1710 }
699b209e 1711#endif
9c182afa 1712
44dc669e 1713 return size;
578bfd0a
AL
1714}
1715 /*}}}*/
8e06abb2 1716// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
1717// ---------------------------------------------------------------------
1718/* */
8e06abb2 1719bool FileFd::Close()
578bfd0a 1720{
032bd56f
DK
1721 if (iFd == -1)
1722 return true;
1723
578bfd0a
AL
1724 bool Res = true;
1725 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 1726 {
500400fe
DK
1727 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1728 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1729
1730 if (d != NULL)
1731 {
1732 Res &= d->CloseDown(FileName);
1733 delete d;
1734 d = NULL;
1735 }
d13c2d3f 1736 }
3010fb0e 1737
d3aac32e 1738 if ((Flags & Replace) == Replace) {
3010fb0e 1739 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
1740 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1741
fd3b761e 1742 FileName = TemporaryFileName; // for the unlink() below.
257e8d66 1743 TemporaryFileName.clear();
3010fb0e 1744 }
62d073d9
DK
1745
1746 iFd = -1;
1747
578bfd0a
AL
1748 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1749 FileName.empty() == false)
1750 if (unlink(FileName.c_str()) != 0)
62d073d9 1751 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
3010fb0e 1752
fbb89d94
DK
1753 if (Res == false)
1754 Flags |= Fail;
578bfd0a
AL
1755 return Res;
1756}
1757 /*}}}*/
b2e465d6
AL
1758// FileFd::Sync - Sync the file /*{{{*/
1759// ---------------------------------------------------------------------
1760/* */
1761bool FileFd::Sync()
1762{
b2e465d6 1763 if (fsync(iFd) != 0)
ae635e3c
DK
1764 return FileFdErrno("sync",_("Problem syncing the file"));
1765 return true;
1766}
1767 /*}}}*/
1768// FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1769bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
1770{
1771 Flags |= Fail;
1772 va_list args;
1773 size_t msgSize = 400;
1774 int const errsv = errno;
1775 while (true)
fbb89d94 1776 {
ae635e3c
DK
1777 va_start(args,Description);
1778 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
1779 break;
1780 va_end(args);
fbb89d94 1781 }
ae635e3c
DK
1782 return false;
1783}
1784 /*}}}*/
1785// FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1786bool FileFd::FileFdError(const char *Description,...) {
1787 Flags |= Fail;
1788 va_list args;
1789 size_t msgSize = 400;
1790 while (true)
1791 {
1792 va_start(args,Description);
1793 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
1794 break;
1795 va_end(args);
1796 }
1797 return false;
b2e465d6
AL
1798}
1799 /*}}}*/
699b209e 1800
e788a834 1801gzFile FileFd::gzFd() { return d->gz; }
488011fa
MV
1802
1803
1804// Glob - wrapper around "glob()" /*{{{*/
1805// ---------------------------------------------------------------------
1806/* */
1807std::vector<std::string> Glob(std::string const &pattern, int flags)
1808{
1809 std::vector<std::string> result;
1810 glob_t globbuf;
ec4835a1
ÁGM
1811 int glob_res;
1812 unsigned int i;
488011fa
MV
1813
1814 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
1815
1816 if (glob_res != 0)
1817 {
1818 if(glob_res != GLOB_NOMATCH) {
1819 _error->Errno("glob", "Problem with glob");
1820 return result;
1821 }
1822 }
1823
1824 // append results
1825 for(i=0;i<globbuf.gl_pathc;i++)
1826 result.push_back(string(globbuf.gl_pathv[i]));
1827
1828 globfree(&globbuf);
1829 return result;
1830}
1831 /*}}}*/
68e01721
MV
1832
1833std::string GetTempDir()
1834{
1835 const char *tmpdir = getenv("TMPDIR");
1836
1837#ifdef P_tmpdir
1838 if (!tmpdir)
1839 tmpdir = P_tmpdir;
1840#endif
1841
1842 // check that tmpdir is set and exists
1843 struct stat st;
a077861a 1844 if (!tmpdir || strlen(tmpdir) == 0 || stat(tmpdir, &st) != 0)
68e01721
MV
1845 tmpdir = "/tmp";
1846
1847 return string(tmpdir);
1848}