Switch to unified view

a/src/utils/pathut.cpp b/src/utils/pathut.cpp
...
...
14
 *   Free Software Foundation, Inc.,
14
 *   Free Software Foundation, Inc.,
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16
 */
16
 */
17
17
18
#ifndef TEST_PATHUT
18
#ifndef TEST_PATHUT
19
#ifdef BUILDING_RECOLL
19
#include "autoconfig.h"
20
#include "autoconfig.h"
21
#else
22
#include "config.h"
23
#endif
20
24
21
#include <stdio.h>
25
#include <stdio.h>
26
#ifdef _WIN32
27
#include "dirent.h"
22
#include "safefcntl.h"
28
#include "safefcntl.h"
23
#include "safeunistd.h"
29
#include "safeunistd.h"
24
#include "dirent.h"
25
#include "cstr.h"
26
#ifdef _WIN32
27
#include "safewindows.h"
30
#include "safewindows.h"
31
#include "safesysstat.h"
28
#else
32
#else
33
#include <fcntl.h>
34
#include <unistd.h>
29
#include <sys/param.h>
35
#include <sys/param.h>
30
#include <pwd.h>
36
#include <pwd.h>
31
#include <sys/file.h>
37
#include <sys/file.h>
38
#include <sys/stat.h>
39
#include <dirent.h>
32
#endif
40
#endif
33
#include <math.h>
41
#include <math.h>
34
#include <errno.h>
42
#include <errno.h>
35
#include <sys/types.h>
43
#include <sys/types.h>
36
#include "safesysstat.h"
37
#include "ptmutex.h"
38
44
39
// Let's include all files where statfs can be defined and hope for no
45
// Let's include all files where statfs can be defined and hope for no
40
// conflict...
46
// conflict...
41
#ifdef HAVE_SYS_MOUNT_H 
47
#ifdef HAVE_SYS_MOUNT_H
42
#include <sys/mount.h>
48
#include <sys/mount.h>
43
#endif
49
#endif
44
#ifdef HAVE_SYS_STATFS_H 
50
#ifdef HAVE_SYS_STATFS_H
45
#include <sys/statfs.h>
51
#include <sys/statfs.h>
46
#endif
52
#endif
47
#ifdef HAVE_SYS_STATVFS_H 
53
#ifdef HAVE_SYS_STATVFS_H
48
#include <sys/statvfs.h>
54
#include <sys/statvfs.h>
49
#endif
55
#endif
50
#ifdef HAVE_SYS_VFS_H 
56
#ifdef HAVE_SYS_VFS_H
51
#include <sys/vfs.h>
57
#include <sys/vfs.h>
52
#endif
58
#endif
53
59
54
#include <cstdlib>
60
#include <cstdlib>
55
#include <cstring>
61
#include <cstring>
...
...
58
#include <stack>
64
#include <stack>
59
#include <set>
65
#include <set>
60
#include <vector>
66
#include <vector>
61
67
62
#include "pathut.h"
68
#include "pathut.h"
63
#include "transcode.h"
64
#include "wipedir.h"
65
#include "md5ut.h"
66
69
67
using namespace std;
70
using namespace std;
68
71
69
#ifdef _WIN32
72
#ifdef _WIN32
70
/// Convert \ separators to /
73
/// Convert \ separators to /
71
void path_slashize(string& s)
74
void path_slashize(string& s)
72
{
75
{
73
    for (string::size_type i = 0; i < s.size(); i++) {
76
    for (string::size_type i = 0; i < s.size(); i++) {
74
        if (s[i] == '\\')
77
        if (s[i] == '\\') {
75
            s[i] = '/';
78
            s[i] = '/';
79
        }
76
    }
80
    }
77
}
81
}
78
static bool path_strlookslikedrive(const string& s)
82
static bool path_strlookslikedrive(const string& s)
79
{
83
{
80
    return s.size() == 2 && isalpha(s[0]) && s[1] == ':';
84
    return s.size() == 2 && isalpha(s[0]) && s[1] == ':';
81
}
85
}
82
86
83
static bool path_hasdrive(const string& s)
87
static bool path_hasdrive(const string& s)
84
{
88
{
85
    if (s.size() >= 2 && isalpha(s[0]) && s[1] == ':')
89
    if (s.size() >= 2 && isalpha(s[0]) && s[1] == ':') {
86
        return true;
90
        return true;
91
    }
87
    return false;
92
    return false;
88
}
93
}
89
static bool path_isdriveabs(const string& s)
94
static bool path_isdriveabs(const string& s)
90
{
95
{
91
    if (s.size() >= 3 && isalpha(s[0]) && s[1] == ':' && s[2] == '/')
96
    if (s.size() >= 3 && isalpha(s[0]) && s[1] == ':' && s[2] == '/') {
92
        return true;
97
        return true;
98
    }
93
    return false;
99
    return false;
94
}
100
}
95
101
96
#include <Shlwapi.h>
102
#include <Shlwapi.h>
97
#pragma comment(lib, "shlwapi.lib")
103
#pragma comment(lib, "shlwapi.lib")
...
...
107
    // Windows API
113
    // Windows API
108
    std::vector<char> buffer;
114
    std::vector<char> buffer;
109
    int size = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL);
115
    int size = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL);
110
    if (size > 0) {
116
    if (size > 0) {
111
        buffer.resize(size);
117
        buffer.resize(size);
112
        WideCharToMultiByte(CP_UTF8, 0, text, -1, 
118
        WideCharToMultiByte(CP_UTF8, 0, text, -1,
113
                            &buffer[0], int(buffer.size()), NULL, NULL);
119
                            &buffer[0], int(buffer.size()), NULL, NULL);
114
    } else {
120
    } else {
115
        return string();
121
        return string();
116
    }
122
    }
117
    return string(&buffer[0]);
123
    return string(&buffer[0]);
...
...
128
    PathCchRemoveFileSpec(text, MAX_PATH);
134
    PathCchRemoveFileSpec(text, MAX_PATH);
129
#else
135
#else
130
    PathRemoveFileSpec(text);
136
    PathRemoveFileSpec(text);
131
#endif
137
#endif
132
    string path = path_tchartoutf8(text);
138
    string path = path_tchartoutf8(text);
133
    if (path.empty())
139
    if (path.empty()) {
134
        path = "c:/";
140
        path = "c:/";
141
    }
135
142
136
    return path;
143
    return path;
137
}
144
}
138
145
139
string path_wingettempfilename(TCHAR *pref)
146
string path_wingettempfilename(TCHAR *pref)
140
{
147
{
141
    TCHAR buf[(MAX_PATH +1)*sizeof(TCHAR)];
148
    TCHAR buf[(MAX_PATH + 1)*sizeof(TCHAR)];
142
    TCHAR dbuf[(MAX_PATH +1)*sizeof(TCHAR)];
149
    TCHAR dbuf[(MAX_PATH + 1)*sizeof(TCHAR)];
143
    GetTempPath(MAX_PATH+1, dbuf);
150
    GetTempPath(MAX_PATH + 1, dbuf);
144
    GetTempFileName(dbuf, pref, 0, buf);
151
    GetTempFileName(dbuf, pref, 0, buf);
145
    // Windows will have created a temp file, we delete it.
152
    // Windows will have created a temp file, we delete it.
146
    string filename = path_tchartoutf8(buf);
153
    string filename = path_tchartoutf8(buf);
147
    unlink(filename.c_str());
154
    unlink(filename.c_str());
148
    path_slashize(filename);
155
    path_slashize(filename);
149
    return filename;
156
    return filename;
150
}
157
}
151
#endif
158
#endif
152
159
153
160
#if defined(HAVE_SYS_MOUNT_H) || defined(HAVE_SYS_STATFS_H) || \
161
    defined(HAVE_SYS_STATVFS_H) || defined(HAVE_SYS_VFS_H)
154
bool fsocc(const string &path, int *pc, long long *avmbs)
162
bool fsocc(const string& path, int *pc, long long *avmbs)
155
{
163
{
156
    static const int FSOCC_MB = 1024*1024;
164
    static const int FSOCC_MB = 1024 * 1024;
157
#ifdef _WIN32
165
#ifdef _WIN32
158
    ULARGE_INTEGER freebytesavail;
166
    ULARGE_INTEGER freebytesavail;
159
    ULARGE_INTEGER totalbytes;
167
    ULARGE_INTEGER totalbytes;
160
    if (!GetDiskFreeSpaceEx(path.c_str(), &freebytesavail,
168
    if (!GetDiskFreeSpaceEx(path.c_str(), &freebytesavail,
161
                            &totalbytes, NULL)) {
169
                            &totalbytes, NULL)) {
162
        return false;
170
        return false;
163
    }
171
    }
164
    if (pc)
172
    if (pc) {
165
        *pc = int((100 * freebytesavail.QuadPart) / totalbytes.QuadPart);
173
        *pc = int((100 * freebytesavail.QuadPart) / totalbytes.QuadPart);
174
    }
166
    if (avmbs)
175
    if (avmbs) {
167
        *avmbs = int(totalbytes.QuadPart / FSOCC_MB);
176
        *avmbs = int(totalbytes.QuadPart / FSOCC_MB);
177
    }
168
    return true;
178
    return true;
169
#else
179
#else
170
#ifdef sun
180
#ifdef sun
171
    struct statvfs buf;
181
    struct statvfs buf;
172
    if (statvfs(path.c_str(), &buf) != 0) {
182
    if (statvfs(path.c_str(), &buf) != 0) {
173
  return false;
183
        return false;
174
    }
184
    }
175
#else
185
#else
176
    struct statfs buf;
186
    struct statfs buf;
177
    if (statfs(path.c_str(), &buf) != 0) {
187
    if (statfs(path.c_str(), &buf) != 0) {
178
  return false;
188
        return false;
179
    }
189
    }
180
#endif
190
#endif
181
191
182
    // used blocks
192
    // used blocks
183
    double fpc = 0.0;
193
    double fpc = 0.0;
184
#define FSOCC_USED (double(buf.f_blocks - buf.f_bfree))
194
#define FSOCC_USED (double(buf.f_blocks - buf.f_bfree))
185
#define FSOCC_TOTAVAIL (FSOCC_USED + double(buf.f_bavail))
195
#define FSOCC_TOTAVAIL (FSOCC_USED + double(buf.f_bavail))
186
    if (FSOCC_TOTAVAIL > 0) {
196
    if (FSOCC_TOTAVAIL > 0) {
187
  fpc = 100.0 * FSOCC_USED / FSOCC_TOTAVAIL;
197
        fpc = 100.0 * FSOCC_USED / FSOCC_TOTAVAIL;
188
    }
198
    }
189
    if (pc)
199
    if (pc) {
190
        *pc = int(fpc);
200
        *pc = int(fpc);
201
    }
191
    if (avmbs) {
202
    if (avmbs) {
192
  *avmbs = 0;
203
        *avmbs = 0;
193
  if (buf.f_bsize > 0) {
204
        if (buf.f_bsize > 0) {
194
      int ratio = buf.f_bsize > FSOCC_MB ? buf.f_bsize / FSOCC_MB :
205
            int ratio = buf.f_bsize > FSOCC_MB ? buf.f_bsize / FSOCC_MB :
195
      FSOCC_MB / buf.f_bsize;
206
                        FSOCC_MB / buf.f_bsize;
196
207
197
      *avmbs = buf.f_bsize > FSOCC_MB ? 
208
            *avmbs = buf.f_bsize > FSOCC_MB ?
198
                ((long long)buf.f_bavail) * ratio :
209
                     ((long long)buf.f_bavail) * ratio :
199
      ((long long)buf.f_bavail) / ratio;
210
                     ((long long)buf.f_bavail) / ratio;
200
  }
211
        }
201
    }
212
    }
202
    return true;
213
    return true;
203
#endif
214
#endif
204
}
215
}
205
216
#endif // we have found an appropriate include file
206
const string& tmplocation()
207
{
208
    static string stmpdir;
209
    if (stmpdir.empty()) {
210
        const char *tmpdir = getenv("RECOLL_TMPDIR");
211
        if (tmpdir == 0) 
212
            tmpdir = getenv("TMPDIR");
213
        if (tmpdir == 0) 
214
            tmpdir = getenv("TMP");
215
        if (tmpdir == 0) 
216
            tmpdir = getenv("TEMP");
217
        if (tmpdir == 0) {
218
#ifdef _WIN32
219
            TCHAR bufw[(MAX_PATH+1)*sizeof(TCHAR)];
220
            GetTempPath(MAX_PATH+1, bufw);
221
            stmpdir = path_tchartoutf8(bufw);
222
#else
223
            stmpdir = "/tmp";
224
#endif
225
        } else {
226
            stmpdir = tmpdir;
227
        }
228
        stmpdir = path_canon(stmpdir);
229
    }
230
231
    return stmpdir;
232
}
233
234
// Location for sample config, filters, etc. (e.g. /usr/share/recoll/)
235
const string& path_sharedatadir()
236
{
237
    static string datadir;
238
    if (datadir.empty()) {
239
#ifdef _WIN32
240
        datadir = path_cat(path_thisexecpath(), "Share");
241
#else
242
        const char *cdatadir = getenv("RECOLL_DATADIR");
243
        if (cdatadir == 0) {
244
            // If not in environment, use the compiled-in constant. 
245
            datadir = RECOLL_DATADIR;
246
        } else {
247
            datadir = cdatadir;
248
        }
249
#endif
250
    }
251
    return datadir;
252
}
253
217
254
string path_PATHsep()
218
string path_PATHsep()
255
{
219
{
256
    static const string w(";");
220
    static const string w(";");
257
    static const string u(":");
221
    static const string u(":");
...
...
260
#else
224
#else
261
    return u;
225
    return u;
262
#endif
226
#endif
263
}
227
}
264
228
265
bool maketmpdir(string& tdir, string& reason)
266
{
267
#ifndef _WIN32
268
    tdir = path_cat(tmplocation(), "rcltmpXXXXXX");
269
270
    char *cp = strdup(tdir.c_str());
271
    if (!cp) {
272
  reason = "maketmpdir: out of memory (for file name !)\n";
273
  tdir.erase();
274
  return false;
275
    }
276
277
    // There is a race condition between name computation and
278
    // mkdir. try to make sure that we at least don't shoot ourselves
279
    // in the foot
280
#if !defined(HAVE_MKDTEMP) || defined(_WIN32)
281
    static PTMutexInit mlock;
282
    PTMutexLocker lock(mlock);
283
#endif
284
285
    if (!
286
#ifdef HAVE_MKDTEMP
287
  mkdtemp(cp)
288
#else
289
  mktemp(cp)
290
#endif // HAVE_MKDTEMP
291
  ) {
292
  free(cp);
293
  reason = "maketmpdir: mktemp failed for [" + tdir + "] : " +
294
      strerror(errno);
295
  tdir.erase();
296
  return false;
297
    } 
298
    tdir = cp;
299
    free(cp);
300
#else // _WIN32
301
    // There is a race condition between name computation and
302
    // mkdir. try to make sure that we at least don't shoot ourselves
303
    // in the foot
304
    static PTMutexInit mlock;
305
    PTMutexLocker lock(mlock);
306
    tdir = path_wingettempfilename(TEXT("rcltmp"));
307
#endif
308
309
    // At this point the directory does not exist yet except if we used
310
    // mkdtemp
311
312
#if !defined(HAVE_MKDTEMP) || defined(_WIN32)
313
    if (mkdir(tdir.c_str(), 0700) < 0) {
314
  reason = string("maketmpdir: mkdir ") + tdir + " failed";
315
  tdir.erase();
316
  return false;
317
    }
318
#endif
319
320
    return true;
321
}
322
323
TempFileInternal::TempFileInternal(const string& suffix)
324
    : m_noremove(false)
