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 <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/cons.h> 41 #include <sys/kdb.h> 42 #include <sys/kernel.h> 43 #include <sys/sysctl.h> 44 45 #include <machine/stdarg.h> 46 47 #include <ddb/ddb.h> 48 #include <ddb/db_output.h> 49 50 /* 51 * Character output - tracks position in line. 52 * To do this correctly, we should know how wide 53 * the output device is - then we could zero 54 * the line position when the output device wraps 55 * around to the start of the next line. 56 * 57 * Instead, we count the number of spaces printed 58 * since the last printing character so that we 59 * don't print trailing spaces. This avoids most 60 * of the wraparounds. 61 */ 62 static int db_output_position = 0; /* output column */ 63 static int db_last_non_space = 0; /* last non-space character */ 64 db_expr_t db_tab_stop_width = 8; /* how wide are tab stops? */ 65 #define NEXT_TAB(i) \ 66 ((((i) + db_tab_stop_width) / db_tab_stop_width) * db_tab_stop_width) 67 db_expr_t db_max_width = 79; /* output line width */ 68 db_expr_t db_lines_per_page = 20; /* lines per page */ 69 volatile int db_pager_quit; /* user requested quit */ 70 static int db_newlines; /* # lines this page */ 71 static int db_maxlines; /* max lines/page when paging */ 72 static int ddb_use_printf = 0; 73 SYSCTL_INT(_debug, OID_AUTO, ddb_use_printf, CTLFLAG_RW, &ddb_use_printf, 0, 74 "use printf for all ddb output"); 75 76 static void db_putchar(int c, void *arg); 77 static void db_pager(void); 78 79 /* 80 * Force pending whitespace. 81 */ 82 void 83 db_force_whitespace() 84 { 85 register int last_print, next_tab; 86 87 last_print = db_last_non_space; 88 while (last_print < db_output_position) { 89 next_tab = NEXT_TAB(last_print); 90 if (next_tab <= db_output_position) { 91 while (last_print < next_tab) { /* DON'T send a tab!!! */ 92 cnputc(' '); 93 last_print++; 94 } 95 } 96 else { 97 cnputc(' '); 98 last_print++; 99 } 100 } 101 db_last_non_space = db_output_position; 102 } 103 104 /* 105 * Output character. Buffer whitespace. 106 */ 107 static void 108 db_putchar(c, arg) 109 int c; /* character to output */ 110 void * arg; 111 { 112 113 /* 114 * If not in the debugger or the user requests it, output data to 115 * both the console and the message buffer. 116 */ 117 if (!kdb_active || ddb_use_printf) { 118 printf("%c", c); 119 if (!kdb_active) 120 return; 121 if (c == '\r' || c == '\n') 122 db_check_interrupt(); 123 if (c == '\n' && db_maxlines > 0) { 124 db_newlines++; 125 if (db_newlines >= db_maxlines) 126 db_pager(); 127 } 128 return; 129 } 130 131 /* Otherwise, output data directly to the console. */ 132 if (c > ' ' && c <= '~') { 133 /* 134 * Printing character. 135 * If we have spaces to print, print them first. 136 * Use tabs if possible. 137 */ 138 db_force_whitespace(); 139 cnputc(c); 140 db_output_position++; 141 db_last_non_space = db_output_position; 142 } 143 else if (c == '\n') { 144 /* Newline */ 145 cnputc(c); 146 db_output_position = 0; 147 db_last_non_space = 0; 148 db_check_interrupt(); 149 if (db_maxlines > 0) { 150 db_newlines++; 151 if (db_newlines >= db_maxlines) 152 db_pager(); 153 } 154 } 155 else if (c == '\r') { 156 /* Return */ 157 cnputc(c); 158 db_output_position = 0; 159 db_last_non_space = 0; 160 db_check_interrupt(); 161 } 162 else if (c == '\t') { 163 /* assume tabs every 8 positions */ 164 db_output_position = NEXT_TAB(db_output_position); 165 } 166 else if (c == ' ') { 167 /* space */ 168 db_output_position++; 169 } 170 else if (c == '\007') { 171 /* bell */ 172 cnputc(c); 173 } 174 /* other characters are assumed non-printing */ 175 } 176 177 /* 178 * Turn on the pager. 179 */ 180 void 181 db_enable_pager(void) 182 { 183 if (db_maxlines == 0) { 184 db_maxlines = db_lines_per_page; 185 db_newlines = 0; 186 db_pager_quit = 0; 187 } 188 } 189 190 /* 191 * Turn off the pager. 192 */ 193 void 194 db_disable_pager(void) 195 { 196 db_maxlines = 0; 197 } 198 199 /* 200 * A simple paging callout function. It supports several simple more(1)-like 201 * commands as well as a quit command that sets db_pager_quit which db 202 * commands can poll to see if they should terminate early. 203 */ 204 void 205 db_pager(void) 206 { 207 int c, done; 208 209 db_printf("--More--\r"); 210 done = 0; 211 while (!done) { 212 c = cngetc(); 213 switch (c) { 214 case 'e': 215 case 'j': 216 case '\n': 217 /* Just one more line. */ 218 db_maxlines = 1; 219 done++; 220 break; 221 case 'd': 222 /* Half a page. */ 223 db_maxlines = db_lines_per_page / 2; 224 done++; 225 break; 226 case 'f': 227 case ' ': 228 /* Another page. */ 229 db_maxlines = db_lines_per_page; 230 done++; 231 break; 232 case 'q': 233 case 'Q': 234 case 'x': 235 case 'X': 236 /* Quit */ 237 db_maxlines = 0; 238 db_pager_quit = 1; 239 done++; 240 break; 241 #if 0 242 /* FALLTHROUGH */ 243 default: 244 cnputc('\007'); 245 #endif 246 } 247 } 248 db_printf(" "); 249 db_force_whitespace(); 250 db_printf("\r"); 251 db_newlines = 0; 252 } 253 254 /* 255 * Return output position 256 */ 257 int 258 db_print_position() 259 { 260 return (db_output_position); 261 } 262 263 /* 264 * Printing 265 */ 266 void 267 #if __STDC__ 268 db_printf(const char *fmt, ...) 269 #else 270 db_printf(fmt) 271 const char *fmt; 272 #endif 273 { 274 va_list listp; 275 276 va_start(listp, fmt); 277 kvprintf (fmt, db_putchar, NULL, db_radix, listp); 278 va_end(listp); 279 } 280 281 int db_indent; 282 283 void 284 #if __STDC__ 285 db_iprintf(const char *fmt,...) 286 #else 287 db_iprintf(fmt) 288 const char *fmt; 289 #endif 290 { 291 register int i; 292 va_list listp; 293 294 for (i = db_indent; i >= 8; i -= 8) 295 db_printf("\t"); 296 while (--i >= 0) 297 db_printf(" "); 298 va_start(listp, fmt); 299 kvprintf (fmt, db_putchar, NULL, db_radix, listp); 300 va_end(listp); 301 } 302 303 /* 304 * End line if too long. 305 */ 306 void 307 db_end_line(int field_width) 308 { 309 if (db_output_position + field_width > db_max_width) 310 db_printf("\n"); 311 } 312