1 /*- 2 * Mach Operating System 3 * Copyright (c) 1991,1990 Carnegie Mellon University 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify and distribute this software and its 7 * documentation is hereby granted, provided that both the copyright 8 * notice and this permission notice appear in all copies of the 9 * software, derivative works or modified versions, and any portions 10 * thereof, and that both notices appear in supporting documentation. 11 * 12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 15 * 16 * Carnegie Mellon requests users of this software to return to 17 * 18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 19 * School of Computer Science 20 * Carnegie Mellon University 21 * Pittsburgh PA 15213-3890 22 * 23 * any improvements or extensions that they make and grant Carnegie the 24 * rights to redistribute these changes. 25 */ 26 /* 27 * Author: David B. Golub, Carnegie Mellon University 28 * Date: 7/90 29 */ 30 31 /* 32 * Printf and character output for debugger. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include "opt_ddb.h" 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/cons.h> 43 #include <sys/kdb.h> 44 #include <sys/kernel.h> 45 #include <sys/sysctl.h> 46 47 #include <machine/stdarg.h> 48 49 #include <ddb/ddb.h> 50 #include <ddb/db_output.h> 51 52 struct dbputchar_arg { 53 size_t da_nbufr; 54 size_t da_remain; 55 char *da_pbufr; 56 char *da_pnext; 57 }; 58 59 /* 60 * Character output - tracks position in line. 61 * To do this correctly, we should know how wide 62 * the output device is - then we could zero 63 * the line position when the output device wraps 64 * around to the start of the next line. 65 * 66 * Instead, we count the number of spaces printed 67 * since the last printing character so that we 68 * don't print trailing spaces. This avoids most 69 * of the wraparounds. 70 */ 71 static int db_output_position = 0; /* output column */ 72 static int db_last_non_space = 0; /* last non-space character */ 73 db_expr_t db_tab_stop_width = 8; /* how wide are tab stops? */ 74 #define NEXT_TAB(i) \ 75 ((((i) + db_tab_stop_width) / db_tab_stop_width) * db_tab_stop_width) 76 db_expr_t db_max_width = 79; /* output line width */ 77 db_expr_t db_lines_per_page = 20; /* lines per page */ 78 volatile int db_pager_quit; /* user requested quit */ 79 static int db_newlines; /* # lines this page */ 80 static int db_maxlines; /* max lines/page when paging */ 81 static int ddb_use_printf = 0; 82 SYSCTL_INT(_debug, OID_AUTO, ddb_use_printf, CTLFLAG_RW, &ddb_use_printf, 0, 83 "use printf for all ddb output"); 84 85 static void db_putc(int c); 86 static void db_puts(const char *str); 87 static void db_putchar(int c, void *arg); 88 static void db_pager(void); 89 90 /* 91 * Force pending whitespace. 92 */ 93 void 94 db_force_whitespace(void) 95 { 96 register int last_print, next_tab; 97 98 last_print = db_last_non_space; 99 while (last_print < db_output_position) { 100 next_tab = NEXT_TAB(last_print); 101 if (next_tab <= db_output_position) { 102 while (last_print < next_tab) { /* DON'T send a tab!!! */ 103 cnputc(' '); 104 db_capture_writech(' '); 105 last_print++; 106 } 107 } 108 else { 109 cnputc(' '); 110 db_capture_writech(' '); 111 last_print++; 112 } 113 } 114 db_last_non_space = db_output_position; 115 } 116 117 /* 118 * Output character. Buffer whitespace. 119 */ 120 static void 121 db_putchar(int c, void *arg) 122 { 123 struct dbputchar_arg *dap = arg; 124 125 if (dap->da_pbufr == NULL) { 126 127 /* No bufferized output is provided. */ 128 db_putc(c); 129 } else { 130 131 *dap->da_pnext++ = c; 132 dap->da_remain--; 133 134 /* Leave always the buffer 0 terminated. */ 135 *dap->da_pnext = '\0'; 136 137 /* Check if the buffer needs to be flushed. */ 138 if (dap->da_remain < 2 || c == '\n') { 139 db_puts(dap->da_pbufr); 140 dap->da_pnext = dap->da_pbufr; 141 dap->da_remain = dap->da_nbufr; 142 *dap->da_pnext = '\0'; 143 } 144 } 145 } 146 147 static void 148 db_putc(int c) 149 { 150 151 /* 152 * If not in the debugger or the user requests it, output data to 153 * both the console and the message buffer. 154 */ 155 if (!kdb_active || ddb_use_printf) { 156 printf("%c", c); 157 if (!kdb_active) 158 return; 159 if (c == '\r' || c == '\n') 160 db_check_interrupt(); 161 if (c == '\n' && db_maxlines > 0) { 162 db_newlines++; 163 if (db_newlines >= db_maxlines) 164 db_pager(); 165 } 166 return; 167 } 168 169 /* Otherwise, output data directly to the console. */ 170 if (c > ' ' && c <= '~') { 171 /* 172 * Printing character. 173 * If we have spaces to print, print them first. 174 * Use tabs if possible. 175 */ 176 db_force_whitespace(); 177 cnputc(c); 178 db_capture_writech(c); 179 db_output_position++; 180 db_last_non_space = db_output_position; 181 } 182 else if (c == '\n') { 183 /* Newline */ 184 cnputc(c); 185 db_capture_writech(c); 186 db_output_position = 0; 187 db_last_non_space = 0; 188 db_check_interrupt(); 189 if (db_maxlines > 0) { 190 db_newlines++; 191 if (db_newlines >= db_maxlines) 192 db_pager(); 193 } 194 } 195 else if (c == '\r') { 196 /* Return */ 197 cnputc(c); 198 db_capture_writech(c); 199 db_output_position = 0; 200 db_last_non_space = 0; 201 db_check_interrupt(); 202 } 203 else if (c == '\t') { 204 /* assume tabs every 8 positions */ 205 db_output_position = NEXT_TAB(db_output_position); 206 } 207 else if (c == ' ') { 208 /* space */ 209 db_output_position++; 210 } 211 else if (c == '\007') { 212 /* bell */ 213 cnputc(c); 214 /* No need to beep in a log: db_capture_writech(c); */ 215 } 216 /* other characters are assumed non-printing */ 217 } 218 219 static void 220 db_puts(const char *str) 221 { 222 int i; 223 224 for (i = 0; str[i] != '\0'; i++) 225 db_putc(str[i]); 226 } 227 228 /* 229 * Turn on the pager. 230 */ 231 void 232 db_enable_pager(void) 233 { 234 if (db_maxlines == 0) { 235 db_maxlines = db_lines_per_page; 236 db_newlines = 0; 237 db_pager_quit = 0; 238 } 239 } 240 241 /* 242 * Turn off the pager. 243 */ 244 void 245 db_disable_pager(void) 246 { 247 db_maxlines = 0; 248 } 249 250 /* 251 * A simple paging callout function. It supports several simple more(1)-like 252 * commands as well as a quit command that sets db_pager_quit which db 253 * commands can poll to see if they should terminate early. 254 */ 255 void 256 db_pager(void) 257 { 258 int c, done; 259 260 db_capture_enterpager(); 261 db_printf("--More--\r"); 262 done = 0; 263 while (!done) { 264 c = cngetc(); 265 switch (c) { 266 case 'e': 267 case 'j': 268 case '\n': 269 /* Just one more line. */ 270 db_maxlines = 1; 271 done++; 272 break; 273 case 'd': 274 /* Half a page. */ 275 db_maxlines = db_lines_per_page / 2; 276 done++; 277 break; 278 case 'f': 279 case ' ': 280 /* Another page. */ 281 db_maxlines = db_lines_per_page; 282 done++; 283 break; 284 case 'q': 285 case 'Q': 286 case 'x': 287 case 'X': 288 /* Quit */ 289 db_maxlines = 0; 290 db_pager_quit = 1; 291 done++; 292 break; 293 #if 0 294 /* FALLTHROUGH */ 295 default: 296 cnputc('\007'); 297 #endif 298 } 299 } 300 db_printf(" "); 301 db_force_whitespace(); 302 db_printf("\r"); 303 db_newlines = 0; 304 db_capture_exitpager(); 305 } 306 307 /* 308 * Return output position 309 */ 310 int 311 db_print_position(void) 312 { 313 return (db_output_position); 314 } 315 316 /* 317 * Printing 318 */ 319 int 320 db_printf(const char *fmt, ...) 321 { 322 #ifdef DDB_BUFR_SIZE 323 char bufr[DDB_BUFR_SIZE]; 324 #endif 325 struct dbputchar_arg dca; 326 va_list listp; 327 int retval; 328 329 #ifdef DDB_BUFR_SIZE 330 dca.da_pbufr = bufr; 331 dca.da_pnext = dca.da_pbufr; 332 dca.da_nbufr = sizeof(bufr); 333 dca.da_remain = sizeof(bufr); 334 *dca.da_pnext = '\0'; 335 #else 336 dca.da_pbufr = NULL; 337 #endif 338 339 va_start(listp, fmt); 340 retval = kvprintf (fmt, db_putchar, &dca, db_radix, listp); 341 va_end(listp); 342 343 #ifdef DDB_BUFR_SIZE 344 if (*dca.da_pbufr != '\0') 345 db_puts(dca.da_pbufr); 346 #endif 347 return (retval); 348 } 349 350 int db_indent; 351 352 void 353 db_iprintf(const char *fmt,...) 354 { 355 #ifdef DDB_BUFR_SIZE 356 char bufr[DDB_BUFR_SIZE]; 357 #endif 358 struct dbputchar_arg dca; 359 register int i; 360 va_list listp; 361 362 for (i = db_indent; i >= 8; i -= 8) 363 db_printf("\t"); 364 while (--i >= 0) 365 db_printf(" "); 366 367 #ifdef DDB_BUFR_SIZE 368 dca.da_pbufr = bufr; 369 dca.da_pnext = dca.da_pbufr; 370 dca.da_nbufr = sizeof(bufr); 371 dca.da_remain = sizeof(bufr); 372 *dca.da_pnext = '\0'; 373 #else 374 dca.da_pbufr = NULL; 375 #endif 376 377 va_start(listp, fmt); 378 kvprintf (fmt, db_putchar, &dca, db_radix, listp); 379 va_end(listp); 380 381 #ifdef DDB_BUFR_SIZE 382 if (*dca.da_pbufr != '\0') 383 db_puts(dca.da_pbufr); 384 #endif 385 } 386 387 /* 388 * End line if too long. 389 */ 390 void 391 db_end_line(int field_width) 392 { 393 if (db_output_position + field_width > db_max_width) 394 db_printf("\n"); 395 } 396