Coverage Report

Created: 2025-07-18 06:42

/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
13.6k
#  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
18.4k
  bool need_dir(ParmString file) {
47
18.4k
    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
18.4k
      )
53
7.40k
      return false;
54
11.0k
    else
55
11.0k
      return true;
56
18.4k
  }
57
58
15.0k
  String add_possible_dir(ParmString dir, ParmString file) {
59
15.0k
    if (need_dir(file)) {
60
9.88k
      String path;
61
9.88k
      path += dir;
62
9.88k
      path += '/';
63
9.88k
      path += file;
64
9.88k
      return path;
65
9.88k
    } else {
66
5.21k
      return file;
67
5.21k
    }
68
15.0k
  }
69
70
  String figure_out_dir(ParmString dir, ParmString file)
71
3.38k
  {
72
3.38k
    String temp;
73
3.38k
    int s = file.size() - 1;
74
43.0k
    while (s != -1 && file[s] != '/') --s;
75
3.38k
    if (need_dir(file)) {
76
1.18k
      temp += dir;
77
1.18k
      temp += '/';
78
1.18k
    }
79
3.38k
    if (s != -1) {
80
2.33k
      temp.append(file, s);
81
2.33k
    }
82
3.38k
    return temp;
83
3.38k
  }
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.02k
  PosibErr<void> open_file_readlock(FStream & in, ParmString file) {
92
2.02k
    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.02k
  }
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
13.6k
  bool file_exists(ParmString name) {
148
13.6k
    return ACCESS(name, F_OK) == 0;
149
13.6k
  }
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
5.53k
  {
171
5.53k
    StringList sl;
172
5.53k
    config->retrieve_list(option, &sl);
173
5.53k
    return find_file(sl, filename);
174
5.53k
  }
175
176
  unsigned find_file(const StringList & sl, String & filename)
177
5.53k
  {
178
5.53k
    StringListEnumeration els = sl.elements_obj();
179
5.53k
    const char * dir;
180
5.53k
    String path;
181
13.4k
    while ( (dir = els.next()) != 0 ) 
182
7.90k
    {
183
7.90k
      path = dir;
184
7.90k
      if (path.empty()) continue;
185
7.50k
      if (path.back() != '/') path += '/';
186
7.50k
      unsigned dir_len = path.size();
187
7.50k
      path += filename;
188
7.50k
      if (file_exists(path)) {
189
0
        filename.swap(path);
190
0
        return dir_len;
191
0
      }
192
7.50k
    }
193
5.53k
    return 0;
194
5.53k
  }
195
196
  PathBrowser::PathBrowser(const StringList & sl, const char * suf)
197
2.13k
    : dir_handle(0)
198
2.13k
  {
199
2.13k
    els = sl.elements();
200
2.13k
    suffix = suf;
201
2.13k
  }
202
203
  PathBrowser::~PathBrowser() 
204
2.13k
  {
205
2.13k
    delete els;
206
2.13k
    if (dir_handle) closedir((DIR *)dir_handle);
207
2.13k
  }
208
209
  const char * PathBrowser::next()
210
5.38k
  {
211
5.38k
    if (dir_handle == 0) goto get_next_dir;
212
67.6k
  begin: {
213
67.6k
      struct dirent * entry = readdir((DIR *)dir_handle);
214
67.6k
      if (entry == 0) goto try_again;
215
66.6k
      const char * name = entry->d_name;
216
66.6k
      unsigned name_len = strlen(name);
217
66.6k
      if (suffix.size() != 0 && 
218
66.6k
          !(name_len > suffix.size() 
219
66.6k
            && memcmp(name + name_len - suffix.size(), suffix.str(), suffix.size()) == 0))
220
63.4k
        goto begin;
221
3.25k
      path = dir;
222
3.25k
      if (path.back() != '/') path += '/';
223
3.25k
      path += name;
224
3.25k
    }
225
0
    return path.str();
226
3.96k
  try_again:
227
3.96k
    if (dir_handle) closedir((DIR *)dir_handle);
228
3.96k
    dir_handle = 0;
229
6.10k
  get_next_dir:
230
6.10k
    dir = els->next();
231
6.10k
    if (!dir) return 0;
232
3.96k
    dir_handle = opendir(dir);
233
3.96k
    if (dir_handle == 0) goto try_again;
234
964
    goto begin;
235
3.96k
  }
236
}