1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING.  If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 #  include "config.h"
28 #endif
29 
30 #include <ctime>
31 #include <cerrno>
32 #include <cstring>
33 
34 #include "file-ops.h"
35 #include "file-stat.h"
36 #include "stat-wrappers.h"
37 #include "strmode-wrapper.h"
38 
39 #if defined (OCTAVE_USE_WINDOWS_API)
40 #  include "lo-regexp.h"
41 #  include "oct-env.h"
42 #endif
43 
44 namespace octave
45 {
46   namespace sys
47   {
48     // FIXME: the is_* and mode_as_string functions are only valid
49     // for initialized objects.  If called for an object that is not
50     // initialized, they should throw an exception.
51 
52     bool
is_blk(void) const53     base_file_stat::is_blk (void) const
54     {
55       return exists () && is_blk (m_mode);
56     }
57 
58     bool
is_chr(void) const59     base_file_stat::is_chr (void) const
60     {
61       return exists () && is_chr (m_mode);
62     }
63 
64     bool
is_dir(void) const65     base_file_stat::is_dir (void) const
66     {
67       return exists () && is_dir (m_mode);
68     }
69 
70     bool
is_fifo(void) const71     base_file_stat::is_fifo (void) const
72     {
73       return exists () && is_fifo (m_mode);
74     }
75 
76     bool
is_lnk(void) const77     base_file_stat::is_lnk (void) const
78     {
79       return exists () && is_lnk (m_mode);
80     }
81 
82     bool
is_reg(void) const83     base_file_stat::is_reg (void) const
84     {
85       return exists () && is_reg (m_mode);
86     }
87 
88     bool
is_sock(void) const89     base_file_stat::is_sock (void) const
90     {
91       return exists () && is_sock (m_mode);
92     }
93 
94     bool
is_blk(mode_t mode)95     base_file_stat::is_blk (mode_t mode)
96     {
97       return octave_is_blk_wrapper (mode);
98     }
99 
100     bool
is_chr(mode_t mode)101     base_file_stat::is_chr (mode_t mode)
102     {
103       return octave_is_chr_wrapper (mode);
104     }
105 
106     bool
is_dir(mode_t mode)107     base_file_stat::is_dir (mode_t mode)
108     {
109       return octave_is_dir_wrapper (mode);
110     }
111 
112     bool
is_fifo(mode_t mode)113     base_file_stat::is_fifo (mode_t mode)
114     {
115       return octave_is_fifo_wrapper (mode);
116     }
117 
118     bool
is_lnk(mode_t mode)119     base_file_stat::is_lnk (mode_t mode)
120     {
121       return octave_is_lnk_wrapper (mode);
122     }
123 
124     bool
is_reg(mode_t mode)125     base_file_stat::is_reg (mode_t mode)
126     {
127       return octave_is_reg_wrapper (mode);
128     }
129 
130     bool
is_sock(mode_t mode)131     base_file_stat::is_sock (mode_t mode)
132     {
133       return octave_is_sock_wrapper (mode);
134     }
135 
136     bool
have_struct_stat_st_rdev(void)137     base_file_stat::have_struct_stat_st_rdev (void)
138     {
139       return ::octave_have_struct_stat_st_rdev ();
140     }
141 
142     bool
have_struct_stat_st_blksize(void)143     base_file_stat::have_struct_stat_st_blksize (void)
144     {
145       return octave_have_struct_stat_st_blksize ();
146     }
147 
148     bool
have_struct_stat_st_blocks(void)149     base_file_stat::have_struct_stat_st_blocks (void)
150     {
151       return octave_have_struct_stat_st_blocks ();
152     }
153 
154     std::string
mode_as_string(void) const155     base_file_stat::mode_as_string (void) const
156     {
157       char buf[12];
158 
159       octave_strmode_wrapper (m_mode, buf);
160 
161       return std::string (buf);
162     }
163 
164     // Has FILE been modified since TIME?  Returns 1 for yes, 0 for no,
165     // and -1 for any error.
166 
167     int
is_newer(const std::string & file,const sys::time & time)168     base_file_stat::is_newer (const std::string& file,
169                               const sys::time& time)
170     {
171       file_stat fs (file);
172 
173       return fs ? fs.is_newer (time) : -1;
174     }
175 
176     // Private stuff:
177 
file_stat(const std::string & n,bool fl)178     file_stat::file_stat (const std::string& n, bool fl)
179       : base_file_stat (), file_name (n), follow_links (fl)
180     {
181       if (! file_name.empty ())
182         update_internal ();
183     }
184 
~file_stat()185     file_stat::~file_stat () { }
186 
187     void
update_internal(bool force)188     file_stat::update_internal (bool force)
189     {
190       if (! initialized || force)
191         {
192           initialized = false;
193           fail = false;
194 
195           std::string full_file_name = sys::file_ops::tilde_expand (file_name);
196 
197 #if defined (OCTAVE_USE_WINDOWS_API)
198           full_file_name = sys::env::make_absolute (full_file_name);
199 
200           // Remove trailing slashes
201           while (full_file_name.length () > 1
202               && sys::file_ops::is_dir_sep (full_file_name.back ()))
203             full_file_name.pop_back ();
204 
205           // If path is a root (like "C:" or "\\SERVER\share"), add a
206           // trailing backslash.
207           // FIXME: This pattern does not match all possible UNC roots:
208           //        https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/62e862f4-2a51-452e-8eeb-dc4ff5ee33cc
209           static regexp pat (R"(^\\\\[\w.-]+\\[\w\$-]+$)");
210           if ((full_file_name.length () == 2 && full_file_name[1] == ':')
211               || (full_file_name.length () > 4  && full_file_name[0] == '\\'
212                   && full_file_name[1] == '\\' && pat.is_match (full_file_name)))
213             full_file_name.push_back ('\\');
214 #endif
215 
216           const char *cname = full_file_name.c_str ();
217 
218           time_t sys_atime, sys_mtime, sys_ctime;
219 
220           int status
221             = (follow_links
222                ? octave_stat_wrapper (cname, &m_mode, &m_ino, &m_dev,
223                                       &m_nlink, &m_uid, &m_gid, &m_size,
224                                       &sys_atime, &sys_mtime, &sys_ctime,
225                                       &m_rdev, &m_blksize, &m_blocks)
226                : octave_lstat_wrapper (cname, &m_mode, &m_ino, &m_dev,
227                                        &m_nlink, &m_uid, &m_gid, &m_size,
228                                        &sys_atime, &sys_mtime, &sys_ctime,
229                                        &m_rdev, &m_blksize, &m_blocks));
230 
231           if (status < 0)
232             {
233               fail = true;
234               errmsg = std::strerror (errno);
235             }
236           else
237             {
238               m_atime = sys::time (sys_atime);
239               m_mtime = sys::time (sys_mtime);
240               m_ctime = sys::time (sys_ctime);
241             }
242 
243           initialized = true;
244         }
245     }
246 
247     void
update_internal(bool force)248     file_fstat::update_internal (bool force)
249     {
250       if (! initialized || force)
251         {
252           initialized = false;
253           fail = false;
254 
255           time_t sys_atime, sys_mtime, sys_ctime;
256 
257           int status
258             = octave_fstat_wrapper (fid, &m_mode, &m_ino, &m_dev,
259                                     &m_nlink, &m_uid, &m_gid, &m_size,
260                                     &sys_atime, &sys_mtime, &sys_ctime,
261                                     &m_rdev, &m_blksize, &m_blocks);
262 
263           if (status < 0)
264             {
265               fail = true;
266               errmsg = std::strerror (errno);
267             }
268           else
269             {
270               m_atime = sys::time (sys_atime);
271               m_mtime = sys::time (sys_mtime);
272               m_ctime = sys::time (sys_ctime);
273             }
274 
275           initialized = true;
276         }
277     }
278   }
279 }
280