Switch to unified view

a b/src/pathut.cpp
1
/* Copyright (C) 2004 J.F.Dockes
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the
14
 *   Free Software Foundation, Inc.,
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16
 */
17
18
#ifndef TEST_PATHUT
19
#ifdef BUILDING_RECOLL
20
#include "autoconfig.h"
21
#else
22
#include "config.h"
23
#endif
24
25
#include <stdio.h>
26
#ifdef _WIN32
27
#include "dirent.h"
28
#include "safefcntl.h"
29
#include "safeunistd.h"
30
#include "safewindows.h"
31
#include "safesysstat.h"
32
#else
33
#include <fcntl.h>
34
#include <unistd.h>
35
#include <sys/param.h>
36
#include <pwd.h>
37
#include <sys/file.h>
38
#include <sys/stat.h>
39
#include <dirent.h>
40
#endif
41
#include <math.h>
42
#include <errno.h>
43
#include <sys/types.h>
44
45
// Let's include all files where statfs can be defined and hope for no
46
// conflict...
47
#ifdef HAVE_SYS_MOUNT_H
48
#include <sys/mount.h>
49
#endif
50
#ifdef HAVE_SYS_STATFS_H
51
#include <sys/statfs.h>
52
#endif
53
#ifdef HAVE_SYS_STATVFS_H
54
#include <sys/statvfs.h>
55
#endif
56
#ifdef HAVE_SYS_VFS_H
57
#include <sys/vfs.h>
58
#endif
59
60
#include <cstdlib>
61
#include <cstring>
62
#include <iostream>
63
#include <sstream>
64
#include <stack>
65
#include <set>
66
#include <vector>
67
68
#include "pathut.h"
69
#include "smallut.h"
70
71
using namespace std;
72
73
#ifdef _WIN32
74
/// Convert \ separators to /
75
void path_slashize(string& s)
76
{
77
    for (string::size_type i = 0; i < s.size(); i++) {
78
        if (s[i] == '\\') {
79
            s[i] = '/';
80
        }
81
    }
82
}
83
static bool path_strlookslikedrive(const string& s)
84
{
85
    return s.size() == 2 && isalpha(s[0]) && s[1] == ':';
86
}
87
88
static bool path_hasdrive(const string& s)
89
{
90
    if (s.size() >= 2 && isalpha(s[0]) && s[1] == ':') {
91
        return true;
92
    }
93
    return false;
94
}
95
static bool path_isdriveabs(const string& s)
96
{
97
    if (s.size() >= 3 && isalpha(s[0]) && s[1] == ':' && s[2] == '/') {
98
        return true;
99
    }
100
    return false;
101
}
102
#endif
103
104
#if defined(HAVE_SYS_MOUNT_H) || defined(HAVE_SYS_STATFS_H) || \
105
    defined(HAVE_SYS_STATVFS_H) || defined(HAVE_SYS_VFS_H) || defined(_WIN32)
