Blind fix macOS build
[geeqie.git] / src / pan-view / pan-util.cc
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "pan-util.h"
23
24 #ifdef __linux__
25 #  include <features.h>
26 #endif
27 #include <sys/stat.h>
28 #include <unistd.h>
29
30 #include <cstring>
31
32 #include "filedata.h"
33 #include "main-defines.h"
34 #include "ui-fileops.h"
35
36
37 /*
38  *-----------------------------------------------------------------------------
39  * date functions
40  *-----------------------------------------------------------------------------
41  */
42
43 gboolean pan_date_compare(time_t a, time_t b, PanDateLengthType length)
44 {
45         struct tm ta;
46         struct tm tb;
47
48         if (length == PAN_DATE_LENGTH_EXACT) return (a == b);
49
50         if (!localtime_r(&a, &ta) ||
51             !localtime_r(&b, &tb)) return FALSE;
52
53         if (ta.tm_year != tb.tm_year) return FALSE;
54         if (length == PAN_DATE_LENGTH_YEAR) return TRUE;
55
56         if (ta.tm_mon != tb.tm_mon) return FALSE;
57         if (length == PAN_DATE_LENGTH_MONTH) return TRUE;
58
59         if (length == PAN_DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
60
61         if (ta.tm_mday != tb.tm_mday) return FALSE;
62         if (length == PAN_DATE_LENGTH_DAY) return TRUE;
63
64         return (ta.tm_hour == tb.tm_hour);
65 }
66
67 gint pan_date_value(time_t d, PanDateLengthType length)
68 {
69         struct tm td;
70
71         if (!localtime_r(&d, &td)) return -1;
72
73         switch (length)
74                 {
75                 case PAN_DATE_LENGTH_DAY:
76                         return td.tm_mday;
77                         break;
78                 case PAN_DATE_LENGTH_WEEK:
79                         return td.tm_wday;
80                         break;
81                 case PAN_DATE_LENGTH_MONTH:
82                         return td.tm_mon + 1;
83                         break;
84                 case PAN_DATE_LENGTH_YEAR:
85                         return td.tm_year + 1900;
86                         break;
87                 case PAN_DATE_LENGTH_EXACT:
88                 default:
89                         break;
90                 }
91
92         return -1;
93 }
94
95 #if defined(__GLIBC_PREREQ)
96 # if __GLIBC_PREREQ(2, 27)
97 #  define HAS_GLIBC_STRFTIME_EXTENSIONS
98 # endif
99 #endif
100
101 gchar *pan_date_value_string(time_t d, PanDateLengthType length)
102 {
103         struct tm td;
104         gchar buf[128];
105         const gchar *format = nullptr;
106
107         if (!localtime_r(&d, &td)) return g_strdup("");
108
109         switch (length)
110                 {
111                 case PAN_DATE_LENGTH_DAY:
112                         return g_strdup_printf("%d", td.tm_mday);
113                         break;
114                 case PAN_DATE_LENGTH_WEEK:
115                         format = "%A %e";
116                         break;
117                 case PAN_DATE_LENGTH_MONTH:
118 #if defined(HAS_GLIBC_STRFTIME_EXTENSIONS) || defined(__FreeBSD__)
119                         format = "%OB %Y";
120 #else
121                         format = "%B %Y";
122 #endif
123                         break;
124                 case PAN_DATE_LENGTH_YEAR:
125                         return g_strdup_printf("%d", td.tm_year + 1900);
126                         break;
127                 case PAN_DATE_LENGTH_EXACT:
128                 default:
129                         return g_strdup(text_from_time(d));
130                         break;
131                 }
132
133
134         if (format && strftime(buf, sizeof(buf), format, &td) > 0)
135                 {
136                 gchar *ret = g_locale_to_utf8(buf, -1, nullptr, nullptr, nullptr);
137                 if (ret) return ret;
138                 }
139
140         return g_strdup("");
141 }
142
143 time_t pan_date_to_time(gint year, gint month, gint day)
144 {
145         struct tm lt;
146
147         lt.tm_sec = 0;
148         lt.tm_min = 0;
149         lt.tm_hour = 0;
150         lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
151         lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
152         lt.tm_year = year - 1900;
153         lt.tm_isdst = 0;
154
155         return mktime(&lt);
156 }
157
158
159 /*
160  *-----------------------------------------------------------------------------
161  * folder validation
162  *-----------------------------------------------------------------------------
163  */
164
165 gboolean pan_is_link_loop(const gchar *s)
166 {
167         gchar *sl;
168         struct stat st;
169         gboolean ret = FALSE;
170
171         sl = path_from_utf8(s);
172
173         if (lstat(sl, &st) == 0 && S_ISLNK(st.st_mode))
174                 {
175                 gchar *buf;
176                 gint l;
177
178                 buf = static_cast<gchar *>(g_malloc(st.st_size + 1));
179                 l = readlink(sl, buf, st.st_size);
180                 if (l == st.st_size)
181                         {
182                         buf[l] = '\0';
183
184                         parse_out_relatives(buf);
185                         l = strlen(buf);
186
187                         parse_out_relatives(sl);
188
189                         if (buf[0] == G_DIR_SEPARATOR)
190                                 {
191                                 if (strncmp(sl, buf, l) == 0 &&
192                                     (sl[l] == '\0' || sl[l] == G_DIR_SEPARATOR || l == 1)) ret = TRUE;
193                                 }
194                         else
195                                 {
196                                 gchar *link_path;
197
198                                 link_path = g_build_filename(sl, buf, NULL);
199                                 parse_out_relatives(link_path);
200
201                                 if (strncmp(sl, link_path, l) == 0 &&
202                                     (sl[l] == '\0' || sl[l] == G_DIR_SEPARATOR || l == 1)) ret = TRUE;
203
204                                 g_free(link_path);
205                                 }
206                         }
207
208                 g_free(buf);
209                 }
210
211         g_free(sl);
212
213         return ret;
214 }
215
216 gboolean pan_is_ignored(const gchar *s, gboolean ignore_symlinks)
217 {
218         struct stat st;
219         const gchar *n;
220
221         if (!lstat_utf8(s, &st)) return TRUE;
222
223 #if 0
224         /* normal filesystems have directories with some size or block allocation,
225          * special filesystems (like linux /proc) set both to zero.
226          * enable this check if you enable listing the root "/" folder
227          */
228         if (st.st_size == 0 && st.st_blocks == 0) return TRUE;
229 #endif
230
231         if (S_ISLNK(st.st_mode) && (ignore_symlinks || pan_is_link_loop(s))) return TRUE;
232
233         n = filename_from_path(s);
234         if (n && strcmp(n, GQ_RC_DIR) == 0) return TRUE;
235
236         return FALSE;
237 }
238
239 GList *pan_list_tree(FileData *dir_fd, SortType sort, gboolean ascend, gboolean case_sensitive,
240                      gboolean ignore_symlinks)
241 {
242         GList *flist;
243         GList *dlist;
244         GList *result;
245         GList *folders;
246
247         filelist_read(dir_fd, &flist, &dlist);
248         if (sort != SORT_NONE)
249                 {
250                 flist = filelist_sort(flist, sort, ascend, case_sensitive);
251                 dlist = filelist_sort(dlist, sort, ascend, case_sensitive);
252                 }
253
254         result = flist;
255         folders = dlist;
256         while (folders)
257                 {
258                 FileData *fd;
259
260                 fd = static_cast<FileData *>(folders->data);
261                 folders = g_list_remove(folders, fd);
262
263                 if (!pan_is_ignored(fd->path, ignore_symlinks) &&
264                     filelist_read(fd, &flist, &dlist))
265                         {
266                         if (sort != SORT_NONE)
267                                 {
268                                 flist = filelist_sort(flist, sort, ascend, case_sensitive);
269                                 dlist = filelist_sort(dlist, sort, ascend, case_sensitive);
270                                 }
271
272                         result = g_list_concat(result, flist);
273                         folders = g_list_concat(dlist, folders);
274                         }
275
276                 file_data_unref(fd);
277                 }
278
279         return result;
280 }
281 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */