1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: debuging.c 900 2008-01-05 01:13:26Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2016 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
18 #include <system.h> /* os-dep defs/includes */
19 #include <general.h> /* generally useful definitions */
21 #include "../../c-client/mail.h" /* for MAILSTREAM and friends */
22 #include "../../c-client/osdep.h"
23 #include "../../c-client/rfc822.h" /* for soutr_t and such */
24 #include "../../c-client/misc.h" /* for cpystr proto */
25 #include "../../c-client/utf8.h" /* for CHARSET and such*/
26 #include "../../c-client/imap4r1.h"
28 #include "../../pith/osdep/canaccess.h"
30 #include "../../pith/debug.h"
31 #include "../../pith/osdep/debugtime.h"
32 #include "../../pith/osdep/color.h"
33 #include "../../pith/osdep/bldpath.h"
34 #include "../../pith/osdep/rename.h"
35 #include "../../pith/osdep/filesize.h"
36 #include "../../pith/init.h"
37 #include "../../pith/status.h"
38 #include "../../pith/sort.h"
39 #include "../../pith/state.h"
40 #include "../../pith/msgno.h"
41 #include "../../pith/conf.h"
42 #include "../../pith/flag.h"
43 #include "../../pith/foldertype.h"
44 #include "../../pith/folder.h"
45 #include "../../pith/charconv/utf8.h"
46 #include "../../pith/charconv/filesys.h"
57 FILE *debugfile
= NULL
;
60 /*----------------------------------------------------------------------
61 Initialize debugging - open the debug log file
65 Result: opens the debug logfile for dprints
67 Opens the file "~/.pine-debug1. Also maintains .pine-debug[2-4]
68 by renaming them each time so the last 4 sessions are saved.
74 char newfname
[MAXPATH
+1], filename
[MAXPATH
+1], *dfile
= NULL
;
77 if(!(debug
|| ps_global
->debug_imap
|| ps_global
->debug_tcp
))
80 for(i
= ps_global
->debug_nfiles
- 1; i
> 0; i
--){
81 build_path(filename
, ps_global
->home_dir
, DEBUGFILE
, sizeof(filename
));
82 strncpy(newfname
, filename
, sizeof(newfname
)-1);
83 newfname
[sizeof(newfname
)-1] = '\0';
84 snprintf(nbuf
, sizeof(nbuf
), "%d", i
);
85 strncat(filename
, nbuf
, sizeof(filename
)-1-strlen(filename
));
86 snprintf(nbuf
, sizeof(nbuf
), "%d", i
+1);
87 strncat(newfname
, nbuf
, sizeof(newfname
)-1-strlen(newfname
));
88 (void)rename_file(filename
, newfname
);
91 build_path(filename
, ps_global
->home_dir
, DEBUGFILE
, sizeof(filename
)-1);
92 strncat(filename
, "1", sizeof(filename
)-1-strlen(filename
));
93 filename
[sizeof(filename
)-1] = '\0';
98 * We were doing our_open so _WINDOWS would open it in widechar mode, but
99 * that means we have to print in widechar mode. For now just do open, since
100 * we it's a debug file and still readable. The alternative is copying all
101 * debug text to a widechar buffer, which would be a drag.
103 if((fd
= open(filename
, O_TRUNC
|O_RDWR
|O_CREAT
, 0600)) >= 0)
104 debugfile
= fdopen(fd
, "w+");
106 if(debugfile
!= NULL
){
108 time_t now
= time((time_t *)0);
109 if(ps_global
->debug_flush
)
110 setvbuf(debugfile
, (char *)NULL
, _IOLBF
, BUFSIZ
);
112 if(ps_global
->debug_nfiles
== 0){
114 * If no debug files are asked for, make filename a tempfile
115 * to be used for a record should pine later crash...
117 if(debug
< 9 && !ps_global
->debug_flush
&& ps_global
->debug_imap
<4){
118 our_unlink(filename
);
123 dprint((0, "Debug output of the Alpine program (debug=%d debug_imap=%d). Version %s (%s %s)\n%s\n",
124 debug
, ps_global
->debug_imap
,
126 SYSTYPE
? SYSTYPE
: "?",
127 get_alpine_revision_string(rev
, sizeof(rev
)),
130 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"));
131 if(dfile
&& (debug
> DEFAULT_DEBUG
||
132 ps_global
->debug_imap
> 0 ||
133 ps_global
->debug_tcp
> 0)){
134 snprintf(newfname
, sizeof(newfname
), "Debug file: %s (level=%d imap=%d)", dfile
,
135 debug
, ps_global
->debug_imap
);
136 init_error(ps_global
, SM_ORDER
, 3, 5, newfname
);
142 /*----------------------------------------------------------------------
143 Try to save the debug file if we crash in a controlled way
145 Args: dfile: pointer to open debug file
147 Result: tries to move the appropriate .pine-debugx file to .pine-crash
149 Looks through the four .pine-debug files hunting for the one that is
150 associated with this pine, and then renames it.
153 save_debug_on_crash(FILE *dfile
, int (*keystrokes
) (int *, char *, size_t))
155 char nbuf
[5], crashfile
[MAXPATH
+1], filename
[MAXPATH
+1];
157 struct stat dbuf
, tbuf
;
158 time_t now
= time((time_t *)0);
160 if(!(dfile
&& fstat(fileno(dfile
), &dbuf
) == 0))
163 fprintf(dfile
, "save_debug_on_crash: Version %s: debug level %d",
164 ALPINE_VERSION
, debug
);
165 fprintf(dfile
, "\n : %s\n", ctime(&now
));
167 build_path(crashfile
, ps_global
->home_dir
, ".pine-crash",sizeof(crashfile
));
169 fprintf(dfile
, "Attempting to save debug file to %s\n\n", crashfile
);
171 "\n\n Attempting to save debug file to %s\n\n", crashfile
);
173 /* Blat out last n keystrokes */
178 fputs("========== Latest Keystrokes =========================\n", dfile
);
180 while((*keystrokes
)(&cval
, cstr
, sizeof(cstr
)) != -1){
181 fprintf(dfile
, "\t%s\t(0x%4.4x)\n", cstr
, cval
);
185 fputs("========== Latest Keystrokes End =====================\n\n", dfile
);
188 fputs("========== Append DebugJournal =======================\n", dfile
);
189 #else /* DEBUGJOURNAL */
190 fputs("========== Append Journal =======================\n", dfile
);
191 #endif /* DEBUGJOURNAL */
192 debugjournal_to_file(dfile
);
194 fputs("========== Append DebugJournal End ===================\n", dfile
);
195 #else /* DEBUGJOURNAL */
196 fputs("========== Append Journal End ===================\n", dfile
);
197 #endif /* DEBUGJOURNAL */
199 /* look for existing debug file */
200 for(i
= 1; i
<= ps_global
->debug_nfiles
; i
++){
201 build_path(filename
, ps_global
->home_dir
, DEBUGFILE
, sizeof(filename
));
202 snprintf(nbuf
, sizeof(nbuf
), "%d", i
);
203 strncat(filename
, nbuf
, sizeof(filename
)-1-strlen(filename
));
204 if(our_stat(filename
, &tbuf
) != 0)
207 /* This must be the current debug file */
208 if(tbuf
.st_dev
== dbuf
.st_dev
&& tbuf
.st_ino
== dbuf
.st_ino
){
209 rename_file(filename
, crashfile
);
214 /* if current debug file name not found, write it by hand */
215 if(i
> ps_global
->debug_nfiles
){
220 * Copy the debug temp file into the
222 if((cfp
= our_fopen(crashfile
, "wb")) != NULL
){
223 buf
[sizeof(buf
)-1] = '\0';
225 while(fgets(buf
, sizeof(buf
)-1, dfile
) && fputs(buf
, cfp
) != EOF
)
237 * functions required by pith library
239 #define CHECK_EVERY_N_TIMES 100
240 #define MAX_DEBUG_FILE_SIZE 200000L
242 * This is just to catch runaway Pines that are looping spewing out
243 * debugging (and filling up a file system). The stop doesn't have to be
244 * at all precise, just soon enough to hopefully prevent filling the
245 * file system. If the debugging level is high (9 for now), then we're
246 * presumably looking for some problem, so don't truncate.
249 do_debug(FILE *debug_fp
)
251 static int counter
= CHECK_EVERY_N_TIMES
;
258 if(debug
<= DEFAULT_DEBUG
259 && !ps_global
->debug_flush
260 && !ps_global
->debug_tcp
261 && !ps_global
->debug_timestamp
262 && !ps_global
->debug_imap
265 if((filesize
= fp_file_size(debug_fp
)) != -1L)
266 ok
= (unsigned long)filesize
< (unsigned long)MAX_DEBUG_FILE_SIZE
;
268 counter
= CHECK_EVERY_N_TIMES
;
270 fprintf(debug_fp
, "\n\n --- No more debugging ---\n");
272 " (debug file growing too large - over %ld bytes)\n\n",
273 MAX_DEBUG_FILE_SIZE
);
278 if(ok
&& ps_global
->debug_timestamp
)
279 fprintf(debug_fp
, "\n%s\n", debug_time(0, ps_global
->debug_timestamp
));
285 output_debug_msg(int dlevel
, char *fmt
, ...)
289 if(debugfile
&& debug
>= dlevel
&& do_debug(debugfile
)){
293 vfprintf(debugfile
, fmt
, args
);
296 if((l
= strlen(fmt
)) > 2 && fmt
[l
-1] != '\n')
297 fputc('\n', debugfile
);
299 if(ps_global
->debug_flush
)
308 if(dlevel
<= 9 || dlevel
<= debug
){
310 * Make this static in order to move it off of
311 * the stack. We were getting "random" crashes
312 * on some systems when this size interacted with
313 * pthread stack size somehow. Taking it off of
314 * the stack ought to fix that without us having to
315 * understand how it all works.
317 static char b
[64000];
320 vsnprintf(b
, sizeof(b
), fmt
, args
);
323 b
[sizeof(b
)-1] = '\0';
324 add_review_message(b
, dlevel
);
327 #endif /* DEBUGJOURNAL */
332 /*----------------------------------------------------------------------
333 Dump the whole config enchilada using the given function
335 Args: ps -- pine struct containing vars to dump
336 pc -- function to use to write the config data
337 brief -- brief dump, only dump user_vals
338 Result: command line, global, user var's written with given function
342 dump_configuration(int brief
)
346 if(!do_debug(debugfile
))
349 gf_set_writec(&pc
, debugfile
, 0L, FileStar
, 0);
351 dump_config(ps_global
, pc
, brief
);
356 dump_config(struct pine
*ps
, gf_io_t pc
, int brief
)
359 char quotes
[3], tmp
[MAILTMPLEN
];
360 register struct variable
*vars
;
363 quotes
[0] = '"'; quotes
[1] = '"'; quotes
[2] = '\0';
365 for(i
= (brief
? 2 : 0); i
< (brief
? 4 : 6); i
++){
366 snprintf(tmp
, sizeof(tmp
), "======= %.20s_val options set",
367 (i
== 0) ? "Current" :
368 (i
== 1) ? "Command_line" :
370 (i
== 3) ? "PostloadUser" :
375 snprintf(tmp
, sizeof(tmp
), " (%.100s)",
376 (i
== 2) ? ((ps
->prc
) ? ps
->prc
->name
: ".pinerc") :
377 (i
== 3) ? ((ps
->post_prc
) ? ps
->post_prc
->name
: "postload") :
378 (i
== 4) ? ((ps
->pconf
) ? ps
->pconf
->name
380 #if defined(DOS) || defined(OS2)
383 ((can_access(SYSTEM_PINERC_FIXED
, ACCESS_EXISTS
) == 0)
384 ? SYSTEM_PINERC_FIXED
: "NO pine.conf.fixed")
390 gf_puts(" =======\n", pc
);
391 for(vars
= ps
->vars
; vars
->name
; vars
++){
394 t
= (i
== 0) ? vars
->current_val
.l
:
395 (i
== 1) ? vars
->cmdline_val
.l
:
396 (i
== 2) ? vars
->main_user_val
.l
:
397 (i
== 3) ? vars
->post_user_val
.l
:
398 (i
== 4) ? vars
->global_val
.l
400 if(t
&& *t
){ /* MAILTMPLEN = sizeof(tmp) */
401 snprintf(tmp
, sizeof(tmp
), " %20.20s : %.*s\n", vars
->name
,
402 MAILTMPLEN
-26, **t
? *t
: quotes
);
405 snprintf(tmp
, sizeof(tmp
)," %20.20s : %.*s\n","",
406 MAILTMPLEN
-26, **t
? *t
: quotes
);
413 t
= (i
== 0) ? vars
->current_val
.p
:
414 (i
== 1) ? vars
->cmdline_val
.p
:
415 (i
== 2) ? vars
->main_user_val
.p
:
416 (i
== 3) ? vars
->post_user_val
.p
:
417 (i
== 4) ? vars
->global_val
.p
419 if(t
){ /* MAILTMPLEN = sizeof(tmp) */
420 snprintf(tmp
, sizeof(tmp
), " %20.20s : %.*s\n", vars
->name
,
421 MAILTMPLEN
-26, *t
? t
: quotes
);
429 gf_puts("========== Feature settings ==========\n", pc
);
430 for(i
= 0; (feat
= feature_list(i
)); i
++)
431 if(feat
->id
!= F_OLD_GROWTH
){
432 snprintf(tmp
, sizeof(tmp
),
433 " %.50s%.100s\n", F_ON(feat
->id
, ps
) ? " " : "no-",
441 /*----------------------------------------------------------------------
442 Dump interesting variables from the given pine struct
444 Args: ps -- pine struct to dump
445 pc -- function to use to write the config data
447 Result: various important pine struct data written
451 dump_pine_struct(struct pine
*ps
, gf_io_t pc
)
454 extern char term_name
[];
459 gf_puts("========== struct pine * ==========\n", pc
);
461 gf_puts("!No struct!\n", pc
);
465 gf_puts("ui:\tlogin = ", pc
);
466 gf_puts((ps
->ui
.login
) ? ps
->ui
.login
: "NULL", pc
);
467 gf_puts(", full = ", pc
);
468 gf_puts((ps
->ui
.fullname
) ? ps
->ui
.fullname
: "NULL", pc
);
469 gf_puts("\n\thome = ", pc
);
470 gf_puts((ps
->ui
.homedir
) ? ps
->ui
.homedir
: "NULL", pc
);
472 gf_puts("\nhome_dir=\t", pc
);
473 gf_puts(ps
->home_dir
? ps
->home_dir
: "NULL", pc
);
474 gf_puts("\nhostname=\t", pc
);
475 gf_puts(ps
->hostname
? ps
->hostname
: "NULL", pc
);
476 gf_puts("\nlocaldom=\t", pc
);
477 gf_puts(ps
->localdomain
? ps
->localdomain
: "NULL", pc
);
478 gf_puts("\nuserdom=\t", pc
);
479 gf_puts(ps
->userdomain
? ps
->userdomain
: "NULL", pc
);
480 gf_puts("\nmaildom=\t", pc
);
481 gf_puts(ps
->maildomain
? ps
->maildomain
: "NULL", pc
);
483 gf_puts("\ncur_cntxt=\t", pc
);
484 gf_puts((ps
->context_current
&& ps
->context_current
->context
)
485 ? ps
->context_current
->context
: "None", pc
);
486 gf_puts("\ncur_fldr=\t", pc
);
487 gf_puts(ps
->cur_folder
, pc
);
489 gf_puts("\nnstream=\t", pc
);
490 gf_puts(long2string((long) ps
->s_pool
.nstream
), pc
);
492 for(i
= 0; i
< ps
->s_pool
.nstream
; i
++){
493 m
= ps
->s_pool
.streams
[i
];
494 gf_puts("\nstream slot ", pc
);
495 gf_puts(long2string((long) i
), pc
);
497 gf_puts(" empty", pc
);
501 if(ps
->mail_stream
== m
)
502 gf_puts("\nThis is the current mailstream", pc
);
503 if(sp_flagged(m
, SP_INBOX
))
504 gf_puts("\nThis is the inbox stream", pc
);
506 gf_puts("\nactual mbox=\t", pc
);
507 gf_puts(m
->mailbox
? m
->mailbox
: "no mailbox!", pc
);
509 gf_puts("\nflags=", pc
);
510 gf_puts(long2string((long) sp_flags(m
)), pc
);
511 gf_puts("\nnew_mail_count=", pc
);
512 gf_puts(long2string((long) sp_new_mail_count(m
)), pc
);
513 gf_puts("\nmail_since_cmd=", pc
);
514 gf_puts(long2string((long) sp_mail_since_cmd(m
)), pc
);
515 gf_puts("\nmail_box_changed=", pc
);
516 gf_puts(long2string((long) sp_mail_box_changed(m
)), pc
);
517 gf_puts("\nunsorted_newmail=", pc
);
518 gf_puts(long2string((long) sp_unsorted_newmail(m
)), pc
);
519 gf_puts("\nneed_to_rethread=", pc
);
520 gf_puts(long2string((long) sp_need_to_rethread(m
)), pc
);
521 gf_puts("\nviewing_a_thread=", pc
);
522 gf_puts(long2string((long) sp_viewing_a_thread(m
)), pc
);
523 gf_puts("\ndead_stream=", pc
);
524 gf_puts(long2string((long) sp_dead_stream(m
)), pc
);
525 gf_puts("\nnoticed_dead_stream=", pc
);
526 gf_puts(long2string((long) sp_noticed_dead_stream(m
)), pc
);
527 gf_puts("\nio_error_on_stream=", pc
);
528 gf_puts(long2string((long) sp_io_error_on_stream(m
)), pc
);
530 msgmap
= sp_msgmap(m
);
532 gf_puts("\nmsgmap: tot=", pc
);
533 gf_puts(long2string(mn_get_total(msgmap
)), pc
);
534 gf_puts(", cur=", pc
);
535 gf_puts(long2string(mn_get_cur(msgmap
)), pc
);
536 gf_puts(", del=", pc
);
537 gf_puts(long2string(count_flagged(m
, F_DEL
)),pc
);
538 gf_puts(", hid=", pc
);
539 gf_puts(long2string(any_lflagged(msgmap
, MN_HIDE
)), pc
);
540 gf_puts(", exld=", pc
);
541 gf_puts(long2string(any_lflagged(msgmap
, MN_EXLD
)), pc
);
542 gf_puts(", slct=", pc
);
543 gf_puts(long2string(any_lflagged(msgmap
, MN_SLCT
)), pc
);
544 gf_puts(", chid=", pc
);
545 gf_puts(long2string(any_lflagged(msgmap
, MN_CHID
)), pc
);
546 gf_puts(", coll=", pc
);
547 gf_puts(long2string(any_lflagged(msgmap
, MN_COLL
)), pc
);
548 gf_puts(", usor=", pc
);
549 gf_puts(long2string(any_lflagged(msgmap
, MN_USOR
)), pc
);
550 gf_puts(", stmp=", pc
);
551 gf_puts(long2string(any_lflagged(msgmap
, MN_STMP
)), pc
);
552 gf_puts(", sort=", pc
);
553 if(mn_get_revsort(msgmap
))
556 gf_puts(sort_name(mn_get_sort(msgmap
)), pc
);
559 gf_puts("\nNo msgmap", pc
);
563 gf_puts("\nterm ", pc
);
564 #if !defined(DOS) && !defined(OS2)
565 gf_puts("type=", pc
);
566 gf_puts(term_name
, pc
);
567 gf_puts(", ttyname=", pc
);
568 gf_puts((p
= (char *)ttyname(0)) ? p
: "NONE", pc
);
570 gf_puts(", size=", pc
);
571 gf_puts(int2string(ps
->ttyo
->screen_rows
), pc
);
573 gf_puts(int2string(ps
->ttyo
->screen_cols
), pc
);
574 gf_puts(", speed=", pc
);
575 gf_puts((ps
->low_speed
) ? "slow" : "normal", pc
);
584 CONTEXT_S
*c
= ps_global
->context_list
;
586 if(!(debugfile
&& debug
> 7 && do_debug(debugfile
)))
589 while(debugfile
&& c
!= NULL
){
590 fprintf(debugfile
, "\n***** context %s\n", c
->context
);
592 fprintf(debugfile
,"LABEL: %s\n", c
->label
);
595 fprintf(debugfile
,"COMMENT: %s\n", c
->comment
);
597 for(i
= 0; i
< folder_total(FOLDERS(c
)); i
++)
598 fprintf(debugfile
, " %d) %s\n", i
+ 1, folder_entry(i
,FOLDERS(c
))->name
);