106
bool fsocc(const string& path, int *pc, long long *avmbs)
107
{
108
    static const int FSOCC_MB = 1024 * 1024;
109
#ifdef _WIN32
110
    ULARGE_INTEGER freebytesavail;
111
    ULARGE_INTEGER totalbytes;
112
    if (!GetDiskFreeSpaceEx(path.c_str(), &freebytesavail,
113
                            &totalbytes, NULL)) {
114
        return false;
115
    }
116
    if (pc) {
117
        *pc = int((100 * freebytesavail.QuadPart) / totalbytes.QuadPart);
118
    }
119
    if (avmbs) {
120
        *avmbs = int(totalbytes.QuadPart / FSOCC_MB);
121
    }
122
    return true;
123
#else
124
#ifdef sun
125
    struct statvfs buf;
126
    if (statvfs(path.c_str(), &buf) != 0) {
127
        return false;
128
    }
129
#else
130
    struct statfs buf;
131
    if (statfs(path.c_str(), &buf) != 0) {
132
        return false;
133
    }
134
#endif
135
136
    // used blocks
137
    double fpc = 0.0;
138
#define FSOCC_USED (double(buf.f_blocks - buf.f_bfree))
139
#define FSOCC_TOTAVAIL (FSOCC_USED + double(buf.f_bavail))
140
    if (FSOCC_TOTAVAIL > 0) {
141
        fpc = 100.0 * FSOCC_USED / FSOCC_TOTAVAIL;
142
    }
143
    if (pc) {
144
        *pc = int(fpc);
145
    }
146
    if (avmbs) {
147
        *avmbs = 0;
148
        if (buf.f_bsize > 0) {
149
            int ratio = buf.f_bsize > FSOCC_MB ? buf.f_bsize / FSOCC_MB :
150
                        FSOCC_MB / buf.f_bsize;
151
152
            *avmbs = buf.f_bsize > FSOCC_MB ?
153
                     ((long long)buf.f_bavail) * ratio :
154
                     ((long long)buf.f_bavail) / ratio;
155
        }
156
    }
157
    return true;
158
#endif
159
}
160
#endif // we have found an appropriate include file
161
162
string path_PATHsep()
163
{
164
    static const string w(";");
165
    static const string u(":");
166
#ifdef _WIN32
167
    return w;
168
#else
169
    return u;
170
#endif
171
}
172
173
void path_catslash(string& s)
174
{
175
#ifdef _WIN32
176
    path_slashize(s);
177
#endif
178
    if (s.empty() || s[s.length() - 1] != '/') {
179
        s += '/';
180
    }
181
}
182
183
string path_cat(const string& s1, const string& s2)
184
{
185
    string res = s1;
186
    path_catslash(res);
187
    res +=  s2;
188
    return res;
189
}
190
191
string path_getfather(const string& s)
192
{
193
    string father = s;
194
#ifdef _WIN32
195
    path_slashize(father);
196
#endif
197
198
    // ??
199
    if (father.empty()) {
200
        return "./";
201
    }
202
203
    if (path_isroot(father)) {
204
        return father;
205
    }
206
207
    if (father[father.length() - 1] == '/') {
208
        // Input ends with /. Strip it, root special case was tested above
209
        father.erase(father.length() - 1);
210
    }
211
212
    string::size_type slp = father.rfind('/');
213
    if (slp == string::npos) {
214
        return "./";
215
    }
216
217
    father.erase(slp);
218
    path_catslash(father);
219
    return father;
220
}
221
222
string path_getsimple(const string& s)
223
{
224
    string simple = s;
225
#ifdef _WIN32
226
    path_slashize(simple);
227
#endif
228
229
    if (simple.empty()) {
230
        return simple;
231
    }
232
233
    string::size_type slp = simple.rfind('/');
234
    if (slp == string::npos) {
235
        return simple;
236
    }
237
238
    simple.erase(0, slp + 1);
239
    return simple;
240
}
241
242
string path_basename(const string& s, const string& suff)
243
{
244
    string simple = path_getsimple(s);
245
    string::size_type pos = string::npos;
246
    if (suff.length() && simple.length() > suff.length()) {
247
        pos = simple.rfind(suff);
248
        if (pos != string::npos && pos + suff.length() == simple.length()) {
249
            return simple.substr(0, pos);
250
        }
251
    }
252
    return simple;
253
}
254
255
string path_suffix(const string& s)
256
{
257
    string::size_type dotp = s.rfind('.');
258
    if (dotp == string::npos) {
259
        return string();
260
    }
261
    return s.substr(dotp + 1);
262
}
263
264
string path_home()
265
{
266
#ifdef _WIN32
267
    string dir;
268
    const char *cp = getenv("USERPROFILE");
269
    if (cp != 0) {
270
        dir = cp;
271
    }
272
    if (dir.empty()) {
273
        cp = getenv("HOMEDRIVE");
274
        if (cp != 0) {
275
            const char *cp1 = getenv("HOMEPATH");
276
            if (cp1 != 0) {
277
                dir = string(cp) + string(cp1);
278
            }
279
        }
280
    }
281
    if (dir.empty()) {
282
        dir = "C:\\";
283
    }
284
    dir = path_canon(dir);
285
    path_catslash(dir);
286
    return dir;
287
#else
288
    uid_t uid = getuid();
289
290
    struct passwd *entry = getpwuid(uid);
291
    if (entry == 0) {
292
        const char *cp = getenv("HOME");
293
        if (cp) {
294
            return cp;
295
        } else {
296
            return "/";
297
        }
298
    }
299
300
    string homedir = entry->pw_dir;
301
    path_catslash(homedir);
302
    return homedir;
303
#endif
304
}
305
306
// The default place to store the default config and other stuff (e.g webqueue)
307
string path_homedata()
308
{
309
#ifdef _WIN32
310
    const char *cp = getenv("LOCALAPPDATA");
311
    string dir;
312
    if (cp != 0) {
313
        dir = path_canon(cp);
314
    }
315
    if (dir.empty()) {
316
        dir = path_cat(path_home(), "AppData/Local/");
317
    }
318
    return dir;
319
#else
320
    // We should use an xdg-conforming location, but, history...
321
    return path_home();
322
#endif
323
}
324
325
string path_tildexpand(const string& s)
326
{
327
    if (s.empty() || s[0] != '~') {
328
        return s;
329
    }
330
    string o = s;
331
#ifdef _WIN32
332
    path_slashize(o);
333
#endif
334
335
    if (s.length() == 1) {
336
        o.replace(0, 1, path_home());
337
    } else if (s[1] == '/') {
338
        o.replace(0, 2, path_home());
339
    } else {
340
        string::size_type pos = s.find('/');
341
        string::size_type l = (pos == string::npos) ? s.length() - 1 : pos - 1;
342
#ifdef _WIN32
343
        // Dont know what this means. Just replace with HOME
344
        o.replace(0, l + 1, path_home());
345
#else
346
        struct passwd *entry = getpwnam(s.substr(1, l).c_str());
347
        if (entry) {
348
            o.replace(0, l + 1, entry->pw_dir);
349
        }
350
#endif
351
    }
352
    return o;
353
}
354
355
bool path_isroot(const string& path)
356
{
357
    if (path.size() == 1 && path[0] == '/') {
358
        return true;
359
    }
360
#ifdef _WIN32
361
    if (path.size() == 3 && isalpha(path[0]) && path[1] == ':' &&
362
            (path[2] == '/' || path[2] == '\\')) {
363
        return true;
364
    }
365
#endif
366
    return false;
367
}
368
369
bool path_isabsolute(const string& path)
370
{
371
    if (!path.empty() && (path[0] == '/'
372
#ifdef _WIN32
373
                          || path_isdriveabs(path)
374
#endif
375
                         )) {
376
        return true;
377
    }
378
    return false;
379
}
380
381
string path_absolute(const string& is)
382
{
383
    if (is.length() == 0) {
384
        return is;
385
    }
386
    string s = is;
387
    if (!path_isabsolute(s)) {
388
        char buf[MAXPATHLEN];
389
        if (!getcwd(buf, MAXPATHLEN)) {
390
            return string();
391
        }
392
        s = path_cat(string(buf), s);
393
#ifdef _WIN32
394
        path_slashize(s);
395
#endif
396
    }
397
    return s;
398
}
399
400
string path_canon(const string& is, const string* cwd)
401
{
402
    if (is.length() == 0) {
403
        return is;
404
    }
405
    string s = is;
406
#ifdef _WIN32
407
    path_slashize(s);
408
    // fix possible path from file: absolute url
409
    if (s.size() && s[0] == '/' && path_hasdrive(s.substr(1))) {
410
        s = s.substr(1);
411
    }
412
#endif
413
414
    if (!path_isabsolute(s)) {
415
        char buf[MAXPATHLEN];
416
        const char *cwdp = buf;
417
        if (cwd) {
418
            cwdp = cwd->c_str();
419
        } else {
420
            if (!getcwd(buf, MAXPATHLEN)) {
421
                return string();
422
            }
423
        }
424
        s = path_cat(string(cwdp), s);
425
    }
426
    vector<string> elems;
427
    stringToTokens(s, elems, "/");
428
    vector<string> cleaned;
429
    for (vector<string>::const_iterator it = elems.begin();
430
            it != elems.end(); it++) {
431
        if (*it == "..") {
432
            if (!cleaned.empty()) {
433
                cleaned.pop_back();
434
            }
435
        } else if (it->empty() || *it == ".") {
436
        } else {
437
            cleaned.push_back(*it);
438
        }
439
    }
440
    string ret;
441
    if (!cleaned.empty()) {
442
        for (vector<string>::const_iterator it = cleaned.begin();
443
                it != cleaned.end(); it++) {
444
            ret += "/";
445
#ifdef _WIN32
446
            if (it == cleaned.begin() && path_strlookslikedrive(*it)) {
447
                // Get rid of just added initial "/"
448
                ret.clear();
449
            }
450
#endif
451
            ret += *it;
452
        }
453
    } else {
454
        ret = "/";
455
    }
456
    return ret;
457
}
458
459
bool path_makepath(const string& ipath, int mode)
460
{
461
    string path = path_canon(ipath);
462
    vector<string> elems;
463
    stringToTokens(path, elems, "/");
464
    path = "/";
465
    for (vector<string>::const_iterator it = elems.begin();
466
            it != elems.end(); it++) {
467
#ifdef _WIN32
468
        if (it == elems.begin() && path_strlookslikedrive(*it)) {
469
            path = "";
470
        }
471
#endif
472
        path += *it;
473
        // Not using path_isdir() here, because this cant grok symlinks
474
        // If we hit an existing file, no worry, mkdir will just fail.
475
        if (access(path.c_str(), 0) != 0) {
476
            if (mkdir(path.c_str(), mode) != 0)  {
477
                return false;
478
            }
479
        }
480
        path += "/";
481
    }
482
    return true;
483
}
484
485
bool path_isdir(const string& path)
486
{
487
    struct stat st;
488
    if (lstat(path.c_str(), &st) < 0) {
489
        return false;
490
    }
491
    if (S_ISDIR(st.st_mode)) {
492
        return true;
493
    }
494
    return false;
495
}
496
497
long long path_filesize(const string& path)
498
{
499
    struct stat st;
500
    if (stat(path.c_str(), &st) < 0) {
501
        return -1;
502
    }
503
    return (long long)st.st_size;
504
}
505
506
int path_fileprops(const std::string path, struct stat *stp, bool follow)
507
{
508
    if (!stp) {
509
        return -1;
510
    }
511
    memset(stp, 0, sizeof(struct stat));
512
    struct stat mst;
513
    int ret = follow ? stat(path.c_str(), &mst) : lstat(path.c_str(), &mst);
514
    if (ret != 0) {
515
        return ret;
516
    }
517
    stp->st_size = mst.st_size;
518
    stp->st_mode = mst.st_mode;
519
    stp->st_mtime = mst.st_mtime;
520
#ifdef _WIN32
521
    stp->st_ctime = mst.st_mtime;
522
#else
523
    stp->st_ino = mst.st_ino;
524
    stp->st_dev = mst.st_dev;
525
    stp->st_ctime = mst.st_ctime;
526
#endif
527
    return 0;
528
}
529
530
bool path_exists(const string& path)
531
{
532
    return access(path.c_str(), 0) == 0;
533
}
534
535
// Allowed punctuation in the path part of an URI according to RFC2396
536
// -_.!~*'():@&=+$,
537
/*
538
21 !
539
    22 "
540
    23 #
541
24 $
542
    25 %
543
26 &
544
27 '
545
28 (
546
29 )
547
2A *
548
2B +
549
2C ,
550
2D -
551
2E .
552
2F /
553
30 0
554
...
555
39 9
556
3A :
557
    3B ;
558
    3C <
559
3D =
560
    3E >
561
    3F ?
562
40 @
563
41 A
564
...
565
5A Z
566
    5B [
567
    5C \
568
    5D ]
569
    5E ^
570
5F _
571
    60 `
572
61 a
573
...
574
7A z
575
    7B {
576
    7C |
577
    7D }
578
7E ~
579
    7F DEL
580
*/
581
string url_encode(const string& url, string::size_type offs)
582
{
583
    string out = url.substr(0, offs);
584
    const char *cp = url.c_str();
585
    for (string::size_type i = offs; i < url.size(); i++) {
586
        unsigned int c;
587
        const char *h = "0123456789ABCDEF";
588
        c = cp[i];
589
        if (c <= 0x20 ||
590
                c >= 0x7f ||
591
                c == '"' ||
592
                c == '#' ||
593
                c == '%' ||
594
                c == ';' ||
595
                c == '<' ||
596
                c == '>' ||
597
                c == '?' ||
598
                c == '[' ||
599
                c == '\\' ||
600
                c == ']' ||
601
                c == '^' ||
602
                c == '`' ||
603
                c == '{' ||
604
                c == '|' ||
605
                c == '}') {
606
            out += '%';
607
            out += h[(c >> 4) & 0xf];
608
            out += h[c & 0xf];
609
        } else {
610
            out += char(c);
611
        }
612
    }
613
    return out;
614
}
615
616
string url_gpath(const string& url)
617
{
618
    // Remove the access schema part (or whatever it's called)
619
    string::size_type colon = url.find_first_of(":");
620
    if (colon == string::npos || colon == url.size() - 1) {
621
        return url;
622
    }
623
    // If there are non-alphanum chars before the ':', then there
624
    // probably is no scheme. Whatever...
625
    for (string::size_type i = 0; i < colon; i++) {
626
        if (!isalnum(url.at(i))) {
627
            return url;
628
        }
629
    }
630
631
    // In addition we canonize the path to remove empty host parts
632
    // (for compatibility with older versions of recoll where file://
633
    // was hardcoded, but the local path was used for doc
634
    // identification.
635
    return path_canon(url.substr(colon + 1));
636
}
637
638
string url_parentfolder(const string& url)
639
{
640
    // In general, the parent is the directory above the full path
641
    string parenturl = path_getfather(url_gpath(url));
642
    // But if this is http, make sure to keep the host part. Recoll
643
    // only has file or http urls for now.
644
    bool isfileurl = urlisfileurl(url);
645
    if (!isfileurl && parenturl == "/") {
646
        parenturl = url_gpath(url);
647
    }
648
    return isfileurl ? string("file://") + parenturl :
649
           string("http://") + parenturl;
650
}
651
652
653
// Convert to file path if url is like file:
654
// Note: this only works with our internal pseudo-urls which are not
655
// encoded/escaped
656
string fileurltolocalpath(string url)
657
{
658
    if (url.find("file://") == 0) {
659
        url = url.substr(7, string::npos);
660
    } else {
661
        return string();
662
    }
663
664
#ifdef _WIN32
665
    // Absolute file urls are like: file:///c:/mydir/...
666
    // Get rid of the initial '/'
667
    if (url.size() >= 3 && url[0] == '/' && isalpha(url[1]) && url[2] == ':') {
668
        url = url.substr(1);
669
    }
670
#endif
671
672
    // Removing the fragment part. This is exclusively used when
673
    // executing a viewer for the recoll manual, and we only strip the
674
    // part after # if it is preceded by .html
675
    string::size_type pos;
676
    if ((pos = url.rfind(".html#")) != string::npos) {
677
        url.erase(pos + 5);
678
    } else if ((pos = url.rfind(".htm#")) != string::npos) {
679
        url.erase(pos + 4);
680
    }
681
682
    return url;
683
}
684
685
static const string cstr_fileu("file://");
686
687
string path_pathtofileurl(const string& path)
688
{
689
    // We're supposed to receive a canonic absolute path, but on windows we
690
    // may need to add a '/' in front of the drive spec
691
    string url(cstr_fileu);
692
    if (path.empty() || path[0] != '/') {
693
        url.push_back('/');
694
    }
695
    url += path;
696
    return url;
697
}
698
699
bool urlisfileurl(const string& url)
700
{
701
    return url.find("file://") == 0;
702
}
703
704
bool readdir(const string& dir, string& reason, set<string>& entries)
705
{
706
    struct stat st;
707
    int statret;
708
    ostringstream msg;
709
    DIR *d = 0;
710
    statret = lstat(dir.c_str(), &st);
711
    if (statret == -1) {
712
        msg << "readdir: cant stat " << dir << " errno " <<  errno;
713
        goto out;
714
    }
715
    if (!S_ISDIR(st.st_mode)) {
716
        msg << "readdir: " << dir <<  " not a directory";
717
        goto out;
718
    }
719
    if (access(dir.c_str(), R_OK) < 0) {
720
        msg << "readdir: no read access to " << dir;
721
        goto out;
722
    }
723
724
    d = opendir(dir.c_str());
725
    if (d == 0) {
726
        msg << "readdir: cant opendir " << dir << ", errno " << errno;
727
        goto out;
728
    }
729
730
    struct dirent *ent;
731
    while ((ent = readdir(d)) != 0) {
732
        if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
733
            continue;
734
        }
735
        entries.insert(ent->d_name);
736
    }
