1 /*- 2 * SPDX-License-Identifier: MIT-CMU 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 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 /* 29 * Author: David B. Golub, Carnegie Mellon University 30 * Date: 7/90 31 */ 32 /* 33 * Lexical analyzer. 34 */ 35 36 #include <sys/cdefs.h> 37 __FBSDID("$FreeBSD$"); 38 39 #include <sys/param.h> 40 #include <sys/libkern.h> 41 #include <sys/lock.h> 42 43 #include <ddb/ddb.h> 44 #include <ddb/db_lex.h> 45 46 static char db_line[DB_MAXLINE]; 47 static char * db_lp, *db_endlp; 48 49 static int db_lex(int); 50 static void db_flush_line(void); 51 static int db_read_char(void); 52 static void db_unread_char(int); 53 54 int 55 db_read_line(void) 56 { 57 int i; 58 59 i = db_readline(db_line, sizeof(db_line)); 60 if (i == 0) 61 return (0); /* EOI */ 62 db_lp = db_line; 63 db_endlp = db_lp + i; 64 return (i); 65 } 66 67 /* 68 * Simulate a line of input into DDB. 69 */ 70 void 71 db_inject_line(const char *command) 72 { 73 74 strlcpy(db_line, command, sizeof(db_line)); 75 db_lp = db_line; 76 db_endlp = db_lp + strlen(command); 77 } 78 79 /* 80 * In rare cases, we may want to pull the remainder of the line input 81 * verbatim, rather than lexing it. For example, when assigning literal 82 * values associated with scripts. In that case, return a static pointer to 83 * the current location in the input buffer. The caller must be aware that 84 * the contents are not stable if other lex/input calls are made. 85 */ 86 char * 87 db_get_line(void) 88 { 89 90 return (db_lp); 91 } 92 93 static void 94 db_flush_line(void) 95 { 96 db_lp = db_line; 97 db_endlp = db_line; 98 } 99 100 static int 101 db_read_char(void) 102 { 103 int c; 104 105 if (db_lp >= db_endlp) 106 c = -1; 107 else 108 c = *db_lp++; 109 return (c); 110 } 111 112 static void 113 db_unread_char(int c) 114 { 115 116 if (c == -1) { 117 /* Unread EOL at EOL is okay. */ 118 if (db_lp < db_endlp) 119 db_error("db_unread_char(-1) before end of line\n"); 120 } else { 121 if (db_lp > db_line) { 122 db_lp--; 123 if (*db_lp != c) 124 db_error("db_unread_char() wrong char\n"); 125 } else { 126 db_error("db_unread_char() at beginning of line\n"); 127 } 128 } 129 } 130 131 static int db_look_token = 0; 132 133 void 134 db_unread_token(int t) 135 { 136 db_look_token = t; 137 } 138 139 int 140 db_read_token_flags(int flags) 141 { 142 int t; 143 144 MPASS((flags & ~(DRT_VALID_FLAGS_MASK)) == 0); 145 146 if (db_look_token) { 147 t = db_look_token; 148 db_look_token = 0; 149 } 150 else 151 t = db_lex(flags); 152 return (t); 153 } 154 155 db_expr_t db_tok_number; 156 char db_tok_string[TOK_STRING_SIZE]; 157 158 db_expr_t db_radix = 16; 159 160 void 161 db_flush_lex(void) 162 { 163 db_flush_line(); 164 db_look_token = 0; 165 } 166 167 static int 168 db_lex(int flags) 169 { 170 int c, n, radix_mode; 171 bool lex_wspace, lex_hex_numbers; 172 173 switch (flags & DRT_RADIX_MASK) { 174 case DRT_DEFAULT_RADIX: 175 radix_mode = -1; 176 break; 177 case DRT_OCTAL: 178 radix_mode = 8; 179 break; 180 case DRT_DECIMAL: 181 radix_mode = 10; 182 break; 183 case DRT_HEXADECIMAL: 184 radix_mode = 16; 185 break; 186 } 187 188 lex_wspace = ((flags & DRT_WSPACE) != 0); 189 lex_hex_numbers = ((flags & DRT_HEX) != 0); 190 191 c = db_read_char(); 192 for (n = 0; c <= ' ' || c > '~'; n++) { 193 if (c == '\n' || c == -1) 194 return (tEOL); 195 c = db_read_char(); 196 } 197 if (lex_wspace && n != 0) { 198 db_unread_char(c); 199 return (tWSPACE); 200 } 201 202 if ((c >= '0' && c <= '9') || 203 (lex_hex_numbers && 204 ((c >= 'a' && c <= 'f') || 205 (c >= 'A' && c <= 'F')))) { 206 /* number */ 207 int r, digit = 0; 208 209 if (radix_mode != -1) 210 r = radix_mode; 211 else if (c != '0') 212 r = db_radix; 213 else { 214 c = db_read_char(); 215 if (c == 'O' || c == 'o') 216 r = 8; 217 else if (c == 'T' || c == 't') 218 r = 10; 219 else if (c == 'X' || c == 'x') 220 r = 16; 221 else { 222 r = db_radix; 223 db_unread_char(c); 224 } 225 c = db_read_char(); 226 } 227 db_tok_number = 0; 228 for (;;) { 229 if (c >= '0' && c <= ((r == 8) ? '7' : '9')) 230 digit = c - '0'; 231 else if (r == 16 && ((c >= 'A' && c <= 'F') || 232 (c >= 'a' && c <= 'f'))) { 233 if (c >= 'a') 234 digit = c - 'a' + 10; 235 else if (c >= 'A') 236 digit = c - 'A' + 10; 237 } 238 else 239 break; 240 db_tok_number = db_tok_number * r + digit; 241 c = db_read_char(); 242 } 243 if ((c >= '0' && c <= '9') || 244 (c >= 'A' && c <= 'Z') || 245 (c >= 'a' && c <= 'z') || 246 (c == '_')) 247 { 248 db_error("Bad character in number\n"); 249 db_flush_lex(); 250 return (tEOF); 251 } 252 db_unread_char(c); 253 return (tNUMBER); 254 } 255 if ((c >= 'A' && c <= 'Z') || 256 (c >= 'a' && c <= 'z') || 257 c == '_' || c == '\\') 258 { 259 /* string */ 260 char *cp; 261 262 cp = db_tok_string; 263 if (c == '\\') { 264 c = db_read_char(); 265 if (c == '\n' || c == -1) 266 db_error("Bad escape\n"); 267 } 268 *cp++ = c; 269 while (1) { 270 c = db_read_char(); 271 if ((c >= 'A' && c <= 'Z') || 272 (c >= 'a' && c <= 'z') || 273 (c >= '0' && c <= '9') || 274 c == '_' || c == '\\' || c == ':' || c == '.') 275 { 276 if (c == '\\') { 277 c = db_read_char(); 278 if (c == '\n' || c == -1) 279 db_error("Bad escape\n"); 280 } 281 *cp++ = c; 282 if (cp == db_tok_string+sizeof(db_tok_string)) { 283 db_error("String too long\n"); 284 db_flush_lex(); 285 return (tEOF); 286 } 287 continue; 288 } 289 else { 290 *cp = '\0'; 291 break; 292 } 293 } 294 db_unread_char(c); 295 return (tIDENT); 296 } 297 298 switch (c) { 299 case '+': 300 return (tPLUS); 301 case '-': 302 return (tMINUS); 303 case '.': 304 c = db_read_char(); 305 if (c == '.') 306 return (tDOTDOT); 307 db_unread_char(c); 308 return (tDOT); 309 case '*': 310 return (tSTAR); 311 case '/': 312 return (tSLASH); 313 case '=': 314 c = db_read_char(); 315 if (c == '=') 316 return (tLOG_EQ); 317 db_unread_char(c); 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 c = db_read_char(); 335 if (c == '='){ 336 return (tLOG_NOT_EQ); 337 } 338 db_unread_char(c); 339 return (tEXCL); 340 case ':': 341 c = db_read_char(); 342 if (c == ':') 343 return (tCOLONCOLON); 344 db_unread_char(c); 345 return (tCOLON); 346 case ';': 347 return (tSEMI); 348 case '&': 349 c = db_read_char(); 350 if (c == '&') 351 return (tLOG_AND); 352 db_unread_char(c); 353 return (tBIT_AND); 354 case '|': 355 c = db_read_char(); 356 if (c == '|') 357 return (tLOG_OR); 358 db_unread_char(c); 359 return (tBIT_OR); 360 case '<': 361 c = db_read_char(); 362 if (c == '<') 363 return (tSHIFT_L); 364 if (c == '=') 365 return (tLESS_EQ); 366 db_unread_char(c); 367 return (tLESS); 368 case '>': 369 c = db_read_char(); 370 if (c == '>') 371 return (tSHIFT_R); 372 if (c == '=') 373 return (tGREATER_EQ); 374 db_unread_char(c); 375 return (tGREATER); 376 case '?': 377 return (tQUESTION); 378 case '~': 379 return (tBIT_NOT); 380 case -1: 381 return (tEOF); 382 } 383 db_printf("Bad character\n"); 384 db_flush_lex(); 385 return (tEOF); 386 } 387