1 /* $NetBSD: db_lex.c,v 1.27 2022/08/30 22:37:03 riastradh Exp $ */ 2 3 /* 4 * Mach Operating System 5 * Copyright (c) 1991,1990 Carnegie Mellon University 6 * All Rights Reserved. 7 * 8 * Permission to use, copy, modify and distribute this software and its 9 * documentation is hereby granted, provided that both the copyright 10 * notice and this permission notice appear in all copies of the 11 * software, derivative works or modified versions, and any portions 12 * thereof, and that both notices appear in supporting documentation. 13 * 14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 17 * 18 * Carnegie Mellon requests users of this software to return to 19 * 20 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 21 * School of Computer Science 22 * Carnegie Mellon University 23 * Pittsburgh PA 15213-3890 24 * 25 * any improvements or extensions that they make and grant Carnegie the 26 * rights to redistribute these changes. 27 * 28 * Author: David B. Golub, Carnegie Mellon University 29 * Date: 7/90 30 */ 31 32 /* 33 * Lexical analyzer. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: db_lex.c,v 1.27 2022/08/30 22:37:03 riastradh Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/cpu.h> 42 43 #include <ddb/ddb.h> 44 45 db_expr_t db_tok_number; 46 char db_tok_string[TOK_STRING_SIZE]; 47 48 static char db_line[DB_LINE_MAXLEN]; 49 static const char *db_lp; 50 static const char *db_endlp; 51 52 static int db_look_char = 0; 53 static int db_look_token = 0; 54 55 static void db_flush_line(void); 56 static int db_read_char(void); 57 static void db_unread_char(int); 58 static int db_lex(void); 59 60 int 61 db_read_line(void) 62 { 63 int i; 64 65 #ifdef _KERNEL 66 /* 67 * crash(8) prints the prompt using libedit. That's why we used to 68 * print it in db_readline(). But now people are using db_read_line() 69 * for general purpose input, so.. 70 */ 71 #ifdef MULTIPROCESSOR 72 db_printf("db{%ld}> ", (long)cpu_number()); 73 #else 74 db_printf("db> "); 75 #endif 76 #endif 77 i = db_readline(db_line, sizeof(db_line)); 78 if (i == 0) 79 return (0); /* EOI */ 80 db_set_line(db_line, db_line + i); 81 return (i); 82 } 83 84 void 85 db_set_line(const char *sp, const char *ep) 86 { 87 88 db_lp = sp; 89 db_endlp = ep; 90 } 91 92 void 93 db_get_line(const char **psp, const char **pep) 94 { 95 96 if (psp != NULL) 97 *psp = db_lp; 98 if (pep != NULL) 99 *pep = db_endlp; 100 } 101 102 static void 103 db_flush_line(void) 104 { 105 106 db_lp = db_line; 107 db_endlp = db_line; 108 } 109 110 static int 111 db_read_char(void) 112 { 113 int c; 114 115 if (db_look_char != 0) { 116 c = db_look_char; 117 db_look_char = 0; 118 } 119 else if (db_lp >= db_endlp) 120 c = -1; 121 else 122 c = *db_lp++; 123 return (c); 124 } 125 126 static void 127 db_unread_char(int c) 128 { 129 130 db_look_char = c; 131 } 132 133 void 134 db_unread_token(int t) 135 { 136 137 db_look_token = t; 138 } 139 140 int 141 db_read_token(void) 142 { 143 int t; 144 145 if (db_look_token) { 146 t = db_look_token; 147 db_look_token = 0; 148 } 149 else 150 t = db_lex(); 151 return (t); 152 } 153 154 int db_radix = 16; 155 156 /* 157 * Convert the number to a string in the current radix. 158 * This replaces the non-standard %n printf() format. 159 */ 160 161 char * 162 db_num_to_str(db_expr_t val) 163 { 164 165 /* 166 * 2 chars for "0x", 1 for a sign ("-") 167 * up to 21 chars for a 64-bit number: 168 * % echo 2^64 | bc | wc -c 169 * 21 170 * and 1 char for a terminal NUL 171 * 2+1+21+1 => 25 172 */ 173 static char buf[25]; 174 175 db_num_to_strbuf(val, buf, sizeof(buf)); 176 177 return (buf); 178 } 179 180 void 181 db_num_to_strbuf(db_expr_t val, char *buf, size_t len) 182 { 183 184 if (db_radix == 16) 185 snprintf(buf, len, "%" DDB_EXPR_FMT "x", val); 186 else if (db_radix == 8) 187 snprintf(buf, len, "%" DDB_EXPR_FMT "o", val); 188 else 189 snprintf(buf, len, "%" DDB_EXPR_FMT "u", val); 190 } 191 192 void 193 db_flush_lex(void) 194 { 195 196 db_flush_line(); 197 db_look_char = 0; 198 db_look_token = 0; 199 } 200 201 static int 202 db_lex(void) 203 { 204 int c; 205 206 c = db_read_char(); 207 while (c <= ' ' || c > '~') { 208 if (c == '\n' || c == -1) 209 return (tEOL); 210 c = db_read_char(); 211 } 212 213 if (c >= '0' && c <= '9') { 214 /* number */ 215 db_expr_t r, digit = 0; 216 217 if (c > '0') 218 r = db_radix; 219 else { 220 c = db_read_char(); 221 if (c == 'O' || c == 'o') 222 r = 8; 223 else if (c == 'T' || c == 't') 224 r = 10; 225 else if (c == 'X' || c == 'x') 226 r = 16; 227 else { 228 r = db_radix; 229 db_unread_char(c); 230 } 231 c = db_read_char(); 232 } 233 db_tok_number = 0; 234 for (;;) { 235 if (c >= '0' && c <= ((r == 8) ? '7' : '9')) 236 digit = c - '0'; 237 else if (r == 16) { 238 if (c >= 'A' && c <= 'F') 239 digit = c - 'A' + 10; 240 else if (c >= 'a' && c <= 'f') 241 digit = c - 'a' + 10; 242 else 243 break; 244 } else 245 break; 246 db_tok_number = db_tok_number * r + digit; 247 c = db_read_char(); 248 } 249 if ((c >= '0' && c <= '9') || 250 (c >= 'A' && c <= 'Z') || 251 (c >= 'a' && c <= 'z') || 252 (c == '_')) { 253 db_error("Bad character in number\n"); 254 /*NOTREACHED*/ 255 } 256 db_unread_char(c); 257 return (tNUMBER); 258 } 259 if ((c >= 'A' && c <= 'Z') || 260 (c >= 'a' && c <= 'z') || 261 c == '_' || c == '\\') { 262 /* string */ 263 char *cp; 264 265 cp = db_tok_string; 266 if (c == '\\') { 267 c = db_read_char(); 268 if (c == '\n' || c == -1) { 269 db_error("Bad escape\n"); 270 /*NOTREACHED*/ 271 } 272 } 273 *cp++ = c; 274 while (1) { 275 c = db_read_char(); 276 if ((c >= 'A' && c <= 'Z') || 277 (c >= 'a' && c <= 'z') || 278 (c >= '0' && c <= '9') || 279 c == '_' || c == '\\' || c == ':') { 280 if (c == '\\') { 281 c = db_read_char(); 282 if (c == '\n' || c == -1) { 283 db_error("Bad escape\n"); 284 /*NOTREACHED*/ 285 } 286 } 287 *cp++ = c; 288 if (cp == db_tok_string+sizeof(db_tok_string)) { 289 db_error("String too long\n"); 290 /*NOTREACHED*/ 291 } 292 continue; 293 } else { 294 *cp = '\0'; 295 break; 296 } 297 } 298 db_unread_char(c); 299 return (tIDENT); 300 } 301 302 switch (c) { 303 case '+': 304 return (tPLUS); 305 case '-': 306 return (tMINUS); 307 case '.': 308 c = db_read_char(); 309 if (c == '.') 310 return (tDOTDOT); 311 db_unread_char(c); 312 return (tDOT); 313 case '*': 314 return (tSTAR); 315 case '/': 316 return (tSLASH); 317 case '=': 318 return (tEQ); 319 case '%': 320 return (tPCT); 321 case '#': 322 return (tHASH); 323 case '(': 324 return (tLPAREN); 325 case ')': 326 return (tRPAREN); 327 case ',': 328 return (tCOMMA); 329 case '"': 330 return (tDITTO); 331 case '$': 332 return (tDOLLAR); 333 case '!': 334 return (tEXCL); 335 case '<': 336 c = db_read_char(); 337 if (c == '<') 338 return (tSHIFT_L); 339 db_unread_char(c); 340 break; 341 case '>': 342 c = db_read_char(); 343 if (c == '>') 344 return (tSHIFT_R); 345 db_unread_char(c); 346 break; 347 case -1: 348 return (tEOF); 349 } 350 db_printf("Bad character\n"); 351 db_flush_lex(); 352 return (tEOF); 353 } 354 355 /* 356 * Utility routine - discard tokens through end-of-line. 357 */ 358 void 359 db_skip_to_eol(void) 360 { 361 int t; 362 363 do { 364 t = db_read_token(); 365 } while (t != tEOL); 366 } 367 368 void 369 db_error(const char *s) 370 { 371 372 if (s) 373 db_printf("%s", s); 374 db_flush_lex(); 375 longjmp(db_recover); 376 } 377