737
738
out:
739
    if (d) {
740
        closedir(d);
741
    }
742
    reason = msg.str();
743
    if (reason.empty()) {
744
        return true;
745
    }
746
    return false;
747
}
748
749
// We do not want to mess with the pidfile content in the destructor:
750
// the lock might still be in use in a child process. In fact as much
751
// as we'd like to reset the pid inside the file when we're done, it
752
// would be very difficult to do it right and it's probably best left
753
// alone.
754
Pidfile::~Pidfile()
755
{
756
    if (m_fd >= 0) {
757
        ::close(m_fd);
758
    }
759
    m_fd = -1;
760
}
761
762
pid_t Pidfile::read_pid()
763
{
764
    int fd = ::open(m_path.c_str(), O_RDONLY);
765
    if (fd == -1) {
766
        return (pid_t) - 1;
767
    }
768
769
    char buf[16];
770
    int i = read(fd, buf, sizeof(buf) - 1);
771
    ::close(fd);
772
    if (i <= 0) {
773
        return (pid_t) - 1;
774
    }
775
    buf[i] = '\0';
776
    char *endptr;
777
    pid_t pid = strtol(buf, &endptr, 10);
778
    if (endptr != &buf[i]) {
779
        return (pid_t) - 1;
780
    }
781
    return pid;
782
}
783
784
int Pidfile::flopen()
785
{
786
    const char *path = m_path.c_str();
787
    if ((m_fd = ::open(path, O_RDWR | O_CREAT, 0644)) == -1) {
788
        m_reason = "Open failed: [" + m_path + "]: " + strerror(errno);
789
        return -1;
790
    }
791
792
#ifdef sun
793
    struct flock lockdata;
794
    lockdata.l_start = 0;
795
    lockdata.l_len = 0;
796
    lockdata.l_type = F_WRLCK;
797
    lockdata.l_whence = SEEK_SET;
798
    if (fcntl(m_fd, F_SETLK,  &lockdata) != 0) {
799
        int serrno = errno;
800
        (void)::close(m_fd);
801
        errno = serrno;
802
        m_reason = "fcntl lock failed";
803
        return -1;
804
    }
805
#else
806
#ifdef _WIN32
807
    return 0;
808
#else
809
    int operation = LOCK_EX | LOCK_NB;
810
    if (flock(m_fd, operation) == -1) {
811
        int serrno = errno;
812
        (void)::close(m_fd);
813
        errno = serrno;
814
        m_reason = "flock failed";
815
        return -1;
816
    }
817
#endif // ! win32
818
#endif // ! sun
819
820
    if (ftruncate(m_fd, 0) != 0) {
821
        /* can't happen [tm] */
822
        int serrno = errno;
823
        (void)::close(m_fd);
824
        errno = serrno;
825
        m_reason = "ftruncate failed";
826
        return -1;
827
    }
828
    return 0;
829
}
830
831
pid_t Pidfile::open()
832
{
833
    if (flopen() < 0) {
834
        return read_pid();
835
    }
836
    return (pid_t)0;
837
}
838
839
int Pidfile::write_pid()
840
{
841
    /* truncate to allow multiple calls */
842
    if (ftruncate(m_fd, 0) == -1) {
843
        m_reason = "ftruncate failed";
844
        return -1;
845
    }
846
    char pidstr[20];
847
    sprintf(pidstr, "%u", int(getpid()));
848
    lseek(m_fd, 0, 0);
849
    if (::write(m_fd, pidstr, strlen(pidstr)) != (ssize_t)strlen(pidstr)) {
850
        m_reason = "write failed";
851
        return -1;
852
    }
853
    return 0;
854
}
855
856
int Pidfile::close()
857
{
858
    return ::close(m_fd);
859
}
860
861
int Pidfile::remove()
862
{
863
    return unlink(m_path.c_str());
864
}
865
866
// Call funcs that need static init (not initially reentrant)
867
void pathut_init_mt()
868
{
869
    path_home();
870
}
871
872
873
#else // TEST_PATHUT
874
#include <stdlib.h>
875
#include <iostream>
876
using namespace std;
877
878
#include "pathut.h"
879
880
void path_to_thumb(const string& _input)
881
{
882
    string input(_input);
883
    // Make absolute path if needed
884
    if (input[0] != '/') {
885
        input = path_absolute(input);
886
    }
887
888
    input = string("file://") + path_canon(input);
889
890
    string path;
891
    //path = url_encode(input, 7);
892
    thumbPathForUrl(input, 7, path);
893
    cout << path << endl;
894
}
895
896
const char *tstvec[] = {"", "/", "/dir", "/dir/", "/dir1/dir2",
897
                        "/dir1/dir2",
898
                        "./dir", "./dir1/", "dir", "../dir", "/dir/toto.c",
899
                        "/dir/.c", "/dir/toto.txt", "toto.txt1"
900
                       };
