Coverage Report

Created: 2026-02-26 06:53

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
13.3k
#  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
16.7k
  bool need_dir(ParmString file) {
48
16.7k
    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
16.7k
      )
54
7.10k
      return false;
55
9.68k
    else
56
9.68k
      return true;
57
16.7k
  }
58
59
13.9k
  String add_possible_dir(ParmString dir, ParmString file) {
60
13.9k
    if (need_dir(file)) {
61
8.92k
      String path;
62
8.92k
      path += dir;
63
8.92k
      path += '/';
64
8.92k
      path += file;
65
8.92k
      return path;
66
8.92k
    } else {
67
5.00k
      return file;
68
5.00k
    }
69
13.9k
  }
70
71
  String figure_out_dir(ParmString dir, ParmString file)
72
2.85k
  {
73
2.85k
    String temp;
74
2.85k
    int s = file.size() - 1;
75
45.7k
    while (s != -1 && file[s] != '/') --s;
76
2.85k
    if (need_dir(file)) {
77
756
      temp += dir;
78
756
      temp += '/';
79
756
    }
80
2.85k
    if (s != -1) {
81
2.10k
      temp.append(file, s);
82
2.10k
    }
83
2.85k
    return temp;
84
2.85k
  }
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
1.93k
  PosibErr<void> open_file_readlock(FStream & in, ParmString file) {
93
1.93k
    RET_ON_ERR(in.open(file, "r"));
94
0
#ifdef USE_FILE_LOCKS
95
0
    int fd = in.file_no();
96
0
    struct flock fl;
97
0
    fl.l_type   = F_RDLCK;
98
0
    fl.l_whence = SEEK_SET;
99
0
    fl.l_start  = 0;
100
0
    fl.l_len    = 0;
101
0
    fcntl(fd, F_SETLKW, &fl); // ignore errors
102
0
#endif
103
0
    return no_err;
104
1.93k
  }
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
13.3k
  bool file_exists(ParmString name) {
149
13.3k
    return ACCESS(name, F_OK) == 0;
150
13.3k
  }
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.77k
  {
172
4.77k
    StringList sl;
173
4.77k
    config->retrieve_list(option, &sl);
174
4.77k
    return find_file(sl, filename);
175
4.77k
  }
176
177
  unsigned find_file(const StringList & sl, String & filename)
178
4.77k
  {
179
4.77k
    StringListEnumeration els = sl.elements_obj();
180
4.77k
    const char * dir;
181
4.77k
    String path;
182
12.7k
    while ( (dir = els.next()) != 0 ) 
183
8.01k
    {
184
8.01k
      path = dir;
185
8.01k
      if (path.empty()) continue;
186
7.55k
      if (path.back() != '/') path += '/';
187
7.55k
      unsigned dir_len = path.size();
188
7.55k
      path += filename;
189
7.55k
      if (file_exists(path)) {
190
0
        filename.swap(path);
191
0
        return dir_len;
192
0
      }
193
7.55k
    }
194
4.77k
    return 0;
195
4.77k
  }
196
197
  PathBrowser::PathBrowser(const StringList & sl, const char * suf)
198
2.51k
    : dir_handle(0)
199
2.51k
  {
200
2.51k
    els = sl.elements();
201
2.51k
    suffix = suf;
202
2.51k
  }
203
204
  PathBrowser::~PathBrowser() 
205
2.51k
  {
206
2.51k
    delete els;
207
2.51k
    if (dir_handle) closedir((DIR *)dir_handle);
208
2.51k
  }
209
210
  const char * PathBrowser::next()
211
3.10k
  {
212
3.10k
    if (dir_handle == 0) goto get_next_dir;
213
30.1k
  begin: {
214
30.1k
      struct dirent * entry = readdir((DIR *)dir_handle);
215
30.1k
      if (entry == 0) goto try_again;
216
29.4k
      const char * name = entry->d_name;
217
29.4k
      unsigned name_len = strlen(name);
218
29.4k
      if (suffix.size() != 0 && 
219
29.4k
          !(name_len > suffix.size() 
220
14.2k
            && memcmp(name + name_len - suffix.size(), suffix.str(), suffix.size()) == 0))
221
28.8k
        goto begin;
222
588
      path = dir;
223
588
      if (path.back() != '/') path += '/';
224
588
      path += name;
225
588
    }
226
0
    return path.str();
227
4.67k
  try_again:
228
4.67k
    if (dir_handle) closedir((DIR *)dir_handle);
229
4.67k
    dir_handle = 0;
230
7.19k
  get_next_dir:
231
7.19k
    dir = els->next();
232
7.19k
    if (!dir) return 0;
233
4.67k
    dir_handle = opendir(dir);
234
4.67k
    if (dir_handle == 0) goto try_again;
235
735
    goto begin;
236
4.67k
  }
237
}