325
{
326
    // Because we need a specific suffix, can't use mkstemp
327
    // well. There is a race condition between name computation and
328
    // file creation. try to make sure that we at least don't shoot
329
    // our own selves in the foot. maybe we'll use mkstemps one day.
330
    static PTMutexInit mlock;
331
    PTMutexLocker lock(mlock);
332
333
#ifndef _WIN32
334
    string filename = path_cat(tmplocation(), "rcltmpfXXXXXX");
335
    char *cp = strdup(filename.c_str());
336
    if (!cp) {
337
  m_reason = "Out of memory (for file name !)\n";
338
  return;
339
    }
340
341
    // Using mkstemp this way is awful (bot the suffix adding and
342
    // using mkstemp() instead of mktemp just to avoid the warnings)
343
    int fd;
344
    if ((fd = mkstemp(cp)) < 0) {
345
  free(cp);
346
  m_reason = "TempFileInternal: mkstemp failed\n";
347
  return;
348
    }
349
    close(fd);
350
    unlink(cp);
351
    filename = cp;
352
    free(cp);
353
#else
354
    string filename = path_wingettempfilename(TEXT("recoll"));
355
#endif
356
357
    m_filename = filename + suffix;
358
    if (close(open(m_filename.c_str(), O_CREAT|O_EXCL, 0600)) != 0) {
359
  m_reason = string("Could not open/create") + m_filename;
360
  m_filename.erase();
361
    }
362
}
363
364
TempFileInternal::~TempFileInternal()
365
{
366
    if (!m_filename.empty() && !m_noremove)
367
  unlink(m_filename.c_str());
368
}
369
370
TempDir::TempDir()
371
{
372
    if (!maketmpdir(m_dirname, m_reason)) {
373
  m_dirname.erase();
374
  return;
375
    }
376
}
377
378
TempDir::~TempDir()
379
{
380
    if (!m_dirname.empty()) {
381
  (void)wipedir(m_dirname, true, true);
382
  m_dirname.erase();
383
    }
384
}
385
386
bool TempDir::wipe()
387
{
388
    if (m_dirname.empty()) {
389
  m_reason = "TempDir::wipe: no directory !\n";
390
  return false;
391
    }
392
    if (wipedir(m_dirname, false, true)) {
393
  m_reason = "TempDir::wipe: wipedir failed\n";
394
  return false;
395
    }
396
    return true;
397
}
398
399
void path_catslash(string &s)
229
void path_catslash(string& s)
400
{
230
{
401
#ifdef _WIN32
231
#ifdef _WIN32
402
    path_slashize(s);
232
    path_slashize(s);
403
#endif
233
#endif
404
    if (s.empty() || s[s.length() - 1] != '/')
234
    if (s.empty() || s[s.length() - 1] != '/') {
405
  s += '/';
235
        s += '/';
236
    }
406
}
237
}
407
238
408
string path_cat(const string &s1, const string &s2)
239
string path_cat(const string& s1, const string& s2)
409
{
240
{
410
    string res = s1;
241
    string res = s1;
411
    path_catslash(res);
242
    path_catslash(res);
412
    res +=  s2;
243
    res +=  s2;
413
    return res;
244
    return res;
414
}
245
}
415
246
416
string path_getfather(const string &s)
247
string path_getfather(const string& s)
417
{
248
{
418
    string father = s;
249
    string father = s;
419
#ifdef _WIN32
250
#ifdef _WIN32
420
    path_slashize(father);
251
    path_slashize(father);
421
#endif
252
#endif
422
253
423
    // ??
254
    // ??
424
    if (father.empty())
255
    if (father.empty()) {
425
  return "./";
256
        return "./";
257
    }
426
258
427
    if (path_isroot(father))
259
    if (path_isroot(father)) {
428
        return father;
260
        return father;
429
    
261
    }
262
430
    if (father[father.length() - 1] == '/') {
263
    if (father[father.length() - 1] == '/') {
431
  // Input ends with /. Strip it, root special case was tested above
264
        // Input ends with /. Strip it, root special case was tested above
432
  father.erase(father.length()-1);
265
        father.erase(father.length() - 1);
433
    }
266
    }
434
267
435
    string::size_type slp = father.rfind('/');
268
    string::size_type slp = father.rfind('/');
436
    if (slp == string::npos)
269
    if (slp == string::npos) {
437
  return "./";
270
        return "./";
271
    }
438
272
439
    father.erase(slp);
273
    father.erase(slp);
440
    path_catslash(father);
274
    path_catslash(father);
441
    return father;
275
    return father;
442
}
276
}
443
277
444
string path_getsimple(const string &s)
278
string path_getsimple(const string& s)
445
{
279
{
446
    string simple = s;
280
    string simple = s;
447
#ifdef _WIN32
281
#ifdef _WIN32
448
    path_slashize(simple);
282
    path_slashize(simple);
449
#endif
283
#endif
450
284
451
    if (simple.empty())
285
    if (simple.empty()) {
452
  return simple;
286
        return simple;
287
    }
453
288
454
    string::size_type slp = simple.rfind('/');
289
    string::size_type slp = simple.rfind('/');
455
    if (slp == string::npos)
290
    if (slp == string::npos) {
456
  return simple;
291
        return simple;
292
    }
457
293
458
    simple.erase(0, slp+1);
294
    simple.erase(0, slp + 1);
459
    return simple;
295
    return simple;
460
}
296
}
461
297
462
string path_basename(const string &s, const string &suff)
298
string path_basename(const string& s, const string& suff)
463
{
299
{
464
    string simple = path_getsimple(s);
300
    string simple = path_getsimple(s);
465
    string::size_type pos = string::npos;
301
    string::size_type pos = string::npos;
466
    if (suff.length() && simple.length() > suff.length()) {
302
    if (suff.length() && simple.length() > suff.length()) {
467
  pos = simple.rfind(suff);
303
        pos = simple.rfind(suff);
468
  if (pos != string::npos && pos + suff.length() == simple.length())
304
        if (pos != string::npos && pos + suff.length() == simple.length()) {
469
      return simple.substr(0, pos);
305
            return simple.substr(0, pos);
306
        }
470
    } 
307
    }
471
    return simple;
308
    return simple;
472
}
309
}
473
310
474
string path_suffix(const string& s)
311
string path_suffix(const string& s)
475
{
312
{
476
    string::size_type dotp = s.rfind('.');
313
    string::size_type dotp = s.rfind('.');
477
    if (dotp == string::npos)
314
    if (dotp == string::npos) {
478
  return string();
315
        return string();
316
    }
479
    return s.substr(dotp+1);
317
    return s.substr(dotp + 1);
480
}
318
}
481
319
482
string path_home()
320
string path_home()
483
{
321
{
484
#ifdef _WIN32
322
#ifdef _WIN32
...
...
505
#else
343
#else
506
    uid_t uid = getuid();
344
    uid_t uid = getuid();
507
345
508
    struct passwd *entry = getpwuid(uid);
346
    struct passwd *entry = getpwuid(uid);
509
    if (entry == 0) {
347
    if (entry == 0) {
510
  const char *cp = getenv("HOME");
348
        const char *cp = getenv("HOME");
511
  if (cp)
349
        if (cp) {
512
      return cp;
350
            return cp;
513
  else 
351
        } else {
514
  return "/";
352
            return "/";
353
        }
515
    }
354
    }
516
355
517
    string homedir = entry->pw_dir;
356
    string homedir = entry->pw_dir;
518
    path_catslash(homedir);
357
    path_catslash(homedir);
519
    return homedir;
358
    return homedir;
...
...
537
    // We should use an xdg-conforming location, but, history...
376
    // We should use an xdg-conforming location, but, history...
538
    return path_home();
377
    return path_home();
539
#endif
378
#endif
540
}
379
}
541
380
542
string path_tildexpand(const string &s) 
381
string path_tildexpand(const string& s)
543
{
382
{
544
    if (s.empty() || s[0] != '~')
383
    if (s.empty() || s[0] != '~') {
545
  return s;
384
        return s;
385
    }
546
    string o = s;
386
    string o = s;
547
#ifdef _WIN32
387
#ifdef _WIN32
548
    path_slashize(o);
388
    path_slashize(o);
549
#endif
389
#endif
550
    
390
551
    if (s.length() == 1) {
391
    if (s.length() == 1) {
552
  o.replace(0, 1, path_home());
392
        o.replace(0, 1, path_home());
553
    } else if  (s[1] == '/') {
393
    } else if (s[1] == '/') {
554
  o.replace(0, 2, path_home());
394
        o.replace(0, 2, path_home());
555
    } else {
395
    } else {
556
  string::size_type pos = s.find('/');
396
        string::size_type pos = s.find('/');
557
        string::size_type l = (pos == string::npos) ? s.length() - 1 : pos - 1;
397
        string::size_type l = (pos == string::npos) ? s.length() - 1 : pos - 1;
558
#ifdef _WIN32
398
#ifdef _WIN32
559
        // Dont know what this means. Just replace with HOME
399
        // Dont know what this means. Just replace with HOME
560
        o.replace(0, l+1, path_home());
400
        o.replace(0, l + 1, path_home());
561
#else
401
#else
562
  struct passwd *entry = getpwnam(s.substr(1, l).c_str());
402
        struct passwd *entry = getpwnam(s.substr(1, l).c_str());
563
  if (entry)
403
        if (entry) {
564
      o.replace(0, l+1, entry->pw_dir);
404
            o.replace(0, l + 1, entry->pw_dir);
405
        }
565
#endif
406
#endif
566
    }
407
    }
567
    return o;
408
    return o;
568
}
409
}
569
410
570
bool path_isroot(const string& path)
411
bool path_isroot(const string& path)
571
{
412
{
572
    if (path.size() == 1 && path[0] == '/')
413
    if (path.size() == 1 && path[0] == '/') {
573
        return true;
414
        return true;
415
    }
574
#ifdef _WIN32
416
#ifdef _WIN32
575
    if (path.size() == 3 && isalpha(path[0]) && path[1] == ':' &&
417
    if (path.size() == 3 && isalpha(path[0]) && path[1] == ':' &&
576
        (path[2] == '/' || path[2] == '\\'))
418
            (path[2] == '/' || path[2] == '\\')) {
577
        return true;
419
        return true;
420
    }
578
#endif
421
#endif
579
    return false;
422
    return false;
580
}
423
}
581
424
582
bool path_isabsolute(const string &path)
425
bool path_isabsolute(const string& path)
583
{
426
{
584
    if (!path.empty() && (path[0] == '/'
427
    if (!path.empty() && (path[0] == '/'
585
#ifdef _WIN32
428
#ifdef _WIN32
586
                          || path_isdriveabs(path)
429
                          || path_isdriveabs(path)
587
#endif
430
#endif
588
            )) {
431
                         )) {
589
        return true;
432
        return true;
590
    } 
433
    }
591
    return false;
434
    return false;
592
}
435
}
593
    
436
594
string path_absolute(const string &is)
437
string path_absolute(const string& is)
595
{
438
{
596
    if (is.length() == 0)
439
    if (is.length() == 0) {
597
  return is;
440
        return is;
441
    }
598
    string s = is;
442
    string s = is;
599
    if (!path_isabsolute(s)) {
443
    if (!path_isabsolute(s)) {
600
  char buf[MAXPATHLEN];
444
        char buf[MAXPATHLEN];
601
  if (!getcwd(buf, MAXPATHLEN)) {
445
        if (!getcwd(buf, MAXPATHLEN)) {
602
      return string();
446
            return string();
603
  }
447
        }
604
  s = path_cat(string(buf), s);
448
        s = path_cat(string(buf), s);
605
#ifdef _WIN32
449
#ifdef _WIN32
606
        path_slashize(s);
450
        path_slashize(s);
607
#endif
451
#endif
608
    }
452
    }
609
    return s;
453
    return s;
610
}
454
}
611
455
612
#include <smallut.h>
456
#include <smallut.h>
613
string path_canon(const string &is, const string* cwd)
457
string path_canon(const string& is, const string* cwd)
614
{
458
{
615
    if (is.length() == 0)
459
    if (is.length() == 0) {
616
  return is;
460
        return is;
461
    }
617
    string s = is;
462
    string s = is;
618
#ifdef _WIN32
463
#ifdef _WIN32
619
    path_slashize(s);
464
    path_slashize(s);
620
    // fix possible path from file: absolute url
465
    // fix possible path from file: absolute url
621
    if (s.size() && s[0] == '/' && path_hasdrive(s.substr(1))) {
466
    if (s.size() && s[0] == '/' && path_hasdrive(s.substr(1))) {
622
        s = s.substr(1);
467
        s = s.substr(1);
623
    }
468
    }
624
#endif
469
#endif
625
470
626
    if (!path_isabsolute(s)) {
471
    if (!path_isabsolute(s)) {
627
  char buf[MAXPATHLEN];
472
        char buf[MAXPATHLEN];
628
  const char *cwdp = buf;
473
        const char *cwdp = buf;
629
  if (cwd) {
474
        if (cwd) {
630
      cwdp = cwd->c_str();
475
            cwdp = cwd->c_str();
631
  } else {
476
        } else {
632
      if (!getcwd(buf, MAXPATHLEN)) {
477
            if (!getcwd(buf, MAXPATHLEN)) {
633
      return string();
478
                return string();
634
      }
479
            }
635
  }
480
        }
636
  s = path_cat(string(cwdp), s); 
481
        s = path_cat(string(cwdp), s);
637
    }
482
    }
638
    vector<string> elems;
483
    vector<string> elems;
639
    stringToTokens(s, elems, "/");
484
    stringToTokens(s, elems, "/");
640
    vector<string> cleaned;
485
    vector<string> cleaned;
641
    for (vector<string>::const_iterator it = elems.begin(); 
486
    for (vector<string>::const_iterator it = elems.begin();
642
   it != elems.end(); it++){
487
            it != elems.end(); it++) {
643
  if (*it == "..") {
488
        if (*it == "..") {
644
      if (!cleaned.empty())
489
            if (!cleaned.empty()) {
645
      cleaned.pop_back();
490
                cleaned.pop_back();
491
            }
646
  } else if (it->empty() || *it == ".") {
492
        } else if (it->empty() || *it == ".") {
647
  } else {
493
        } else {
648
      cleaned.push_back(*it);
494
            cleaned.push_back(*it);
649
  }
495
        }
650
    }
496
    }
651
    string ret;
497
    string ret;
652
    if (!cleaned.empty()) {
498
    if (!cleaned.empty()) {
653
  for (vector<string>::const_iterator it = cleaned.begin(); 
499
        for (vector<string>::const_iterator it = cleaned.begin();
654
       it != cleaned.end(); it++) {
500
                it != cleaned.end(); it++) {
655
            ret += "/";
501
            ret += "/";
656
#ifdef _WIN32
502
#ifdef _WIN32
657
            if (it == cleaned.begin() && path_strlookslikedrive(*it)) {
503
            if (it == cleaned.begin() && path_strlookslikedrive(*it)) {
658
                // Get rid of just added initial "/"
504
                // Get rid of just added initial "/"
659
                ret.clear();
505
                ret.clear();
660
            }
506
            }
661
#endif
507
#endif
662
      ret += *it;
508
            ret += *it;
663
  }
509
        }
664
    } else {
510
    } else {
665
  ret = "/";
511
        ret = "/";
666
    }
512
    }
667
    return ret;
513
    return ret;
668
}
514
}
669
515
670
bool makepath(const string& ipath)
516
bool makepath(const string& ipath)
671
{
517
{
672
    string path = path_canon(ipath);
518
    string path = path_canon(ipath);
673
    vector<string> elems;
519
    vector<string> elems;
674
    stringToTokens(path, elems, "/");
520
    stringToTokens(path, elems, "/");
675
    path = "/";
521
    path = "/";
676
    for (vector<string>::const_iterator it = elems.begin(); 
522
    for (vector<string>::const_iterator it = elems.begin();
677
   it != elems.end(); it++){
523
            it != elems.end(); it++) {
678
#ifdef _WIN32
524
#ifdef _WIN32
679
        if (it == elems.begin() && path_strlookslikedrive(*it))
525
        if (it == elems.begin() && path_strlookslikedrive(*it)) {
680
            path = "";
526
            path = "";
527
        }
681
#endif
528
#endif
682
  path += *it;
529
        path += *it;
683
  // Not using path_isdir() here, because this cant grok symlinks
530
        // Not using path_isdir() here, because this cant grok symlinks
684
  // If we hit an existing file, no worry, mkdir will just fail.
531
        // If we hit an existing file, no worry, mkdir will just fail.
685
  if (access(path.c_str(), 0) != 0) {
532
        if (access(path.c_str(), 0) != 0) {
686
      if (mkdir(path.c_str(), 0700) != 0)  {
533
            if (mkdir(path.c_str(), 0700) != 0)  {
687
      return false;
534
                return false;
688
      }
535
            }
689
  }
536
        }
690
  path += "/";
537
        path += "/";
691
    }
538
    }
692
    return true;
539
    return true;
693
}
540
}
694
541
695
bool path_isdir(const string& path)
542
bool path_isdir(const string& path)
696
{
543
{
697
    struct stat st;
544
    struct stat st;
698
    if (lstat(path.c_str(), &st) < 0) 
545
    if (lstat(path.c_str(), &st) < 0) {
699
  return false;
546
        return false;
547
    }
700
    if (S_ISDIR(st.st_mode))
548
    if (S_ISDIR(st.st_mode)) {
701
  return true;
549
        return true;
550
    }
702
    return false;
551
    return false;
703
}
552
}
704
553
705
long long path_filesize(const string& path)
554
long long path_filesize(const string& path)
706
{
555
{
707
    struct stat st;
556
    struct stat st;
708
    if (stat(path.c_str(), &st) < 0) 
557
    if (stat(path.c_str(), &st) < 0) {
709
  return -1;
558
        return -1;
559
    }
710
    return (long long)st.st_size;
560
    return (long long)st.st_size;
711
}
561
}
712
562
713
int path_fileprops(const std::string path, struct stat *stp, bool follow)
563
int path_fileprops(const std::string path, struct stat *stp, bool follow)
714
{
564
{
715
    if (!stp)
565
    if (!stp) {
716
        return -1;
566
        return -1;
567
    }
717
    memset(stp, 0, sizeof(struct stat));
568
    memset(stp, 0, sizeof(struct stat));
718
    struct stat mst;
569
    struct stat mst;
719
    int ret = follow ? stat(path.c_str(), &mst) : lstat(path.c_str(), &mst);
570
    int ret = follow ? stat(path.c_str(), &mst) : lstat(path.c_str(), &mst);
720
    if (ret != 0)
571
    if (ret != 0) {
721
        return ret;
572
        return ret;
573
    }
722
    stp->st_size = mst.st_size;
574
    stp->st_size = mst.st_size;
723
    stp->st_mode = mst.st_mode;
575
    stp->st_mode = mst.st_mode;
724
    stp->st_mtime = mst.st_mtime;
576
    stp->st_mtime = mst.st_mtime;
725
#ifdef _WIN32
577
#ifdef _WIN32
726
    stp->st_ctime = mst.st_mtime;
578
    stp->st_ctime = mst.st_mtime;
...
...
749
27 '
601
27 '
750
28 (
602
28 (
751
29 )
603
29 )
752
2A *
604
2A *
753
2B +
605
2B +
754
2C , 
606
2C ,
755
2D -
607
2D -
756
2E .
608
2E .
757
2F /
609
2F /
758
30 0
610
30 0
759
...
611
...
...
...
786
string url_encode(const string& url, string::size_type offs)
638
string url_encode(const string& url, string::size_type offs)
787
{
639
{
788
    string out = url.substr(0, offs);
640
    string out = url.substr(0, offs);
789
    const char *cp = url.c_str();
641
    const char *cp = url.c_str();
790
    for (string::size_type i = offs; i < url.size(); i++) {
642
    for (string::size_type i = offs; i < url.size(); i++) {
791
  unsigned int c;
643
        unsigned int c;
792
  const char *h = "0123456789ABCDEF";
644
        const char *h = "0123456789ABCDEF";
793
  c = cp[i];
645
        c = cp[i];
794
  if (c <= 0x20 || 
646
        if (c <= 0x20 ||
795
     c >= 0x7f || 
647
                c >= 0x7f ||
796
     c == '"' ||
648
                c == '"' ||
797
     c == '#' ||
649
                c == '#' ||
798
     c == '%' ||
650
                c == '%' ||
799
     c == ';' ||
651
                c == ';' ||
800
     c == '<' ||
652
                c == '<' ||
801
     c == '>' ||
653
                c == '>' ||
802
     c == '?' ||
654
                c == '?' ||
803
     c == '[' ||
655
                c == '[' ||
804
     c == '\\' ||
656
                c == '\\' ||
805
     c == ']' ||
657
                c == ']' ||
806
     c == '^' ||
658
                c == '^' ||
807
     c == '`' ||
659
                c == '`' ||
808
     c == '{' ||
660
                c == '{' ||
809
     c == '|' ||
661
                c == '|' ||
810
     c == '}' ) {
662
                c == '}') {
811
      out += '%';
663
            out += '%';
812
      out += h[(c >> 4) & 0xf];
664
            out += h[(c >> 4) & 0xf];
813
      out += h[c & 0xf];
665
            out += h[c & 0xf];
814
  } else {
666
        } else {
815
      out += char(c);
667
            out += char(c);
816
  }
668
        }
817
    }
669
    }
818
    return out;
670
    return out;
819
}
671
}
820
672
821
string url_gpath(const string& url)
673
string url_gpath(const string& url)
822
{
674
{
823
    // Remove the access schema part (or whatever it's called)
675
    // Remove the access schema part (or whatever it's called)
824
    string::size_type colon = url.find_first_of(":");
676
    string::size_type colon = url.find_first_of(":");
825
    if (colon == string::npos || colon == url.size() - 1)
677
    if (colon == string::npos || colon == url.size() - 1) {
826
        return url;
678
        return url;
679
    }
827
    // If there are non-alphanum chars before the ':', then there
680
    // If there are non-alphanum chars before the ':', then there
828
    // probably is no scheme. Whatever...
681
    // probably is no scheme. Whatever...
829
    for (string::size_type i = 0; i < colon; i++) {
682
    for (string::size_type i = 0; i < colon; i++) {
830
        if (!isalnum(url.at(i)))
683
        if (!isalnum(url.at(i))) {
831
            return url;
684
            return url;
685
        }
832
    }
686
    }
833
687
834
    // In addition we canonize the path to remove empty host parts
688
    // In addition we canonize the path to remove empty host parts
835
    // (for compatibility with older versions of recoll where file://
689
    // (for compatibility with older versions of recoll where file://
836
    // was hardcoded, but the local path was used for doc
690
    // was hardcoded, but the local path was used for doc
837
    // identification.
691
    // identification.
838
    return path_canon(url.substr(colon+1));
692
    return path_canon(url.substr(colon + 1));
839
}
693
}
840
841
string url_gpathS(const string& url)
842
{
843
#ifdef _WIN32
844
    string u = url_gpath(url);
845
    string nu;
846
    if (path_hasdrive(u)) {
847
        nu.append(1, '/');
848
        nu.append(1, u[0]);
849
        if (path_isdriveabs(u)) {
850
            nu.append(u.substr(2));
851
        } else {
852
            // This should be an error really
853
            nu.append(1, '/');
854
            nu.append(u.substr(2));
855
        }
856
    }
857
    return nu;
858
#else
859
    return url_gpath(url);
860
#endif
861
}
862
863
694
864
string url_parentfolder(const string& url)
695
string url_parentfolder(const string& url)
865
{
696
{
866
    // In general, the parent is the directory above the full path
697
    // In general, the parent is the directory above the full path
867
    string parenturl = path_getfather(url_gpath(url));
698
    string parenturl = path_getfather(url_gpath(url));
...
...
870
    bool isfileurl = urlisfileurl(url);
701
    bool isfileurl = urlisfileurl(url);
871
    if (!isfileurl && parenturl == "/") {
702
    if (!isfileurl && parenturl == "/") {
872
        parenturl = url_gpath(url);
703
        parenturl = url_gpath(url);
873
    }
704
    }
874
    return isfileurl ? string("file://") + parenturl :
705
    return isfileurl ? string("file://") + parenturl :
875
        string("http://") + parenturl;
706
           string("http://") + parenturl;
876
}
707
}
877
708
878
879
string path_defaultrecollconfsubdir()
880
{
881
#ifdef _WIN32
882
    return "Recoll";
883
#else
884
    return ".recoll";
885
#endif
886
}
887
709
888
// Convert to file path if url is like file:
710
// Convert to file path if url is like file:
889
// Note: this only works with our internal pseudo-urls which are not
711
// Note: this only works with our internal pseudo-urls which are not
890
// encoded/escaped
712
// encoded/escaped
891
string fileurltolocalpath(string url)
713
string fileurltolocalpath(string url)
892
{
714
{
893
    if (url.find("file://") == 0)
715
    if (url.find("file://") == 0) {
894
        url = url.substr(7, string::npos);
716
        url = url.substr(7, string::npos);
895
    else
717
    } else {
896
        return string();
718
        return string();
719
    }
897
720
898
#ifdef _WIN32
721
#ifdef _WIN32
899
    // Absolute file urls are like: file:///c:/mydir/...
722
    // Absolute file urls are like: file:///c:/mydir/...
900
    // Get rid of the initial '/'
723
    // Get rid of the initial '/'
901
    if (url.size() >= 3 && url[0] == '/' && isalpha(url[1]) && url[2] == ':') {
724
    if (url.size() >= 3 && url[0] == '/' && isalpha(url[1]) && url[2] == ':') {
...
...
906
    // Removing the fragment part. This is exclusively used when
729
    // Removing the fragment part. This is exclusively used when
907
    // executing a viewer for the recoll manual, and we only strip the
730
    // executing a viewer for the recoll manual, and we only strip the
908
    // part after # if it is preceded by .html
731
    // part after # if it is preceded by .html
909
    string::size_type pos;
732
    string::size_type pos;
910
    if ((pos = url.rfind(".html#")) != string::npos) {
733
    if ((pos = url.rfind(".html#")) != string::npos) {
911
        url.erase(pos+5);
734
        url.erase(pos + 5);
912
    } else if ((pos = url.rfind(".htm#")) != string::npos) {
735
    } else if ((pos = url.rfind(".htm#")) != string::npos) {
913
        url.erase(pos+4);
736
        url.erase(pos + 4);
914
    }
737
    }
915
738
916
    return url;
739
    return url;
917
}
740
}
918
741
742
static const string cstr_fileu("file://");
743
919
string path_pathtofileurl(const string& path)
744
string path_pathtofileurl(const string& path)
920
{
745
{
921
  // We're supposed to receive a canonic absolute path, but on windows we
746
    // We're supposed to receive a canonic absolute path, but on windows we
922
  // may need to add a '/' in front of the drive spec
747
    // may need to add a '/' in front of the drive spec
923
  string url(cstr_fileu);
748
    string url(cstr_fileu);
924
  if (path.empty() || path[0] != '/')
749
    if (path.empty() || path[0] != '/') {
925
      url.push_back('/');
750
        url.push_back('/');
751
    }
926
  url += path;
752
    url += path;
927
  return url;
753
    return url;
928
}
754
}
929
755
930
bool urlisfileurl(const string& url)
756
bool urlisfileurl(const string& url)
931
{
757
{
932
    return url.find("file://") == 0;
758
    return url.find("file://") == 0;
933
}
934
935
// Printable url: this is used to transcode from the system charset
936
// into either utf-8 if transcoding succeeds, or url-encoded
937
bool printableUrl(const string &fcharset, const string &in, string &out)
938
{
939
    int ecnt = 0;
940
    if (!transcode(in, out, fcharset, "UTF-8", &ecnt) || ecnt) {
941
  out = url_encode(in, 7);
942
    }
943
    return true;
944
}
759
}
945
760
946
bool readdir(const string& dir, string& reason, set<string>& entries)
761
bool readdir(const string& dir, string& reason, set<string>& entries)
947
{
762
{
948
    struct stat st;
763
    struct stat st;
949
    int statret;
764
    int statret;
950
    ostringstream msg;
765
    ostringstream msg;
951
    DIR *d = 0;
766
    DIR *d = 0;
952
    statret = lstat(dir.c_str(), &st);
767
    statret = lstat(dir.c_str(), &st);
953
    if (statret == -1) {
768
    if (statret == -1) {
954
  msg << "readdir: cant stat " << dir << " errno " <<  errno;
769
        msg << "readdir: cant stat " << dir << " errno " <<  errno;
955
  goto out;
770
        goto out;
956
    }
771
    }
957
    if (!S_ISDIR(st.st_mode)) {
772
    if (!S_ISDIR(st.st_mode)) {
958
  msg << "readdir: " << dir <<  " not a directory";
773
        msg << "readdir: " << dir <<  " not a directory";
959
  goto out;
774
        goto out;
960
    }
775
    }
961
    if (access(dir.c_str(), R_OK) < 0) {
776
    if (access(dir.c_str(), R_OK) < 0) {
962
  msg << "readdir: no read access to " << dir;
777
        msg << "readdir: no read access to " << dir;
963
  goto out;
778
        goto out;
964
    }
779
    }
965
780
966
    d = opendir(dir.c_str());
781
    d = opendir(dir.c_str());
967
    if (d == 0) {
782
    if (d == 0) {
968
  msg << "readdir: cant opendir " << dir << ", errno " << errno;
783
        msg << "readdir: cant opendir " << dir << ", errno " << errno;
969
  goto out;
784
        goto out;
970
    }
785
    }
971
786
972
    struct dirent *ent;
787
    struct dirent *ent;
973
    while ((ent = readdir(d)) != 0) {
788
    while ((ent = readdir(d)) != 0) {
974
  if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) 
789
        if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
975
      continue;
790
            continue;
791
        }
976
  entries.insert(ent->d_name);
792
        entries.insert(ent->d_name);
977
    }
793
    }
978
794
979
out:
795
out:
980
    if (d)
796
    if (d) {
981
  closedir(d);
797
        closedir(d);
798
    }
982
    reason = msg.str();
799
    reason = msg.str();
983
    if (reason.empty())
800
    if (reason.empty()) {
984
  return true;
801
        return true;
802
    }
985
    return false;
803
    return false;
986
}
804
}
987
805
988
// We do not want to mess with the pidfile content in the destructor:
806
// We do not want to mess with the pidfile content in the destructor:
989
// the lock might still be in use in a child process. In fact as much
807
// the lock might still be in use in a child process. In fact as much
990
// as we'd like to reset the pid inside the file when we're done, it
808
// as we'd like to reset the pid inside the file when we're done, it
991
// would be very difficult to do it right and it's probably best left
809
// would be very difficult to do it right and it's probably best left
992
// alone.
810
// alone.
993
Pidfile::~Pidfile()
811
Pidfile::~Pidfile()
994
{
812
{
995
    if (m_fd >= 0)
813
    if (m_fd >= 0) {
996
  ::close(m_fd);
814
        ::close(m_fd);
815
    }
997
    m_fd = -1;
816
    m_fd = -1;
998
}
817
}
999
818
1000
pid_t Pidfile::read_pid()
819
pid_t Pidfile::read_pid()
1001
{
820
{
1002
    int fd = ::open(m_path.c_str(), O_RDONLY);
821
    int fd = ::open(m_path.c_str(), O_RDONLY);
1003
    if (fd == -1)
822
    if (fd == -1) {
1004
  return (pid_t)-1;
823
        return (pid_t) - 1;
824
    }
1005
825
1006
    char buf[16];
826
    char buf[16];
1007
    int i = read(fd, buf, sizeof(buf) - 1);
827
    int i = read(fd, buf, sizeof(buf) - 1);
1008
    ::close(fd);
828
    ::close(fd);
1009
    if (i <= 0)
829
    if (i <= 0) {
1010
  return (pid_t)-1;
830
        return (pid_t) - 1;
831
    }
1011
    buf[i] = '\0';
832
    buf[i] = '\0';
1012
    char *endptr;
833
    char *endptr;
1013
    pid_t pid = strtol(buf, &endptr, 10);
834
    pid_t pid = strtol(buf, &endptr, 10);
1014
    if (endptr != &buf[i])
835
    if (endptr != &buf[i]) {
1015
  return (pid_t)-1;
836
        return (pid_t) - 1;
837
    }
1016
    return pid;
838
    return pid;
1017
}
839
}
1018
840
1019
int Pidfile::flopen()
841
int Pidfile::flopen()
1020
{
842
{
1021
    const char *path = m_path.c_str();
843
    const char *path = m_path.c_str();
1022
    if ((m_fd = ::open(path, O_RDWR|O_CREAT, 0644)) == -1) {
844
    if ((m_fd = ::open(path, O_RDWR | O_CREAT, 0644)) == -1) {
1023
  m_reason = "Open failed: [" + m_path + "]: " + strerror(errno);
845
        m_reason = "Open failed: [" + m_path + "]: " + strerror(errno);
1024
  return -1;
846
        return -1;
1025
    }
847
    }
1026
848
1027
#ifdef sun
849
#ifdef sun
1028
    struct flock lockdata;
850
    struct flock lockdata;
1029
    lockdata.l_start = 0;
851
    lockdata.l_start = 0;
1030
    lockdata.l_len = 0;
852
    lockdata.l_len = 0;
1031
    lockdata.l_type = F_WRLCK;
853
    lockdata.l_type = F_WRLCK;
1032
    lockdata.l_whence = SEEK_SET;
854
    lockdata.l_whence = SEEK_SET;
1033
    if (fcntl(m_fd, F_SETLK,  &lockdata) != 0) {
855
    if (fcntl(m_fd, F_SETLK,  &lockdata) != 0) {
1034
  int serrno = errno;
856
        int serrno = errno;
1035
  (void)::close(m_fd);
857
        (void)::close(m_fd);
1036
  errno = serrno;
858
        errno = serrno;
1037
  m_reason = "fcntl lock failed";
859
        m_reason = "fcntl lock failed";
1038
  return -1;
860
        return -1;
1039
    }
861
    }
1040
#else
862
#else
1041
#ifdef _WIN32
863
#ifdef _WIN32
1042
  return 0;
864
    return 0;
1043
#else
865
#else
1044
    int operation = LOCK_EX | LOCK_NB;
866
    int operation = LOCK_EX | LOCK_NB;
1045
    if (flock(m_fd, operation) == -1) {
867
    if (flock(m_fd, operation) == -1) {
1046
  int serrno = errno;
868
        int serrno = errno;
1047
  (void)::close(m_fd);
869
        (void)::close(m_fd);
1048
  errno = serrno;
870
        errno = serrno;
1049
  m_reason = "flock failed";
871
        m_reason = "flock failed";
1050
  return -1;
872
        return -1;
1051
    }
873
    }
1052
#endif // ! win32
874
#endif // ! win32
1053
#endif // ! sun
875
#endif // ! sun
1054
876
1055
    if (ftruncate(m_fd, 0) != 0) {
877
    if (ftruncate(m_fd, 0) != 0) {
1056
  /* can't happen [tm] */
878
        /* can't happen [tm] */
1057
  int serrno = errno;
879
        int serrno = errno;
1058
  (void)::close(m_fd);
880
        (void)::close(m_fd);
1059
  errno = serrno;
881
        errno = serrno;
1060
  m_reason = "ftruncate failed";
882
        m_reason = "ftruncate failed";
1061
  return -1;
883
        return -1;
1062
    }
884
    }
1063
    return 0;
885
    return 0;
1064
}
886
}
1065
887
1066
pid_t Pidfile::open()
888
pid_t Pidfile::open()
1067
{
889
{
1068
    if (flopen() < 0) {
890
    if (flopen() < 0) {
1069
  return read_pid();
891
        return read_pid();
1070
    }
892
    }
1071
    return (pid_t)0;
893
    return (pid_t)0;
1072
}
894
}
1073
895
1074
int Pidfile::write_pid()
896
int Pidfile::write_pid()
1075
{
897
{
1076
    /* truncate to allow multiple calls */
898
    /* truncate to allow multiple calls */
1077
    if (ftruncate(m_fd, 0) == -1) {
899
    if (ftruncate(m_fd, 0) == -1) {
1078
  m_reason = "ftruncate failed";
900
        m_reason = "ftruncate failed";
1079
  return -1;
901
        return -1;
1080
    }
902
    }
1081
    char pidstr[20];
903
    char pidstr[20];
1082
    sprintf(pidstr, "%u", int(getpid()));
904
    sprintf(pidstr, "%u", int(getpid()));
1083
    lseek(m_fd, 0, 0);
905
    lseek(m_fd, 0, 0);
1084
    if (::write(m_fd, pidstr, strlen(pidstr)) != (ssize_t)strlen(pidstr)) {
906
    if (::write(m_fd, pidstr, strlen(pidstr)) != (ssize_t)strlen(pidstr)) {
1085
  m_reason = "write failed";
907
        m_reason = "write failed";
1086
  return -1;
908
        return -1;
1087
    }
909
    }
1088
    return 0;
910
    return 0;
1089
}
911
}
1090
912
1091
int Pidfile::close()
913
int Pidfile::close()
...
...
1094
}
916
}
1095
917
1096
int Pidfile::remove()
918
int Pidfile::remove()
1097
{
919
{
1098
    return unlink(m_path.c_str());
920
    return unlink(m_path.c_str());
1099
}
1100
1101
1102
// Freedesktop standard paths for cache directory (thumbnails are now in there)
1103
static const string& xdgcachedir()
1104
{
1105
    static string xdgcache;
1106
    if (xdgcache.empty()) {
1107
  const char *cp = getenv("XDG_CACHE_HOME");
1108
  if (cp == 0) 
1109
      xdgcache = path_cat(path_home(), ".cache");
1110
  else
1111
      xdgcache = string(cp);
1112
    }
1113
    return xdgcache;
1114
}
1115
static const string& thumbnailsdir()
1116
{
1117
    static string thumbnailsd;
1118
    if (thumbnailsd.empty()) {
1119
  thumbnailsd = path_cat(xdgcachedir(), "thumbnails");
1120
  if (access(thumbnailsd.c_str(), 0) != 0) {
1121
      thumbnailsd = path_cat(path_home(), ".thumbnails");
1122
  }
1123
    }
1124
    return thumbnailsd;
1125
}
1126
1127
// Place for 256x256 files
1128
static const string thmbdirlarge = "large";
1129
// 128x128
1130
static const string thmbdirnormal = "normal";
1131
1132
static void thumbname(const string& url, string& name)
1133
{
1134
    string digest;
1135
    string l_url = url_encode(url);
1136
    MD5String(l_url, digest);
1137
    MD5HexPrint(digest, name);
1138
    name += ".png";
1139
}
1140
1141
bool thumbPathForUrl(const string& url, int size, string& path)
1142
{
1143
    string name;
1144
    thumbname(url, name);
1145
    if (size <= 128) {
1146
  path = path_cat(thumbnailsdir(), thmbdirnormal);
1147
  path = path_cat(path, name);
1148
  if (access(path.c_str(), R_OK) == 0) {
1149
      return true;
1150
  }
1151
    } 
1152
    path = path_cat(thumbnailsdir(), thmbdirlarge);
1153
    path = path_cat(path, name);
1154
    if (access(path.c_str(), R_OK) == 0) {
1155
  return true;
1156
    }
1157
1158
    // File does not exist. Path corresponds to the large version at this point,
1159
    // fix it if needed.
1160
    if (size <= 128) {
1161
  path = path_cat(path_home(), thmbdirnormal);
1162
  path = path_cat(path, name);
1163
    }
1164
    return false;
1165
}
921
}
1166
922
1167
// Call funcs that need static init (not initially reentrant)
923
// Call funcs that need static init (not initially reentrant)
1168
void pathut_init_mt()
924
void pathut_init_mt()
1169
{
925
{
1170
    path_home();
926
    path_home();
1171
    tmplocation();
1172
    thumbnailsdir();
1173
    path_sharedatadir();
1174
}
927
}
1175
928
1176
929
1177
#else // TEST_PATHUT
930
#else // TEST_PATHUT
1178
#include <stdlib.h>
931
#include <stdlib.h>
...
...
1183
936
1184
void path_to_thumb(const string& _input)
937
void path_to_thumb(const string& _input)
1185
{
938
{
1186
    string input(_input);
939
    string input(_input);
1187
    // Make absolute path if needed
940
    // Make absolute path if needed
1188
    if (input[0] != '/')
941
    if (input[0] != '/') {
1189
        input = path_absolute(input);
942
        input = path_absolute(input);
943
    }
1190
944
1191
    input = string("file://") + path_canon(input);
945
    input = string("file://") + path_canon(input);
1192
946
1193
    string path;
947
    string path;
1194
    //path = url_encode(input, 7);
948
    //path = url_encode(input, 7);
1195
    thumbPathForUrl(input, 7, path);
949
    thumbPathForUrl(input, 7, path);
1196
    cout << path << endl;
950
    cout << path << endl;
1197
}
951
}
1198
952
1199
const char *tstvec[] = {"", "/", "/dir", "/dir/", "/dir1/dir2",
953
const char *tstvec[] = {"", "/", "/dir", "/dir/", "/dir1/dir2",
1200
           "/dir1/dir2",
954
                        "/dir1/dir2",
1201
          "./dir", "./dir1/", "dir", "../dir", "/dir/toto.c",
955
                        "./dir", "./dir1/", "dir", "../dir", "/dir/toto.c",
1202
          "/dir/.c", "/dir/toto.txt", "toto.txt1"
956
                        "/dir/.c", "/dir/toto.txt", "toto.txt1"
1203
};
957
                       };
