* New version 2.20.11
[alpine.git] / alpine / osdep / debuging.c
bloba86690df956aa9718e03bad68f0840dc7e3749a9
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: debuging.c 900 2008-01-05 01:13:26Z hubert@u.washington.edu $";
3 #endif
5 /*
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"
47 #include "../help.h"
49 #include "debuging.h"
52 #ifdef DEBUG
55 * globally visible
57 FILE *debugfile = NULL;
60 /*----------------------------------------------------------------------
61 Initialize debugging - open the debug log file
63 Args: none
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.
69 ----*/
70 void
71 init_debug(void)
73 char nbuf[5];
74 char newfname[MAXPATH+1], filename[MAXPATH+1], *dfile = NULL;
75 int i, fd;
77 if(!(debug || ps_global->debug_imap || ps_global->debug_tcp))
78 return;
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';
95 debugfile = NULL;
96 dfile = filename;
97 /*
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){
107 char rev[128];
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);
119 dfile = NULL;
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,
125 ALPINE_VERSION,
126 SYSTYPE ? SYSTYPE : "?",
127 get_alpine_revision_string(rev, sizeof(rev)),
128 ctime(&now)));
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.
151 ----*/
152 void
153 save_debug_on_crash(FILE *dfile, int (*keystrokes) (int *, char *, size_t))
155 char nbuf[5], crashfile[MAXPATH+1], filename[MAXPATH+1];
156 int i;
157 struct stat dbuf, tbuf;
158 time_t now = time((time_t *)0);
160 if(!(dfile && fstat(fileno(dfile), &dbuf) == 0))
161 return;
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);
170 fprintf(stderr,
171 "\n\n Attempting to save debug file to %s\n\n", crashfile);
173 /* Blat out last n keystrokes */
174 if(keystrokes){
175 int cval;
176 char cstr[256];
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);
187 #ifdef DEBUGJOURNAL
188 fputs("========== Append DebugJournal =======================\n", dfile);
189 #else /* DEBUGJOURNAL */
190 fputs("========== Append Journal =======================\n", dfile);
191 #endif /* DEBUGJOURNAL */
192 debugjournal_to_file(dfile);
193 #ifdef DEBUGJOURNAL
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)
205 continue;
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);
210 break;
214 /* if current debug file name not found, write it by hand */
215 if(i > ps_global->debug_nfiles){
216 FILE *cfp;
217 char buf[1025];
220 * Copy the debug temp file into the
222 if((cfp = our_fopen(crashfile, "wb")) != NULL){
223 buf[sizeof(buf)-1] = '\0';
224 fseek(dfile, 0L, 0);
225 while(fgets(buf, sizeof(buf)-1, dfile) && fputs(buf, cfp) != EOF)
228 fclose(cfp);
232 fclose(dfile);
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;
252 static int ok = 1;
253 long filesize;
255 if(!debugfile)
256 return(0);
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
263 && ok
264 && --counter <= 0){
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;
269 if(!ok){
270 fprintf(debug_fp, "\n\n --- No more debugging ---\n");
271 fprintf(debug_fp,
272 " (debug file growing too large - over %ld bytes)\n\n",
273 MAX_DEBUG_FILE_SIZE);
274 fflush(debug_fp);
278 if(ok && ps_global->debug_timestamp)
279 fprintf(debug_fp, "\n%s\n", debug_time(0, ps_global->debug_timestamp));
281 return(ok);
284 void
285 output_debug_msg(int dlevel, char *fmt, ...)
287 va_list args;
289 if(debugfile && debug >= dlevel && do_debug(debugfile)){
290 int l;
292 va_start(args, fmt);
293 vfprintf(debugfile, fmt, args);
294 va_end(args);
296 if((l = strlen(fmt)) > 2 && fmt[l-1] != '\n')
297 fputc('\n', debugfile);
299 if(ps_global->debug_flush)
300 fflush(debugfile);
303 if(panicking())
304 return;
306 #ifdef DEBUGJOURNAL
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];
319 va_start(args, fmt);
320 vsnprintf(b, sizeof(b), fmt, args);
321 va_end(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
340 ----*/
341 void
342 dump_configuration(int brief)
344 gf_io_t pc;
346 if(!do_debug(debugfile))
347 return;
349 gf_set_writec(&pc, debugfile, 0L, FileStar, 0);
351 dump_config(ps_global, pc, brief);
355 void
356 dump_config(struct pine *ps, gf_io_t pc, int brief)
358 int i;
359 char quotes[3], tmp[MAILTMPLEN];
360 register struct variable *vars;
361 FEATURE_S *feat;
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" :
369 (i == 2) ? "User" :
370 (i == 3) ? "PostloadUser" :
371 (i == 4) ? "Global"
372 : "Fixed");
373 gf_puts(tmp, pc);
374 if(i > 1){
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
379 : SYSTEM_PINERC) :
380 #if defined(DOS) || defined(OS2)
381 "NO FIXED"
382 #else
383 ((can_access(SYSTEM_PINERC_FIXED, ACCESS_EXISTS) == 0)
384 ? SYSTEM_PINERC_FIXED : "NO pine.conf.fixed")
385 #endif
387 gf_puts(tmp, pc);
390 gf_puts(" =======\n", pc);
391 for(vars = ps->vars; vars->name; vars++){
392 if(vars->is_list){
393 char **t;
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
399 : vars->fixed_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);
403 gf_puts(tmp, pc);
404 while(++t && *t){
405 snprintf(tmp, sizeof(tmp)," %20.20s : %.*s\n","",
406 MAILTMPLEN-26, **t ? *t : quotes);
407 gf_puts(tmp, pc);
411 else{
412 char *t;
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
418 : vars->fixed_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);
422 gf_puts(tmp, pc);
428 if(!brief){
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-",
434 feat->name);
435 gf_puts(tmp, pc);
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
449 ----*/
450 void
451 dump_pine_struct(struct pine *ps, gf_io_t pc)
453 char *p;
454 extern char term_name[];
455 int i;
456 MAILSTREAM *m;
457 MSGNO_S *msgmap;
459 gf_puts("========== struct pine * ==========\n", pc);
460 if(!ps){
461 gf_puts("!No struct!\n", pc);
462 return;
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);
496 if(!m){
497 gf_puts(" empty", pc);
498 continue;
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);
531 if(msgmap){
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))
554 gf_puts("rev-", pc);
556 gf_puts(sort_name(mn_get_sort(msgmap)), pc);
558 else
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);
569 #endif
570 gf_puts(", size=", pc);
571 gf_puts(int2string(ps->ttyo->screen_rows), pc);
572 gf_puts("x", pc);
573 gf_puts(int2string(ps->ttyo->screen_cols), pc);
574 gf_puts(", speed=", pc);
575 gf_puts((ps->low_speed) ? "slow" : "normal", pc);
576 gf_puts("\n", pc);
580 void
581 dump_contexts(void)
583 int i = 0;
584 CONTEXT_S *c = ps_global->context_list;
586 if(!(debugfile && debug > 7 && do_debug(debugfile)))
587 return;
589 while(debugfile && c != NULL){
590 fprintf(debugfile, "\n***** context %s\n", c->context);
591 if(c->label)
592 fprintf(debugfile,"LABEL: %s\n", c->label);
594 if(c->comment)
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);
600 c = c->next;
604 #endif /* DEBUG */