https://github.com/akkartik/mu/blob/master/073next-token.subx
   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 */
pan> # if (curr >= end) break 1803 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx 1804 73/jump-if-addr>= $skip-until-close-paren-in-slice:break/disp8 1805 # c = *curr 1806 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL 1807 $skip-until-close-paren-in-slice:check-close: 1808 # if (c == ')') break 1809 3d/compare-eax-and 0x29/imm32/close-paren 1810 74/jump-if-= $skip-until-close-paren-in-slice:break/disp8 1811 # ++curr 1812 41/increment-ecx 1813 eb/jump $skip-until-close-paren-in-slice:loop/disp8 1814 $skip-until-close-paren-in-slice:break: 1815 # return curr 1816 89/copy 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to eax 1817 $skip-until-close-paren-in-slice:end: 1818 # . restore registers 1819 5a/pop-to-edx 1820 59/pop-to-ecx 1821 # . epilogue 1822 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 1823 5d/pop-to-ebp 1824 c3/return 1825 1826 test-skip-until-close-paren-in-slice: 1827 # . prologue 1828 55/push-ebp 1829 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 1830 # setup: (eax..ecx) = "*(abc) def" 1831 b8/copy-to-eax "*(abc) def"/imm32 1832 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 1833 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 1834 05/add-to-eax 4/imm32 1835 # eax = skip-until-close-paren-in-slice(eax, ecx) 1836 # . . push args 1837 51/push-ecx 1838 50/push-eax 1839 # . . call 1840 e8/call skip-until-close-paren-in-slice/disp32 1841 # . . discard args 1842 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 1843 # check-ints-equal(ecx-eax, 5, msg) # eax is at the ')' 1844 # . . push args 1845 68/push "F - test-skip-until-close-paren-in-slice"/imm32 1846 68/push 5/imm32 1847 # . . push ecx-eax 1848 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx 1849 51/push-ecx 1850 # . . call 1851 e8/call check-ints-equal/disp32 1852 # . . discard args 1853 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 1854 # . epilogue 1855 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 1856 5d/pop-to-ebp 1857 c3/return 1858 1859 test-skip-until-close-paren-in-slice-ignores-spaces: 1860 # . prologue 1861 55/push-ebp 1862 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 1863 # setup: (eax..ecx) = "*(a b)/yz" 1864 b8/copy-to-eax "*(a b)/yz"/imm32 1865 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 1866 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 1867 05/add-to-eax 4/imm32 1868 # eax = skip-until-close-paren-in-slice(eax, ecx) 1869 # . . push args 1870 51/push-ecx 1871 50/push-eax 1872 # . . call 1873 e8/call skip-until-close-paren-in-slice/disp32 1874 # . . discard args 1875 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 1876 # check-ints-equal(ecx-eax, 4, msg) # eax is at the ')' 1877 # . . push args 1878 68/push "F - test-skip-until-close-paren-in-slice-ignores-spaces"/imm32 1879 68/push 4/imm32 1880 # . . push ecx-eax 1881 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx 1882 51/push-ecx 1883 # . . call 1884 e8/call check-ints-equal/disp32 1885 # . . discard args 1886 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 1887 # . epilogue 1888 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 1889 5d/pop-to-ebp 1890 c3/return 1891 1892 test-skip-until-close-paren-in-slice-stops-at-end: 1893 # . prologue 1894 55/push-ebp 1895 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 1896 # setup: (eax..ecx) = "*(abc" # unbalanced dquote 1897 b8/copy-to-eax "*(abc"/imm32 1898 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 1899 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 1900 05/add-to-eax 4/imm32 1901 # eax = skip-until-close-paren-in-slice(eax, ecx) 1902 # . . push args 1903 51/push-ecx 1904 50/push-eax 1905 # . . call 1906 e8/call skip-until-close-paren-in-slice/disp32 1907 # . . discard args 1908 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 1909 # check-ints-equal(ecx-eax, 0, msg) # skipped to end of slice 1910 # . . push args 1911 68/push "F - test-skip-until-close-paren-in-slice-stops-at-end"/imm32 1912 68/push 0/imm32 1913 # . . push ecx-eax 1914 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx 1915 51/push-ecx 1916 # . . call 1917 e8/call check-ints-equal/disp32 1918 # . . discard args 1919 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 1920 # . epilogue 1921 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 1922 5d/pop-to-ebp 1923 c3/return 1924 1925 # . . vim:nowrap:textwidth=0