1 # Some tokenization primitives. 2 3 == code 4 # instruction effective address register displacement immediate 5 # . op subop mod rm32 base index scale r32 6 # . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes 7 8 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary) 9 # on reaching end of file, return an empty interval 10 next-token: # in: (addr stream byte), delimiter: byte, out: (addr slice) 11 # . prologue 12 55/push-ebp 13 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 14 # . save registers 15 50/push-eax 16 51/push-ecx 17 56/push-esi 18 57/push-edi 19 # esi = in 20 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi 21 # edi = out 22 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0x10/disp8 . # copy *(ebp+16) to edi 23 # skip-chars-matching(in, delimiter) 24 # . . push args 25 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 26 56/push-esi 27 # . . call 28 e8/call skip-chars-matching/disp32 29 # . . discard args 30 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 31 # out->start = &in->data[in->read] 32 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx 33 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy esi+ecx+12 to eax 34 89/copy 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # copy eax to *edi 35 # skip-chars-not-matching(in, delimiter) 36 # . . push args 37 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 38 56/push-esi 39 # . . call 40 e8/call skip-chars-not-matching/disp32 41 # . . discard args 42 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 43 # out->end = &in->data[in->read] 44 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx 45 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy esi+ecx+12 to eax 46 89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4) 47 # . restore registers 48 5f/pop-to-edi 49 5e/pop-to-esi 50 59/pop-to-ecx 51 58/pop-to-eax 52 # . epilogue 53 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 54 5d/pop-to-ebp 55 c3/return 56 57 test-next-token: 58 # . prologue 59 55/push-ebp 60 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 61 # setup 62 # . clear-stream(_test-stream) 63 # . . push args 64 68/push _test-stream/imm32 65 # . . call 66 e8/call clear-stream/disp32 67 # . . discard args 68 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 69 # var slice/ecx: slice 70 68/push 0/imm32/end 71 68/push 0/imm32/start 72 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 73 # write(_test-stream, " ab") 74 # . . push args 75 68/push " ab"/imm32 76 68/push _test-stream/imm32 77 # . . call 78 e8/call write/disp32 79 # . . discard args 80 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 81 # next-token(_test-stream, 0x20/space, slice) 82 # . . push args 83 51/push-ecx 84 68/push 0x20/imm32 85 68/push _test-stream/imm32 86 # . . call 87 e8/call next-token/disp32 88 # . . discard args 89 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 90 # check-ints-equal(slice->start - _test-stream->data, 2, msg) 91 # . check-ints-equal(slice->start - _test-stream, 14, msg) 92 # . . push args 93 68/push "F - test-next-token: start"/imm32 94 68/push 0xe/imm32 95 # . . push slice->start - _test-stream 96 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax 97 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax 98 50/push-eax 99 # . . call 100 e8/call check-ints-equal/disp32 101 # . . discard args 102 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 103 # check-ints-equal(slice->end - _test-stream->data, 4, msg) 104 # . check-ints-equal(slice->end - _test-stream, 16, msg) 105 # . . push args 106 68/push "F - test-next-token: end"/imm32 107 68/push 0x10/imm32 108 # . . push slice->end - _test-stream 109 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax 110 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax 111 50/push-eax 112 # . . call 113 e8/call check-ints-equal/disp32 114 # . . discard args 115 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 116 # . epilogue 117 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 118 5d/pop-to-ebp 119 c3/return 120 121 test-next-token-Eof: 122 # . prologue 123 55/push-ebp 124 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 125 # setup 126 # . clear-stream(_test-stream) 127 # . . push args 128 68/push _test-stream/imm32 129 # . . call 130 e8/call clear-stream/disp32 131 # . . discard args 132 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 133 # var slice/ecx: slice 134 68/push 0/imm32/end 135 68/push 0/imm32/start 136 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 137 # write nothing to _test-stream 138 # next-token(_test-stream, 0x20/space, slice) 139 # . . push args 140 51/push-ecx 141 68/push 0x20/imm32 142 68/push _test-stream/imm32 143 # . . call 144 e8/call next-token/disp32 145 # . . discard args 146 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 147 # check-ints-equal(slice->end, slice->start, msg) 148 # . . push args 149 68/push "F - test-next-token-Eof"/imm32 150 ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+4) 151 ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx 152 # . . call 153 e8/call check-ints-equal/disp32 154 # . . discard args 155 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 156 # . epilogue 157 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 158 5d/pop-to-ebp 159 c3/return 160 161 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary) 162 # on reaching end of file, return an empty interval 163 next-token-from-slice: # start: (addr byte), end: (addr byte), delimiter: byte, out: (addr slice) 164 # . prologue 165 55/push-ebp 166 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 167 # . save registers 168 50/push-eax 169 51/push-ecx 170 52/push-edx 171 57/push-edi 172 # ecx = end 173 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx 174 # edx = delimiter 175 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0x10/disp8 . # copy *(ebp+16) to edx 176 # edi = out 177 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0x14/disp8 . # copy *(ebp+20) to edi 178 # eax = skip-chars-matching-in-slice(start, end, delimiter) 179 # . . push args 180 52/push-edx 181 51/push-ecx 182 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 183 # . . call 184 e8/call skip-chars-matching-in-slice/disp32 185 # . . discard args 186 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 187 # out->start = eax 188 89/copy 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # copy eax to *edi 189 # eax = skip-chars-not-matching-in-slice(eax, end, delimiter) 190 # . . push args 191 52/push-edx 192 51/push-ecx 193 50/push-eax 194 # . . call 195 e8/call skip-chars-not-matching-in-slice/disp32 196 # . . discard args 197 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 198 # out->end = eax 199 89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4) 200 # . restore registers 201 5f/pop-to-edi 202 5a/pop-to-edx 203 59/pop-to-ecx 204 58/pop-to-eax 205 # . epilogue 206 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 207 5d/pop-to-ebp 208 c3/return 209 210 test-next-token-from-slice: 211 # . prologue 212 55/push-ebp 213 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 214 # (eax..ecx) = " ab" 215 b8/copy-to-eax " ab"/imm32 216 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 217 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx 218 05/add-to-eax 4/imm32 219 # var out/edi: slice 220 68/push 0/imm32/end 221 68/push 0/imm32/start 222 89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi 223 # next-token-from-slice(eax, ecx, 0x20/space, out) 224 # . . push args 225 57/push-edi 226 68/push 0x20/imm32 227 51/push-ecx 228 50/push-eax 229 # . . call 230 e8/call next-token-from-slice/disp32 231 # . . discard args 232 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp 233 # out->start should be at the 'a' 234 # . check-ints-equal(out->start - in->start, 2, msg) 235 # . . push args 236 68/push "F - test-next-token-from-slice: start"/imm32 237 68/push 2/imm32 238 # . . push out->start - in->start 239 8b/copy 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # copy *edi to ecx 240 2b/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract eax from ecx 241 51/push-ecx 242 # . . call 243 e8/call check-ints-equal/disp32 244 # . . discard args 245 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 246 # out->end should be after the 'b' 247 # check-ints-equal(out->end - in->start, 4, msg) 248 # . . push args 249 68/push "F - test-next-token-from-slice: end"/imm32 250 68/push 4/imm32 251 # . . push out->end - in->start 252 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx 253 2b/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract eax from ecx 254 51/push-ecx 255 # . . call 256 e8/call check-ints-equal/disp32 257 # . . discard args 258 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 259 # . epilogue 260 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 261 5d/pop-to-ebp 262 c3/return 263 264 test-next-token-from-slice-Eof: 265 # . prologue 266 55/push-ebp 267 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 268 # var out/edi: slice 269 68/push 0/imm32/end 270 68/push 0/imm32/start 271 89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi 272 # next-token-from-slice(0, 0, 0x20/space, out) 273 # . . push args 274 57/push-edi 275 68/push 0x20/imm32 276 68/push 0/imm32 277 68/push 0/imm32 278 # . . call 279 e8/call next-token-from-slice/disp32 280 # . . discard args 281 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp 282 # out should be empty 283 # . check-ints-equal(out->end - out->start, 0, msg) 284 # . . push args 285 68/push "F - test-next-token-from-slice-Eof"/imm32 286 68/push 0/imm32 287 # . . push out->start - in->start 288 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx 289 2b/subtract 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # subtract *edi from ecx 290 51/push-ecx 291 # . . call 292 e8/call check-ints-equal/disp32 293 # . . discard args 294 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 295 # . epilogue 296 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 297 5d/pop-to-ebp 298 c3/return 299 300 test-next-token-from-slice-nothing: 301 # . prologue 302 55/push-ebp 303 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 304 # (eax..ecx) = " " 305 b8/copy-to-eax " "/imm32 306 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 307 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx 308 05/add-to-eax 4/imm32 309 # var out/edi: slice 310 68/push 0/imm32/end 311 68/push 0/imm32/start 312 89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi 313 # next-token-from-slice(in, 0x20/space, out) 314 # . . push args 315 57/push-edi 316 68/push 0x20/imm32 317 51/push-ecx 318 50/push-eax 319 # . . call 320 e8/call next-token-from-slice/disp32 321 # . . discard args 322 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp 323 # out should be empty 324 # . check-ints-equal(out->end - out->start, 0, msg) 325 # . . push args 326 68/push "F - test-next-token-from-slice-Eof"/imm32 327 68/push 0/imm32 328 # . . push out->start - in->start 329 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx 330 2b/subtract 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # subtract *edi from ecx 331 51/push-ecx 332 # . . call 333 e8/call check-ints-equal/disp32 334 # . . discard args 335 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 336 # . epilogue 337 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 338 5d/pop-to-ebp 339 c3/return 340 341 skip-chars-matching: # in: (addr stream byte), delimiter: byte 342 # . prologue 343 55/push-ebp 344 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 345 # . save registers 346 50/push-eax 347 51/push-ecx 348 52/push-edx 349 53/push-ebx 350 56/push-esi 351 # esi = in 352 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi 353 # ecx = in->read 354 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx 355 # ebx = in->write 356 8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx 357 # edx = delimiter 358 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx 359 $skip-chars-matching:loop: 360 # if (in->read >= in->write) break 361 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx 362 7d/jump-if->= $skip-chars-matching:end/disp8 363 # eax = in->data[in->read] 364 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax 365 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL 366 # if (eax != delimiter) break 367 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax and edx 368 75/jump-if-!= $skip-chars-matching:end/disp8 369 # ++in->read 370 41/increment-ecx 371 eb/jump $skip-chars-matching:loop/disp8 372 $skip-chars-matching:end: 373 # persist in->read 374 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4) 375 # . restore registers 376 5e/pop-to-esi 377 5b/pop-to-ebx 378 5a/pop-to-edx 379 59/pop-to-ecx 380 58/pop-to-eax 381 # . epilogue 382 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 383 5d/pop-to-ebp 384 c3/return 385 386 test-skip-chars-matching: 387 # setup 388 # . clear-stream(_test-stream) 389 # . . push args 390 68/push _test-stream/imm32 391 # . . call 392 e8/call clear-stream/disp32 393 # . . discard args 394 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 395 # write(_test-stream, " ab") 396 # . . push args 397 68/push " ab"/imm32 398 68/push _test-stream/imm32 399 # . . call 400 e8/call write/disp32 401 # . . discard args 402 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 403 # skip-chars-matching(_test-stream, 0x20/space) 404 # . . push args 405 68/push 0x20/imm32 406 68/push _test-stream/imm32 407 # . . call 408 e8/call skip-chars-matching/disp32 409 # . . discard args 410 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 411 # check-ints-equal(_test-stream->read, 2, msg) 412 # . . push args 413 68/push "F - test-skip-chars-matching"/imm32 414 68/push 2/imm32 415 # . . push *_test-stream->read 416 b8/copy-to-eax _test-stream/imm32 417 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 . # push *(eax+4) 418 # . . call 419 e8/call check-ints-equal/disp32 420 # . . discard args 421 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 422 # end 423 c3/return 424 425 test-skip-chars-matching-none: 426 # setup 427 # . clear-stream(_test-stream) 428 # . . push args 429 68/push _test-stream/imm32 430 # . . call 431 e8/call clear-stream/disp32 432 # . . discard args 433 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 434 # write(_test-stream, "ab") 435 # . . push args 436 68/push "ab"/imm32 437 68/push _test-stream/imm32 438 # . . call 439 e8/call write/disp32 440 # . . discard args 441 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 442 # skip-chars-matching(_test-stream, 0x20/space) 443 # . . push args 444 68/push 0x20/imm32 445 68/push _test-stream/imm32 446 # . . call 447 e8/call skip-chars-matching/disp32 448 # . . discard args 449 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 450 # check-ints-equal(_test-stream->read, 0, msg) 451 # . . push args 452 68/push "F - test-skip-chars-matching-none"/imm32 453 68/push 0/imm32 454 # . . push *_test-stream->read 455 b8/copy-to-eax _test-stream/imm32 456 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 . # push *(eax+4) 457 # . . call 458 e8/call check-ints-equal/disp32 459 # . . discard args 460 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 461 # end 462 c3/return 463 464 skip-chars-matching-whitespace: # in: (addr stream byte) 465 # . prologue 466 55/push-ebp 467 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 468 # . save registers 469 50/push-eax 470 51/push-ecx 471 53/push-ebx 472 56/push-esi 473 # esi = in 474 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi 475 # ecx = in->read 476 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx 477 # ebx = in->write 478 8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx 479 $skip-chars-matching-whitespace:loop: 480 # if (in->read >= in->write) break 481 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx 482 7d/jump-if->= $skip-chars-matching-whitespace:end/disp8 483 # eax = in->data[in->read] 484 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax 485 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL 486 # if (eax == ' ') goto body 487 3d/compare-eax-and 0x20/imm32/space 488 74/jump-if-= $skip-chars-matching-whitespace:body/disp8 489 # if (eax == '\n') goto body 490 3d/compare-eax-and 0x0a/imm32/newline 491 74/jump-if-= $skip-chars-matching-whitespace:body/disp8 492 # if (eax == '\t') goto body 493 3d/compare-eax-and 0x09/imm32/tab 494 74/jump-if-= $skip-chars-matching-whitespace:body/disp8 495 # if (eax != '\r') break 496 3d/compare-eax-and 0x0d/imm32/cr 497 75/jump-if-!= $skip-chars-matching-whitespace:end/disp8 498 $skip-chars-matching-whitespace:body: 499 # ++in->read 500 41/increment-ecx 501 eb/jump $skip-chars-matching-whitespace:loop/disp8 502 $skip-chars-matching-whitespace:end: 503 # persist in->read 504 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4) 505 # . restore registers 506 5e/pop-to-esi 507 5b/pop-to-ebx 508 59/pop-to-ecx 509 58/pop-to-eax 510 # . epilogue 511 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 512 5d/pop-to-ebp 513 c3/return 514 515 test-skip-chars-matching-whitespace: 516 # setup 517 # . clear-stream(_test-stream) 518 # . . push args 519 68/push _test-stream/imm32 520 # . . call 521 e8/call clear-stream/disp32 522 # . . discard args 523 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 524 # write(_test-stream, " \nab") 525 # . . push args 526 68/push " \nab"/imm32 527 68/push _test-stream/imm32 528 # . . call 529 e8/call write/disp32 530 # . . discard args 531 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 532 # skip-chars-matching-whitespace(_test-stream) 533 # . . push args 534 68/push _test-stream/imm32 535 # . . call 536 e8/call skip-chars-matching-whitespace/disp32 537 # . . discard args 538 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 539 # check-ints-equal(_test-stream->read, 2, msg) 540 # . . push args 541 68/push "F - test-skip-chars-matching-whitespace"/imm32 542 68/push 2/imm32 543 # . . push *_test-stream->read 544 b8/copy-to-eax _test-stream/imm32 545 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 . # push *(eax+4) 546 # . . call 547 e8/call check-ints-equal/disp32 548 # . . discard args 549 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 550 # end 551 c3/return 552 553 # minor fork of 'skip-chars-matching' 554 skip-chars-not-matching: # in: (addr stream byte), delimiter: byte 555 # . prologue 556 55/push-ebp 557 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 558 # . save registers 559 50/push-eax 560 51/push-ecx 561 52/push-edx 562 53/push-ebx 563 56/push-esi 564 # esi = in 565 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi 566 # ecx = in->read 567 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx 568 # ebx = in->write 569 8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx 570 # edx = delimiter 571 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx 572 $skip-chars-not-matching:loop: 573 # if (in->read >= in->write) break 574 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx 575 7d/jump-if->= $skip-chars-not-matching:end/disp8 576 # eax = in->data[in->read] 577 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax 578 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL 579 # if (eax == delimiter) break 580 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax and edx 581 74/jump-if-= $skip-chars-not-matching:end/disp8 582 # ++in->read 583 41/increment-ecx 584 eb/jump $skip-chars-not-matching:loop/disp8 585 $skip-chars-not-matching:end: 586 # persist in->read 587 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4) 588 # . restore registers 589 5e/pop-to-esi 590 5b/pop-to-ebx 591 5a/pop-to-edx 592 59/pop-to-ecx 593 58/pop-to-eax 594 # . epilogue 595 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 596 5d/pop-to-ebp 597 c3/return 598 599 test-skip-chars-not-matching: 600 # setup 601 # . clear-stream(_test-stream) 602 # . . push args 603 68/push _test-stream/imm32 604 # . . call 605 e8/call clear-stream/disp32 606 # . . discard args 607 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 608 # write(_test-stream, "ab ") 609 # . . push args 610 68/push "ab "/imm32 611 68/push _test-stream/imm32 612 # . . call 613 e8/call write/disp32 614 # . . discard args 615 81 0/subop/add 3/mod/direct 4/rm32/esp . . . pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long *//* * $LynxId: LYStyle.c,v 1.97 2016/10/12 00:50:05 tom Exp $ * * character level styles for Lynx * (c) 1996 Rob Partington -- donated to the Lyncei (if they want it :-) */ #include <HTUtils.h> #include <HTML.h> #include <LYGlobalDefs.h> #include <LYStructs.h> #include <LYReadCFG.h> #include <LYCurses.h> #include <LYCharUtils.h> #include <LYUtils.h> /* defines TABLESIZE */ #include <AttrList.h> #include <SGML.h> #include <HTMLDTD.h> /* Hash table definitions */ #include <LYHash.h> #include <LYStyle.h> #include <LYOptions.h> #include <LYPrettySrc.h> #include <LYexit.h> #include <LYLeaks.h> #include <LYStrings.h> #include <LYHash.h> #define CTRACE1(p) CTRACE2(TRACE_CFG || TRACE_STYLE, p) #ifdef USE_COLOR_STYLE static HTList *list_of_lss_files; /* because curses isn't started when we parse the config file, we * need to remember the STYLE: lines we encounter and parse them * after curses has started */ static HTList *lss_styles = NULL; #define CACHEW 128 #define CACHEH 64 static unsigned *cached_styles_ptr = NULL; static int cached_styles_rows = 0; static int cached_styles_cols = 0; static BOOL empty_lss_list = FALSE; /* true if list explicitly emptied */ /* stack of attributes during page rendering */ int last_styles[MAX_LAST_STYLES + 1] = {0}; int last_colorattr_ptr = 0; bucket hashStyles[CSHASHSIZE]; int cached_tag_styles[HTML_ELEMENTS]; int current_tag_style; BOOL force_current_tag_style = FALSE; char *forced_classname; BOOL force_classname; /* Remember the hash codes for common elements */ int s_a = NOSTYLE; int s_aedit = NOSTYLE; int s_aedit_arr = NOSTYLE; int s_aedit_pad = NOSTYLE; int s_aedit_sel = NOSTYLE; int s_alert = NOSTYLE; int s_alink = NOSTYLE; int s_curedit = NOSTYLE; int s_forw_backw = NOSTYLE; int s_hot_paste = NOSTYLE; int s_menu_active = NOSTYLE; int s_menu_bg = NOSTYLE; int s_menu_entry = NOSTYLE; int s_menu_frame = NOSTYLE; int s_menu_number = NOSTYLE; int s_menu_sb = NOSTYLE; int s_normal = NOSTYLE; int s_prompt_edit = NOSTYLE; int s_prompt_edit_arr = NOSTYLE; int s_prompt_edit_pad = NOSTYLE; int s_prompt_sel = NOSTYLE; int s_status = NOSTYLE; int s_title = NOSTYLE; int s_whereis = NOSTYLE; #ifdef USE_SCROLLBAR int s_sb_aa = NOSTYLE; int s_sb_bar = NOSTYLE; int s_sb_bg = NOSTYLE; int s_sb_naa = NOSTYLE; #endif /* start somewhere safe */ #define MAX_COLOR 16 static int colorPairs = 0; #ifdef USE_BLINK # define MAX_BLINK 2 # define M_BLINK A_BLINK #else # define MAX_BLINK 1 # define M_BLINK 0 #endif #define MAX_PAIR 255 /* because our_pairs[] type is unsigned-char */ static unsigned char our_pairs[2] [MAX_BLINK] [MAX_COLOR + 1] [MAX_COLOR + 1]; static void style_initialiseHashTable(void); static bucket *new_bucket(const char *name) { bucket *result = typecalloc(bucket); if (!result) outofmem(__FILE__, "new_bucket"); StrAllocCopy(result->name, name); return result; } #if OMIT_SCN_KEEPING bucket *special_bucket(void) { return new_bucket("<special>"); } #endif bucket *nostyle_bucket(void) { return new_bucket("<NOSTYLE>"); } static char *TrimLowercase(char *buffer) { LYRemoveBlanks(buffer); strtolower(buffer); return buffer; } /* * Parse a string containing a combination of video attributes and color. */ static void parse_either(const char *attrs, int dft_color, int *monop, int *colorp) { int value; char *temp_attrs = NULL; if (StrAllocCopy(temp_attrs, attrs) != NULL) { char *to_free = temp_attrs; while (*temp_attrs != '\0') { char *next = StrChr(temp_attrs, '+'); char save = (char) ((next != NULL) ? *next : '\0'); if (next == NULL) next = temp_attrs + strlen(temp_attrs); if (save != 0) *next = '\0'; if ((value = string_to_attr(temp_attrs)) != 0) *monop |= value; else if (colorp != 0 && (value = check_color(temp_attrs, dft_color)) != ERR_COLOR) *colorp = value; temp_attrs = next; if (save != '\0') *temp_attrs++ = save; } FREE(to_free); } } /* icky parsing of the style options */ static void parse_attributes(const char *mono, const char *fg, const char *bg, int style, const char *element) { int mA = A_NORMAL; int fA = default_fg; int bA = default_bg; int cA = A_NORMAL; int newstyle = hash_code(element); int colored_attr; CTRACE2(TRACE_STYLE, (tfp, "CSS(PA):style d=%d / h=%d, e=%s\n", style, newstyle, element)); parse_either(mono, ERR_COLOR, &mA, (int *) 0); parse_either(bg, default_bg, &cA, &bA); parse_either(fg, default_fg, &cA, &fA); if (style == -1) { /* default */ CTRACE2(TRACE_STYLE, (tfp, "CSS(DEF):default_fg=%d, default_bg=%d\n", fA, bA)); default_fg = fA; default_bg = bA; default_color_reset = TRUE; return; } if (fA == NO_COLOR) { bA = NO_COLOR; } else if (COLORS) { #ifdef USE_BLINK if (term_blink_is_boldbg) { if (fA >= COLORS) cA = A_BOLD; if (bA >= COLORS) cA |= M_BLINK; } else #endif if (fA >= COLORS || bA >= COLORS) cA = A_BOLD; if (fA >= COLORS) fA %= COLORS; if (bA >= COLORS) bA %= COLORS; } else { cA = A_BOLD; fA = NO_COLOR; bA = NO_COLOR; } /* * If we have colour, and space to create a new colour attribute, * and we have a valid colour description, then add this style */ if (lynx_has_color && colorPairs < COLOR_PAIRS - 1 && fA != NO_COLOR) { int curPair = 0; int iFg = (1 + (fA >= 0 ? fA : 0)); int iBg = (1 + (bA >= 0 ? bA : 0)); int iBold = !!((unsigned) cA & A_BOLD); int iBlink = !!((unsigned) cA & M_BLINK); CTRACE2(TRACE_STYLE, (tfp, "parse_attributes %d/%d %d/%d %#x\n", fA, default_fg, bA, default_bg, cA)); if (fA < MAX_COLOR && bA < MAX_COLOR #ifdef USE_CURSES_PAIR_0 && (cA != A_NORMAL || fA != default_fg || bA != default_bg) #endif && curPair < MAX_PAIR) { if (our_pairs[iBold][iBlink][iFg][iBg] != 0) { curPair = our_pairs[iBold][iBlink][iFg][iBg]; } else { curPair = ++colorPairs; init_pair((short) curPair, (short) fA, (short) bA); our_pairs[iBold][iBlink][iFg][iBg] = UCH(curPair); } } CTRACE2(TRACE_STYLE, (tfp, "CSS(CURPAIR):%d\n", curPair)); colored_attr = ((int) COLOR_PAIR(curPair)) | ((int) cA); if (style < DSTYLE_ELEMENTS) setStyle(style, colored_attr, cA, mA); setHashStyle(newstyle, colored_attr, cA, mA, element); } else { if (lynx_has_color && fA != NO_COLOR) { CTRACE2(TRACE_STYLE, (tfp, "CSS(NC): maximum of %d colorpairs exhausted\n", COLOR_PAIRS - 1)); } /* only mono is set */ if (style < DSTYLE_ELEMENTS) setStyle(style, -1, -1, mA); setHashStyle(newstyle, -1, -1, mA, element); } } /* parse a style option of the format * STYLE:<OBJECT>:FG:BG */ static void parse_style(char *param) { /* *INDENT-OFF* */ static struct { const char *name; int style; int *set_hash; } table[] = { { "default", -1, 0 }, /* default fg/bg */ { "alink", DSTYLE_ALINK, 0 }, /* active link */ { "a", DSTYLE_LINK, 0 }, /* normal link */ { "a", HTML_A, 0 }, /* normal link */ { "status", DSTYLE_STATUS, 0 }, /* status bar */ { "label", DSTYLE_OPTION, 0 }, /* [INLINE]'s */ { "value", DSTYLE_VALUE, 0 }, /* [INLINE]'s */ { "normal", DSTYLE_NORMAL, 0 }, { "candy", DSTYLE_CANDY, 0 }, /* [INLINE]'s */ { "whereis", DSTYLE_WHEREIS, &s_whereis }, { "edit.active.pad", DSTYLE_ELEMENTS, &s_aedit_pad }, { "edit.active.arrow", DSTYLE_ELEMENTS, &s_aedit_arr }, { "edit.active.marked", DSTYLE_ELEMENTS, &s_aedit_sel }, { "edit.active", DSTYLE_ELEMENTS, &s_aedit }, { "edit.current", DSTYLE_ELEMENTS, &s_curedit }, { "edit.prompt.pad", DSTYLE_ELEMENTS, &s_prompt_edit_pad }, { "edit.prompt.arrow", DSTYLE_ELEMENTS, &s_prompt_edit_arr }, { "edit.prompt.marked", DSTYLE_ELEMENTS, &s_prompt_sel }, { "edit.prompt", DSTYLE_ELEMENTS, &s_prompt_edit }, { "forwbackw.arrow", DSTYLE_ELEMENTS, &s_forw_backw }, { "hot.paste", DSTYLE_ELEMENTS, &s_hot_paste }, { "menu.frame", DSTYLE_ELEMENTS, &s_menu_frame }, { "menu.bg", DSTYLE_ELEMENTS, &s_menu_bg }, { "menu.n", DSTYLE_ELEMENTS, &s_menu_number }, { "menu.entry", DSTYLE_ELEMENTS, &s_menu_entry }, { "menu.active", DSTYLE_ELEMENTS, &s_menu_active }, { "menu.sb", DSTYLE_ELEMENTS, &s_menu_sb }, }; /* *INDENT-ON* */ unsigned n; BOOL found = FALSE; char *buffer = 0; char *tmp = 0; char *element, *mono; const char *fg, *bg; if (param == 0) return; CTRACE2(TRACE_STYLE, (tfp, "parse_style(%s)\n", param)); StrAllocCopy(buffer, param); if (buffer == 0) return; TrimLowercase(buffer); if ((tmp = StrChr(buffer, ':')) == 0) { fprintf(stderr, gettext("\ Syntax Error parsing style in lss file:\n\ [%s]\n\ The line must be of the form:\n\ OBJECT:MONO:COLOR (ie em:bold:brightblue:white)\n\ where OBJECT is one of EM,STRONG,B,I,U,BLINK etc.\n\n"), buffer); exit_immediately(EXIT_FAILURE); } *tmp = '\0'; element = buffer; mono = tmp + 1; tmp = StrChr(mono, ':'); if (!tmp) { fg = "nocolor"; bg = "nocolor"; } else { *tmp = '\0'; fg = tmp + 1; tmp = StrChr(fg, ':'); if (!tmp) bg = "default"; else { *tmp = '\0'; bg = tmp + 1; } } CTRACE2(TRACE_STYLE, (tfp, "CSSPARSE:%s => %d %s\n", element, hash_code(element), (hashStyles[hash_code(element)].name ? "used" : ""))); /* * We use some pseudo-elements, so catch these first */ for (n = 0; n < TABLESIZE(table); n++) { if (!strcasecomp(element, table[n].name)) { parse_attributes(mono, fg, bg, table[n].style, table[n].name); if (table[n].set_hash != 0) *(table[n].set_hash) = hash_code(table[n].name); found = TRUE; break; } } if (found) { if (!strcasecomp(element, "normal")) { /* added - kw */ parse_attributes(mono, fg, bg, DSTYLE_NORMAL, "html"); s_normal = hash_code("html"); /* rather bizarre... - kw */ LYnormalColor(); } } else { /* It must be a HTML element, so look through the list until we find it. */ int element_number = -1; HTTag *t = SGMLFindTag(&HTML_dtd, element); if (t && t->name) { element_number = (int) (t - HTML_dtd.tags); } if (element_number >= HTML_A && element_number < HTML_ELEMENTS) { parse_attributes(mono, fg, bg, element_number + STARTAT, element); } else { parse_attributes(mono, fg, bg, DSTYLE_ELEMENTS, element); } } FREE(buffer); } static void style_deleteStyleList(void) { LYFreeStringList(lss_styles); lss_styles = NULL; } static void free_lss_list(void) { LSS_NAMES *obj; while ((obj = HTList_objectAt(list_of_lss_files, 0)) != 0) { if (HTList_unlinkObject(list_of_lss_files, obj)) { FREE(obj->given); FREE(obj->actual); FREE(obj); } else { break; } } HTList_delete(list_of_lss_files); } static void free_colorstylestuff(void) { style_initialiseHashTable(); style_deleteStyleList(); memset(our_pairs, 0, sizeof(our_pairs)); FreeCachedStyles(); } /* Set all the buckets in the hash table to be empty */ static void style_initialiseHashTable(void) { int i; static int firsttime = 1; for (i = 0; i < CSHASHSIZE; i++) { if (firsttime) hashStyles[i].name = NULL; else FREE(hashStyles[i].name); hashStyles[i].color = 0; hashStyles[i].cattr = 0; hashStyles[i].mono = 0; } if (firsttime) { firsttime = 0; #ifdef LY_FIND_LEAKS atexit(free_colorstylestuff); atexit(free_colorstyle_leaks); #endif } s_alink = hash_code("alink"); s_a = hash_code("a"); s_status = hash_code("status"); s_alert = hash_code("alert"); s_title = hash_code("title"); #ifdef USE_SCROLLBAR s_sb_bar = hash_code("scroll.bar"); s_sb_bg = hash_code("scroll.back"); s_sb_aa = hash_code("scroll.arrow"); s_sb_naa = hash_code("scroll.noarrow"); #endif } /* * Initialise the default style sheet to match the vanilla-curses lynx. */ static void initialise_default_stylesheet(void) { /* Use the data setup in USE_COLOR_TABLE */ /* *INDENT-OFF* */ static const struct { int color; /* index into lynx_color_pairs[] */ const char *type; } table2[] = { /* * non-color-style colors encode bold/reverse/underline as a 0-7 * index like this: * b,r,u 0 * b,r,U 1 * b,R,u 2 * b,R,U 3 * B,r,u 4 * B,r,U 5 * B,R,u 6 * B,R,U 7 */ { 0, "normal" }, { 1, "a" }, { 2, "status" }, { 4, "b" }, { 4, "blink" }, { 4, "cite" }, { 4, "del" }, { 4, "em" }, { 4, "i" }, { 4, "ins" }, { 4, "strike" }, { 4, "strong" }, { 4, "u" }, { 5, "input" }, { 6, "alink" }, { 7, "whereis" }, #ifdef USE_PRETTYSRC /* FIXME: HTL_tagspecs_defaults[] has similar info */ { 4, "span.htmlsrc_comment" }, { 4, "span.htmlsrc_tag" }, { 4, "span.htmlsrc_attrib" }, { 4, "span.htmlsrc_attrval" }, { 4, "span.htmlsrc_abracket" }, { 4, "span.htmlsrc_entity" }, { 4, "span.htmlsrc_href" }, { 4, "span.htmlsrc_entire" }, { 4, "span.htmlsrc_badseq" }, { 4, "span.htmlsrc_badtag" }, { 4, "span.htmlsrc_badattr" }, { 4, "span.htmlsrc_sgmlspecial" }, #endif }; /* *INDENT-ON* */ unsigned n; char *normal = LYgetTableString(0); char *strong = LYgetTableString(4); CTRACE1((tfp, "initialise_default_stylesheet\n")); /* * For debugging this function, create hash codes for all of the tags. * That makes it simpler to find the cases that are overlooked in the * table. */ for (n = 0; n < (unsigned) HTML_dtd.number_of_tags; ++n) { char *name = 0; HTSprintf0(&name, "%s:%s", HTML_dtd.tags[n].name, normal); parse_style(name); FREE(name); } for (n = 0; n < TABLESIZE(table2); ++n) { int code = table2[n].color; char *name = 0; char *value = 0; switch (code) { case 0: value = normal; break; case 4: value = strong; break; default: value = LYgetTableString(code); break; } HTSprintf0(&name, "%s:%s", table2[n].type, value); parse_style(name); FREE(name); if (value != normal && value != strong && value != 0) free(value); } FREE(normal); FREE(strong); } void parse_userstyles(void) { char *name; HTList *cur = LYuse_color_style ? lss_styles : 0; colorPairs = 0; style_initialiseHashTable(); if (HTList_isEmpty(cur)) { initialise_default_stylesheet(); } else { while ((name = (char *) HTList_nextObject(cur)) != NULL) { CTRACE2(TRACE_STYLE, (tfp, "LSS:%s\n", (name ? name : "!?! empty !?!"))); if (name != NULL) parse_style(name); } } #define dft_style(a,b) if (a == NOSTYLE) a = b /* *INDENT-OFF* */ dft_style(s_prompt_edit, s_normal); dft_style(s_prompt_edit_arr, s_prompt_edit); dft_style(s_prompt_edit_pad, s_prompt_edit); dft_style(s_prompt_sel, s_prompt_edit); dft_style(s_aedit, s_alink); dft_style(s_aedit_arr, s_aedit); dft_style(s_aedit_pad, s_aedit); dft_style(s_curedit, s_aedit); dft_style(s_aedit_sel, s_aedit); dft_style(s_menu_bg, s_normal); dft_style(s_menu_entry, s_menu_bg); dft_style(s_menu_frame, s_menu_bg); dft_style(s_menu_number, s_menu_bg); dft_style(s_menu_active, s_alink); /* *INDENT-ON* */ } /* Add a STYLE: option line to our list. Process "default:" early * for it to have the same semantic as other lines: works at any place * of the style file, the first line overrides the later ones. */ static void HStyle_addStyle(char *buffer) { char *name = NULL; CTRACE1((tfp, "HStyle_addStyle(%s)\n", buffer)); StrAllocCopy(name, buffer); TrimLowercase(name); if (lss_styles == NULL) lss_styles = HTList_new(); if (!strncasecomp(name, "default:", 8)) { /* default fg/bg */ CTRACE2(TRACE_STYLE, (tfp, "READCSS.default%s:%s\n", (default_color_reset ? ".ignore" : ""), name ? name : "!?! empty !?!")); if (!default_color_reset) parse_style(name); FREE(name); return; /* do not need to process it again */ } CTRACE2(TRACE_STYLE, (tfp, "READCSS:%s\n", name ? name : "!?! empty !?!")); HTList_addObject(lss_styles, name); } static int style_readFromFileREC(char *lss_filename, char *parent_filename) { FILE *fh; char *buffer = NULL; CTRACE2(TRACE_STYLE, (tfp, "CSS:Reading styles from file: %s\n", lss_filename ? lss_filename : "?!? empty ?!?")); if (isEmpty(lss_filename)) return -1; if ((fh = LYOpenCFG(lss_filename, parent_filename, LYNX_LSS_FILE)) == 0) { /* this should probably be an alert or something */ CTRACE2(TRACE_STYLE, (tfp, "CSS:Can't open style file '%s', using defaults\n", lss_filename)); return -1; } if (parent_filename == 0) { free_colorstylestuff(); } while (LYSafeGets(&buffer, fh) != NULL) { LYTrimTrailing(buffer); LYTrimTail(buffer); LYTrimHead(buffer); if (!strncasecomp(buffer, "include:", 8)) style_readFromFileREC(LYSkipBlanks(buffer + 8), lss_filename); else if (buffer[0] != '#' && strlen(buffer) != 0) HStyle_addStyle(buffer); } LYCloseInput(fh); if ((parent_filename == 0) && LYCursesON) parse_userstyles(); return 0; } int style_readFromFile(char *filename) { return style_readFromFileREC(filename, (char *) 0); } /* Used in HTStructured methods: - kw */ void TrimColorClass(const char *tagname, char *styleclassname, int *phcode) { char *end, *start = NULL, *lookfrom; char tmp[64]; sprintf(tmp, ";%.*s", (int) sizeof(tmp) - 3, tagname); TrimLowercase(tmp); if ((lookfrom = styleclassname) != 0) { do { end = start; start = strstr(lookfrom, tmp); if (start) lookfrom = start + 1; } while (start); /* trim the last matching element off the end * - should match classes here as well (rp) */ if (end) *end = '\0'; } *phcode = hash_code(lookfrom && *lookfrom ? lookfrom : &tmp[1]); } /* This function is designed as faster analog to TrimColorClass. * It assumes that tag_name is present in stylename! -HV */ void FastTrimColorClass(const char *tag_name, unsigned name_len, char *stylename, char **pstylename_end, /*will be modified */ int *phcode) /*will be modified */ { char *tag_start = *pstylename_end; BOOLEAN found = FALSE; CTRACE2(TRACE_STYLE, (tfp, "STYLE.fast-trim: [%s] from [%s]: ", tag_name, stylename)); while (tag_start >= stylename) { for (; (tag_start >= stylename) && (*tag_start != ';'); --tag_start) ; if (!strncasecomp(tag_start + 1, tag_name, (int) name_len)) { found = TRUE; break; } --tag_start; } if (found) { *tag_start = '\0'; *pstylename_end = tag_start; } CTRACE2(TRACE_STYLE, (tfp, found ? "success.\n" : "failed.\n")); *phcode = hash_code(tag_start + 1); } /* This is called each time lss styles are read. It will fill * each element of 'cached_tag_styles' -HV */ void cache_tag_styles(void) { char buf[200]; int i; for (i = 0; i < HTML_ELEMENTS; ++i) { LYStrNCpy(buf, HTML_dtd.tags[i].name, sizeof(buf) - 1); LYLowerCase(buf); cached_tag_styles[i] = hash_code(buf); } } #define SIZEOF_CACHED_STYLES (unsigned) (cached_styles_rows * cached_styles_cols) static unsigned *RefCachedStyle(int y, int x) { unsigned *result = 0; if (cached_styles_ptr == 0) { cached_styles_rows = display_lines; cached_styles_cols = LYcols; cached_styles_ptr = typecallocn(unsigned, SIZEOF_CACHED_STYLES); } if (y >= 0 && x >= 0 && y < cached_styles_rows && x < cached_styles_cols) { result = cached_styles_ptr + (y * cached_styles_cols) + x; } return result; } BOOL ValidCachedStyle(int y, int x) { return (BOOL) (RefCachedStyle(y, x) != 0); } unsigned GetCachedStyle(int y, int x) { unsigned value = 0; unsigned *cache = RefCachedStyle(y, x); if (cache != 0) { value = *cache; } return value; } void SetCachedStyle(int y, int x, unsigned value) { unsigned *cache = RefCachedStyle(y, x); if (cache != 0) { *cache = value; } } void ResetCachedStyles(void) { if (cached_styles_ptr != NULL) { memset(cached_styles_ptr, 0, sizeof(unsigned) * SIZEOF_CACHED_STYLES); } } void FreeCachedStyles(void) { if (cached_styles_ptr != NULL) { FREE(cached_styles_ptr); cached_styles_rows = 0; cached_styles_cols = 0; } } /* * Recompute the pairs associated with the color style. */ void update_color_style(void) { CTRACE((tfp, "update_color_style %p\n", (void *) lss_styles)); memset(our_pairs, 0, sizeof(our_pairs)); parse_userstyles(); } static char *find_lss_file(const char *nominal) { return LYFindConfigFile(nominal, LYNX_LSS_FILE); } void clear_lss_list(void) { CTRACE((tfp, "clear_lss_list()\n")); free_lss_list(); empty_lss_list = TRUE; } /* * Add an entry to the lss-list, and cache the resolved filename if known. */ void add_to_lss_list(const char *source, const char *resolved) { LSS_NAMES *obj; LSS_NAMES *chk; BOOLEAN found = FALSE; int position = 0; #ifdef LY_FIND_LEAKS atexit(free_colorstyle_leaks); #endif CTRACE((tfp, "add_to_lss_list(\"%s\", \"%s\")\n", NonNull(source), NonNull(resolved))); if (list_of_lss_files == 0) { list_of_lss_files = HTList_new(); } while ((chk = HTList_objectAt(list_of_lss_files, position++)) != 0) { if (!strcmp(source, chk->given)) { found = TRUE; if (resolved && !chk->actual) { StrAllocCopy(chk->actual, resolved); } break; } } if (!found) { obj = typecalloc(LSS_NAMES); if (obj == NULL) outofmem(__FILE__, "add_to_lss_list"); StrAllocCopy(obj->given, source); StrAllocCopy(obj->actual, resolved); HTList_appendObject(list_of_lss_files, obj); empty_lss_list = FALSE; } } /* * This is called after reading lynx.cfg, to set the initial value for the * lss-file, and read its data. */ void init_color_styles(char **from_cmdline, const char *default_styles) { char *user_lss_file = *from_cmdline; char *cp; /* * If a command-line "-lss" option was given, or if an environment variable * is found, use that in preference to data from lynx.cfg */ if (user_lss_file == 0) user_lss_file = LYGetEnv("LYNX_LSS"); if (user_lss_file == 0) user_lss_file = LYGetEnv("lynx_lss"); if (user_lss_file != 0) empty_lss_list = (*user_lss_file == '\0'); /* * If the color-style is explicitly emptied, go no further. */ if (empty_lss_list) { CTRACE((tfp, "init_color_styles: overridden/empty\n")); return; } else if (list_of_lss_files == 0) { char *source = 0; char *config; StrAllocCopy(source, default_styles); config = source; while ((cp = LYstrsep(&config, ";")) != 0) { char *target; target = find_lss_file(LYPathLeaf(cp)); if (target != 0) add_to_lss_list(cp, target); } FREE(source); } if (user_lss_file != 0) { FREE(lynx_lss_file); lynx_lss_file = find_lss_file(cp = user_lss_file); *from_cmdline = 0; } else { lynx_lss_file = find_lss_file(cp = DeConst(LYNX_LSS_FILE)); } CTRACE1((tfp, "init_color_styles(%s)\n", NonNull(lynx_lss_file))); /* * If the lynx-style file is not available, inform the user and exit. */ if (isEmpty(lynx_lss_file) || !LYCanReadFile(lynx_lss_file)) { fprintf(stderr, gettext("\nLynx file \"%s\" is not available.\n\n"), NonNull(cp)); exit_immediately(EXIT_FAILURE); } /* * Otherwise, load the initial lss-file and add it to the list for the * options menu. */ style_readFromFile(lynx_lss_file); add_to_lss_list(LYPathLeaf(lynx_lss_file), lynx_lss_file); #ifndef NO_OPTION_FORMS build_lss_enum(list_of_lss_files); #endif } void reinit_color_styles(void) { int cs; for (cs = 0; cs < CSHASHSIZE; ++cs) { bucket *style = &hashStyles[cs]; while (style != 0) { bucket *next = style->next; if (next != 0) { hashStyles[cs] = *next; free(next); } style = hashStyles[cs].next; } } #ifdef USE_PRETTYSRC for (cs = 0; cs < HTL_num_lexemes; ++cs) { html_src_clean_item((HTlexeme) cs); } #endif free_colorstylestuff(); style_readFromFile(lynx_lss_file); } #endif /* USE_COLOR_STYLE */