* Changes in the source code of Alpine to define internal prototypes
[alpine.git] / alpine / osdep / debuging.c
blob504ba077d4225302fc4cc030e417942df8140773
1 /*
2 * ========================================================================
3 * Copyright 2006-2008 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
14 #include <system.h> /* os-dep defs/includes */
15 #include <general.h> /* generally useful definitions */
17 #include "../../c-client/mail.h" /* for MAILSTREAM and friends */
18 #include "../../c-client/osdep.h"
19 #include "../../c-client/rfc822.h" /* for soutr_t and such */
20 #include "../../c-client/misc.h" /* for cpystr proto */
21 #include "../../c-client/utf8.h" /* for CHARSET and such*/
22 #include "../../c-client/imap4r1.h"
24 #include "../../pith/osdep/canaccess.h"
26 #include "../../pith/debug.h"
27 #include "../../pith/osdep/debugtime.h"
28 #include "../../pith/osdep/color.h"
29 #include "../../pith/osdep/bldpath.h"
30 #include "../../pith/osdep/rename.h"
31 #include "../../pith/osdep/filesize.h"
32 #include "../../pith/init.h"
33 #include "../../pith/status.h"
34 #include "../../pith/sort.h"
35 #include "../../pith/state.h"
36 #include "../../pith/msgno.h"
37 #include "../../pith/conf.h"
38 #include "../../pith/flag.h"
39 #include "../../pith/foldertype.h"
40 #include "../../pith/folder.h"
41 #include "../../pith/charconv/utf8.h"
42 #include "../../pith/charconv/filesys.h"
43 #include "../help.h"
45 #include "debuging.h"
48 #ifdef DEBUG
51 * globally visible
53 FILE *debugfile = NULL;
56 /*----------------------------------------------------------------------
57 Initialize debugging - open the debug log file
59 Args: none
61 Result: opens the debug logfile for dprints
63 Opens the file "~/.pine-debug1. Also maintains .pine-debug[2-4]
64 by renaming them each time so the last 4 sessions are saved.
65 ----*/
66 void
67 init_debug(void)
69 char nbuf[5];
70 char newfname[MAXPATH+1], filename[MAXPATH+1], *dfile = NULL;
71 char basename[MAXPATH+1];
72 #ifdef DEBUGFILEEXT
73 struct stat sbuf;
74 #endif /* DEBUGFILEEXT */
75 int i, fd;
77 if(!((debug || ps_global->debug_imap || ps_global->debug_tcp || ps_global->debug_http) && ps_global->write_debug_file))
78 return;
80 build_path(basename, ps_global->home_dir, DEBUGFILE, sizeof(basename));
81 #ifdef DEBUGFILEEXT
82 if (ps_global->debug_nfiles > 0) {
83 strncpy(filename, basename, sizeof(filename) - 1);
84 filename[sizeof(filename) - 1] = '\0';
85 strncat(filename, DEBUGFILEEXT, sizeof(filename) - 1 - strlen(filename));
86 snprintf(nbuf, sizeof(nbuf), "%d", ps_global->debug_nfiles);
87 strncat(filename, nbuf, sizeof(filename) - 1 - strlen(filename));
88 our_unlink(filename);
90 #endif /* DEBUGFILEEXT */
92 for(i = ps_global->debug_nfiles - 1; i > 0; i--){
93 strncpy(filename, basename, sizeof(filename)-1);
94 filename[sizeof(filename)-1] = '\0';
95 snprintf(nbuf, sizeof(nbuf), "%d", i);
96 strncat(filename, nbuf, sizeof(filename)-1-strlen(filename));
97 #ifdef DEBUGFILEEXT
98 strncat(filename, DEBUGFILEEXT, sizeof(filename)-1-strlen(filename));
99 if (our_stat(filename, &sbuf) < 0){
100 strncpy(filename, basename, sizeof(filename)-1);
101 filename[sizeof(filename)-1] = '\0';
102 strncat(filename, DEBUGFILEEXT, sizeof(filename)-1-strlen(filename));
103 strncat(filename, nbuf, sizeof(filename)-1-strlen(filename));
105 #endif /* DEBUGFILEEXT */
106 strncpy(newfname, basename, sizeof(newfname)-1);
107 newfname[sizeof(newfname)-1] = '\0';
108 snprintf(nbuf, sizeof(nbuf), "%d", i+1);
109 strncat(newfname, nbuf, sizeof(newfname)-1-strlen(newfname));
110 #ifdef DEBUGFILEEXT
111 strncat(newfname, DEBUGFILEEXT, sizeof(newfname)-1-strlen(newfname));
112 #endif /* DEBUGFILEEXT */
113 (void)rename_file(filename, newfname);
116 strncpy(filename, basename, sizeof(filename) - 1);
117 filename[sizeof(filename) - 1] = '\0';
118 strncat(filename, "1", sizeof(filename)-1-strlen(filename));
119 filename[sizeof(filename)-1] = '\0';
120 #ifdef DEBUGFILEEXT
121 strncat(filename, DEBUGFILEEXT, sizeof(filename) - 1 - strlen(filename));
122 #endif /* DEBUGFILEEXT */
124 debugfile = NULL;
125 dfile = filename;
127 * We were doing our_open so _WINDOWS would open it in widechar mode, but
128 * that means we have to print in widechar mode. For now just do open, since
129 * we it's a debug file and still readable. The alternative is copying all
130 * debug text to a widechar buffer, which would be a drag.
132 if((fd = open(filename, O_TRUNC|O_RDWR|O_CREAT, 0600)) >= 0)
133 debugfile = fdopen(fd, "w+");
135 if(debugfile != NULL){
136 char rev[128];
137 time_t now = time((time_t *)0);
138 if(ps_global->debug_flush)
139 setvbuf(debugfile, (char *)NULL, _IOLBF, BUFSIZ);
141 if(ps_global->debug_nfiles == 0){
143 * If no debug files are asked for, make filename a tempfile
144 * to be used for a record should pine later crash...
146 if(debug < 9 && !ps_global->debug_flush && ps_global->debug_imap<4){
147 our_unlink(filename);
148 dfile = NULL;
152 dprint((0, "Debug output of the Alpine program (debug=%d debug_imap=%d). Version %s (%s %s)\n%s\n",
153 debug, ps_global->debug_imap,
154 ALPINE_VERSION,
155 SYSTYPE ? SYSTYPE : "?",
156 get_alpine_revision_string(rev, sizeof(rev)),
157 ctime(&now)));
159 dprint((0, "Starting after the reading_pinerc calls, the data in this file should\nbe encoded as UTF-8. Before that it will be in the user's native charset.\n"));
160 if(dfile && (debug > DEFAULT_DEBUG ||
161 ps_global->debug_imap > 0 ||
162 ps_global->debug_tcp > 0 ||
163 ps_global->debug_http > 0)){
164 snprintf(newfname, sizeof(newfname), "Debug file: %s (level=%d imap=%d)", dfile,
165 debug, ps_global->debug_imap);
166 init_error(ps_global, SM_ORDER, 3, 5, newfname);
172 /*----------------------------------------------------------------------
173 Try to save the debug file if we crash in a controlled way
175 Args: dfile: pointer to open debug file
177 Result: tries to move the appropriate .pine-debugx file to .pine-crash
179 Looks through the four .pine-debug files hunting for the one that is
180 associated with this pine, and then renames it.
181 ----*/
182 void
183 save_debug_on_crash(FILE *dfile, int (*keystrokes) (int *, char *, size_t))
185 char nbuf[11], crashfile[MAXPATH+1], filename[MAXPATH+1];
186 int i;
187 struct stat dbuf, tbuf;
188 time_t now = time((time_t *)0);
190 if(!(dfile && fstat(fileno(dfile), &dbuf) == 0))
191 return;
193 fprintf(dfile, "save_debug_on_crash: Version %s: debug level %d",
194 ALPINE_VERSION, debug);
195 fprintf(dfile, "\n : %s\n", ctime(&now));
197 build_path(crashfile, ps_global->home_dir, ".pine-crash",sizeof(crashfile));
199 fprintf(dfile, "Attempting to save debug file to %s\n\n", crashfile);
200 fprintf(stderr,
201 "\n\n Attempting to save debug file to %s\n\n", crashfile);
203 /* Blat out last n keystrokes */
204 if(keystrokes){
205 int cval;
206 char cstr[256];
208 fputs("========== Latest Keystrokes =========================\n", dfile);
210 while((*keystrokes)(&cval, cstr, sizeof(cstr)) != -1){
211 fprintf(dfile, "\t%s\t(0x%4.4x)\n", cstr, cval);
215 fputs("========== Latest Keystrokes End =====================\n\n", dfile);
217 #ifdef DEBUGJOURNAL
218 fputs("========== Append DebugJournal =======================\n", dfile);
219 #else /* DEBUGJOURNAL */
220 fputs("========== Append Journal =======================\n", dfile);
221 #endif /* DEBUGJOURNAL */
222 debugjournal_to_file(dfile);
223 #ifdef DEBUGJOURNAL
224 fputs("========== Append DebugJournal End ===================\n", dfile);
225 #else /* DEBUGJOURNAL */
226 fputs("========== Append Journal End ===================\n", dfile);
227 #endif /* DEBUGJOURNAL */
229 /* look for existing debug file */
230 for(i = 1; i <= ps_global->debug_nfiles; i++){
231 build_path(filename, ps_global->home_dir, DEBUGFILE, sizeof(filename));
232 snprintf(nbuf, sizeof(nbuf), "%d", i);
233 strncat(filename, nbuf, sizeof(filename)-1-strlen(filename));
234 if(our_stat(filename, &tbuf) != 0)
235 continue;
237 /* This must be the current debug file */
238 if(tbuf.st_dev == dbuf.st_dev && tbuf.st_ino == dbuf.st_ino){
239 rename_file(filename, crashfile);
240 break;
244 /* if current debug file name not found, write it by hand */
245 if(i > ps_global->debug_nfiles){
246 FILE *cfp;
247 char buf[1025];
250 * Copy the debug temp file into the
252 if((cfp = our_fopen(crashfile, "wb")) != NULL){
253 buf[sizeof(buf)-1] = '\0';
254 fseek(dfile, 0L, 0);
255 while(fgets(buf, sizeof(buf)-1, dfile) && fputs(buf, cfp) != EOF)
258 fclose(cfp);
262 fclose(dfile);
267 * functions required by pith library
269 #define CHECK_EVERY_N_TIMES 100
270 #define MAX_DEBUG_FILE_SIZE 200000L
272 * This is just to catch runaway Pines that are looping spewing out
273 * debugging (and filling up a file system). The stop doesn't have to be
274 * at all precise, just soon enough to hopefully prevent filling the
275 * file system. If the debugging level is high (9 for now), then we're
276 * presumably looking for some problem, so don't truncate.
279 do_debug(FILE *debug_fp)
281 static int counter = CHECK_EVERY_N_TIMES;
282 static int ok = 1;
283 long filesize;
285 if(!debugfile)
286 return(0);
288 if(debug <= DEFAULT_DEBUG
289 && !ps_global->debug_flush
290 && !ps_global->debug_tcp
291 && !ps_global->debug_http
292 && !ps_global->debug_timestamp
293 && !ps_global->debug_imap
294 && ok
295 && --counter <= 0){
296 if((filesize = fp_file_size(debug_fp)) != -1L)
297 ok = (unsigned long)filesize < (unsigned long)MAX_DEBUG_FILE_SIZE;
299 counter = CHECK_EVERY_N_TIMES;
300 if(!ok){
301 fprintf(debug_fp, "\n\n --- No more debugging ---\n");
302 fprintf(debug_fp,
303 " (debug file growing too large - over %ld bytes)\n\n",
304 MAX_DEBUG_FILE_SIZE);
305 fflush(debug_fp);
309 if(ok && ps_global->debug_timestamp)
310 fprintf(debug_fp, "\n%s\n", debug_time(0, ps_global->debug_timestamp, ps_global->signal_in_progress));
312 return(ok);
315 void
316 output_debug_msg(int dlevel, char *fmt, ...)
318 va_list args;
320 if(debugfile && debug >= dlevel && do_debug(debugfile)){
321 int l;
323 va_start(args, fmt);
324 vfprintf(debugfile, fmt, args);
325 va_end(args);
327 if((l = strlen(fmt)) > 2 && fmt[l-1] != '\n')
328 fputc('\n', debugfile);
330 if(ps_global->debug_flush)
331 fflush(debugfile);
334 if(panicking())
335 return;
337 #ifdef DEBUGJOURNAL
339 if(dlevel <= 9 || dlevel <= debug){
341 * Make this static in order to move it off of
342 * the stack. We were getting "random" crashes
343 * on some systems when this size interacted with
344 * pthread stack size somehow. Taking it off of
345 * the stack ought to fix that without us having to
346 * understand how it all works.
348 static char b[64000];
350 va_start(args, fmt);
351 vsnprintf(b, sizeof(b), fmt, args);
352 va_end(args);
354 b[sizeof(b)-1] = '\0';
355 add_review_message(b, dlevel);
358 #endif /* DEBUGJOURNAL */
363 /*----------------------------------------------------------------------
364 Dump the whole config enchilada using the given function
366 Args: ps -- pine struct containing vars to dump
367 pc -- function to use to write the config data
368 brief -- brief dump, only dump user_vals
369 Result: command line, global, user var's written with given function
371 ----*/
372 void
373 dump_configuration(int brief)
375 gf_o_t pc;
377 if(!do_debug(debugfile))
378 return;
380 gf_set_writec(&pc, debugfile, 0L, FileStar, 0);
382 dump_config(ps_global, pc, brief);
386 void
387 dump_config(struct pine *ps, gf_o_t pc, int brief)
389 int i;
390 char quotes[3], tmp[MAILTMPLEN];
391 register struct variable *vars;
392 FEATURE_S *feat;
394 quotes[0] = '"'; quotes[1] = '"'; quotes[2] = '\0';
396 for(i = (brief ? 2 : 0); i < (brief ? 4 : 6); i++){
397 snprintf(tmp, sizeof(tmp), "======= %.20s_val options set",
398 (i == 0) ? "Current" :
399 (i == 1) ? "Command_line" :
400 (i == 2) ? "User" :
401 (i == 3) ? "PostloadUser" :
402 (i == 4) ? "Global"
403 : "Fixed");
404 gf_puts(tmp, pc);
405 if(i > 1){
406 snprintf(tmp, sizeof(tmp), " (%.100s)",
407 (i == 2) ? ((ps->prc) ? ps->prc->name : ".pinerc") :
408 (i == 3) ? ((ps->post_prc) ? ps->post_prc->name : "postload") :
409 (i == 4) ? ((ps->pconf) ? ps->pconf->name
410 : SYSTEM_PINERC) :
411 #if defined(DOS) || defined(OS2)
412 "NO FIXED"
413 #else
414 ((can_access(SYSTEM_PINERC_FIXED, ACCESS_EXISTS) == 0)
415 ? SYSTEM_PINERC_FIXED : "NO pine.conf.fixed")
416 #endif
418 gf_puts(tmp, pc);
421 gf_puts(" =======\n", pc);
422 for(vars = ps->vars; vars->name; vars++){
423 if(vars->is_list){
424 char **t;
425 t = (i == 0) ? vars->current_val.l :
426 (i == 1) ? vars->cmdline_val.l :
427 (i == 2) ? vars->main_user_val.l :
428 (i == 3) ? vars->post_user_val.l :
429 (i == 4) ? vars->global_val.l
430 : vars->fixed_val.l;
431 if(t && *t){ /* MAILTMPLEN = sizeof(tmp) */
432 snprintf(tmp, sizeof(tmp), " %20.20s : %.*s\n", vars->name,
433 MAILTMPLEN-26, **t ? *t : quotes);
434 gf_puts(tmp, pc);
435 while(++t && *t){
436 snprintf(tmp, sizeof(tmp)," %20.20s : %.*s\n","",
437 MAILTMPLEN-26, **t ? *t : quotes);
438 gf_puts(tmp, pc);
442 else{
443 char *t;
444 t = (i == 0) ? vars->current_val.p :
445 (i == 1) ? vars->cmdline_val.p :
446 (i == 2) ? vars->main_user_val.p :
447 (i == 3) ? vars->post_user_val.p :
448 (i == 4) ? vars->global_val.p
449 : vars->fixed_val.p;
450 if(t){ /* MAILTMPLEN = sizeof(tmp) */
451 snprintf(tmp, sizeof(tmp), " %20.20s : %.*s\n", vars->name,
452 MAILTMPLEN-26, *t ? t : quotes);
453 gf_puts(tmp, pc);
459 if(!brief){
460 gf_puts("========== Feature settings ==========\n", pc);
461 for(i = 0; (feat = feature_list(i)); i++)
462 if(feat->id != F_OLD_GROWTH){
463 snprintf(tmp, sizeof(tmp),
464 " %.50s%.100s\n", F_ON(feat->id, ps) ? " " : "no-",
465 feat->name);
466 gf_puts(tmp, pc);
472 /*----------------------------------------------------------------------
473 Dump interesting variables from the given pine struct
475 Args: ps -- pine struct to dump
476 pc -- function to use to write the config data
478 Result: various important pine struct data written
480 ----*/
481 void
482 dump_pine_struct(struct pine *ps, gf_o_t pc)
484 char *p;
485 extern char term_name[];
486 int i;
487 MAILSTREAM *m;
488 MSGNO_S *msgmap;
490 gf_puts("========== struct pine * ==========\n", pc);
491 if(!ps){
492 gf_puts("!No struct!\n", pc);
493 return;
496 gf_puts("ui:\tlogin = ", pc);
497 gf_puts((ps->ui.login) ? ps->ui.login : "NULL", pc);
498 gf_puts(", full = ", pc);
499 gf_puts((ps->ui.fullname) ? ps->ui.fullname : "NULL", pc);
500 gf_puts("\n\thome = ", pc);
501 gf_puts((ps->ui.homedir) ? ps->ui.homedir : "NULL", pc);
503 gf_puts("\nhome_dir=\t", pc);
504 gf_puts(ps->home_dir ? ps->home_dir : "NULL", pc);
505 gf_puts("\nhostname=\t", pc);
506 gf_puts(ps->hostname ? ps->hostname : "NULL", pc);
507 gf_puts("\nlocaldom=\t", pc);
508 gf_puts(ps->localdomain ? ps->localdomain : "NULL", pc);
509 gf_puts("\nuserdom=\t", pc);
510 gf_puts(ps->userdomain ? ps->userdomain : "NULL", pc);
511 gf_puts("\nmaildom=\t", pc);
512 gf_puts(ps->maildomain ? ps->maildomain : "NULL", pc);
514 gf_puts("\ncur_cntxt=\t", pc);
515 gf_puts((ps->context_current && ps->context_current->context)
516 ? ps->context_current->context : "None", pc);
517 gf_puts("\ncur_fldr=\t", pc);
518 gf_puts(ps->cur_folder, pc);
520 gf_puts("\nnstream=\t", pc);
521 gf_puts(long2string((long) ps->s_pool.nstream), pc);
523 for(i = 0; i < ps->s_pool.nstream; i++){
524 m = ps->s_pool.streams[i];
525 gf_puts("\nstream slot ", pc);
526 gf_puts(long2string((long) i), pc);
527 if(!m){
528 gf_puts(" empty", pc);
529 continue;
532 if(ps->mail_stream == m)
533 gf_puts("\nThis is the current mailstream", pc);
534 if(sp_flagged(m, SP_INBOX))
535 gf_puts("\nThis is the inbox stream", pc);
537 gf_puts("\nactual mbox=\t", pc);
538 gf_puts(m->mailbox ? m->mailbox : "no mailbox!", pc);
540 gf_puts("\nflags=", pc);
541 gf_puts(long2string((long) sp_flags(m)), pc);
542 gf_puts("\nnew_mail_count=", pc);
543 gf_puts(long2string((long) sp_new_mail_count(m)), pc);
544 gf_puts("\nmail_since_cmd=", pc);
545 gf_puts(long2string((long) sp_mail_since_cmd(m)), pc);
546 gf_puts("\nmail_box_changed=", pc);
547 gf_puts(long2string((long) sp_mail_box_changed(m)), pc);
548 gf_puts("\nunsorted_newmail=", pc);
549 gf_puts(long2string((long) sp_unsorted_newmail(m)), pc);
550 gf_puts("\nneed_to_rethread=", pc);
551 gf_puts(long2string((long) sp_need_to_rethread(m)), pc);
552 gf_puts("\nviewing_a_thread=", pc);
553 gf_puts(long2string((long) sp_viewing_a_thread(m)), pc);
554 gf_puts("\ndead_stream=", pc);
555 gf_puts(long2string((long) sp_dead_stream(m)), pc);
556 gf_puts("\nnoticed_dead_stream=", pc);
557 gf_puts(long2string((long) sp_noticed_dead_stream(m)), pc);
558 gf_puts("\nio_error_on_stream=", pc);
559 gf_puts(long2string((long) sp_io_error_on_stream(m)), pc);
561 msgmap = sp_msgmap(m);
562 if(msgmap){
563 gf_puts("\nmsgmap: tot=", pc);
564 gf_puts(long2string(mn_get_total(msgmap)), pc);
565 gf_puts(", cur=", pc);
566 gf_puts(long2string(mn_get_cur(msgmap)), pc);
567 gf_puts(", del=", pc);
568 gf_puts(long2string(count_flagged(m, F_DEL)),pc);
569 gf_puts(", hid=", pc);
570 gf_puts(long2string(any_lflagged(msgmap, MN_HIDE)), pc);
571 gf_puts(", exld=", pc);
572 gf_puts(long2string(any_lflagged(msgmap, MN_EXLD)), pc);
573 gf_puts(", slct=", pc);
574 gf_puts(long2string(any_lflagged(msgmap, MN_SLCT)), pc);
575 gf_puts(", chid=", pc);
576 gf_puts(long2string(any_lflagged(msgmap, MN_CHID)), pc);
577 gf_puts(", coll=", pc);
578 gf_puts(long2string(any_lflagged(msgmap, MN_COLL)), pc);
579 gf_puts(", usor=", pc);
580 gf_puts(long2string(any_lflagged(msgmap, MN_USOR)), pc);
581 gf_puts(", stmp=", pc);
582 gf_puts(long2string(any_lflagged(msgmap, MN_STMP)), pc);
583 gf_puts(", sort=", pc);
584 if(mn_get_revsort(msgmap))
585 gf_puts("rev-", pc);
587 gf_puts(sort_name(mn_get_sort(msgmap)), pc);
589 else
590 gf_puts("\nNo msgmap", pc);
594 gf_puts("\nterm ", pc);
595 #if !defined(DOS) && !defined(OS2)
596 gf_puts("type=", pc);
597 gf_puts(term_name, pc);
598 gf_puts(", ttyname=", pc);
599 gf_puts((p = (char *)ttyname(0)) ? p : "NONE", pc);
600 #endif
601 gf_puts(", size=", pc);
602 gf_puts(int2string(ps->ttyo->screen_rows), pc);
603 gf_puts("x", pc);
604 gf_puts(int2string(ps->ttyo->screen_cols), pc);
605 gf_puts(", speed=", pc);
606 gf_puts((ps->low_speed) ? "slow" : "normal", pc);
607 gf_puts("\n", pc);
611 void
612 dump_contexts(void)
614 int i = 0;
615 CONTEXT_S *c = ps_global->context_list;
617 if(!(debugfile && debug > 7 && do_debug(debugfile)))
618 return;
620 while(debugfile && c != NULL){
621 fprintf(debugfile, "\n***** context %s\n", c->context);
622 if(c->label)
623 fprintf(debugfile,"LABEL: %s\n", c->label);
625 if(c->comment)
626 fprintf(debugfile,"COMMENT: %s\n", c->comment);
628 for(i = 0; i < folder_total(FOLDERS(c)); i++)
629 fprintf(debugfile, " %d) %s\n", i + 1, folder_entry(i,FOLDERS(c))->name);
631 c = c->next;
635 #endif /* DEBUG */