1204
958
1205
const string ttvec[] = {"/dir", "", "~", "~/sub", "~root", "~root/sub",
959
const string ttvec[] = {"/dir", "", "~", "~/sub", "~root", "~root/sub",
1206
       "~nosuch", "~nosuch/sub"};
960
                        "~nosuch", "~nosuch/sub"
961
                       };
1207
int nttvec = sizeof(ttvec) / sizeof(string);
962
int nttvec = sizeof(ttvec) / sizeof(string);
1208
963
1209
const char *thisprog;
964
const char *thisprog;
1210
965
1211
int main(int argc, const char **argv)
966
int main(int argc, const char **argv)
1212
{
967
{
1213
    thisprog = *argv++;argc--;
968
    thisprog = *argv++;
969
    argc--;
1214
970
1215
    string s;
971
    string s;
1216
    vector<string>::const_iterator it;
972
    vector<string>::const_iterator it;
1217
#if 0
973
#if 0
1218
    for (unsigned int i = 0;i < sizeof(tstvec) / sizeof(char *); i++) {
974
    for (unsigned int i = 0; i < sizeof(tstvec) / sizeof(char *); i++) {
1219
  cout << tstvec[i] << " Father " << path_getfather(tstvec[i]) << endl;
975
        cout << tstvec[i] << " Father " << path_getfather(tstvec[i]) << endl;
1220
    }
976
    }
1221
    for (unsigned int i = 0;i < sizeof(tstvec) / sizeof(char *); i++) {
977
    for (unsigned int i = 0; i < sizeof(tstvec) / sizeof(char *); i++) {
1222
  cout << tstvec[i] << " Simple " << path_getsimple(tstvec[i]) << endl;
978
        cout << tstvec[i] << " Simple " << path_getsimple(tstvec[i]) << endl;
1223
    }
979
    }
1224
    for (unsigned int i = 0;i < sizeof(tstvec) / sizeof(char *); i++) {
980
    for (unsigned int i = 0; i < sizeof(tstvec) / sizeof(char *); i++) {
1225
  cout << tstvec[i] << " Basename " << 
981
        cout << tstvec[i] << " Basename " <<
1226
      path_basename(tstvec[i], ".txt") << endl;
982
             path_basename(tstvec[i], ".txt") << endl;
1227
    }
983
    }
1228
#endif
984
#endif
1229
985
1230
#if 0
986
#if 0
1231
    for (int i = 0; i < nttvec; i++) {
987
    for (int i = 0; i < nttvec; i++) {
1232
  cout << "tildexp: '" << ttvec[i] << "' -> '" << 
988
        cout << "tildexp: '" << ttvec[i] << "' -> '" <<
1233
      path_tildexpand(ttvec[i]) << "'" << endl;
989
             path_tildexpand(ttvec[i]) << "'" << endl;
1234
    }
990
    }
1235
#endif
991
#endif
1236
992
1237
#if 0
993
#if 0
1238
    const string canontst[] = {"/dir1/../../..", "/////", "", 
994
    const string canontst[] = {"/dir1/../../..", "/////", "",
1239
                 "/dir1/../../.././/////dir2///////",
995
                               "/dir1/../../.././/////dir2///////",
1240
                 "../../", 
996
                               "../../",
1241
                 "../../../../../../../../../../"
997
                               "../../../../../../../../../../"
1242
    };
998
                              };
1243
    unsigned int nttvec = sizeof(canontst) / sizeof(string);
999
    unsigned int nttvec = sizeof(canontst) / sizeof(string);
1244
    for (unsigned int i = 0; i < nttvec; i++) {
1000
    for (unsigned int i = 0; i < nttvec; i++) {
1245
  cout << "canon: '" << canontst[i] << "' -> '" << 
1001
        cout << "canon: '" << canontst[i] << "' -> '" <<
1246
      path_canon(canontst[i]) << "'" << endl;
1002
             path_canon(canontst[i]) << "'" << endl;
1247
    }
1003
    }
1248
#endif    
1004
#endif
1249
#if 0
1005
#if 0
1250
    if (argc != 2) {
1006
    if (argc != 2) {
1251
  cerr << "Usage: trpathut <dir> <pattern>" << endl;
1007
        cerr << "Usage: trpathut <dir> <pattern>" << endl;
1252
  exit(1);
1008
        exit(1);
1253
    }
1009
    }
1254
    string dir = *argv++;argc--;
1010
    string dir = *argv++;
1011
    argc--;
1255
    string pattern =  *argv++;argc--;
1012
    string pattern =  *argv++;
1013
    argc--;
1256
    vector<string> matched = path_dirglob(dir, pattern);
1014
    vector<string> matched = path_dirglob(dir, pattern);
1257
    for (it = matched.begin(); it != matched.end();it++) {
1015
    for (it = matched.begin(); it != matched.end(); it++) {
1258
  cout << *it << endl;
1016
        cout << *it << endl;
1259
    }
1017
    }
1260
#endif
1018
#endif
1261
1019
1262
#if 0
1020
#if 0
1263
    if (argc != 1) {
1021
    if (argc != 1) {
1264
  fprintf(stderr, "Usage: fsocc: trpathut <path>\n");
1022
        fprintf(stderr, "Usage: fsocc: trpathut <path>\n");
1265
  exit(1);
1023
        exit(1);
1266
    }
1024
    }
1267
  string path = *argv++;argc--;
1025
    string path = *argv++;
1026
    argc--;
1268
1027
1269
  int pc;
1028
    int pc;
1270
  long long blocks;
1029
    long long blocks;
1271
  if (!fsocc(path, &pc, &blocks)) {
1030
    if (!fsocc(path, &pc, &blocks)) {
1272
      fprintf(stderr, "fsocc failed\n");
1031
        fprintf(stderr, "fsocc failed\n");
1273
      return 1;
1032
        return 1;
1274
  }
1033
    }
1275
  printf("pc %d, megabytes %ld\n", pc, blocks);
1034
    printf("pc %d, megabytes %ld\n", pc, blocks);
1276
#endif
1035
#endif
1277
1036
1278
#if 0
1037
#if 0
1279
  Pidfile pidfile("/tmp/pathutpidfile");
1038
    Pidfile pidfile("/tmp/pathutpidfile");
1280
  pid_t pid;
1039
    pid_t pid;
1281
  if ((pid = pidfile.open()) != 0) {
1040
    if ((pid = pidfile.open()) != 0) {
1282
      cerr << "open failed. reason: " << pidfile.getreason() << 
1041
        cerr << "open failed. reason: " << pidfile.getreason() <<
1283
    " return " << pid << endl;
1042
             " return " << pid << endl;
1284
      exit(1);
1043
        exit(1);
1285
  }
1044
    }
1286
  pidfile.write_pid();
1045
    pidfile.write_pid();
1287
  sleep(10);
1046
    sleep(10);
1288
  pidfile.close();
1047
    pidfile.close();
1289
  pidfile.remove();
1048
    pidfile.remove();
1290
#endif
1049
#endif
1291
1050
1292
#if 0
1051
#if 0
1293
  if (argc > 1) {
1052
    if (argc > 1) {
1294
      cerr <<  "Usage: thumbpath <filepath>" << endl;
1053
        cerr <<  "Usage: thumbpath <filepath>" << endl;
1295
      exit(1);
1054
        exit(1);
1296
  }
1055
    }
1297
  string input;
1056
    string input;
1298
  if (argc == 1) {
1057
    if (argc == 1) {
1299
      input = *argv++;
1058
        input = *argv++;
1300
      if (input.empty())  {
1059
        if (input.empty())  {
1301
          cerr << "Usage: thumbpath <filepath>" << endl;
1060
            cerr << "Usage: thumbpath <filepath>" << endl;
1302
          exit(1);
1061
            exit(1);
1303
      }
1062
        }
1304
      path_to_thumb(input);
1063
        path_to_thumb(input);
1305
  } else {
1064
    } else {
1306
      while (getline(cin, input))
1065
        while (getline(cin, input)) {
1307
          path_to_thumb(input);
1066
            path_to_thumb(input);
1067
        }
1308
  }
1068
    }
1309
1069
1310
  
1070
1311
  exit(0);
1071
    exit(0);
1312
#endif
1072
#endif
1313
1073
1314
#if 0
1074
#if 0
1315
    if (argc != 1) {
1075
    if (argc != 1) {
1316
  cerr << "Usage: trpathut <filename>" << endl;
1076
        cerr << "Usage: trpathut <filename>" << endl;
1317
  exit(1);
1077
        exit(1);
1318
    }
1078
    }
1319
    string fn = *argv++;argc--;
1079
    string fn = *argv++;
1080
    argc--;
1320
    string ext = path_suffix(fn);
1081
    string ext = path_suffix(fn);
1321
    cout << "Suffix: [" << ext << "]" << endl;
1082
    cout << "Suffix: [" << ext << "]" << endl;
1322
    return 0;
1083
    return 0;
1323
#endif
1084
#endif
1324
1085
1325
#if 1
1086
#if 1
1326
    if (argc != 1) {
1087
    if (argc != 1) {
1327
  cerr << "Usage: trpathut url" << endl;
1088
        cerr << "Usage: trpathut url" << endl;
1328
  exit(1);
1089
        exit(1);
1329
    }
1090
    }
1330
    string url = *argv++;argc--;
1091
    string url = *argv++;
1092
    argc--;
1331
1093
1332
    cout << "File: [" << fileurltolocalpath(url) << "]\n";
1094
    cout << "File: [" << fileurltolocalpath(url) << "]\n";
1333
    return 0;
1095
    return 0;
1334
#endif
1096
#endif
1335
1097