Coverage Report

Created: 2025-07-11 06:34

/src/aspell/common/file_util.cpp
Line
Count
Source (jump to first uncovered line)
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
34
#else
35
36
#  include <unistd.h>
37
11.2k
#  define ACCESS access
38
39
#endif
40
41
42
namespace acommon {
43
44
  // Return false if file is already an absolute path and does not need
45
  // a directory prepended.
46
15.8k
  bool need_dir(ParmString file) {
47
15.8k
    if (file[0] == '/' || (file[0] == '.' && file[1] == '/')
48
#ifdef WIN32
49
        || (asc_isalpha(file[0]) && file[1] == ':')
50
        || file[0] == '\\' || (file[0] == '.' && file[1] == '\\')
51
#endif
52
15.8k
      )
53
7.38k
      return false;
54
8.46k
    else
55
8.46k
      return true;
56
15.8k
  }
57
58
13.6k
  String add_possible_dir(ParmString dir, ParmString file) {
59
13.6k
    if (need_dir(file)) {
60
8.45k
      String path;
61
8.45k
      path += dir;
62
8.45k
      path += '/';
63
8.45k
      path += file;
64
8.45k
      return path;
65
8.45k
    } else {
66
5.22k
      return file;
67
5.22k
    }
68
13.6k
  }
69
70
  String figure_out_dir(ParmString dir, ParmString file)
71
2.17k
  {
72
2.17k
    String temp;
73
2.17k
    int s = file.size() - 1;
74
35.7k
    while (s != -1 && file[s] != '/') --s;
75
2.17k
    if (need_dir(file)) {
76
16
      temp += dir;
77
16
      temp += '/';
78
16
    }
79
2.17k
    if (s != -1) {
80
2.16k
      temp.append(file, s);
81
2.16k
    }
82
2.17k
    return temp;
83
2.17k
  }
84
85
0
  time_t get_modification_time(FStream & f) {
86
0
    struct stat s;
87
0
    fstat(f.file_no(), &s);
88
0
    return s.st_mtime;
89
0
  }
90
91
2.05k
  PosibErr<void> open_file_readlock(FStream & in, ParmString file) {
92
2.05k
    RET_ON_ERR(in.open(file, "r"));
93
0
#ifdef USE_FILE_LOCKS
94
0
    int fd = in.file_no();
95
0
    struct flock fl;
96
0
    fl.l_type   = F_RDLCK;
97
0
    fl.l_whence = SEEK_SET;
98
0
    fl.l_start  = 0;
99
0
    fl.l_len    = 0;
100
0
    fcntl(fd, F_SETLKW, &fl); // ignore errors
101
0
#endif
102
0
    return no_err;
103
2.05k
  }
104
105
0
  PosibErr<bool> open_file_writelock(FStream & inout, ParmString file) {
106
0
    typedef PosibErr<bool> Ret;
107
#ifndef USE_FILE_LOCKS
108
    bool exists = file_exists(file);
109
#endif
110
0
    {
111
0
     Ret pe = inout.open(file, "r+");
112
0
     if (pe.get_err() != 0)
113
0
       pe = inout.open(file, "w+");
114
0
     if (pe.has_err())
115
0
       return pe;
116
0
    }
117
0
#ifdef USE_FILE_LOCKS
118
0
    int fd = inout.file_no();
119
0
    struct flock fl;
120
0
    fl.l_type   = F_WRLCK;
121
0
    fl.l_whence = SEEK_SET;
122
0
    fl.l_start  = 0;
123
0
    fl.l_len    = 0;
124
0
    fcntl(fd, F_SETLKW, &fl); // ignore errors
125
0
    struct stat s;
126
0
    fstat(fd, &s);
127
0
    return s.st_size != 0;
128
#else
129
    return exists;
130
#endif
131
0
  }
132
133
0
  void truncate_file(FStream & f, ParmString name) {
134
0
#ifdef USE_FILE_LOCKS
135
0
    f.restart();
136
0
    ftruncate(f.file_no(),0);
137
#else
138
    f.close();
139
    f.open(name, "w+");
140
#endif
141
0
  }
142
143
0
  bool remove_file(ParmString name) {
144
0
    return remove(name) == 0;
145
0
  }
146
147
11.2k
  bool file_exists(ParmString name) {
148
11.2k
    return ACCESS(name, F_OK) == 0;
149
11.2k
  }
150
151
  bool rename_file(ParmString orig_name, ParmString new_name)
152
0
  {
153
0
    remove(new_name);
154
0
    return rename(orig_name, new_name) == 0;
155
0
  }
156
 
157
0
  const char * get_file_name(const char * path) {
158
0
    const char * file_name;
159
0
    if (path != 0) {
160
0
      file_name = strrchr(path,'/');
161
0
      if (file_name == 0)
162
0
        file_name = path;
163
0
    } else {
164
0
      file_name = 0;
165
0
    }
166
0
    return file_name;
167
0
  }
168
169
  unsigned find_file(const Config * config, const char * option, String & filename)
170
4.04k
  {
171
4.04k
    StringList sl;
172
4.04k
    config->retrieve_list(option, &sl);
173
4.04k
    return find_file(sl, filename);
174
4.04k
  }
175
176
  unsigned find_file(const StringList & sl, String & filename)
177
4.04k
  {
178
4.04k
    StringListEnumeration els = sl.elements_obj();
179
4.04k
    const char * dir;
180
4.04k
    String path;
181
9.37k
    while ( (dir = els.next()) != 0 ) 
182
5.32k
    {
183
5.32k
      path = dir;
184
5.32k
      if (path.empty()) continue;
185
4.98k
      if (path.back() != '/') path += '/';
186
4.98k
      unsigned dir_len = path.size();
187
4.98k
      path += filename;
188
4.98k
      if (file_exists(path)) {
189
0
        filename.swap(path);
190
0
        return dir_len;
191
0
      }
192
4.98k
    }
193
4.04k
    return 0;
194
4.04k
  }
195
196
  PathBrowser::PathBrowser(const StringList & sl, const char * suf)
197
1.80k
    : dir_handle(0)
198
1.80k
  {
199
1.80k
    els = sl.elements();
200
1.80k
    suffix = suf;
201
1.80k
  }
202
203
  PathBrowser::~PathBrowser() 
204
1.80k
  {
205
1.80k
    delete els;
206
1.80k
    if (dir_handle) closedir((DIR *)dir_handle);
207
1.80k
  }
208
209
  const char * PathBrowser::next()
210
2.99k
  {
211
2.99k
    if (dir_handle == 0) goto get_next_dir;
212
25.1k
  begin: {
213
25.1k
      struct dirent * entry = readdir((DIR *)dir_handle);
214
25.1k
      if (entry == 0) goto try_again;
215
24.8k
      const char * name = entry->d_name;
216
24.8k
      unsigned name_len = strlen(name);
217
24.8k
      if (suffix.size() != 0 && 
218
24.8k
          !(name_len > suffix.size() 
219
24.8k
            && memcmp(name + name_len - suffix.size(), suffix.str(), suffix.size()) == 0))
220
23.6k
        goto begin;
221
1.18k
      path = dir;
222
1.18k
      if (path.back() != '/') path += '/';
223
1.18k
      path += name;
224
1.18k
    }
225
0
    return path.str();
226
2.20k
  try_again:
227
2.20k
    if (dir_handle) closedir((DIR *)dir_handle);
228
2.20k
    dir_handle = 0;
229
4.01k
  get_next_dir:
230
4.01k
    dir = els->next();
231
4.01k
    if (!dir) return 0;
232
2.20k
    dir_handle = opendir(dir);
233
2.20k
    if (dir_handle == 0) goto try_again;
234
367
    goto begin;
235
2.20k
  }
236
}