daemon: Fix possible use-after-free.
[jackhill/guix/guix.git] / nix / libutil / util.cc
CommitLineData
36457566
LC
1#include "config.h"
2
3#include <iostream>
4#include <cerrno>
5#include <cstdio>
6#include <cstdlib>
7#include <sstream>
8#include <cstring>
9
10#include <sys/wait.h>
11#include <unistd.h>
12#include <fcntl.h>
13#include <limits.h>
14
15#ifdef __APPLE__
16#include <sys/syscall.h>
17#endif
18
19#include "util.hh"
20
21
22extern char * * environ;
23
24
25namespace nix {
26
27
28BaseError::BaseError(const FormatOrString & fs, unsigned int status)
29 : status(status)
30{
31 err = fs.s;
32}
33
34
35BaseError & BaseError::addPrefix(const FormatOrString & fs)
36{
37 prefix_ = fs.s + prefix_;
38 return *this;
39}
40
41
42SysError::SysError(const FormatOrString & fs)
43 : Error(format("%1%: %2%") % fs.s % strerror(errno))
44 , errNo(errno)
45{
46}
47
48
49string getEnv(const string & key, const string & def)
50{
51 char * value = getenv(key.c_str());
52 return value ? string(value) : def;
53}
54
55
56Path absPath(Path path, Path dir)
57{
58 if (path[0] != '/') {
59 if (dir == "") {
60#ifdef __GNU__
61 /* GNU (aka. GNU/Hurd) doesn't have any limitation on path
62 lengths and doesn't define `PATH_MAX'. */
63 char *buf = getcwd(NULL, 0);
64 if (buf == NULL)
65#else
66 char buf[PATH_MAX];
67 if (!getcwd(buf, sizeof(buf)))
68#endif
69 throw SysError("cannot get cwd");
70 dir = buf;
71#ifdef __GNU__
72 free(buf);
73#endif
74 }
75 path = dir + "/" + path;
76 }
77 return canonPath(path);
78}
79
80
81Path canonPath(const Path & path, bool resolveSymlinks)
82{
83 string s;
84
85 if (path[0] != '/')
86 throw Error(format("not an absolute path: `%1%'") % path);
87
88 string::const_iterator i = path.begin(), end = path.end();
89 string temp;
90
91 /* Count the number of times we follow a symlink and stop at some
92 arbitrary (but high) limit to prevent infinite loops. */
93 unsigned int followCount = 0, maxFollow = 1024;
94
95 while (1) {
96
97 /* Skip slashes. */
98 while (i != end && *i == '/') i++;
99 if (i == end) break;
100
101 /* Ignore `.'. */
102 if (*i == '.' && (i + 1 == end || i[1] == '/'))
103 i++;
104
105 /* If `..', delete the last component. */
106 else if (*i == '.' && i + 1 < end && i[1] == '.' &&
107 (i + 2 == end || i[2] == '/'))
108 {
109 if (!s.empty()) s.erase(s.rfind('/'));
110 i += 2;
111 }
112
113 /* Normal component; copy it. */
114 else {
115 s += '/';
116 while (i != end && *i != '/') s += *i++;
117
118 /* If s points to a symlink, resolve it and restart (since
119 the symlink target might contain new symlinks). */
120 if (resolveSymlinks && isLink(s)) {
121 if (++followCount >= maxFollow)
122 throw Error(format("infinite symlink recursion in path `%1%'") % path);
123 temp = absPath(readLink(s), dirOf(s))
124 + string(i, end);
125 i = temp.begin(); /* restart */
126 end = temp.end();
127 s = "";
128 /* !!! potential for infinite loop */
129 }
130 }
131 }
132
133 return s.empty() ? "/" : s;
134}
135
136
137Path dirOf(const Path & path)
138{
139 Path::size_type pos = path.rfind('/');
140 if (pos == string::npos)
141 throw Error(format("invalid file name `%1%'") % path);
142 return pos == 0 ? "/" : Path(path, 0, pos);
143}
144
145
146string baseNameOf(const Path & path)
147{
148 Path::size_type pos = path.rfind('/');
149 if (pos == string::npos)
150 throw Error(format("invalid file name `%1%'") % path);
151 return string(path, pos + 1);
152}
153
154
155bool isInDir(const Path & path, const Path & dir)
156{
157 return path[0] == '/'
158 && string(path, 0, dir.size()) == dir
159 && path.size() >= dir.size() + 2
160 && path[dir.size()] == '/';
161}
162
163
164struct stat lstat(const Path & path)
165{
166 struct stat st;
167 if (lstat(path.c_str(), &st))
168 throw SysError(format("getting status of `%1%'") % path);
169 return st;
170}
171
172
173bool pathExists(const Path & path)
174{
175 int res;
176 struct stat st;
177 res = lstat(path.c_str(), &st);
178 if (!res) return true;
179 if (errno != ENOENT && errno != ENOTDIR)
180 throw SysError(format("getting status of %1%") % path);
181 return false;
182}
183
184
185Path readLink(const Path & path)
186{
187 checkInterrupt();
188 struct stat st = lstat(path);
189 if (!S_ISLNK(st.st_mode))
190 throw Error(format("`%1%' is not a symlink") % path);
191 char buf[st.st_size];
192 if (readlink(path.c_str(), buf, st.st_size) != st.st_size)
193 throw SysError(format("reading symbolic link `%1%'") % path);
194 return string(buf, st.st_size);
195}
196
197
198bool isLink(const Path & path)
199{
200 struct stat st = lstat(path);
201 return S_ISLNK(st.st_mode);
202}
203
204
205Strings readDirectory(const Path & path)
206{
207 Strings names;
208
209 AutoCloseDir dir = opendir(path.c_str());
210 if (!dir) throw SysError(format("opening directory `%1%'") % path);
211
212 struct dirent * dirent;
213 while (errno = 0, dirent = readdir(dir)) { /* sic */
214 checkInterrupt();
215 string name = dirent->d_name;
216 if (name == "." || name == "..") continue;
217 names.push_back(name);
218 }
219 if (errno) throw SysError(format("reading directory `%1%'") % path);
220
221 return names;
222}
223
224
225string readFile(int fd)
226{
227 struct stat st;
228 if (fstat(fd, &st) == -1)
229 throw SysError("statting file");
230
231 unsigned char * buf = new unsigned char[st.st_size];
232 AutoDeleteArray<unsigned char> d(buf);
233 readFull(fd, buf, st.st_size);
234
235 return string((char *) buf, st.st_size);
236}
237
238
239string readFile(const Path & path, bool drain)
240{
241 AutoCloseFD fd = open(path.c_str(), O_RDONLY);
242 if (fd == -1)
243 throw SysError(format("opening file `%1%'") % path);
244 return drain ? drainFD(fd) : readFile(fd);
245}
246
247
248void writeFile(const Path & path, const string & s)
249{
250 AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
251 if (fd == -1)
252 throw SysError(format("opening file `%1%'") % path);
253 writeFull(fd, (unsigned char *) s.data(), s.size());
254}
255
256
257string readLine(int fd)
258{
259 string s;
260 while (1) {
261 checkInterrupt();
262 char ch;
263 ssize_t rd = read(fd, &ch, 1);
264 if (rd == -1) {
265 if (errno != EINTR)
266 throw SysError("reading a line");
267 } else if (rd == 0)
268 throw EndOfFile("unexpected EOF reading a line");
269 else {
270 if (ch == '\n') return s;
271 s += ch;
272 }
273 }
274}
275
276
277void writeLine(int fd, string s)
278{
279 s += '\n';
280 writeFull(fd, (const unsigned char *) s.data(), s.size());
281}
282
283
284static void _deletePath(const Path & path, unsigned long long & bytesFreed)
285{
286 checkInterrupt();
287
288 printMsg(lvlVomit, format("%1%") % path);
289
290 struct stat st = lstat(path);
291
292 if (!S_ISDIR(st.st_mode) && st.st_nlink == 1)
293 bytesFreed += st.st_blocks * 512;
294
295 if (S_ISDIR(st.st_mode)) {
296 Strings names = readDirectory(path);
297
298 /* Make the directory writable. */
299 if (!(st.st_mode & S_IWUSR)) {
300 if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
301 throw SysError(format("making `%1%' writable") % path);
302 }
303
304 for (Strings::iterator i = names.begin(); i != names.end(); ++i)
305 _deletePath(path + "/" + *i, bytesFreed);
306 }
307
308 if (remove(path.c_str()) == -1)
309 throw SysError(format("cannot unlink `%1%'") % path);
310}
311
312
313void deletePath(const Path & path)
314{
315 unsigned long long dummy;
316 deletePath(path, dummy);
317}
318
319
320void deletePath(const Path & path, unsigned long long & bytesFreed)
321{
322 startNest(nest, lvlDebug,
323 format("recursively deleting path `%1%'") % path);
324 bytesFreed = 0;
325 _deletePath(path, bytesFreed);
326}
327
328
329static Path tempName(Path tmpRoot, const Path & prefix, bool includePid,
330 int & counter)
331{
332 tmpRoot = canonPath(tmpRoot.empty() ? getEnv("TMPDIR", "/tmp") : tmpRoot, true);
333 if (includePid)
334 return (format("%1%/%2%-%3%-%4%") % tmpRoot % prefix % getpid() % counter++).str();
335 else
336 return (format("%1%/%2%-%3%") % tmpRoot % prefix % counter++).str();
337}
338
339
340Path createTempDir(const Path & tmpRoot, const Path & prefix,
341 bool includePid, bool useGlobalCounter, mode_t mode)
342{
343 static int globalCounter = 0;
344 int localCounter = 0;
345 int & counter(useGlobalCounter ? globalCounter : localCounter);
346
347 while (1) {
348 checkInterrupt();
349 Path tmpDir = tempName(tmpRoot, prefix, includePid, counter);
350 if (mkdir(tmpDir.c_str(), mode) == 0) {
351 /* Explicitly set the group of the directory. This is to
352 work around around problems caused by BSD's group
353 ownership semantics (directories inherit the group of
354 the parent). For instance, the group of /tmp on
355 FreeBSD is "wheel", so all directories created in /tmp
356 will be owned by "wheel"; but if the user is not in
357 "wheel", then "tar" will fail to unpack archives that
358 have the setgid bit set on directories. */
359 if (chown(tmpDir.c_str(), (uid_t) -1, getegid()) != 0)
360 throw SysError(format("setting group of directory `%1%'") % tmpDir);
361 return tmpDir;
362 }
363 if (errno != EEXIST)
364 throw SysError(format("creating directory `%1%'") % tmpDir);
365 }
366}
367
368
369Paths createDirs(const Path & path)
370{
371 Paths created;
372 if (path == "/") return created;
373
374 struct stat st;
375 if (lstat(path.c_str(), &st) == -1) {
376 created = createDirs(dirOf(path));
377 if (mkdir(path.c_str(), 0777) == -1 && errno != EEXIST)
378 throw SysError(format("creating directory `%1%'") % path);
379 st = lstat(path);
380 created.push_back(path);
381 }
382
383 if (!S_ISDIR(st.st_mode)) throw Error(format("`%1%' is not a directory") % path);
384
385 return created;
386}
387
388
389void createSymlink(const Path & target, const Path & link)
390{
391 if (symlink(target.c_str(), link.c_str()))
392 throw SysError(format("creating symlink from `%1%' to `%2%'") % link % target);
393}
394
395
396LogType logType = ltPretty;
397Verbosity verbosity = lvlInfo;
398
399static int nestingLevel = 0;
400
401
402Nest::Nest()
403{
404 nest = false;
405}
406
407
408Nest::~Nest()
409{
410 close();
411}
412
413
414static string escVerbosity(Verbosity level)
415{
416 return int2String((int) level);
417}
418
419
420void Nest::open(Verbosity level, const FormatOrString & fs)
421{
422 if (level <= verbosity) {
423 if (logType == ltEscapes)
424 std::cerr << "\033[" << escVerbosity(level) << "p"
425 << fs.s << "\n";
426 else
427 printMsg_(level, fs);
428 nest = true;
429 nestingLevel++;
430 }
431}
432
433
434void Nest::close()
435{
436 if (nest) {
437 nestingLevel--;
438 if (logType == ltEscapes)
439 std::cerr << "\033[q";
440 nest = false;
441 }
442}
443
444
445void printMsg_(Verbosity level, const FormatOrString & fs)
446{
447 checkInterrupt();
448 if (level > verbosity) return;
449 string prefix;
450 if (logType == ltPretty)
451 for (int i = 0; i < nestingLevel; i++)
452 prefix += "| ";
453 else if (logType == ltEscapes && level != lvlInfo)
454 prefix = "\033[" + escVerbosity(level) + "s";
455 string s = (format("%1%%2%\n") % prefix % fs.s).str();
456 writeToStderr(s);
457}
458
459
460void warnOnce(bool & haveWarned, const FormatOrString & fs)
461{
462 if (!haveWarned) {
463 printMsg(lvlError, format("warning: %1%") % fs.s);
464 haveWarned = true;
465 }
466}
467
468
469void writeToStderr(const string & s)
470{
471 try {
472 _writeToStderr((const unsigned char *) s.data(), s.size());
473 } catch (SysError & e) {
474 /* Ignore failing writes to stderr if we're in an exception
475 handler, otherwise throw an exception. We need to ignore
476 write errors in exception handlers to ensure that cleanup
477 code runs to completion if the other side of stderr has
478 been closed unexpectedly. */
479 if (!std::uncaught_exception()) throw;
480 }
481}
482
483
484static void defaultWriteToStderr(const unsigned char * buf, size_t count)
485{
486 writeFull(STDERR_FILENO, buf, count);
487}
488
489
490void (*_writeToStderr) (const unsigned char * buf, size_t count) = defaultWriteToStderr;
491
492
493void readFull(int fd, unsigned char * buf, size_t count)
494{
495 while (count) {
496 checkInterrupt();
497 ssize_t res = read(fd, (char *) buf, count);
498 if (res == -1) {
499 if (errno == EINTR) continue;
500 throw SysError("reading from file");
501 }
502 if (res == 0) throw EndOfFile("unexpected end-of-file");
503 count -= res;
504 buf += res;
505 }
506}
507
508
509void writeFull(int fd, const unsigned char * buf, size_t count)
510{
511 while (count) {
512 checkInterrupt();
513 ssize_t res = write(fd, (char *) buf, count);
514 if (res == -1) {
515 if (errno == EINTR) continue;
516 throw SysError("writing to file");
517 }
518 count -= res;
519 buf += res;
520 }
521}
522
523
524string drainFD(int fd)
525{
526 string result;
527 unsigned char buffer[4096];
528 while (1) {
529 checkInterrupt();
530 ssize_t rd = read(fd, buffer, sizeof buffer);
531 if (rd == -1) {
532 if (errno != EINTR)
533 throw SysError("reading from file");
534 }
535 else if (rd == 0) break;
536 else result.append((char *) buffer, rd);
537 }
538 return result;
539}
540
541
542
543//////////////////////////////////////////////////////////////////////
544
545
546AutoDelete::AutoDelete(const string & p, bool recursive) : path(p)
547{
548 del = true;
549 this->recursive = recursive;
550}
551
552AutoDelete::~AutoDelete()
553{
554 try {
555 if (del) {
556 if (recursive)
557 deletePath(path);
558 else {
559 if (remove(path.c_str()) == -1)
560 throw SysError(format("cannot unlink `%1%'") % path);
561 }
562 }
563 } catch (...) {
564 ignoreException();
565 }
566}
567
568void AutoDelete::cancel()
569{
570 del = false;
571}
572
573
574
575//////////////////////////////////////////////////////////////////////
576
577
578AutoCloseFD::AutoCloseFD()
579{
580 fd = -1;
581}
582
583
584AutoCloseFD::AutoCloseFD(int fd)
585{
586 this->fd = fd;
587}
588
589
590AutoCloseFD::AutoCloseFD(const AutoCloseFD & fd)
591{
592 /* Copying an AutoCloseFD isn't allowed (who should get to close
593 it?). But as an edge case, allow copying of closed
594 AutoCloseFDs. This is necessary due to tiresome reasons
595 involving copy constructor use on default object values in STL
596 containers (like when you do `map[value]' where value isn't in
597 the map yet). */
598 this->fd = fd.fd;
599 if (this->fd != -1) abort();
600}
601
602
603AutoCloseFD::~AutoCloseFD()
604{
605 try {
606 close();
607 } catch (...) {
608 ignoreException();
609 }
610}
611
612
613void AutoCloseFD::operator =(int fd)
614{
615 if (this->fd != fd) close();
616 this->fd = fd;
617}
618
619
620AutoCloseFD::operator int() const
621{
622 return fd;
623}
624
625
626void AutoCloseFD::close()
627{
628 if (fd != -1) {
629 if (::close(fd) == -1)
630 /* This should never happen. */
631 throw SysError(format("closing file descriptor %1%") % fd);
632 fd = -1;
633 }
634}
635
636
637bool AutoCloseFD::isOpen()
638{
639 return fd != -1;
640}
641
642
643/* Pass responsibility for closing this fd to the caller. */
644int AutoCloseFD::borrow()
645{
646 int oldFD = fd;
647 fd = -1;
648 return oldFD;
649}
650
651
652void Pipe::create()
653{
654 int fds[2];
655 if (pipe(fds) != 0) throw SysError("creating pipe");
656 readSide = fds[0];
657 writeSide = fds[1];
658 closeOnExec(readSide);
659 closeOnExec(writeSide);
660}
661
662
663
664//////////////////////////////////////////////////////////////////////
665
666
667AutoCloseDir::AutoCloseDir()
668{
669 dir = 0;
670}
671
672
673AutoCloseDir::AutoCloseDir(DIR * dir)
674{
675 this->dir = dir;
676}
677
678
679AutoCloseDir::~AutoCloseDir()
680{
681 close();
682}
683
684
685void AutoCloseDir::operator =(DIR * dir)
686{
687 this->dir = dir;
688}
689
690
691AutoCloseDir::operator DIR *()
692{
693 return dir;
694}
695
696
697void AutoCloseDir::close()
698{
699 if (dir) {
700 closedir(dir);
701 dir = 0;
702 }
703}
704
705
706//////////////////////////////////////////////////////////////////////
707
708
709Pid::Pid()
710{
711 pid = -1;
712 separatePG = false;
713 killSignal = SIGKILL;
714}
715
716
717Pid::~Pid()
718{
719 kill();
720}
721
722
723void Pid::operator =(pid_t pid)
724{
725 if (this->pid != pid) kill();
726 this->pid = pid;
727 killSignal = SIGKILL; // reset signal to default
728}
729
730
731Pid::operator pid_t()
732{
733 return pid;
734}
735
736
737void Pid::kill()
738{
739 if (pid == -1 || pid == 0) return;
740
741 printMsg(lvlError, format("killing process %1%") % pid);
742
743 /* Send the requested signal to the child. If it has its own
744 process group, send the signal to every process in the child
745 process group (which hopefully includes *all* its children). */
746 if (::kill(separatePG ? -pid : pid, killSignal) != 0)
747 printMsg(lvlError, (SysError(format("killing process %1%") % pid).msg()));
748
749 /* Wait until the child dies, disregarding the exit status. */
750 int status;
751 while (waitpid(pid, &status, 0) == -1) {
752 checkInterrupt();
753 if (errno != EINTR) {
754 printMsg(lvlError,
755 (SysError(format("waiting for process %1%") % pid).msg()));
756 break;
757 }
758 }
759
760 pid = -1;
761}
762
763
764int Pid::wait(bool block)
765{
766 assert(pid != -1);
767 while (1) {
768 int status;
769 int res = waitpid(pid, &status, block ? 0 : WNOHANG);
770 if (res == pid) {
771 pid = -1;
772 return status;
773 }
774 if (res == 0 && !block) return -1;
775 if (errno != EINTR)
776 throw SysError("cannot get child exit status");
777 checkInterrupt();
778 }
779}
780
781
782void Pid::setSeparatePG(bool separatePG)
783{
784 this->separatePG = separatePG;
785}
786
787
788void Pid::setKillSignal(int signal)
789{
790 this->killSignal = signal;
791}
792
793
794void killUser(uid_t uid)
795{
796 debug(format("killing all processes running under uid `%1%'") % uid);
797
798 assert(uid != 0); /* just to be safe... */
799
800 /* The system call kill(-1, sig) sends the signal `sig' to all
801 users to which the current process can send signals. So we
802 fork a process, switch to uid, and send a mass kill. */
803
804 Pid pid;
805 pid = fork();
806 switch (pid) {
807
808 case -1:
809 throw SysError("unable to fork");
810
811 case 0:
812 try { /* child */
813
814 if (setuid(uid) == -1)
815 throw SysError("setting uid");
816
817 while (true) {
818#ifdef __APPLE__
819 /* OSX's kill syscall takes a third parameter that, among other
820 things, determines if kill(-1, signo) affects the calling
821 process. In the OSX libc, it's set to true, which means
822 "follow POSIX", which we don't want here
823 */
824 if (syscall(SYS_kill, -1, SIGKILL, false) == 0) break;
825#else
826 if (kill(-1, SIGKILL) == 0) break;
827#endif
828 if (errno == ESRCH) break; /* no more processes */
829 if (errno != EINTR)
830 throw SysError(format("cannot kill processes for uid `%1%'") % uid);
831 }
832
833 } catch (std::exception & e) {
834 writeToStderr((format("killing processes belonging to uid `%1%': %2%\n") % uid % e.what()).str());
835 _exit(1);
836 }
837 _exit(0);
838 }
839
840 /* parent */
841 int status = pid.wait(true);
842 if (status != 0)
843 throw Error(format("cannot kill processes for uid `%1%': %2%") % uid % statusToString(status));
844
845 /* !!! We should really do some check to make sure that there are
846 no processes left running under `uid', but there is no portable
847 way to do so (I think). The most reliable way may be `ps -eo
848 uid | grep -q $uid'. */
849}
850
851
852//////////////////////////////////////////////////////////////////////
853
854
1303a4a4
LC
855std::vector<const char *> stringsToCharPtrs(const Strings & ss)
856{
857 std::vector<const char *> res;
858 foreach (Strings::const_iterator, i, ss)
859 res.push_back(i->c_str());
860 res.push_back(0);
861 return res;
862}
863
864
36457566
LC
865string runProgram(Path program, bool searchPath, const Strings & args)
866{
867 checkInterrupt();
868
36457566
LC
869 /* Create a pipe. */
870 Pipe pipe;
871 pipe.create();
872
873 /* Fork. */
874 Pid pid;
875 pid = maybeVfork();
876
877 switch (pid) {
878
879 case -1:
880 throw SysError("unable to fork");
881
882 case 0: /* child */
883 try {
884 if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
885 throw SysError("dupping stdout");
886
1303a4a4
LC
887 Strings args_(args);
888 args_.push_front(program);
889 auto cargs = stringsToCharPtrs(args_);
890
36457566
LC
891 if (searchPath)
892 execvp(program.c_str(), (char * *) &cargs[0]);
893 else
894 execv(program.c_str(), (char * *) &cargs[0]);
895 throw SysError(format("executing `%1%'") % program);
896
897 } catch (std::exception & e) {
898 writeToStderr("error: " + string(e.what()) + "\n");
899 }
900 _exit(1);
901 }
902
903 /* Parent. */
904
905 pipe.writeSide.close();
906
907 string result = drainFD(pipe.readSide);
908
909 /* Wait for the child to finish. */
910 int status = pid.wait(true);
911 if (!statusOk(status))
912 throw Error(format("program `%1%' %2%")
913 % program % statusToString(status));
914
915 return result;
916}
917
918
919void closeMostFDs(const set<int> & exceptions)
920{
921 int maxFD = 0;
922 maxFD = sysconf(_SC_OPEN_MAX);
923 for (int fd = 0; fd < maxFD; ++fd)
924 if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO
925 && exceptions.find(fd) == exceptions.end())
926 close(fd); /* ignore result */
927}
928
929
930void closeOnExec(int fd)
931{
932 int prev;
933 if ((prev = fcntl(fd, F_GETFD, 0)) == -1 ||
934 fcntl(fd, F_SETFD, prev | FD_CLOEXEC) == -1)
935 throw SysError("setting close-on-exec flag");
936}
937
938
939#if HAVE_VFORK
940pid_t (*maybeVfork)() = vfork;
941#else
942pid_t (*maybeVfork)() = fork;
943#endif
944
945
946//////////////////////////////////////////////////////////////////////
947
948
949volatile sig_atomic_t _isInterrupted = 0;
950
951void _interrupted()
952{
953 /* Block user interrupts while an exception is being handled.
954 Throwing an exception while another exception is being handled
955 kills the program! */
956 if (!std::uncaught_exception()) {
957 _isInterrupted = 0;
958 throw Interrupted("interrupted by the user");
959 }
960}
961
962
963
964//////////////////////////////////////////////////////////////////////
965
966
967template<class C> C tokenizeString(const string & s, const string & separators)
968{
969 C result;
970 string::size_type pos = s.find_first_not_of(separators, 0);
971 while (pos != string::npos) {
972 string::size_type end = s.find_first_of(separators, pos + 1);
973 if (end == string::npos) end = s.size();
974 string token(s, pos, end - pos);
975 result.insert(result.end(), token);
976 pos = s.find_first_not_of(separators, end);
977 }
978 return result;
979}
980
981template Strings tokenizeString(const string & s, const string & separators);
982template StringSet tokenizeString(const string & s, const string & separators);
983template vector<string> tokenizeString(const string & s, const string & separators);
984
985
986string concatStringsSep(const string & sep, const Strings & ss)
987{
988 string s;
989 foreach (Strings::const_iterator, i, ss) {
990 if (s.size() != 0) s += sep;
991 s += *i;
992 }
993 return s;
994}
995
996
997string concatStringsSep(const string & sep, const StringSet & ss)
998{
999 string s;
1000 foreach (StringSet::const_iterator, i, ss) {
1001 if (s.size() != 0) s += sep;
1002 s += *i;
1003 }
1004 return s;
1005}
1006
1007
1008string chomp(const string & s)
1009{
1010 size_t i = s.find_last_not_of(" \n\r\t");
1011 return i == string::npos ? "" : string(s, 0, i + 1);
1012}
1013
1014
1015string statusToString(int status)
1016{
1017 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
1018 if (WIFEXITED(status))
1019 return (format("failed with exit code %1%") % WEXITSTATUS(status)).str();
1020 else if (WIFSIGNALED(status)) {
1021 int sig = WTERMSIG(status);
1022#if HAVE_STRSIGNAL
1023 const char * description = strsignal(sig);
1024 return (format("failed due to signal %1% (%2%)") % sig % description).str();
1025#else
1026 return (format("failed due to signal %1%") % sig).str();
1027#endif
1028 }
1029 else
1030 return "died abnormally";
1031 } else return "succeeded";
1032}
1033
1034
1035bool statusOk(int status)
1036{
1037 return WIFEXITED(status) && WEXITSTATUS(status) == 0;
1038}
1039
1040
1041bool hasSuffix(const string & s, const string & suffix)
1042{
1043 return s.size() >= suffix.size() && string(s, s.size() - suffix.size()) == suffix;
1044}
1045
1046
1047void expect(std::istream & str, const string & s)
1048{
1049 char s2[s.size()];
1050 str.read(s2, s.size());
1051 if (string(s2, s.size()) != s)
15ddeff5 1052 throw FormatError(format("expected string `%1%'") % s);
36457566
LC
1053}
1054
1055
1056string parseString(std::istream & str)
1057{
1058 string res;
1059 expect(str, "\"");
1060 int c;
1061 while ((c = str.get()) != '"')
1062 if (c == '\\') {
1063 c = str.get();
1064 if (c == 'n') res += '\n';
1065 else if (c == 'r') res += '\r';
1066 else if (c == 't') res += '\t';
1067 else res += c;
1068 }
1069 else res += c;
1070 return res;
1071}
1072
1073
1074bool endOfList(std::istream & str)
1075{
1076 if (str.peek() == ',') {
1077 str.get();
1078 return false;
1079 }
1080 if (str.peek() == ']') {
1081 str.get();
1082 return true;
1083 }
1084 return false;
1085}
1086
1087
1088string decodeOctalEscaped(const string & s)
1089{
1090 string r;
1091 for (string::const_iterator i = s.begin(); i != s.end(); ) {
1092 if (*i != '\\') { r += *i++; continue; }
1093 unsigned char c = 0;
1094 ++i;
1095 while (i != s.end() && *i >= '0' && *i < '8')
1096 c = c * 8 + (*i++ - '0');
1097 r += c;
1098 }
1099 return r;
1100}
1101
1102
1103void ignoreException()
1104{
1105 try {
1106 throw;
1107 } catch (std::exception & e) {
1108 printMsg(lvlError, format("error (ignored): %1%") % e.what());
1109 }
1110}
1111
1112
1113}