Coverage Report

Created: 2026-01-17 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/aspell/common/file_util.cpp
Line
Count
Source
1
// This file is part of The New Aspell
2
// Copyright (C) 2001 by Kevin Atkinson under the GNU LGPL license
3
// version 2.0 or 2.1.  You should have received a copy of the LGPL
4
// license along with this library if you did not you can find
5
// it at http://www.gnu.org/.
6
7
#include "settings.h"
8
9
//#include "iostream.hpp"
10
11
#include "config.hpp"
12
#include "file_util.hpp"
13
#include "fstream.hpp"
14
#include "errors.hpp"
15
#include "string_list.hpp"
16
17
#ifdef USE_FILE_LOCKS
18
#  include <fcntl.h>
19
#  include <unistd.h>
20
#  include <sys/types.h>
21
#endif
22
23
#include <stdio.h>
24
#include <sys/stat.h>
25
#include <dirent.h>
26
27
#ifdef WIN32
28
29
#  include <io.h>
30
#  define ACCESS _access
31
#  include <windows.h>
32
#  include <winbase.h>
33
#  include "asc_ctype.hpp"
34
35
#else
36
37
#  include <unistd.h>
38
11.6k
#  define ACCESS access
39
40
#endif
41
42
43
namespace acommon {
44
45
  // Return false if file is already an absolute path and does not need
46
  // a directory prepended.
47
19.9k
  bool need_dir(ParmString file) {
48
19.9k
    if (file[0] == '/' || (file[0] == '.' && file[1] == '/')
49
#ifdef WIN32
50
        || (asc_isalpha(file[0]) && file[1] == ':')
51
        || file[0] == '\\' || (file[0] == '.' && file[1] == '\\')
52
#endif
53
19.9k
      )
54
8.64k
      return false;
55
11.2k
    else
56
11.2k
      return true;
57
19.9k
  }
58
59
15.7k
  String add_possible_dir(ParmString dir, ParmString file) {
60
15.7k
    if (need_dir(file)) {
61
10.1k
      String path;
62
10.1k
      path += dir;
63
10.1k
      path += '/';
64
10.1k
      path += file;
65
10.1k
      return path;
66
10.1k
    } else {
67
5.66k
      return file;
68
5.66k
    }
69
15.7k
  }
70
71
  String figure_out_dir(ParmString dir, ParmString file)
72
4.15k
  {
73
4.15k
    String temp;
74
4.15k
    int s = file.size() - 1;
75
45.2k
    while (s != -1 && file[s] != '/') --s;
76
4.15k
    if (need_dir(file)) {
77
1.18k
      temp += dir;
78
1.18k
      temp += '/';
79
1.18k
    }
80
4.15k
    if (s != -1) {
81
2.97k
      temp.append(file, s);
82
2.97k
    }
83
4.15k
    return temp;
84
4.15k
  }
85
86
0
  time_t get_modification_time(FStream & f) {
87
0
    struct stat s;
88
0
    fstat(f.file_no(), &s);
89
0
    return s.st_mtime;
90
0
  }
91
92
2.04k
  PosibErr<void> open_file_readlock(FStream & in, ParmString file) {
93
2.04k
    RET_ON_ERR(in.open(file, "r"));
94
1
#ifdef USE_FILE_LOCKS
95
1
    int fd = in.file_no();
96
1
    struct flock fl;
97
1
    fl.l_type   = F_RDLCK;
98
1
    fl.l_whence = SEEK_SET;
99
1
    fl.l_start  = 0;
100
1
    fl.l_len    = 0;
101
1
    fcntl(fd, F_SETLKW, &fl); // ignore errors
102
1
#endif
103
1
    return no_err;
104
2.04k
  }
105
106
0
  PosibErr<bool> open_file_writelock(FStream & inout, ParmString file) {
107
0
    typedef PosibErr<bool> Ret;
108
#ifndef USE_FILE_LOCKS
109
    bool exists = file_exists(file);
110
#endif
111
0
    {
112
0
     Ret pe = inout.open(file, "r+");
113
0
     if (pe.get_err() != 0)
114
0
       pe = inout.open(file, "w+");
115
0
     if (pe.has_err())
116
0
       return pe;
117
0
    }
118
0
#ifdef USE_FILE_LOCKS
119
0
    int fd = inout.file_no();
120
0
    struct flock fl;
121
0
    fl.l_type   = F_WRLCK;
122
0
    fl.l_whence = SEEK_SET;
123
0
    fl.l_start  = 0;
124
0
    fl.l_len    = 0;
125
0
    fcntl(fd, F_SETLKW, &fl); // ignore errors
126
0
    struct stat s;
127
0
    fstat(fd, &s);
128
0
    return s.st_size != 0;
129
#else
130
    return exists;
131
#endif
132
0
  }
133
134
0
  void truncate_file(FStream & f, ParmString name) {
135
0
#ifdef USE_FILE_LOCKS
136
0
    f.restart();
137
0
    ftruncate(f.file_no(),0);
138
#else
139
    f.close();
140
    f.open(name, "w+");
141
#endif
142
0
  }
143
144
0
  bool remove_file(ParmString name) {
145
0
    return remove(name) == 0;
146
0
  }
147
148
11.6k
  bool file_exists(ParmString name) {
149
11.6k
    return ACCESS(name, F_OK) == 0;
150
11.6k
  }
151
152
  bool rename_file(ParmString orig_name, ParmString new_name)
153
0
  {
154
0
    remove(new_name);
155
0
    return rename(orig_name, new_name) == 0;
156
0
  }
157
 
158
0
  const char * get_file_name(const char * path) {
159
0
    const char * file_name;
160
0
    if (path != 0) {
161
0
      file_name = strrchr(path,'/');
162
0
      if (file_name == 0)
163
0
        file_name = path;
164
0
    } else {
165
0
      file_name = 0;
166
0
    }
167
0
    return file_name;
168
0
  }
169
170
  unsigned find_file(const Config * config, const char * option, String & filename)
171
4.82k
  {
172
4.82k
    StringList sl;
173
4.82k
    config->retrieve_list(option, &sl);
174
4.82k
    return find_file(sl, filename);
175
4.82k
  }
176
177
  unsigned find_file(const StringList & sl, String & filename)
178
4.82k
  {
179
4.82k
    StringListEnumeration els = sl.elements_obj();
180
4.82k
    const char * dir;
181
4.82k
    String path;
182
10.4k
    while ( (dir = els.next()) != 0 ) 
183
5.64k
    {
184
5.64k
      path = dir;
185
5.64k
      if (path.empty()) continue;
186
5.47k
      if (path.back() != '/') path += '/';
187
5.47k
      unsigned dir_len = path.size();
188
5.47k
      path += filename;
189
5.47k
      if (file_exists(path)) {
190
0
        filename.swap(path);
191
0
        return dir_len;
192
0
      }
193
5.47k
    }
194
4.82k
    return 0;
195
4.82k
  }
196
197
  PathBrowser::PathBrowser(const StringList & sl, const char * suf)
198
2.06k
    : dir_handle(0)
199
2.06k
  {
200
2.06k
    els = sl.elements();
201
2.06k
    suffix = suf;
202
2.06k
  }
203
204
  PathBrowser::~PathBrowser() 
205
2.06k
  {
206
2.06k
    delete els;
207
2.06k
    if (dir_handle) closedir((DIR *)dir_handle);
208
2.06k
  }
209
210
  const char * PathBrowser::next()
211
3.46k
  {
212
3.46k
    if (dir_handle == 0) goto get_next_dir;
213
34.2k
  begin: {
214
34.2k
      struct dirent * entry = readdir((DIR *)dir_handle);
215
34.2k
      if (entry == 0) goto try_again;
216
33.6k
      const char * name = entry->d_name;
217
33.6k
      unsigned name_len = strlen(name);
218
33.6k
      if (suffix.size() != 0 && 
219
33.6k
          !(name_len > suffix.size() 
220
23.4k
            && memcmp(name + name_len - suffix.size(), suffix.str(), suffix.size()) == 0))
221
32.2k
        goto begin;
222
1.40k
      path = dir;
223
1.40k
      if (path.back() != '/') path += '/';
224
1.40k
      path += name;
225
1.40k
    }
226
0
    return path.str();
227
2.58k
  try_again:
228
2.58k
    if (dir_handle) closedir((DIR *)dir_handle);
229
2.58k
    dir_handle = 0;
230
4.64k
  get_next_dir:
231
4.64k
    dir = els->next();
232
4.64k
    if (!dir) return 0;
233
2.58k
    dir_handle = opendir(dir);
234
2.58k
    if (dir_handle == 0) goto try_again;
235
572
    goto begin;
236
2.58k
  }
237
}