901
902
const string ttvec[] = {"/dir", "", "~", "~/sub", "~root", "~root/sub",
903
                        "~nosuch", "~nosuch/sub"
904
                       };
905
int nttvec = sizeof(ttvec) / sizeof(string);
906
907
const char *thisprog;
908
909
int main(int argc, const char **argv)
910
{
911
    thisprog = *argv++;
912
    argc--;
913
914
    string s;
915
    vector<string>::const_iterator it;
916
#if 0
917
    for (unsigned int i = 0; i < sizeof(tstvec) / sizeof(char *); i++) {
918
        cout << tstvec[i] << " Father " << path_getfather(tstvec[i]) << endl;
919
    }
920
    for (unsigned int i = 0; i < sizeof(tstvec) / sizeof(char *); i++) {
921
        cout << tstvec[i] << " Simple " << path_getsimple(tstvec[i]) << endl;
922
    }
923
    for (unsigned int i = 0; i < sizeof(tstvec) / sizeof(char *); i++) {
924
        cout << tstvec[i] << " Basename " <<
925
             path_basename(tstvec[i], ".txt") << endl;
926
    }
927
#endif
928
929
#if 0
930
    for (int i = 0; i < nttvec; i++) {
931
        cout << "tildexp: '" << ttvec[i] << "' -> '" <<
932
             path_tildexpand(ttvec[i]) << "'" << endl;
933
    }
934
#endif
935
936
#if 0
937
    const string canontst[] = {"/dir1/../../..", "/////", "",
938
                               "/dir1/../../.././/////dir2///////",
939
                               "../../",
940
                               "../../../../../../../../../../"
941
                              };
942
    unsigned int nttvec = sizeof(canontst) / sizeof(string);
943
    for (unsigned int i = 0; i < nttvec; i++) {
944
        cout << "canon: '" << canontst[i] << "' -> '" <<
945
             path_canon(canontst[i]) << "'" << endl;
946
    }
947
#endif
948
#if 0
949
    if (argc != 2) {
950
        cerr << "Usage: trpathut <dir> <pattern>" << endl;
951
        exit(1);
952
    }
953
    string dir = *argv++;
954
    argc--;
955
    string pattern =  *argv++;
956
    argc--;
957
    vector<string> matched = path_dirglob(dir, pattern);
958
    for (it = matched.begin(); it != matched.end(); it++) {
959
        cout << *it << endl;
960
    }
961
#endif
962
963
#if 0
964
    if (argc != 1) {
965
        fprintf(stderr, "Usage: fsocc: trpathut <path>\n");
966
        exit(1);
967
    }
968
    string path = *argv++;
969
    argc--;
970
971
    int pc;
972
    long long blocks;
973
    if (!fsocc(path, &pc, &blocks)) {
974
        fprintf(stderr, "fsocc failed\n");
975
        return 1;
976
    }
977
    printf("pc %d, megabytes %ld\n", pc, blocks);
978
#endif
979
980
#if 0
981
    Pidfile pidfile("/tmp/pathutpidfile");
982
    pid_t pid;
983
    if ((pid = pidfile.open()) != 0) {
984
        cerr << "open failed. reason: " << pidfile.getreason() <<
985
             " return " << pid << endl;
986
        exit(1);
987
    }
988
    pidfile.write_pid();
989
    sleep(10);
990
    pidfile.close();
991
    pidfile.remove();
992
#endif
993
994
#if 0
995
    if (argc > 1) {
996
        cerr <<  "Usage: thumbpath <filepath>" << endl;
997
        exit(1);
998
    }
999
    string input;
1000
    if (argc == 1) {
1001
        input = *argv++;
1002
        if (input.empty())  {
1003
            cerr << "Usage: thumbpath <filepath>" << endl;
1004
            exit(1);
1005
        }
1006
        path_to_thumb(input);
1007
    } else {
1008
        while (getline(cin, input)) {
1009
            path_to_thumb(input);
1010
        }
1011
    }
1012
1013
1014
    exit(0);
1015
#endif
1016
1017
#if 0
1018
    if (argc != 1) {
1019
        cerr << "Usage: trpathut <filename>" << endl;
1020
        exit(1);
1021
    }
1022
    string fn = *argv++;
1023
    argc--;
1024
    string ext = path_suffix(fn);
1025
    cout << "Suffix: [" << ext << "]" << endl;
1026
    return 0;
1027
#endif
1028
1029
#if 1
1030
    if (argc != 1) {
1031
        cerr << "Usage: trpathut url" << endl;
1032
        exit(1);
1033
    }
1034
    string url = *argv++;
1035
    argc--;
1036
1037
    cout << "File: [" << fileurltolocalpath(url) << "]\n";
1038
    return 0;
1039
#endif
1040
1041
1042
}
1043
1044
#endif // TEST_PATHUT
1045