db_output.c revision 207922
1193323Sed/*- 2193323Sed * Mach Operating System 3193323Sed * Copyright (c) 1991,1990 Carnegie Mellon University 4193323Sed * All Rights Reserved. 5193323Sed * 6193323Sed * Permission to use, copy, modify and distribute this software and its 7193323Sed * documentation is hereby granted, provided that both the copyright 8193323Sed * notice and this permission notice appear in all copies of the 9193323Sed * software, derivative works or modified versions, and any portions 10193323Sed * thereof, and that both notices appear in supporting documentation. 11193323Sed * 12193323Sed * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 13193323Sed * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 14193323Sed * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 15193323Sed * 16193323Sed * Carnegie Mellon requests users of this software to return to 17193323Sed * 18198090Srdivacky * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 19193323Sed * School of Computer Science 20194178Sed * Carnegie Mellon University 21193323Sed * Pittsburgh PA 15213-3890 22193323Sed * 23195340Sed * any improvements or extensions that they make and grant Carnegie the 24195340Sed * rights to redistribute these changes. 25195340Sed */ 26195340Sed/* 27195340Sed * Author: David B. Golub, Carnegie Mellon University 28195340Sed * Date: 7/90 29195340Sed */ 30195340Sed 31195340Sed/* 32195340Sed * Printf and character output for debugger. 33195340Sed */ 34195340Sed 35193323Sed#include <sys/cdefs.h> 36195340Sed__FBSDID("$FreeBSD: head/sys/ddb/db_output.c 207922 2010-05-11 17:01:14Z attilio $"); 37193323Sed 38193323Sed#include "opt_ddb.h" 39193323Sed 40195340Sed#include <sys/param.h> 41193323Sed#include <sys/systm.h> 42193323Sed#include <sys/cons.h> 43193323Sed#include <sys/kdb.h> 44193323Sed#include <sys/kernel.h> 45193323Sed#include <sys/sysctl.h> 46193323Sed 47193323Sed#include <machine/stdarg.h> 48193323Sed 49193323Sed#include <ddb/ddb.h> 50193323Sed#include <ddb/db_output.h> 51193323Sed 52193323Sedstruct dbputchar_arg { 53193323Sed size_t da_nbufr; 54193323Sed size_t da_remain; 55193323Sed char *da_pbufr; 56193323Sed char *da_pnext; 57195340Sed}; 58198090Srdivacky 59193323Sed/* 60193323Sed * Character output - tracks position in line. 61193323Sed * To do this correctly, we should know how wide 62195098Sed * the output device is - then we could zero 63193323Sed * the line position when the output device wraps 64193323Sed * around to the start of the next line. 65195340Sed * 66193323Sed * Instead, we count the number of spaces printed 67195098Sed * since the last printing character so that we 68193323Sed * don't print trailing spaces. This avoids most 69195098Sed * of the wraparounds. 70195098Sed */ 71195098Sedstatic int db_output_position = 0; /* output column */ 72195098Sedstatic int db_last_non_space = 0; /* last non-space character */ 73195340Seddb_expr_t db_tab_stop_width = 8; /* how wide are tab stops? */ 74195098Sed#define NEXT_TAB(i) \ 75195098Sed ((((i) + db_tab_stop_width) / db_tab_stop_width) * db_tab_stop_width) 76195098Seddb_expr_t db_max_width = 79; /* output line width */ 77195098Seddb_expr_t db_lines_per_page = 20; /* lines per page */ 78193323Sedvolatile int db_pager_quit; /* user requested quit */ 79193323Sedstatic int db_newlines; /* # lines this page */ 80193323Sedstatic int db_maxlines; /* max lines/page when paging */ 81195098Sedstatic int ddb_use_printf = 0; 82195340SedSYSCTL_INT(_debug, OID_AUTO, ddb_use_printf, CTLFLAG_RW, &ddb_use_printf, 0, 83193323Sed "use printf for all ddb output"); 84202878Srdivacky 85193323Sedstatic void db_putc(int c); 86193323Sedstatic void db_puts(const char *str); 87193323Sedstatic void db_putchar(int c, void *arg); 88193323Sedstatic void db_pager(void); 89193323Sed 90193323Sed/* 91193323Sed * Force pending whitespace. 92193323Sed */ 93193323Sedvoid 94193323Seddb_force_whitespace() 95195340Sed{ 96193323Sed register int last_print, next_tab; 97193323Sed 98193323Sed last_print = db_last_non_space; 99193323Sed while (last_print < db_output_position) { 100193323Sed next_tab = NEXT_TAB(last_print); 101202878Srdivacky if (next_tab <= db_output_position) { 102195098Sed while (last_print < next_tab) { /* DON'T send a tab!!! */ 103195098Sed cnputc(' '); 104195340Sed db_capture_writech(' '); 105195098Sed last_print++; 106195098Sed } 107202878Srdivacky } 108195098Sed else { 109193323Sed cnputc(' '); 110202878Srdivacky db_capture_writech(' '); 111193323Sed last_print++; 112193323Sed } 113193323Sed } 114193323Sed db_last_non_space = db_output_position; 115193323Sed} 116193323Sed 117193323Sed/* 118195340Sed * Output character. Buffer whitespace. 119202878Srdivacky */ 120193323Sedstatic void 121193323Seddb_putchar(int c, void *arg) 122193323Sed{ 123193323Sed struct dbputchar_arg *dap = arg; 124193323Sed 125195340Sed if (dap->da_pbufr == NULL) { 126195340Sed 127193323Sed /* No bufferized output is provided. */ 128193323Sed db_putc(c); 129193323Sed } else { 130193323Sed 131193323Sed *dap->da_pnext++ = c; 132193323Sed dap->da_remain--; 133193323Sed 134195340Sed /* Leave always the buffer 0 terminated. */ 135195340Sed *dap->da_pnext = '\0'; 136195340Sed 137195340Sed /* Check if the buffer needs to be flushed. */ 138193323Sed if (dap->da_remain < 2 || c == '\n') { 139193323Sed db_puts(dap->da_pbufr); 140193323Sed dap->da_pnext = dap->da_pbufr; 141193323Sed dap->da_remain = dap->da_nbufr; 142193323Sed *dap->da_pnext = '\0'; 143193323Sed } 144193323Sed } 145193323Sed} 146195340Sed 147193323Sedstatic void 148195340Seddb_putc(int c) 149202878Srdivacky{ 150193323Sed 151193323Sed /* 152193323Sed * If not in the debugger or the user requests it, output data to 153193323Sed * both the console and the message buffer. 154193323Sed */ 155193323Sed if (!kdb_active || ddb_use_printf) { 156193323Sed printf("%c", c); 157193323Sed if (!kdb_active) 158193323Sed return; 159193323Sed if (c == '\r' || c == '\n') 160198090Srdivacky db_check_interrupt(); 161198090Srdivacky if (c == '\n' && db_maxlines > 0) { 162198090Srdivacky db_newlines++; 163198090Srdivacky if (db_newlines >= db_maxlines) 164198090Srdivacky db_pager(); 165193323Sed } 166198090Srdivacky return; 167193323Sed } 168193323Sed 169198090Srdivacky /* Otherwise, output data directly to the console. */ 170198090Srdivacky if (c > ' ' && c <= '~') { 171198090Srdivacky /* 172198090Srdivacky * Printing character. 173198090Srdivacky * If we have spaces to print, print them first. 174198090Srdivacky * Use tabs if possible. 175193323Sed */ 176193323Sed db_force_whitespace(); 177198090Srdivacky cnputc(c); 178198090Srdivacky db_capture_writech(c); 179193323Sed db_output_position++; 180198090Srdivacky db_last_non_space = db_output_position; 181198090Srdivacky } 182198090Srdivacky else if (c == '\n') { 183198090Srdivacky /* Newline */ 184198090Srdivacky cnputc(c); 185198090Srdivacky db_capture_writech(c); 186198090Srdivacky db_output_position = 0; 187198090Srdivacky db_last_non_space = 0; 188198090Srdivacky db_check_interrupt(); 189198090Srdivacky if (db_maxlines > 0) { 190198090Srdivacky db_newlines++; 191198090Srdivacky if (db_newlines >= db_maxlines) 192198090Srdivacky db_pager(); 193198090Srdivacky } 194198090Srdivacky } 195198090Srdivacky else if (c == '\r') { 196198090Srdivacky /* Return */ 197198090Srdivacky cnputc(c); 198193323Sed db_capture_writech(c); 199193323Sed db_output_position = 0; 200193323Sed db_last_non_space = 0; 201193323Sed db_check_interrupt(); 202193323Sed } 203198090Srdivacky else if (c == '\t') { 204198090Srdivacky /* assume tabs every 8 positions */ 205198090Srdivacky db_output_position = NEXT_TAB(db_output_position); 206198090Srdivacky } 207198090Srdivacky else if (c == ' ') { 208198090Srdivacky /* space */ 209198090Srdivacky db_output_position++; 210198090Srdivacky } 211198090Srdivacky else if (c == '\007') { 212198090Srdivacky /* bell */ 213198090Srdivacky cnputc(c); 214198090Srdivacky /* No need to beep in a log: db_capture_writech(c); */ 215198090Srdivacky } 216198090Srdivacky /* other characters are assumed non-printing */ 217198090Srdivacky} 218198090Srdivacky 219198090Srdivackystatic void 220198090Srdivackydb_puts(const char *str) 221198090Srdivacky{ 222198090Srdivacky int i; 223198090Srdivacky 224198090Srdivacky for (i = 0; str[i] != '\0'; i++) 225198090Srdivacky db_putc(str[i]); 226198090Srdivacky} 227198090Srdivacky 228198090Srdivacky/* 229198090Srdivacky * Turn on the pager. 230198090Srdivacky */ 231198090Srdivackyvoid 232198090Srdivackydb_enable_pager(void) 233198090Srdivacky{ 234198090Srdivacky if (db_maxlines == 0) { 235193323Sed db_maxlines = db_lines_per_page; 236198090Srdivacky db_newlines = 0; 237198090Srdivacky db_pager_quit = 0; 238193323Sed } 239193323Sed} 240193323Sed 241198090Srdivacky/* 242198090Srdivacky * Turn off the pager. 243198090Srdivacky */ 244193323Sedvoid 245193323Seddb_disable_pager(void) 246193323Sed{ 247198090Srdivacky db_maxlines = 0; 248198090Srdivacky} 249193323Sed 250193323Sed/* 251193323Sed * A simple paging callout function. It supports several simple more(1)-like 252193323Sed * commands as well as a quit command that sets db_pager_quit which db 253193323Sed * commands can poll to see if they should terminate early. 254193323Sed */ 255193323Sedvoid 256193323Seddb_pager(void) 257198090Srdivacky{ 258198090Srdivacky int c, done; 259198090Srdivacky 260193323Sed db_capture_enterpager(); 261198090Srdivacky db_printf("--More--\r"); 262198090Srdivacky done = 0; 263198090Srdivacky while (!done) { 264198090Srdivacky c = cngetc(); 265198090Srdivacky switch (c) { 266198090Srdivacky case 'e': 267198090Srdivacky case 'j': 268198090Srdivacky case '\n': 269198090Srdivacky /* Just one more line. */ 270198090Srdivacky db_maxlines = 1; 271198090Srdivacky done++; 272198090Srdivacky break; 273198090Srdivacky case 'd': 274198090Srdivacky /* Half a page. */ 275198090Srdivacky db_maxlines = db_lines_per_page / 2; 276198090Srdivacky done++; 277198090Srdivacky break; 278198090Srdivacky case 'f': 279198090Srdivacky case ' ': 280198090Srdivacky /* Another page. */ 281198090Srdivacky db_maxlines = db_lines_per_page; 282198090Srdivacky done++; 283198090Srdivacky break; 284198090Srdivacky case 'q': 285198090Srdivacky case 'Q': 286198090Srdivacky case 'x': 287198090Srdivacky case 'X': 288198090Srdivacky /* Quit */ 289198090Srdivacky db_maxlines = 0; 290198090Srdivacky db_pager_quit = 1; 291198090Srdivacky done++; 292198090Srdivacky break; 293198090Srdivacky#if 0 294198090Srdivacky /* FALLTHROUGH */ 295198090Srdivacky default: 296198090Srdivacky cnputc('\007'); 297198090Srdivacky#endif 298198090Srdivacky } 299198090Srdivacky } 300198090Srdivacky db_printf(" "); 301198090Srdivacky db_force_whitespace(); 302198090Srdivacky db_printf("\r"); 303198090Srdivacky db_newlines = 0; 304198090Srdivacky db_capture_exitpager(); 305198090Srdivacky} 306198090Srdivacky 307198090Srdivacky/* 308198090Srdivacky * Return output position 309198090Srdivacky */ 310198090Srdivackyint 311198090Srdivackydb_print_position() 312198090Srdivacky{ 313198090Srdivacky return (db_output_position); 314198090Srdivacky} 315198090Srdivacky 316198090Srdivacky/* 317198090Srdivacky * Printing 318198090Srdivacky */ 319198090Srdivackyint 320198090Srdivackydb_printf(const char *fmt, ...) 321198090Srdivacky{ 322198090Srdivacky#ifdef DDB_BUFR_SIZE 323198090Srdivacky char bufr[DDB_BUFR_SIZE]; 324198090Srdivacky#endif 325198090Srdivacky struct dbputchar_arg dca; 326198090Srdivacky va_list listp; 327198090Srdivacky int retval; 328202878Srdivacky 329198090Srdivacky#ifdef DDB_BUFR_SIZE 330198090Srdivacky dca.da_pbufr = bufr; 331198090Srdivacky dca.da_pnext = dca.da_pbufr; 332198090Srdivacky dca.da_nbufr = sizeof(bufr); 333193323Sed dca.da_remain = sizeof(bufr); 334193323Sed *dca.da_pnext = '\0'; 335193323Sed#else 336193323Sed dca.da_pbufr = NULL; 337193323Sed#endif 338193323Sed 339193323Sed va_start(listp, fmt); 340193323Sed retval = kvprintf (fmt, db_putchar, &dca, db_radix, listp); 341193323Sed va_end(listp); 342193323Sed 343193323Sed#ifdef DDB_BUFR_SIZE 344193323Sed if (*dca.da_pbufr != '\0') 345193323Sed db_puts(dca.da_pbufr); 346193323Sed#endif 347193323Sed return (retval); 348198090Srdivacky} 349198090Srdivacky 350193323Sedint db_indent; 351193323Sed 352193323Sedvoid 353193323Seddb_iprintf(const char *fmt,...) 354193323Sed{ 355198090Srdivacky#ifdef DDB_BUFR_SIZE 356193323Sed char bufr[DDB_BUFR_SIZE]; 357193323Sed#endif 358198090Srdivacky struct dbputchar_arg dca; 359198090Srdivacky register int i; 360198090Srdivacky va_list listp; 361198090Srdivacky 362200581Srdivacky for (i = db_indent; i >= 8; i -= 8) 363193323Sed db_printf("\t"); 364193323Sed while (--i >= 0) 365193323Sed db_printf(" "); 366193323Sed 367193323Sed#ifdef DDB_BUFR_SIZE 368198090Srdivacky dca.da_pbufr = bufr; 369198090Srdivacky dca.da_pnext = dca.da_pbufr; 370198090Srdivacky dca.da_nbufr = sizeof(bufr); 371198090Srdivacky dca.da_remain = sizeof(bufr); 372198090Srdivacky *dca.da_pnext = '\0'; 373198090Srdivacky#else 374198090Srdivacky dca.da_pbufr = NULL; 375198090Srdivacky#endif 376198090Srdivacky 377198090Srdivacky va_start(listp, fmt); 378198090Srdivacky kvprintf (fmt, db_putchar, &dca, db_radix, listp); 379198090Srdivacky va_end(listp); 380198090Srdivacky 381198090Srdivacky#ifdef DDB_BUFR_SIZE 382198090Srdivacky if (*dca.da_pbufr != '\0') 383198090Srdivacky db_puts(dca.da_pbufr); 384198090Srdivacky#endif 385198090Srdivacky} 386198090Srdivacky 387193323Sed/* 388193323Sed * End line if too long. 389198090Srdivacky */ 390198090Srdivackyvoid 391198090Srdivackydb_end_line(int field_width) 392198090Srdivacky{ 393198090Srdivacky if (db_output_position + field_width > db_max_width) 394193323Sed db_printf("\n"); 395198090Srdivacky} 396198090Srdivacky