Coverage Report

Created: 2025-04-22 06:17

/src/neomutt/browser/expando.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * Browser Expando definitions
4
 *
5
 * @authors
6
 * Copyright (C) 2024 Richard Russon <rich@flatcap.org>
7
 *
8
 * @copyright
9
 * This program is free software: you can redistribute it and/or modify it under
10
 * the terms of the GNU General Public License as published by the Free Software
11
 * Foundation, either version 2 of the License, or (at your option) any later
12
 * version.
13
 *
14
 * This program is distributed in the hope that it will be useful, but WITHOUT
15
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17
 * details.
18
 *
19
 * You should have received a copy of the GNU General Public License along with
20
 * this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
/**
24
 * @page browser_expando Browser Expando definitions
25
 *
26
 * Browser Expando definitions
27
 */
28
29
#include "config.h"
30
#include <grp.h>
31
#include <pwd.h>
32
#include <stdbool.h>
33
#include <stdio.h>
34
#include <sys/stat.h>
35
#include <time.h>
36
#include "mutt/lib.h"
37
#include "config/lib.h"
38
#include "core/lib.h"
39
#include "expando.h"
40
#include "lib.h"
41
#include "expando/lib.h"
42
#include "muttlib.h"
43
44
/**
45
 * folder_date - Browser: Last modified - Implements ::get_string_t - @ingroup expando_get_string_api
46
 */
47
static void folder_date(const struct ExpandoNode *node, void *data,
48
                        MuttFormatFlags flags, struct Buffer *buf)
49
0
{
50
0
  const struct Folder *folder = data;
51
0
  if (!folder->ff->local)
52
0
    return;
53
54
0
  static const time_t one_year = 31536000;
55
0
  const char *t_fmt = ((mutt_date_now() - folder->ff->mtime) < one_year) ?
56
0
                          "%b %d %H:%M" :
57
0
                          "%b %d  %Y";
58
59
0
  char tmp[128] = { 0 };
60
61
0
  mutt_date_localtime_format(tmp, sizeof(tmp), t_fmt, folder->ff->mtime);
62
63
0
  buf_strcpy(buf, tmp);
64
0
}
65
66
/**
67
 * folder_date_num - Browser: Last modified - Implements ::get_number_t - @ingroup expando_get_number_api
68
 */
69
static long folder_date_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
70
0
{
71
0
  const struct Folder *folder = data;
72
0
  if (!folder->ff->local)
73
0
    return 0;
74
75
0
  return folder->ff->mtime;
76
0
}
77
78
/**
79
 * folder_date_format - Browser: Last modified ($date_format) - Implements ::get_string_t - @ingroup expando_get_string_api
80
 */
81
static void folder_date_format(const struct ExpandoNode *node, void *data,
82
                               MuttFormatFlags flags, struct Buffer *buf)
83
0
{
84
0
  const struct Folder *folder = data;
85
0
  if (!folder->ff->local)
86
0
    return;
87
88
0
  char tmp[128] = { 0 };
89
0
  bool use_c_locale = false;
90
0
  const char *const c_date_format = cs_subset_string(NeoMutt->sub, "date_format");
91
0
  const char *t_fmt = NONULL(c_date_format);
92
0
  if (*t_fmt == '!')
93
0
  {
94
0
    t_fmt++;
95
0
    use_c_locale = true;
96
0
  }
97
98
0
  if (use_c_locale)
99
0
  {
100
0
    mutt_date_localtime_format_locale(tmp, sizeof(tmp), t_fmt,
101
0
                                      folder->ff->mtime, NeoMutt->time_c_locale);
102
0
  }
103
0
  else
104
0
  {
105
0
    mutt_date_localtime_format(tmp, sizeof(tmp), t_fmt, folder->ff->mtime);
106
0
  }
107
108
0
  buf_strcpy(buf, tmp);
109
0
}
110
111
/**
112
 * folder_date_format_num - Browser: Last modified ($date_format) - Implements ::get_number_t - @ingroup expando_get_number_api
113
 */
114
static long folder_date_format_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
115
0
{
116
0
  const struct Folder *folder = data;
117
0
  if (!folder->ff->local)
118
0
    return 0;
119
120
0
  return folder->ff->mtime;
121
0
}
122
123
/**
124
 * folder_date_strf - Browser: Last modified (strftime) - Implements ::get_string_t - @ingroup expando_get_string_api
125
 */
126
static void folder_date_strf(const struct ExpandoNode *node, void *data,
127
                             MuttFormatFlags flags, struct Buffer *buf)
128
0
{
129
0
  const struct Folder *folder = data;
130
131
0
  if (!folder->ff->local)
132
0
    return;
133
134
0
  bool use_c_locale = false;
135
0
  const char *text = node->text;
136
0
  if (*text == '!')
137
0
  {
138
0
    use_c_locale = true;
139
0
    text++;
140
0
  }
141
142
0
  char tmp[128] = { 0 };
143
0
  struct tm tm = mutt_date_localtime(folder->ff->mtime);
144
145
0
  if (use_c_locale)
146
0
  {
147
0
    strftime_l(tmp, sizeof(tmp), text, &tm, NeoMutt->time_c_locale);
148
0
  }
149
0
  else
150
0
  {
151
0
    strftime(tmp, sizeof(tmp), text, &tm);
152
0
  }
153
154
0
  buf_strcpy(buf, tmp);
155
0
}
156
157
/**
158
 * folder_date_strf_num - Browser: Last modified (strftime) - Implements ::get_number_t - @ingroup expando_get_number_api
159
 */
160
static long folder_date_strf_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
161
0
{
162
0
  const struct Folder *folder = data;
163
164
0
  if (!folder->ff->local)
165
0
    return 0;
166
167
0
  return folder->ff->mtime;
168
0
}
169
170
/**
171
 * folder_description - Browser: Description - Implements ::get_string_t - @ingroup expando_get_string_api
172
 */
173
static void folder_description(const struct ExpandoNode *node, void *data,
174
                               MuttFormatFlags flags, struct Buffer *buf)
175
0
{
176
0
  const struct Folder *folder = data;
177
178
0
  const char *s = NULL;
179
0
  if (folder->ff->desc)
180
0
    s = folder->ff->desc;
181
0
  else
182
0
    s = folder->ff->name;
183
184
0
  buf_printf(buf, "%s%s", s,
185
0
             folder->ff->local ?
186
0
                 (S_ISLNK(folder->ff->mode) ?
187
0
                      "@" :
188
0
                      (S_ISDIR(folder->ff->mode) ?
189
0
                           "/" :
190
0
                           (((folder->ff->mode & S_IXUSR) != 0) ? "*" : ""))) :
191
0
                 "");
192
0
}
193
194
/**
195
 * folder_filename - Browser: Filename - Implements ::get_string_t - @ingroup expando_get_string_api
196
 */
197
static void folder_filename(const struct ExpandoNode *node, void *data,
198
                            MuttFormatFlags flags, struct Buffer *buf)
199
0
{
200
0
  const struct Folder *folder = data;
201
202
0
  const char *s = NONULL(folder->ff->name);
203
204
0
  buf_printf(buf, "%s%s", s,
205
0
             folder->ff->local ?
206
0
                 (S_ISLNK(folder->ff->mode) ?
207
0
                      "@" :
208
0
                      (S_ISDIR(folder->ff->mode) ?
209
0
                           "/" :
210
0
                           (((folder->ff->mode & S_IXUSR) != 0) ? "*" : ""))) :
211
0
                 "");
212
0
}
213
214
/**
215
 * folder_file_group - Browser: Group name - Implements ::get_string_t - @ingroup expando_get_string_api
216
 */
217
static void folder_file_group(const struct ExpandoNode *node, void *data,
218
                              MuttFormatFlags flags, struct Buffer *buf)
219
0
{
220
0
  const struct Folder *folder = data;
221
0
  if (!folder->ff->local)
222
0
    return;
223
224
0
  struct group *gr = getgrgid(folder->ff->gid);
225
0
  if (gr)
226
0
  {
227
0
    buf_addstr(buf, gr->gr_name);
228
0
  }
229
0
  else
230
0
  {
231
0
    buf_printf(buf, "%u", folder->ff->gid);
232
0
  }
233
0
}
234
235
/**
236
 * folder_file_mode - Browser: File permissions - Implements ::get_string_t - @ingroup expando_get_string_api
237
 */
238
static void folder_file_mode(const struct ExpandoNode *node, void *data,
239
                             MuttFormatFlags flags, struct Buffer *buf)
240
0
{
241
0
  const struct Folder *folder = data;
242
243
0
  if (folder->ff->local)
244
0
  {
245
0
    buf_printf(buf, "%c%c%c%c%c%c%c%c%c%c",
246
0
               S_ISDIR(folder->ff->mode) ? 'd' : (S_ISLNK(folder->ff->mode) ? 'l' : '-'),
247
0
               ((folder->ff->mode & S_IRUSR) != 0) ? 'r' : '-',
248
0
               ((folder->ff->mode & S_IWUSR) != 0) ? 'w' : '-',
249
0
               ((folder->ff->mode & S_ISUID) != 0) ? 's' :
250
0
               ((folder->ff->mode & S_IXUSR) != 0) ? 'x' :
251
0
                                                     '-',
252
0
               ((folder->ff->mode & S_IRGRP) != 0) ? 'r' : '-',
253
0
               ((folder->ff->mode & S_IWGRP) != 0) ? 'w' : '-',
254
0
               ((folder->ff->mode & S_ISGID) != 0) ? 's' :
255
0
               ((folder->ff->mode & S_IXGRP) != 0) ? 'x' :
256
0
                                                     '-',
257
0
               ((folder->ff->mode & S_IROTH) != 0) ? 'r' : '-',
258
0
               ((folder->ff->mode & S_IWOTH) != 0) ? 'w' : '-',
259
0
               ((folder->ff->mode & S_ISVTX) != 0) ? 't' :
260
0
               ((folder->ff->mode & S_IXOTH) != 0) ? 'x' :
261
0
                                                     '-');
262
0
  }
263
0
  else if (folder->ff->imap)
264
0
  {
265
    /* mark folders with subfolders AND mail */
266
0
    buf_printf(buf, "IMAP %c", (folder->ff->inferiors && folder->ff->selectable) ? '+' : ' ');
267
0
  }
268
0
}
269
270
/**
271
 * folder_file_owner - Browser: Owner name - Implements ::get_string_t - @ingroup expando_get_string_api
272
 */
273
static void folder_file_owner(const struct ExpandoNode *node, void *data,
274
                              MuttFormatFlags flags, struct Buffer *buf)
275
0
{
276
0
  const struct Folder *folder = data;
277
0
  if (!folder->ff->local)
278
0
    return;
279
280
0
  struct passwd *pw = getpwuid(folder->ff->uid);
281
0
  if (pw)
282
0
  {
283
0
    buf_addstr(buf, pw->pw_name);
284
0
  }
285
0
  else
286
0
  {
287
0
    buf_printf(buf, "%u", folder->ff->uid);
288
0
  }
289
0
}
290
291
/**
292
 * folder_file_size - Browser: Size in bytes - Implements ::get_string_t - @ingroup expando_get_string_api
293
 */
294
static void folder_file_size(const struct ExpandoNode *node, void *data,
295
                             MuttFormatFlags flags, struct Buffer *buf)
296
0
{
297
0
  const struct Folder *folder = data;
298
299
0
  mutt_str_pretty_size(buf, folder->ff->size);
300
0
}
301
302
/**
303
 * folder_file_size_num - Browser: Size in bytes - Implements ::get_number_t - @ingroup expando_get_number_api
304
 */
305
static long folder_file_size_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
306
0
{
307
0
  const struct Folder *folder = data;
308
0
  return folder->ff->size;
309
0
}
310
311
/**
312
 * folder_hard_links - Browser: Hard links - Implements ::get_string_t - @ingroup expando_get_string_api
313
 */
314
static void folder_hard_links(const struct ExpandoNode *node, void *data,
315
                              MuttFormatFlags flags, struct Buffer *buf)
316
0
{
317
0
  const struct Folder *folder = data;
318
0
  if (!folder->ff->local)
319
0
    return;
320
321
0
  buf_add_printf(buf, "%d", (int) folder->ff->nlink);
322
0
}
323
324
/**
325
 * folder_hard_links_num - Browser: Hard links - Implements ::get_number_t - @ingroup expando_get_number_api
326
 */
327
static long folder_hard_links_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
328
0
{
329
0
  const struct Folder *folder = data;
330
331
0
  if (folder->ff->local)
332
0
    return folder->ff->nlink;
333
334
0
  return 0;
335
0
}
336
337
/**
338
 * folder_message_count - Browser: Number of messages - Implements ::get_string_t - @ingroup expando_get_string_api
339
 */
340
static void folder_message_count(const struct ExpandoNode *node, void *data,
341
                                 MuttFormatFlags flags, struct Buffer *buf)
342
0
{
343
0
  const struct Folder *folder = data;
344
0
  if (!folder->ff->has_mailbox)
345
0
    return;
346
347
0
  buf_add_printf(buf, "%d", folder->ff->msg_count);
348
0
}
349
350
/**
351
 * folder_message_count_num - Browser: Number of messages - Implements ::get_number_t - @ingroup expando_get_number_api
352
 */
353
static long folder_message_count_num(const struct ExpandoNode *node, void *data,
354
                                     MuttFormatFlags flags)
355
0
{
356
0
  const struct Folder *folder = data;
357
358
0
  if (folder->ff->has_mailbox)
359
0
    return folder->ff->msg_count;
360
361
0
  return 0;
362
0
}
363
364
/**
365
 * folder_new_mail - Browser: New mail flag - Implements ::get_string_t - @ingroup expando_get_string_api
366
 */
367
static void folder_new_mail(const struct ExpandoNode *node, void *data,
368
                            MuttFormatFlags flags, struct Buffer *buf)
369
0
{
370
0
  const struct Folder *folder = data;
371
372
  // NOTE(g0mb4): use $to_chars?
373
0
  const char *s = folder->ff->has_new_mail ? "N" : " ";
374
0
  buf_strcpy(buf, s);
375
0
}
376
377
/**
378
 * folder_new_mail_num - Browser: New mail flag - Implements ::get_number_t - @ingroup expando_get_number_api
379
 */
380
static long folder_new_mail_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
381
0
{
382
0
  const struct Folder *folder = data;
383
0
  return folder->ff->has_new_mail;
384
0
}
385
386
/**
387
 * folder_notify_num - Browser: Alert for new mail - Implements ::get_number_t - @ingroup expando_get_number_api
388
 */
389
static long folder_notify_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
390
0
{
391
0
  const struct Folder *folder = data;
392
393
0
  return folder->ff->notify_user;
394
0
}
395
396
/**
397
 * folder_number_num - Browser: Index number - Implements ::get_number_t - @ingroup expando_get_number_api
398
 */
399
static long folder_number_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
400
0
{
401
0
  const struct Folder *folder = data;
402
403
0
  return folder->num + 1;
404
0
}
405
406
/**
407
 * folder_poll_num - Browser: Poll for new mail - Implements ::get_number_t - @ingroup expando_get_number_api
408
 */
409
static long folder_poll_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
410
0
{
411
0
  const struct Folder *folder = data;
412
413
0
  return folder->ff->poll_new_mail;
414
0
}
415
416
/**
417
 * folder_tagged - Browser: Is Tagged - Implements ::get_string_t - @ingroup expando_get_string_api
418
 */
419
static void folder_tagged(const struct ExpandoNode *node, void *data,
420
                          MuttFormatFlags flags, struct Buffer *buf)
421
0
{
422
0
  const struct Folder *folder = data;
423
424
  // NOTE(g0mb4): use $to_chars?
425
0
  const char *s = folder->ff->tagged ? "*" : " ";
426
0
  buf_strcpy(buf, s);
427
0
}
428
429
/**
430
 * folder_tagged_num - Browser: Is Tagged - Implements ::get_number_t - @ingroup expando_get_number_api
431
 */
432
static long folder_tagged_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
433
0
{
434
0
  const struct Folder *folder = data;
435
0
  return folder->ff->tagged;
436
0
}
437
438
/**
439
 * folder_unread_count - Browser: Number of unread messages - Implements ::get_string_t - @ingroup expando_get_string_api
440
 */
441
static void folder_unread_count(const struct ExpandoNode *node, void *data,
442
                                MuttFormatFlags flags, struct Buffer *buf)
443
0
{
444
0
  const struct Folder *folder = data;
445
0
  if (!folder->ff->has_mailbox)
446
0
    return;
447
448
0
  buf_add_printf(buf, "%d", folder->ff->msg_unread);
449
0
}
450
451
/**
452
 * folder_unread_count_num - Browser: Number of unread messages - Implements ::get_number_t - @ingroup expando_get_number_api
453
 */
454
static long folder_unread_count_num(const struct ExpandoNode *node, void *data,
455
                                    MuttFormatFlags flags)
456
0
{
457
0
  const struct Folder *folder = data;
458
459
0
  if (folder->ff->has_mailbox)
460
0
    return folder->ff->msg_unread;
461
462
0
  return 0;
463
0
}
464
465
/**
466
 * global_padding_space - Fixed whitespace - Implements ::get_string_t - @ingroup expando_get_string_api
467
 */
468
static void global_padding_space(const struct ExpandoNode *node, void *data,
469
                                 MuttFormatFlags flags, struct Buffer *buf)
470
0
{
471
0
  buf_addstr(buf, " ");
472
0
}
473
474
/**
475
 * FolderRenderCallbacks - Callbacks for Browser Expandos
476
 *
477
 * @sa FolderFormatDef, ExpandoDataFolder, ExpandoDataGlobal
478
 */
479
const struct ExpandoRenderCallback FolderRenderCallbacks[] = {
480
  // clang-format off
481
  { ED_FOLDER, ED_FOL_DATE,          folder_date,          folder_date_num },
482
  { ED_FOLDER, ED_FOL_DATE_FORMAT,   folder_date_format,   folder_date_format_num },
483
  { ED_FOLDER, ED_FOL_DATE_STRF,     folder_date_strf,     folder_date_strf_num },
484
  { ED_FOLDER, ED_FOL_DESCRIPTION,   folder_description,   NULL },
485
  { ED_FOLDER, ED_FOL_FILENAME,      folder_filename,      NULL },
486
  { ED_FOLDER, ED_FOL_FILE_GROUP,    folder_file_group,    NULL },
487
  { ED_FOLDER, ED_FOL_FILE_MODE,     folder_file_mode,     NULL },
488
  { ED_FOLDER, ED_FOL_FILE_OWNER,    folder_file_owner,    NULL },
489
  { ED_FOLDER, ED_FOL_FILE_SIZE,     folder_file_size,     folder_file_size_num },
490
  { ED_FOLDER, ED_FOL_HARD_LINKS,    folder_hard_links,    folder_hard_links_num },
491
  { ED_FOLDER, ED_FOL_MESSAGE_COUNT, folder_message_count, folder_message_count_num },
492
  { ED_FOLDER, ED_FOL_NEW_MAIL,      folder_new_mail,      folder_new_mail_num },
493
  { ED_FOLDER, ED_FOL_NOTIFY,        NULL,                 folder_notify_num },
494
  { ED_FOLDER, ED_FOL_NUMBER,        NULL,                 folder_number_num },
495
  { ED_FOLDER, ED_FOL_POLL,          NULL,                 folder_poll_num },
496
  { ED_FOLDER, ED_FOL_TAGGED,        folder_tagged,        folder_tagged_num },
497
  { ED_FOLDER, ED_FOL_UNREAD_COUNT,  folder_unread_count,  folder_unread_count_num },
498
  { ED_GLOBAL, ED_GLO_PADDING_SPACE, global_padding_space, NULL },
499
  { -1, -1, NULL, NULL },
500
  // clang-format on
501
};