Fix FreeBSD 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 // IWYU pragma: no_include <features.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27
28 #include <cstring>
29
30 #include "filedata.h"
31 #include "main-defines.h"
32 #include "ui-fileops.h"
33
34
35 /*
36  *-----------------------------------------------------------------------------
37  * date functions
38  *-----------------------------------------------------------------------------
39  */
40
41 gboolean pan_date_compare(time_t a, time_t b, PanDateLengthType length)
42 {
43         struct tm ta;
44         struct tm tb;
45
46         if (length == PAN_DATE_LENGTH_EXACT) return (a == b);
47
48         if (!localtime_r(&a, &ta) ||
49             !localtime_r(&b, &tb)) return FALSE;
50
51         if (ta.tm_year != tb.tm_year) return FALSE;
52         if (length == PAN_DATE_LENGTH_YEAR) return TRUE;
53
54         if (ta.tm_mon != tb.tm_mon) return FALSE;
55         if (length == PAN_DATE_LENGTH_MONTH) return TRUE;
56
57         if (length == PAN_DATE_LENGTH_WEEK) return (ta.tm_yday / 7 == tb.tm_yday / 7);
58
59         if (ta.tm_mday != tb.tm_mday) return FALSE;
60         if (length == PAN_DATE_LENGTH_DAY) return TRUE;
61
62         return (ta.tm_hour == tb.tm_hour);
63 }
64
65 gint pan_date_value(time_t d, PanDateLengthType length)
66 {
67         struct tm td;
68
69         if (!localtime_r(&d, &td)) return -1;
70
71         switch (length)
72                 {
73                 case PAN_DATE_LENGTH_DAY:
74                         return td.tm_mday;
75                         break;
76                 case PAN_DATE_LENGTH_WEEK:
77                         return td.tm_wday;
78                         break;
79                 case PAN_DATE_LENGTH_MONTH:
80                         return td.tm_mon + 1;
81                         break;
82                 case PAN_DATE_LENGTH_YEAR:
83                         return td.tm_year + 1900;
84                         break;
85                 case PAN_DATE_LENGTH_EXACT:
86                 default:
87                         break;
88                 }
89
90         return -1;
91 }
92
93 #if defined(__GLIBC_PREREQ)
94 # if __GLIBC_PREREQ(2, 27)
95 #  define HAS_GLIBC_STRFTIME_EXTENSIONS
96 # endif
97 #endif
98
99 gchar *pan_date_value_string(time_t d, PanDateLengthType length)
100 {
101         struct tm td;
102         gchar buf[128];
103         const gchar *format = nullptr;
104
105         if (!localtime_r(&d, &td)) return g_strdup("");
106
107         switch (length)
108                 {
109                 case PAN_DATE_LENGTH_DAY:
110                         return g_strdup_printf("%d", td.tm_mday);
111                         break;
112                 case PAN_DATE_LENGTH_WEEK:
113                         format = "%A %e";
114                         break;
115                 case PAN_DATE_LENGTH_MONTH:
116 #if defined(HAS_GLIBC_STRFTIME_EXTENSIONS) || defined(__FreeBSD__)
117                         format = "%OB %Y";
118 #else
119                         format = "%B %Y";
120 #endif
121                         break;
122                 case PAN_DATE_LENGTH_YEAR:
123                         return g_strdup_printf("%d", td.tm_year + 1900);
124                         break;
125                 case PAN_DATE_LENGTH_EXACT:
126                 default:
127                         return g_strdup(text_from_time(d));
128                         break;
129                 }
130
131
132         if (format && strftime(buf, sizeof(buf), format, &td) > 0)
133                 {
134                 gchar *ret = g_locale_to_utf8(buf, -1, nullptr, nullptr, nullptr);
135                 if (ret) return ret;
136                 }
137
138         return g_strdup("");
139 }
140
141 time_t pan_date_to_time(gint year, gint month, gint day)
142 {
143         struct tm lt;
144
145         lt.tm_sec = 0;
146         lt.tm_min = 0;
147         lt.tm_hour = 0;
148         lt.tm_mday = (day >= 1 && day <= 31) ? day : 1;
149         lt.tm_mon = (month >= 1 && month <= 12) ? month - 1 : 0;
150         lt.tm_year = year - 1900;
151         lt.tm_isdst = 0;
152
153         return mktime(&lt);
154 }
155
156
157 /*
158  *-----------------------------------------------------------------------------
159  * folder validation
160  *-----------------------------------------------------------------------------
161  */
162
163 gboolean pan_is_link_loop(const gchar *s)
164 {
165         gchar *sl;
166         struct stat st;
167         gboolean ret = FALSE;
168
169         sl = path_from_utf8(s);
170
171         if (lstat(sl, &st) == 0 && S_ISLNK(st.st_mode))
172                 {
173                 gchar *buf;
174                 gint l;
175
176                 buf = static_cast<gchar *>(g_malloc(st.st_size + 1));
177                 l = readlink(sl, buf, st.st_size);
178                 if (l == st.st_size)
179                         {
180                         buf[l] = '\0';
181
182                         parse_out_relatives(buf);
183                         l = strlen(buf);
184
185                         parse_out_relatives(sl);
186
187                         if (buf[0] == G_DIR_SEPARATOR)
188                                 {
189                                 if (strncmp(sl, buf, l) == 0 &&
190                                     (sl[l] == '\0' || sl[l] == G_DIR_SEPARATOR || l == 1)) ret = TRUE;
191                                 }
192                         else
193                                 {
194                                 gchar *link_path;
195
196                                 link_path = g_build_filename(sl, buf, NULL);
197                                 parse_out_relatives(link_path);
198
199                                 if (strncmp(sl, link_path, l) == 0 &&
200                                     (sl[l] == '\0' || sl[l] == G_DIR_SEPARATOR || l == 1)) ret = TRUE;
201
202                                 g_free(link_path);
203                                 }
204                         }
205
206                 g_free(buf);
207                 }
208
209         g_free(sl);
210
211         return ret;
212 }
213
214 gboolean pan_is_ignored(const gchar *s, gboolean ignore_symlinks)
215 {
216         struct stat st;
217         const gchar *n;
218
219         if (!lstat_utf8(s, &st)) return TRUE;
220
221 #if 0
222         /* normal filesystems have directories with some size or block allocation,
223          * special filesystems (like linux /proc) set both to zero.
224          * enable this check if you enable listing the root "/" folder
225          */
226         if (st.st_size == 0 && st.st_blocks == 0) return TRUE;
227 #endif
228
229         if (S_ISLNK(st.st_mode) && (ignore_symlinks || pan_is_link_loop(s))) return TRUE;
230
231         n = filename_from_path(s);
232         if (n && strcmp(n, GQ_RC_DIR) == 0) return TRUE;
233
234         return FALSE;
235 }
236
237 GList *pan_list_tree(FileData *dir_fd, SortType sort, gboolean ascend, gboolean case_sensitive,
238                      gboolean ignore_symlinks)
239 {
240         GList *flist;
241         GList *dlist;
242         GList *result;
243         GList *folders;
244
245         filelist_read(dir_fd, &flist, &dlist);
246         if (sort != SORT_NONE)
247                 {
248                 flist = filelist_sort(flist, sort, ascend, case_sensitive);
249                 dlist = filelist_sort(dlist, sort, ascend, case_sensitive);
250                 }
251
252         result = flist;
253         folders = dlist;
254         while (folders)
255                 {
256                 FileData *fd;
257
258                 fd = static_cast<FileData *>(folders->data);
259                 folders = g_list_remove(folders, fd);
260
261                 if (!pan_is_ignored(fd->path, ignore_symlinks) &&
262                     filelist_read(fd, &flist, &dlist))
263                         {
264                         if (sort != SORT_NONE)
265                                 {
266                                 flist = filelist_sort(flist, sort, ascend, case_sensitive);
267                                 dlist = filelist_sort(dlist, sort, ascend, case_sensitive);
268                                 }
269
270                         result = g_list_concat(result, flist);
271                         folders = g_list_concat(dlist, folders);
272                         }
273
274                 file_data_unref(fd);
275                 }
276
277         return result;
278 }
279 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */