Line data Source code
1 : /**
2 : * @file main_ro.c
3 : * @brief Entry point for email-cli-ro — read-only, non-interactive CLI.
4 : *
5 : * email-cli-ro is a strict subset of email-cli:
6 : * - All output is batch/non-interactive (no TUI, no pager prompts).
7 : * - No write operations (no SMTP, no IMAP flag changes, no cron writes).
8 : * - No setup wizard — configuration must already exist.
9 : * - Safe to give to AI agents: there is no code path that sends email.
10 : *
11 : * Supported commands: list, show, folders, attachments, save-attachment, help.
12 : */
13 :
14 : #include <stdio.h>
15 : #include <stdlib.h>
16 : #include <string.h>
17 : #include <ctype.h>
18 : #include <locale.h>
19 : #include "config_store.h"
20 : #include "email_service.h"
21 : #include "platform/terminal.h"
22 : #include "platform/path.h"
23 : #include "raii.h"
24 : #include "logger.h"
25 : #include "local_store.h"
26 : #include "fs_util.h"
27 : #include "config.h"
28 :
29 : #define BATCH_DEFAULT_LIMIT 100
30 :
31 : /* ── Help pages ──────────────────────────────────────────────────────── */
32 :
33 3 : static void help_general(void) {
34 3 : printf(
35 : "Usage: email-cli-ro [<account>] <command> [options]\n"
36 : "\n"
37 : "Read-only email CLI. All output is non-interactive (batch mode).\n"
38 : "Safe for use by AI agents: no send or write operations are available.\n"
39 : "\n"
40 : " <account> Email address of the account to use (e.g. user@example.com).\n"
41 : " Required when multiple accounts are configured.\n"
42 : " Alternative: --account <email>.\n"
43 : "\n"
44 : "Reading:\n"
45 : " list List messages in the configured mailbox\n"
46 : " show <uid> Display the full content of a message\n"
47 : " list-folders List available IMAP folders / Gmail labels\n"
48 : " list-labels List all labels (Gmail) or folders (IMAP)\n"
49 : " list-attachments <uid> List attachments in a message\n"
50 : " save-attachment <uid> <filename> [dir]\n"
51 : " Save an attachment to disk\n"
52 : "\n"
53 : "Account management:\n"
54 : " list-accounts List all configured accounts\n"
55 : "\n"
56 : "Help:\n"
57 : " help [command] Show this help, or detailed help for a command\n"
58 : "\n"
59 : "Run 'email-cli-ro help <command>' for more information.\n"
60 : "For write operations (send, mark-read, add-label, etc.) use 'email-cli'.\n"
61 : );
62 3 : }
63 :
64 1 : static void help_list(void) {
65 1 : printf(
66 : "Usage: email-cli-ro [<account>] list [options]\n"
67 : "\n"
68 : "Lists messages in the configured mailbox.\n"
69 : "Shows unread (UNSEEN) messages by default; use --all for everything.\n"
70 : "\n"
71 : "Options:\n"
72 : " --all Show all messages (not just unread).\n"
73 : " --folder <name> IMAP: use <name> instead of the configured folder.\n"
74 : " --label <id-or-name> Gmail: filter by label (alias for --folder).\n"
75 : " --limit <n> Show at most <n> messages (default: %d).\n"
76 : " --offset <n> Start listing from the <n>-th message (1-based).\n"
77 : "\n"
78 : "Gmail notes:\n"
79 : " Use 'email-cli-ro list-labels' to see available labels and their IDs.\n"
80 : " Predefined labels: INBOX, SENT, DRAFT, SPAM, TRASH, STARRED, IMPORTANT.\n"
81 : "\n"
82 : "Examples (IMAP):\n"
83 : " email-cli-ro list\n"
84 : " email-cli-ro list --folder INBOX.Sent --limit 50\n"
85 : "\n"
86 : "Examples (Gmail):\n"
87 : " email-cli-ro list\n"
88 : " email-cli-ro list --label INBOX\n"
89 : " email-cli-ro list --label SENT\n"
90 : " email-cli-ro user@gmail.com list --label Label_42\n",
91 : BATCH_DEFAULT_LIMIT
92 : );
93 1 : }
94 :
95 3 : static void help_show(void) {
96 3 : printf(
97 : "Usage: email-cli-ro show <uid>\n"
98 : "\n"
99 : "Displays the full content of the message identified by <uid>.\n"
100 : "\n"
101 : " <uid> Numeric IMAP UID shown by 'email-cli-ro list'\n"
102 : "\n"
103 : "The message is fetched from the server on first access and stored\n"
104 : "locally at ~/.local/share/email-cli/messages/<folder>/<uid>.eml.\n"
105 : "Subsequent reads are served from the local store.\n"
106 : );
107 3 : }
108 :
109 1 : static void help_folders(void) {
110 1 : printf(
111 : "Usage: email-cli-ro list-folders [options]\n"
112 : "\n"
113 : "Lists all available IMAP folders on the server.\n"
114 : "\n"
115 : "Options:\n"
116 : " --tree Render the folder hierarchy as a tree.\n"
117 : "\n"
118 : "Examples:\n"
119 : " email-cli-ro list-folders\n"
120 : " email-cli-ro list-folders --tree\n"
121 : );
122 1 : }
123 :
124 3 : static void help_attachments(void) {
125 3 : printf(
126 : "Usage: email-cli-ro list-attachments <uid>\n"
127 : "\n"
128 : "Lists all attachments in the message identified by <uid>.\n"
129 : "Prints one line per attachment: filename and decoded size.\n"
130 : "\n"
131 : " <uid> Numeric IMAP UID shown by 'email-cli-ro list'\n"
132 : "\n"
133 : "Examples:\n"
134 : " email-cli-ro list-attachments 42\n"
135 : );
136 3 : }
137 :
138 3 : static void help_save_attachment(void) {
139 3 : printf(
140 : "Usage: email-cli-ro save-attachment <uid> <filename> [dir]\n"
141 : "\n"
142 : "Saves the named attachment from message <uid> to disk.\n"
143 : "\n"
144 : " <uid> Numeric IMAP UID shown by 'email-cli-ro list'\n"
145 : " <filename> Exact attachment filename shown by 'email-cli-ro list-attachments'\n"
146 : " [dir] Destination directory (default: ~/Downloads or ~)\n"
147 : "\n"
148 : "Examples:\n"
149 : " email-cli-ro save-attachment 42 report.pdf\n"
150 : " email-cli-ro save-attachment 42 report.pdf /tmp\n"
151 : );
152 3 : }
153 :
154 2 : static void help_list_labels(void) {
155 2 : printf(
156 : "Usage: email-cli-ro list-labels\n"
157 : "\n"
158 : "List all available labels (Gmail) or folders (IMAP).\n"
159 : "For Gmail, shows both the display name and the label ID.\n"
160 : "\n"
161 : "Examples:\n"
162 : " email-cli-ro list-labels\n"
163 : );
164 2 : }
165 :
166 2 : static void help_list_accounts(void) {
167 2 : printf(
168 : "Usage: email-cli-ro list-accounts\n"
169 : "\n"
170 : "List all configured accounts with their type and server.\n"
171 : "\n"
172 : "Examples:\n"
173 : " email-cli-ro list-accounts\n"
174 : );
175 2 : }
176 :
177 : /* ── Helpers ─────────────────────────────────────────────────────────── */
178 :
179 21 : static int parse_uid(const char *s, char uid_out[17]) {
180 21 : if (!s || !*s) return -1;
181 : /* Accept 16-character hex strings directly (Gmail message IDs shown by list). */
182 21 : if (strlen(s) == 16) {
183 12 : int all_hex = 1;
184 204 : for (int i = 0; i < 16; i++) {
185 192 : if (!isxdigit((unsigned char)s[i])) { all_hex = 0; break; }
186 : }
187 12 : if (all_hex) {
188 12 : memcpy(uid_out, s, 16);
189 12 : uid_out[16] = '\0';
190 12 : return 0;
191 : }
192 : }
193 : /* Accept positive decimal integers (IMAP UIDs). */
194 : char *end;
195 9 : unsigned long v = strtoul(s, &end, 10);
196 9 : if (*end != '\0' || v == 0 || v > 4294967295UL) return -1;
197 6 : snprintf(uid_out, 17, "%016lu", v);
198 6 : return 0;
199 : }
200 :
201 2 : static void unknown_option(const char *cmd, const char *opt) {
202 2 : fprintf(stderr, "Unknown option '%s' for '%s'.\n", opt, cmd);
203 2 : fprintf(stderr, "Run 'email-cli-ro help %s' for usage.\n", cmd);
204 2 : }
205 :
206 : /* ── Entry point ─────────────────────────────────────────────────────── */
207 :
208 : #ifndef EMAIL_CLI_VERSION
209 : #define EMAIL_CLI_VERSION "unknown"
210 : #endif
211 :
212 76 : int main(int argc, char *argv[]) {
213 76 : setlocale(LC_ALL, "");
214 :
215 76 : if (argc >= 2 && (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)) {
216 1 : printf("email-cli-ro %s\n", EMAIL_CLI_VERSION);
217 1 : return EXIT_SUCCESS;
218 : }
219 :
220 : /* 1. Determine cache directory for logs */
221 75 : const char *cache_base = platform_cache_dir();
222 75 : if (!cache_base) {
223 0 : fprintf(stderr, "Fatal: Could not determine cache directory.\n");
224 0 : return EXIT_FAILURE;
225 : }
226 :
227 75 : RAII_STRING char *log_dir = NULL;
228 75 : RAII_STRING char *log_file = NULL;
229 150 : if (asprintf(&log_dir, "%s/email-cli/logs", cache_base) == -1 ||
230 75 : asprintf(&log_file, "%s/session.log", log_dir) == -1) {
231 0 : fprintf(stderr, "Fatal: Memory allocation failed.\n");
232 0 : return EXIT_FAILURE;
233 : }
234 :
235 : /* 2. Account + command detection (mirrors main.c logic).
236 : * Supported forms:
237 : * email-cli-ro [<account>] <command> [options]
238 : * email-cli-ro --account <email> <command> [options] */
239 75 : const char *account_arg = NULL;
240 75 : int account_arg_idx = -1;
241 :
242 : /* Pass A: scan for --account flag anywhere in args */
243 279 : for (int i = 1; i < argc; i++) {
244 204 : if (strcmp(argv[i], "--account") == 0 && i + 1 < argc) {
245 2 : account_arg = argv[++i]; continue;
246 : }
247 : }
248 :
249 : /* Pass B: if no --account flag, check whether first positional arg is an email */
250 75 : if (!account_arg) {
251 74 : for (int i = 1; i < argc; i++) {
252 72 : if (argv[i][0] == '-') {
253 1 : if (strcmp(argv[i], "--account") == 0) i++;
254 1 : continue;
255 : }
256 71 : if (strchr(argv[i], '@')) {
257 24 : account_arg = argv[i];
258 24 : account_arg_idx = i;
259 : }
260 71 : break;
261 : }
262 : }
263 :
264 : /* Command: first non-flag, non-account arg */
265 75 : const char *cmd = NULL;
266 75 : int cmd_idx = 0;
267 118 : for (int i = 1; i < argc; i++) {
268 116 : if (strcmp(argv[i], "--help") == 0) continue;
269 115 : if (strcmp(argv[i], "--account") == 0) { i++; continue; }
270 113 : if (strcmp(argv[i], "--batch") == 0) continue; /* no-op: always batch */
271 97 : if (i == account_arg_idx) continue;
272 73 : cmd = argv[i]; cmd_idx = i; break;
273 : }
274 :
275 : /* --help anywhere: treat as "help <cmd>" */
276 273 : for (int i = 1; i < argc; i++) {
277 206 : if (strcmp(argv[i], "--help") == 0) {
278 8 : if (cmd && strcmp(cmd, "--help") != 0) {
279 7 : if (strcmp(cmd, "list") == 0) { help_list(); return EXIT_SUCCESS; }
280 6 : if (strcmp(cmd, "show") == 0) { help_show(); return EXIT_SUCCESS; }
281 5 : if (strcmp(cmd, "list-folders") == 0) { help_folders(); return EXIT_SUCCESS; }
282 4 : if (strcmp(cmd, "list-attachments") == 0) { help_attachments(); return EXIT_SUCCESS; }
283 3 : if (strcmp(cmd, "save-attachment") == 0) { help_save_attachment(); return EXIT_SUCCESS; }
284 2 : if (strcmp(cmd, "list-labels") == 0) { help_list_labels(); return EXIT_SUCCESS; }
285 1 : if (strcmp(cmd, "list-accounts") == 0) { help_list_accounts(); return EXIT_SUCCESS; }
286 : }
287 1 : help_general();
288 1 : return EXIT_SUCCESS;
289 : }
290 : }
291 :
292 67 : if (cmd && strcmp(cmd, "help") == 0) {
293 7 : const char *topic = NULL;
294 7 : for (int i = cmd_idx + 1; i < argc; i++) { topic = argv[i]; break; }
295 7 : if (topic) {
296 6 : if (strcmp(topic, "list") == 0) { help_list(); return EXIT_SUCCESS; }
297 6 : if (strcmp(topic, "show") == 0) { help_show(); return EXIT_SUCCESS; }
298 5 : if (strcmp(topic, "list-folders") == 0) { help_folders(); return EXIT_SUCCESS; }
299 5 : if (strcmp(topic, "list-attachments") == 0) { help_attachments(); return EXIT_SUCCESS; }
300 4 : if (strcmp(topic, "save-attachment") == 0) { help_save_attachment(); return EXIT_SUCCESS; }
301 3 : if (strcmp(topic, "list-labels") == 0) { help_list_labels(); return EXIT_SUCCESS; }
302 2 : if (strcmp(topic, "list-accounts") == 0) { help_list_accounts(); return EXIT_SUCCESS; }
303 1 : fprintf(stderr, "Unknown command '%s'.\n", topic);
304 1 : fprintf(stderr, "Run 'email-cli-ro help' for available commands.\n");
305 1 : return EXIT_FAILURE;
306 : }
307 1 : help_general();
308 1 : return EXIT_SUCCESS;
309 : }
310 :
311 60 : if (!cmd) {
312 1 : help_general();
313 1 : return EXIT_SUCCESS;
314 : }
315 :
316 : /* 3. Initialize logger */
317 59 : if (fs_mkdir_p(log_dir, 0700) != 0)
318 0 : fprintf(stderr, "Warning: Could not create log directory %s\n", log_dir);
319 59 : if (logger_init(log_file, LOG_DEBUG) != 0)
320 0 : fprintf(stderr, "Warning: Logging system failed to initialize.\n");
321 59 : logger_log(LOG_INFO, "--- email-cli-ro starting (cmd: %s) ---", cmd);
322 :
323 : /* 4. Load configuration — no wizard: must already exist */
324 59 : Config *cfg = NULL;
325 59 : if (strcmp(cmd, "list-accounts") != 0) {
326 56 : if (account_arg) {
327 26 : cfg = config_load_account(account_arg);
328 26 : if (!cfg) {
329 1 : fprintf(stderr,
330 : "Error: Account '%s' not found.\n"
331 : "Run 'email-cli-ro list-accounts' to list configured accounts.\n",
332 : account_arg);
333 1 : logger_close();
334 1 : return EXIT_FAILURE;
335 : }
336 : } else {
337 30 : int count = 0;
338 30 : AccountEntry *list = config_list_accounts(&count);
339 30 : if (count == 1) {
340 29 : cfg = list[0].cfg; list[0].cfg = NULL;
341 29 : config_free_account_list(list, count);
342 1 : } else if (count > 1) {
343 1 : fprintf(stderr, "Multiple accounts configured. Specify which to use:\n");
344 3 : for (int i = 0; i < count; i++)
345 4 : fprintf(stderr, " email-cli-ro %s %s\n",
346 2 : list[i].name ? list[i].name : "?", cmd ? cmd : "");
347 1 : fprintf(stderr, "Run 'email-cli-ro list-accounts' for the full list.\n");
348 1 : config_free_account_list(list, count);
349 1 : logger_close();
350 1 : return EXIT_FAILURE;
351 : } else {
352 0 : config_free_account_list(list, count);
353 0 : fprintf(stderr,
354 : "Error: No configuration found.\n"
355 : "Run 'email-cli' once to complete the setup wizard.\n");
356 0 : logger_close();
357 0 : return EXIT_FAILURE;
358 : }
359 : }
360 : }
361 :
362 : /* 5. Initialize local store */
363 57 : if (cfg && local_store_init(cfg->host, cfg->user) != 0)
364 0 : logger_log(LOG_WARN, "Failed to initialize local store for %s", cfg->host);
365 :
366 : /* 6. Dispatch — batch mode only (pager = 0) */
367 57 : int result = -1;
368 :
369 57 : if (strcmp(cmd, "list") == 0) {
370 21 : EmailListOpts opts = {0, NULL, BATCH_DEFAULT_LIMIT, 0, 0, {0}, {0}};
371 21 : int ok = 1;
372 47 : for (int i = cmd_idx + 1; i < argc && ok; i++) {
373 26 : if (strcmp(argv[i], "--batch") == 0) {
374 : /* accepted as no-op: email-cli-ro is always batch mode */
375 22 : } else if (strcmp(argv[i], "--all") == 0) {
376 6 : opts.all = 1;
377 16 : } else if (strcmp(argv[i], "--folder") == 0 ||
378 14 : strcmp(argv[i], "--label") == 0) {
379 3 : if (i + 1 >= argc) {
380 1 : fprintf(stderr, "Error: %s requires a name.\n", argv[i]);
381 1 : ok = 0;
382 : } else {
383 2 : opts.folder = argv[++i];
384 : }
385 13 : } else if (strcmp(argv[i], "--limit") == 0) {
386 9 : if (i + 1 >= argc) {
387 1 : fprintf(stderr, "Error: --limit requires a number.\n");
388 1 : ok = 0;
389 : } else {
390 : char *end;
391 8 : long v = strtol(argv[++i], &end, 10);
392 8 : if (*end != '\0' || v <= 0) {
393 1 : fprintf(stderr, "Error: --limit must be a positive integer.\n");
394 1 : ok = 0;
395 : } else {
396 7 : opts.limit = (int)v;
397 : }
398 : }
399 4 : } else if (strcmp(argv[i], "--offset") == 0) {
400 3 : if (i + 1 >= argc) {
401 1 : fprintf(stderr, "Error: --offset requires a number.\n");
402 1 : ok = 0;
403 : } else {
404 : char *end;
405 2 : long v = strtol(argv[++i], &end, 10);
406 2 : if (*end != '\0' || v < 1) {
407 1 : fprintf(stderr, "Error: --offset must be a positive integer.\n");
408 1 : ok = 0;
409 : } else {
410 1 : opts.offset = (int)v;
411 : }
412 : }
413 : } else {
414 1 : unknown_option("list", argv[i]);
415 1 : ok = 0;
416 : }
417 : }
418 21 : if (ok) result = email_service_list(cfg, &opts);
419 :
420 36 : } else if (strcmp(cmd, "show") == 0) {
421 11 : const char *uid_str = NULL;
422 11 : for (int i = cmd_idx + 1; i < argc; i++) {
423 10 : if (strcmp(argv[i], "--batch") == 0) continue; /* no-op */
424 10 : uid_str = argv[i]; break;
425 : }
426 11 : if (!uid_str) {
427 1 : fprintf(stderr, "Error: 'show' requires a UID argument.\n");
428 1 : help_show();
429 : } else {
430 : char uid[17];
431 10 : if (parse_uid(uid_str, uid) != 0)
432 1 : fprintf(stderr,
433 : "Error: UID must be a positive integer (got '%s').\n",
434 : uid_str);
435 : else
436 9 : result = email_service_read(cfg, uid, 0, BATCH_DEFAULT_LIMIT);
437 : }
438 :
439 25 : } else if (strcmp(cmd, "list-folders") == 0) {
440 4 : int tree = 0, ok = 1;
441 8 : for (int i = cmd_idx + 1; i < argc && ok; i++) {
442 4 : if (strcmp(argv[i], "--batch") == 0) { /* no-op */
443 2 : } else if (strcmp(argv[i], "--tree") == 0)
444 1 : tree = 1;
445 1 : else { unknown_option("list-folders", argv[i]); ok = 0; }
446 : }
447 4 : if (ok) result = email_service_list_folders(cfg, tree);
448 :
449 21 : } else if (strcmp(cmd, "list-attachments") == 0) {
450 7 : const char *uid_str = NULL;
451 7 : for (int i = cmd_idx + 1; i < argc; i++) {
452 6 : if (strcmp(argv[i], "--batch") == 0) continue;
453 6 : uid_str = argv[i]; break;
454 : }
455 7 : if (!uid_str) {
456 1 : fprintf(stderr, "Error: 'list-attachments' requires a UID argument.\n");
457 1 : help_attachments();
458 : } else {
459 : char uid[17];
460 6 : if (parse_uid(uid_str, uid) != 0)
461 1 : fprintf(stderr,
462 : "Error: UID must be a positive integer (got '%s').\n",
463 : uid_str);
464 : else
465 5 : result = email_service_list_attachments(cfg, uid);
466 : }
467 :
468 14 : } else if (strcmp(cmd, "save-attachment") == 0) {
469 6 : const char *uid_str = NULL;
470 6 : const char *filename = NULL;
471 6 : const char *outdir = NULL;
472 6 : int argn = 0;
473 20 : for (int i = cmd_idx + 1; i < argc; i++) {
474 14 : if (strcmp(argv[i], "--batch") == 0) continue;
475 14 : if (argn == 0) { uid_str = argv[i]; argn++; }
476 9 : else if (argn == 1) { filename = argv[i]; argn++; }
477 4 : else if (argn == 2) { outdir = argv[i]; argn++; }
478 : }
479 6 : if (!uid_str || !filename) {
480 1 : fprintf(stderr,
481 : "Error: 'save-attachment' requires a UID and a filename.\n");
482 1 : help_save_attachment();
483 : } else {
484 : char uid[17];
485 5 : if (parse_uid(uid_str, uid) != 0)
486 1 : fprintf(stderr,
487 : "Error: UID must be a positive integer (got '%s').\n",
488 : uid_str);
489 : else
490 4 : result = email_service_save_attachment(cfg, uid, filename, outdir);
491 : }
492 :
493 8 : } else if (strcmp(cmd, "list-labels") == 0) {
494 3 : result = email_service_list_labels(cfg);
495 :
496 5 : } else if (strcmp(cmd, "list-accounts") == 0) {
497 3 : int count = 0;
498 3 : AccountEntry *accs = config_list_accounts(&count);
499 3 : if (count == 0) {
500 1 : printf("No accounts configured.\n");
501 1 : result = 0;
502 : } else {
503 2 : printf("%-40s %-8s %s\n", "Account", "Type", "Server");
504 2 : printf("%-40s %-8s %s\n",
505 : "----------------------------------------",
506 : "--------",
507 : "----------------------------");
508 5 : for (int i = 0; i < count; i++) {
509 3 : const char *type = (accs[i].cfg && accs[i].cfg->gmail_mode) ? "Gmail" : "IMAP";
510 3 : const char *server = accs[i].cfg ? (accs[i].cfg->host ? accs[i].cfg->host : "-") : "-";
511 3 : printf("%-40s %-8s %s\n",
512 3 : accs[i].name ? accs[i].name : "?",
513 : type, server);
514 : }
515 2 : config_free_account_list(accs, count);
516 2 : result = 0;
517 : }
518 :
519 : } else {
520 : /* Check if the command is a write-only command blocked in ro mode */
521 : static const char *ro_blocked[] = {
522 : "mark-read", "mark-unread", "mark-starred", "remove-starred",
523 : "add-label", "remove-label", "create-label", "delete-label",
524 : "create-folder", "delete-folder",
525 : "mark-junk", "mark-notjunk",
526 : "add-account", "remove-account", NULL
527 : };
528 2 : int blocked = 0;
529 16 : for (int i = 0; ro_blocked[i]; i++) {
530 15 : if (strcmp(cmd, ro_blocked[i]) == 0) {
531 1 : fprintf(stderr, "Error: '%s' is not available in read-only mode (email-cli-ro).\n", cmd);
532 1 : fprintf(stderr, "Use 'email-cli' for write operations.\n");
533 1 : config_free(cfg);
534 1 : logger_log(LOG_INFO, "--- email-cli-ro session finished ---");
535 1 : logger_close();
536 1 : return EXIT_FAILURE;
537 : }
538 : }
539 1 : if (!blocked) {
540 1 : fprintf(stderr, "Unknown command '%s'.\n", cmd);
541 1 : fprintf(stderr, "Run 'email-cli-ro help' for available commands.\n");
542 : }
543 : }
544 :
545 : /* 7. Cleanup */
546 56 : config_free(cfg);
547 56 : logger_log(LOG_INFO, "--- email-cli-ro session finished ---");
548 56 : logger_close();
549 :
550 56 : if (result >= 0)
551 41 : return EXIT_SUCCESS;
552 15 : fprintf(stderr, "\nFailed. Check logs in %s\n", log_file);
553 15 : return EXIT_FAILURE;
554 : }
|